acfs 0.32.1.1.b275 → 0.32.1.1.b276

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NGZmNGMyYTFkNTc3MDVlNzE2OTYxN2FkZjI5YmFiYzFlZTIyMWIwNQ==
4
+ YmRiYmQxMWZhODVlMWQxMjIwYTY3NDhkMjlkM2NjNjQ2YjA0NjIwMQ==
5
5
  data.tar.gz: !binary |-
6
- MGFhMTlhN2U0MmY5NDZlZDhiYzk2ZmI1MTliY2I3MjM0OGNkY2IxMQ==
6
+ MmQ5YzdiNjJhYmM0Y2NjZDM2OWRhMzZlMjhiY2JjODM0MzY4YjdkZA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NzMyNDkzYTI4YTE5MzRkYTY0NWEzMjUwNThhYzliZjc1ZWMzNDYwNWE3N2E2
10
- Njg3YWY4NWU2OTNlNzk4MDNmNWQ1ZjdjNGM5MTE3ZmRhYjIwYTM3NDJlNWRi
11
- NzRkZjA0ZjU4OTRmMjdiMWYxOGNkNmYyOTMxMWU3YmNjNWUyOGM=
9
+ Yzk4YTk4ZGU2NGY0ZGZhNTk4YzY4MzIzZmUyMTkyODY3ZjdlNDFhMGRlNzc4
10
+ OTA1NDc4YjU3MGUwOTMwNzNmMWUyOGIyZGNiMjVlNDExMGRhZDE4NTA4NDVm
11
+ NDY0NDhkZTE1MTgyYzllMjlkMGRkODMzMGQ1Njg5N2RlYWFhYjE=
12
12
  data.tar.gz: !binary |-
13
- NDllMTVmMjU2ZmE5NjQwOGQxNWU4MGIyMTI5Yjg0ZDg4NWZmZDUxYmM1NWY3
14
- YTM5Mjc1ZTFhMDVlNTU1ZTRmOGQ4OWVlMDFkZDg3ZWJiZDFjMmI2NzBlODhj
15
- MDIwZjM5MGI5ZWQzOGU3MmFiOWFkMjBmZmFkYTM1YzkwNzZjMzc=
13
+ YjY1YjYzZjU1YjZhNGQwZGFmYTNkNDgyZWI2NTNjMzE0NWU3ZTIzOTA3Yjky
14
+ MDdhNTQ4MGZhMTJjZjgzZmRjZWQ2N2M3ZDQzMTQ0NWZkYWJiMzZjNzYzNjY4
15
+ ZThkMzAzY2U0ZWM2MDM3M2FhZWQ5NTgzMmQyOTNjYThjOWUxMmU=
@@ -3,6 +3,7 @@
3
3
  ## Next
4
4
 
5
5
  * Do not raise errors on unknown attributes by default, add :unknown option.
6
+ * Add support to store unknown attributes
6
7
 
7
8
  ## 0.32.1
8
9
 
@@ -3,6 +3,7 @@ require 'active_model'
3
3
  # @api public
4
4
  #
5
5
  module Acfs::Model
6
+ require 'acfs/model/initialization'
6
7
  require 'acfs/model/attributes'
7
8
  require 'acfs/model/dirty'
8
9
  require 'acfs/model/loadable'
@@ -24,11 +25,10 @@ module Acfs::Model
24
25
  extend ActiveModel::Translation
25
26
  include ActiveModel::Conversion
26
27
  include ActiveModel::Validations
27
-
28
- require 'acfs/model/initialization'
29
- include Initialization
30
28
  end
31
29
 
30
+ include Initialization
31
+
32
32
  include Attributes
33
33
  include Loadable
34
34
  include Persistence
@@ -27,7 +27,8 @@ module Acfs::Model
27
27
  # @see ClassMethods#attributes
28
28
  #
29
29
  def initialize(*attrs)
30
- self.write_attributes self.class.attributes, change: false
30
+ self.write_attributes self.class.attributes
31
+ reset_changes
31
32
  super
32
33
  end
33
34
 
@@ -46,7 +47,7 @@ module Acfs::Model
46
47
  # @return [ HashWithIndifferentAccess{ Symbol => Object } ] Attributes and their values.
47
48
  #
48
49
  def attributes
49
- HashWithIndifferentAccess.new self.class.attributes.keys.inject({}) { |h, k| h[k.to_sym] = public_send k; h }
50
+ @attributes ||= HashWithIndifferentAccess.new
50
51
  end
51
52
 
52
53
  # @api public
@@ -73,7 +74,7 @@ module Acfs::Model
73
74
  # @return [ Object ] Attribute value.
74
75
  #
75
76
  def read_attribute(name)
76
- instance_variable_get :"@#{name}"
77
+ self.attributes[name.to_s]
77
78
  end
78
79
 
79
80
  # @api public
@@ -96,7 +97,9 @@ module Acfs::Model
96
97
  # @see #write_attribute Delegates attribute values to `#write_attribute`.
97
98
  #
98
99
  def write_attributes(attributes, opts = {})
99
- return false unless attributes.respond_to? :each
100
+ unless attributes.respond_to?(:each) && attributes.respond_to?(:keys)
101
+ return false
102
+ end
100
103
 
101
104
  if opts.fetch(:unknown,:ignore) == :raise
102
105
  if (attributes.keys.map(&:to_s) - self.class.attributes.keys).any?
@@ -104,27 +107,39 @@ module Acfs::Model
104
107
  end
105
108
  end
106
109
 
107
- attributes = attributes.select do |k, v|
108
- self.class.attributes.keys.include? k.to_s
109
- end
110
-
111
110
  procs = {}
112
111
 
113
112
  attributes.each do |key, _|
114
113
  if attributes[key].is_a? Proc
115
114
  procs[key] = attributes[key]
116
115
  else
117
- write_attribute key, attributes[key], opts
116
+ write_local_attribute key, attributes[key], opts
118
117
  end
119
118
  end
120
119
 
121
120
  procs.each do |key, proc|
122
- write_attribute key, instance_exec(&proc), opts
121
+ write_local_attribute key, instance_exec(&proc), opts
123
122
  end
124
123
 
125
124
  true
126
125
  end
127
126
 
127
+ # @api private
128
+ #
129
+ # Check if a public getter for attribute exists that should be called to
130
+ # write it or of {#write_attribute} should be called directly. This is
131
+ # necessary as {#write_attribute} should go though setters but can also
132
+ # handle unknown attribute that will not have a generated setter method.
133
+ #
134
+ def write_local_attribute(name, value, opts = {})
135
+ method = "#{name}="
136
+ if respond_to? method, true
137
+ public_send method, value
138
+ else
139
+ write_attribute name, value, opts
140
+ end
141
+ end
142
+
128
143
  # @api public
129
144
  #
130
145
  # Write single attribute with given value. Value will be casted
@@ -135,11 +150,12 @@ module Acfs::Model
135
150
  # @raise [ ArgumentError ] If no attribute with given name is defined.
136
151
  #
137
152
  def write_attribute(name, value, opts = {})
138
- if (attr = self.class.defined_attributes[name.to_s]).nil?
139
- raise ArgumentError.new "Unknown attribute `#{name}`."
153
+ attr_type = self.class.defined_attributes[name.to_s]
154
+ if attr_type
155
+ write_raw_attribute name, attr_type.cast(value), opts
156
+ else
157
+ write_raw_attribute name, value, opts
140
158
  end
141
-
142
- write_raw_attribute name, attr.cast(value), opts
143
159
  end
144
160
 
145
161
  # @api private
@@ -152,7 +168,7 @@ module Acfs::Model
152
168
  # @param [ Object ] value Attribute value.
153
169
  #
154
170
  def write_raw_attribute(name, value, _ = {})
155
- instance_variable_set :"@#{name}", value
171
+ self.attributes[name.to_s] = value
156
172
  end
157
173
 
158
174
  module ClassMethods
@@ -24,9 +24,7 @@ module Acfs::Model
24
24
  # @param [ Hash{ Symbol => Object } ] params Attributes to set on resource.
25
25
  #
26
26
  def initialize(params = {})
27
- params.each do |attr, value|
28
- self.public_send("#{attr}=", value)
29
- end if params
27
+ self.write_attributes params if params
30
28
  end
31
29
 
32
30
  end
@@ -195,8 +195,8 @@ module Acfs
195
195
  # @see #create
196
196
  #
197
197
  def create!(data, opts = {})
198
- new.tap do |model|
199
- model.save! opts.merge data: data
198
+ new(data).tap do |model|
199
+ model.save!
200
200
  end
201
201
  end
202
202
 
@@ -218,8 +218,8 @@ module Acfs
218
218
  # @see #create!
219
219
  #
220
220
  def create(data, opts = {})
221
- model = new
222
- model.save opts.merge data: data
221
+ model = new data
222
+ model.save
223
223
  model
224
224
  end
225
225
  end
@@ -23,7 +23,13 @@ module Acfs
23
23
  params = op.full_params.stringify_keys
24
24
  data = op.data.stringify_keys
25
25
 
26
- opts[:with] == params || data == opts[:with] || (opts[:with].nil? && params.empty? && data.empty?)
26
+ return true if opts[:with] == params || data == opts[:with]
27
+ return true if (opts[:with].nil? && params.empty? && data.empty?)
28
+
29
+ return true if opts[:with].reject{|k,v|v.nil?} == params.reject{|k,v|v.nil?}
30
+ return true if opts[:with].reject{|k,v|v.nil?} == data.reject{|k,v|v.nil?}
31
+
32
+ false
27
33
  end
28
34
 
29
35
  def calls
@@ -41,6 +41,9 @@ describe Acfs::Model::Attributes do
41
41
  before do
42
42
  model.attribute :name, :string, default: 'John'
43
43
  model.attribute :age, :integer, default: 25
44
+ model.send :define_method, :name= do |name|
45
+ write_attribute :name, "The Great #{name}"
46
+ end
44
47
  end
45
48
  let(:args) { [params] }
46
49
  let(:params){ {name: 'James'} }
@@ -50,8 +53,8 @@ describe Acfs::Model::Attributes do
50
53
 
51
54
  it 'should update attributes' do
52
55
  should change(m, :attributes)
53
- .from({'name' => 'John', 'age' => 25})
54
- .to({'name' => 'James', 'age' => 25})
56
+ .from({'name' => 'The Great John', 'age' => 25})
57
+ .to({'name' => 'The Great James', 'age' => 25})
55
58
  end
56
59
 
57
60
  context 'without non-hash params' do
@@ -62,14 +65,14 @@ describe Acfs::Model::Attributes do
62
65
  end
63
66
 
64
67
  context 'with unknown attributes' do
65
- let(:params) { {name: 'James', born_at: Time.now} }
68
+ let(:params) { {name: 'James', born_at: 'today'} }
66
69
 
67
70
  it { should_not raise_error }
68
71
 
69
- it 'should update known attributes' do
72
+ it 'should update known attributes and store unknown' do
70
73
  should change(m, :attributes)
71
- .from({'name' => 'John', 'age' => 25})
72
- .to({'name' => 'James', 'age' => 25})
74
+ .from({'name' => 'The Great John', 'age' => 25})
75
+ .to({'name' => 'The Great James', 'age' => 25, 'born_at' => 'today'})
73
76
  end
74
77
 
75
78
  context 'with unknown: :raise option' do
@@ -94,13 +97,6 @@ describe Acfs::Model::Attributes do
94
97
  it 'should return default value' do
95
98
  expect(model.new.name).to be == 'John'
96
99
  end
97
-
98
- it 'should return matching ivar\'s value' do
99
- o = model.new
100
- o.instance_variable_set :@name, 'Johannes'
101
-
102
- expect(o.name).to be == 'Johannes'
103
- end
104
100
  end
105
101
 
106
102
  describe '#_setter_' do
@@ -116,13 +112,6 @@ describe Acfs::Model::Attributes do
116
112
  expect(o.name).to be == 'Paul'
117
113
  end
118
114
 
119
- it 'should set instance var' do
120
- o = model.new
121
- o.name = 'Paul'
122
-
123
- expect(o.instance_variable_get(:@name)).to be == 'Paul'
124
- end
125
-
126
115
  it 'should update attributes hash' do
127
116
  o = model.new
128
117
  o.name = 'Johannes'
@@ -13,7 +13,7 @@ describe 'Acfs::Model::Initialization' do
13
13
 
14
14
  describe '#initialize' do
15
15
  it 'should allow to set attributes with initializer' do
16
- m = model.new(name: "John")
16
+ m = model.new name: "John"
17
17
  expect(m.name).to be == "John"
18
18
  end
19
19
 
@@ -18,11 +18,11 @@ describe Acfs::Model::Persistence do
18
18
  .to_return response({ id: 5, name: 'Anon', age: 12 })
19
19
 
20
20
  stub_request(:post, 'http://users.example.org/users')
21
- .with(body: '{"name":"Idefix","age":12}')
21
+ .with(body: '{id:null,"name":"Idefix","age":12}')
22
22
  .to_return response({ id: 5, name: 'Idefix', age: 12 })
23
23
 
24
24
  stub_request(:post, 'http://users.example.org/users')
25
- .with(body: '{"age":12}')
25
+ .with(body: '{"id":null,"name":null,"age":12}')
26
26
  .to_return response({ errors: { name: [ 'required' ] }}, status: 422)
27
27
 
28
28
  @del = stub_request(:delete, 'http://users.example.org/users/1')
@@ -60,6 +60,30 @@ describe Acfs::Model::Persistence do
60
60
 
61
61
  expect(@post_stub).to have_been_requested
62
62
  end
63
+
64
+ context 'with unknown attributes' do
65
+ let!(:req) do
66
+ stub_request(:post, 'http://users.example.org/users')
67
+ .with(body: '{"id":null,"name":"Idefix","age":null,"born_at":"Berlin"}')
68
+ .to_return response({id: 5, name: 'Idefix', age: 12, wuff: 'woa'})
69
+ end
70
+ let(:model) { model_class.new name: 'Idefix', born_at: 'Berlin' }
71
+
72
+ it 'should POST to collection URL' do
73
+ model.save!
74
+ expect(req).to have_been_requested
75
+ end
76
+
77
+ it 'should still have unknown attribute' do
78
+ model.save!
79
+ expect(model.attributes).to include 'born_at' => 'Berlin'
80
+ end
81
+
82
+ it 'should include server send unknown attribute' do
83
+ model.save!
84
+ expect(model.attributes).to include 'wuff' => 'woa'
85
+ end
86
+ end
63
87
  end
64
88
  end
65
89
 
@@ -205,43 +229,56 @@ describe Acfs::Model::Persistence do
205
229
  end
206
230
 
207
231
  context 'with invalid data' do
208
- let(:data) { { age: 12 } }
232
+ let(:data) { {name: nil, age: 12} }
209
233
 
210
234
  it 'should raise an error' do
211
- expect { model_class.create! data }.to raise_error(::Acfs::InvalidResource) do |error|
212
- expect(error.errors).to be == { 'name' => %w(required) }
213
- end
235
+ expect{ model_class.create! data }.to \
236
+ raise_error(::Acfs::InvalidResource) do |error|
237
+ expect(error.errors).to be == { 'name' => %w(required) }
238
+ end
214
239
  end
215
240
  end
216
241
  end
217
242
 
218
243
  describe '.create' do
244
+ subject { model_class.create data }
245
+
219
246
  context 'with valid data' do
220
- let(:data) { { name: 'Idefix', age: 12 } }
247
+ let(:data) { {name: 'Idefix', age: 12} }
221
248
 
222
249
  it 'should create new resource' do
223
- model = model_class.create! data
224
- expect(model.name).to be == 'Idefix'
225
- expect(model.age).to be == 12
250
+ expect(subject.name).to be == 'Idefix'
251
+ expect(subject.age).to be == 12
226
252
  end
227
253
 
228
254
  it 'should be persisted' do
229
- model = model_class.create! data
230
- expect(model).to be_persisted
255
+ expect(subject).to be_persisted
231
256
  end
232
257
  end
233
258
 
234
259
  context 'with invalid data' do
235
- let(:data) { { age: 12 } }
260
+ let(:data) { {name: nil, age: 12} }
236
261
 
237
262
  it 'should return not persisted resource' do
238
- model = model_class.create data
239
- expect(model).to_not be_persisted
263
+ expect(subject).to_not be_persisted
240
264
  end
241
265
 
242
266
  it 'should contain error hash' do
243
- model = model_class.create data
244
- expect(model.errors.to_hash).to be == { name: %w(required) }
267
+ expect(subject.errors.to_hash).to eq name: %w(required)
268
+ end
269
+ end
270
+
271
+ context 'with additional data' do
272
+ let!(:req) do
273
+ stub_request(:post, 'http://users.example.org/users')
274
+ .with(body: '{"id":null,"name":"Anon","age":9,"born_at":"today"}')
275
+ .to_return response({id: 5, name: 'Anon', age: 9})
276
+ end
277
+ let(:data) { {age: 9, born_at: 'today'} }
278
+
279
+ it 'should store them in attributes' do
280
+ expect(subject.attributes).to eq 'id' => 5, 'name' => 'Anon',
281
+ 'age' => 9, 'born_at' => 'today'
245
282
  end
246
283
  end
247
284
  end
@@ -7,15 +7,16 @@ describe Acfs::Model::QueryMethods do
7
7
  context 'with single id' do
8
8
  context 'with successful response' do
9
9
  before do
10
- stub_request(:get, 'http://users.example.org/users/1').to_return response({ id: 1, name: 'Anon', age: 12 })
11
- stub_request(:get, 'http://users.example.org/users/2').to_return response({ id: 2, type: 'Customer', name: 'Clare Customer', age: 24 })
10
+ stub_request(:get, 'http://users.example.org/users/1').to_return response({id: 1, name: 'Anon', age: 12, born_at: 'Berlin'})
11
+ stub_request(:get, 'http://users.example.org/users/2').to_return response({id: 2, type: 'Customer', name: 'Clare Customer', age: 24 })
12
12
  end
13
13
 
14
14
  it 'should load a single remote resource' do
15
15
  user = model.find 1
16
16
  Acfs.run
17
17
 
18
- expect(user.attributes).to be == { id: 1, name: 'Anon', age: 12 }.stringify_keys
18
+ expect(user.attributes).to eq(
19
+ {id: 1, name: 'Anon', age: 12, born_at: 'Berlin'}.stringify_keys)
19
20
  end
20
21
 
21
22
  it 'should invoke callback after model is loaded' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acfs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.32.1.1.b275
4
+ version: 0.32.1.1.b276
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Graichen