requisite 0.4.1 → 0.4.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fefe3a8b4f675162d0bf7ac4b8302d84845fd9437e1e358493acf1960d677207
4
- data.tar.gz: 307e2a0d91ba1be12292e58da0dd89ad5deeed52b469e7c7c0b28b4ec4ff2b55
3
+ metadata.gz: 828a5465ab9ec59c47bda9e6ab72c34e9bdbab05536ab99ad45c3cdaec933f16
4
+ data.tar.gz: b1c387023439e720d6600310604828d672abd8dd4eabb25d0412bcec423c4529
5
5
  SHA512:
6
- metadata.gz: 341d9dcc25d67f708964dce7807b2b69e90fc11426f1190d4664506d2bf08451afabeace89adef613efba625f3c3fb6f842cc8cf3e5aa554d422db5cc8696032
7
- data.tar.gz: '03853411c5332125af440424210f55fe067d23ba4308aca4de8a8bc1a2f2d899fa246542543b00f75ef6e61d80db780f15d638d21c25081a0f01fa853549fe14'
6
+ metadata.gz: eb0c6e6e4cc10fb351e01334754196f2a65ced999a32980f416a2c226b4ea1a76a77b0a1b0523e408de9b372fc40676ab7e9482290f28d13d983947ae750a9f9
7
+ data.tar.gz: 3269605161e8264e04dc0640637a107d4db5ef079f635e31c674cc35cb409eb32ccb991a3f17bcebe4f31bedcd6616fbfb09856829a86f5a802f67f998df9ac1
data/.gitignore CHANGED
@@ -1 +1,2 @@
1
- *.gem
1
+ *.gem
2
+ Gemfile.lock
data/README.md CHANGED
@@ -38,7 +38,7 @@ class UserApiModel < Requisite::ApiModel
38
38
  attribute! :username
39
39
  attribute :real_name
40
40
  end
41
-
41
+
42
42
  # method with the name of of an attribute will be called to calculate the mapped value
43
43
  def real_name
44
44
  "#{attribute_from_model(:first_name)} #{attribute_from_model(:last_name)}"
@@ -88,10 +88,10 @@ Example:
88
88
  class UserApiModel < Requisite::ApiModel
89
89
  serialized_attributes do
90
90
  attribute :id, stringify: true
91
- attribute :custom_attributes, rename: :custom_data
91
+ attribute :custom_attributes, rename: :custom_data
92
92
  attribute :is_awesome, default: true
93
93
  attribute :awesome_score, rename: :score, stringify: true, default: 9001
94
- attribute :age, type: Fixnum,
94
+ attribute :age, type: Integer,
95
95
  attribute :tired, type: Requisite::Boolean
96
96
  end
97
97
  end
@@ -131,7 +131,7 @@ With typed hashes, only values specified with a type are permitted:
131
131
  ```ruby
132
132
  class UserApiModel < Requisite::ApiModel
133
133
  serialized_attributes do
134
- attribute :data, typed_hash: { is_awesome: Requisite::Boolean, score: Fixnum, name: String }
134
+ attribute :data, typed_hash: { is_awesome: Requisite::Boolean, score: Integer, name: String }
135
135
  end
136
136
  end
137
137
 
@@ -198,7 +198,27 @@ class ApiUser < Requisite::ApiModel
198
198
  raise IdentifierNotFoundError unless identifier
199
199
  end
200
200
  end
201
- ```
201
+ ```
202
+
203
+ #### Around each attribute
204
+
205
+ An `around_each_attribute` method can be defined to wrap each attribute fetch in a block. This can be useful for instrumenting processing on a per attribute basis.
206
+
207
+ ```ruby
208
+ class ApiUser < Requisite::ApiModel
209
+ serialized_attributes do
210
+ attribute :id, type: String
211
+ attribute :email, type: String
212
+ end
213
+
214
+ def around_each_attribute(name, &block)
215
+ start = Time.now
216
+ yield
217
+ end = Time.now
218
+ puts "Fetching #{name} took #{end - start}"
219
+ end
220
+ end
221
+ ```
202
222
 
203
223
  #### Thanks
204
224
 
@@ -4,14 +4,17 @@ module Requisite
4
4
  def attribute(name, options={})
5
5
  attribute_keys << name
6
6
  define_method(name) do
7
- resolved_name = options[:rename] || name
8
- result = self.send(:convert, resolved_name)
9
- result = self.send(:parse_typed_hash, resolved_name, options[:typed_hash]) if options[:typed_hash]
10
- result = self.send(:parse_scalar_hash, resolved_name) if options[:scalar_hash]
11
- result = self.send(:parse_typed_array, resolved_name, options[:typed_array]) if options[:typed_array]
12
- result = options[:default] if (options[:default] && empty_result?(result))
13
- raise_bad_type_if_type_mismatch(result, options[:type]) if options[:type] && result
14
- result = result.to_s if options[:stringify]
7
+ result = nil
8
+ self.send(:around_each_attribute, name) do
9
+ resolved_name = options[:rename] || name
10
+ result = self.send(:convert, resolved_name)
11
+ result = self.send(:parse_typed_hash, resolved_name, options[:typed_hash]) if options[:typed_hash]
12
+ result = self.send(:parse_scalar_hash, resolved_name) if options[:scalar_hash]
13
+ result = self.send(:parse_typed_array, resolved_name, options[:typed_array]) if options[:typed_array]
14
+ result = options[:default] if (options.key?(:default) && empty_result?(result))
15
+ raise_bad_type_if_type_mismatch(result, options[:type]) if options[:type] && result
16
+ result = result.to_s if options[:stringify]
17
+ end
15
18
  result
16
19
  end
17
20
  end
@@ -19,13 +22,16 @@ module Requisite
19
22
  def attribute!(name, options={})
20
23
  attribute_keys << name
21
24
  define_method(name) do
22
- resolved_name = options[:rename] || name
23
- result = self.send(:convert!, resolved_name)
24
- result = self.send(:parse_typed_hash, resolved_name, options[:typed_hash]) if options[:typed_hash]
25
- result = self.send(:parse_scalar_hash, resolved_name) if options[:scalar_hash]
26
- result = self.send(:parse_typed_array, resolved_name, options[:typed_array]) if options[:typed_array]
27
- result = result.to_s if options[:stringify]
28
- raise_bad_type_if_type_mismatch(result, options[:type]) if options[:type]
25
+ result = nil
26
+ self.send(:around_each_attribute, name) do
27
+ resolved_name = options[:rename] || name
28
+ result = self.send(:convert!, resolved_name)
29
+ result = self.send(:parse_typed_hash, resolved_name, options[:typed_hash]) if options[:typed_hash]
30
+ result = self.send(:parse_scalar_hash, resolved_name) if options[:scalar_hash]
31
+ result = self.send(:parse_typed_array, resolved_name, options[:typed_array]) if options[:typed_array]
32
+ result = result.to_s if options[:stringify]
33
+ raise_bad_type_if_type_mismatch(result, options[:type]) if options[:type]
34
+ end
29
35
  result
30
36
  end
31
37
  end
@@ -46,11 +52,15 @@ module Requisite
46
52
 
47
53
  private
48
54
 
55
+ def around_each_attribute(name)
56
+ yield
57
+ end
58
+
49
59
  self.singleton_class.send(:alias_method, :a, :attribute)
50
60
  self.singleton_class.send(:alias_method, :a!, :attribute!)
51
61
 
52
62
  def raise_bad_type_if_type_mismatch(value, desired_type)
53
- raise BadTypeError.new(value, desired_type) unless (value.kind_of?(desired_type)) || ((value.kind_of?(TrueClass) || value.kind_of?(TrueClass)) && desired_type == Requisite::Boolean)
63
+ raise BadTypeError.new(value, desired_type) unless (value.kind_of?(desired_type)) || ((value.kind_of?(TrueClass) || value.kind_of?(FalseClass)) && desired_type == Requisite::Boolean)
54
64
  end
55
65
 
56
66
  def raise_not_implemented_for_attribute(name)
@@ -1,3 +1,3 @@
1
1
  module Requisite
2
- VERSION = '0.4.1'
2
+ VERSION = '0.4.3'
3
3
  end
@@ -1,5 +1,4 @@
1
1
  require 'test_helper'
2
- # require_relative '../dummy_api_model'
3
2
 
4
3
  module Requisite
5
4
  class DummyApiModel < Requisite::ApiModel
@@ -11,16 +10,16 @@ module Requisite
11
10
  response = DummyApiModel.new
12
11
  def response.a; 'A'; end
13
12
  def response.b; 2; end
14
- response.to_hash.must_equal( :a => 'A', :b => 2 )
15
- response.must_respond_to :a
16
- response.must_respond_to :b
13
+ _(response.to_hash).must_equal( :a => 'A', :b => 2 )
14
+ _(response).must_respond_to :a
15
+ _(response).must_respond_to :b
17
16
  end
18
17
 
19
18
  it 'attribute provides a default implementation of calling a hash model' do
20
19
  DummyApiModel.serialized_attributes { attribute :c }
21
20
  mock = {:c => 'C'}
22
21
  response = DummyApiModel.new(mock)
23
- response.to_hash.must_equal( :c => 'C' )
22
+ _(response.to_hash).must_equal( :c => 'C' )
24
23
  end
25
24
 
26
25
  let(:params_hash) { {:c => 'C', :num => 12} }
@@ -28,89 +27,101 @@ module Requisite
28
27
  it 'attribute provides a default implementation of calling a model' do
29
28
  DummyApiModel.serialized_attributes { attribute :c }
30
29
  response = DummyApiModel.new(params_hash)
31
- response.to_hash.must_equal(:c => 'C')
30
+ _(response.to_hash).must_equal(:c => 'C')
32
31
  end
33
32
 
34
33
  it 'attribute can work with a default' do
35
34
  DummyApiModel.serialized_attributes { attribute :c, default: 'see' }
36
35
  response = DummyApiModel.new
37
- response.to_hash.must_equal(:c => 'see')
36
+ _(response.to_hash).must_equal(:c => 'see')
38
37
  end
39
38
 
40
39
  it 'ignores default if value given' do
41
40
  DummyApiModel.serialized_attributes { attribute :num, default: 0 }
42
41
  response = DummyApiModel.new(params_hash)
43
- response.to_hash.must_equal(:num => 12)
42
+ _(response.to_hash).must_equal(:num => 12)
43
+ end
44
+
45
+ it 'can set a default value of true for a boolean attribute' do
46
+ DummyApiModel.serialized_attributes { attribute :truthy_val, type: Requisite::Boolean, default: true }
47
+ response = DummyApiModel.new
48
+ _(response.to_hash).must_equal(:truthy_val => true)
49
+ end
50
+
51
+ it 'can set a default value of false for a boolean attribute' do
52
+ DummyApiModel.serialized_attributes { attribute :truthy_val, type: Requisite::Boolean, default: false }
53
+ response = DummyApiModel.new
54
+ _(response.to_hash).must_equal(:truthy_val => false)
44
55
  end
45
56
 
46
57
  it 'attribute can be set to stringify fields' do
47
58
  DummyApiModel.serialized_attributes { attribute :num, stringify: true }
48
59
  response = DummyApiModel.new(params_hash)
49
- response.to_hash.must_equal(:num => '12')
60
+ _(response.to_hash).must_equal(:num => '12')
50
61
  end
51
62
 
52
63
  it 'attribute can be set to rename fields' do
53
64
  DummyApiModel.serialized_attributes { attribute :my_num, rename: :num }
54
65
  response = DummyApiModel.new(params_hash)
55
- response.to_hash.must_equal(:my_num => 12)
66
+ _(response.to_hash).must_equal(:my_num => 12)
56
67
  end
57
68
 
58
69
  it 'attribute can assert type of a field' do
59
70
  DummyApiModel.serialized_attributes { attribute :num, type: String }
60
71
  response = DummyApiModel.new(params_hash)
61
- proc { response.to_hash }.must_raise(BadTypeError)
72
+ _(proc { response.to_hash }).must_raise(BadTypeError)
62
73
  end
63
74
 
64
75
  it 'with_type! helper raises on mismatched type' do
65
76
  model = DummyApiModel.new()
66
- proc { model.send(:with_type!, String) { 1 + 2 }}.must_raise(Requisite::BadTypeError)
77
+ _(proc { model.send(:with_type!, String) { 1 + 2 }}).must_raise(Requisite::BadTypeError)
67
78
  end
68
79
 
69
80
  it 'first_attribute_from_model helper finds first matching attriubute' do
70
81
  model = DummyApiModel.new(:oh => 12, :a => nil, :b => 'B', :c => 'C')
71
- model.send(:first_attribute_from_model, :a, :b, :c).must_equal('B')
82
+ _(model.send(:first_attribute_from_model, :a, :b, :c)).must_equal('B')
72
83
  end
73
84
 
74
85
  it 'attribute can assert type of a boolean field' do
75
86
  DummyApiModel.serialized_attributes { attribute :truthy_val, type: Requisite::Boolean }
76
87
  response = DummyApiModel.new(:truthy_val => false)
77
- response.to_hash.must_equal(:truthy_val => false)
88
+ _(response.to_hash).must_equal(:truthy_val => false)
78
89
  end
79
90
 
80
91
  it 'attribute does not include values of nil' do
81
92
  DummyApiModel.serialized_attributes { attribute :num, type: String }
82
93
  response = DummyApiModel.new({:num => nil})
83
- response.to_hash.must_equal({})
94
+ _(response.to_hash).must_equal({})
84
95
  end
85
96
 
86
97
  it 'attribute includes values of nil if permitted' do
87
98
  DummyApiModel.serialized_attributes { attribute :num, type: String }
88
99
  response = DummyApiModel.new({:num => nil})
89
- response.to_hash(show_nil: true).must_equal({:num => nil})
100
+ _(response.to_hash(show_nil: true)).must_equal({:num => nil})
90
101
  end
91
102
 
92
103
  it 'attribute can be stringified and renamed with default fields' do
93
104
  DummyApiModel.serialized_attributes { attribute :my_num, rename: :num, stringify: true, default: 22 }
94
105
  response = DummyApiModel.new
95
- response.to_hash.must_equal(:my_num => '22')
106
+ _(response.to_hash).must_equal(:my_num => '22')
96
107
  end
97
108
 
98
109
  it 'attribute can be stringified after type check' do
99
- DummyApiModel.serialized_attributes { attribute :num, stringify: true, type: Fixnum }
110
+ DummyApiModel.serialized_attributes { attribute :num, stringify: true, type: Integer }
100
111
  response = DummyApiModel.new(params_hash)
101
- response.to_hash.must_equal(:num => '12')
112
+ _(response.to_hash).must_equal(:num => '12')
102
113
  end
103
114
 
104
115
  it 'attribute type checks after rename' do
105
116
  DummyApiModel.serialized_attributes { attribute :my_num, rename: :num, type: String }
106
117
  response = DummyApiModel.new(params_hash)
107
- proc { response.to_hash }.must_raise(BadTypeError)
118
+ _(proc { response.to_hash }).must_raise(BadTypeError)
108
119
  end
109
120
 
110
121
  it 'attribute can be stringified, renamed, defaulted and have type checking on a field' do
111
122
  DummyApiModel.serialized_attributes { attribute :my_num, rename: :num, stringify: true, default: 22, type: String }
112
123
  response = DummyApiModel.new
113
- proc { response.to_hash }.must_raise(BadTypeError)
124
+ _(proc { response.to_hash }).must_raise(BadTypeError)
114
125
  end
115
126
 
116
127
  let(:invalid_params_hash) { {:d => nil} }
@@ -118,32 +129,32 @@ module Requisite
118
129
  it "attribute! raises an error if not found on model" do
119
130
  DummyApiModel.serialized_attributes { attribute! :d }
120
131
  response = DummyApiModel.new(invalid_params_hash)
121
- proc { response.to_hash }.must_raise(NotImplementedError, "'d' not found on model")
132
+ _(proc { response.to_hash }).must_raise(NotImplementedError, "'d' not found on model")
122
133
  end
123
134
 
124
135
  it 'attribute! does not raise an error if value on model is false' do
125
136
  params_hash = {:d => false}
126
137
  DummyApiModel.serialized_attributes { attribute! :d }
127
138
  response = DummyApiModel.new(params_hash)
128
- response.to_hash.must_equal({d: false})
139
+ _(response.to_hash).must_equal({d: false})
129
140
  end
130
141
 
131
142
  it 'attribute! can be set to stringify fields' do
132
143
  DummyApiModel.serialized_attributes { attribute! :num, stringify: true }
133
144
  response = DummyApiModel.new(params_hash)
134
- response.to_hash.must_equal(:num => '12')
145
+ _(response.to_hash).must_equal(:num => '12')
135
146
  end
136
147
 
137
148
  it 'attribute! can be set to rename fields' do
138
149
  DummyApiModel.serialized_attributes { attribute! :my_num, rename: :num }
139
150
  response = DummyApiModel.new(params_hash)
140
- response.to_hash.must_equal(:my_num => 12)
151
+ _(response.to_hash).must_equal(:my_num => 12)
141
152
  end
142
153
 
143
154
  it 'sets the model from a hash' do
144
155
  DummyApiModel.serialized_attributes { }
145
156
  response = DummyApiModel.new(params_hash)
146
- response.model.must_equal(params_hash)
157
+ _(response.model).must_equal(params_hash)
147
158
  end
148
159
 
149
160
  it 'sets the model from an object' do
@@ -152,54 +163,54 @@ module Requisite
152
163
  mc.b = 2
153
164
  DummyApiModel.serialized_attributes { attribute :a }
154
165
  response = DummyApiModel.new(mc)
155
- response.model.must_equal(mc)
156
- response.to_hash.must_equal(:a => 'a')
166
+ _(response.model).must_equal(mc)
167
+ _(response.to_hash).must_equal(:a => 'a')
157
168
  end
158
169
 
159
170
  it 'has alias a for attribute' do
160
171
  DummyApiModel.serialized_attributes { a :num }
161
172
  response = DummyApiModel.new(params_hash)
162
- response.to_hash.must_equal(:num => 12)
173
+ _(response.to_hash).must_equal(:num => 12)
163
174
  end
164
175
 
165
176
  it 'has alias a! for attribute!' do
166
177
  DummyApiModel.serialized_attributes { a! :num }
167
178
  response = DummyApiModel.new(params_hash)
168
- response.to_hash.must_equal(:num => 12)
179
+ _(response.to_hash).must_equal(:num => 12)
169
180
  end
170
181
 
171
182
  it 'can convert to json' do
172
183
  DummyApiModel.serialized_attributes { a! :num }
173
184
  response = DummyApiModel.new(params_hash)
174
- response.to_json.must_equal("{\"num\":12}")
185
+ _(response.to_json).must_equal("{\"num\":12}")
175
186
  end
176
187
 
177
188
  it 'drops non-listed parameters' do
178
189
  DummyApiModel.serialized_attributes { attribute :num }
179
190
  response = DummyApiModel.new({num: 12, other: 'value'})
180
- response.to_hash.must_equal(:num => 12)
191
+ _(response.to_hash).must_equal(:num => 12)
181
192
  end
182
193
 
183
194
  it 'works with ActionController::Parameters' do
184
195
  DummyApiModel.serialized_attributes { attribute :num }
185
196
  params = ActionController::Parameters.new(num: 12)
186
197
  response = DummyApiModel.new(params)
187
- response.to_hash.must_equal(:num => 12)
198
+ _(response.to_hash).must_equal(:num => 12)
188
199
  end
189
200
 
190
201
  describe 'with nested structures' do
191
202
 
192
203
  describe 'with typed arrays' do
193
204
  it 'allows arrays of one type' do
194
- DummyApiModel.serialized_attributes { attribute :ids, typed_array: Fixnum }
205
+ DummyApiModel.serialized_attributes { attribute :ids, typed_array: Integer }
195
206
  response = DummyApiModel.new({ids: [1, 2, 3]})
196
- response.to_hash.must_equal(:ids => [1, 2, 3])
207
+ _(response.to_hash).must_equal(:ids => [1, 2, 3])
197
208
  end
198
209
 
199
210
  it 'raises errors when array has a wrongly typed value' do
200
211
  DummyApiModel.serialized_attributes { attribute :ids, typed_array: Requisite::Boolean }
201
212
  response = DummyApiModel.new({ids: [true, 'value', false]})
202
- Proc.new {response.to_hash}.must_raise(BadTypeError)
213
+ _(Proc.new {response.to_hash}).must_raise(BadTypeError)
203
214
  end
204
215
  end
205
216
 
@@ -207,42 +218,42 @@ module Requisite
207
218
  it 'drops non listed parameters in nested hashes' do
208
219
  DummyApiModel.serialized_attributes { attribute :data, typed_hash: { num: Numeric, bool: Requisite::Boolean } }
209
220
  response = DummyApiModel.new({data: { num: 12, value: 'x', bool: true }})
210
- response.to_hash.must_equal(:data => { :num => 12, :bool => true })
221
+ _(response.to_hash).must_equal(:data => { :num => 12, :bool => true })
211
222
  end
212
223
 
213
224
  it 'can stringify nested hashes' do
214
225
  DummyApiModel.serialized_attributes { attribute :data, typed_hash: { num: Numeric }, stringify: true }
215
226
  response = DummyApiModel.new({data: { num: 12, value: 'x' }})
216
- response.to_hash.must_equal(:data => "{:num=>12}")
227
+ _(response.to_hash).must_equal(:data => "{:num=>12}")
217
228
  end
218
229
 
219
230
  it 'raises an error when nested hash values of the wrong type' do
220
231
  DummyApiModel.serialized_attributes { attribute :data, typed_hash: { num: Numeric } }
221
- Proc.new {DummyApiModel.new({data: { num: '12'}}).to_hash}.must_raise(BadTypeError)
232
+ _(Proc.new {DummyApiModel.new({data: { num: '12'}}).to_hash}).must_raise(BadTypeError)
222
233
  end
223
234
 
224
235
  it 'can rename param and work with nested hashes' do
225
236
  DummyApiModel.serialized_attributes { attribute :my_data, typed_hash: { num: Numeric }, rename: :data }
226
237
  response = DummyApiModel.new({data: { num: 12, value: 'x' }})
227
- response.to_hash.must_equal(:my_data => { :num => 12 })
238
+ _(response.to_hash).must_equal(:my_data => { :num => 12 })
228
239
  end
229
240
 
230
241
  it 'can set a default value for a nested hash' do
231
242
  DummyApiModel.serialized_attributes { attribute :data, typed_hash: { num: Numeric }, default: { num: 4 } }
232
243
  response = DummyApiModel.new({data: { value: 'x' }})
233
- response.to_hash.must_equal(:data => { :num => 4 })
244
+ _(response.to_hash).must_equal(:data => { :num => 4 })
234
245
  end
235
246
 
236
247
  it 'drops non listed fields with attribute!' do
237
248
  DummyApiModel.serialized_attributes { attribute! :data, typed_hash: { num: Numeric } }
238
249
  response = DummyApiModel.new({data: { num: 12, value: 'x' }})
239
- response.to_hash.must_equal(:data => { :num => 12 })
250
+ _(response.to_hash).must_equal(:data => { :num => 12 })
240
251
  end
241
252
 
242
253
  it 'attribute! does not raise an error with missing values in hash' do
243
254
  DummyApiModel.serialized_attributes { attribute! :data, typed_hash: { num: Numeric } }
244
255
  response = DummyApiModel.new({data: { value: 'x' }})
245
- response.to_hash.must_equal(:data => { })
256
+ _(response.to_hash).must_equal(:data => { })
246
257
  end
247
258
  end
248
259
 
@@ -250,19 +261,19 @@ module Requisite
250
261
  it 'should parse scalar hashes permitting anything scalar' do
251
262
  DummyApiModel.serialized_attributes { attribute :data, scalar_hash: true }
252
263
  response = DummyApiModel.new({data: { num: 12, value: 'x', :truthy => false }})
253
- response.to_hash.must_equal(:data => { :num => 12, :value => 'x', :truthy => false })
264
+ _(response.to_hash).must_equal(:data => { :num => 12, :value => 'x', :truthy => false })
254
265
  end
255
266
 
256
267
  it 'should parse a renamed scalar hash' do
257
268
  DummyApiModel.serialized_attributes { attribute :my_data, scalar_hash: true, rename: :data }
258
269
  response = DummyApiModel.new({data: { num: 12, value: 'x' }})
259
- response.to_hash.must_equal(:my_data => { :num => 12, :value => 'x' })
270
+ _(response.to_hash).must_equal(:my_data => { :num => 12, :value => 'x' })
260
271
  end
261
272
 
262
273
  it 'should stringify a scalar hash' do
263
274
  DummyApiModel.serialized_attributes { attribute :data, scalar_hash: true, stringify: true }
264
275
  response = DummyApiModel.new({data: { num: 12, value: 'x' }})
265
- response.to_hash.must_equal(:data => "{:num=>12, :value=>\"x\"}")
276
+ _(response.to_hash).must_equal(:data => "{:num=>12, :value=>\"x\"}")
266
277
  end
267
278
 
268
279
  it 'should parse scalar hashes permitting anything scalar with object' do
@@ -271,13 +282,13 @@ module Requisite
271
282
  mc.b = { num: 12, value: 'x' }
272
283
  DummyApiModel.serialized_attributes { attribute :b, scalar_hash: true }
273
284
  response = DummyApiModel.new(mc)
274
- response.to_hash.must_equal(:b => { :num => 12, :value => 'x' })
285
+ _(response.to_hash).must_equal(:b => { :num => 12, :value => 'x' })
275
286
  end
276
287
 
277
288
  it 'should fail to parse scalar hashes when non scalar values present' do
278
289
  DummyApiModel.serialized_attributes { attribute :data, scalar_hash: true }
279
- Proc.new { DummyApiModel.new({data: { num: 12, value: { nested: 'value' } }}).to_hash}.must_raise(BadTypeError)
280
- Proc.new { DummyApiModel.new({data: { num: 12, value: ['array value'] }}).to_hash}.must_raise(BadTypeError)
290
+ _(Proc.new { DummyApiModel.new({data: { num: 12, value: { nested: 'value' } }}).to_hash}).must_raise(BadTypeError)
291
+ _(Proc.new { DummyApiModel.new({data: { num: 12, value: ['array value'] }}).to_hash}).must_raise(BadTypeError)
281
292
  end
282
293
 
283
294
  it 'should fail to parse scalar hashes permitting anything scalar with object' do
@@ -286,25 +297,25 @@ module Requisite
286
297
  mc.b = { value: { nested: 'value' } }
287
298
  DummyApiModel.serialized_attributes { attribute :b, scalar_hash: true }
288
299
  response = DummyApiModel.new(mc)
289
- Proc.new { response.to_hash }.must_raise(BadTypeError)
300
+ _(Proc.new { response.to_hash }).must_raise(BadTypeError)
290
301
  end
291
302
 
292
303
  it 'can set a default value for a scalar hash' do
293
304
  DummyApiModel.serialized_attributes { attribute :data, scalar_hash: true, default: { num: 9, value: 'y' } }
294
305
  response = DummyApiModel.new({data: { }})
295
- response.to_hash.must_equal(:data => { :num => 9, :value => 'y' })
306
+ _(response.to_hash).must_equal(:data => { :num => 9, :value => 'y' })
296
307
  end
297
308
 
298
309
  it 'doesnt raise with attribute! when an empty hash passed' do
299
310
  DummyApiModel.serialized_attributes { attribute! :data, scalar_hash: true }
300
311
  response = DummyApiModel.new({data: {}})
301
- response.to_hash.must_equal(:data => {})
312
+ _(response.to_hash).must_equal(:data => {})
302
313
  end
303
314
 
304
315
  it 'raises with attribute! when nil is passed' do
305
316
  DummyApiModel.serialized_attributes { attribute! :data, scalar_hash: true }
306
317
  response = DummyApiModel.new({data: nil})
307
- Proc.new {response.to_hash}.must_raise(NotImplementedError)
318
+ _(Proc.new {response.to_hash}).must_raise(NotImplementedError)
308
319
  end
309
320
  end
310
321
  end
@@ -1,15 +1,17 @@
1
1
  require 'test_helper'
2
+ require 'benchmark'
2
3
 
3
4
  # Example Object
4
5
  class ApiUser < Requisite::ApiModel
6
+
5
7
  serialized_attributes do
6
8
  attribute :id, type: String
7
9
  attribute :user_id
8
10
  attribute :email, type: String
9
11
  attribute :name, type: String
10
- attribute :created_at, type: Fixnum
12
+ attribute :created_at, type: Integer
11
13
  attribute :last_seen_user_agent, type: String
12
- attribute :last_request_at, type: Fixnum
14
+ attribute :last_request_at, type: Integer
13
15
  attribute :unsubscribed_from_emails, type: Requisite::Boolean
14
16
  attribute :update_last_request_at, type: Requisite::Boolean
15
17
  attribute :new_session, type: Requisite::Boolean
@@ -17,7 +19,7 @@ class ApiUser < Requisite::ApiModel
17
19
  attribute :company
18
20
  attribute :companies
19
21
  end
20
-
22
+
21
23
  # Ensure that at least one identifier is passed
22
24
  def preprocess_model
23
25
  identifier = attribute_from_model(:id)
@@ -25,10 +27,30 @@ class ApiUser < Requisite::ApiModel
25
27
  identifier ||= attribute_from_model(:email)
26
28
  raise StandardError unless identifier
27
29
  end
28
-
30
+
31
+ def last_attribute_fetch_time
32
+ @last_attribute_fetch_time
33
+ end
34
+
35
+ def attribute_names
36
+ @attribute_names
37
+ end
38
+
39
+ def around_each_attribute(name)
40
+ @last_attribute_fetch_time = nil
41
+ @attribute_names ||= []
42
+
43
+ result = nil
44
+
45
+ @last_attribute_fetch_time = Benchmark.measure do
46
+ result = yield
47
+ end.total
48
+ @attribute_names << name
49
+ end
50
+
29
51
  # We want to accept someone sending `created_at` or `created` as parameters
30
52
  def created_at
31
- with_type!(Fixnum) { attribute_from_model(:created_at) || attribute_from_model(:created) }
53
+ with_type!(Integer) { attribute_from_model(:created_at) || attribute_from_model(:created) }
32
54
  end
33
55
  end
34
56
 
@@ -71,13 +93,13 @@ module Requisite
71
93
  })
72
94
  user.name.must_equal('Bob')
73
95
  end
74
-
96
+
75
97
  it 'raises an error without an identifier' do
76
98
  user_request_params = { :name => 'Bob' }
77
99
  user = ApiUser.new(user_request_params)
78
100
  proc { user.to_hash }.must_raise(StandardError)
79
101
  end
80
-
102
+
81
103
  it 'raises an error when created or created_at is not of the right type' do
82
104
  user_request_params = { :user_id => 'abcdef', :created => 'Thursday' }
83
105
  user = ApiUser.new(user_request_params)
@@ -116,7 +138,7 @@ module Requisite
116
138
  :new_attribute => 'hi'
117
139
  })
118
140
  end
119
-
141
+
120
142
  it 'accepts a user model' do
121
143
  user_model = UserModel.new
122
144
  user_model.user_id = 'abcdef'
@@ -129,7 +151,7 @@ module Requisite
129
151
  })
130
152
  user.name.must_equal('Bob')
131
153
  end
132
-
154
+
133
155
  it 'accepts a user model and renders nils if asked' do
134
156
  user_model = UserModel.new
135
157
  user_model.user_id = 'abcdef'
@@ -152,5 +174,16 @@ module Requisite
152
174
  })
153
175
  user.name.must_equal('Bob')
154
176
  end
177
+
178
+ it 'calls around_each_attribute for each attribute' do
179
+ user_model = UserModel.new
180
+ user_model.user_id = 'abcdef'
181
+ user = ApiUser.new(user_model)
182
+
183
+ user.to_hash(show_nil: true)
184
+
185
+ user.attribute_names.must_equal [:id, :user_id, :email, :name, :last_seen_user_agent, :last_request_at, :unsubscribed_from_emails, :update_last_request_at, :new_session, :custom_data, :company, :companies]
186
+ user.last_attribute_fetch_time.must_be :>, 0
187
+ end
155
188
  end
156
189
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: requisite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Osler
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-06 00:00:00.000000000 Z
11
+ date: 2025-03-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -82,7 +82,7 @@ homepage: https://www.intercom.io
82
82
  licenses:
83
83
  - Apache License Version 2.0
84
84
  metadata: {}
85
- post_install_message:
85
+ post_install_message:
86
86
  rdoc_options: []
87
87
  require_paths:
88
88
  - lib
@@ -97,9 +97,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
97
97
  - !ruby/object:Gem::Version
98
98
  version: '0'
99
99
  requirements: []
100
- rubyforge_project:
101
- rubygems_version: 2.7.6
102
- signing_key:
100
+ rubygems_version: 3.5.22
101
+ signing_key:
103
102
  specification_version: 4
104
103
  summary: Strongly defined models for HTTP APIs
105
104
  test_files: