acfs 0.32.1.1.b275 → 0.32.1.1.b276

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,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