First of all this is not an other authorization gem! The whole concept is about "Feature rolling" and "Feature flipping" which we can resume to "Feature enabling"!
The idea is to dynamically enable (and disable) application feature depending on the user status (ex: beta, standard) and the feature stage (ex: beta, production). Then the purpose is to make this process easy, dynamic and as much automatic as possible!
The Gem let you describe in a simple DSL a set of possible status for user, instance (group of users) and feature. Then you describe the relation (mapping) between each feature status and a set of user and instance status.
Finally you describe all your application feature and their respective release status.
At the end you get access to the (not so) magic access_to?(:feature_name) method that does all the hard work to tell you true or false!
In order to use this gem you need to understand those keywords:
- user: Define a user of your App
- instance: Define a group of users (optional)
- feature: Define a feature or your App
- action: Define a sub-feature of your App (optional)
- status: Stage of rollout of a feature
- locales: I18n locales on which a feature is available. (optional: by defaut will use I18n.available_locales)
The gem will give you the access status (true or false) of a feature by checking in this exact order:
- Is the feature available for the current locale (I18n.locale)
- If it does, check if the user has access to the feature (based on the relations)
- If not, check if the instance has access to the feature (also based on the relations)
## helper method
def access_to?(feature, *actions)
return false if !locale_access_to?(feature, *actions)
return true if DSL.roles.user.present? && user_access_to?(feature, *actions)
return true if DSL.roles.instance.present? && instance_access_to?(feature, *actions)
return false
end
Add to your Gemfile:
gem 'helioth', '~> 0.1.0'
-
Add an "helioth.rb" file inside your config/ folder.
-
Now it's time to use the DSL! First describe the different roles (user, instance and feature) and affect each of them a set of status:
roles do
user :beta, :standard
instance :beta, :standard, :critical
feature :disabled, :beta, :pre_release, :production
end
- Then describe the relations between feature status and user and instance status:
relations do
feature :disabled
feature :beta do
instance :beta
user :beta
end
feature :pre_release do
instance :beta, :standard
user :beta
end
feature :production do
instance :beta, :standard, :critical
user :beta, :standard
end
end
- Now describe your application features:
features do
feature :no_name do
status :disabled
end
feature :tutoring do
status :pre_release
actions :search, :send do ## this is optional
status :beta
locales :fr ## this is optional
end
actions :index do
status :production
end
end
feature :social_learning do
status :beta
locales :fr, :en ## this is optional
end
end
As you can see :actions and :locales are optional. Those give you more flexibility over the rollout process. You can find this complete DSL example inside the /examples directory.
- You need to link the roles user and instance to your corresponding model. In order to do that use the class method has_helioth_role in your corresponding models:
class MyUser < ActiveRecord::Base
...
has_helioth_role :user
...
end
class MyInstance < ActiveRecord::Base
...
has_helioth_role :instance
...
end
- By default the Gem will look for a column named "role". You can configure an other column by using the column: option with the has_helioth_role method:
class MyUser < ActiveRecord::Base
...
has_helioth_role :user, column: "my_role_column"
...
end
- In your controller and view you have access to the access_to? helper method:
access_to?(:feature_name)
#OR
access_to?(:feature_name, :action_name)
- For example you can use this method to change the behavior of your view:
if access_to?(:tutoring, :search)
link_to tutoring_path()
end
- There is also a DSL available to define at a controller level the access:
## Declare if an entire controller is accessible based on a specific feature
load_and_authorize_for :feature_name
## Declare if a controller method (:index) is accessible based on an action (:index) related to a feature (:tutoring)
load_and_authorize_for :tutoring, :action=>:index, :only => :index
## Declare if a controller method (:search) is accessible based on a multiple actions (:index, :search) related to a feature (:tutoring)
load_and_authorize_for :tutoring, :actions=>[:search, :index], :only => :search
## All before_filter keywords are available:
:only, :except, :if, :unless
- You can also access the main Helioth object by calling:
## Access DSL object
Helioth::DSL.method_name
## For ex. retrieved all features
Helioth::DSL.features
## For ex. retrieve info about a specific feature
Helioth::DSL.feature(:feature_name)
## Etc.. for more information check the lib/helioth/dsl.rb file
- ActiveRecord >= 3.0 and Ruby >= 2.0.0
- Internally this Gem rely on two helper methods that must be available in your app:
current_user
#AND
current_instance
Those helpers must return an instance of User and Instance class where your defined the has_helioth_role class method.
- Your User and Instance models need to inherit from ActiveRecord::Base
- Why using the keyword instance? At my company we develop a B2B SaaS App. Our customers are companies that buy licences for their employees (aka users). For each customers we let them manage and configure their own version of our App, which is called an Instance.
- Run the RSpec tests:
bundle exec rake
- Also you will you'll find a simple Rails app that live in the /test/dummy directory, start and play!
cd test/dummy && bundle install && rails s
Copyright © 2014 Guillaume Montard and Vodeclic SAS released under the MIT license