parametric 0.0.4 → 0.0.5

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
2
  SHA1:
3
- metadata.gz: 2e111716f0cd0f60d10a0354e70ac7ecfea42634
4
- data.tar.gz: 2012ce66941c5a24f4bf8b3f2dc7e7ceab86311a
3
+ metadata.gz: 4d80f56c0ada8312a25dfcc6ed080b0295f17cfa
4
+ data.tar.gz: 7940d26ce661dfdd7ead181f77ead1979d62413b
5
5
  SHA512:
6
- metadata.gz: f146875b9b83e0b8e85cc89ff04dc817e731653bbda09d0a54c85db1957ef68b6b0007e52a9cda1a704069d9438a4d0b205acc6ce12ef62429f1ab316c4ad800
7
- data.tar.gz: d52634e2b6526a3c81ecef0126a6558351beb030b49f28ae6c07861d2d389d777cbbbfc710f6d32ebf55f9fe6f55b8d9fe8b7fb18b697363f20b7b49c828f767
6
+ metadata.gz: 6ae1bfae9b04299ee38aff8f5be88a15dd505a6a69fd162c2be0ee261d789a2d55795603f7536d9de30df08b5548423cfbeb2db8f05cc0ce64dbc584392d46c3
7
+ data.tar.gz: effe305919dab6e2c4d47636b2711e1ec4600532b3f1429fecfa85234f32a50894cbde7d58334a04de3724759f4948918225ca3c139e9864e3ed0c81fb3275bb
data/README.md CHANGED
@@ -124,6 +124,23 @@ search = OrdersSearch.new
124
124
  search.params[:status] # => ['closed']
125
125
  ```
126
126
 
127
+ ### :nullable fields
128
+
129
+ In same cases you won't want Parametric to provide nil or empty keys for attributes missing from the input. For example when missing keys has special meaning in your application.
130
+
131
+ In those cases you can add the `:nullable` option to said param definitions:
132
+
133
+ ```ruby
134
+ class OrdersSearch
135
+ include Parametric::Params
136
+ param :query, 'Search query. optional', nullable: true
137
+ param :tags, 'Tags', multiple: true
138
+ end
139
+
140
+ search = OrdersSearch.new({})
141
+ search.params # {tags: []}
142
+ ```
143
+
127
144
  ## `available_params`
128
145
 
129
146
  `#available_params` returns the subset of keys that were populated (including defaults). Useful for building query strings.
@@ -172,6 +189,7 @@ search.available_params[:name] # => 'Mr. Ismael'
172
189
 
173
190
  The `Parametric::TypedParams` module includes extra DSL methods to coerce values to standard Ruby types.
174
191
 
192
+ ```ruby
175
193
  class UsersSearch
176
194
  include Parametric::TypedParams
177
195
  integer :age, 'User age'
@@ -180,6 +198,7 @@ class UsersSearch
180
198
  # you can still use :coerce
181
199
  param :name, 'User name', coerce: lambda{|name| "Mr. #{name}"}
182
200
  end
201
+ ```
183
202
 
184
203
  ## Parametric::Hash
185
204
 
@@ -1,4 +1,5 @@
1
1
  require 'ostruct'
2
+ require 'support/class_attribute'
2
3
  module Parametric
3
4
 
4
5
  class ParamsHash < Hash
@@ -13,6 +14,8 @@ module Parametric
13
14
 
14
15
  def self.included(base)
15
16
  base.send(:attr_reader, :params)
17
+ base.class_attribute :_allowed_params
18
+ base._allowed_params = {}
16
19
  base.extend DSL
17
20
  end
18
21
 
@@ -42,7 +45,9 @@ module Parametric
42
45
 
43
46
  def _reduce(raw_params)
44
47
  self.class._allowed_params.each_with_object(ParamsHash.new) do |(key,options),memo|
45
- policy = Policies::Policy.new((raw_params.has_key?(key) ? raw_params[key] : []), options)
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)
46
51
  policy = policy.wrap(Policies::CoercePolicy) if options[:coerce]
47
52
  policy = policy.wrap(Policies::NestedPolicy) if options[:nested]
48
53
  policy = policy.wrap(Policies::MultiplePolicy) if options[:multiple]
@@ -50,13 +55,19 @@ module Parametric
50
55
  policy = policy.wrap(Policies::MatchPolicy) if options[:match]
51
56
  policy = policy.wrap(Policies::DefaultPolicy) if options.has_key?(:default)
52
57
  policy = policy.wrap(Policies::SinglePolicy) unless options[:multiple]
53
- memo[key] = policy.value
58
+ memo[key] = policy.value unless options[:nullable] && !has_key
54
59
  end
55
60
  end
56
61
 
57
62
  module DSL
58
- def _allowed_params
59
- @allowed_params ||= {}
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
60
71
  end
61
72
 
62
73
  def param(field_name, label = '', opts = {}, &block)
@@ -72,4 +83,4 @@ module Parametric
72
83
 
73
84
  end
74
85
 
75
- end
86
+ end
@@ -38,7 +38,7 @@ module Parametric
38
38
 
39
39
  class NestedPolicy < Policy
40
40
  def value
41
- decorated.value.map do |v|
41
+ decorated.value.find_all{|v| v.respond_to?(:has_key?)}.map do |v|
42
42
  options[:nested].new(v)
43
43
  end
44
44
  end
@@ -81,4 +81,4 @@ module Parametric
81
81
  end
82
82
 
83
83
  end
84
- end
84
+ end
@@ -1,3 +1,3 @@
1
1
  module Parametric
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -0,0 +1,68 @@
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
@@ -89,6 +89,10 @@ describe Parametric do
89
89
  it 'does not accept single option if not in declared options' do
90
90
  klass.new(country: 'USA').params[:country].should be_nil
91
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
92
96
  end
93
97
 
94
98
  describe 'TypedParams' do
@@ -103,11 +107,25 @@ describe Parametric do
103
107
  string :country, 'country', options: ['UK', 'CL', 'JPN']
104
108
  string :email, 'email', match: /\w+@\w+\.\w+/
105
109
  array :emails, 'emails', match: /\w+@\w+\.\w+/, default: 'default@email.com'
110
+ param :nullable, 'nullable param', nullable: true
106
111
  end
107
112
  end
108
113
 
109
114
  let(:subject) { klass.new(foo: 'bar', per_page: '20', status: 'four') }
110
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
111
129
  end
112
130
 
113
131
  describe Parametric::Params do
@@ -124,6 +142,7 @@ describe Parametric do
124
142
  param :email, 'email', match: /\w+@\w+\.\w+/
125
143
  param :emails, 'emails', match: /\w+@\w+\.\w+/, multiple: true, default: 'default@email.com'
126
144
  param :available, 'available', default: true
145
+ param :nullable, 'nullable param', nullable: true
127
146
  end
128
147
  end
129
148
 
@@ -132,6 +151,12 @@ describe Parametric do
132
151
  it_should_behave_like 'a configurable params object'
133
152
  end
134
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
+
135
160
  describe '#available_params' do
136
161
  let(:subject) { klass.new(foo: 'bar', name: 'lala', per_page: 20, status: 'four', emails: 'one@email.com,two@email.com') }
137
162
 
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.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ismael Celis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-19 00:00:00.000000000 Z
11
+ date: 2014-07-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -74,6 +74,7 @@ files:
74
74
  - lib/parametric/typed_params.rb
75
75
  - lib/parametric/utils.rb
76
76
  - lib/parametric/version.rb
77
+ - lib/support/class_attribute.rb
77
78
  - parametric.gemspec
78
79
  - spec/nested_params_spec.rb
79
80
  - spec/parametric_spec.rb