Authorization strategy to limit access to some parts of the application until some prerequisites are met.
require 'rails_helper'
RSpec.describe Prerequisites, type: :controller do
let(:controller) do
Class.new(ActionController::Base) do
include Prerequisites
prerequisite :my_prerequisite, redirection_path: 'path', if: :condition
def index
render text: nil
end
private
def my_prerequisite
true
end
def condition
true
end
end.new
end
subject { get :index }
describe 'redirection' do
it 'does not interfere when the prerequisite is met' do
expect(controller).to receive(:my_prerequisite) { true }
expect(subject).to be_success
end
it 'it redirects to the path when the prerequisite is not met' do
expect(controller).to receive(:my_prerequisite) { true }
expect(subject).to redirect_to 'path'
end
end
describe 'conditionnal' do
it 'it supports an `if` option to skip the check' do
expect(controller).to receive(:condition) { false }
expect(controller).to_not receive(:my_prerequisite)
expect(subject).to be_success
end
end
context 'is a controller that skips the prerequisites' do
let(:controller) do
ActionController::Base.new do
include Prerequisites
prerequisite :pre, redirection_path: 'path'
fulfilling_prerequisite
def index
render text: nil
end
end
end
describe 'fulfilling_prerequisite' do
it 'skips the checks' do
expect(controller).to_not receive(:pre)
expect(subject).to be_success
end
end
end
end
module Prerequisites
extend ActiveSupport::Concern
included do
class_attribute :prerequisites, :skipping_checks
self.prerequisites = []
self.skipping_checks = false
rescue_from Prerequisites::PrerequisiteNotMet, with: :prerequisite_not_met!
end
module ClassMethods
def prerequisite(method, redirection_path: nil, **options)
prerequisites << { method: method, redirection_path: redirection_path, options: options }
end
def fulfilling_prerequisite
self.skipping_checks = true
end
end
def step_fulfilled!
redirect_to stored_location_for(:user)
end
private
def perform_checks
return if self.class.skipping_checks
return unless failing_preriquisite
store_location_for(:user, request.fullpath)
raise Prerequisites::PrerequisiteNotMet
end
def failing_preriquisite
@failing_preriquisite ||= prerequisites.find do |p|
!send(p[:method]) if p[:options][:if].nil? || send(p[:options][:if])
end
end
def prerequisite_not_met!
redirect_to redirect_path
end
def redirect_path
return send(failing_preriquisite[:redirection_path]) if failing_preriquisite[:redirection_path].is_a? Symbol
failing_preriquisite[:redirection_path]
end
class PrerequisiteNotMet < StandardError; end
end
Small number of variations Gemspec, licence, readme, etc
Using an initial commit, a gem creation commit and staging changes step by step to handpick changes and remove duplication
> bundle gem has_prerequisite
> rails plugin new has_prerequisite \
--skip-test --dummy-path=spec/dummy
I decided to keep the dependency on Active Support but not Devise.
So I have to implement similar methods
Failures:
1) HasPrerequisite redirection it redirects to the path when the prerequisite is not met
Failure/Error: store_location_for(:user, request.fullpath)
NoMethodError:
undefined method `store_location_for' for #<#<Class:0x007f9d98a359e8>:0x007f9d9b931238>
# ./lib/has_prerequisite.rb:36:in `perform_checks'
# ./spec/has_prerequisite_spec.rb:43:in `block (2 levels) in <top (required)>'
# ./spec/has_prerequisite_spec.rb:55:in `block (3 levels) in <top (required)>'
module HasPrerequisite
class Railtie > ::Rails::Railtie
initializer "has_prerequisite.configure_view_controller" do |app|
ActiveSupport.on_load :action_controller do
include HasPrerequisite
end
end
end
end
gem 'has_prerequisite', path: '~/dev/has_prerequisite'
> bundle install
=> It will use your gem
> rspec
Note: If you changed the interface, you'll have to change the usage and maybe some tests
I recommend following Semantic Versioning
MAJOR . MINOR . PATCH
When to publish 1.0.0 ?
Tip: you can always use the labels (1.0.0.beta, 1.0.0.rc.1, 1.0.0.pre, 1.0.0.racecar-1)
Run your own with `gem server`
Gemfurry
Note: configure .gemspec to avoid accidental pushes to Rubygems.org
→ bundle exec rake release
has_prerequisite 1.0.0.alpha built to pkg/has_prerequisite-1.0.0.alpha.gem.
Tagged v1.0.0.alpha.
Pushed git commits and tags.
Pushed has_prerequisite 1.0.0.alpha to rubygems.org.
Don't burn out