Skip to content

Commit

Permalink
Add authorization to store
Browse files Browse the repository at this point in the history
  • Loading branch information
bonyiii committed Mar 28, 2018
1 parent f94a4ee commit 27168cf
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 18 deletions.
11 changes: 9 additions & 2 deletions lib/documentary/params.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module Params
before_action :describe_params

def self.params(action = nil, **_args, &block)
return @store unless action
return @store.authorized unless action

unless public_method_defined?(action)
raise(Documentary::PublicMethodMissing, "'#{self}' has no public instance method '#{action}' defined!")
Expand Down Expand Up @@ -35,6 +35,7 @@ def describe_params
end

class ParamBuilder
VALID_OPTIONS = [:if].freeze # :nodoc:
attr_reader :store

def self.build(&block)
Expand All @@ -55,12 +56,18 @@ def optional(param, **args, &block)

private

def build(param, required:, type: nil, desc: nil, &block)
def build(param, required:, type: nil, desc: nil, **args, &block)
args.each_key do |k|
unless VALID_OPTIONS.include?(k)
raise ArgumentError.new("Unknown key for Documentary param: #{k.inspect}. Valid keys are: #{VALID_OPTIONS.map(&:inspect).join(', ')}.")
end
end
store[param] = block ? self.class.build(&block) : Store.new

store[param][:required] = required
store[param][:type] = type.to_s if type
store[param][:desc] = desc if desc
store[param][:if] = args[:if] if args[:if]
end
end
end
21 changes: 20 additions & 1 deletion lib/documentary/store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,27 @@ def to_strong
recursive_each(self)
end

def authorized
dup.tap { |store| make_sure_top_level_keys_included(store.delete_unauthorized) }
end

protected

def delete_unauthorized
delete_if do |k, v|
next unless v.instance_of?(Store)
(v[:if] && !v[:if].call) || v.delete_unauthorized.empty?
end
end

private

def make_sure_top_level_keys_included(allowed_params)
each_key do |key|
allowed_params[key] = Store.new unless allowed_params[key]
end
end

def recursive_each(hash)
hash.map do |key, value|
if nested?(value)
Expand All @@ -25,7 +44,7 @@ def recursive_each(hash)
end

def nested?(value)
value.is_a?(Hash) && !(value.keys - %i[type desc required]).empty?
value.is_a?(Hash) && !(value.keys - %i[type desc required if]).empty?
end
end
end
91 changes: 81 additions & 10 deletions spec/documentary/params_spec.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
require 'spec_helper'
require 'action_controller'

# Test controller to emaulte a class in which
# Documentary::Params can be mixed in
class TestController < ActionController::Base
include Documentary::Params
def show; end
end

RSpec.describe Documentary::Params do
subject { TestController }
Expand All @@ -29,14 +21,15 @@ def show; end
# params :index do
# required :name
# end

before do
expect(Documentary::ParamBuilder).to receive(:build) do
{ type: 'Foo', vintage: 'Bar' }
end
expect(subject).to receive(:public_method_defined?).and_return(true)
end

after { TestController.instance_eval { @store.delete(:edit) } }

it 'should good to go' do
expect { subject.params(:edit) }.not_to raise_error
end
Expand All @@ -54,7 +47,6 @@ def show; end
# end
# def index
# end

before { expect(subject).to receive(:public_method_defined?).and_return(false) }

it 'should blow up' do
Expand Down Expand Up @@ -113,5 +105,84 @@ def show; end
it { expect(subject.params[:show][:vintage][:year][:required]).to eq(true) }
it { expect(subject.params[:show][:vintage][:day][:required]).to eq(false) }
end

context 'authorization' do
it 'should raise error for invalid option' do
expect do
subject.params :show do
optional(:type, not_defined: ->(controller) { controller.to_s })
end
end.to raise_error ArgumentError
end

context 'when only a single param which is not authorized present' do
let(:year_desc) { 'Year of the vintage' }

before do
subject.params :show do
optional(:type, if: -> { false })
end
end

it { expect(subject.params[:show]).not_to include(:type) }
it { expect(subject.params[:edit]).not_to be }
end

context 'when two method defined, one with a single
param which is not allowed for current user and
another with a param which is allowed for anyone' do
let(:year_desc) { 'Year of the vintage' }

before do
subject.params :show do
optional(:type, if: -> { false })
end

# Set up edit method here
expect(Documentary::ParamBuilder).to receive(:build) do
{ type: 'Foo', vintage: 'Bar' }
end
expect(subject).to receive(:public_method_defined?).and_return(true)
subject.params(:edit)
end

after { TestController.instance_eval { @store.delete(:edit) } }

it { expect(subject.params[:show]).not_to include(:type) }
it { expect(subject.params[:edit]).to include(:type) }
end

context 'without nested params' do
let(:year_desc) { 'Year of the vintage' }

before do
subject.params :show do
optional(:type, if: -> { false })
required :name
end
end

it { expect(subject.params[:show]).not_to include(:type) }
end

context 'with nested params' do
let(:year_desc) { 'Year of the vintage' }

before do
subject.params :show do
optional(:type)
required(:vintage, type: Array) do
required(:year, type: Integer, desc: 'Year of the vintage', if: -> { false })
optional(:month, type: Integer)
end
end
end

it { expect(subject.params[:show][:vintage]).not_to include(:year) }
it { expect(subject.params[:show][:vintage]).to include(:month) }
it { expect(subject.params[:show]).to include(:type) }
it { expect(subject.params[:edit]).not_to be }
end
end
end
end
6 changes: 1 addition & 5 deletions spec/documentary/store_spec.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
require 'spec_helper'

class TestController
extend Documentary::Params
def show; end
end

RSpec.describe Documentary::Store do
context 'when store object defined directly' do
describe '#to_strong' do
Expand Down Expand Up @@ -98,5 +93,6 @@ def show; end
}
end
end

end
end
8 changes: 8 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'bundler/setup'
require 'documentary'
require 'action_controller'

RSpec.configure do |config|
# Enable flags like --only-failures and --next-failure
Expand All @@ -12,3 +13,10 @@
c.syntax = :expect
end
end

# Test controller to emaulte a class in which
# Documentary::Params can be mixed in
class TestController < ActionController::Base
include Documentary::Params
def show; end
end

0 comments on commit 27168cf

Please sign in to comment.