Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically permit validated params #100

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion lib/rails_param/param.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
module RailsParam
attr_accessor :rails_params

def param!(name, type, options = {}, &block)
ParamEvaluator.new(params).param!(name, type, options, &block)
hierarchy = ParamEvaluator.new(params).param!(name, type, options, &block)

@rails_params =
if params.is_a?(ActionController::Parameters)
params.permit(hierarchy)
else
params
end
end
end
46 changes: 41 additions & 5 deletions lib/rails_param/param_evaluator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ module RailsParam
class ParamEvaluator
attr_accessor :params

def initialize(params, context = nil)
def initialize(params, context = nil, hierarchy = nil)
@params = params
@context = context
@hierarchy = hierarchy || {}
end

def param!(name, type, options = {}, &block)
name = name.is_a?(Integer)? name : name.to_s
@child_key = name = name.is_a?(Integer)? name : name.to_s
return unless params.include?(name) || check_param_presence?(options[:default]) || options[:required]

parameter_name = @context ? "#{@context}[#{name}]" : name
Expand All @@ -33,7 +34,11 @@ def param!(name, type, options = {}, &block)
)
end

recurse_on_parameter(parameter, &block) if block_given?
update_hierarchy(type, name)

if block_given?
@hierarchy[@child_key] = recurse_on_parameter(parameter, &block)
end

# apply transformation
parameter.transform if options[:transform]
Expand All @@ -43,10 +48,40 @@ def param!(name, type, options = {}, &block)

# set params value
params[name] = parameter.value

[@hierarchy, parameter.value]
end

private

def update_hierarchy(type, name)
# calculate next child type
@child =
if type == Array && !block_given?
[]
elsif type == Hash || type == Array
{}
else
name
end

if type == Hash || type == Array
# intermediate nodes
if @hierarchy.is_a?(Hash)
@hierarchy[name] = @child
@child_key = name
else
@hierarchy << @child
@child_key = @hierarchy.size - 1
end
else
# this is a leaf
@hierarchy = [] if @hierarchy.is_a?(Hash)
@hierarchy << name
@child_key = @hierarchy.size - 1
end
end

def recurse_on_parameter(parameter, &block)
return if parameter.value.nil?

Expand All @@ -55,7 +90,8 @@ def recurse_on_parameter(parameter, &block)
if element.is_a?(Hash) || element.is_a?(ActionController::Parameters)
recurse element, "#{parameter.name}[#{i}]", &block
else
parameter.value[i] = recurse({ i => element }, parameter.name, i, &block) # supply index as key unless value is hash
_, value = recurse({ i => element }, parameter.name, i, &block) # supply index as key unless value is hash
parameter.value[i] = value
end
end
else
Expand All @@ -66,7 +102,7 @@ def recurse_on_parameter(parameter, &block)
def recurse(element, context, index = nil)
raise InvalidParameterError, 'no block given' unless block_given?

yield(ParamEvaluator.new(element, context), index)
yield(ParamEvaluator.new(element, context, @hierarchy[@child_key]), index)
end

def check_param_presence? param
Expand Down
47 changes: 47 additions & 0 deletions spec/rails_param/param_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -804,5 +804,52 @@ def params;
end
end
end

describe "permitting" do
it 'permits all nested attributes' do
input_params = {
mimmo: {
'foo' => { 'bar' => BigDecimal(1), 'baz' => 2 },
'arr' => [1, 2, 3]
}
}
allow(controller).to receive(:params).and_return(ActionController::Parameters.new(input_params))
safe_params = controller.param! :mimmo, Hash do |mimmo|
mimmo.param! :foo, Hash do |p|
p.param! :bar, BigDecimal
p.param! :baz, Float
end
mimmo.param! :arr, Array
end

expect(safe_params).to be_permitted
expect(safe_params.to_h.with_indifferent_access).to eq({
'mimmo' => {
'foo' => { 'bar' => BigDecimal(1), 'baz' => 2 },
'arr' => [1, 2, 3]
}
})
end

it 'permits only specified attributes' do
input_params = {
mimmo: {
'foo' => { 'bar' => 1, 'baz' => 2 },
'arr' => [1, 2, 3]
}
}
allow(controller).to receive(:params).and_return(ActionController::Parameters.new(input_params))
safe_params = controller.param! :mimmo, Hash do |mimmo|
mimmo.param! :arr, Array
end

expect(safe_params).to be_permitted
expect(safe_params.to_h.with_indifferent_access).to eq({
'mimmo' => {
'arr' => [1, 2, 3]
}
})
end
end
end
end