parametric 0.0.5 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +1 -2
- data/README.md +596 -163
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/parametric/block_validator.rb +64 -0
- data/lib/parametric/context.rb +44 -0
- data/lib/parametric/default_types.rb +95 -0
- data/lib/parametric/dsl.rb +47 -0
- data/lib/parametric/field.rb +111 -0
- data/lib/parametric/field_dsl.rb +20 -0
- data/lib/parametric/policies.rb +94 -55
- data/lib/parametric/registry.rb +21 -0
- data/lib/parametric/results.rb +13 -0
- data/lib/parametric/schema.rb +151 -0
- data/lib/parametric/version.rb +1 -1
- data/lib/parametric.rb +16 -6
- data/parametric.gemspec +2 -1
- data/spec/dsl_spec.rb +135 -0
- data/spec/field_spec.rb +404 -0
- data/spec/policies_spec.rb +72 -0
- data/spec/schema_spec.rb +253 -0
- data/spec/schema_walk_spec.rb +42 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/validators_spec.rb +97 -0
- metadata +54 -24
- data/lib/parametric/hash.rb +0 -38
- data/lib/parametric/params.rb +0 -86
- data/lib/parametric/typed_params.rb +0 -23
- data/lib/parametric/utils.rb +0 -24
- data/lib/support/class_attribute.rb +0 -68
- data/spec/nested_params_spec.rb +0 -90
- data/spec/parametric_spec.rb +0 -261
data/spec/schema_spec.rb
ADDED
@@ -0,0 +1,253 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Parametric::Schema do
|
4
|
+
before do
|
5
|
+
Parametric.policy :flexible_bool do
|
6
|
+
coerce do |v, k, c|
|
7
|
+
case v
|
8
|
+
when '1', 'true', 'TRUE', true
|
9
|
+
true
|
10
|
+
else
|
11
|
+
false
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
subject do
|
18
|
+
described_class.new do
|
19
|
+
field(:title).policy(:string).present
|
20
|
+
field(:price).policy(:integer).meta(label: "A price")
|
21
|
+
field(:status).policy(:string).options(['visible', 'hidden'])
|
22
|
+
field(:tags).policy(:split).policy(:array)
|
23
|
+
field(:description).policy(:string)
|
24
|
+
field(:variants).policy(:array).schema do
|
25
|
+
field(:name).policy(:string).present
|
26
|
+
field(:sku)
|
27
|
+
field(:stock).policy(:integer).default(1)
|
28
|
+
field(:available_if_no_stock).policy(:boolean).policy(:flexible_bool).default(false)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#structure" do
|
34
|
+
it "represents data structure and meta data" do
|
35
|
+
sc = subject.structure
|
36
|
+
expect(sc[:title][:present]).to be true
|
37
|
+
expect(sc[:title][:type]).to eq :string
|
38
|
+
expect(sc[:price][:type]).to eq :integer
|
39
|
+
expect(sc[:price][:label]).to eq "A price"
|
40
|
+
expect(sc[:variants][:type]).to eq :array
|
41
|
+
sc[:variants][:structure].tap do |sc|
|
42
|
+
expect(sc[:name][:type]).to eq :string
|
43
|
+
expect(sc[:name][:present]).to be true
|
44
|
+
expect(sc[:stock][:default]).to eq 1
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def resolve(schema, payload, &block)
|
50
|
+
yield schema.resolve(payload)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_schema(schema, payload, result)
|
54
|
+
resolve(schema, payload) do |results|
|
55
|
+
expect(results.output).to eq result
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'works' do
|
60
|
+
test_schema(subject, {
|
61
|
+
title: 'iPhone 6 Plus',
|
62
|
+
price: '100.0',
|
63
|
+
status: 'visible',
|
64
|
+
tags: 'tag1, tag2',
|
65
|
+
description: 'A description',
|
66
|
+
variants: [{name: 'v1', sku: 'ABC', stock: '10', available_if_no_stock: true}]
|
67
|
+
},
|
68
|
+
{
|
69
|
+
title: 'iPhone 6 Plus',
|
70
|
+
price: 100,
|
71
|
+
status: 'visible',
|
72
|
+
tags: ['tag1', 'tag2'],
|
73
|
+
description: 'A description',
|
74
|
+
variants: [{name: 'v1', sku: 'ABC', stock: 10, available_if_no_stock: true}]
|
75
|
+
})
|
76
|
+
|
77
|
+
test_schema(subject, {
|
78
|
+
title: 'iPhone 6 Plus',
|
79
|
+
variants: [{name: 'v1', available_if_no_stock: '1'}]
|
80
|
+
},
|
81
|
+
{
|
82
|
+
title: 'iPhone 6 Plus',
|
83
|
+
variants: [{name: 'v1', stock: 1, available_if_no_stock: true}]
|
84
|
+
})
|
85
|
+
|
86
|
+
resolve(subject, {}) do |results|
|
87
|
+
expect(results.valid?).to be false
|
88
|
+
expect(results.errors['$.title']).not_to be_nil
|
89
|
+
expect(results.errors['$.variants']).to be_nil
|
90
|
+
expect(results.errors['$.status']).to be_nil
|
91
|
+
end
|
92
|
+
|
93
|
+
resolve(subject, {title: 'Foobar', variants: [{name: 'v1'}, {sku: '345'}]}) do |results|
|
94
|
+
expect(results.valid?).to be false
|
95
|
+
expect(results.errors['$.variants[1].name']).not_to be_nil
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#policy" do
|
100
|
+
it "applies policy to all fields" do
|
101
|
+
subject.policy(:declared)
|
102
|
+
|
103
|
+
resolve(subject, {}) do |results|
|
104
|
+
expect(results.valid?).to be true
|
105
|
+
expect(results.errors.keys).to be_empty
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
it "replaces previous policies" do
|
110
|
+
subject.policy(:declared)
|
111
|
+
subject.policy(:present)
|
112
|
+
|
113
|
+
resolve(subject, {title: "hello"}) do |results|
|
114
|
+
expect(results.valid?).to be false
|
115
|
+
expect(results.errors.keys).to match_array(%w(
|
116
|
+
$.price
|
117
|
+
$.status
|
118
|
+
$.tags
|
119
|
+
$.description
|
120
|
+
$.variants
|
121
|
+
))
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
it "applies :noop policy to all fields" do
|
126
|
+
subject.policy(:noop)
|
127
|
+
|
128
|
+
resolve(subject, {}) do |results|
|
129
|
+
expect(results.valid?).to be false
|
130
|
+
expect(results.errors['$.title']).not_to be_nil
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "#merge" do
|
136
|
+
context "no options" do
|
137
|
+
let!(:schema1) {
|
138
|
+
described_class.new do
|
139
|
+
field(:title).policy(:string).present
|
140
|
+
field(:price).policy(:integer)
|
141
|
+
end
|
142
|
+
}
|
143
|
+
|
144
|
+
let!(:schema2) {
|
145
|
+
described_class.new do
|
146
|
+
field(:price).policy(:string)
|
147
|
+
field(:description).policy(:string)
|
148
|
+
end
|
149
|
+
}
|
150
|
+
|
151
|
+
it "returns a new schema adding new fields and updating existing ones" do
|
152
|
+
new_schema = schema1.merge(schema2)
|
153
|
+
expect(new_schema.fields.keys).to match_array([:title, :price, :description])
|
154
|
+
|
155
|
+
# did not mutate original
|
156
|
+
expect(schema1.fields[:price].meta_data[:type]).to eq :integer
|
157
|
+
|
158
|
+
expect(new_schema.fields[:title].meta_data[:type]).to eq :string
|
159
|
+
expect(new_schema.fields[:price].meta_data[:type]).to eq :string
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context "with options" do
|
164
|
+
let!(:schema1) {
|
165
|
+
described_class.new(price_type: :integer, label: "Foo") do |opts|
|
166
|
+
field(:title).policy(:string).present
|
167
|
+
field(:price).policy(opts[:price_type]).meta(label: opts[:label])
|
168
|
+
end
|
169
|
+
}
|
170
|
+
|
171
|
+
let!(:schema2) {
|
172
|
+
described_class.new(price_type: :string) do
|
173
|
+
field(:description).policy(:string)
|
174
|
+
end
|
175
|
+
}
|
176
|
+
|
177
|
+
it "inherits options" do
|
178
|
+
new_schema = schema1.merge(schema2)
|
179
|
+
expect(new_schema.fields[:price].meta_data[:type]).to eq :string
|
180
|
+
expect(new_schema.fields[:price].meta_data[:label]).to eq "Foo"
|
181
|
+
end
|
182
|
+
|
183
|
+
it "re-applies blocks with new options" do
|
184
|
+
new_schema = schema1.merge(schema2)
|
185
|
+
expect(new_schema.fields.keys).to match_array([:title, :price, :description])
|
186
|
+
|
187
|
+
# did not mutate original
|
188
|
+
expect(schema1.fields[:price].meta_data[:type]).to eq :integer
|
189
|
+
|
190
|
+
expect(new_schema.fields[:title].meta_data[:type]).to eq :string
|
191
|
+
expect(new_schema.fields[:price].meta_data[:type]).to eq :string
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe "#clone" do
|
197
|
+
let!(:schema1) {
|
198
|
+
described_class.new do |opts|
|
199
|
+
field(:id).present
|
200
|
+
field(:title).policy(:string).present
|
201
|
+
field(:price)
|
202
|
+
end
|
203
|
+
}
|
204
|
+
|
205
|
+
it "returns a copy that can be further manipulated" do
|
206
|
+
schema2 = schema1.clone.policy(:declared).ignore(:id)
|
207
|
+
expect(schema1.fields.keys).to match_array([:id, :title, :price])
|
208
|
+
expect(schema2.fields.keys).to match_array([:title, :price])
|
209
|
+
|
210
|
+
results1 = schema1.resolve(id: "abc", price: 100)
|
211
|
+
expect(results1.errors.keys).to eq ["$.title"]
|
212
|
+
|
213
|
+
results2 = schema2.resolve(id: "abc", price: 100)
|
214
|
+
expect(results2.errors.keys).to eq []
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
describe "#ignore" do
|
219
|
+
it "ignores fields" do
|
220
|
+
s1 = described_class.new.ignore(:title, :status) do
|
221
|
+
field(:status)
|
222
|
+
field(:title).policy(:string).present
|
223
|
+
field(:price).policy(:integer)
|
224
|
+
end
|
225
|
+
|
226
|
+
output = s1.resolve(status: "draft", title: "foo", price: "100").output
|
227
|
+
expect(output).to eq({price: 100})
|
228
|
+
end
|
229
|
+
|
230
|
+
it "ignores when merging" do
|
231
|
+
s1 = described_class.new do
|
232
|
+
field(:status)
|
233
|
+
field(:title).policy(:string).present
|
234
|
+
end
|
235
|
+
|
236
|
+
s1 = described_class.new.ignore(:title, :status) do
|
237
|
+
field(:price).policy(:integer)
|
238
|
+
end
|
239
|
+
|
240
|
+
output = s1.resolve(title: "foo", status: "draft", price: "100").output
|
241
|
+
expect(output).to eq({price: 100})
|
242
|
+
end
|
243
|
+
|
244
|
+
it "returns self so it can be chained" do
|
245
|
+
s1 = described_class.new do
|
246
|
+
field(:status)
|
247
|
+
field(:title).policy(:string).present
|
248
|
+
end
|
249
|
+
|
250
|
+
expect(s1.ignore(:status)).to eq s1
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Schema#walk' do
|
4
|
+
let(:schema) do
|
5
|
+
Parametric::Schema.new do
|
6
|
+
field(:title).meta(example: 'a title', label: 'custom title')
|
7
|
+
field(:tags).policy(:array).meta(example: ['tag1', 'tag2'], label: 'comma-separated tags')
|
8
|
+
field(:friends).policy(:array).schema do
|
9
|
+
field(:name).meta(example: 'a friend', label: 'friend full name')
|
10
|
+
field(:age).meta(example: 34, label: 'age')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "recursively walks the schema and collects meta data" do
|
16
|
+
results = schema.walk(:label)
|
17
|
+
expect(results.output).to eq({
|
18
|
+
title: 'custom title',
|
19
|
+
tags: 'comma-separated tags',
|
20
|
+
friends: [
|
21
|
+
{
|
22
|
+
name: 'friend full name',
|
23
|
+
age: 'age'
|
24
|
+
}
|
25
|
+
]
|
26
|
+
})
|
27
|
+
end
|
28
|
+
|
29
|
+
it "works with blocks" do
|
30
|
+
results = schema.walk{|field| field.meta_data[:example]}
|
31
|
+
expect(results.output).to eq({
|
32
|
+
title: 'a title',
|
33
|
+
tags: ['tag1', 'tag2'],
|
34
|
+
friends: [
|
35
|
+
{
|
36
|
+
name: 'a friend',
|
37
|
+
age: 34
|
38
|
+
}
|
39
|
+
]
|
40
|
+
})
|
41
|
+
end
|
42
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'default validators' do
|
4
|
+
|
5
|
+
def test_validator(payload, key, name, eligible, valid, *args)
|
6
|
+
validator = Parametric.registry.policies[name]
|
7
|
+
validator = validator.new(*args) if validator.respond_to?(:new)
|
8
|
+
expect(validator.eligible?(payload[key], key, payload)).to eq eligible
|
9
|
+
expect(validator.valid?(payload[key], key, payload)).to eq valid
|
10
|
+
end
|
11
|
+
|
12
|
+
describe ':format' do
|
13
|
+
it {
|
14
|
+
test_validator({key: 'Foobar'}, :key, :format, true, true, /^Foo/)
|
15
|
+
test_validator({key: 'Foobar'}, :key, :format, true, false, /^Bar/)
|
16
|
+
test_validator({foo: 'Foobar'}, :key, :format, false, true, /^Foo/)
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
describe ':email' do
|
21
|
+
it {
|
22
|
+
test_validator({key: 'foo@bar.com'}, :key, :email, true, true)
|
23
|
+
test_validator({key: 'foo@'}, :key, :email, true, false)
|
24
|
+
test_validator({foo: 'foo@bar.com'}, :key, :email, false, true)
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
describe ':required' do
|
29
|
+
it {
|
30
|
+
test_validator({key: 'foo'}, :key, :required, true, true)
|
31
|
+
test_validator({key: ''}, :key, :required, true, true)
|
32
|
+
test_validator({key: nil}, :key, :required, true, true)
|
33
|
+
test_validator({foo: 'foo'}, :key, :required, true, false)
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
describe ':present' do
|
38
|
+
it {
|
39
|
+
test_validator({key: 'foo'}, :key, :present, true, true)
|
40
|
+
test_validator({key: ''}, :key, :present, true, false)
|
41
|
+
test_validator({key: nil}, :key, :present, true, false)
|
42
|
+
test_validator({foo: 'foo'}, :key, :present, true, false)
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
describe ':gt' do
|
47
|
+
it {
|
48
|
+
test_validator({key: 10}, :key, :gt, true, true, 9)
|
49
|
+
test_validator({key: '10'}, :key, :gt, true, true, 9)
|
50
|
+
test_validator({key: 10}, :key, :gt, true, false, 11)
|
51
|
+
test_validator({key: '10'}, :key, :gt, true, false, 11)
|
52
|
+
test_validator({foo: '10'}, :key, :gt, true, true, 11)
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
describe ':lt' do
|
57
|
+
it {
|
58
|
+
test_validator({key: 10}, :key, :lt, true, true, 11)
|
59
|
+
test_validator({key: '10'}, :key, :lt, true, true, 11)
|
60
|
+
test_validator({key: 10}, :key, :lt, true, false, 9)
|
61
|
+
test_validator({key: '10'}, :key, :lt, true, false, 9)
|
62
|
+
test_validator({foo: '10'}, :key, :lt, true, true, 9)
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
describe ':options' do
|
67
|
+
it {
|
68
|
+
test_validator({key: 'b'}, :key, :options, true, true, %w(a b c))
|
69
|
+
test_validator({key: 'd'}, :key, :options, true, false, %w(a b c))
|
70
|
+
test_validator({key: ['c', 'b']}, :key, :options, true, true, %w(a b c))
|
71
|
+
test_validator({key: ['c', 'b', 'd']}, :key, :options, true, false, %w(a b c))
|
72
|
+
test_validator({foo: 'b'}, :key, :options, false, true, %w(a b c))
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
describe ':array' do
|
77
|
+
it {
|
78
|
+
test_validator({key: ['a', 'b']}, :key, :array, true, true)
|
79
|
+
test_validator({key: []}, :key, :array, true, true)
|
80
|
+
test_validator({key: nil}, :key, :array, true, false)
|
81
|
+
test_validator({key: 'hello'}, :key, :array, true, false)
|
82
|
+
test_validator({key: 123}, :key, :array, true, false)
|
83
|
+
test_validator({foo: []}, :key, :array, true, true)
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
describe ':object' do
|
88
|
+
it {
|
89
|
+
test_validator({key: {'a' =>'b'}}, :key, :object, true, true)
|
90
|
+
test_validator({key: {}}, :key, :object, true, true)
|
91
|
+
test_validator({key: ['a', 'b']}, :key, :object, true, false)
|
92
|
+
test_validator({key: nil}, :key, :object, true, false)
|
93
|
+
test_validator({key: 123}, :key, :object, true, false)
|
94
|
+
test_validator({foo: {}}, :key, :object, true, true)
|
95
|
+
}
|
96
|
+
end
|
97
|
+
end
|
metadata
CHANGED
@@ -1,41 +1,41 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: parametric
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.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:
|
11
|
+
date: 2016-12-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.5'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.5'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
@@ -44,41 +44,67 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - '='
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 3.4.0
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - '='
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 3.4.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: byebug
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
description: Useful for modelling search or form objects, white-listed query parameters
|
56
70
|
and safe parameter defaults.
|
57
71
|
email:
|
58
72
|
- ismaelct@gmail.com
|
59
|
-
executables:
|
73
|
+
executables:
|
74
|
+
- console
|
75
|
+
- setup
|
60
76
|
extensions: []
|
61
77
|
extra_rdoc_files: []
|
62
78
|
files:
|
63
|
-
- .gitignore
|
64
|
-
- .rspec
|
65
|
-
- .travis.yml
|
79
|
+
- ".gitignore"
|
80
|
+
- ".rspec"
|
81
|
+
- ".travis.yml"
|
66
82
|
- Gemfile
|
67
83
|
- LICENSE.txt
|
68
84
|
- README.md
|
69
85
|
- Rakefile
|
86
|
+
- bin/console
|
87
|
+
- bin/setup
|
70
88
|
- lib/parametric.rb
|
71
|
-
- lib/parametric/
|
72
|
-
- lib/parametric/
|
89
|
+
- lib/parametric/block_validator.rb
|
90
|
+
- lib/parametric/context.rb
|
91
|
+
- lib/parametric/default_types.rb
|
92
|
+
- lib/parametric/dsl.rb
|
93
|
+
- lib/parametric/field.rb
|
94
|
+
- lib/parametric/field_dsl.rb
|
73
95
|
- lib/parametric/policies.rb
|
74
|
-
- lib/parametric/
|
75
|
-
- lib/parametric/
|
96
|
+
- lib/parametric/registry.rb
|
97
|
+
- lib/parametric/results.rb
|
98
|
+
- lib/parametric/schema.rb
|
76
99
|
- lib/parametric/version.rb
|
77
|
-
- lib/support/class_attribute.rb
|
78
100
|
- parametric.gemspec
|
79
|
-
- spec/
|
80
|
-
- spec/
|
101
|
+
- spec/dsl_spec.rb
|
102
|
+
- spec/field_spec.rb
|
103
|
+
- spec/policies_spec.rb
|
104
|
+
- spec/schema_spec.rb
|
105
|
+
- spec/schema_walk_spec.rb
|
81
106
|
- spec/spec_helper.rb
|
107
|
+
- spec/validators_spec.rb
|
82
108
|
homepage: ''
|
83
109
|
licenses:
|
84
110
|
- MIT
|
@@ -89,22 +115,26 @@ require_paths:
|
|
89
115
|
- lib
|
90
116
|
required_ruby_version: !ruby/object:Gem::Requirement
|
91
117
|
requirements:
|
92
|
-
- -
|
118
|
+
- - ">="
|
93
119
|
- !ruby/object:Gem::Version
|
94
120
|
version: '0'
|
95
121
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
122
|
requirements:
|
97
|
-
- -
|
123
|
+
- - ">="
|
98
124
|
- !ruby/object:Gem::Version
|
99
125
|
version: '0'
|
100
126
|
requirements: []
|
101
127
|
rubyforge_project:
|
102
|
-
rubygems_version: 2.
|
128
|
+
rubygems_version: 2.4.5
|
103
129
|
signing_key:
|
104
130
|
specification_version: 4
|
105
131
|
summary: DSL for declaring allowed parameters with options, regexp patern and default
|
106
132
|
values.
|
107
133
|
test_files:
|
108
|
-
- spec/
|
109
|
-
- spec/
|
134
|
+
- spec/dsl_spec.rb
|
135
|
+
- spec/field_spec.rb
|
136
|
+
- spec/policies_spec.rb
|
137
|
+
- spec/schema_spec.rb
|
138
|
+
- spec/schema_walk_spec.rb
|
110
139
|
- spec/spec_helper.rb
|
140
|
+
- spec/validators_spec.rb
|
data/lib/parametric/hash.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
module Parametric
|
3
|
-
class Hash
|
4
|
-
include TypedParams
|
5
|
-
include Enumerable
|
6
|
-
extend ::Forwardable
|
7
|
-
def_delegators(:params,
|
8
|
-
:[],
|
9
|
-
:[]=,
|
10
|
-
:each,
|
11
|
-
:each_value,
|
12
|
-
:each_key,
|
13
|
-
:each_pair,
|
14
|
-
:keys,
|
15
|
-
:values,
|
16
|
-
:values_at,
|
17
|
-
:fetch,
|
18
|
-
:size,
|
19
|
-
:to_hash,
|
20
|
-
:merge,
|
21
|
-
:merge!,
|
22
|
-
:replace,
|
23
|
-
:update,
|
24
|
-
:has_key?,
|
25
|
-
:key?,
|
26
|
-
:key,
|
27
|
-
:select,
|
28
|
-
:select!,
|
29
|
-
:delete,
|
30
|
-
:store,
|
31
|
-
:inspect,
|
32
|
-
:stringify_keys,
|
33
|
-
:symbolize_keys
|
34
|
-
)
|
35
|
-
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
data/lib/parametric/params.rb
DELETED
@@ -1,86 +0,0 @@
|
|
1
|
-
require 'ostruct'
|
2
|
-
require 'support/class_attribute'
|
3
|
-
module Parametric
|
4
|
-
|
5
|
-
class ParamsHash < Hash
|
6
|
-
def flat(separator = ',')
|
7
|
-
self.each_with_object({}) do |(k,v),memo|
|
8
|
-
memo[k] = Utils.value(v, separator)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
module Params
|
14
|
-
|
15
|
-
def self.included(base)
|
16
|
-
base.send(:attr_reader, :params)
|
17
|
-
base.class_attribute :_allowed_params
|
18
|
-
base._allowed_params = {}
|
19
|
-
base.extend DSL
|
20
|
-
end
|
21
|
-
|
22
|
-
def initialize(raw_params = {})
|
23
|
-
@params = _reduce(raw_params)
|
24
|
-
end
|
25
|
-
|
26
|
-
def available_params
|
27
|
-
@available_params ||= params.each_with_object(ParamsHash.new) do |(k,v),memo|
|
28
|
-
if Utils.present?(v)
|
29
|
-
memo[k] = v.respond_to?(:available_params) ? v.available_params : v
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def schema
|
35
|
-
@schema ||= params.each_with_object({}) do |(k,v),memo|
|
36
|
-
is_nested = v.kind_of?(Parametric::Hash)
|
37
|
-
attrs = self.class._allowed_params[k].dup
|
38
|
-
attrs[:value] = is_nested ? v : Utils.value(v)
|
39
|
-
attrs[:schema] = v.schema if is_nested
|
40
|
-
memo[k] = OpenStruct.new(attrs)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
protected
|
45
|
-
|
46
|
-
def _reduce(raw_params)
|
47
|
-
self.class._allowed_params.each_with_object(ParamsHash.new) do |(key,options),memo|
|
48
|
-
has_key = raw_params.respond_to?(:has_key?) && raw_params.has_key?(key)
|
49
|
-
value = has_key ? raw_params[key] : []
|
50
|
-
policy = Policies::Policy.new(value, options)
|
51
|
-
policy = policy.wrap(Policies::CoercePolicy) if options[:coerce]
|
52
|
-
policy = policy.wrap(Policies::NestedPolicy) if options[:nested]
|
53
|
-
policy = policy.wrap(Policies::MultiplePolicy) if options[:multiple]
|
54
|
-
policy = policy.wrap(Policies::OptionsPolicy) if options[:options]
|
55
|
-
policy = policy.wrap(Policies::MatchPolicy) if options[:match]
|
56
|
-
policy = policy.wrap(Policies::DefaultPolicy) if options.has_key?(:default)
|
57
|
-
policy = policy.wrap(Policies::SinglePolicy) unless options[:multiple]
|
58
|
-
memo[key] = policy.value unless options[:nullable] && !has_key
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
module DSL
|
63
|
-
|
64
|
-
# When subclasses params definitions
|
65
|
-
# we want to copy parent class definitions
|
66
|
-
# so changes in the child class
|
67
|
-
# don't mutate the parent definitions
|
68
|
-
#
|
69
|
-
def inherited(subclass)
|
70
|
-
subclass._allowed_params = self._allowed_params.dup
|
71
|
-
end
|
72
|
-
|
73
|
-
def param(field_name, label = '', opts = {}, &block)
|
74
|
-
opts[:label] = label
|
75
|
-
if block_given?
|
76
|
-
nested = Class.new(Parametric::Hash)
|
77
|
-
nested.instance_eval &block
|
78
|
-
opts[:nested] = nested
|
79
|
-
end
|
80
|
-
_allowed_params[field_name] = opts
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
end
|
85
|
-
|
86
|
-
end
|