parametric 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 723fbc2c52d12ad990405295ef4a0a857d0d6638
4
- data.tar.gz: 685e8e6b97ffebbf51e413e1780fcfd69bb626a4
2
+ SHA256:
3
+ metadata.gz: f5dfee33e2a3f175505899e0238390e2795e0a862469b851a3a550aef152e8be
4
+ data.tar.gz: c1225c196aeb662384da471dd31b62ea8e072a90049eecb609976dfa21eec1d6
5
5
  SHA512:
6
- metadata.gz: a5f63c830feb5c708b64be0d2de108949cd2b07a3157686f64b6535c77ca79fa157a24293a1983a17a69ce0c30cd24df6b482aad9a4d295e254ac2400771de00
7
- data.tar.gz: 3eb0a6f083e87303716931e6e55247e044b7d969af09322638758029adef56c6c1ecf66c79f148ae7b14b3879a590835af1f5af83e6e8475517b9b76205ca7eb
6
+ metadata.gz: 2645e19eda4c75f82f8620fa16cce48fad7b60a14d1749e78a3de05411b55f1daf2ae9e41149467ccd1ccecd613178972d8283ead6a8becdf0c35bae1d4a7123
7
+ data.tar.gz: 84ecd165ff1355b17f5596f7f7c47a1b2965a2a7c50e6211801400f019aa8d318847ebf68ecbbf2c6c75da6cd8f3bb257707f9e69a4d9b5500ddf82c52733ff2
data/README.md CHANGED
@@ -474,10 +474,10 @@ create_user_schema.structure[:friends].structure[:name].label # => "Friend full
474
474
  Note that many field policies add field meta data.
475
475
 
476
476
  ```ruby
477
- create_user_schema.schema[:name][:type] # => :string
478
- create_user_schema.schema[:name][:required] # => true
479
- create_user_schema.schema[:status][:options] # => ["published", "unpublished"]
480
- create_user_schema.schema[:status][:default] # => "published"
477
+ create_user_schema.structure[:name][:type] # => :string
478
+ create_user_schema.structure[:name][:required] # => true
479
+ create_user_schema.structure[:status][:options] # => ["published", "unpublished"]
480
+ create_user_schema.structure[:status][:default] # => "published"
481
481
  ```
482
482
 
483
483
  ## #walk
@@ -487,7 +487,7 @@ The `#walk` method can recursively walk a schema definition and extract meta dat
487
487
  ```ruby
488
488
  schema_documentation = create_user_schema.walk do |field|
489
489
  {type: field.meta_data[:type], label: field.meta_data[:label]}
490
- end
490
+ end.output
491
491
 
492
492
  # Returns
493
493
 
@@ -507,7 +507,7 @@ end
507
507
  When passed a _symbol_, it will collect that key from field meta data.
508
508
 
509
509
  ```ruby
510
- schema_labels = create_user_schema.walk(:label)
510
+ schema_labels = create_user_schema.walk(:label).output
511
511
 
512
512
  # returns
513
513
 
@@ -690,8 +690,7 @@ class CreateUserForm
690
690
  attr_reader :errors, :params
691
691
 
692
692
  def initialize(payload: {})
693
- @payload = payload
694
- results = self.class.schema.resolve(params)
693
+ results = self.class.schema.resolve(payload)
695
694
  @errors = results.errors
696
695
  @params = results.output
697
696
  end
@@ -743,6 +742,165 @@ class CreateUserForm
743
742
  end
744
743
  ```
745
744
 
745
+ ## Expanding fields dynamically
746
+
747
+ Sometimes you don't know the exact field names but you want to allow arbitrary fields depending on a given pattern.
748
+
749
+ ```ruby
750
+ # with this payload:
751
+ # {
752
+ # title: "A title",
753
+ # :"custom_attr_Color" => "red",
754
+ # :"custom_attr_Material" => "leather"
755
+ # }
756
+
757
+ schema = Parametric::Schema.new do
758
+ field(:title).type(:string).present
759
+ # here we allow any field starting with /^custom_attr/
760
+ # this yields a MatchData object to the block
761
+ # where you can define a Field and validations on the fly
762
+ # https://ruby-doc.org/core-2.2.0/MatchData.html
763
+ expand(/^custom_attr_(.+)/) do |match|
764
+ field(match[1]).type(:string).present
765
+ end
766
+ end
767
+
768
+ results = schema.resolve({
769
+ title: "A title",
770
+ :"custom_attr_Color" => "red",
771
+ :"custom_attr_Material" => "leather",
772
+ :"custom_attr_Weight" => "",
773
+ })
774
+
775
+ results.ouput[:Color] # => "red"
776
+ results.ouput[:Material] # => "leather"
777
+ results.errors["$.Weight"] # => ["is required and value must be present"]
778
+ ```
779
+
780
+ NOTES: dynamically expanded field names are not included in `Schema#structure` metadata, and they are only processes if fields with the given expressions are present in the payload. This means that validations applied to those fields only run if keys are present in the first place.
781
+
782
+ ## Structs
783
+
784
+ Structs turn schema definitions into objects graphs with attribute readers.
785
+
786
+ Add optional `Parametrict::Struct` module to define struct-like objects with schema definitions.
787
+
788
+ ```ruby
789
+ require 'parametric/struct'
790
+
791
+ class User
792
+ include Parametric::Struct
793
+
794
+ schema do
795
+ field(:name).type(:string).present
796
+ field(:friends).type(:array).schema do
797
+ field(:name).type(:string).present
798
+ field(:age).type(:integer)
799
+ end
800
+ end
801
+ end
802
+ ```
803
+
804
+ `User` objects can be instantiated with hash data, which will be coerced and validated as per the schema definition.
805
+
806
+ ```ruby
807
+ user = User.new(
808
+ name: 'Joe',
809
+ friends: [
810
+ {name: 'Jane', age: 40},
811
+ {name: 'John', age: 30},
812
+ ]
813
+ )
814
+
815
+ # properties
816
+ user.name # => 'Joe'
817
+ user.friends.first.name # => 'Jane'
818
+ user.friends.last.age # => 30
819
+ ```
820
+
821
+ ### Errors
822
+
823
+ Both the top-level and nested instances contain error information:
824
+
825
+ ```ruby
826
+ user = User.new(
827
+ name: '', # invalid
828
+ friends: [
829
+ # friend name also invalid
830
+ {name: '', age: 40},
831
+ ]
832
+ )
833
+
834
+ user.valid? # false
835
+ user.errors['$.name'] # => "is required and must be present"
836
+ user.errors['$.friends[0].name'] # => "is required and must be present"
837
+
838
+ # also access error in nested instances directly
839
+ user.friends.first.valid? # false
840
+ user.friends.first.errors['$.name'] # "is required and must be valid"
841
+ ```
842
+
843
+ ### Nested structs
844
+
845
+ You can also pass separate struct classes in a nested schema definition.
846
+
847
+ ```ruby
848
+ class Friend
849
+ include Parametric::Struct
850
+
851
+ schema do
852
+ field(:name).type(:string).present
853
+ field(:age).type(:integer)
854
+ end
855
+ end
856
+
857
+ class User
858
+ include Parametric::Struct
859
+
860
+ schema do
861
+ field(:name).type(:string).present
862
+ # here we use the Friend class
863
+ field(:friends).type(:array).schema Friend
864
+ end
865
+ end
866
+ ```
867
+
868
+ ### Inheritance
869
+
870
+ Struct subclasses can add to inherited schemas, or override fields defined in the parent.
871
+
872
+ ```ruby
873
+ class AdminUser < User
874
+ # inherits User schema, and can add stuff to its own schema
875
+ schema do
876
+ field(:permissions).type(:array)
877
+ end
878
+ end
879
+ ```
880
+
881
+ ### #to_h
882
+
883
+ `Struct#to_h` returns the ouput hash, with values coerced and any defaults populated.
884
+
885
+ ```ruby
886
+ class User
887
+ include Parametrict::Struct
888
+ schema do
889
+ field(:name).type(:string)
890
+ field(:age).type(:integer).default(30)
891
+ end
892
+ end
893
+
894
+ user = User.new(name: "Joe")
895
+ user.to_h # {name: "Joe", age: 30}
896
+ ```
897
+
898
+ ### Struct equality
899
+
900
+ `Parametric::Struct` implements `#==()` to compare two structs Hash representation (same as `struct1.to_h.eql?(struct2.to_h)`.
901
+
902
+ Users can override `#==()` in their own classes to do whatever they need.
903
+
746
904
  ## Installation
747
905
 
748
906
  Add this line to your application's Gemfile:
@@ -41,6 +41,11 @@ module Parametric
41
41
 
42
42
  new_schema = Parametric::Schema.new(options, &block)
43
43
  @schema = @schema.merge(new_schema)
44
+ after_define_schema(@schema)
45
+ end
46
+
47
+ def after_define_schema(sc)
48
+ # noop hook
44
49
  end
45
50
  end
46
51
  end
@@ -40,7 +40,7 @@ module Parametric
40
40
  def schema(sc = nil, &block)
41
41
  sc = (sc ? sc : Schema.new(&block))
42
42
  meta schema: sc
43
- policy sc
43
+ policy sc.schema
44
44
  end
45
45
 
46
46
  def visit(meta_key = nil, &visitor)
@@ -11,6 +11,11 @@ module Parametric
11
11
  @definitions << block if block_given?
12
12
  @default_field_policies = []
13
13
  @ignored_field_keys = []
14
+ @expansions = {}
15
+ end
16
+
17
+ def schema
18
+ self
14
19
  end
15
20
 
16
21
  def fields
@@ -80,6 +85,11 @@ module Parametric
80
85
  end
81
86
  end
82
87
 
88
+ def expand(exp, &block)
89
+ expansions[exp] = block
90
+ self
91
+ end
92
+
83
93
  def resolve(payload)
84
94
  context = Context.new
85
95
  output = coerce(payload, nil, context)
@@ -112,10 +122,13 @@ module Parametric
112
122
  def coerce(val, _, context)
113
123
  if val.is_a?(Array)
114
124
  val.map.with_index{|v, idx|
115
- coerce_one(v, context.sub(idx))
125
+ subcontext = context.sub(idx)
126
+ out = coerce_one(v, subcontext)
127
+ resolve_expansions(v, out, subcontext)
116
128
  }
117
129
  else
118
- coerce_one val, context
130
+ out = coerce_one(val, context)
131
+ resolve_expansions(val, out, context)
119
132
  end
120
133
  end
121
134
 
@@ -125,10 +138,10 @@ module Parametric
125
138
 
126
139
  private
127
140
 
128
- attr_reader :default_field_policies, :ignored_field_keys
141
+ attr_reader :default_field_policies, :ignored_field_keys, :expansions
129
142
 
130
- def coerce_one(val, context)
131
- fields.each_with_object({}) do |(_, field), m|
143
+ def coerce_one(val, context, flds: fields)
144
+ flds.each_with_object({}) do |(_, field), m|
132
145
  r = field.resolve(val, context.sub(field.key))
133
146
  if r.eligible?
134
147
  m[field.key] = r.value
@@ -136,6 +149,27 @@ module Parametric
136
149
  end
137
150
  end
138
151
 
152
+ class MatchContext
153
+ def field(key)
154
+ Field.new(key.to_sym)
155
+ end
156
+ end
157
+
158
+ def resolve_expansions(payload, into, context)
159
+ expansions.each do |exp, block|
160
+ payload.each do |key, value|
161
+ if match = exp.match(key.to_s)
162
+ fld = MatchContext.new.instance_exec(match, &block)
163
+ if fld
164
+ into.update(coerce_one({fld.key => value}, context, flds: {fld.key => apply_default_field_policies_to(fld)}))
165
+ end
166
+ end
167
+ end
168
+ end
169
+
170
+ into
171
+ end
172
+
139
173
  def apply_default_field_policies_to(field)
140
174
  default_field_policies.reduce(field) {|f, policy_name| f.policy(policy_name) }
141
175
  end
@@ -0,0 +1,80 @@
1
+ require 'parametric/dsl'
2
+
3
+ module Parametric
4
+ module Struct
5
+ def self.included(base)
6
+ base.send(:include, Parametric::DSL)
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ def initialize(attrs = {})
11
+ @_results = self.class.schema.resolve(attrs)
12
+ @_graph = self.class.build(@_results.output)
13
+ end
14
+
15
+ def valid?
16
+ !_results.errors.any?
17
+ end
18
+
19
+ def errors
20
+ _results.errors
21
+ end
22
+
23
+ # returns a shallow copy.
24
+ def to_h
25
+ _results.output.clone
26
+ end
27
+
28
+ def ==(other)
29
+ other.respond_to?(:to_h) && other.to_h.eql?(to_h)
30
+ end
31
+
32
+ def merge(attrs = {})
33
+ self.class.new(to_h.merge(attrs))
34
+ end
35
+
36
+ private
37
+ attr_reader :_graph, :_results
38
+
39
+ module ClassMethods
40
+ # this hook is called after schema definition in DSL module
41
+ def after_define_schema(schema)
42
+ schema.fields.keys.each do |key|
43
+ define_method key do
44
+ _graph[key]
45
+ end
46
+ end
47
+ end
48
+
49
+ def build(attrs)
50
+ attrs.each_with_object({}) do |(k, v), obj|
51
+ obj[k] = wrap(k, v)
52
+ end
53
+ end
54
+
55
+ def wrap(key, value)
56
+ field = schema.fields[key]
57
+ return value unless field
58
+
59
+ case value
60
+ when Hash
61
+ # find constructor for field
62
+ cons = field.meta_data[:schema]
63
+ if cons.kind_of?(Parametric::Schema)
64
+ klass = Class.new do
65
+ include Struct
66
+ end
67
+ klass.schema = cons
68
+ klass.after_define_schema(cons)
69
+ cons = klass
70
+ end
71
+ cons ? cons.new(value) : value.freeze
72
+ when Array
73
+ value.map{|v| wrap(key, v) }.freeze
74
+ else
75
+ value.freeze
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -1,3 +1,3 @@
1
1
  module Parametric
2
- VERSION = "0.1.3"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parametric::Schema do
4
+ it "expands fields dynamically" do
5
+ schema = described_class.new do
6
+ field(:title).type(:string).present
7
+ expand(/^attr_(.+)/) do |match|
8
+ field(match[1]).type(:string)
9
+ end
10
+ expand(/^validate_(.+)/) do |match|
11
+ field(match[1]).type(:string).present
12
+ end
13
+ end
14
+
15
+ out = schema.resolve({
16
+ title: "foo",
17
+ :"attr_Attribute 1" => "attr 1",
18
+ :"attr_Attribute 2" => "attr 2",
19
+ :"validate_valid_attr" => "valid",
20
+ :"validate_invalid_attr" => "",
21
+ })
22
+
23
+ expect(out.output[:title]).to eq 'foo'
24
+ expect(out.output[:"Attribute 1"]).to eq 'attr 1'
25
+ expect(out.output[:"Attribute 2"]).to eq 'attr 2'
26
+
27
+ expect(out.errors['$.invalid_attr']).to eq ['is required and value must be present']
28
+ end
29
+ end
@@ -0,0 +1,277 @@
1
+ require 'spec_helper'
2
+ require 'parametric/struct'
3
+
4
+ describe Parametric::Struct do
5
+ it "works" do
6
+ friend_class = Class.new do
7
+ include Parametric::Struct
8
+
9
+ schema do
10
+ field(:name).type(:string).present
11
+ field(:age).type(:integer)
12
+ end
13
+ end
14
+
15
+ klass = Class.new do
16
+ include Parametric::Struct
17
+
18
+ schema do
19
+ field(:title).type(:string).present
20
+ field(:friends).type(:array).default([]).schema friend_class
21
+ end
22
+ end
23
+
24
+ new_instance = klass.new
25
+ expect(new_instance.title).to eq ''
26
+ expect(new_instance.friends).to eq []
27
+ expect(new_instance.valid?).to be false
28
+ expect(new_instance.errors['$.title']).not_to be_nil
29
+
30
+ instance = klass.new({
31
+ title: 'foo',
32
+ friends: [
33
+ {name: 'Ismael', age: 40},
34
+ {name: 'Joe', age: 39},
35
+ ]
36
+ })
37
+
38
+ expect(instance.title).to eq 'foo'
39
+ expect(instance.friends.size).to eq 2
40
+ expect(instance.friends.first.name).to eq 'Ismael'
41
+ expect(instance.friends.first).to be_a friend_class
42
+
43
+ invalid_instance = klass.new({
44
+ friends: [
45
+ {name: 'Ismael', age: 40},
46
+ {age: 39},
47
+ ]
48
+ })
49
+
50
+ expect(invalid_instance.valid?).to be false
51
+ expect(invalid_instance.errors['$.title']).not_to be_nil
52
+ expect(invalid_instance.errors['$.friends[1].name']).not_to be_nil
53
+ expect(invalid_instance.friends[1].errors['$.name']).not_to be_nil
54
+ end
55
+
56
+ it "is inmutable by default" do
57
+ klass = Class.new do
58
+ include Parametric::Struct
59
+
60
+ schema do
61
+ field(:title).type(:string).present
62
+ field(:friends).type(:array).default([])
63
+ field(:friend).type(:object)
64
+ end
65
+ end
66
+
67
+ instance = klass.new
68
+ expect {
69
+ instance.title = "foo"
70
+ }.to raise_error NoMethodError
71
+
72
+ expect {
73
+ instance.friends << 1
74
+ }.to raise_error RuntimeError
75
+ end
76
+
77
+ it "works with anonymous nested schemas" do
78
+ klass = Class.new do
79
+ include Parametric::Struct
80
+
81
+ schema do
82
+ field(:title).type(:string).present
83
+ field(:friends).type(:array).schema do
84
+ field(:age).type(:integer)
85
+ end
86
+ end
87
+ end
88
+
89
+ instance = klass.new({
90
+ title: 'foo',
91
+ friends: [
92
+ {age: 10},
93
+ {age: 39},
94
+ ]
95
+ })
96
+
97
+ expect(instance.title).to eq 'foo'
98
+ expect(instance.friends.size).to eq 2
99
+ expect(instance.friends.first.age).to eq 10
100
+ end
101
+
102
+ it "wraps regular schemas in structs" do
103
+ friend_schema = Parametric::Schema.new do
104
+ field(:name)
105
+ end
106
+
107
+ klass = Class.new do
108
+ include Parametric::Struct
109
+
110
+ schema do
111
+ field(:title).type(:string).present
112
+ field(:friends).type(:array).schema friend_schema
113
+ end
114
+ end
115
+
116
+ instance = klass.new({
117
+ title: 'foo',
118
+ friends: [{name: 'Ismael'}]
119
+ })
120
+
121
+ expect(instance.friends.first.name).to eq 'Ismael'
122
+ end
123
+
124
+ it "#to_h" do
125
+ klass = Class.new do
126
+ include Parametric::Struct
127
+
128
+ schema do
129
+ field(:title).type(:string).present
130
+ field(:friends).type(:array).schema do
131
+ field(:name).type(:string)
132
+ field(:age).type(:integer).default(20)
133
+ end
134
+ end
135
+ end
136
+
137
+ instance = klass.new({
138
+ title: 'foo',
139
+ friends: [
140
+ {name: 'Jane'},
141
+ {name: 'Joe', age: '39'},
142
+ ]
143
+ })
144
+
145
+ expect(instance.to_h).to eq({
146
+ title: 'foo',
147
+ friends: [
148
+ {name: 'Jane', age: 20},
149
+ {name: 'Joe', age: 39},
150
+ ]
151
+ })
152
+
153
+ new_instance = klass.new(instance.to_h)
154
+ expect(new_instance.title).to eq 'foo'
155
+
156
+ # it returns a copy so we can't break things!
157
+ data = new_instance.to_h
158
+ data[:title] = 'nope'
159
+ expect(new_instance.to_h[:title]).to eq 'foo'
160
+ end
161
+
162
+ it "works with inheritance" do
163
+ klass = Class.new do
164
+ include Parametric::Struct
165
+
166
+ schema do
167
+ field(:title).type(:string).present
168
+ field(:friends).type(:array).schema do
169
+ field(:name).type(:string)
170
+ field(:age).type(:integer).default(20)
171
+ end
172
+ end
173
+ end
174
+
175
+ subclass = Class.new(klass) do
176
+ schema do
177
+ field(:email)
178
+ end
179
+ end
180
+
181
+ instance = subclass.new(
182
+ title: 'foo',
183
+ email: 'email@me.com',
184
+ friends: [
185
+ {name: 'Jane', age: 20},
186
+ {name: 'Joe', age: 39},
187
+ ]
188
+ )
189
+
190
+ expect(instance.title).to eq 'foo'
191
+ expect(instance.email).to eq 'email@me.com'
192
+ expect(instance.friends.size).to eq 2
193
+ end
194
+
195
+ it "implements deep struct equality" do
196
+ klass = Class.new do
197
+ include Parametric::Struct
198
+
199
+ schema do
200
+ field(:title).type(:string).present
201
+ field(:friends).type(:array).schema do
202
+ field(:age).type(:integer)
203
+ end
204
+ end
205
+ end
206
+
207
+ s1 = klass.new({
208
+ title: 'foo',
209
+ friends: [
210
+ {age: 10},
211
+ {age: 39},
212
+ ]
213
+ })
214
+
215
+
216
+ s2 = klass.new({
217
+ title: 'foo',
218
+ friends: [
219
+ {age: 10},
220
+ {age: 39},
221
+ ]
222
+ })
223
+
224
+ s3 = klass.new({
225
+ title: 'foo',
226
+ friends: [
227
+ {age: 11},
228
+ {age: 39},
229
+ ]
230
+ })
231
+
232
+ s4 = klass.new({
233
+ title: 'bar',
234
+ friends: [
235
+ {age: 10},
236
+ {age: 39},
237
+ ]
238
+ })
239
+
240
+ expect(s1 == s2).to be true
241
+ expect(s1 == s3).to be false
242
+ expect(s1 == s4).to be false
243
+ end
244
+
245
+ it "#merge returns a new instance" do
246
+ klass = Class.new do
247
+ include Parametric::Struct
248
+
249
+ schema do
250
+ field(:title).type(:string).present
251
+ field(:desc)
252
+ field(:friends).type(:array).schema do
253
+ field(:name).type(:string)
254
+ end
255
+ end
256
+ end
257
+
258
+ original = klass.new(
259
+ title: 'foo',
260
+ desc: 'no change',
261
+ friends: [{name: 'joe'}]
262
+ )
263
+
264
+ copy = original.merge(
265
+ title: 'bar',
266
+ friends: [{name: 'jane'}]
267
+ )
268
+
269
+ expect(original.title).to eq 'foo'
270
+ expect(original.desc).to eq 'no change'
271
+ expect(original.friends.first.name).to eq 'joe'
272
+
273
+ expect(copy.title).to eq 'bar'
274
+ expect(copy.desc).to eq 'no change'
275
+ expect(copy.friends.first.name).to eq 'jane'
276
+ end
277
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parametric
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ismael Celis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-29 00:00:00.000000000 Z
11
+ date: 2018-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -96,14 +96,17 @@ files:
96
96
  - lib/parametric/registry.rb
97
97
  - lib/parametric/results.rb
98
98
  - lib/parametric/schema.rb
99
+ - lib/parametric/struct.rb
99
100
  - lib/parametric/version.rb
100
101
  - parametric.gemspec
101
102
  - spec/dsl_spec.rb
103
+ - spec/expand_spec.rb
102
104
  - spec/field_spec.rb
103
105
  - spec/policies_spec.rb
104
106
  - spec/schema_spec.rb
105
107
  - spec/schema_walk_spec.rb
106
108
  - spec/spec_helper.rb
109
+ - spec/struct_spec.rb
107
110
  - spec/validators_spec.rb
108
111
  homepage: ''
109
112
  licenses:
@@ -125,16 +128,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
128
  version: '0'
126
129
  requirements: []
127
130
  rubyforge_project:
128
- rubygems_version: 2.5.1
131
+ rubygems_version: 2.7.6
129
132
  signing_key:
130
133
  specification_version: 4
131
134
  summary: DSL for declaring allowed parameters with options, regexp patern and default
132
135
  values.
133
136
  test_files:
134
137
  - spec/dsl_spec.rb
138
+ - spec/expand_spec.rb
135
139
  - spec/field_spec.rb
136
140
  - spec/policies_spec.rb
137
141
  - spec/schema_spec.rb
138
142
  - spec/schema_walk_spec.rb
139
143
  - spec/spec_helper.rb
144
+ - spec/struct_spec.rb
140
145
  - spec/validators_spec.rb