diff options
Diffstat (limited to 'actionpack/lib/action_controller/metal/strong_parameters.rb')
-rw-r--r-- | actionpack/lib/action_controller/metal/strong_parameters.rb | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb new file mode 100644 index 0000000000..b027901f28 --- /dev/null +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -0,0 +1,120 @@ +require 'active_support/concern' +require 'active_support/core_ext/hash/indifferent_access' +require 'active_support/rescuable' + +module ActionController + class ParameterMissing < IndexError + attr_reader :param + + def initialize(param) + @param = param + super("key not found: #{param}") + end + end + + class Parameters < ActiveSupport::HashWithIndifferentAccess + attr_accessor :permitted + alias :permitted? :permitted + + def initialize(attributes = nil) + super(attributes) + @permitted = false + end + + def permit! + @permitted = true + self + end + + def require(key) + self[key].presence || raise(ParameterMissing.new(key)) + end + + alias :required :require + + def permit(*filters) + params = self.class.new + + filters.each do |filter| + case filter + when Symbol, String then + params[filter] = self[filter] if has_key?(filter) + when Hash then + self.slice(*filter.keys).each do |key, value| + return unless value + + key = key.to_sym + + params[key] = each_element(value) do |value| + # filters are a Hash, so we expect value to be a Hash too + next if filter.is_a?(Hash) && !value.is_a?(Hash) + + value = self.class.new(value) if !value.respond_to?(:permit) + + value.permit(*Array.wrap(filter[key])) + end + end + end + end + + params.permit! + end + + def [](key) + convert_hashes_to_parameters(key, super) + end + + def fetch(key, *args) + convert_hashes_to_parameters(key, super) + rescue KeyError + raise ActionController::ParameterMissing.new(key) + end + + def slice(*keys) + self.class.new(super) + end + + def dup + super.tap do |duplicate| + duplicate.instance_variable_set :@permitted, @permitted + end + end + + private + def convert_hashes_to_parameters(key, value) + if value.is_a?(Parameters) || !value.is_a?(Hash) + value + else + # Convert to Parameters on first access + self[key] = self.class.new(value) + end + end + + def each_element(object) + if object.is_a?(Array) + object.map { |el| yield el }.compact + else + yield object + end + end + end + + module StrongParameters + extend ActiveSupport::Concern + include ActiveSupport::Rescuable + + included do + rescue_from(ActionController::ParameterMissing) do |parameter_missing_exception| + render text: "Required parameter missing: #{parameter_missing_exception.param}", status: :bad_request + end + end + + def params + @_params ||= Parameters.new(request.parameters) + end + + def params=(val) + @_params = val.is_a?(Hash) ? Parameters.new(val) : val + end + end +end |