simple_params 1.1.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +8 -8
- data/Gemfile.lock +1 -1
- data/lib/simple_params/api_pie_doc/nested_attribute.rb +8 -1
- data/lib/simple_params/api_pie_doc.rb +7 -22
- data/lib/simple_params/attribute.rb +2 -6
- data/lib/simple_params/concerns/date_time_helpers.rb +68 -0
- data/lib/simple_params/concerns/has_attributes.rb +68 -0
- data/lib/simple_params/concerns/has_typed_params.rb +30 -0
- data/lib/simple_params/concerns/hash_helpers.rb +27 -0
- data/lib/simple_params/concerns/rails_helpers.rb +33 -0
- data/lib/simple_params/concerns/strict_params.rb +68 -0
- data/lib/simple_params/concerns/validations.rb +41 -0
- data/lib/simple_params/errors.rb +57 -49
- data/lib/simple_params/nested_params.rb +73 -0
- data/lib/simple_params/params.rb +59 -277
- data/lib/simple_params/validation_matchers/nested_array_matcher.rb +2 -1
- data/lib/simple_params/validation_matchers/nested_parameter_matcher.rb +2 -1
- data/lib/simple_params/version.rb +1 -1
- data/lib/simple_params.rb +8 -2
- data/spec/api_pie_doc_spec.rb +7 -20
- data/spec/errors_spec.rb +33 -10
- data/spec/nested_params_spec.rb +193 -0
- data/spec/params_spec.rb +15 -0
- data/spec/rails_integration_spec.rb +70 -0
- metadata +14 -6
- data/lib/simple_params/api_pie_doc/nested_array.rb +0 -31
- data/lib/simple_params/validations.rb +0 -41
- data/spec/api_pie_doc/nested_array_spec.rb +0 -46
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NjYyNWJhNWU2ZmNkMGMxZWFjMmEzMjM3OWQyNWY0ZWZhNzY2N2ZhNw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NzllYWUzNDJjMDRkNTM5YzIwODMyYWRiYzhmOTU4M2NkNTczNGE0NA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZTNkMzU1YjhkMjdjNzg0MWFkZDhmNTUzYmU3OGVhZmNlOTBiYjM0NWRiMjRl
|
10
|
+
YWRiMTkzNTBiNjY3MDBiODAzOGUxYzk0MDY0YzJiNTUwNGRlNThmMTM0ODg0
|
11
|
+
MDc5MGU5MTgxNjQzMWY4ZjQ0MTc2OWQ5MTI3ZjUyNmVjM2ZiZTk=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
N2ExNzhkZjk0MmM4YTM4ZWQ2YjYwZGVjZTJlMDBiNjVmMGVkYzE1NWVlMDA0
|
14
|
+
Y2IwMWFjNjAyMjE1MWE2MDk5ZDY4NWNlODAxMDEyOGNiZjgxMTJlYzg4ZWZk
|
15
|
+
ZTE5N2E2ZTFhZTcxZDY0OWJiZjViNzZiOGE0NTNjMTZmNWZlNzY=
|
data/Gemfile.lock
CHANGED
@@ -19,9 +19,16 @@ module SimpleParams
|
|
19
19
|
end
|
20
20
|
|
21
21
|
private
|
22
|
+
def nested_type
|
23
|
+
if options[:type].present? && options[:type].to_sym == :array
|
24
|
+
'Array'
|
25
|
+
else
|
26
|
+
'Hash'
|
27
|
+
end
|
28
|
+
end
|
22
29
|
|
23
30
|
def nested_description
|
24
|
-
start = "param :#{name},
|
31
|
+
start = "param :#{name}, #{nested_type}, #{description}, #{requirement_description} do"
|
25
32
|
attribute_descriptors = []
|
26
33
|
attributes.each { |attribute| attribute_descriptors << attribute.to_s }
|
27
34
|
finish = "end"
|
@@ -2,22 +2,17 @@ module SimpleParams
|
|
2
2
|
class ApiPieDoc
|
3
3
|
|
4
4
|
attr_accessor :base_attributes,
|
5
|
-
:
|
6
|
-
:nested_arrays,
|
5
|
+
:nested_classes,
|
7
6
|
:nested_attributes,
|
8
|
-
:nested_array_attributes,
|
9
7
|
:docs
|
10
8
|
|
11
9
|
def initialize(simple_params)
|
12
10
|
self.base_attributes = simple_params.defined_attributes
|
13
|
-
self.
|
14
|
-
self.nested_arrays = simple_params.nested_arrays
|
11
|
+
self.nested_classes = simple_params.nested_classes
|
15
12
|
self.nested_attributes = []
|
16
|
-
self.nested_array_attributes = []
|
17
13
|
self.docs = []
|
18
14
|
|
19
|
-
|
20
|
-
build_nested_array_attributes
|
15
|
+
build_nested_classes
|
21
16
|
end
|
22
17
|
|
23
18
|
def build
|
@@ -25,12 +20,8 @@ module SimpleParams
|
|
25
20
|
docs << Attribute.new(attribute).to_s
|
26
21
|
end
|
27
22
|
|
28
|
-
nested_attributes.each do |
|
29
|
-
docs << NestedAttribute.new(
|
30
|
-
end
|
31
|
-
|
32
|
-
nested_array_attributes.each do |nested_attribute|
|
33
|
-
docs << NestedArray.new(nested_attribute).to_s
|
23
|
+
nested_attributes.each do |nested_class|
|
24
|
+
docs << NestedAttribute.new(nested_class).to_s
|
34
25
|
end
|
35
26
|
|
36
27
|
docs.join("\n")
|
@@ -38,16 +29,10 @@ module SimpleParams
|
|
38
29
|
|
39
30
|
private
|
40
31
|
|
41
|
-
def
|
42
|
-
|
32
|
+
def build_nested_classes
|
33
|
+
nested_classes.each do |name, parameter_set|
|
43
34
|
nested_attributes << { name => parameter_set.defined_attributes, options: parameter_set.options }
|
44
35
|
end
|
45
36
|
end
|
46
|
-
|
47
|
-
def build_nested_array_attributes
|
48
|
-
nested_arrays.each do |name, parameter_set|
|
49
|
-
nested_array_attributes << { name => parameter_set.defined_attributes, options: parameter_set.options }
|
50
|
-
end
|
51
|
-
end
|
52
37
|
end
|
53
38
|
end
|
@@ -3,12 +3,8 @@ require "virtus"
|
|
3
3
|
|
4
4
|
module SimpleParams
|
5
5
|
class Attribute
|
6
|
-
attr_reader :parent
|
7
|
-
|
8
|
-
attr_reader :type
|
9
|
-
attr_reader :default
|
10
|
-
attr_reader :validations
|
11
|
-
attr_reader :formatter
|
6
|
+
attr_reader :parent, :name, :type, :default,
|
7
|
+
:validations, :formatter
|
12
8
|
|
13
9
|
def initialize(parent, name, opts={})
|
14
10
|
@parent = parent
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module SimpleParams
|
2
|
+
module DateTimeHelpers
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def define_date_helper_methods(name)
|
10
|
+
define_method("#{name}(3i)=") do |day|
|
11
|
+
attribute = send("#{name}_attribute")
|
12
|
+
value = attribute.send("value") || Date.today
|
13
|
+
attribute.send("value=", Date.new(value.year, value.month, day.to_i))
|
14
|
+
end
|
15
|
+
|
16
|
+
define_method("#{name}(2i)=") do |month|
|
17
|
+
attribute = send("#{name}_attribute")
|
18
|
+
value = attribute.send("value") || Date.today
|
19
|
+
attribute.send("value=", Date.new(value.year, month.to_i, value.day))
|
20
|
+
end
|
21
|
+
|
22
|
+
define_method("#{name}(1i)=") do |year|
|
23
|
+
attribute = send("#{name}_attribute")
|
24
|
+
value = attribute.send("value") || Date.today
|
25
|
+
attribute.send("value=", Date.new(year.to_i, value.month, value.day))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def define_datetime_helper_methods(name)
|
30
|
+
define_method("#{name}(6i)=") do |sec|
|
31
|
+
attribute = send("#{name}_attribute")
|
32
|
+
value = attribute.send("value") || Time.now.utc
|
33
|
+
attribute.send("value=", Time.new(value.year, value.month, value.day, value.hour, value.min, sec.to_i, value.utc_offset))
|
34
|
+
end
|
35
|
+
|
36
|
+
define_method("#{name}(5i)=") do |minute|
|
37
|
+
attribute = send("#{name}_attribute")
|
38
|
+
value = attribute.send("value") || Time.now.utc
|
39
|
+
attribute.send("value=", Time.new(value.year, value.month, value.day, value.hour, minute.to_i, value.sec, value.utc_offset))
|
40
|
+
end
|
41
|
+
|
42
|
+
define_method("#{name}(4i)=") do |hour|
|
43
|
+
attribute = send("#{name}_attribute")
|
44
|
+
value = attribute.send("value") || Time.now.utc
|
45
|
+
attribute.send("value=", Time.new(value.year, value.month, value.day, hour.to_i, value.min, value.sec, value.utc_offset))
|
46
|
+
end
|
47
|
+
|
48
|
+
define_method("#{name}(3i)=") do |day|
|
49
|
+
attribute = send("#{name}_attribute")
|
50
|
+
value = attribute.send("value") || Time.now.utc
|
51
|
+
attribute.send("value=", Time.new(value.year, value.month, day.to_i, value.hour, value.min, value.sec, value.utc_offset))
|
52
|
+
end
|
53
|
+
|
54
|
+
define_method("#{name}(2i)=") do |month|
|
55
|
+
attribute = send("#{name}_attribute")
|
56
|
+
value = attribute.send("value") || Time.now.utc
|
57
|
+
attribute.send("value=", Time.new(value.year, month.to_i, value.day, value.hour, value.min, value.sec, value.utc_offset))
|
58
|
+
end
|
59
|
+
|
60
|
+
define_method("#{name}(1i)=") do |year|
|
61
|
+
attribute = send("#{name}_attribute")
|
62
|
+
value = attribute.send("value") || Time.now.utc
|
63
|
+
attribute.send("value=", Time.new(year.to_i, value.month, value.day, value.hour, value.min, value.sec, value.utc_offset))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module SimpleParams
|
2
|
+
module HasAttributes
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
included do
|
5
|
+
def define_attributes(params)
|
6
|
+
self.class.defined_attributes.each_pair do |key, opts|
|
7
|
+
send("#{key}_attribute=", Attribute.new(self, key, opts))
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def attributes
|
12
|
+
(defined_attributes.keys + nested_classes.keys).flatten
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def defined_attributes
|
18
|
+
@define_attributes ||= {}
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def define_attribute(name, opts = {})
|
23
|
+
opts[:type] ||= :string
|
24
|
+
defined_attributes[name.to_sym] = opts
|
25
|
+
attr_accessor "#{name}_attribute"
|
26
|
+
|
27
|
+
define_method("#{name}") do
|
28
|
+
attribute = send("#{name}_attribute")
|
29
|
+
attribute.send("value")
|
30
|
+
end
|
31
|
+
|
32
|
+
define_method("raw_#{name}") do
|
33
|
+
attribute = send("#{name}_attribute")
|
34
|
+
attribute.send("raw_value")
|
35
|
+
end
|
36
|
+
|
37
|
+
define_method("#{name}=") do |val|
|
38
|
+
attribute = send("#{name}_attribute")
|
39
|
+
attribute.send("value=", val)
|
40
|
+
end
|
41
|
+
|
42
|
+
if [Date, 'Date', :date].include?(opts[:type])
|
43
|
+
define_date_helper_methods(name)
|
44
|
+
elsif [DateTime, 'DateTime', :datetime, Time, 'Time', :time].include?(opts[:type])
|
45
|
+
define_datetime_helper_methods(name)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_validations(name, opts = {})
|
50
|
+
validations = opts[:validations] || {}
|
51
|
+
has_default = opts.has_key?(:default) # checking has_key? because :default may be nil
|
52
|
+
optional = opts[:optional]
|
53
|
+
if !validations.empty?
|
54
|
+
if optional || has_default
|
55
|
+
validations.merge!(allow_nil: true)
|
56
|
+
else
|
57
|
+
validations.merge!(presence: true)
|
58
|
+
end
|
59
|
+
else
|
60
|
+
if !optional && !has_default
|
61
|
+
validations.merge!(presence: true)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
validates name, validations unless validations.empty?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module SimpleParams
|
2
|
+
module HasTypedParams
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
TYPES = [
|
6
|
+
:integer,
|
7
|
+
:string,
|
8
|
+
:decimal,
|
9
|
+
:datetime,
|
10
|
+
:date,
|
11
|
+
:time,
|
12
|
+
:float,
|
13
|
+
:boolean,
|
14
|
+
:array,
|
15
|
+
:hash,
|
16
|
+
:object
|
17
|
+
]
|
18
|
+
|
19
|
+
included do
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
TYPES.each do |sym|
|
24
|
+
define_method("#{sym}_param") do |name, opts={}|
|
25
|
+
param(name, opts.merge(type: sym))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module SimpleParams
|
2
|
+
module HashHelpers
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
|
7
|
+
private
|
8
|
+
def hash_to_symbolized_hash(hash)
|
9
|
+
hash.inject({}){|result, (key, value)|
|
10
|
+
new_key = case key
|
11
|
+
when String then key.to_sym
|
12
|
+
else key
|
13
|
+
end
|
14
|
+
new_value = case value
|
15
|
+
when Hash then hash_to_symbolized_hash(value)
|
16
|
+
else value
|
17
|
+
end
|
18
|
+
result[new_key] = new_value
|
19
|
+
result
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'active_support/core_ext/string'
|
2
|
+
|
3
|
+
module SimpleParams
|
4
|
+
module RailsHelpers
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
included do
|
7
|
+
# Required for ActiveModel
|
8
|
+
def persisted?; false end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def define_rails_helpers(name, klass)
|
13
|
+
# E.g. if we have a nested_class named :phones, then we need:
|
14
|
+
# - a method called :phones_attributes that also sets :phones
|
15
|
+
# - a method called :build_phone
|
16
|
+
|
17
|
+
define_method("#{name}_attributes=") do |value|
|
18
|
+
send("#{name}=", value)
|
19
|
+
end
|
20
|
+
|
21
|
+
singular_key = singularized_key(name)
|
22
|
+
define_method("build_#{singular_key}") do |value={}|
|
23
|
+
klass.new(value, self)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def singularized_key(key)
|
29
|
+
key.to_s.singularize
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module SimpleParams
|
2
|
+
module StrictParams
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
def set_strictness
|
7
|
+
if self.class.strict_enforcement.nil?
|
8
|
+
self.class.strict_enforcement = true
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Overriding this method to allow for non-strict enforcement!
|
13
|
+
def method_missing(method_name, *arguments, &block)
|
14
|
+
if strict_enforcement?
|
15
|
+
raise SimpleParamsError, "parameter #{method_name} is not defined."
|
16
|
+
else
|
17
|
+
if @original_params.include?(method_name.to_sym)
|
18
|
+
value = @original_params[method_name.to_sym]
|
19
|
+
if value.is_a?(Hash)
|
20
|
+
define_anonymous_class(method_name, value)
|
21
|
+
else
|
22
|
+
Attribute.new(self, method_name).value = value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def respond_to?(method_name, include_private = false)
|
29
|
+
if strict_enforcement?
|
30
|
+
super
|
31
|
+
else
|
32
|
+
@original_params.include?(method_name.to_sym) || super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def define_anonymous_class(name, hash)
|
37
|
+
klass_name = name.to_s.split('_').collect(&:capitalize).join
|
38
|
+
anonymous_klass = Class.new(Params).tap do |klass|
|
39
|
+
if self.class.const_defined?(klass_name)
|
40
|
+
begin
|
41
|
+
self.class.send(:remove_const, klass_name)
|
42
|
+
rescue NameError
|
43
|
+
end
|
44
|
+
end
|
45
|
+
self.class.const_set(klass_name, klass)
|
46
|
+
end
|
47
|
+
anonymous_klass.allow_undefined_params
|
48
|
+
anonymous_klass.new(hash)
|
49
|
+
end
|
50
|
+
|
51
|
+
def strict_enforcement?
|
52
|
+
self.class.strict_enforcement
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module ClassMethods
|
57
|
+
attr_accessor :strict_enforcement
|
58
|
+
|
59
|
+
def strict
|
60
|
+
@strict_enforcement = true
|
61
|
+
end
|
62
|
+
|
63
|
+
def allow_undefined_params
|
64
|
+
@strict_enforcement = false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "active_model"
|
2
|
+
|
3
|
+
module SimpleParams
|
4
|
+
module Validations
|
5
|
+
extend ActiveModel::Validations
|
6
|
+
|
7
|
+
def valid?(context = nil)
|
8
|
+
current_context, self.validation_context = validation_context, context
|
9
|
+
errors.clear
|
10
|
+
run_validations!
|
11
|
+
|
12
|
+
# Make sure that nested classes are also valid
|
13
|
+
nested_classes_valid? && errors.empty?
|
14
|
+
ensure
|
15
|
+
self.validation_context = current_context
|
16
|
+
end
|
17
|
+
|
18
|
+
def validate!
|
19
|
+
unless valid?
|
20
|
+
raise SimpleParamsError, self.errors.to_hash.to_s
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def nested_classes_valid?
|
26
|
+
nested_classes.map do |key, array|
|
27
|
+
nested_class_valid?(send("#{key}") )
|
28
|
+
end.all?
|
29
|
+
end
|
30
|
+
|
31
|
+
def nested_class_valid?(nested_class)
|
32
|
+
if nested_class.is_a?(Array)
|
33
|
+
# Have to map? & THEN all?, or else it won't
|
34
|
+
# necessarily call valid? on every object
|
35
|
+
nested_class.map(&:valid?).all?
|
36
|
+
else
|
37
|
+
nested_class.valid?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|