parametric 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,23 +0,0 @@
1
- require 'parametric/params'
2
- module Parametric
3
- module TypedParams
4
- def self.included(base)
5
- base.send(:include, Params)
6
- base.extend DSL
7
- end
8
-
9
- module DSL
10
- def integer(field_name, label = '', opts = {}, &block)
11
- param(field_name, label, opts.merge(coerce: :to_i), &block)
12
- end
13
-
14
- def string(field_name, label = '', opts = {}, &block)
15
- param(field_name, label, opts.merge(coerce: :to_s), &block)
16
- end
17
-
18
- def array(field_name, label = '', opts = {}, &block)
19
- param(field_name, label, opts.merge(multiple: true), &block)
20
- end
21
- end
22
- end
23
- end
@@ -1,24 +0,0 @@
1
- module Parametric
2
- module Utils
3
- def self.value(val, separator = ',')
4
- if val.nil?
5
- ''
6
- elsif val.is_a?(Array)
7
- val.join(separator)
8
- else
9
- val
10
- end
11
- end
12
-
13
- def self.present?(value)
14
- case value
15
- when String
16
- value.strip != ''
17
- when Array, Hash
18
- value.any?
19
- else
20
- !value.nil?
21
- end
22
- end
23
- end
24
- end
@@ -1,68 +0,0 @@
1
- begin
2
- require 'active_support/core_ext/class/attribute'
3
- rescue LoadError
4
- module Kernel
5
- # Returns the object's singleton class.
6
- def singleton_class
7
- class << self
8
- self
9
- end
10
- end unless respond_to?(:singleton_class) # exists in 1.9.2
11
-
12
- # class_eval on an object acts like singleton_class.class_eval.
13
- def class_eval(*args, &block)
14
- singleton_class.class_eval(*args, &block)
15
- end
16
- end
17
-
18
- class Module
19
- def remove_possible_method(method)
20
- if method_defined?(method) || private_method_defined?(method)
21
- remove_method(method)
22
- end
23
- rescue NameError
24
- # If the requested method is defined on a superclass or included module,
25
- # method_defined? returns true but remove_method throws a NameError.
26
- # Ignore this.
27
- end
28
-
29
- def redefine_method(method, &block)
30
- remove_possible_method(method)
31
- define_method(method, &block)
32
- end
33
- end
34
-
35
- class Class
36
- def class_attribute(*attrs)
37
- attrs.each do |name|
38
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
39
- def self.#{name}() nil end
40
- def self.#{name}?() !!#{name} end
41
-
42
- def self.#{name}=(val)
43
- singleton_class.class_eval do
44
- remove_possible_method(:#{name})
45
- define_method(:#{name}) { val }
46
- end
47
-
48
- if singleton_class?
49
- class_eval do
50
- remove_possible_method(:#{name})
51
- def #{name}
52
- defined?(@#{name}) ? @#{name} : singleton_class.#{name}
53
- end
54
- end
55
- end
56
- val
57
- end
58
- RUBY
59
-
60
- end
61
- end
62
-
63
- private
64
- def singleton_class?
65
- ancestors.first != self
66
- end
67
- end
68
- end
@@ -1,90 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Parametric do
4
-
5
- describe Parametric::Params do
6
-
7
- let(:klass) do
8
- Class.new do
9
- include Parametric::Params
10
- param :name, 'User name'
11
- param :tags, 'Tags', multiple: true, default: 'defaulttag'
12
- param :lala, 'Lala' do
13
- param :foo, 'foo'
14
- end
15
- param :account, 'Account' do
16
- param :name, 'Account name'
17
- param :admin, 'Admin user' do
18
- param :name, 'User name'
19
- end
20
- param :shop, 'Shop' do
21
- param :url, 'Shop url', default: 'http://google.com'
22
- end
23
- end
24
- param :variants, 'Product variants', multiple: true do
25
- param :name, 'variant name', default: 'default'
26
- param :price
27
- param :tags, 'Variant tags', multiple: true
28
- end
29
- end
30
- end
31
-
32
- let(:subject) do
33
- klass.new({
34
- foo: 'bar',
35
- name: 'user1',
36
- account: {
37
- name: 'account1',
38
- lala: 1,
39
- admin: {
40
- name: 'Joe Bloggs'
41
- }
42
- },
43
- variants: [
44
- {name: 'red', price: 10},
45
- {price: 11, tags: 'foo,bar'}
46
- ]
47
- })
48
- end
49
-
50
- describe '#params' do
51
- it 'filters nested objects' do
52
- expect(subject.params.has_key?(:foo)).to be_false
53
- expect(subject.params[:name]).to eql('user1')
54
- expect(subject.params[:account][:name]).to eql('account1')
55
- expect(subject.params[:account].has_key?(:lala)).to be_false
56
- expect(subject.params[:account][:admin][:name]).to eql('Joe Bloggs')
57
- end
58
-
59
- it 'nullifies nested objects that were not passed' do
60
- expect(subject.params[:account].has_key?(:shop)).to be_true
61
- expect(subject.params[:account][:shop]).to be_nil
62
- end
63
-
64
- it 'filters nested :multiple into arrays of objects' do
65
- expect(subject.params[:variants].size).to eql(2)
66
- expect(subject.params[:variants][0][:name]).to eql('red')
67
- expect(subject.params[:variants][0][:price]).to eql(10)
68
- expect(subject.params[:variants][0][:tags]).to match_array([])
69
- expect(subject.params[:variants][1][:name]).to eql('default')
70
- expect(subject.params[:variants][1][:price]).to eql(11)
71
- expect(subject.params[:variants][1][:tags]).to match_array(['foo', 'bar'])
72
- end
73
- end
74
-
75
- describe '#available_params' do
76
- it 'does not include key for nested objects that were not passed' do
77
- expect(subject.available_params.has_key?(:lala)).to be_false
78
- expect(subject.available_params[:account].has_key?(:shop)).to be_false
79
- end
80
- end
81
-
82
- describe '#schema' do
83
- it 'includes nested param schemas' do
84
- expect(subject.schema[:account].schema[:name].value).to eql('account1')
85
- expect(subject.schema[:account].schema[:name].label).to eql('Account name')
86
- expect(subject.schema[:account].schema[:admin].schema[:name].value).to eql('Joe Bloggs')
87
- end
88
- end
89
- end
90
- end
@@ -1,261 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Parametric do
4
- it 'should have a version number' do
5
- Parametric::VERSION.should_not be_nil
6
- end
7
-
8
- shared_examples 'a configurable params object' do
9
- it 'ignores undeclared fields' do
10
- subject.params.has_key?(:foo).should be_false
11
- end
12
-
13
- it 'sets passed values' do
14
- subject.params[:per_page].should == 20
15
- end
16
-
17
- it 'uses defaults if no value passed' do
18
- subject.params[:page].should == 1
19
- end
20
-
21
- it 'does not set value if outside of declared options' do
22
- subject.params[:status].should == []
23
- end
24
-
25
- it 'does not set value if it does not :match' do
26
- klass.new(email: 'my@email').params[:email].should be_nil
27
- end
28
-
29
- it 'does set value if it does :match' do
30
- klass.new(email: 'my@email.com').params[:email].should == 'my@email.com'
31
- end
32
-
33
- it 'only sets value for :multiple values that :match' do
34
- klass.new(emails: 'my@email,your,her@email.com').params[:emails].should == ['her@email.com']
35
- end
36
-
37
- it 'returns :default wrapped in array if :multiple' do
38
- klass.new().params[:emails].should == ['default@email.com']
39
- end
40
-
41
- it 'turns :multiple comma-separated values into arrays' do
42
- klass.new(status: 'one,three').params[:status].should == ['one', 'three']
43
- end
44
-
45
- it 'does set value if it does :match' do
46
- klass.new(email: 'my@email.com').params[:email].should == 'my@email.com'
47
- end
48
-
49
- it ':multiple values can be arrays' do
50
- klass.new(status: ['one','three']).params[:status].should == ['one', 'three']
51
- end
52
-
53
- it 'defaults work for false values' do
54
- klass.new(email: 'my@email').params[:email].should be_nil
55
- end
56
-
57
- it 'does set value if it does :match' do
58
- klass.new(available: false).params[:available].should be_false
59
- end
60
-
61
- it 'turns :multiple separated values with custom separator into arrays' do
62
- klass.new(piped_status: 'one|three').params[:piped_status].should == ['one', 'three']
63
- end
64
-
65
- it 'does not turn non-multiple comma-separated values into arrays' do
66
- klass.new(name: 'foo,bar').params[:name].should == 'foo,bar'
67
- end
68
-
69
- it 'filters out undeclared options' do
70
- klass.new(status: 'one,three,fourteen').params[:status].should == ['one', 'three']
71
- end
72
-
73
- it 'defaults empty multiple options to empty array' do
74
- klass.new().params[:status].should == []
75
- end
76
-
77
- it 'wraps single multiple options in array' do
78
- klass.new(status: 'one').params[:status].should == ['one']
79
- end
80
-
81
- it 'does not accept comma-separated values outside of options unless :multiple == true' do
82
- klass.new(country: 'UK,CL').params[:country].should be_nil
83
- end
84
-
85
- it 'does accept single option' do
86
- klass.new(country: 'UK').params[:country].should == 'UK'
87
- end
88
-
89
- it 'does not accept single option if not in declared options' do
90
- klass.new(country: 'USA').params[:country].should be_nil
91
- end
92
-
93
- it 'does not include parameters marked as :nullable' do
94
- klass.new.params.has_key?(:nullable).should be_false
95
- end
96
- end
97
-
98
- describe 'TypedParams' do
99
- let(:klass) do
100
- Class.new do
101
- include Parametric::TypedParams
102
- string :name, 'User name'
103
- integer :page, 'page number', default: 1
104
- integer :per_page, 'items per page', default: 50
105
- array :status, 'status', options: ['one', 'two', 'three']
106
- array :piped_status, 'status with pipes', separator: '|'
107
- string :country, 'country', options: ['UK', 'CL', 'JPN']
108
- string :email, 'email', match: /\w+@\w+\.\w+/
109
- array :emails, 'emails', match: /\w+@\w+\.\w+/, default: 'default@email.com'
110
- param :nullable, 'nullable param', nullable: true
111
- end
112
- end
113
-
114
- let(:subject) { klass.new(foo: 'bar', per_page: '20', status: 'four') }
115
- it_should_behave_like 'a configurable params object'
116
-
117
- it 'does not break when value is nil' do
118
- klass = Class.new do
119
- include Parametric::TypedParams
120
- array :friends, 'friends', nullable: true do
121
- string :name, 'Name'
122
- end
123
- end
124
- klass.new(friends: nil).params[:friends].should == []
125
- klass.new(friends: []).params[:friends].should == []
126
- klass.new(friends: [{name: 'foo'}]).params[:friends].first[:name].should == 'foo'
127
- klass.new.params.has_key?(:friends).should be_false
128
- end
129
- end
130
-
131
- describe Parametric::Params do
132
-
133
- let(:klass) do
134
- Class.new do
135
- include Parametric::Params
136
- param :name, 'User name'
137
- param :page, 'page number', default: 1, coerce: :to_i
138
- param :per_page, 'items per page', default: 50, coerce: lambda{|value| value.to_i}
139
- param :status, 'status', options: ['one', 'two', 'three'], multiple: true
140
- param :piped_status, 'status with pipes', multiple: true, separator: '|'
141
- param :country, 'country', options: ['UK', 'CL', 'JPN']
142
- param :email, 'email', match: /\w+@\w+\.\w+/
143
- param :emails, 'emails', match: /\w+@\w+\.\w+/, multiple: true, default: 'default@email.com'
144
- param :available, 'available', default: true
145
- param :nullable, 'nullable param', nullable: true
146
- end
147
- end
148
-
149
- describe '#params' do
150
- let(:subject) { klass.new(foo: 'bar', per_page: 20, status: 'four') }
151
- it_should_behave_like 'a configurable params object'
152
- end
153
-
154
- describe 'subclassing' do
155
- let(:subclass){ Class.new(klass) }
156
- let(:subject){ subclass.new(foo: 'bar', per_page: 20, status: 'four') }
157
- it_should_behave_like 'a configurable params object'
158
- end
159
-
160
- describe '#available_params' do
161
- let(:subject) { klass.new(foo: 'bar', name: 'lala', per_page: 20, status: 'four', emails: 'one@email.com,two@email.com') }
162
-
163
- it 'only includes declared params with values or defaults' do
164
- subject.available_params.keys.sort.should == [:available, :emails, :name, :page, :per_page]
165
- subject.available_params[:emails].should == ['one@email.com', 'two@email.com']
166
- subject.available_params[:name].should == 'lala'
167
- subject.available_params[:per_page].should == 20
168
- subject.available_params[:page].should == 1
169
- end
170
-
171
- describe ':coerce option' do
172
- it 'accepts method_name as a symbol' do
173
- klass.new(page: '10').available_params[:page].should == 10
174
- end
175
-
176
- it 'accepts a proc' do
177
- klass.new(per_page: '10').available_params[:per_page].should == 10
178
- end
179
- end
180
-
181
- describe '#flat' do
182
- it 'joins values back' do
183
- subject.available_params.flat[:emails].should == 'one@email.com,two@email.com'
184
- end
185
- end
186
- end
187
-
188
- describe '#schema' do
189
- let(:subject) { klass.new(foo: 'bar', name: 'lala', per_page: 20, status: 'four') }
190
-
191
- it 'returns full param definitions with populated value' do
192
- regexp = /\w+@\w+\.\w+/
193
-
194
- subject.schema[:name].label.should == 'User name'
195
- subject.schema[:name].value.should == 'lala'
196
-
197
- subject.schema[:page].label.should == 'page number'
198
- subject.schema[:page].value.should == 1
199
-
200
- subject.schema[:per_page].label.should == 'items per page'
201
- subject.schema[:per_page].value.should == 20
202
-
203
- subject.schema[:status].label.should == 'status'
204
- subject.schema[:status].value.should == ''
205
- subject.schema[:status].options.should == ['one', 'two', 'three']
206
- subject.schema[:status].multiple.should be_true
207
-
208
- subject.schema[:piped_status].label.should == 'status with pipes'
209
- subject.schema[:piped_status].value.should == ''
210
- subject.schema[:piped_status].multiple.should be_true
211
-
212
- subject.schema[:country].label.should == 'country'
213
- subject.schema[:country].value.should == ''
214
- subject.schema[:country].options.should == ['UK', 'CL', 'JPN']
215
-
216
- subject.schema[:email].label.should == 'email'
217
- subject.schema[:email].value.should == ''
218
- subject.schema[:email].match.should == regexp
219
-
220
- subject.schema[:emails].label.should == 'emails'
221
- subject.schema[:emails].value.should == 'default@email.com'
222
- subject.schema[:emails].multiple.should be_true
223
- subject.schema[:emails].match.should == regexp
224
- end
225
- end
226
- end
227
-
228
- describe Parametric::Hash do
229
- let(:klass) do
230
- Class.new(Parametric::Hash) do
231
- string :name, 'User name'
232
- integer :page, 'page number', default: 1
233
- param :per_page, 'items per page', default: 50
234
- end
235
- end
236
-
237
- let(:subject) { klass.new(name: 'Ismael', page: 2) }
238
-
239
- it 'quacks like a hash' do
240
- subject[:name].should == 'Ismael'
241
- subject[:page].should == 2
242
- subject[:per_page].should == 50
243
- subject.map{|k,v| k}.sort.should == [:name, :page, :per_page]
244
- subject.keys.sort.should == [:name, :page, :per_page]
245
- subject.values.map(&:to_s).sort.should == ['2', '50', 'Ismael']
246
- subject.fetch(:page, 0).should == 2
247
- subject.fetch(:foo, 0).should == 0
248
- subject.merge(foo: 22).should == {name: 'Ismael', page: 2, per_page: 50, foo: 22}
249
- subject.select{|k,v| k == :name}.should == {name: 'Ismael'}
250
- end
251
-
252
- it 'has #available_params' do
253
- subject.available_params[:name].should == 'Ismael'
254
- end
255
-
256
- it 'has #schema' do
257
- subject.schema[:name].label.should == 'User name'
258
- subject.schema[:name].value.should == 'Ismael'
259
- end
260
- end
261
- end