Class: Kithe::Parameters

Inherits:
ActionController::Parameters
  • Object
show all
Defined in:
app/models/kithe/parameters.rb

Overview

Kithe::Parameters are a sub-class of Rails [ActionController::Parameters](api.rubyonrails.org/classes/ActionController/Parameters.html) (ie [StrongParameters](guides.rubyonrails.org/action_controller_overview.html#strong-parameters) ) with a couple additional features:

  1. White-list top-level keys containing any content with a ‘true’ value in the filter list.

  2. Automatically add attributes declared with ‘attr_json` to the whitelisted-for-anything values.

## Motivation

Complex nested data with attr_json can be used with ordinary Rails strong params just like rails associations. But attr_json data can get more complex than typical for Rails, and really unwieldy to use.

The strong param guide itself says “The strong parameter API was designed with the most common use cases in mind. It is not meant as a silver bullet to handle all of your whitelisting problems.”

Since attr_json keys are serialized in their entirety replacing whatever else was already there, we believe it is safe to use them with a heavy-handed ‘whitelist whatever scalar or nested values occur there’ approach.

## Examples

Wrap the ‘params` Rails gives you in a Kithe::Parameters, and use the ’true’ key to whitelist anything.

Kithe::Parameters.new(params).require(:model).permit(some_attr_json_value: true)

Will allow the ‘some_attr_json_value` key to have anything in it’s value: primitives, hashes, nested, arrays, etc. Instead of requiring you to describe it’s “shape” as usual with strong params.

Or auto-allow all declared attr_json keys from some model:

Kithe::Parameters.new(params).require(:model).permit_attr_json(MyModel).permit

Exclude some attr_json definitions:

Kithe::Parameters.new(params).require(:model).
  permit_attr_json(MyModel, except: ["not_this_one", "or_this_one"]).
  permit

Combine permit_attr_json with ordinary strong params permit filters:

Kithe::Parameters.new(params).require(:model).
  permit_attr_json(MyModel).
  permit(:title, :parent_id, :representative_id, :contained_by_ids => [])

## Alternatives

We believe this is safe and effective for our use cases. If you disagree or find it sketchy, you can use ordinary Rails strong params. Or you can use [reform](github.com/trailblazer/reform), which should work just fine with attr_json/kithe (let us know if you run into any trouble). Or anything else!

## Future?

Should we move this class to attr_json, is it generally useful for attr_json users?

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hash = {}) ⇒ Parameters

Returns a new instance of Parameters.



69
70
71
72
73
74
75
# File 'app/models/kithe/parameters.rb', line 69

def initialize(hash = {}, logging_context = {})
  if hash.respond_to?(:to_unsafe_h)
    hash = hash.to_unsafe_h
  end

  super(hash, logging_context)
end

Instance Attribute Details

#auto_allowed_keysObject (readonly)

Returns the value of attribute auto_allowed_keys.



64
65
66
# File 'app/models/kithe/parameters.rb', line 64

def auto_allowed_keys
  @auto_allowed_keys
end

Instance Method Details

#permit(*filters) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'app/models/kithe/parameters.rb', line 98

def permit(*filters)
  whitelisted = self.class.new

  hash_arg = permit_keyword_args(filters)

  hash_arg.keys.each do |key|
    if hash_arg[key] == true
      hash_arg.delete(key)
      if self.has_key?(key)
        whitelisted[key] = self[key]
        self.delete(key)
      end
    end
  end

  return super.merge(whitelisted.permit!)
end

#permit_attr_json(klass, except: nil) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
# File 'app/models/kithe/parameters.rb', line 86

def permit_attr_json(klass, except:nil)
  keys = klass.attr_json_registry.attribute_names.collect(&:to_sym)
  keys = keys - Array(except).collect(&:to_sym) if except
  keys = keys.flat_map do |k|
    [k, "#{k}_attributes".to_sym]
  end

  @auto_allowed_keys = keys

  return self
end