Giter Club home page Giter Club logo

flipper's Introduction

Flipper Mark

Website | Documentation | Examples | Chat | Twitter | Ruby.social

Flipper

Beautiful, performant feature flags for Ruby and Rails.

Flipper gives you control over who has access to features in your app.

  • Enable or disable features for everyone, specific actors, groups of actors, a percentage of actors, or a percentage of time.
  • Configure your feature flags from the console or a web UI.
  • Regardless of what data store you are using, Flipper can performantly store your feature flags.
  • Use Flipper Cloud to cascade features from multiple environments, share settings with your team, control permissions, keep an audit history, and rollback.

Control your software β€” don't let it control you.

Installation

Add this line to your application's Gemfile:

gem 'flipper'

You'll also want to pick a storage adapter, for example:

gem 'flipper-active_record'

And then execute:

$ bundle

Or install it yourself with:

$ gem install flipper

Subscribe & Ship

πŸ’Œ Β Subscribe - we'll send you short and sweet emails when we release new versions (examples).

Getting Started

Use Flipper#enabled? in your app to check if a feature is enabled.

# check if search is enabled
if Flipper.enabled?(:search, current_user)
  puts 'Search away!'
else
  puts 'No search for you!'
end

All features are disabled by default, so you'll need to explicitly enable them.

# Enable a feature for everyone
Flipper.enable :search

# Enable a feature for a specific actor
Flipper.enable_actor :search, current_user

# Enable a feature for a group of actors
Flipper.enable_group :search, :admin

# Enable a feature for a percentage of actors
Flipper.enable_percentage_of_actors :search, 2

Read more about getting started with Flipper and enabling features.

Flipper Cloud

Like Flipper and want more? Check out Flipper Cloud, which comes with:

  • multiple environments β€” production, staging, per continent, whatever you need. Every environment inherits from production by default and every project comes with a project overview page that shows each feature and its status in each environment.
  • personal environments β€” everyone on your team gets a personal environment (that inherits from production) which they can modify however they want without stepping on anyone else's toes.
  • permissions β€” grant access to everyone in your organization or lockdown each project to particular people. You can even limit access to a particular environment (like production) to specific people.
  • audit history β€” every feature change and who made it.
  • rollbacks β€” enable or disable a feature accidentally? No problem. You can roll back to any point in the audit history with a single click.
  • maintenance β€” we'll keep the lights on for you. We also have handy webhooks and background polling for keeping your app in sync with Cloud, so our availability won't affect yours. All your feature flag reads are local to your app.
  • everything in one place β€” no need to bounce around from different application UIs or IRB consoles.

Flipper Cloud Screenshot

Cloud is super simple to integrate with Rails (demo app), Sinatra or any other framework.

We also have a free plan that you can use forever.

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Run the tests (bundle exec rake). Check out Docker-Compose if you need help getting all the adapters running.
  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. Create a new GitHub Release

Brought To You By

pic @mention area
@jnunemaker @jnunemaker most things
@bkeepers @bkeepers most things
@dpep @dpep tbd
@alexwheeler @alexwheeler api
@thetimbanks @thetimbanks ui
@lazebny @lazebny docker
@pagertree @pagertree sponsor
@kdaigle @kdaigle sponsor

flipper's People

Contributors

alexwheeler avatar aroben avatar bendb-instacart avatar bf4 avatar bkeepers avatar camallen avatar chastell avatar dependabot[bot] avatar dpep avatar greatuserongithub avatar gshutler avatar irphilli avatar ivan-garanin avatar ivorpad avatar jackca avatar jdelstrother avatar jevin avatar jnunemaker avatar juanroldan1989 avatar kathf avatar kevinbongart avatar krzcho avatar lazebny avatar onesneakymofo avatar pbstriker38 avatar petergoldstein avatar radar avatar rsanheim avatar thetimbanks avatar xlgmokha avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flipper's Issues

Create memcached adapter

I can definitely see use for a memcached adapter that is simply a cache adapter. It will work just like the memoized adapter but talk to memcache instead of memory.

We fronted rollout with memcache when working on words with friends and it really relieved the feature reads burden.

Conflict between Time & Actors percentages

The interface appears to suggest that it's possible to enable, for example, a feature for 50% of actors and 25% of the time.

image

In my experience when the actors percentage gate is enabled, it means that whoever does have the feature enabled will have it enabled 100% of the time. In that case, the interface should not allow the other gate to be enabled simultaneously, should it?

403 Forbidden response from flipper ui

I am sorry in advance for maybe a noobish question but I am stuck
After deploying flipper (with ui) to production and trying to flip feature I got 403 Forbidden from every action
I am running two thins, http and ssl one with force ssl option on
I see ui just fine but I can't trigger any action getting 403 Forbidden
Going back to development environment (with single ssl thin) and actions work fine
Any hints?

uncomfortable solution is to flip on development and copy flipper.pstore to production

Gems included by the bundle:

  • actionmailer (4.1.14)
  • actionpack (4.1.14)
  • actionview (4.1.14)
  • activemodel (4.1.14)
  • activerecord (4.1.14)
  • activerecord-sqlserver-adapter (4.1.1)
  • activesupport (4.1.14)
  • addressable (2.3.8)
  • ar-octopus (0.8.5)
  • arel (5.0.1.20140414130214)
  • bcrypt (3.1.10)
  • builder (3.2.2)
  • bundler (1.10.3)
  • coffee-rails (4.1.0)
  • coffee-script (2.4.1)
  • coffee-script-source (1.9.1.1)
  • concurrent-ruby (1.0.0)
  • daemons (1.2.3)
  • devise (3.5.2)
  • devise_ldap_authenticatable (0.8.5)
  • erubis (2.7.0)
  • eventmachine (1.0.8)
  • exception_notification (4.1.1)
  • execjs (2.6.0)
  • faraday (0.9.2)
  • flipper (0.7.4)
  • flipper-ui (0.7.4)
  • hashie (3.4.3)
  • i18n (0.7.0)
  • json (1.8.3)
  • jwt (1.5.2)
  • mail (2.6.3)
  • mime-types (2.99)
  • mini_portile (0.6.2)
  • minitest (5.8.4)
  • multi_json (1.11.2)
  • multi_xml (0.5.5)
  • multipart-post (2.0.0)
  • net-ldap (0.11)
  • oauth2 (1.0.0)
  • omniauth (1.2.2)
  • omniauth-google-oauth2 (0.2.9 b861c83)
  • omniauth-oauth2 (1.3.1)
  • orm_adapter (0.5.0)
  • protected_attributes (1.1.3)
  • rack (1.5.5)
  • rack-protection (1.5.3)
  • rack-test (0.6.3)
  • rails (4.1.14)
  • railties (4.1.14)
  • rake (10.5.0)
  • responders (1.1.2)
  • sass (3.4.19)
  • sass-rails (5.0.4)
  • sprockets (3.5.2)
  • sprockets-rails (2.3.3)
  • thin (1.6.4)
  • thor (0.19.1)
  • thread_safe (0.3.5)
  • tilt (2.0.1)
  • tiny_tds (0.7.0)
  • tzinfo (1.2.2)
  • tzinfo-data (1.2015.7)
  • uglifier (2.7.2)
  • warden (1.2.3)

Allow customizing UI text

Each gates text/help/instructions should be customizable so companies/developers can make it feel more like there own. I can also see providing an actor to instance conversion block for changing things like User:6 to @jnunemaker or whatever makes sense.

Add Feature #enabled/disabled_gates

Should return all gates enabled for enabled_gates and disabled for disabled_gates. Something like:

        def enabled_gates
          values = gate_values
          gates.select { |gate|
            gate.enabled?(values[gate.key])
          }
        end

        def enabled_gate_names
          enabled_gates.map(&:key)
        end

Feature Request: make `Flipper::Feature#name` public (not internal)

This is really just a request for a change to the comment on line 11 (https://github.com/jnunemaker/flipper/blob/master/lib/flipper/feature.rb#L11) to document the attribute reader as public instead of "Internal".

If I use Flipper::DSL#features to get a set of all known features, I want to be sure that, according to the official public API, I'm allowed to get the name of each of those features. For my use case, I need to be able to enumerate over all the features and know their names (I'm essentially "exporting" this list) and the DSL #features method seems like the most straight forward way to do it.

Thanks.

undefined method `name' for module `Kernel'

When I go to /flipper in flipper-ui I get this stack trace:

NameError - undefined method `name' for module `Kernel':
  /Users/kcoleman/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/delegate.rb:89:in `method_missing'
  flipper (0.7.0.beta6) lib/flipper/adapters/instrumented.rb:32:in `features'
  /Users/kcoleman/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/delegate.rb:87:in `method_missing'
  flipper (0.7.0.beta6) lib/flipper/adapters/memoizable.rb:25:in `block in features'
  flipper (0.7.0.beta6) lib/flipper/adapters/memoizable.rb:24:in `features'
  flipper (0.7.0.beta6) lib/flipper/dsl.rb:230:in `features'
  flipper-ui (0.7.0.beta6) lib/flipper/ui/actions/features.rb:14:in `get'
  flipper-ui (0.7.0.beta6) lib/flipper/ui/action.rb:67:in `block in run'
  flipper-ui (0.7.0.beta6) lib/flipper/ui/action.rb:67:in `run'
  flipper-ui (0.7.0.beta6) lib/flipper/ui/action.rb:27:in `run'
  flipper-ui (0.7.0.beta6) lib/flipper/ui/middleware.rb:70:in `call!'
  flipper-ui (0.7.0.beta6) lib/flipper/ui/middleware.rb:60:in `call'
  flipper (0.7.0.beta6) lib/flipper/middleware/memoizer.rb:40:in `call'
  rack (1.6.0) lib/rack/methodoverride.rb:22:in `call'
  rack-protection (1.5.3) lib/rack/protection/base.rb:49:in `call'
  rack-protection (1.5.3) lib/rack/protection/xss_header.rb:18:in `call'
  rack-protection (1.5.3) lib/rack/protection/base.rb:49:in `call'
  rack-protection (1.5.3) lib/rack/protection/base.rb:49:in `call'
  rack-protection (1.5.3) lib/rack/protection/path_traversal.rb:16:in `call'
  rack-protection (1.5.3) lib/rack/protection/json_csrf.rb:18:in `call'
  rack-protection (1.5.3) lib/rack/protection/base.rb:49:in `call'
  rack-protection (1.5.3) lib/rack/protection/base.rb:49:in `call'
  rack-protection (1.5.3) lib/rack/protection/frame_options.rb:31:in `call'
  rack (1.6.0) lib/rack/builder.rb:153:in `call'
  actionpack (4.2.0) lib/action_dispatch/routing/mapper.rb:51:in `serve'
  actionpack (4.2.0) lib/action_dispatch/journey/router.rb:43:in `block in serve'
  actionpack (4.2.0) lib/action_dispatch/journey/router.rb:30:in `serve'
  actionpack (4.2.0) lib/action_dispatch/routing/route_set.rb:802:in `call'
  warden (1.2.3) lib/warden/manager.rb:35:in `block in call'
  warden (1.2.3) lib/warden/manager.rb:34:in `call'
  rack (1.6.0) lib/rack/etag.rb:24:in `call'
  rack (1.6.0) lib/rack/conditionalget.rb:25:in `call'
  rack (1.6.0) lib/rack/head.rb:13:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/params_parser.rb:27:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/flash.rb:260:in `call'
  rack (1.6.0) lib/rack/session/abstract/id.rb:225:in `context'
  rack (1.6.0) lib/rack/session/abstract/id.rb:220:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/cookies.rb:560:in `call'
  activerecord (4.2.0) lib/active_record/query_cache.rb:36:in `call'
  activerecord (4.2.0) lib/active_record/connection_adapters/abstract/connection_pool.rb:647:in `call'
  activerecord (4.2.0) lib/active_record/migration.rb:378:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call'
  activesupport (4.2.0) lib/active_support/callbacks.rb:88:in `_run_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:734:in `_run_call_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:81:in `run_callbacks'
  actionpack (4.2.0) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/reloader.rb:73:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/remote_ip.rb:78:in `call'
  better_errors (2.1.1) lib/better_errors/middleware.rb:84:in `protected_app_call'
  better_errors (2.1.1) lib/better_errors/middleware.rb:79:in `better_errors_call'
  better_errors (2.1.1) lib/better_errors/middleware.rb:57:in `call'
  web-console (2.0.0) lib/action_dispatch/debug_exceptions.rb:18:in `middleware_call'
  web-console (2.0.0) lib/action_dispatch/debug_exceptions.rb:13:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'
  railties (4.2.0) lib/rails/rack/logger.rb:38:in `call_app'
  railties (4.2.0) lib/rails/rack/logger.rb:20:in `block in call'
  activesupport (4.2.0) lib/active_support/tagged_logging.rb:68:in `block in tagged'
  activesupport (4.2.0) lib/active_support/tagged_logging.rb:26:in `tagged'
  activesupport (4.2.0) lib/active_support/tagged_logging.rb:68:in `tagged'
  railties (4.2.0) lib/rails/rack/logger.rb:20:in `call'
  quiet_assets (1.1.0) lib/quiet_assets.rb:27:in `call_with_quiet_assets'
  actionpack (4.2.0) lib/action_dispatch/middleware/request_id.rb:21:in `call'
  rack (1.6.0) lib/rack/methodoverride.rb:22:in `call'
  rack (1.6.0) lib/rack/runtime.rb:18:in `call'
  activesupport (4.2.0) lib/active_support/cache/strategy/local_cache_middleware.rb:28:in `call'
  rack (1.6.0) lib/rack/lock.rb:17:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/static.rb:113:in `call'
  rack (1.6.0) lib/rack/sendfile.rb:113:in `call'
  sentry-raven (0.12.3) lib/raven/integrations/rack.rb:61:in `call'
  rack-cors (0.3.1) lib/rack/cors.rb:72:in `call'
  railties (4.2.0) lib/rails/engine.rb:518:in `call'
  railties (4.2.0) lib/rails/application.rb:164:in `call'
  rack (1.6.0) lib/rack/lock.rb:17:in `call'
  rack (1.6.0) lib/rack/content_length.rb:15:in `call'
  rack (1.6.0) lib/rack/handler/webrick.rb:89:in `service'
  /Users/kcoleman/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/webrick/httpserver.rb:138:in `service'
  /Users/kcoleman/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/webrick/httpserver.rb:94:in `run'
  /Users/kcoleman/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/webrick/server.rb:294:in `block in start_thread'a

1/3 of requests are without an enabled feature

Using flipper v 0.70

application_controller

 helper_method :feature_enabled?
 def feature_enabled?(feature_name)
    $flipper[feature_name].enabled?(current_user)
  end

in template just ask for

if feature_enabled? :chatting

the feature was enabled for all via ui

around every third request, the features is disabled. we are using 3 applicationserver, but all are having a good connection to redis.

known bug?

Adapters to no longer support delete + set_delete?

With the updated set of adapters for the 0.5 release, the #set_delete and #delete methods have been removed from the adapter specs. This appeared to be the only way to abstractly delete features/sets from any given adapter and it was definitely nice to have. Is this move completely intentional?

Tests fail with ActiveSupport 4.0.0+

Failures look like:

Failures:

  1) Flipper::Instrumentation::LogSubscriber feature enabled checks logs feature calls with result after operation
     Failure/Error: raise("Could not find line matching #{str.inspect} in #{lines.inspect}")
     RuntimeError:
       Could not find line matching "Flipper feature(search) enabled? false" in []
     # ./spec/flipper/instrumentation/log_subscriber_spec.rb:105:in `find_line'
     # ./spec/flipper/instrumentation/log_subscriber_spec.rb:35:in `block (3 levels) in <top (required)>'

Works fine with < 4.0.0

Groups don't intersect?

If an actor belongs to two groups, once it's enabled for one group it's automatically enabled for that user even if the second group defines it as disabled.

This might be the desired output, but it felt counterintuitive to me.

# Contrived example, but bear with me.
Flipper.register(:large_group) do |actor|
  actor.part_of_large_group?
end

Flipper.register(:subset_of_large_group) do |actor|
  actor.part_of_small_group? 
end

$flipper[:can_do].enable_group :large_group
$flipper[:can_do].disable_group :subset_of_large_group

$flipper[:can_do].enabled? user_in_subset # => true

Is this the desired behavior?

Best practices with Rails?

Flipper looks great, very flexible and with a good selection of adapters. But with great choice comes… great confusion?

Rails setup

The DSL example in the README is ok but it could do with some suggested best practices in Rails.

  • Put the flipper init code in a initializer?
  • use a Flipper global variable? Any concerns when using multi-threaded servers like Puma?
  • I'd probably wrap access to $flipper in helper method anyway. Perhaps it makes more sense to do the initialization there to avoid the global variable?

I'd love to hear how others actually use Flipper in Rails.

Adapter Selection

Is there any guidance we could offer on which adapters to choose? For example, I'm guessing the selection process goes like:

  1. if you use redis already, use flipper-redis
  2. if you don't have redis, use one of the activerecord adapters as appropriate
  3. pstore for single-server environments (i.e., not Heroku?)
  4. memory adapter for tests
  5. Mongo/Cassandra for special cases where you already have this dependancy

Also, I'm not sure of the maturity for these adapters so maybe that also plays a factor. And is there a performance consideration?

Help?

I'm just setting up Flipper so I don't have the answers myself right now but I'll happily write a PR on the docs when I figure it out. Help me?

Add read-only mode

I appreciate the fact that flipper-ui is a separate gem from the core flipper gem, as it means we can have a separate lightweight management application (using flipper-ui) for enabling and disabling feature flags. In fact, we intend to use a single lightweight rack application to manage feature flags for a few smaller ruby applications (and one large one). However, with this kind of setup it would be ideal to prevent accidental changes to state except from the management application. A 'read_only' setting would be a simple way to address this.

Problem with flipper-ui and rails 4.2.1

We recently tried to update to the latest version of the gem for our app and found a problem with the flipper ui that won't even load.

Here is a trace log of the request:

Started GET "/flipper" for ::1 at 2015-04-28 15:08:59 -0500

NoMethodError (undefined method `each' for #<ActionDispatch::Request::Session:0x7fb5738240f8 not yet loaded>):
  rack (1.6.0) lib/rack/session/abstract/id.rb:158:in `stringify_keys'
  rack (1.6.0) lib/rack/session/abstract/id.rb:95:in `update'
  rack (1.6.0) lib/rack/session/abstract/id.rb:258:in `prepare_session'
  rack (1.6.0) lib/rack/session/abstract/id.rb:224:in `context'
  rack (1.6.0) lib/rack/session/abstract/id.rb:220:in `call'
  rack (1.6.0) lib/rack/builder.rb:153:in `call'
  actionpack (4.2.1) lib/action_dispatch/routing/mapper.rb:51:in `serve'
  actionpack (4.2.1) lib/action_dispatch/journey/router.rb:43:in `block in serve'
  actionpack (4.2.1) lib/action_dispatch/journey/router.rb:30:in `each'
  actionpack (4.2.1) lib/action_dispatch/journey/router.rb:30:in `serve'
  actionpack (4.2.1) lib/action_dispatch/routing/route_set.rb:819:in `call'
  warden (1.2.3) lib/warden/manager.rb:35:in `block in call'
  warden (1.2.3) lib/warden/manager.rb:34:in `catch'
  warden (1.2.3) lib/warden/manager.rb:34:in `call'
  rack (1.6.0) lib/rack/etag.rb:24:in `call'
  rack (1.6.0) lib/rack/conditionalget.rb:25:in `call'
  rack (1.6.0) lib/rack/head.rb:13:in `call'
  actionpack (4.2.1) lib/action_dispatch/middleware/params_parser.rb:27:in `call'
  actionpack (4.2.1) lib/action_dispatch/middleware/flash.rb:260:in `call'
  rack (1.6.0) lib/rack/session/abstract/id.rb:225:in `context'
  rack (1.6.0) lib/rack/session/abstract/id.rb:220:in `call'
  actionpack (4.2.1) lib/action_dispatch/middleware/cookies.rb:560:in `call'
  activerecord (4.2.1) lib/active_record/query_cache.rb:36:in `call'
  activerecord (4.2.1) lib/active_record/connection_adapters/abstract/connection_pool.rb:649:in `call'
  activerecord (4.2.1) lib/active_record/migration.rb:378:in `call'
  actionpack (4.2.1) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call'
  activesupport (4.2.1) lib/active_support/callbacks.rb:88:in `call'
  activesupport (4.2.1) lib/active_support/callbacks.rb:88:in `_run_callbacks'
  activesupport (4.2.1) lib/active_support/callbacks.rb:776:in `_run_call_callbacks'
  activesupport (4.2.1) lib/active_support/callbacks.rb:81:in `run_callbacks'
  actionpack (4.2.1) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
  actionpack (4.2.1) lib/action_dispatch/middleware/reloader.rb:73:in `call'
  actionpack (4.2.1) lib/action_dispatch/middleware/remote_ip.rb:78:in `call'
  actionpack (4.2.1) lib/action_dispatch/middleware/debug_exceptions.rb:17:in `call'
  web-console (2.1.2) lib/web_console/middleware.rb:37:in `call'
  actionpack (4.2.1) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'
  railties (4.2.1) lib/rails/rack/logger.rb:38:in `call_app'
  railties (4.2.1) lib/rails/rack/logger.rb:20:in `block in call'
  activesupport (4.2.1) lib/active_support/tagged_logging.rb:68:in `block in tagged'
  activesupport (4.2.1) lib/active_support/tagged_logging.rb:26:in `tagged'
  activesupport (4.2.1) lib/active_support/tagged_logging.rb:68:in `tagged'
  railties (4.2.1) lib/rails/rack/logger.rb:20:in `call'
  actionpack (4.2.1) lib/action_dispatch/middleware/request_id.rb:21:in `call'
  rack (1.6.0) lib/rack/methodoverride.rb:22:in `call'
  rack (1.6.0) lib/rack/runtime.rb:18:in `call'
  activesupport (4.2.1) lib/active_support/cache/strategy/local_cache_middleware.rb:28:in `call'
  rack (1.6.0) lib/rack/lock.rb:17:in `call'
  actionpack (4.2.1) lib/action_dispatch/middleware/static.rb:113:in `call'
  rack (1.6.0) lib/rack/sendfile.rb:113:in `call'
  railties (4.2.1) lib/rails/engine.rb:518:in `call'
  railties (4.2.1) lib/rails/application.rb:164:in `call'
  rack (1.6.0) lib/rack/lock.rb:17:in `call'
  rack (1.6.0) lib/rack/content_length.rb:15:in `call'
  rack (1.6.0) lib/rack/handler/webrick.rb:89:in `service'

And a sample app that reproduce this error: https://github.com/gssbzn/flipper-test

From what I could find there's an Incompatibility between Rack::Session and ActionDispatch::Request::Session that could be the cause of this error. Thanks in advance.

Add Flipper::API module

Now that we have Flipper::UI it would be nice to have Flipper::API. Flipper::API would provide the same functionality as the UI but all json responses instead of html/css views. Clients could then be created for chatops/hubot to enable/disable features and all that.

Would like support for custom/dynamic groups

It would be nice to have a way to define a custom group type that corresponds to, e.g., a GitHub team. Something like this:

class GitHubTeam
  def flipper_id
    "GitHubTeam:#{id}"
  end

  def self.find_by_flipper_id(flipper_id)
    find flipper_id.split(":")[1]
  end
end

Flipper.register_custom_group(:github_team) do |group_id, actor|
  GitHubTeam.find_by_flipper_id(group_id).include? actor
end

$flipper[:my_feature].enable $flipper.custom_group(:github_team, GitHubTeam.find("rails/owners"))
$flipper[:my_feature].enabled? some_user #=> true because some_user is a member of @rails/owners

Basically the only difference from normal groups is that the group's ID is passed to the block as well as the actor, so we can look up the group (team) at runtime.

/cc @jnunemaker @rsanheim

Question: How do percentages work with non-sequential IDs?

If you are using MySQL with application replication, you'll often have something like this in your my.cnf:

auto-increment-increment = 10
auto-increment-offset    = 3 # this is different on each database

which means that you won't have an even distribution of users being enabled if you say id % 100 < percentage.

Allow redis keys to be namespaces

I would like to add the ability to namespace keys in redis so that we can reduce the number of collisions in redis. I'm going to start working on this and hopefully make a PR for it once I'm done. Just wanted to open the issue and get some feedback on this.

Mongoid support

Thank you for creating such a valuable gem. I see that there is a support for Mongo generic Ruby driver. Is there a way to make Flipper work with Mongoid? Especially 4x version since it still uses the previous Moped driver vs Mongoid 5 (which uses Mongo Ruby driver)

Add file adapter

Maybe based on yaml or something. Just for something that can be persisted across requests if using shotgun or something in dev, but without requiring an external adapter like flipper-redis or whatever. Ran into this while working on flipper-ui which now uses flipper-redis in dev mode.

Create a zookeeper adapter

It's HA and gives you an easy ability to watch for changes to update your in-memory cache of the current state of things.

Some feature names collide with Action routes

I ran into an issue implementing my first feature, refactor-images. The application would crash with a bizarre error: Flipper::UI::RequestMethodNotSupported - Flipper::UI::Actions::File does not support request method "post"
This happened when I tried to add a specific actor using the UI.

This made me think that I had broken it somehow, or otherwise set it up incorrectly, but my only mistake was treading upon A Regular Expression

I discovered my error by the expedient combo of debugging, stepping, and print @action_collection.method(:action_for_request).source. But of course I think more common consumers of the gem may be completely confused and give up or post an issue.

Shouldn't you specify the route match more strictly, rather than matching any part of the full path?

route %r{\a#{base_path}(images|css|js|octicons|fonts)/.*\Z}

Else, no features containing these five words will be permitted.

`Flipper.register` should match any truthy value, not just `true`

We recently wrote something like the following, hoping to set a generic group for flipper usage.

Flipper.register(:reviewers) do |actor|
  actor.try(:email) =~ /@example.com\z/
end

It didn't work, because String#=~ returns an integer (the position of the match in the string) rather than true/false. Flipper groups only respond if the register block explicitly returns true.

This seems a bit unrubyish, so I wondered if there was a specific reason that only true was allowed rather than (IMO) the more usual convention of any truthy value?

Ax description

It is kind of terrible. Instead, inspect could use enabled gates or include the gate values. This also makes description available as an actual field that users fill out for each feature.

How can I have multiple classes of actors?

Some features should be enabled on a per-account basis, others on a per-user basis (where an account has multiple users).

How can I query and enable certain features by account and others by user?

Meta description attribute for features

We hardcode defaults for all features in the app in a config file to act as documentation for all existing toggles. And while the name is usually descriptive ('new_dashboard'), it's usually short and sweet. It'd be useful to have a meta attribute for a feature to describe the feature in more detail (intended to help other devs / non-technical admins fully understand a feature).

And while it's definitely possible to put this meta description in a readme, readme's aren't easily accessible to non-technical admin vs a dashboard like Flipper UI.

Example:

$flipper[:new_dashboard].meta = 'The new user dashboard layout including the a redesigned profile section.'

active_record adapter migration file not included in the gem (gemspec file issue)

I think the gem.file matcher for flipper-active_record is leaving out a necessary migration template file.
I propose adding it as a special-case, and cutting a new minor gem version.

Details

On Rails 4.2.4 I'm seeing this error

$ bundle exec rails g flipper:active_record
Could not find "migration.rb" in any of your source paths. 
Please invoke Flipper::Generators::ActiveRecordGenerator.source_root(PATH) with the PATH containing your templates. 
Your current source paths are: 
/Users/nruth/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/flipper-active_record-0.7.3/lib/generators/flipper/templates

bundle open flipper-active_record showed me that the file really is missing; there's no template directory at all.

Cloning the repo and trying out the gem.file matcher in irb I found it's including these files, which have active_record in their name (as per the regex in the gemspec)

docs/active_record/README.md
examples/active_record/basic.rb
examples/active_record/internals.rb
flipper-active_record.gemspec
lib/flipper-active_record.rb
lib/flipper/adapters/active_record.rb
lib/generators/flipper/active_record_generator.rb
spec/flipper/adapters/active_record_spec.rb
test/generators/flipper/active_record_generator_test.rb
lib/flipper/version.rb

but not the lib/generators/flipper/templates/migration.rb

I guess the simplest fix is to add it in the same way as lib/flipper/version.rb?

Code

gem file matcher: https://github.com/jnunemaker/flipper/blob/master/flipper-active_record.gemspec#L16
gem file regex: https://github.com/jnunemaker/flipper/blob/master/flipper-active_record.gemspec#L5

Overriding templates for flipper-ui

Hi @jnunemaker πŸ‘‹

We're currently adding flipper to Classroom for GitHub education/classroom#474, and I was trying to figure out how to override the flipper-ui views.

Is there a way to do this?

My attempt at doing it myself was rather unsuccessful.

I tried to override adding to the Rails lib directory /lib/flipper-ui/flipper/lib/views/layout.erb, but I was unsuccessful.

Any help would be much appreciated!

defined method `name' for module `Kernel'

When I go to /flipper in flipper-ui I get this stack trace:

NameError - undefined method name' for moduleKernel':
/Users/kcoleman/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/delegate.rb:89:in method_missing' flipper (0.7.0.beta6) lib/flipper/adapters/instrumented.rb:32:infeatures'
/Users/kcoleman/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/delegate.rb:87:in method_missing' flipper (0.7.0.beta6) lib/flipper/adapters/memoizable.rb:25:inblock in features'
flipper (0.7.0.beta6) lib/flipper/adapters/memoizable.rb:24:in features' flipper (0.7.0.beta6) lib/flipper/dsl.rb:230:infeatures'
flipper-ui (0.7.0.beta6) lib/flipper/ui/actions/features.rb:14:in get' flipper-ui (0.7.0.beta6) lib/flipper/ui/action.rb:67:inblock in run'
flipper-ui (0.7.0.beta6) lib/flipper/ui/action.rb:67:in run' flipper-ui (0.7.0.beta6) lib/flipper/ui/action.rb:27:inrun'
flipper-ui (0.7.0.beta6) lib/flipper/ui/middleware.rb:70:in call!' flipper-ui (0.7.0.beta6) lib/flipper/ui/middleware.rb:60:incall'
flipper (0.7.0.beta6) lib/flipper/middleware/memoizer.rb:40:in call' rack (1.6.0) lib/rack/methodoverride.rb:22:incall'
rack-protection (1.5.3) lib/rack/protection/base.rb:49:in call' rack-protection (1.5.3) lib/rack/protection/xss_header.rb:18:incall'
rack-protection (1.5.3) lib/rack/protection/base.rb:49:in call' rack-protection (1.5.3) lib/rack/protection/base.rb:49:incall'
rack-protection (1.5.3) lib/rack/protection/path_traversal.rb:16:in call' rack-protection (1.5.3) lib/rack/protection/json_csrf.rb:18:incall'
rack-protection (1.5.3) lib/rack/protection/base.rb:49:in call' rack-protection (1.5.3) lib/rack/protection/base.rb:49:incall'
rack-protection (1.5.3) lib/rack/protection/frame_options.rb:31:in call' rack (1.6.0) lib/rack/builder.rb:153:incall'
actionpack (4.2.0) lib/action_dispatch/routing/mapper.rb:51:in serve' actionpack (4.2.0) lib/action_dispatch/journey/router.rb:43:inblock in serve'
actionpack (4.2.0) lib/action_dispatch/journey/router.rb:30:in serve' actionpack (4.2.0) lib/action_dispatch/routing/route_set.rb:802:incall'
warden (1.2.3) lib/warden/manager.rb:35:in block in call' warden (1.2.3) lib/warden/manager.rb:34:incall'
rack (1.6.0) lib/rack/etag.rb:24:in call' rack (1.6.0) lib/rack/conditionalget.rb:25:incall'
rack (1.6.0) lib/rack/head.rb:13:in call' actionpack (4.2.0) lib/action_dispatch/middleware/params_parser.rb:27:incall'
actionpack (4.2.0) lib/action_dispatch/middleware/flash.rb:260:in call' rack (1.6.0) lib/rack/session/abstract/id.rb:225:incontext'
rack (1.6.0) lib/rack/session/abstract/id.rb:220:in call' actionpack (4.2.0) lib/action_dispatch/middleware/cookies.rb:560:incall'
activerecord (4.2.0) lib/active_record/query_cache.rb:36:in call' activerecord (4.2.0) lib/active_record/connection_adapters/abstract/connection_pool.rb:647:incall'
activerecord (4.2.0) lib/active_record/migration.rb:378:in call' actionpack (4.2.0) lib/action_dispatch/middleware/callbacks.rb:29:inblock in call'
activesupport (4.2.0) lib/active_support/callbacks.rb:88:in _run_callbacks' activesupport (4.2.0) lib/active_support/callbacks.rb:734:in_run_call_callbacks'
activesupport (4.2.0) lib/active_support/callbacks.rb:81:in run_callbacks' actionpack (4.2.0) lib/action_dispatch/middleware/callbacks.rb:27:incall'
actionpack (4.2.0) lib/action_dispatch/middleware/reloader.rb:73:in call' actionpack (4.2.0) lib/action_dispatch/middleware/remote_ip.rb:78:incall'
better_errors (2.1.1) lib/better_errors/middleware.rb:84:in protected_app_call' better_errors (2.1.1) lib/better_errors/middleware.rb:79:inbetter_errors_call'
better_errors (2.1.1) lib/better_errors/middleware.rb:57:in call' web-console (2.0.0) lib/action_dispatch/debug_exceptions.rb:18:inmiddleware_call'
web-console (2.0.0) lib/action_dispatch/debug_exceptions.rb:13:in call' actionpack (4.2.0) lib/action_dispatch/middleware/show_exceptions.rb:30:incall'
railties (4.2.0) lib/rails/rack/logger.rb:38:in call_app' railties (4.2.0) lib/rails/rack/logger.rb:20:inblock in call'
activesupport (4.2.0) lib/active_support/tagged_logging.rb:68:in block in tagged' activesupport (4.2.0) lib/active_support/tagged_logging.rb:26:intagged'
activesupport (4.2.0) lib/active_support/tagged_logging.rb:68:in tagged' railties (4.2.0) lib/rails/rack/logger.rb:20:incall'
quiet_assets (1.1.0) lib/quiet_assets.rb:27:in call_with_quiet_assets' actionpack (4.2.0) lib/action_dispatch/middleware/request_id.rb:21:incall'
rack (1.6.0) lib/rack/methodoverride.rb:22:in call' rack (1.6.0) lib/rack/runtime.rb:18:incall'
activesupport (4.2.0) lib/active_support/cache/strategy/local_cache_middleware.rb:28:in call' rack (1.6.0) lib/rack/lock.rb:17:incall'
actionpack (4.2.0) lib/action_dispatch/middleware/static.rb:113:in call' rack (1.6.0) lib/rack/sendfile.rb:113:incall'
sentry-raven (0.12.3) lib/raven/integrations/rack.rb:61:in call' rack-cors (0.3.1) lib/rack/cors.rb:72:incall'
railties (4.2.0) lib/rails/engine.rb:518:in call' railties (4.2.0) lib/rails/application.rb:164:incall'
rack (1.6.0) lib/rack/lock.rb:17:in call' rack (1.6.0) lib/rack/content_length.rb:15:incall'
rack (1.6.0) lib/rack/handler/webrick.rb:89:in service' /Users/kcoleman/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/webrick/httpserver.rb:138:inservice'
/Users/kcoleman/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/webrick/httpserver.rb:94:in run' /Users/kcoleman/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/webrick/server.rb:294:inblock in start_thread'

Create group through UI

Hi,

I've been trying to create a group from the UI but it seems that's not possible yet.
Can you confirm that?

I'm thinking about add a feature on the UI but I'm not sure if it's the best approach according to your original idea of the project.

What do you think of add a separate button aside "Add Feature", redirecting to a new group creation page? Then I'll be able to see it on the group session. Now the API seems to be the only way. Image below:

Option 1

Another option is add a similar to the actors form but, since a group doesn't belong to a specific feature, I don't know if it is a good idea.

Option 2

What are your thoughts?

Thanks!!

Percentage of users per group

Does percentage of users handle groups properly (and vice versa)?

Let's say I have two groups of users: East Coast, West Coast

Is there any way to deploy to, say, 10% of users in East Coast group? Or, more generally, to 10% of users per enabled group?

Make this UI work

@orderedlist has been working on a UI. He has it mostly done. I need to start wiring it into the gem as a middleware or something and then make the adapters provide access to the data in a way that works for the UI.

flipper web

Getting enabled features based on user

Is there anyway I could do something like this?:

flipper.features(current_user)
# => [:stats, :analytic]

or

current_user.enabled_features
# => [:stats, :analytic]

Is it even possible for the GEM to have such feature?

Disable a boolean gate via the UI would auto-remove it

It's cause: ec711815 -- the feature, boolean gate, is gone once I press Disable and then leave the page. I would have to re-add the feature although I simply want to disable it for awhile for experiment.

What was the reason to introduce that commit? Would it be reverted?

Logging

Would be nice to be able to log feature checks (which features were checked and what did they return) and adapter reads (to verify efficiency).

More methods to control access to UI

I'm trying to figure out if there is a good way to control access to the UI other than with a routes constraint. My main concern with a routes constraint is that if you don't use devise to get your current_user then you would have to unpack a cookie and check the db in your routing which violates a number of best practices.

Would it be possible to put some sort of callback into the UI which would hand control over to the rails app and let it determine if the user has permissions to access the feature flipper? It would be helpful to have this after the application controller has loaded because that way it would be easier to grab the current user in most rails apps.

Limit the size of a set at some level

The individual actor and group gates are not made to store hundreds or thousands of values. We should limit the size these sets can grow to in an effort to help prevent people from hurting themselves. This check could happen at enable time to prevent enabling more than 1k or some number of individual elements in a set.

Distinguish production environment from others

One useful trick we use in production is to have a banner, or some sort of distinguishing color/banner/icon/popup that indicates that you are making changes to production. What do you all think about adding something similar to Flipper? It's definitely helpful to prevent mistakes from happening.

Shortcuts

No one wants to type out all this gibberish. Need to make it more succinct for creating a flipper instance and getting access to features, groups, etc.

Ruby 1.8.x support

Currently the Actor#id stuff fails on 1.8.

Failures:

  1) Flipper::DSL#actor for a number returns actor instance with identifer set to number
     Failure/Error: actor.identifier.should eq(33)

       expected: 33
            got: 67

       (compared using ==)
     # ./spec/flipper/dsl_spec.rb:151

  2) Flipper::Types::Actor converts identifier to integer
     Failure/Error: actor.identifier.should eq(2)

       expected: 2
            got: 2156693720

       (compared using ==)
     # ./spec/flipper/types/actor_spec.rb:90

  3) Flipper::Types::Actor has identifier
     Failure/Error: actor.identifier.should eq(2)

       expected: 2
            got: 5

       (compared using ==)
     # ./spec/flipper/types/actor_spec.rb:95

  4) Flipper::Types::Actor.wrappable? returns false if not actor and does not respond to identifier, id, nor to_i
     Failure/Error: described_class.wrappable?(:nope).should be_false
       expected: false value
            got: true
     # ./spec/flipper/types/actor_spec.rb:44

I'm thinking I'll just drop support for #id and force using identifier or something like that more specific to flipper.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.