acfs 0.22.2 → 0.23.0.b197

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,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 3c1129b462fe2105edbb850cfd19d2f1e9702e94
4
- data.tar.gz: 4aa5d1819e9be688eaba32d37d88c489a88e7888
5
- SHA512:
6
- metadata.gz: 611d2c58fec55d1c71c843a6381d9923f59c7efa03cbee128663456a8698e7cb6454cb0f0787dddfd1d8c66c8e057b19384e89426ae834524bbb581020cfffa9
7
- data.tar.gz: 1e960def103264283e2be8e4fac3e59139713ec24aaeb15463ec472351ea116aba837ecaf1bd9442b23b3dd5fdb34129643cf97eca2946d4622aac89cca6f616
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ Mjc5ZTBkMmJiN2NkNzQ5YzBmYTYwZGU5ZTUzZTNhZjUzMGM3NTdhYw==
5
+ data.tar.gz: !binary |-
6
+ ZjEyMjlkZTA5MWQwYjg0MjA3ZTFjOGI0OTgwYjczNTNkYTkzMTUwYw==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ YTU2ZTMyMzU3MjUzNzIwNWZjNTUzNDZjMDY2NTY3N2U1YWY4NTZkOTA2OWY3
10
+ MTY3NWVkNjBkOTc3OTExMTY0NjM2ODc3MDRhNzU3NzVlZDYxMjZkM2NhNjQw
11
+ OGU0MGZmNjZiYThlODAzOWQ3MmI0ZGM3MzMzYzMzMGNmMWZiNDI=
12
+ data.tar.gz: !binary |-
13
+ MzdlMzdiN2MyMjE5NTM0YWNlYjZjNWNmYWY5Zjg1NGU2YjFjNGM5MDZkZTBi
14
+ MmZiYmVhOGUzOTQxZDU1OGU2Zjg1YzIxZjAzNjk3MjY3NjIzMGVlMGE5YjNi
15
+ MmQwNjBjNTk3NDYyM2U4Y2FmMjc2ZjE1ZGZiYzRlOGE3ZjA0NGI=
data/README.md CHANGED
@@ -138,6 +138,42 @@ Acfs has basic update support using `PUT` requests:
138
138
  @user.persisted? # => true
139
139
  ```
140
140
 
141
+ ## Resource Inheritance
142
+
143
+ Acfs provides a resource inheritance similar to ActiveRecord Single Table Inheritance. If a
144
+ `type` attribute exists and is a valid subclass of your resource they will be converted
145
+ to you subclassed resources:
146
+
147
+ ```ruby
148
+ class Computer < Acfs::Resource
149
+ ...
150
+ end
151
+
152
+ class Pc < Computer end
153
+ class Mac < Computer end
154
+ ```
155
+
156
+ With the following response on `GET /computers` the collection will contain the appropriate
157
+ subclass resources:
158
+
159
+ ```json
160
+ [
161
+ { "id": 5, "type": "Computer"},
162
+ { "id": 6, "type": "Mac"},
163
+ { "id": 8, "type": "Pc"}
164
+ ]
165
+ ```
166
+
167
+ ```ruby
168
+ @computers = Computer.all
169
+
170
+ Acfs.run
171
+
172
+ @computer[0].class # => Computer
173
+ @computer[1].class # => Mac
174
+ @computer[2].class # => Pc
175
+ ```
176
+
141
177
  ## Stubbing
142
178
 
143
179
  You can stub resources in applications using an Acfs service client:
@@ -193,10 +229,6 @@ it 'should find user number one' do
193
229
  end
194
230
  ```
195
231
 
196
- ## Messaging (Experimental) [Depricated] {Removed}
197
-
198
- Removed in 0.20.0. See [jgraichen/msgr](https://github.com/jgraichen/msgr) for a Rails-like messaging solution.
199
-
200
232
  ## Roadmap
201
233
 
202
234
  * Update
@@ -224,6 +256,11 @@ Removed in 0.20.0. See [jgraichen/msgr](https://github.com/jgraichen/msgr) for a
224
256
  7. Push to the branch (`git push origin my-new-feature`)
225
257
  8. Create new Pull Request
226
258
 
259
+ ## Contributors
260
+
261
+ * [Nicolas Fricke](https://github.com/nicolas-fricke)
262
+ * [Tino Junge](https://github.com/tino-junge)
263
+
227
264
  ## License
228
265
 
229
266
  MIT License
@@ -86,5 +86,20 @@ module Acfs
86
86
  end
87
87
  end
88
88
 
89
+ # Gets raised if ressource type is no valid subclass of
90
+ # parent resource. Check if the type is set to the correct
91
+ # Acfs::Resource Name
92
+ class RessourceTypeError < Error
93
+ attr_reader :base_class
94
+ attr_reader :type_name
95
+
96
+ def initialize(opts = {})
97
+ @base_class = opts.delete :base_class
98
+ @type_name = opts.delete :type_name
99
+ opts[:message] = "Recieved ressource type #{type_name} is no subclass of #{base_class}"
100
+ super
101
+ end
102
+ end
103
+
89
104
  class RealRequestsNotAllowedError < StandardError; end
90
105
  end
@@ -125,11 +125,11 @@ module Acfs::Model
125
125
  # @raise [ ArgumentError ] If no attribute with given name is defined.
126
126
  #
127
127
  def write_attribute(name, value, opts = {})
128
- if (type = self.class.attribute_types[name.to_sym]).nil?
128
+ if (attr = self.class.defined_attributes[name.to_s]).nil?
129
129
  raise ArgumentError.new "Unknown attribute `#{name}`."
130
130
  end
131
131
 
132
- write_raw_attribute name, value.nil? ? nil : type.cast(value), opts
132
+ write_raw_attribute name, attr.cast(value), opts
133
133
  end
134
134
 
135
135
  # @api private
@@ -187,7 +187,15 @@ module Acfs::Model
187
187
  # @return [ Hash{ String => Object, Proc } ] Attributes with default values.
188
188
  #
189
189
  def attributes
190
- @attributes ||= {}.merge superclass.respond_to?(:attributes) ? superclass.attributes : {}
190
+ Hash.new.tap do |attrs|
191
+ defined_attributes.each do |key, attr|
192
+ attrs[key] = attr.default_value
193
+ end
194
+ end
195
+ end
196
+
197
+ def defined_attributes
198
+ @attributes ||= {}.merge superclass.respond_to?(:defined_attributes) ? superclass.defined_attributes : {}
191
199
  end
192
200
 
193
201
  # @api public
@@ -211,11 +219,9 @@ module Acfs::Model
211
219
  private
212
220
  def define_attribute(name, type, opts = {})
213
221
  name = name.to_s
214
- default_value = opts.has_key?(:default) ? opts[:default] : nil
215
- default_value = type.cast default_value unless default_value.is_a? Proc or default_value.nil?
222
+ attribute = type.new opts
216
223
 
217
- attributes[name] = default_value
218
- attribute_types[name.to_sym] = type
224
+ defined_attributes[name] = attribute
219
225
  define_attribute_method name
220
226
 
221
227
  self.send :define_method, name do
@@ -0,0 +1,28 @@
1
+ module Acfs::Model::Attributes
2
+
3
+ class Base
4
+ attr_reader :options
5
+
6
+ def initialize(opts = {})
7
+ @options = opts
8
+ @options.reverse_merge! allow_nil: true
9
+ end
10
+
11
+ def nil_allowed?
12
+ !!options[:allow_nil]
13
+ end
14
+
15
+ def default_value
16
+ options[:default].is_a?(Proc) ? options[:default] : cast(options[:default])
17
+ end
18
+
19
+ def cast(obj)
20
+ return nil if obj.nil? && nil_allowed?
21
+ cast_type obj
22
+ end
23
+
24
+ def cast_type(obj)
25
+ raise NotImplementedError
26
+ end
27
+ end
28
+ end
@@ -16,7 +16,7 @@ module Acfs::Model
16
16
  #
17
17
  # true, on, yes
18
18
  #
19
- module Boolean
19
+ class Boolean < Base
20
20
 
21
21
  TRUE_VALUES = %w(true on yes)
22
22
 
@@ -27,7 +27,7 @@ module Acfs::Model
27
27
  # @param [Object] obj Object to cast.
28
28
  # @return [TrueClass, FalseClass] Casted boolean.
29
29
  #
30
- def self.cast(obj)
30
+ def cast_type(obj)
31
31
  return true if obj.is_a? TrueClass
32
32
  return false if obj.is_a? FalseClass
33
33
 
@@ -1,31 +1,30 @@
1
- module Acfs::Model
2
- module Attributes
1
+ module Acfs::Model::Attributes
2
+
3
+ # @api public
4
+ #
5
+ # DateTime attribute type. Use it in your model as an attribute type:
6
+ #
7
+ # @example
8
+ # class User
9
+ # include Acfs::Model
10
+ # attribute :name, :date_time
11
+ # end
12
+ #
13
+ class DateTime < Base
3
14
 
4
15
  # @api public
5
16
  #
6
- # DateTime attribute type. Use it in your model as an attribute type:
17
+ # Cast given object to DateTime.
18
+ # Expect
7
19
  #
8
- # @example
9
- # class User
10
- # include Acfs::Model
11
- # attribute :name, :date_time
12
- # end
20
+ # @param [Object] obj Object to cast.
21
+ # @return [DateTime] Casted object as DateTime.
13
22
  #
14
- module DateTime
15
-
16
- # @api public
17
- #
18
- # Cast given object to DateTime.
19
- # Expect
20
- #
21
- # @param [Object] obj Object to cast.
22
- # @return [DateTime] Casted object as DateTime.
23
- #
24
- def self.cast(obj)
25
- return obj if obj.is_a? ::DateTime
26
- return ::DateTime.iso8601(obj.iso8601) if obj.is_a? Time or obj.is_a? Date
27
- return ::DateTime.iso8601(obj)
28
- end
23
+ def cast_type(obj)
24
+ return nil if nil_allowed? and obj.blank?
25
+ return obj if obj.is_a? ::DateTime
26
+ return ::DateTime.iso8601(obj.iso8601) if obj.is_a? Time or obj.is_a? Date
27
+ return ::DateTime.iso8601(obj)
29
28
  end
30
29
  end
31
30
  end
@@ -1,28 +1,26 @@
1
- module Acfs::Model
2
- module Attributes
1
+ module Acfs::Model::Attributes
2
+
3
+ # @api public
4
+ #
5
+ # Float attribute type. Use it in your model as an attribute type:
6
+ #
7
+ # @example
8
+ # class User
9
+ # include Acfs::Model
10
+ # attribute :name, :float
11
+ # end
12
+ #
13
+ class Float < Base
3
14
 
4
15
  # @api public
5
16
  #
6
- # Float attribute type. Use it in your model as an attribute type:
17
+ # Cast given object to float.
7
18
  #
8
- # @example
9
- # class User
10
- # include Acfs::Model
11
- # attribute :name, :float
12
- # end
19
+ # @param [Object] obj Object to cast.
20
+ # @return [Float] Casted object as float.
13
21
  #
14
- module Float
15
-
16
- # @api public
17
- #
18
- # Cast given object to float.
19
- #
20
- # @param [Object] obj Object to cast.
21
- # @return [Float] Casted object as float.
22
- #
23
- def self.cast(obj)
24
- obj.to_f
25
- end
22
+ def cast_type(obj)
23
+ obj.to_f
26
24
  end
27
25
  end
28
26
  end
@@ -1,28 +1,26 @@
1
- module Acfs::Model
2
- module Attributes
1
+ module Acfs::Model::Attributes
2
+
3
+ # @api public
4
+ #
5
+ # Integer attribute type. Use it in your model as an attribute type:
6
+ #
7
+ # @example
8
+ # class User
9
+ # include Acfs::Model
10
+ # attribute :name, :integer
11
+ # end
12
+ #
13
+ class Integer < Base
3
14
 
4
15
  # @api public
5
16
  #
6
- # Integer attribute type. Use it in your model as an attribute type:
17
+ # Cast given object to integer.
7
18
  #
8
- # @example
9
- # class User
10
- # include Acfs::Model
11
- # attribute :name, :integer
12
- # end
19
+ # @param [Object] obj Object to cast.
20
+ # @return [Fixnum] Casted object as fixnum.
13
21
  #
14
- module Integer
15
-
16
- # @api public
17
- #
18
- # Cast given object to integer.
19
- #
20
- # @param [Object] obj Object to cast.
21
- # @return [Fixnum] Casted object as fixnum.
22
- #
23
- def self.cast(obj)
24
- obj.to_i
25
- end
22
+ def cast_type(obj)
23
+ obj.to_i
26
24
  end
27
25
  end
28
26
  end
@@ -1,30 +1,28 @@
1
- module Acfs::Model
2
- module Attributes
1
+ module Acfs::Model::Attributes
2
+
3
+ # @api public
4
+ #
5
+ # List attribute type. Use it in your model as an attribute type:
6
+ #
7
+ # @example
8
+ # class User
9
+ # include Acfs::Model
10
+ # attribute :name, :list
11
+ # end
12
+ #
13
+ class List < Base
3
14
 
4
15
  # @api public
5
16
  #
6
- # List attribute type. Use it in your model as an attribute type:
17
+ # Cast given object to a list.
7
18
  #
8
- # @example
9
- # class User
10
- # include Acfs::Model
11
- # attribute :name, :list
12
- # end
19
+ # @param [Object] obj Object to cast.
20
+ # @return [Fixnum] Casted object as list.
21
+ # @raise [TypeError] If object cannot be casted to a list.
13
22
  #
14
- module List
15
-
16
- # @api public
17
- #
18
- # Cast given object to a list.
19
- #
20
- # @param [Object] obj Object to cast.
21
- # @return [Fixnum] Casted object as list.
22
- # @raise [TypeError] If object cannot be casted to a list.
23
- #
24
- def self.cast(obj)
25
- return obj.to_a if obj.respond_to? :to_a
26
- raise TypeError.new "Cannot cast #{obj.inspect} to array."
27
- end
23
+ def cast_type(obj)
24
+ return obj.to_a if obj.respond_to? :to_a
25
+ raise TypeError.new "Cannot cast #{obj.inspect} to array."
28
26
  end
29
27
  end
30
28
  end
@@ -1,28 +1,26 @@
1
- module Acfs::Model
2
- module Attributes
1
+ module Acfs::Model::Attributes
2
+
3
+ # @api public
4
+ #
5
+ # String attribute type. Use it in your model as an attribute type:
6
+ #
7
+ # @example
8
+ # class User
9
+ # include Acfs::Model
10
+ # attribute :name, :string
11
+ # end
12
+ #
13
+ class String < Base
3
14
 
4
15
  # @api public
5
16
  #
6
- # String attribute type. Use it in your model as an attribute type:
17
+ # Cast given object to string.
7
18
  #
8
- # @example
9
- # class User
10
- # include Acfs::Model
11
- # attribute :name, :string
12
- # end
19
+ # @param [Object] obj Object to cast.
20
+ # @return [String] Casted string.
13
21
  #
14
- module String
15
-
16
- # @api public
17
- #
18
- # Cast given object to string.
19
- #
20
- # @param [Object] obj Object to cast.
21
- # @return [String] Casted string.
22
- #
23
- def self.cast(obj)
24
- obj.to_s
25
- end
22
+ def cast_type(obj)
23
+ obj.to_s
26
24
  end
27
25
  end
28
26
  end
@@ -76,10 +76,7 @@ module Acfs::Model
76
76
 
77
77
  operation :list, params: params do |data|
78
78
  data.each do |obj|
79
- collection << self.new.tap do |m|
80
- m.attributes = obj
81
- m.loaded!
82
- end
79
+ collection << create_resource(obj)
83
80
  end
84
81
  collection.loaded!
85
82
  block.call collection unless block.nil?
@@ -91,14 +88,13 @@ module Acfs::Model
91
88
 
92
89
  private
93
90
  def find_single(id, opts, &block)
94
- model = self.new
91
+ model = SimpleDelegator.new self.new
95
92
 
96
93
  opts[:params] ||= {}
97
94
  opts[:params].merge!({ id: id })
98
95
 
99
96
  operation :read, opts do |data|
100
- model.attributes = data
101
- model.loaded!
97
+ model.__setobj__ create_resource data, origin: model.__getobj__
102
98
  block.call model unless block.nil?
103
99
  end
104
100
 
@@ -119,6 +115,23 @@ module Acfs::Model
119
115
  end
120
116
  end
121
117
  end
118
+
119
+ def create_resource(data, opts = {})
120
+ type = data.delete 'type'
121
+ klass = resource_class_lookup(type)
122
+ (opts[:origin].is_a?(klass) ? opts[:origin] : klass.new).tap do |m|
123
+ m.attributes = data
124
+ m.loaded!
125
+ end
126
+ end
127
+
128
+ def resource_class_lookup(type)
129
+ return self if type.nil?
130
+ klass = type.camelize.constantize
131
+ raise Acfs::RessourceTypeError, type_name: type, base_class: self unless klass <= self
132
+ klass
133
+ end
134
+
122
135
  end
123
136
  end
124
137
  end
@@ -1,8 +1,8 @@
1
1
  module Acfs
2
2
  module VERSION
3
3
  MAJOR = 0
4
- MINOR = 22
5
- PATCH = 2
4
+ MINOR = 23
5
+ PATCH = 0
6
6
  STAGE = nil
7
7
 
8
8
  STRING = [MAJOR, MINOR, PATCH, STAGE].reject(&:nil?).join('.')
@@ -2,44 +2,54 @@ require 'spec_helper'
2
2
 
3
3
  describe Acfs::Model::Attributes::DateTime do
4
4
  let(:model) { Class.new.tap { |c| c.send :include, Acfs::Model }}
5
+ let(:params) { {} }
6
+ subject { Acfs::Model::Attributes::DateTime.new params }
5
7
 
6
8
  describe 'cast' do
7
9
  it 'should return same object, if obj is already of DateTime class' do
8
10
  date_time = DateTime.now
9
- retval = Acfs::Model::Attributes::DateTime.cast(date_time)
11
+ retval = subject.cast(date_time)
10
12
  expect(retval).to be == date_time
11
13
  end
12
14
 
13
15
  it 'should return parsed object, if obj is of Time class' do
14
16
  time = Time.now
15
- retval = Acfs::Model::Attributes::DateTime.cast(time)
17
+ retval = subject.cast(time)
16
18
  expect(retval).to be == DateTime.iso8601(time.iso8601)
17
19
  end
18
20
 
19
21
  it 'should return parsed object, if obj is of Date class' do
20
22
  date = Date.today
21
- retval = Acfs::Model::Attributes::DateTime.cast(date)
23
+ retval = subject.cast(date)
22
24
  expect(retval).to be == DateTime.iso8601(date.iso8601)
23
25
  end
24
26
 
25
27
  it 'should return parsed object, if obj is of String class in ISO-8601 format' do
26
28
  date_time_string = DateTime.now.iso8601
27
- retval = Acfs::Model::Attributes::DateTime.cast(date_time_string)
29
+ retval = subject.cast(date_time_string)
28
30
  expect(retval).to be == DateTime.iso8601(date_time_string)
29
31
  end
30
32
 
31
33
  it 'should raise an error if obj is of String class not in valid ISO-8601 format' do
32
34
  malformed_string = 'qwe123'
33
35
  expect {
34
- Acfs::Model::Attributes::DateTime.cast(malformed_string)
36
+ subject.cast(malformed_string)
35
37
  }.to raise_error ArgumentError
36
38
  end
37
39
 
38
40
  it 'should raise an error if obj is of wrong class (Fixnum)' do
39
41
  fixnum = 12
40
42
  expect {
41
- Acfs::Model::Attributes::DateTime.cast(fixnum)
43
+ subject.cast(fixnum)
42
44
  }.to raise_error TypeError
43
45
  end
46
+
47
+ context 'with allow_nil option' do
48
+ let(:params) { {allow_nil: true} }
49
+
50
+ it 'should accept empty string as nil' do
51
+ expect(subject.cast('')).to eq nil
52
+ end
53
+ end
44
54
  end
45
55
  end
@@ -2,21 +2,19 @@ require 'spec_helper'
2
2
 
3
3
  describe Acfs::Model::Attributes::Float do
4
4
  let(:model) { Class.new.tap { |c| c.send :include, Acfs::Model }}
5
+ subject { Acfs::Model::Attributes::Float.new }
5
6
 
6
7
  describe 'cast' do
7
8
  it 'should return same object, if obj is already of float class' do
8
- retval = Acfs::Model::Attributes::Float.cast(1.3)
9
- expect(retval).to be == 1.3
9
+ expect(subject.cast(1.3)).to be == 1.3
10
10
  end
11
11
 
12
12
  it 'should return parsed object, if obj is of Fixnum class' do
13
- retval = Acfs::Model::Attributes::Float.cast(7)
14
- expect(retval).to be == 7.0
13
+ expect(subject.cast(7)).to be == 7.0
15
14
  end
16
15
 
17
16
  it 'should return parsed object, if obj is of String class containing a float' do
18
- retval = Acfs::Model::Attributes::Float.cast('1.7')
19
- expect(retval).to be == 1.7
17
+ expect(subject.cast('1.7')).to be == 1.7
20
18
  end
21
19
  end
22
20
  end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Acfs::Model::Attributes::List do
4
4
  let(:model) { Class.new.tap { |c| c.send :include, Acfs::Model }}
5
- subject { Acfs::Model::Attributes::List }
5
+ subject { Acfs::Model::Attributes::List.new }
6
6
 
7
7
  describe '.cast' do
8
8
  context 'with array' do
@@ -115,29 +115,42 @@ describe Acfs::Model::Attributes do
115
115
  end
116
116
  end
117
117
 
118
- describe '.attribute' do
119
- it 'should add an attribute to model attribute list' do
120
- model.send :attribute, :name, :string
118
+ describe 'class' do
119
+ describe '#attribute' do
120
+ it 'should add an attribute to model attribute list' do
121
+ model.send :attribute, :name, :string
121
122
 
122
- expect(model.attributes).to be == { :name => nil }.stringify_keys
123
- end
123
+ expect(model.attributes).to be == { :name => nil }.stringify_keys
124
+ end
124
125
 
125
- it 'should accept a default value' do
126
- model.send :attribute, :name, :string, default: 'John'
126
+ it 'should accept a default value' do
127
+ model.send :attribute, :name, :string, default: 'John'
127
128
 
128
- expect(model.attributes).to be == { :name => 'John' }.stringify_keys
129
- end
129
+ expect(model.attributes).to be == { :name => 'John' }.stringify_keys
130
+ end
130
131
 
131
- it 'should accept an symbolic type' do
132
- model.send :attribute, :age, :integer, default: '12'
132
+ it 'should accept an symbolic type' do
133
+ model.send :attribute, :age, :integer, default: '12'
133
134
 
134
- expect(model.attributes).to be == { :age => 12 }.stringify_keys
135
- end
135
+ expect(model.attributes).to be == { :age => 12 }.stringify_keys
136
+ end
137
+
138
+ it 'should accept an class type' do
139
+ model.send :attribute, :age, Acfs::Model::Attributes::Integer, default: '12'
136
140
 
137
- it 'should accept an class type' do
138
- model.send :attribute, :age, Acfs::Model::Attributes::Integer, default: '12'
141
+ expect(model.attributes).to be == { :age => 12 }.stringify_keys
142
+ end
139
143
 
140
- expect(model.attributes).to be == { :age => 12 }.stringify_keys
144
+ context 'allow nil option' do
145
+ it 'should allow nil as value' do
146
+ model.send :attribute, :updated_at, Acfs::Model::Attributes::DateTime, default: DateTime.new, allow_nil: true
147
+ resource = model.new
148
+ expect(resource.updated_at).to eq DateTime.new
149
+
150
+ resource.updated_at = ''
151
+ expect(resource.updated_at).to eq nil
152
+ end
153
+ end
141
154
  end
142
155
  end
143
156
  end
@@ -113,7 +113,7 @@ describe Acfs::Model::Persistence do
113
113
 
114
114
  it 'should be frozen after DELETE' do
115
115
  model.delete!
116
- expect(model).to be_frozen
116
+ expect(model.__getobj__).to be_frozen
117
117
  end
118
118
  end
119
119
 
@@ -127,12 +127,12 @@ describe Acfs::Model::Persistence do
127
127
  end
128
128
 
129
129
  it 'should save resource' do
130
- expect(model).to receive(:save).with({})
130
+ expect(model.__getobj__).to receive(:save).with({})
131
131
  model.update_attributes name: 'Idefix'
132
132
  end
133
133
 
134
134
  it 'should pass second hash to save' do
135
- expect(model).to receive(:save).with({ bla: 'blub' })
135
+ expect(model.__getobj__).to receive(:save).with({ bla: 'blub' })
136
136
  model.update_attributes({ name: 'Idefix' }, { bla: 'blub' })
137
137
  end
138
138
  end
@@ -147,12 +147,12 @@ describe Acfs::Model::Persistence do
147
147
  end
148
148
 
149
149
  it 'should save resource' do
150
- expect(model).to receive(:save!).with({})
150
+ expect(model.__getobj__).to receive(:save!).with({})
151
151
  model.update_attributes! name: 'Idefix'
152
152
  end
153
153
 
154
154
  it 'should pass second hash to save' do
155
- expect(model).to receive(:save!).with({ bla: 'blub' })
155
+ expect(model.__getobj__).to receive(:save!).with({ bla: 'blub' })
156
156
  model.update_attributes!({ name: 'Idefix' }, { bla: 'blub' })
157
157
  end
158
158
  end
@@ -20,7 +20,7 @@ describe Acfs::Model::QueryMethods do
20
20
  it 'should invoke callback after model is loaded' do
21
21
  proc = Proc.new { }
22
22
  proc.should_receive(:call) do |user|
23
- expect(user).to be === @user
23
+ expect(user.__getobj__).to equal @user.__getobj__
24
24
  expect(user).to be_loaded
25
25
  end
26
26
 
@@ -100,4 +100,40 @@ describe Acfs::Model::QueryMethods do
100
100
  end
101
101
  end
102
102
  end
103
+
104
+ describe '.all' do
105
+ let(:computer) { Computer }
106
+ let(:pc) { PC }
107
+ let(:mac) { Mac }
108
+ before do
109
+ stub_request(:get, 'http://computers.example.org/computers').to_return response([{ id: 1, type: 'PC' }, { id: 2, type: 'Computer' }, { id: 3, type: 'Mac' }])
110
+ end
111
+
112
+ context 'with resource type inheritance' do
113
+ it 'should create appropriate subclass resources' do
114
+ @computers = Computer.all
115
+
116
+ expect(@computers).to_not be_loaded
117
+
118
+ Acfs.run
119
+
120
+ expect(@computers).to be_loaded
121
+ expect(@computers).to have(3).items
122
+ expect(@computers[0]).to be_a PC
123
+ expect(@computers[1]).to be_a Computer
124
+ expect(@computers[2]).to be_a Mac
125
+ end
126
+
127
+ context 'with invalid type set' do
128
+ before do
129
+ stub_request(:get, 'http://computers.example.org/computers').to_return response([{ id: 1, type: 'MyUser' }, { id: 2, type: 'Computer' }, { id: 3, type: 'Mac' }])
130
+ end
131
+
132
+ it 'should raise error if type is no subclass' do
133
+ Computer.all
134
+ expect { Acfs.run }.to raise_error(Acfs::RessourceTypeError)
135
+ end
136
+ end
137
+ end
138
+ end
103
139
  end
@@ -1,6 +1,7 @@
1
1
 
2
2
  Acfs.configure do
3
3
  locate :user_service, 'http://users.example.org'
4
+ locate :computer_service, 'http://computers.example.org'
4
5
  locate :comments, 'http://comments.example.org'
5
6
  end
6
7
 
@@ -47,6 +48,26 @@ class Comment < Acfs::Resource
47
48
  attribute :text, :string
48
49
  end
49
50
 
51
+ class ComputerService < Acfs::Service
52
+ use Acfs::Middleware::MessagePackDecoder
53
+ use Acfs::Middleware::JsonDecoder
54
+ use Acfs::Middleware::JsonEncoder
55
+ end
56
+
57
+ class Computer < Acfs::Resource
58
+ service ComputerService, path: 'computers'
59
+
60
+ attribute :id, :integer
61
+ end
62
+
63
+ class PC < Computer
64
+
65
+ end
66
+
67
+ class Mac < Computer
68
+
69
+ end
70
+
50
71
  # DRAFT: Singular resource
51
72
  #class Singular < Acfs::Resource
52
73
  # service UserService, singular: true
metadata CHANGED
@@ -1,97 +1,97 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acfs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.22.2
4
+ version: 0.23.0.b197
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Graichen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-09-24 00:00:00.000000000 Z
11
+ date: 2013-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ! '>='
18
18
  - !ruby/object:Gem::Version
19
19
  version: '3.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ! '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '3.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activemodel
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ! '>='
32
32
  - !ruby/object:Gem::Version
33
33
  version: '3.1'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ! '>='
39
39
  - !ruby/object:Gem::Version
40
40
  version: '3.1'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: actionpack
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ! '>='
46
46
  - !ruby/object:Gem::Version
47
47
  version: '3.1'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ! '>='
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.1'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: multi_json
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - '>='
59
+ - - ! '>='
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - ! '>='
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: typhoeus
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - '>='
73
+ - - ! '>='
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - '>='
80
+ - - ! '>='
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rack
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - '>='
87
+ - - ! '>='
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - '>='
94
+ - - ! '>='
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
@@ -145,6 +145,7 @@ files:
145
145
  - lib/acfs/middleware/print.rb
146
146
  - lib/acfs/model.rb
147
147
  - lib/acfs/model/attributes.rb
148
+ - lib/acfs/model/attributes/base.rb
148
149
  - lib/acfs/model/attributes/boolean.rb
149
150
  - lib/acfs/model/attributes/date_time.rb
150
151
  - lib/acfs/model/attributes/float.rb
@@ -211,14 +212,14 @@ require_paths:
211
212
  - lib
212
213
  required_ruby_version: !ruby/object:Gem::Requirement
213
214
  requirements:
214
- - - '>='
215
+ - - ! '>='
215
216
  - !ruby/object:Gem::Version
216
217
  version: '0'
217
218
  required_rubygems_version: !ruby/object:Gem::Requirement
218
219
  requirements:
219
- - - '>='
220
+ - - ! '>'
220
221
  - !ruby/object:Gem::Version
221
- version: '0'
222
+ version: 1.3.1
222
223
  requirements: []
223
224
  rubyforge_project:
224
225
  rubygems_version: 2.0.3
@@ -252,4 +253,3 @@ test_files:
252
253
  - spec/spec_helper.rb
253
254
  - spec/support/response.rb
254
255
  - spec/support/service.rb
255
- has_rdoc: