acfs 0.19.0 → 0.20.0.dev.b181

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: 8a9569aa6a00b3a3008156d94d8c3dc615ba3409
4
- data.tar.gz: d8ef1f72e0e3a4017f700d5d2337b5c0e0ad9a1e
3
+ metadata.gz: e3f5f8551e427e98b7157ffd324322fa317f7dfe
4
+ data.tar.gz: dca0624971e5e12a77797a23d27c3589cf174bfd
5
5
  SHA512:
6
- metadata.gz: 03e1f3a9bb3f5fbc709ef673609f9c2a23e8628fde802bb222599fbc9512c2f2097b74339901b8dc1b2541417a4ca9ab12320bade59d6ca3e1189017a6da6827
7
- data.tar.gz: 68f1811f3a68294feb3493766866d811a31760a3638a3e41661935a5b4165e0c7d2b87ab5998f40a7ca6525f50bc93f02d8ce09cca7705b66b3873ecd22fe694
6
+ metadata.gz: 43ee05317185cdaea86d606d5c8931b9d256843e0c90cffdc9714a12b9d71d6c057758d31bf5b12bea5a1a6f22551034b82a3c3a1c8b43af363857e7fec5f355
7
+ data.tar.gz: c84de5ab31435e1121c47b4c781584c286099e2465a2a82b24327576b0f6f7e184bec53def77359a5668e261f53cd56b3b94d0f54cdd1c984aced461467045ba
data/.travis.yml CHANGED
@@ -1,15 +1,21 @@
1
1
  language: ruby
2
2
  bundler_args: --without development
3
3
  rvm:
4
- - 2.0.0
5
- - 1.9.3
6
- - jruby
7
- - rbx-19mode
8
-
4
+ - 2.0.0
5
+ - 1.9.3
6
+ - jruby
7
+ - rbx-19mode
9
8
  gemfile:
10
- - gemfiles/Gemfile.rails-3-1
11
- - gemfiles/Gemfile.rails-3-2
12
- - gemfiles/Gemfile.rails-4-0
13
-
9
+ - gemfiles/Gemfile.rails-3-1
10
+ - gemfiles/Gemfile.rails-3-2
11
+ - gemfiles/Gemfile.rails-4-0
14
12
  services:
15
- - rabbitmq
13
+ - rabbitmq
14
+ deploy:
15
+ provider: rubygems
16
+ api_key:
17
+ secure: gNudZK0JaRRweudmkpdkJjUMydItTSW5cXjpYdYCfahqd/cD0xPjxotr2TCHrJibfVauoT/PytbQWcP3jnOYytp6oS0up5Y+uKpGmbqVYx/rZvShWALszcBs71lUh/IZpDXNHc+yo/01HCn10/uQUFRtrjWgMwHtHxXb09xE4wQ=
18
+ gem: acfs
19
+ on:
20
+ branch: master
21
+ repo: jgraichen/acfs
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.21.0.dev
4
+
5
+ * Add update_attributes
6
+ * Add validation check to `save` method
7
+ * Inherit attributes to subclasses
8
+
9
+ ## 0.20.0
10
+
11
+ * Remove messaging
12
+ * Introduce `Acfs::Resource`
13
+
3
14
  ## 0.19.0
4
15
 
5
16
  * Add support for DateTime and Float attribute types
data/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![Coverage Status](https://coveralls.io/repos/jgraichen/acfs/badge.png?branch=master)](https://coveralls.io/r/jgraichen/acfs)
6
6
  [![Code Climate](https://codeclimate.com/github/jgraichen/acfs.png)](https://codeclimate.com/github/jgraichen/acfs)
7
7
  [![Dependency Status](https://gemnasium.com/jgraichen/acfs.png)](https://gemnasium.com/jgraichen/acfs)
8
- [![RubyDoc Documentation](https://raw.github.com/jgraichen/acfs/master/rubydoc.png)](http://rubydoc.info/github/jgraichen/acfs/master/frames)
8
+ [![RubyDoc Documentation](https://raw.github.com/jgraichen/shields/master/rubydoc.png)](http://rubydoc.info/github/jgraichen/acfs/master/frames)
9
9
 
10
10
  Acfs is a library to develop API client libraries for single services within a larger service oriented application.
11
11
 
@@ -46,8 +46,7 @@ end
46
46
  This specifies where the `UserService` is located. You can now create some models representing resources served by the `UserService`.
47
47
 
48
48
  ```ruby
49
- class User
50
- include Acfs::Model
49
+ class User < Acfs::Resource
51
50
  service UserService # Associate `User` model with `UserService`.
52
51
 
53
52
  # Define model attributes and types
@@ -194,37 +193,9 @@ it 'should find user number one' do
194
193
  end
195
194
  ```
196
195
 
197
- ## Messaging (Experimental)
196
+ ## Messaging (Experimental) [Depricated] {Removed}
198
197
 
199
- Acfs 0.17.0 includes experimental messaging support using RabbitMQ. You can create receivers for messages as well as send custom messages. Messages can be high level structures (like hashes) and will be packed into MessagePack.
200
-
201
- Create your custom receivers e.g. in `app/receivers`:
202
-
203
- ```ruby
204
- # app/receivers/my_receiver.rb
205
-
206
- class MyReceiver
207
- include Acfs::Messaging::Receiver
208
- route "my.#" # Specify routing key for receiving messages
209
-
210
- def receive(delivery_info, metadata, payload)
211
- puts payload.inspect
212
- # more foo...
213
- end
214
- end
215
- ```
216
-
217
- Make sure the receivers get loaded by placing an initializer in you app that require all receivers. You need to do this manually but it will be automated in some future release.
218
-
219
- You can now send messages by calling the `publish` method directly:
220
-
221
- ```ruby
222
- Acfs::Messaging::Client.instance.publish "my.message", { message: "Hi!" }
223
- ```
224
-
225
- This will invoke `#receive` on an instance of your receiver class.
226
-
227
- Be aware that messaging is still experimental and *will* change in future releases.
198
+ Removed in 0.20.0. See [jgraichen/msgr](https://github.com/jgraichen/msgr) for a Rails-like messaging solution.
228
199
 
229
200
  ## Roadmap
230
201
 
data/acfs.gemspec CHANGED
@@ -23,8 +23,9 @@ Gem::Specification.new do |spec|
23
23
  spec.add_runtime_dependency 'actionpack', '>= 3.1'
24
24
  spec.add_runtime_dependency 'multi_json'
25
25
  spec.add_runtime_dependency 'typhoeus'
26
- spec.add_runtime_dependency 'bunny', '~> 0.9.0'
27
26
  spec.add_runtime_dependency 'rack'
28
27
 
29
28
  spec.add_development_dependency 'bundler', '~> 1.3'
29
+
30
+ spec.version = "#{spec.version}.b#{ENV['TRAVIS_BUILD_NUMBER']}" if ENV['TRAVIS_BUILD_NUMBER']
30
31
  end
data/lib/acfs.rb CHANGED
@@ -4,37 +4,30 @@ require 'active_support/core_ext/class'
4
4
  require 'active_support/core_ext/string'
5
5
  require 'active_support/core_ext/module'
6
6
 
7
- require 'acfs/version'
8
- require 'acfs/errors'
9
- require 'acfs/global'
10
-
11
- require 'acfs/messaging'
12
-
13
7
  module Acfs
14
8
  extend ActiveSupport::Autoload
9
+ require 'acfs/version'
10
+ require 'acfs/errors'
11
+ require 'acfs/global'
12
+
13
+ require 'acfs/collection'
14
+ require 'acfs/configuration'
15
+ require 'acfs/model'
16
+ require 'acfs/operation'
17
+ require 'acfs/request'
18
+ require 'acfs/resource'
19
+ require 'acfs/response'
20
+ require 'acfs/runner'
21
+ require 'acfs/service'
22
+
15
23
  extend Global
16
24
 
17
- autoload :Collection
18
- autoload :Model
19
- autoload :Request
20
- autoload :Response
21
- autoload :Service
22
25
  autoload :Stub
23
- autoload :Operation
24
- autoload :Runner
25
- autoload :Configuration
26
-
27
- module Messaging
28
- extend ActiveSupport::Autoload
29
-
30
- autoload :Client
31
- autoload :Receiver
32
- end
33
26
 
34
27
  module Middleware
35
28
  extend ActiveSupport::Autoload
29
+ require 'acfs/middleware/base'
36
30
 
37
- autoload :Base
38
31
  autoload :Print
39
32
  autoload :Logger
40
33
  autoload :JsonDecoder
@@ -44,10 +37,8 @@ module Acfs
44
37
  end
45
38
 
46
39
  module Adapter
47
- extend ActiveSupport::Autoload
48
-
49
- autoload :Base
50
- autoload :Typhoeus
40
+ require 'acfs/adapter/base'
41
+ require 'acfs/adapter/typhoeus'
51
42
  end
52
43
  end
53
44
 
data/lib/acfs/errors.rb CHANGED
@@ -3,46 +3,52 @@ module Acfs
3
3
  # Acfs base error.
4
4
  #
5
5
  class Error < StandardError
6
+ def initialize(opts = {}, message = nil)
7
+ opts.merge! message: message if message
8
+ super opts[:message]
9
+ end
6
10
  end
7
11
 
8
12
  # Response error containing the responsible response object.
9
13
  #
10
14
  class ErroneousResponse < Error
11
- attr_accessor :response
15
+ attr_reader :response
12
16
 
13
- def initialize(data = {})
14
- self.response = data[:response]
15
- message = ''
16
- message << "Received erroneous response: #{response.code}"
17
- if response.data
18
- message << "\n with content:\n "
19
- message << response.data.map{|k,v| "#{k.inspect}: #{v.inspect}"}.join("\n ")
20
- end
21
- if response.headers.any?
22
- message << "\n with headers:\n "
23
- message << response.headers.map{|k,v| "#{k}: #{v}"}.join("\n ")
24
- end
25
- message << "\nbased on request: #{response.request.method.upcase} #{response.request.url} #{response.request.format}"
26
- if response.request.data
27
- message << "\n with content:\n "
28
- message << response.request.data.map{|k,v| "#{k.inspect}: #{v.inspect}"}.join("\n ")
17
+ def initialize(opts = {})
18
+ @response = opts[:response]
19
+ message = 'Received erroneous response'
20
+ if response
21
+ message << ": #{response.code}"
22
+ if response.data
23
+ message << "\n with content:\n "
24
+ message << response.data.map{|k,v| "#{k.inspect}: #{v.inspect}"}.join("\n ")
25
+ end
26
+ if response.headers.any?
27
+ message << "\n with headers:\n "
28
+ message << response.headers.map{|k,v| "#{k}: #{v}"}.join("\n ")
29
+ end
30
+ message << "\nbased on request: #{response.request.method.upcase} #{response.request.url} #{response.request.format}"
31
+ if response.request.data
32
+ message << "\n with content:\n "
33
+ message << response.request.data.map{|k,v| "#{k.inspect}: #{v.inspect}"}.join("\n ")
34
+ end
35
+ if response.request.headers.any?
36
+ message << "\n with headers:\n "
37
+ message << response.request.headers.map{|k,v| "#{k}: #{v}"}.join("\n ")
38
+ end
29
39
  end
30
- if response.request.headers.any?
31
- message << "\n with headers:\n "
32
- message << response.request.headers.map{|k,v| "#{k}: #{v}"}.join("\n ")
33
- end
34
- super message
40
+ super opts, message
35
41
  end
36
42
  end
37
43
 
38
44
  class AmbiguousStubError < Error
39
45
  attr_reader :stubs, :operation
40
46
 
41
- def initialize(stubs, operation)
42
- @stubs = stubs
43
- @operation = operation
47
+ def initialize(opts = {})
48
+ @stubs = opts.delete :stubs
49
+ @operation = opts.delete :operation
44
50
 
45
- super 'Ambiguous stubs.'
51
+ super opts, 'Ambiguous stubs.'
46
52
  end
47
53
 
48
54
  end
@@ -53,10 +59,27 @@ module Acfs
53
59
  end
54
60
 
55
61
  class InvalidResource < ErroneousResponse
56
- attr_accessor :errors
62
+ attr_reader :errors, :resource
63
+
64
+ def initialize(opts = {})
65
+ @errors = opts.delete :errors
66
+ @resource = opts.delete :resource
67
+ super
68
+ end
69
+ end
70
+
71
+ # A ResourceNotLoaded error will be thrown when calling some
72
+ # modifing methods on not loaded resources as it is usally
73
+ # unwanted to call e.g. `update_attributes` on a not loaded
74
+ # resource.
75
+ # Correct solution is to first run `Acfs.run` to fetch the
76
+ # resource and then update the resource.
77
+ #
78
+ class ResourceNotLoaded < Error
79
+ attr_reader :resource
57
80
 
58
- def initialize(data)
59
- self.errors = data[:errors]
81
+ def initialize(opts = {})
82
+ @resource = opts.delete :resource
60
83
  super
61
84
  end
62
85
  end
data/lib/acfs/model.rb CHANGED
@@ -1,44 +1,43 @@
1
1
  require 'active_model'
2
2
 
3
- require 'acfs/model/attributes'
4
- require 'acfs/model/dirty'
5
- require 'acfs/model/loadable'
6
- require 'acfs/model/locatable'
7
- require 'acfs/model/persistence'
8
- require 'acfs/model/operational'
9
- require 'acfs/model/query_methods'
10
- require 'acfs/model/relations'
11
- require 'acfs/model/service'
3
+ # @api public
4
+ #
5
+ module Acfs::Model
6
+ require 'acfs/model/attributes'
7
+ require 'acfs/model/dirty'
8
+ require 'acfs/model/loadable'
9
+ require 'acfs/model/locatable'
10
+ require 'acfs/model/operational'
11
+ require 'acfs/model/persistence'
12
+ require 'acfs/model/query_methods'
13
+ require 'acfs/model/relations'
14
+ require 'acfs/model/service'
15
+ require 'acfs/model/validation'
12
16
 
13
- module Acfs
17
+ extend ActiveSupport::Concern
14
18
 
15
- # @api public
16
- #
17
- module Model
18
- extend ActiveSupport::Concern
19
+ included do
20
+ if ActiveModel::VERSION::MAJOR >= 4
21
+ include ActiveModel::Model
22
+ else
23
+ extend ActiveModel::Naming
24
+ extend ActiveModel::Translation
25
+ include ActiveModel::Conversion
26
+ include ActiveModel::Validations
19
27
 
20
- included do
21
- if ActiveModel::VERSION::MAJOR >= 4
22
- include ActiveModel::Model
23
- else
24
- extend ActiveModel::Naming
25
- extend ActiveModel::Translation
26
- include ActiveModel::Conversion
27
- include ActiveModel::Validations
28
-
29
- require 'acfs/model/initialization'
30
- include Model::Initialization
31
- end
32
-
33
- include Model::Attributes
34
- include Model::Loadable
35
- include Model::Persistence
36
- include Model::Locatable
37
- include Model::Operational
38
- include Model::QueryMethods
39
- include Model::Relations
40
- include Model::Service
41
- include Model::Dirty
28
+ require 'acfs/model/initialization'
29
+ include Initialization
42
30
  end
31
+
32
+ include Attributes
33
+ include Loadable
34
+ include Persistence
35
+ include Locatable
36
+ include Operational
37
+ include QueryMethods
38
+ include Relations
39
+ include Service
40
+ include Dirty
41
+ include Validation
43
42
  end
44
43
  end
@@ -187,7 +187,7 @@ module Acfs::Model
187
187
  # @return [ Hash{ String => Object, Proc } ] Attributes with default values.
188
188
  #
189
189
  def attributes
190
- @attributes ||= {}
190
+ @attributes ||= {}.merge superclass.respond_to?(:attributes) ? superclass.attributes : {}
191
191
  end
192
192
 
193
193
  # @api public
@@ -205,7 +205,7 @@ module Acfs::Model
205
205
  # @return [ Hash{ Symbol => Class } ] Attributes and their types.
206
206
  #
207
207
  def attribute_types
208
- @attribute_types ||= {}
208
+ @attribute_types ||= {}.merge superclass.respond_to?(:attribute_types) ? superclass.attribute_types : {}
209
209
  end
210
210
 
211
211
  private
@@ -26,7 +26,7 @@ module Acfs
26
26
  # user2.save
27
27
  # user2.persisted? # => true
28
28
  #
29
- # @return [TrueClass, FalseClass] True if resource has no changes and is not newly created, false otherwise.
29
+ # @return [Boolean] True if resource has no changes and is not newly created, false otherwise.
30
30
  #
31
31
  def persisted?
32
32
  !new? && !changed?
@@ -36,7 +36,7 @@ module Acfs
36
36
  #
37
37
  # Return true if model is a new record and was not saved yet.
38
38
  #
39
- # @return [TrueClass, FalseClass] True if resource is newly created, false otherwise.
39
+ # @return [Boolean] True if resource is newly created, false otherwise.
40
40
  #
41
41
  def new?
42
42
  read_attribute(:id).nil?
@@ -52,7 +52,7 @@ module Acfs
52
52
  #
53
53
  # Saving a resource is a synchronous operation.
54
54
  #
55
- # @return [TrueClass, FalseClass] True if save operation was successful, false otherwise.
55
+ # @return [Boolean] True if save operation was successful, false otherwise.
56
56
  # @see #save! See #save! for available options.
57
57
  #
58
58
  def save(*args)
@@ -79,8 +79,6 @@ module Acfs
79
79
  # @see #save
80
80
  #
81
81
  def save!(opts = {})
82
- #raise ::Acfs::InvalidResource errors: errors.to_a unless valid?
83
-
84
82
  opts[:data] = attributes unless opts[:data]
85
83
 
86
84
  operation (new? ? :create : :update), opts do |data|
@@ -88,13 +86,60 @@ module Acfs
88
86
  end
89
87
  end
90
88
 
89
+ # @api public
90
+ #
91
+ # Update attributes with given data and save resource.
92
+ #
93
+ # Saving a resource is a synchronous operation.
94
+ #
95
+ # @param [Hash] attrs Hash with attributes to write.
96
+ # @param [Hash] opts Options passed to `save`.
97
+ #
98
+ # @return [Boolean] True if save operation was successful, false otherwise.
99
+ #
100
+ # @see #save
101
+ # @see #attributes=
102
+ # @see #update_attributes!
103
+ #
104
+ def update_attributes(attrs, opts = {})
105
+ check_loaded! opts
106
+
107
+ self.attributes = attrs
108
+ save opts
109
+ end
110
+
111
+ # @api public
112
+ #
113
+ # Update attributes with given data and save resource.
114
+ #
115
+ # Saving a resource is a synchronous operation.
116
+ #
117
+ # @param [Hash] attrs Hash with attributes to write.
118
+ # @param [Hash] opts Options passed to `save!`.
119
+ #
120
+ # @raise [Acfs::InvalidResource]
121
+ # If remote services respond with 422 response. Will fill errors with data from response
122
+ # @raise [Acfs::ErroneousResponse]
123
+ # If remote service respond with not successful response.
124
+ #
125
+ # @see #save!
126
+ # @see #attributes=
127
+ # @see #update_attributes
128
+ #
129
+ def update_attributes!(attrs, opts = {})
130
+ check_loaded! opts
131
+
132
+ self.attributes = attrs
133
+ save! opts
134
+ end
135
+
91
136
  # @api public
92
137
  #
93
138
  # Destroy resource by sending a DELETE request.
94
139
  #
95
140
  # Deleting a resource is a synchronous operation.
96
141
  #
97
- # @return [TrueClass, FalseClass]
142
+ # @return [Boolean]
98
143
  # @see #delete!
99
144
  #
100
145
  def delete(opts = {})
@@ -186,6 +231,10 @@ module Acfs
186
231
  self.attributes = data
187
232
  loaded!
188
233
  end
234
+
235
+ def check_loaded!(opts = {})
236
+ raise ResourceNotLoaded, resource: self unless loaded? or opts[:force]
237
+ end
189
238
  end
190
239
  end
191
240
  end
@@ -0,0 +1,13 @@
1
+ module Acfs::Model
2
+
3
+ #
4
+ #
5
+ module Validation
6
+
7
+ def save!(*_)
8
+ raise ::Acfs::InvalidResource.new resource: self, errors: errors.to_a unless valid? :save
9
+
10
+ super
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ module Acfs
2
+
3
+ # Transition stub for `Model`.
4
+ #
5
+ class Resource
6
+ include Acfs::Model
7
+ end
8
+ end
data/lib/acfs/stub.rb CHANGED
@@ -98,7 +98,7 @@ module Acfs
98
98
 
99
99
  accepted_stubs = stubs.select { |stub| stub.accept? op }
100
100
 
101
- raise AmbiguousStubError.new accepted_stubs, op if accepted_stubs.size > 1
101
+ raise AmbiguousStubError.new stubs: accepted_stubs, operation: op if accepted_stubs.size > 1
102
102
 
103
103
  accepted_stubs.first
104
104
  end
data/lib/acfs/version.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  module Acfs
2
2
  module VERSION
3
3
  MAJOR = 0
4
- MINOR = 19
4
+ MINOR = 20
5
5
  PATCH = 0
6
- STAGE = nil
6
+ STAGE = :dev
7
7
 
8
8
  STRING = [MAJOR, MINOR, PATCH, STAGE].reject(&:nil?).join('.')
9
9
 
@@ -109,7 +109,7 @@ describe Acfs::Model::Attributes do
109
109
 
110
110
  it 'should cast values' do
111
111
  o = model.new
112
- o.age = "28"
112
+ o.age = '28'
113
113
 
114
114
  expect(o.age).to be == 28
115
115
  end
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Acfs::Model::Persistence do
4
4
  let(:model_class) { MyUser }
5
5
  before do
6
- @get_stub = stub_request(:get, 'http://users.example.org/users/1').to_return response({ id: 1, name: "Anon", age: 12 })
6
+ @get_stub = stub_request(:get, 'http://users.example.org/users/1').to_return response({ id: 1, name: 'Anon', age: 12 })
7
7
 
8
8
  @patch_stub = stub_request(:put, 'http://users.example.org/users/1')
9
9
  .with(body: '{"id":1,"name":"Idefix","age":12}')
@@ -71,6 +71,20 @@ describe Acfs::Model::Persistence do
71
71
  end
72
72
  end
73
73
 
74
+ context 'unloaded model' do
75
+ let!(:model) { model_class.find 1 }
76
+
77
+ describe '#update_attributes' do
78
+ subject { -> { model.update_attributes name: 'John' } }
79
+ it { expect{ subject.call }.to raise_error Acfs::ResourceNotLoaded }
80
+ end
81
+
82
+ describe '#update_attributes!' do
83
+ subject { -> { model.update_attributes! name: 'John' } }
84
+ it { expect{ subject.call }.to raise_error Acfs::ResourceNotLoaded }
85
+ end
86
+ end
87
+
74
88
  context 'loaded model' do
75
89
  context 'without changes' do
76
90
  let(:model) { model_class.find 1 }
@@ -102,6 +116,46 @@ describe Acfs::Model::Persistence do
102
116
  expect(model).to be_frozen
103
117
  end
104
118
  end
119
+
120
+ describe '#update_atributes!' do
121
+ let(:model) { model_class.find 1 }
122
+ before { model; Acfs.run }
123
+
124
+ it 'should set attributes' do
125
+ model.update_attributes name: 'Idefix'
126
+ expect(model.attributes.symbolize_keys).to eq id: 1, name: 'Idefix', age: 12
127
+ end
128
+
129
+ it 'should save resource' do
130
+ expect(model).to receive(:save).with({})
131
+ model.update_attributes name: 'Idefix'
132
+ end
133
+
134
+ it 'should pass second hash to save' do
135
+ expect(model).to receive(:save).with({ bla: 'blub' })
136
+ model.update_attributes({ name: 'Idefix' }, { bla: 'blub' })
137
+ end
138
+ end
139
+
140
+ describe '#update_atributes' do
141
+ let(:model) { model_class.find 1 }
142
+ before { model; Acfs.run }
143
+
144
+ it 'should set attributes' do
145
+ model.update_attributes! name: 'Idefix'
146
+ expect(model.attributes.symbolize_keys).to eq id: 1, name: 'Idefix', age: 12
147
+ end
148
+
149
+ it 'should save resource' do
150
+ expect(model).to receive(:save!).with({})
151
+ model.update_attributes! name: 'Idefix'
152
+ end
153
+
154
+ it 'should pass second hash to save' do
155
+ expect(model).to receive(:save!).with({ bla: 'blub' })
156
+ model.update_attributes!({ name: 'Idefix' }, { bla: 'blub' })
157
+ end
158
+ end
105
159
  end
106
160
 
107
161
  describe '.create!' do
@@ -125,7 +179,7 @@ describe Acfs::Model::Persistence do
125
179
 
126
180
  it 'should raise an error' do
127
181
  expect { model_class.create! data }.to raise_error ::Acfs::InvalidResource do |error|
128
- expect(error.errors).to be == { name: ['required'] }.stringify_keys
182
+ expect(error.errors).to be == { name: %w(required) }.stringify_keys
129
183
  end
130
184
  end
131
185
  end
@@ -157,7 +211,7 @@ describe Acfs::Model::Persistence do
157
211
 
158
212
  it 'should contain error hash' do
159
213
  model = model_class.create data
160
- expect(model.errors.to_hash).to be == { name: [ "required" ]}.stringify_keys
214
+ expect(model.errors.to_hash).to be == { name: %w(required) }.stringify_keys
161
215
  end
162
216
  end
163
217
  end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe Acfs::Model::Validation do
4
+ let(:params) { {} }
5
+ let(:model) { MyUserWithValidations.new params }
6
+
7
+ describe '#valid?' do
8
+ context 'with valid attributes' do
9
+ let(:params) { {name: 'john smith', age: 24} }
10
+ subject { model }
11
+
12
+ it { should be_valid }
13
+ end
14
+
15
+ context 'with invalid attributes' do
16
+ let(:params) { {name: 'invname'} }
17
+ subject { model }
18
+
19
+ it { should_not be_valid }
20
+ end
21
+ end
22
+
23
+ describe '#errors' do
24
+ context 'with valid attributes' do
25
+ let(:params) { {name: 'john smith', age: 24} }
26
+ before { model.valid? }
27
+ subject { model.errors }
28
+
29
+ it { should be_empty }
30
+ end
31
+
32
+ context 'with invalid attributes' do
33
+ let(:params) { {name: 'john'} }
34
+ before { model.valid? }
35
+ subject { model.errors }
36
+
37
+ it { should_not be_empty }
38
+ it { should have(2).items }
39
+
40
+ it 'should contain a list of error messages' do
41
+ expect(subject.to_hash).to eq age: ["can't be blank"], name: ['is invalid']
42
+ end
43
+ end
44
+ end
45
+
46
+ describe '#save!' do
47
+ context 'with invalid attributes' do
48
+ let(:params) { {name: 'john'} }
49
+ subject { -> { model.save! } }
50
+
51
+ it { expect { subject.call }.to raise_error Acfs::InvalidResource }
52
+ end
53
+ end
54
+ end
data/spec/spec_helper.rb CHANGED
@@ -21,7 +21,7 @@ RSpec.configure do |config|
21
21
  # order dependency and want to debug it, you can fix the order by providing
22
22
  # the seed, which is printed after each run.
23
23
  # --seed 1234
24
- config.order = "random"
24
+ config.order = 'random'
25
25
 
26
26
  config.expect_with :rspec do |c|
27
27
  # Only allow expect syntax
@@ -16,8 +16,7 @@ class CommentService < Acfs::Service
16
16
  use Acfs::Middleware::JsonDecoder
17
17
  end
18
18
 
19
- class MyUser
20
- include Acfs::Model
19
+ class MyUser < Acfs::Resource
21
20
  service UserService, path: 'users'
22
21
 
23
22
  attribute :id, :integer
@@ -25,27 +24,25 @@ class MyUser
25
24
  attribute :age, :integer
26
25
  end
27
26
 
28
- class Session
29
- include Acfs::Model
27
+ class MyUserInherited < MyUser
28
+
29
+ end
30
+
31
+ class MyUserWithValidations < MyUser
32
+ validates_presence_of :name, :age
33
+ validates_format_of :name, with: /\A\w+\s+\w+.?\z/
34
+ end
35
+
36
+ class Session < Acfs::Resource
30
37
  service UserService
31
38
 
32
39
  attribute :id, :string
33
40
  attribute :user, :integer
34
41
  end
35
42
 
36
- class Comment
37
- include Acfs::Model
43
+ class Comment < Acfs::Resource
38
44
  service CommentService
39
45
 
40
46
  attribute :id, :integer
41
47
  attribute :text, :string
42
48
  end
43
-
44
- class UserMessageReceiver
45
- include Acfs::Messaging::Receiver
46
- route 'my.#'
47
-
48
- def receive(delivery_info, metadata, payload)
49
- puts "UserMessageReceiver received : #{payload.inspect}"
50
- end
51
- end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acfs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.0
4
+ version: 0.20.0.dev.b181
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-08-08 00:00:00.000000000 Z
11
+ date: 2013-08-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -80,20 +80,6 @@ dependencies:
80
80
  - - '>='
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: bunny
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ~>
88
- - !ruby/object:Gem::Version
89
- version: 0.9.0
90
- type: :runtime
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ~>
95
- - !ruby/object:Gem::Version
96
- version: 0.9.0
97
83
  - !ruby/object:Gem::Dependency
98
84
  name: rack
99
85
  requirement: !ruby/object:Gem::Requirement
@@ -150,10 +136,6 @@ files:
150
136
  - lib/acfs/configuration.rb
151
137
  - lib/acfs/errors.rb
152
138
  - lib/acfs/global.rb
153
- - lib/acfs/messaging.rb
154
- - lib/acfs/messaging/client.rb
155
- - lib/acfs/messaging/message.rb
156
- - lib/acfs/messaging/receiver.rb
157
139
  - lib/acfs/middleware/base.rb
158
140
  - lib/acfs/middleware/json_decoder.rb
159
141
  - lib/acfs/middleware/json_encoder.rb
@@ -178,9 +160,11 @@ files:
178
160
  - lib/acfs/model/query_methods.rb
179
161
  - lib/acfs/model/relations.rb
180
162
  - lib/acfs/model/service.rb
163
+ - lib/acfs/model/validation.rb
181
164
  - lib/acfs/operation.rb
182
165
  - lib/acfs/request.rb
183
166
  - lib/acfs/request/callbacks.rb
167
+ - lib/acfs/resource.rb
184
168
  - lib/acfs/response.rb
185
169
  - lib/acfs/response/formats.rb
186
170
  - lib/acfs/response/status.rb
@@ -192,7 +176,6 @@ files:
192
176
  - lib/acfs/yard.rb
193
177
  - rubydoc.png
194
178
  - spec/acfs/configuration_spec.rb
195
- - spec/acfs/messaging/receiver_spec.rb
196
179
  - spec/acfs/middleware/json_decoder_spec.rb
197
180
  - spec/acfs/middleware/msgpack_decoder_spec.rb
198
181
  - spec/acfs/model/attributes/date_time_spec.rb
@@ -205,6 +188,7 @@ files:
205
188
  - spec/acfs/model/locatable_spec.rb
206
189
  - spec/acfs/model/persistance_spec.rb
207
190
  - spec/acfs/model/query_methods_spec.rb
191
+ - spec/acfs/model/validation_spec.rb
208
192
  - spec/acfs/request/callbacks_spec.rb
209
193
  - spec/acfs/request_spec.rb
210
194
  - spec/acfs/response/formats_spec.rb
@@ -212,7 +196,6 @@ files:
212
196
  - spec/acfs/service/middleware_spec.rb
213
197
  - spec/acfs/service_spec.rb
214
198
  - spec/acfs/stub_spec.rb
215
- - spec/acfs_messaging_spec.rb
216
199
  - spec/acfs_spec.rb
217
200
  - spec/fixtures/config.yml
218
201
  - spec/spec_helper.rb
@@ -233,9 +216,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
233
216
  version: '0'
234
217
  required_rubygems_version: !ruby/object:Gem::Requirement
235
218
  requirements:
236
- - - '>='
219
+ - - '>'
237
220
  - !ruby/object:Gem::Version
238
- version: '0'
221
+ version: 1.3.1
239
222
  requirements: []
240
223
  rubyforge_project:
241
224
  rubygems_version: 2.0.3
@@ -244,7 +227,6 @@ specification_version: 4
244
227
  summary: An abstract API base client for service oriented application.
245
228
  test_files:
246
229
  - spec/acfs/configuration_spec.rb
247
- - spec/acfs/messaging/receiver_spec.rb
248
230
  - spec/acfs/middleware/json_decoder_spec.rb
249
231
  - spec/acfs/middleware/msgpack_decoder_spec.rb
250
232
  - spec/acfs/model/attributes/date_time_spec.rb
@@ -257,6 +239,7 @@ test_files:
257
239
  - spec/acfs/model/locatable_spec.rb
258
240
  - spec/acfs/model/persistance_spec.rb
259
241
  - spec/acfs/model/query_methods_spec.rb
242
+ - spec/acfs/model/validation_spec.rb
260
243
  - spec/acfs/request/callbacks_spec.rb
261
244
  - spec/acfs/request_spec.rb
262
245
  - spec/acfs/response/formats_spec.rb
@@ -264,10 +247,8 @@ test_files:
264
247
  - spec/acfs/service/middleware_spec.rb
265
248
  - spec/acfs/service_spec.rb
266
249
  - spec/acfs/stub_spec.rb
267
- - spec/acfs_messaging_spec.rb
268
250
  - spec/acfs_spec.rb
269
251
  - spec/fixtures/config.yml
270
252
  - spec/spec_helper.rb
271
253
  - spec/support/response.rb
272
254
  - spec/support/service.rb
273
- has_rdoc:
@@ -1,24 +0,0 @@
1
- require 'acfs/messaging/client'
2
-
3
- module Acfs
4
-
5
- # @macro experimental
6
- #
7
- module Messaging
8
-
9
- class << self
10
-
11
- # @macro experimental
12
- #
13
- # Quick publish a message using default client instance.
14
- #
15
- # @param [#to_s] routing_key Routing key.
16
- # @param [#to_msgpack, Hash] payload Message payload.
17
- # @return [undefined]
18
- #
19
- def publish(routing_key, payload)
20
- Client.instance.publish routing_key, payload
21
- end
22
- end
23
- end
24
- end
@@ -1,39 +0,0 @@
1
- require 'bunny'
2
-
3
- module Acfs::Messaging
4
-
5
- # @macro experimental
6
- #
7
- class Client
8
-
9
- def initialize
10
- @bunny ||= Bunny.new.tap do |bunny|
11
- bunny.start
12
- end
13
- end
14
-
15
- def channel
16
- @channel ||= @bunny.create_channel
17
- end
18
-
19
- def exchange
20
- @exchange ||= channel.topic 'acfs-0.17.0-2', auto_delete: true
21
- end
22
-
23
- def publish(routing_key, message)
24
- exchange.publish ::MessagePack.pack(message), routing_key: routing_key.to_s
25
- end
26
-
27
- class << self
28
-
29
- def instance
30
- @instance ||= new
31
- end
32
-
33
- def register(receiver)
34
- @receivers ||= []
35
- @receivers << receiver.instance.tap { |r| r.init instance }
36
- end
37
- end
38
- end
39
- end
@@ -1,7 +0,0 @@
1
- module Acfs::Messaging
2
-
3
- #
4
- class Message
5
-
6
- end
7
- end
@@ -1,119 +0,0 @@
1
- module Acfs::Messaging
2
-
3
- # @macro experimental
4
- #
5
- # A {Receiver} subscribes to a messaging queue and
6
- # reacts to received messages.
7
- #
8
- # @example
9
- # class UserWelcomeReceiver < Acfs::Receiver
10
- #
11
- module Receiver
12
- extend ActiveSupport::Concern
13
-
14
- included do
15
- Acfs::Messaging::Client.register self
16
- end
17
-
18
- def init(client)
19
- @channel = client.channel
20
- @queue = @channel.queue self.class.queue, options
21
- @queue.bind client.exchange, routing_key: self.class.routing_key
22
-
23
- @queue.subscribe do |delivery_info, metadata, payload|
24
- process_received delivery_info, metadata, payload
25
- end
26
- end
27
-
28
- def process_received(delivery_info, metadata, payload)
29
- return if delivery_info.nil?
30
-
31
- payload = MessagePack.unpack payload
32
- payload.symbolize_keys! if payload.is_a? Hash
33
- receive delivery_info, metadata, payload
34
- end
35
-
36
- def options
37
- @options ||= self.class.options
38
- end
39
-
40
- # @macro experimental
41
- #
42
- # Handle incoming messages. Should be overridden by derived class.
43
- #
44
- def receive(delivery_info, metadata, payload)
45
-
46
- end
47
-
48
- module ClassMethods
49
-
50
- # @macro experimental
51
- #
52
- # @overload queue
53
- # Return name of queue to listen on. Default name will be
54
- # generated based on full class name.
55
- #
56
- # @return [String] Name of queue to listen on.
57
- #
58
- # @overload queue(name)
59
- # Set queue name to listen on.
60
- #
61
- # @param [String] name Queue name to listen on.
62
- # @return [String] Set name of queue to listen on.
63
- #
64
- def queue(*args)
65
- raise ArgumentError.new 'Receiver.queue accepts zero or one argument.' if args.size > 1
66
-
67
- @queue ||= self.name.underscore.gsub('/', '.')
68
- @queue = args[0].nil? ? nil : args[0].to_s if args.size > 0
69
- @queue
70
- end
71
-
72
- # @macro experimental
73
- #
74
- # Specify routing key for this receiver. The routing key defines
75
- # which exchanges should be subscribed to receive messages from.
76
- #
77
- # @param [#to_s] key Routing key.
78
- #
79
- def route(key)
80
- @routing_key = key.to_s
81
- end
82
-
83
- # @macro experimental
84
- #
85
- # @overload options
86
- # Return configured options for this receiver.
87
- #
88
- # @return [Hash] Configured options.
89
- #
90
- # @overload options(opts)
91
- # Set configuration options.
92
- #
93
- # @param [Hash] opts Messaging channel options.
94
- # @return [Hash] Configured options.
95
- #
96
- def options(opts = nil)
97
- @options ||= {}
98
- return @options if opts.nil?
99
-
100
- @options = opts.to_hash if opts.respond_to? :to_hash
101
- end
102
-
103
- # @api private
104
- #
105
- # Return configured routing key if any.
106
- # Default value is `#`.
107
- #
108
- # @return [String, Nil] Routing key or `nil`.
109
- #
110
- def routing_key
111
- @routing_key ||= '#'
112
- end
113
-
114
- def instance
115
- @instance ||= new
116
- end
117
- end
118
- end
119
- end
@@ -1,61 +0,0 @@
1
- require 'spec_helper'
2
-
3
- module My
4
- class CustomReceiver
5
- include Acfs::Messaging::Receiver
6
- end
7
- end
8
-
9
- describe Acfs::Messaging::Receiver do
10
- let(:rcv_class) { My::CustomReceiver }
11
- after { rcv_class.queue nil }
12
-
13
- describe '.queue' do
14
- it 'should return queue name based on class name' do
15
- expect(rcv_class.queue).to be == 'my.custom_receiver'
16
- end
17
-
18
- it 'should set and return custom queue name' do
19
- rcv_class.queue 'special.queue.name'
20
-
21
- expect(rcv_class.queue).to be == 'special.queue.name'
22
- end
23
- end
24
-
25
- describe '.receive' do
26
- context 'with routed message' do
27
- context 'with exact routing key match' do
28
- it 'should receive messages' do
29
- pending 'Not reliable.'
30
-
31
- rcv_class.instance.should_receive(:receive).with(anything, anything, { message: 'Hello!'})
32
-
33
- Acfs::Messaging::Client.instance.publish('my.custom_receiver', { message: 'Hello!' })
34
- sleep 5 # Nothing better?
35
- end
36
- end
37
-
38
- context 'with partial routing key match' do
39
- it 'should receive messages' do
40
- pending 'Not reliable.'
41
-
42
- rcv_class.instance.should_receive(:receive).with(anything, anything, { message: 'Hello!'})
43
-
44
- Acfs::Messaging::Client.instance.publish('my.different', { message: 'Hello!' })
45
- sleep 5 # Nothing better?
46
- end
47
- end
48
- end
49
-
50
- context 'with not routed message' do
51
- it 'should not receive messages' do
52
- pending 'Not reliable.'
53
-
54
- rcv_class.instance.should_not_receive(:receive)
55
-
56
- Acfs::Messaging::Client.instance.publish('abc.cde', { message: 'Hello!' })
57
- sleep 5 # Nothing better?
58
- end
59
- end
60
- end
61
- end
@@ -1,5 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe 'Acfs messaging' do
4
-
5
- end