pax_global_header 0000666 0000000 0000000 00000000064 13537027355 0014524 g ustar 00root root 0000000 0000000 52 comment=e8690c96bad08145a772a29ce659734ea717664e flipper-0.17.1/ 0000775 0000000 0000000 00000000000 13537027355 0013253 5 ustar 00root root 0000000 0000000 flipper-0.17.1/.codeclimate.yml 0000664 0000000 0000000 00000000056 13537027355 0016326 0 ustar 00root root 0000000 0000000 exclude_patterns: - "lib/flipper/ui/public" flipper-0.17.1/.gitignore 0000664 0000000 0000000 00000000313 13537027355 0015240 0 ustar 00root root 0000000 0000000 *.gem *.rbc .bundle .config .ruby-version .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp log flipper.pstore .sass-cache bin flipper-0.17.1/.rubocop.yml 0000664 0000000 0000000 00000001546 13537027355 0015533 0 ustar 00root root 0000000 0000000 # This is the configuration used to check the rubocop source code. require: rubocop-rspec inherit_from: - .rubocop_todo.yml AllCops: Exclude: - 'docker-compose/**/*' - 'examples/**/*' - 'tmp/**/*' - 'bin/**/*' - 'vendor/bundle/**/*' TargetRubyVersion: 2.6 Style/Alias: Enabled: false Style/Documentation: Enabled: false Style/Encoding: Enabled: false Style/NumericLiterals: Enabled: false Style/StringLiterals: Enabled: false Style/GuardClause: Enabled: false Style/IfUnlessModifier: Enabled: false Metrics/LineLength: Max: 100 Style/RegexpLiteral: EnforcedStyle: mixed Style/TrailingCommaInArrayLiteral: EnforcedStyleForMultiline: consistent_comma Style/TrailingCommaInHashLiteral: EnforcedStyleForMultiline: consistent_comma RSpec/InstanceVariable: Enabled: false Lint/HandleExceptions: Enabled: false flipper-0.17.1/.rubocop_todo.yml 0000664 0000000 0000000 00000037146 13537027355 0016565 0 ustar 00root root 0000000 0000000 # This configuration was generated by # `rubocop --auto-gen-config` # on 2019-09-13 08:34:35 -0400 using RuboCop version 0.74.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # Offense count: 6 # Cop supports --auto-correct. # Configuration parameters: TreatCommentsAsGroupSeparators, Include. # Include: **/*.gemfile, **/Gemfile, **/gems.rb Bundler/OrderedGems: Exclude: - 'Gemfile' # Offense count: 6 # Cop supports --auto-correct. # Configuration parameters: TreatCommentsAsGroupSeparators, Include. # Include: **/*.gemspec Gemspec/OrderedDependencies: Exclude: - 'flipper-active_record.gemspec' - 'flipper-active_support_cache_store.gemspec' - 'flipper-api.gemspec' - 'flipper-dalli.gemspec' - 'flipper-ui.gemspec' # Offense count: 4 # Cop supports --auto-correct. # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. # SupportedHashRocketStyles: key, separator, table # SupportedColonStyles: key, separator, table # SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit Layout/AlignHash: Exclude: - 'lib/flipper/typecast.rb' # Offense count: 1 # Cop supports --auto-correct. Layout/ClosingHeredocIndentation: Exclude: - 'test/generators/flipper/active_record_generator_test.rb' # Offense count: 8 # Cop supports --auto-correct. Layout/EmptyLineAfterGuardClause: Exclude: - 'lib/flipper/adapters/sync/feature_synchronizer.rb' - 'lib/flipper/api/json_params.rb' - 'lib/flipper/api/v1/actions/feature.rb' - 'lib/flipper/type.rb' - 'lib/flipper/types/actor.rb' - 'lib/flipper/types/group.rb' - 'lib/flipper/ui/action.rb' # Offense count: 12 # Cop supports --auto-correct. Layout/EmptyLineAfterMagicComment: Exclude: - 'flipper-active_record.gemspec' - 'flipper-active_support_cache_store.gemspec' - 'flipper-api.gemspec' - 'flipper-cloud.gemspec' - 'flipper-dalli.gemspec' - 'flipper-moneta.gemspec' - 'flipper-mongo.gemspec' - 'flipper-redis.gemspec' - 'flipper-rollout.gemspec' - 'flipper-sequel.gemspec' - 'flipper-ui.gemspec' - 'flipper.gemspec' # Offense count: 1 # Cop supports --auto-correct. Layout/EmptyLinesAroundExceptionHandlingKeywords: Exclude: - 'test/adapters/active_record_test.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: squiggly, active_support, powerpack, unindent Layout/IndentHeredoc: Exclude: - 'test/generators/flipper/active_record_generator_test.rb' # Offense count: 2 # Cop supports --auto-correct. Layout/RescueEnsureAlignment: Exclude: - 'lib/flipper/api/v1/actions/percentage_of_actors_gate.rb' - 'lib/flipper/api/v1/actions/percentage_of_time_gate.rb' # Offense count: 1 Lint/AmbiguousRegexpLiteral: Exclude: - 'lib/flipper/instrumentation/statsd.rb' # Offense count: 6 # Configuration parameters: AllowSafeAssignment. Lint/AssignmentInCondition: Exclude: - 'lib/flipper/adapters/active_record.rb' - 'lib/flipper/adapters/sequel.rb' - 'lib/flipper/feature.rb' - 'lib/flipper/gate_values.rb' # Offense count: 2 Lint/DuplicateMethods: Exclude: - 'lib/flipper/ui.rb' - 'lib/flipper/ui/configuration.rb' # Offense count: 3 # Configuration parameters: MaximumRangeSize. Lint/MissingCopEnableDirective: Exclude: - 'lib/flipper/feature.rb' - 'lib/flipper/spec/shared_adapter_specs.rb' - 'lib/flipper/test/shared_adapter_test.rb' # Offense count: 1 # Cop supports --auto-correct. Lint/ScriptPermission: Exclude: - 'Rakefile' # Offense count: 21 Lint/ShadowingOuterLocalVariable: Exclude: - 'spec/flipper/api/v1/actions/actors_gate_spec.rb' - 'spec/flipper/api/v1/actions/percentage_of_actors_gate_spec.rb' - 'spec/flipper/api/v1/actions/percentage_of_time_gate_spec.rb' - 'spec/flipper/dsl_spec.rb' - 'spec/flipper/feature_spec.rb' - 'spec/flipper/types/group_spec.rb' # Offense count: 2 # Cop supports --auto-correct. Lint/UnneededCopDisableDirective: Exclude: - 'spec/flipper/adapter_spec.rb' # Offense count: 1 # Cop supports --auto-correct. Lint/UnneededRequireStatement: Exclude: - 'lib/flipper/registry.rb' # Offense count: 27 Lint/UselessAssignment: Exclude: - 'lib/flipper/instrumentation/log_subscriber.rb' - 'lib/flipper/instrumentation/subscriber.rb' - 'spec/flipper/api/action_spec.rb' - 'spec/flipper/dsl_spec.rb' - 'spec/flipper/feature_spec.rb' - 'spec/flipper/gates/group_spec.rb' - 'spec/flipper/instrumentation/statsd_subscriber_spec.rb' - 'spec/flipper/middleware/memoizer_spec.rb' - 'spec/flipper_spec.rb' # Offense count: 35 Metrics/AbcSize: Max: 29 # Offense count: 136 # Configuration parameters: CountComments, ExcludedMethods. # ExcludedMethods: refine Metrics/BlockLength: Max: 683 # Offense count: 11 # Configuration parameters: CountComments. Metrics/ClassLength: Max: 150 # Offense count: 20 # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. # URISchemes: http, https Metrics/LineLength: Max: 251 # Offense count: 59 # Configuration parameters: CountComments, ExcludedMethods. Metrics/MethodLength: Max: 23 # Offense count: 18 Naming/AccessorMethodName: Enabled: false # Offense count: 25 Naming/ConstantName: Exclude: - 'lib/flipper.rb' - 'lib/flipper/adapters/active_support_cache_store.rb' - 'lib/flipper/adapters/dalli.rb' - 'lib/flipper/adapters/instrumented.rb' - 'lib/flipper/adapters/memoizable.rb' - 'lib/flipper/adapters/memory.rb' - 'lib/flipper/adapters/mongo.rb' - 'lib/flipper/adapters/operation_logger.rb' - 'lib/flipper/adapters/pstore.rb' - 'lib/flipper/adapters/redis.rb' - 'lib/flipper/adapters/redis_cache.rb' - 'lib/flipper/feature.rb' - 'lib/flipper/gate_values.rb' - 'lib/flipper/typecast.rb' - 'lib/flipper/ui/decorators/feature.rb' # Offense count: 9 # Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts, AllowedAcronyms. # AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS Naming/FileName: Exclude: - 'lib/flipper-active_record.rb' - 'lib/flipper-active_support_cache_store.rb' - 'lib/flipper-api.rb' - 'lib/flipper-cloud.rb' - 'lib/flipper-dalli.rb' - 'lib/flipper-mongo.rb' - 'lib/flipper-redis.rb' - 'lib/flipper-sequel.rb' - 'lib/flipper-ui.rb' # Offense count: 1 # Configuration parameters: Blacklist. # Blacklist: (?-mix:(^|\s)(EO[A-Z]{1}|END)(\s|$)) Naming/HeredocDelimiterNaming: Exclude: - 'test/generators/flipper/active_record_generator_test.rb' # Offense count: 4 # Cop supports --auto-correct. # Configuration parameters: PreferredName. Naming/RescuedExceptionsVariableName: Exclude: - 'lib/flipper/adapters/active_record.rb' - 'lib/flipper/adapters/sync/synchronizer.rb' - 'lib/flipper/ui/actions/percentage_of_actors_gate.rb' - 'lib/flipper/ui/actions/percentage_of_time_gate.rb' # Offense count: 3 RSpec/BeforeAfterAll: Exclude: - 'spec/spec_helper.rb' - 'spec/rails_helper.rb' - 'spec/support/**/*.rb' - 'spec/flipper/adapters/active_record_spec.rb' - 'spec/flipper/adapters/http_spec.rb' # Offense count: 76 # Configuration parameters: Prefixes. # Prefixes: when, with, without RSpec/ContextWording: Enabled: false # Offense count: 1 # Configuration parameters: CustomIncludeMethods. RSpec/EmptyExampleGroup: Exclude: - 'spec/flipper/gates/actor_spec.rb' # Offense count: 3 # Cop supports --auto-correct. RSpec/EmptyLineAfterFinalLet: Exclude: - 'spec/flipper/adapters/moneta_spec.rb' - 'spec/flipper/ui/actions/features_spec.rb' # Offense count: 2 # Cop supports --auto-correct. RSpec/EmptyLineAfterSubject: Exclude: - 'spec/flipper/adapters/http_spec.rb' - 'spec/flipper/types/percentage_spec.rb' # Offense count: 138 # Configuration parameters: Max. RSpec/ExampleLength: Enabled: false # Offense count: 1 # Configuration parameters: CustomTransform, IgnoreMethods. RSpec/FilePath: Exclude: - 'spec/flipper/adapters/pstore_spec.rb' # Offense count: 6 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: implicit, each, example RSpec/HookArgument: Exclude: - 'spec/flipper/adapters/active_record_spec.rb' - 'spec/flipper/adapters/http_spec.rb' - 'spec/flipper/adapters/sequel_spec.rb' - 'spec/helper.rb' # Offense count: 4 # Cop supports --auto-correct. RSpec/HooksBeforeExamples: Exclude: - 'spec/flipper/ui_spec.rb' # Offense count: 22 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: it_behaves_like, it_should_behave_like RSpec/ItBehavesLike: Enabled: false # Offense count: 4 RSpec/IteratedExpectation: Exclude: - 'spec/flipper/dsl_spec.rb' - 'spec/flipper/feature_spec.rb' - 'spec/flipper/gates/percentage_of_actors_spec.rb' - 'spec/flipper/registry_spec.rb' # Offense count: 26 # Cop supports --auto-correct. RSpec/LeadingSubject: Enabled: false # Offense count: 17 # Configuration parameters: . # SupportedStyles: have_received, receive RSpec/MessageSpies: EnforcedStyle: receive # Offense count: 233 # Configuration parameters: AggregateFailuresByDefault. RSpec/MultipleExpectations: Max: 20 # Offense count: 449 # Configuration parameters: IgnoreSharedExamples. RSpec/NamedSubject: Enabled: false # Offense count: 25 RSpec/NestedGroups: Max: 5 # Offense count: 19 # Cop supports --auto-correct. # Configuration parameters: Strict, EnforcedStyle. # SupportedStyles: inflected, explicit RSpec/PredicateMatcher: Exclude: - 'spec/flipper/api/v1/actions/actors_gate_spec.rb' - 'spec/flipper/api/v1/actions/boolean_gate_spec.rb' - 'spec/flipper/api/v1/actions/clear_feature_spec.rb' - 'spec/flipper/api/v1/actions/features_spec.rb' - 'spec/flipper/api/v1/actions/groups_gate_spec.rb' - 'spec/flipper/types/group_spec.rb' # Offense count: 1 # Cop supports --auto-correct. RSpec/ReceiveNever: Exclude: - 'spec/flipper/middleware/memoizer_spec.rb' # Offense count: 2 RSpec/RepeatedDescription: Exclude: - 'spec/flipper/gates/boolean_spec.rb' # Offense count: 4 RSpec/RepeatedExample: Exclude: - 'spec/flipper/cloud_spec.rb' - 'spec/integration_spec.rb' # Offense count: 2 RSpec/ScatteredLet: Exclude: - 'spec/flipper/adapters/http_spec.rb' - 'spec/flipper/instrumentation/log_subscriber_spec.rb' # Offense count: 4 RSpec/SubjectStub: Exclude: - 'spec/flipper/adapters/sync_spec.rb' # Offense count: 17 # Configuration parameters: IgnoreNameless, IgnoreSymbolicNames. RSpec/VerifiedDoubles: Exclude: - 'spec/flipper/api/v1/actions/features_spec.rb' - 'spec/flipper/dsl_spec.rb' - 'spec/flipper/feature_spec.rb' - 'spec/flipper/types/group_spec.rb' - 'spec/flipper_spec.rb' - 'spec/integration_spec.rb' # Offense count: 1 Security/Eval: Exclude: - 'flipper.gemspec' # Offense count: 5 Security/MarshalLoad: Exclude: - 'lib/flipper/adapters/redis_cache.rb' - 'spec/flipper/adapters/redis_cache_spec.rb' # Offense count: 2 # Configuration parameters: EnforcedStyle. # SupportedStyles: inline, group Style/AccessModifierDeclarations: Exclude: - 'lib/flipper/api/action.rb' - 'lib/flipper/ui/action.rb' # Offense count: 3 Style/DoubleNegation: Exclude: - 'lib/flipper/adapters/memoizable.rb' - 'lib/flipper/gates/boolean.rb' - 'lib/flipper/typecast.rb' # Offense count: 1 # Cop supports --auto-correct. Style/EmptyLambdaParameter: Exclude: - 'lib/flipper/ui.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: compact, expanded Style/EmptyMethod: Exclude: - 'lib/flipper/gate.rb' # Offense count: 27 # Cop supports --auto-correct. Style/ExpandPathArguments: Enabled: false # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: format, sprintf, percent Style/FormatString: Exclude: - 'lib/flipper/instrumentation/log_subscriber.rb' # Offense count: 2 # Configuration parameters: . # SupportedStyles: annotated, template, unannotated Style/FormatStringToken: EnforcedStyle: unannotated # Offense count: 219 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: always, never Style/FrozenStringLiteralComment: Enabled: false # Offense count: 1 # Configuration parameters: AllowIfModifier. Style/IfInsideElse: Exclude: - 'lib/flipper/gates/actor.rb' # Offense count: 1 Style/MethodMissingSuper: Exclude: - 'lib/flipper/types/actor.rb' # Offense count: 1 Style/MissingRespondToMissing: Exclude: - 'lib/flipper/types/actor.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: literals, strict Style/MutableConstant: Exclude: - 'lib/flipper/ui/util.rb' # Offense count: 5 # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, EnforcedStyle, IgnoredMethods. # SupportedStyles: predicate, comparison Style/NumericPredicate: Exclude: - 'spec/**/*' - 'lib/flipper/api/v1/actions/percentage_of_actors_gate.rb' - 'lib/flipper/api/v1/actions/percentage_of_time_gate.rb' - 'lib/flipper/gates/percentage_of_actors.rb' - 'lib/flipper/gates/percentage_of_time.rb' - 'lib/flipper/types/percentage.rb' # Offense count: 34 # Cop supports --auto-correct. # Configuration parameters: PreferredDelimiters. Style/PercentLiteralDelimiters: Exclude: - 'Rakefile' - 'lib/flipper/spec/shared_adapter_specs.rb' - 'lib/flipper/test/shared_adapter_test.rb' - 'lib/flipper/ui.rb' - 'lib/flipper/ui/configuration.rb' - 'spec/flipper/adapter_spec.rb' - 'spec/flipper/adapters/http_spec.rb' - 'spec/flipper/adapters/memoizable_spec.rb' - 'spec/flipper/adapters/sync/synchronizer_spec.rb' - 'spec/flipper/adapters/sync_spec.rb' - 'spec/flipper/api/v1/actions/features_spec.rb' - 'spec/flipper/api_spec.rb' - 'spec/flipper/dsl_spec.rb' - 'spec/flipper/middleware/memoizer_spec.rb' - 'spec/flipper/registry_spec.rb' # Offense count: 3 # Cop supports --auto-correct. Style/RedundantBegin: Exclude: - 'spec/flipper/middleware/memoizer_spec.rb' - 'spec/flipper/ui/actions/feature_spec.rb' - 'spec/flipper/ui_spec.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: implicit, explicit Style/RescueStandardError: Exclude: - 'lib/flipper/adapters/sync/synchronizer.rb' - 'spec/flipper/middleware/memoizer_spec.rb' # Offense count: 4 # Cop supports --auto-correct. # Configuration parameters: ConvertCodeThatCanStartToReturnNil, Whitelist. # Whitelist: present?, blank?, presence, try, try! Style/SafeNavigation: Exclude: - 'lib/flipper/instrumentation/statsd_subscriber.rb' - 'lib/flipper/middleware/memoizer.rb' - 'spec/flipper/adapters/http_spec.rb' # Offense count: 8 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, MinSize. # SupportedStyles: percent, brackets Style/SymbolArray: Exclude: - 'Rakefile' - 'lib/flipper/adapters/operation_logger.rb' - 'lib/generators/flipper/templates/sequel_migration.rb' - 'spec/flipper/adapters/rollout_spec.rb' - 'spec/flipper/gate_values_spec.rb' flipper-0.17.1/.travis.yml 0000664 0000000 0000000 00000001156 13537027355 0015367 0 ustar 00root root 0000000 0000000 language: ruby cache: bundler rvm: - 2.4 - 2.5 - 2.6 before_install: - gem install bundler -v 1.17.3 --without guard - bundle _1.17.3_ install script: bundle exec rake services: - redis-server - mongodb - memcached env: - RAILS_VERSION=6.0.0 SQLITE3_VERSION=1.4.1 - RAILS_VERSION=5.2.3 SQLITE3_VERSION=1.3.11 - RAILS_VERSION=5.1.4 SQLITE3_VERSION=1.3.11 - RAILS_VERSION=5.0.0 SQLITE3_VERSION=1.3.11 - RAILS_VERSION=4.2.5 SQLITE3_VERSION=1.3.11 matrix: # don't run rails 5 on ruby versions that can't install rack 2 exclude: - rvm: 2.4 env: RAILS_VERSION=6.0.0 SQLITE3_VERSION=1.4.1 flipper-0.17.1/CODE_OF_CONDUCT.md 0000664 0000000 0000000 00000006220 13537027355 0016052 0 ustar 00root root 0000000 0000000 # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at nunemaker@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ flipper-0.17.1/Changelog.md 0000664 0000000 0000000 00000035320 13537027355 0015467 0 ustar 00root root 0000000 0000000 ## 0.17.1 * Fix require in flipper-active_record (https://github.com/jnunemaker/flipper/pull/437) ## 0.17.0 ### Additions/Changes * Allow shorthand block notation on group types (https://github.com/jnunemaker/flipper/pull/406) * Relax active record/support constraints to support Rails 6 (https://github.com/jnunemaker/flipper/pull/409) * Allow disabling fun (https://github.com/jnunemaker/flipper/pull/413) * Include thing_value in payload of Instrumented#enable and #disable (https://github.com/jnunemaker/flipper/pull/417) * Replace Erubis with Erubi (https://github.com/jnunemaker/flipper/pull/407) * Allow customizing Rack::Protection middleware list (https://github.com/jnunemaker/flipper/pull/385) * Allow setting write_timeout for ruby 2.6+ (https://github.com/jnunemaker/flipper/pull/433) * Drop support for Ruby 2.1, 2.2, and 2.3 (https://github.com/jnunemaker/flipper/commit/cf58982e70de5e6963b018ceced4f36a275f5b5d) * Add support for Ruby 2.6 (https://github.com/jnunemaker/flipper/commit/57888311449ec81184d3d47ba9ae5cb1ad4a2f45) * Remove support for Rails 3.2 (https://github.com/jnunemaker/flipper/commit/177c48c4edf51d4e411e7c673e30e06d1c66fb40) * Add write_timeout for flipper http adapter for ruby 2.6+ (https://github.com/jnunemaker/flipper/pull/433) * Relax moneta version to allow for < 1.2 (https://github.com/jnunemaker/flipper/pull/434). * Improve active record idempotency (https://github.com/jnunemaker/flipper/pull/436). * Allow customizing add actor placeholder text (https://github.com/jnunemaker/flipper/commit/5faa1e9cf66b68f8227d2f8408fb448a14676c45) ## 0.16.2 ### Additions/Changes * Bump rollout redis dependency to < 5 (https://github.com/jnunemaker/flipper/pull/403) * Bump redis dependency to < 5 (https://github.com/jnunemaker/flipper/pull/401) * Bump sequel dependency to < 6 (https://github.com/jnunemaker/flipper/pull/399 and https://github.com/jnunemaker/flipper/commit/edc767e69b4ce8daead9801f38e0e8bf6b238765) ## 0.16.1 ### Additions/Changes * Add actors API endpoint (https://github.com/jnunemaker/flipper/pull/372). * Fix rack body proxy require for those using flipper without rack (https://github.com/jnunemaker/flipper/pull/376). * Unescapes feature_name in FeatureNameFromRoute (https://github.com/jnunemaker/flipper/pull/377). * Replace delete_all with destroy_all in ActiveRecord adapter (https://github.com/jnunemaker/flipper/pull/395) * Target correct bootstrap breakpoints in flipper UI (https://github.com/jnunemaker/flipper/pull/396) ## 0.16.0 ### Bug Fixes * Support slashes in feature names (https://github.com/jnunemaker/flipper/pull/362). ### Additions/Changes * Re-order gates for improved performance in some cases (https://github.com/jnunemaker/flipper/pull/370). * Add Feature#exist?, DSL#exist? and Flipper#exist? (https://github.com/jnunemaker/flipper/pull/371). ## 0.15.0 * Move Flipper::UI configuration options to Flipper::UI::Configuration (https://github.com/jnunemaker/flipper/pull/345). * Bug fix in adapter synchronizing and switched DSL#import to use Synchronizer (https://github.com/jnunemaker/flipper/pull/347). * Fix AR adapter table name prefix/suffix bug (https://github.com/jnunemaker/flipper/pull/350). * Allow feature names to end with "features" in UI (https://github.com/jnunemaker/flipper/pull/353). ## 0.14.0 * Changed sync_interval to be seconds instead of milliseconds. ## 0.13.0 ### Additions/Changes * Update PStore adapter to allow setting thread_safe option (https://github.com/jnunemaker/flipper/pull/334). * Update Flipper::UI to Bootstrap 4 (https://github.com/jnunemaker/flipper/pull/336). * Add Flipper::UI configuration to add a banner with customizeable text and background color (https://github.com/jnunemaker/flipper/pull/337). * Add sync adapter (https://github.com/jnunemaker/flipper/pull/341). * Make cloud use sync adapter (https://github.com/jnunemaker/flipper/pull/342). This makes local flipper operations resilient to cloud failures. ## 0.12.2 ### Additions/Changes * Improvements/fixes/examples for rollout adapter (https://github.com/jnunemaker/flipper/pull/332). ## 0.12.1 ### Additions/Changes * Added rollout adapter documentation (https://github.com/jnunemaker/flipper/pull/328). ### Bug Fixes * Fixed ActiveRecord and Sequel adapters to include disabled features for `get_all` (https://github.com/jnunemaker/flipper/pull/327). ## 0.12 ### Additions/Changes * Added Flipper.instance= writer method for explicitly setting the default instance (https://github.com/jnunemaker/flipper/pull/309). * Added Flipper::UI configuration instance for changing text and things (https://github.com/jnunemaker/flipper/pull/306). * Delegate memoize= and memoizing? for Flipper and Flipper::DSL (https://github.com/jnunemaker/flipper/pull/310). * Fixed error when enabling the same group or actor more than once (https://github.com/jnunemaker/flipper/pull/313). * Fixed redis cache adapter key (and thus cache misses) (https://github.com/jnunemaker/flipper/pull/325). * Added Rollout adapter to make it easy to import rollout data into Flipper (https://github.com/jnunemaker/flipper/pull/319). * Relaxed redis gem dependency constraint to allow redis-rb 4 (https://github.com/jnunemaker/flipper/pull/317). * Added configuration option for Flipper::UI to disable feature removal (https://github.com/jnunemaker/flipper/pull/322). ## 0.11 ### Backwards Compatibility Breaks * Set flipper from env for API and UI (https://github.com/jnunemaker/flipper/pull/223 and https://github.com/jnunemaker/flipper/pull/229). It is documented, but now the memoizing middleware requires that the SetupEnv middleware is used first, unless you are configuring a Flipper default instance. * Drop support for Ruby 2.0 as it is end of lined (https://github.com/jnunemaker/flipper/commit/c2c81ed89938155ce91acb5173ac38580f630e3d). * Allow unregistered groups (https://github.com/jnunemaker/flipper/pull/244). Only break in compatibility is that previously unregistered groups could not be enabled and now they can be. * Removed support for metriks (https://github.com/jnunemaker/flipper/pull/291). ### Additions/Changes * Use primary keys with sequel adapter (https://github.com/jnunemaker/flipper/pull/210). Should be backwards compatible, but if you want it to work this way you will need to migrate your database to the new schema. * Add redis cache adapter (https://github.com/jnunemaker/flipper/pull/211). * Finish API and HTTP adapter that speaks to API. * Add flipper cloud adapter (https://github.com/jnunemaker/flipper/pull/249). Nothing to see here yet, but good stuff soon. ;) * Add importing (https://github.com/jnunemaker/flipper/pull/251). * Added Adapter#get_all to allow for more efficient preload_all (https://github.com/jnunemaker/flipper/pull/255). * Added :unless option to Flipper::Middleware::Memoizer to allow skipping memoization and preloading for certain requests. * Made it possible to instrument Flipper::Cloud (https://github.com/jnunemaker/flipper/commit/4b10e4d807772202f63881f5e2c00d11ac58481f). * Made it possible to wrap Http adapter when using Flipper::Cloud (https://github.com/jnunemaker/flipper/commit/4b10e4d807772202f63881f5e2c00d11ac58481f). * Instrument get_multi in instrumented adapter (https://github.com/jnunemaker/flipper/commit/951d25c5ce07d3b56b0b2337adf5f6bcbe4050e7). * Allow instrumenting Flipper::Cloud http adapter (https://github.com/jnunemaker/flipper/pull/253). * Add DSL#preload_all and Adapter#get_all to allow for making even more efficient loading of features (https://github.com/jnunemaker/flipper/pull/255). * Allow setting debug output of http adapter (https://github.com/jnunemaker/flipper/pull/256 and https://github.com/jnunemaker/flipper/pull/258). * Allow setting env key for middleware (https://github.com/jnunemaker/flipper/pull/259). * Added ActiveSupport cache store adapter for use with Rails.cache (https://github.com/jnunemaker/flipper/pull/265 and https://github.com/jnunemaker/flipper/pull/297). * Added support for up to 3 decimal places in percentage based rollouts (https://github.com/jnunemaker/flipper/pull/274). * Removed Flipper::GroupNotRegistered error as it is now unused (https://github.com/jnunemaker/flipper/pull/270). * Added get_all to all adapters (https://github.com/jnunemaker/flipper/pull/298). * Added support for Rails 5.1 (https://github.com/jnunemaker/flipper/pull/299). * Added Flipper default instance generation (https://github.com/jnunemaker/flipper/pull/279). ## 0.10.2 * Add Adapter#get_multi to allow for efficient loading of more than one feature at a time (https://github.com/jnunemaker/flipper/pull/198) * Add DSL#preload for efficiently loading several features at once using get_mutli (https://github.com/jnunemaker/flipper/pull/198) * Add :preload and :preload_all options to memoizer as a way of efficiently loading several features for a request in one network call instead of N where N is the number of features checked (https://github.com/jnunemaker/flipper/pull/198) * Strip whitespace out of feature/actor/group values posted by UI (https://github.com/jnunemaker/flipper/pull/205) * Fix bug with dalli adapter where deleting a feature using the UI or API was not clearing the cache in the dalli adapter which meant the feature would continue to use whatever cached enabled state was present until the TTL was hit (1cd96f6) * Change cache keys for dalli adapter. Backwards compatible in that it will just repopulate new keys on first check with this version, but old keys are not expired, so if you used the default ttl of 0, you'll have to expire them on your own. The primary reason for the change was safer namespacing of the cache keys to avoid collisions. ## 0.10.1 * Add docker compose support for contributing * Add sequel adapter * Show confirmation dialog when deleting a feature in flipper-ui ## 0.10.0 * Added feature check context (https://github.com/jnunemaker/flipper/pull/158) * Do not use mass assignment for active record adapter (https://github.com/jnunemaker/flipper/pull/171) * Several documentation improvements * Make Flipper::UI.app.inspect return a String (https://github.com/jnunemaker/flipper/pull/176) * changes boolean gate route to api/v1/features/boolean (https://github.com/jnunemaker/flipper/pull/175) * add api v1 percentage_of_actors endpoint (https://github.com/jnunemaker/flipper/pull/179) * add api v1 percentage_of_time endpoint (https://github.com/jnunemaker/flipper/pull/180) * add api v1 actors gate endpoint (https://github.com/jnunemaker/flipper/pull/181) * wait for activesupport to tell us when active record is loaded for active record adapter (https://github.com/jnunemaker/flipper/pull/192) ## 0.9.2 * GET /api/v1/features * POST /api/v1/features - add feature endpoint * rack-protection 2.0.0 support * pretty rake output ## 0.9.1 * bump flipper-active_record to officially support rails 5 ## 0.9.0 * Moves SharedAdapterTests module to Flipper::Test::SharedAdapterTests to avoid clobbering anything top level in apps that use Flipper * Memoizable, Instrumented and OperationLogger now delegate any missing methods to the original adapter. This was lost with the removal of the official decorator in 0.8, but is actually useful functionality for these "wrapping" adapters. * Instrumenting adapters is now off by default. Use Flipper::Adapters::Instrumented.new(adapter) to instrument adapters and maintain the old functionality. * Added dalli cache adapter (https://github.com/jnunemaker/flipper/pull/132) ## 0.8 * removed Flipper::Decorator and Flipper::Adapters::Decorator in favor of just calling methods on wrapped adapter * fix bug where certain versions of AR left off quotes for key column which caused issues with MySQL https://github.com/jnunemaker/flipper/issues/120 * fix bug where AR would store multiple gate values for percentage gates for each enable/disable and then nondeterministically pick one on read (https://github.com/jnunemaker/flipper/pull/122 and https://github.com/jnunemaker/flipper/pull/124) * added readonly adapter (https://github.com/jnunemaker/flipper/pull/111) * flipper groups now match for truthy values rather than explicitly only true (https://github.com/jnunemaker/flipper/issues/110) * removed gate operation instrumentation (https://github.com/jnunemaker/flipper/commit/32f14ed1fb25c64961b23c6be3dc6773143a06c8); I don't think it was useful and never found myself instrumenting it in reality * initial implementation of flipper api - very limited functionality right now (get/delete feature, boolean gate for feature) but more is on the way * made it easy to remove a feature (https://github.com/jnunemaker/flipper/pull/126) * add minitest shared tests for adapters that work the same as the shared specs for rspec (https://github.com/jnunemaker/flipper/pull/127) ## 0.7.5 * support for rails 5 beta/ rack 2 alpha * fix uninitialized constant in rails generators * fix adapter test for clear to ensure that feature is not deleted, only gates ## 0.7.4 * Add missing migration file to gemspec for flipper-active_record ## 0.7.3 * Add Flipper ActiveRecord adapter ## 0.7.2 * Add Flipper::UI.application_breadcrumb_href for setting breadcrumb back to original app from Flipper UI ## 0.7.1 * Fix bug where features with names that match static file routes were incorrectly routing to the file action (https://github.com/jnunemaker/flipper/issues/80) ## 0.7 * Added Flipper.groups and Flipper.group_names * Changed percentage_of_random to percentage_of_time * Added enable/disable convenience methods for all gates (enable_group, enable_actor, enable_percentage_of_actors, enable_percentage_of_time) * Added value convenience methods (boolean_value, groups_value, actors_value, etc.) * Added Feature#gate_values for getting typecast adapter gate values * Added Feature#enabled_gates and #disabled_gates for getting the gates that are enabled/disabled for the feature * Remove Feature#description * Added Flipper::Adapters::PStore * Moved memoizable decorator to instance variable storage from class level thread local stuff. Now not thread safe, but we can make a thread safe version later. UI * Totally new. Works like a charm. Mongo * Updated to latest driver (~> 2.0) ## 0.6.3 * Minor bug fixes ## 0.6.2 * Added Flipper.group_exists? ## 0.6.1 * Added statsd support for instrumentation. ## 0.4.0 * No longer use #id for detecting actors. You must now define #flipper_id on anything that you would like to behave as an actor. * Strings are now used instead of Integers for Actor identifiers. More flexible and the only reason I used Integers was to do modulo for percentage of actors. Since percentage of actors now uses hashing, integer is no longer needed. * Easy integration of instrumentation with AS::Notifications or anything similar. * A bunch of stuff around inspecting and getting names/descriptions out of things to more easily figure out what is going on. * Percentage of actors hash is now also seeded with feature name so the same actors don't get all features instantly. flipper-0.17.1/Dockerfile 0000664 0000000 0000000 00000000634 13537027355 0015250 0 ustar 00root root 0000000 0000000 FROM ruby:2.2.5 RUN apt-get update && apt-get install -y \ # build-essential \ # for postgres # libpq-dev \ # postgresql-client-9.4 \ # for nokogiri # libxml2-dev \ # libxslt1-dev \ # for a JS runtime # imagemagick \ # ghostscript \ # debug tools vim ENV APP_HOME /srv/app ENV BUNDLE_GEMFILE=$APP_HOME/Gemfile \ BUNDLE_JOBS=8 \ BUNDLE_PATH=/bundle_cache WORKDIR $APP_HOME flipper-0.17.1/Gemfile 0000664 0000000 0000000 00000001473 13537027355 0014553 0 ustar 00root root 0000000 0000000 source 'https://rubygems.org' gemspec name: 'flipper' Dir['flipper-*.gemspec'].each do |gemspec| plugin = gemspec.scan(/flipper-(.*)\.gemspec/).flatten.first gemspec(name: "flipper-#{plugin}", development_group: plugin) end gem 'pry' gem 'rake', '~> 12.3.3' gem 'shotgun', '~> 0.9' gem 'statsd-ruby', '~> 1.2.1' gem 'rspec', '~> 3.0' gem 'rack-test', '~> 0.6.3' gem 'sqlite3', "~> #{ENV['SQLITE3_VERSION'] || '1.3.11'}" gem 'rails', "~> #{ENV['RAILS_VERSION'] || '6.0.0'}" gem 'minitest', '~> 5.8' gem 'minitest-documentation' gem 'rubocop' gem 'rubocop-rspec' gem 'webmock', '~> 3.0' group(:guard) do gem 'guard', '~> 2.15' gem 'guard-rubocop', '~> 1.3' gem 'guard-rspec', '~> 4.5' gem 'guard-bundler', '~> 2.2' gem 'guard-coffeescript', '~> 2.0' gem 'guard-sass', '~> 1.6' gem 'rb-fsevent', '~> 0.9' end flipper-0.17.1/Guardfile 0000664 0000000 0000000 00000002115 13537027355 0015077 0 ustar 00root root 0000000 0000000 # A sample Guardfile # More info at https://github.com/guard/guard#readme guard 'bundler' do watch('Gemfile') watch(/^.+\.gemspec/) end rspec_options = { all_after_pass: false, all_on_start: false, failed_mode: :keep, cmd: 'bundle exec rspec', } guard 'rspec', rspec_options do watch(%r{^spec/.+_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch(/shared_adapter_specs\.rb$/) { 'spec' } watch('spec/helper.rb') { 'spec' } end coffeescript_options = { input: 'lib/flipper/ui/assets/javascripts', output: 'lib/flipper/ui/public/js', patterns: [%r{^lib/flipper/ui/assets/javascripts/(.+\.(?:coffee|coffee\.md|litcoffee))$}], } guard 'coffeescript', coffeescript_options do coffeescript_options[:patterns].each { |pattern| watch(pattern) } end sass_options = { input: 'lib/flipper/ui/assets/stylesheets', output: 'lib/flipper/ui/public/css', } guard 'sass', sass_options rubo_options = { all_on_start: false, } guard :rubocop, rubo_options do watch(/.+\.rb$/) watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) } end flipper-0.17.1/LICENSE 0000664 0000000 0000000 00000002056 13537027355 0014263 0 ustar 00root root 0000000 0000000 Copyright (c) 2012 John Nunemaker MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. flipper-0.17.1/README.md 0000664 0000000 0000000 00000007244 13537027355 0014541 0 ustar 00root root 0000000 0000000
__ _.-~ ) _..--~~~~,' ,-/ _ .-'. . . .' ,-',' ,' ) ,'. . . _ ,--~,-'__..-' ,' ,'. . . (@)' ---~~~~ ,' /. . . . '~~ ,-' /. . . . . ,-' ; . . . . - . ,' : . . . . _ / . . . . . `-.: . . . ./ - . ) . . . | _____..---.._/ _____ ~---~~~~----~~~~ ~~Feature flipping is the act of enabling or disabling features or parts of your application, ideally without re-deploying or changing anything in your code base. The goal of this gem is to make turning features on or off so easy that everyone does it. Whatever your data store, throughput, or experience, feature flipping should be easy and have minimal impact on your application. ## Installation Add this line to your application's Gemfile: gem 'flipper' And then execute: $ bundle Or install it yourself with: $ gem install flipper ## Examples The goal of the API for flipper was to have everything revolve around features and what ways they can be enabled. Start with top level and dig into a feature, then dig in further and enable that feature for a given type of access, as opposed to thinking about how the feature will be accessed first (ie: `stats.enable` vs `activate_group(:stats, ...)`). ```ruby require 'flipper' Flipper.configure do |config| config.default do # pick an adapter, this uses memory, any will do adapter = Flipper::Adapters::Memory.new # pass adapter to handy DSL instance Flipper.new(adapter) end end # check if search is enabled if Flipper.enabled?(:search) puts 'Search away!' else puts 'No search for you!' end puts 'Enabling Search...' Flipper.enable(:search) # check if search is enabled if Flipper.enabled?(:search) puts 'Search away!' else puts 'No search for you!' end ``` Of course there are more [examples for you to peruse](examples/). You could also check out the [DSL](lib/flipper/dsl.rb) and [Feature](lib/flipper/feature.rb) classes for code/docs. ## Docs * [Gates](docs/Gates.md) - Boolean, Groups, Actors, % of Actors, and % of Time * [Adapters](docs/Adapters.md) - Mongo, Redis, Cassandra, Active Record... * [Instrumentation](docs/Instrumentation.md) - ActiveSupport::Notifications and Statsd * [Optimization](docs/Optimization.md) - Memoization middleware and Cache adapters * [Web Interface](docs/ui/README.md) - Point and click... * [API](docs/api/README.md) - HTTP API interface * [Caveats](docs/Caveats.md) - Flipper beware! (see what I did there) * [Docker-Compose](docs/DockerCompose.md) - Using docker-compose in contributing ## Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Run the tests (`bundle exec rake`) 4. Commit your changes (`git commit -am 'Added some feature'`) 5. Push to the branch (`git push origin my-new-feature`) 6. Create new Pull Request ## Releasing 1. Update the version to be whatever it should be and commit. 2. `script/release` 3. Profit. ## Brought To You By | pic | @mention | area | |---|---|---| |  | [@jnunemaker](https://github.com/jnunemaker) | most things | |  | [@alexwheeler](https://github.com/alexwheeler) | api | |  | [@thetimbanks](https://github.com/thetimbanks) | ui | |  | [@lazebny](https://github.com/lazebny) | docker | flipper-0.17.1/Rakefile 0000664 0000000 0000000 00000002542 13537027355 0014723 0 ustar 00root root 0000000 0000000 #!/usr/bin/env rake $LOAD_PATH.push File.expand_path('../lib', __FILE__) require 'rake/testtask' require 'rubocop/rake_task' require 'flipper/version' # gem install pkg/*.gem # gem uninstall flipper flipper-ui flipper-redis desc 'Build gem into the pkg directory' task :build do FileUtils.rm_rf('pkg') Dir['*.gemspec'].each do |gemspec| system "gem build #{gemspec}" end FileUtils.mkdir_p('pkg') FileUtils.mv(Dir['*.gem'], 'pkg') end desc 'Tags version, pushes to remote, and pushes gem' task release: :build do sh 'git', 'tag', "v#{Flipper::VERSION}" sh 'git push origin master' sh "git push origin v#{Flipper::VERSION}" sh 'ls pkg/*.gem | xargs -n 1 gem push' end require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) do |t| t.rspec_opts = %w(--color --format documentation) end namespace :spec do desc 'Run specs for UI queue' RSpec::Core::RakeTask.new(:ui) do |t| t.rspec_opts = %w(--color) t.pattern = ['spec/flipper/ui/**/*_spec.rb', 'spec/flipper/ui_spec.rb'] end end Rake::TestTask.new do |t| t.libs = %w(lib test) t.pattern = 'test/**/*_test.rb' t.options = '--documentation' t.warning = false end Rake::TestTask.new(:test_rails) do |t| t.libs = %w(lib test_rails) t.pattern = 'test_rails/**/*_test.rb' t.warning = false end RuboCop::RakeTask.new task default: [:spec, :test, :test_rails, :rubocop] flipper-0.17.1/docker-compose.yml 0000664 0000000 0000000 00000001242 13537027355 0016707 0 ustar 00root root 0000000 0000000 # postgres: # container_name: flipper_postgres # image: postgres:9.4 redis: container_name: flipper_redis image: redis:2.8 mongo: container_name: flipper_mongo image: mongo:3.3 memcached: container_name: flipper_memcached image: memcached:1.4.33 app: container_name: flipper_app build: . dockerfile: Dockerfile volumes: - .:/srv/app volumes_from: - bundle_cache links: # - postgres - redis - mongo - memcached environment: - REDIS_URL=redis://redis:6379 - MONGODB_HOST=mongo - MEMCACHED_URL=memcached:11211 bundle_cache: container_name: flipper_bundle_cache image: busybox volumes: - /bundle_cache flipper-0.17.1/docs/ 0000775 0000000 0000000 00000000000 13537027355 0014203 5 ustar 00root root 0000000 0000000 flipper-0.17.1/docs/Adapters.md 0000664 0000000 0000000 00000012422 13537027355 0016271 0 ustar 00root root 0000000 0000000 # Adapters I plan on supporting the adapters in the flipper repo. Other adapters are welcome, so please let me know if you create one. ## Officially Supported * [ActiveRecord adapter](https://github.com/jnunemaker/flipper/blob/master/docs/active_record) - Rails 3, 4, and 5. * [ActiveSupportCacheStore adapter](https://github.com/jnunemaker/flipper/blob/master/docs/active_support_cache_store) - ActiveSupport::Cache::Store * [Cassanity adapter](https://github.com/jnunemaker/flipper-cassanity) * [Http adapter](https://github.com/jnunemaker/flipper/blob/master/docs/http) * [memory adapter](https://github.com/jnunemaker/flipper/blob/master/lib/flipper/adapters/memory.rb) – great for tests * [Moneta adapter](https://github.com/jnunemaker/flipper/blob/master/docs/moneta) * [Mongo adapter](https://github.com/jnunemaker/flipper/blob/master/docs/mongo) * [PStore adapter](https://github.com/jnunemaker/flipper/blob/master/lib/flipper/adapters/pstore.rb) – great for when a local file is enough * [read-only adapter](https://github.com/jnunemaker/flipper/blob/master/docs/read-only) * [Redis adapter](https://github.com/jnunemaker/flipper/blob/master/docs/redis) * [Sequel adapter](https://github.com/jnunemaker/flipper/blob/master/docs/sequel) ## Community Supported * [Active Record 3 adapter](https://github.com/blueboxjesse/flipper-activerecord) * [Consul adapter](https://github.com/gdavison/flipper-consul) ## Roll Your Own The basic API for an adapter is this: * `features` - Get the set of known features. * `add(feature)` - Add a feature to the set of known features. * `remove(feature)` - Remove a feature from the set of known features. * `clear(feature)` - Clear all gate values for a feature. * `get(feature)` - Get all gate values for a feature. * `enable(feature, gate, thing)` - Enable a gate for a thing. * `disable(feature, gate, thing)` - Disable a gate for a thing. * `get_multi(features)` - Get all gate values for several features at once. Implementation is optional. If none provided, default implementation performs N+1 `get` calls where N is the number of elements in the features parameter. * `get_all` - Get all gate values for all features at once. Implementation is optional. If none provided, default implementation performs two calls, one to `features` to get the names of all features and one to `get_multi` with the feature names from the first call. If you would like to make your own adapter, there are shared adapter specs (RSpec) and tests (MiniTest) that you can use to verify that you have everything working correctly. ### RSpec For example, here is what the in-memory adapter spec looks like: `spec/flipper/adapters/memory_spec.rb` ```ruby require 'helper' # The shared specs are included with the flipper gem so you can use them in # separate adapter specific gems. require 'flipper/spec/shared_adapter_specs' describe Flipper::Adapters::Memory do # an instance of the new adapter you are trying to create subject { described_class.new } # include the shared specs that the subject must pass it_should_behave_like 'a flipper adapter' end ``` ### MiniTest Here is what an in-memory adapter MiniTest looks like: `test/adapters/memory_test.rb` ```ruby require 'test_helper' class MemoryTest < MiniTest::Test prepend SharedAdapterTests def setup # Any code here will run before each test @adapter = Flipper::Adapters::Memory.new end def teardown # Any code here will run after each test end end ``` 1. Create a file under `test/adapters` that inherits from MiniTest::Test. 2. `prepend SharedAdapterTests`. 3. Initialize an instance variable `@adapter` referencing an instance of the adapter. 4. Add any code to run before each test in a `setup` method and any code to run after each test in a `teardown` method. A good place to start when creating your own adapter is to copy one of the adapters mentioned above and replace the client specific code with whatever client you are attempting to adapt. I would also recommend setting `fail_fast = true` in your RSpec configuration as that will just give you one failure at a time to work through. It is also handy to have the shared adapter spec file open. ## Swapping Adapters If you find yourself using one adapter and would like to swap to another, you can do that! Flipper adapters support importing another adapter's data. This will wipe the adapter you are wanting to swap to, if it isn't already clean, so please be careful. ```ruby # Say you are using redis... redis_adapter = Flipper::Adapters::Redis.new(Redis.new) redis_flipper = Flipper.new(redis_adapter) # And redis has some stuff enabled... redis_flipper.enable(:search) redis_flipper.enable_percentage_of_time(:verbose_logging, 5) redis_flipper.enable_percentage_of_actors(:new_feature, 5) redis_flipper.enable_actor(:issues, Flipper::Actor.new('1')) redis_flipper.enable_actor(:issues, Flipper::Actor.new('2')) redis_flipper.enable_group(:request_tracing, :staff) # And you would like to switch to active record... ar_adapter = Flipper::Adapters::ActiveRecord.new ar_flipper = Flipper.new(ar_adapter) # NOTE: This wipes active record clean and copies features/gates from redis into active record. ar_flipper.import(redis_flipper) # active record is now identical to redis. ar_flipper.features.each do |feature| pp feature: feature.key, values: feature.gate_values end ``` flipper-0.17.1/docs/Caveats.md 0000664 0000000 0000000 00000001536 13537027355 0016120 0 ustar 00root root 0000000 0000000 # Caveats 1. The [individual actor gate](https://github.com/jnunemaker/flipper/blob/master/docs/Gates.md#2-individual-actor) is typically not designed for hundreds or thousands of actors to be enabled. This is an explicit choice to make it easier to batch load data from the adapters instead of performing individual checks for actors over and over. If you need to enable something for more than 20 individual people, I would recommend using a [group](https://github.com/jnunemaker/flipper/blob/master/docs/Gates.md#5-group). 2. The disable method exists only to clear something that is enabled. If the thing you are disabling is not enabled, the disable is pointless. This means that if you enable one group an actor is in and disable another group, the feature will be enabled for the actor. ([related issue](https://github.com/jnunemaker/flipper/issues/71)) flipper-0.17.1/docs/DockerCompose.md 0000664 0000000 0000000 00000001433 13537027355 0017263 0 ustar 00root root 0000000 0000000 # Docker Compose for contributors This gem includes different adapters which require specific tools instaled on local machine. With docker this could be achieved inside container and new contributor could start working on code with a minumum efforts. ## Steps: 1. Install Docker Compose https://docs.docker.com/compose/install 1. Build the app container `docker-compose build` 1. Install gems `docker-compose run --rm app bundle install` 1. Run specs `docker-compose run --rm app bundle exec rspec` 1. Run tests `docker-compose run --rm app bundle exec rake test` 1. Clear and check files with Rubocop `docker-compose run --rm app bundle exec rubocop -D` 1. Optional: log in to container an using a `bash` shell for running specs ```sh docker-compose run --rm app bash bundle exec rspec ``` flipper-0.17.1/docs/Gates.md 0000664 0000000 0000000 00000014206 13537027355 0015573 0 ustar 00root root 0000000 0000000 # Gates Out of the box several types of enabling are supported. They are checked in this order: ## 1. Boolean All on or all off. Think top level things like `:stats`, `:search`, `:logging`, etc. Also, an easy way to release a new feature as once a feature is boolean enabled it is on for every situation. ```ruby flipper = Flipper.new(adapter) flipper[:stats].enable # turn on flipper[:stats].disable # turn off flipper[:stats].enabled? # check ``` ## 2. Individual Actor Turn feature on for individual thing. Think enable feature for someone to test or for a buddy. The only requirement for an individual actor is that it must respond to `flipper_id`. ```ruby flipper = Flipper.new(adapter) flipper[:stats].enable user flipper[:stats].enabled? user # true flipper[:stats].disable user flipper[:stats].enabled? user # false # you can enable anything, does not need to be user or person flipper[:search].enable group flipper[:search].enabled? group # you can also use shortcut methods flipper.enable_actor :search, user flipper.disable_actor :search, user flipper[:search].enable_actor user flipper[:search].disable_actor user ``` The key is to make sure you do not enable two different types of objects for the same feature. Imagine that user has a `flipper_id` of 6 and group has a `flipper_id` of 6. Enabling search for user would automatically enable it for group, as they both have a `flipper_id` of 6. The one exception to this rule is if you have globally unique `flipper_ids`, such as UUIDs. If your `flipper_ids` are unique globally in your entire system, enabling two different types should be safe. Another way around this is to prefix the `flipper_id` with the class name like this: ```ruby class User def flipper_id "User;#{id}" end end class Group def flipper_id "Group;#{id}" end end ``` ## 3. Percentage of Actors Turn this on for a percentage of actors (think user, member, account, group, whatever). Consistently on or off for this user as long as percentage increases. Think slow rollout of a new feature to a percentage of things. ```ruby flipper = Flipper.new(adapter) # returns a percentage of actors instance set to 10 percentage = flipper.actors(10) # turn stats on for 10 percent of users in the system flipper[:stats].enable percentage # checks if actor's flipper_id is in the enabled percentage by hashing # user.flipper_id.to_s to ensure enabled distribution is smooth flipper[:stats].enabled? user # you can also use shortcut methods flipper.enable_percentage_of_actors :search, 10 flipper.disable_percentage_of_actors :search # sets to 0 flipper[:search].enable_percentage_of_actors 10 flipper[:search].disable_percentage_of_actors # sets to 0 ``` ## 4. Percentage of Time Turn this on for a percentage of time. Think load testing new features behind the scenes and such. ```ruby flipper = Flipper.new(adapter) # get percentage of time instance set to 5 percentage = flipper.time(5) # Register a feature called logging and turn it on for 5 percent of the time. # This could be on during one request and off the next # could even be on first time in request and off second time flipper[:logging].enable percentage flipper[:logging].enabled? # this will return true 5% of the time. # you can also use shortcut methods flipper.enable_percentage_of_time :search, 5 # registers a feature called "enable_percentage_of_time" and enables it 5% of the time flipper.disable_percentage_of_time :search # sets to 0 flipper[:search].enable_percentage_of_time 5 flipper[:search].disable_percentage_of_time # sets to 0 ``` Timeness is not a good idea for enabling new features in the UI. Most of the time you want a feature on or off for a user, but there are definitely times when I have found percentage of time to be very useful. ## 5. Group Turn on feature based on the return value of block. Super flexible way to turn on a feature for multiple things (users, people, accounts, etc.) as long as the thing returns true when passed to the block. ```ruby # this registers a group Flipper.register(:admins) do |actor| actor.respond_to?(:admin?) && actor.admin? end flipper = Flipper.new(adapter) flipper[:stats].enable flipper.group(:admins) # This registers a stats feature and turns it on for admins (which is anything that returns true from the registered block). flipper[:stats].disable flipper.group(:admins) # turn off the stats feature for admins person = Person.find(params[:id]) flipper[:stats].enabled? person # check if enabled, returns true if person.admin? is true # you can also use shortcut methods. This also registers a stats feature and turns it on for admins. flipper.enable_group :stats, :admins person = Person.find(params[:id]) flipper[:stats].enabled? person # same as above. check if enabled, returns true if person.admin? is true flipper.disable_group :stats, :admins flipper[:stats].enable_group :admins flipper[:stats].disable_group :admins ``` Here's a quick explanation of the above code block: ``` Flipper.register(:admins) do |actor| actor.respond_to?(:admin?) && actor.admin? end ``` - The above first registers a group called `admins` which essentially saves a [Proc](http://www.eriktrautman.com/posts/ruby-explained-blocks-procs-and-lambdas-aka-closures) to be called later. ``` flipper[:stats].enable flipper.group(:admins) ``` - The above enables the stats feature to any object that returns true from the `:admins` proc. ``` person = Person.find(params[:id]) flipper[:stats].enabled? person # check if person is enabled, returns true if person.admin? is true ``` When the `person` object is passed to the `enabled?` method, it is then passed into the proc. If the proc returns true, the entire statement returns true and so `flipper[:stats].enabled? person` returns true. Whatever logic follows this conditional check is then executed. There is no requirement that the thing yielded to the block be a user model or whatever. It can be anything you want, therefore it is a good idea to check that the thing passed into the group block actually responds to what you are trying to do in the `register` proc. In your application code, you can do something like this now: ``` if flipper[:stats].enabled?(some_admin) # do thing... else # do not do thing end ``` flipper-0.17.1/docs/Instrumentation.md 0000664 0000000 0000000 00000001643 13537027355 0017734 0 ustar 00root root 0000000 0000000 # Instrumentation Flipper comes with automatic instrumentation. By default these work with ActiveSupport::Notifications, but only require the pieces of ActiveSupport that are needed and only do so if you actually attempt to require the instrumentation files listed below. To use the log subscriber: ```ruby # Gemfile gem "activesupport" # config/initializers/flipper.rb (or wherever you want it) require "flipper/instrumentation/log_subscriber" ``` To use the statsd instrumentation: ```ruby # Gemfile gem "activesupport" gem "statsd-ruby" # config/initializers/flipper.rb (or wherever you want it) require "flipper/instrumentation/statsd" Flipper::Instrumentation::StatsdSubscriber.client = Statsd.new # or whatever your statsd instance is ``` You can also do whatever you want with the instrumented events. Check out [this example](https://github.com/jnunemaker/flipper/blob/master/examples/instrumentation.rb) for more. flipper-0.17.1/docs/Optimization.md 0000664 0000000 0000000 00000011542 13537027355 0017216 0 ustar 00root root 0000000 0000000 # Optimization ## Memoizing Middleware One optimization that flipper provides is a memoizing middleware. The memoizing middleware ensures that you only make one adapter call per feature per request. This means if you check the same feature over and over, it will only make one Mongo, Redis, or whatever call per feature for the length of the request. You can use the middleware like so for Rails: ```ruby # setup default instance (perhaps in config/initializer/flipper.rb) Flipper.configure do |config| config.default do Flipper.new(...) end end # This assumes you setup a default flipper instance using configure. config.middleware.use Flipper::Middleware::Memoizer ``` **Note**: Be sure that the middleware is high enough up in your stack that all feature checks are wrapped. **Also Note**: If you haven't setup a default instance, you can pass the instance to `SetupEnv` as `Memoizer` uses whatever is setup in the `env`: ```ruby config.middleware.use Flipper::Middleware::SetupEnv, -> { Flipper.new(...) } config.middleware.use Flipper::Middleware::Memoizer ``` ### Options The Memoizer middleware also supports a few options. Use either `preload` or `preload_all`, not both. * **`:preload`** - An `Array` of feature names (`Symbol`) to preload for every request. Useful if you have features that are used on every endpoint. `preload` uses `Adapter#get_multi` to attempt to load the features in one network call instead of N+1 network calls. ```ruby config.middleware.use Flipper::Middleware::Memoizer, preload: [:stats, :search, :some_feature] ``` * **`:preload_all`** - A Boolean value (default: false) of whether or not all features should be preloaded. Using this results in a `preload_all` call with the result of `Adapter#get_all`. Any subsequent feature checks will be memoized and perform no network calls. I wouldn't recommend using this unless you have few features (< 100?) and nearly all of them are used on every request. ```ruby config.middleware.use Flipper::Middleware::Memoizer, preload_all: true ``` * **`:unless`** - A block that prevents preloading and memoization if it evaluates to true. ```ruby # skip preloading and memoizing if path starts with /assets config.middleware.use Flipper::Middleware::Memoizer, unless: ->(request) { request.path.start_with?("/assets") } ``` ## Cache Adapters Cache adapters allow you to cache adapter calls for longer than a single request and should be used alongside the memoization middleware to add another caching layer. ### Dalli > Dalli is a high performance pure Ruby client for accessing memcached servers. https://github.com/petergoldstein/dalli Example using the Dalli cache adapter with the Memory adapter and a TTL of 600 seconds: ```ruby dalli_client = Dalli::Client.new('localhost:11211') memory_adapter = Flipper::Adapters::Memory.new adapter = Flipper::Adapters::Dalli.new(memory_adapter, dalli_client, 600) flipper = Flipper.new(adapter) ``` ### RedisCache Applications using [Redis](https://redis.io/) via the [redis-rb](https://github.com/redis/redis-rb) client can take advantage of the RedisCache adapter. Initialize `RedisCache` with a flipper [adapter](https://github.com/jnunemaker/flipper/blob/master/docs/Adapters.md), a Redis client instance, and an optional TTL in seconds. TTL defaults to 3600 seconds. Example using the RedisCache adapter with the Memory adapter and a TTL of 4800 seconds: ```ruby require 'flipper/adapters/redis_cache' redis = Redis.new(url: ENV['REDIS_URL']) memory_adapter = Flipper::Adapters::Memory.new adapter = Flipper::Adapters::RedisCache.new(memory_adapter, redis, 4800) flipper = Flipper.new(adapter) ``` ### ActiveSupportCacheStore Rails applications can cache Flipper calls in any [ActiveSupport::Cache::Store](http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html) implementation. Add this line to your application's Gemfile: gem 'flipper-active_support_cache_store' And then execute: $ bundle Or install it yourself with: $ gem install flipper-active_support_cache_store Example using the ActiveSupportCacheStore adapter with ActiveSupport's [MemoryStore](http://api.rubyonrails.org/classes/ActiveSupport/Cache/MemoryStore.html), Flipper's [Memory adapter](https://github.com/jnunemaker/flipper/blob/master/lib/flipper/adapters/memory.rb), and a TTL of 5 minutes. ```ruby require 'active_support/cache' require 'flipper/adapters/active_support_cache_store' memory_adapter = Flipper::Adapters::Memory.new cache = ActiveSupport::Cache::MemoryStore.new adapter = Flipper::Adapters::ActiveSupportCacheStore.new(memory_adapter, cache, expires_in: 5.minutes) flipper = Flipper.new(adapter) ``` Setting `expires_in` is optional and will set an expiration time on Flipper cache keys. If specified, all flipper keys will use this `expires_in` over the `expires_in` passed to your ActiveSupport cache constructor. flipper-0.17.1/docs/active_record/ 0000775 0000000 0000000 00000000000 13537027355 0017014 5 ustar 00root root 0000000 0000000 flipper-0.17.1/docs/active_record/README.md 0000664 0000000 0000000 00000011002 13537027355 0020265 0 ustar 00root root 0000000 0000000 # Flipper ActiveRecord An ActiveRecord adapter for [Flipper](https://github.com/jnunemaker/flipper). Supported Active Record versions: * 4.2.x * 5.0.x * 6.0.x ## Installation Add this line to your application's Gemfile: gem 'flipper-active_record' And then execute: $ bundle Or install it yourself with: $ gem install flipper-active_record ## Usage For your convenience a migration generator is provided to create the necessary migrations for using the active record adapter. By default this generates a migration that will create two database tables - flipper_features and flipper_gates. $ rails g flipper:active_record Once you have created and executed the migration, you can use the active record adapter like so: ```ruby require 'flipper/adapters/active_record' adapter = Flipper::Adapters::ActiveRecord.new flipper = Flipper.new(adapter) # profit... ``` ## Internals Each feature is stored as a row in a features table. Each gate is stored as a row in a gates table, related to the feature by the feature's key. ```ruby require 'flipper/adapters/active_record' adapter = Flipper::Adapters::ActiveRecord.new flipper = Flipper.new(adapter) # Register a few groups. Flipper.register(:admins) { |thing| thing.admin? } Flipper.register(:early_access) { |thing| thing.early_access? } # Create a user class that has flipper_id instance method. User = Struct.new(:flipper_id) flipper[:stats].enable flipper[:stats].enable_group :admins flipper[:stats].enable_group :early_access flipper[:stats].enable_actor User.new('25') flipper[:stats].enable_actor User.new('90') flipper[:stats].enable_actor User.new('180') flipper[:stats].enable_percentage_of_time 15 flipper[:stats].enable_percentage_of_actors 45 flipper[:search].enable puts 'all rows in features table' pp Flipper::Adapters::ActiveRecord::Feature.all # [#