acfs 0.16.0 → 0.17.0
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 +4 -4
- data/.gitignore +2 -1
- data/.travis.yml +5 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +13 -5
- data/Guardfile +6 -16
- data/README.md +43 -3
- data/Rakefile +11 -1
- data/acfs.gemspec +7 -6
- data/gemfiles/Gemfile.rails-3-0 +8 -0
- data/gemfiles/Gemfile.rails-3-1 +8 -0
- data/lib/acfs.rb +7 -0
- data/lib/acfs/configuration.rb +52 -1
- data/lib/acfs/global.rb +14 -0
- data/lib/acfs/messaging/client.rb +39 -0
- data/lib/acfs/messaging/message.rb +7 -0
- data/lib/acfs/messaging/receiver.rb +119 -0
- data/lib/acfs/model.rb +3 -0
- data/lib/acfs/model/attributes.rb +97 -12
- data/lib/acfs/model/attributes/boolean.rb +11 -1
- data/lib/acfs/model/attributes/integer.rb +11 -1
- data/lib/acfs/model/attributes/string.rb +11 -1
- data/lib/acfs/model/dirty.rb +14 -4
- data/lib/acfs/model/initialization.rb +7 -2
- data/lib/acfs/model/loadable.rb +16 -0
- data/lib/acfs/model/locatable.rb +25 -4
- data/lib/acfs/model/operational.rb +4 -0
- data/lib/acfs/model/persistence.rb +73 -14
- data/lib/acfs/model/query_methods.rb +44 -7
- data/lib/acfs/model/service.rb +23 -11
- data/lib/acfs/operation.rb +2 -0
- data/lib/acfs/runner.rb +3 -0
- data/lib/acfs/service.rb +38 -5
- data/lib/acfs/service/middleware.rb +23 -5
- data/lib/acfs/version.rb +1 -1
- data/lib/acfs/yard.rb +5 -0
- data/rubydoc.png +0 -0
- data/spec/acfs/configuration_spec.rb +0 -1
- data/spec/acfs/messaging/receiver_spec.rb +55 -0
- data/spec/acfs/model/attributes_spec.rb +4 -4
- data/spec/acfs/stub_spec.rb +1 -1
- data/spec/acfs_messaging_spec.rb +5 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/support/service.rb +12 -1
- metadata +35 -9
@@ -4,8 +4,11 @@ module Acfs::Model
|
|
4
4
|
#
|
5
5
|
module Initialization
|
6
6
|
|
7
|
-
#
|
7
|
+
# @api public
|
8
8
|
#
|
9
|
+
# Initializes a new model with the given `params`.
|
10
|
+
#
|
11
|
+
# @example
|
9
12
|
# class User
|
10
13
|
# include Acfs::Model
|
11
14
|
# attribute :name
|
@@ -18,7 +21,9 @@ module Acfs::Model
|
|
18
21
|
# user.email # => "bob@dom.tld"
|
19
22
|
# user.age # => 18
|
20
23
|
#
|
21
|
-
|
24
|
+
# @param [ Hash{ Symbol => Object } ] params Attributes to set on resource.
|
25
|
+
#
|
26
|
+
def initialize(params = {})
|
22
27
|
params.each do |attr, value|
|
23
28
|
self.public_send("#{attr}=", value)
|
24
29
|
end if params
|
data/lib/acfs/model/loadable.rb
CHANGED
@@ -1,14 +1,30 @@
|
|
1
1
|
module Acfs::Model
|
2
2
|
|
3
|
+
# Provides method to check for loading state of resources.
|
4
|
+
# A resource that is created but not yet fetched will be loaded
|
5
|
+
# after running {Acfs::Global#run Acfs.run}.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# user = User.find 5
|
9
|
+
# user.loaded? # => false
|
10
|
+
# Acfs.run
|
11
|
+
# user.loaded? # => true
|
12
|
+
#
|
3
13
|
module Loadable
|
4
14
|
extend ActiveSupport::Concern
|
5
15
|
|
16
|
+
# @api public
|
17
|
+
#
|
6
18
|
# Check if model is loaded or if request is still queued.
|
7
19
|
#
|
20
|
+
# @return [ Boolean ] True if resource is loaded, false otherwise.
|
21
|
+
#
|
8
22
|
def loaded?
|
9
23
|
!!@loaded
|
10
24
|
end
|
11
25
|
|
26
|
+
# @api private
|
27
|
+
#
|
12
28
|
# Mark model as loaded.
|
13
29
|
#
|
14
30
|
def loaded!
|
data/lib/acfs/model/locatable.rb
CHANGED
@@ -2,25 +2,46 @@ module Acfs::Model
|
|
2
2
|
|
3
3
|
# Provide methods for generation URLs for resources.
|
4
4
|
#
|
5
|
-
#
|
5
|
+
# @example
|
6
6
|
# class User
|
7
7
|
# service AccountService # With base URL `http://acc.svr`
|
8
8
|
# end
|
9
|
-
# User.url
|
10
|
-
# User.url(5)
|
9
|
+
# User.url # => "http://acc.svr/users"
|
10
|
+
# User.url(5) # => "http://acc.svr/users/5"
|
11
11
|
#
|
12
12
|
module Locatable
|
13
13
|
extend ActiveSupport::Concern
|
14
14
|
|
15
15
|
module ClassMethods
|
16
16
|
|
17
|
-
# Return URL for this resource.
|
17
|
+
# Return URL for this class of resource. Given suffix will be appended.
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
# User.url # => "http://users.srv.org/users"
|
21
|
+
# User.url(5) # => "http://users.srv.org/users/5"
|
22
|
+
#
|
23
|
+
# @param [ String ] suffix Suffix to append to URL.
|
24
|
+
# @return [ String ] Generated URL.
|
25
|
+
# @see Acfs::Service#url_for Delegates to Service#url_for with `suffix` option.
|
18
26
|
#
|
19
27
|
def url(suffix = nil)
|
20
28
|
service.url_for(self, suffix: suffix)
|
21
29
|
end
|
22
30
|
end
|
23
31
|
|
32
|
+
# Return URL for this resource. Resource if will be appended
|
33
|
+
# as suffix if present.
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# user.new.url # => "http://users.srv.org/users"
|
37
|
+
#
|
38
|
+
# user = User.find 5
|
39
|
+
# Acfs.run
|
40
|
+
# user.url # => "http://users.srv.org/users/5"
|
41
|
+
#
|
42
|
+
# @return [ String ] Generated URL.
|
43
|
+
# @see ClassMethods#url
|
44
|
+
#
|
24
45
|
def url
|
25
46
|
return nil if id.nil?
|
26
47
|
self.class.service.url_for self, suffix: read_attribute(:id)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module Acfs::Model
|
2
2
|
|
3
|
+
# @api private
|
4
|
+
#
|
3
5
|
# Provide methods for creating and processing CRUD operations and
|
4
6
|
# handling responses. That includes error handling as well as
|
5
7
|
# handling stubbed resources.
|
@@ -11,6 +13,8 @@ module Acfs::Model
|
|
11
13
|
delegate :operation, to: :'self.class'
|
12
14
|
|
13
15
|
module ClassMethods
|
16
|
+
|
17
|
+
# Invoke CRUD operation.
|
14
18
|
def operation(action, opts = {}, &block)
|
15
19
|
Acfs.runner.process ::Acfs::Operation.new self, action, opts, &block
|
16
20
|
end
|
@@ -6,39 +6,54 @@ module Acfs
|
|
6
6
|
module Persistence
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
|
+
# @api public
|
10
|
+
#
|
9
11
|
# Check if the model is persisted. A model is persisted if
|
10
12
|
# it is saved after beeing created or when it was not changed
|
11
13
|
# since it was loaded.
|
12
14
|
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
15
|
+
# @example Newly created resource:
|
16
|
+
# user = User.new name: "John"
|
17
|
+
# user.persisted? # => false
|
18
|
+
# user.save
|
19
|
+
# user.persisted? # => true
|
20
|
+
#
|
21
|
+
# @example Modified resource:
|
22
|
+
# user2 = User.find 5
|
23
|
+
# user2.persisted? # => true
|
24
|
+
# user2.name = 'Amy'
|
25
|
+
# user2.persisted? # => false
|
26
|
+
# user2.save
|
27
|
+
# user2.persisted? # => true
|
17
28
|
#
|
18
|
-
#
|
19
|
-
# user2.persisted? # => true
|
20
|
-
# user2.name = 'Amy'
|
21
|
-
# user2.persisted? # => false
|
22
|
-
# user2.save
|
23
|
-
# user2.persisted? # => true
|
29
|
+
# @return [ Boolean ] True if resource has no changes and is not newly created, false otherwise.
|
24
30
|
#
|
25
31
|
def persisted?
|
26
32
|
!new? && !changed?
|
27
33
|
end
|
28
34
|
|
35
|
+
# @api public
|
36
|
+
#
|
29
37
|
# Return true if model is a new record and was not saved yet.
|
30
38
|
#
|
39
|
+
# @return [ Boolean ] True if resource is newly created, false otherwise.
|
40
|
+
#
|
31
41
|
def new?
|
32
42
|
read_attribute(:id).nil?
|
33
43
|
end
|
34
44
|
alias :new_record? :new?
|
35
45
|
|
36
|
-
#
|
46
|
+
# @api public
|
47
|
+
#
|
48
|
+
# Saves the resource.
|
37
49
|
#
|
38
|
-
# It will
|
50
|
+
# It will PUT to the service to update the resource or send
|
39
51
|
# a POST to create a new one if the resource is new.
|
40
52
|
#
|
41
|
-
#
|
53
|
+
# Saving a resource is a synchronous operation.
|
54
|
+
#
|
55
|
+
# @return [ Boolean ] True if save operation was successful, false otherwise.
|
56
|
+
# @see #save! See #save! for available options.
|
42
57
|
#
|
43
58
|
def save(*args)
|
44
59
|
save! *args
|
@@ -47,7 +62,23 @@ module Acfs
|
|
47
62
|
false
|
48
63
|
end
|
49
64
|
|
50
|
-
|
65
|
+
# @api public
|
66
|
+
#
|
67
|
+
# Saves the resource. Raises an error if something happens.
|
68
|
+
#
|
69
|
+
# Saving a resource is a synchronous operation.
|
70
|
+
#
|
71
|
+
# @param [ Hash ] opts Hash with additional options.
|
72
|
+
# @option opts [ Hash ] :data Data to send to remote service. Default will be resource attributes.
|
73
|
+
#
|
74
|
+
# @raise [ Acfs::InvalidResource ]
|
75
|
+
# If remote services respond with 422 response. Will fill errors with data from response
|
76
|
+
# @raise [ Acfs::ErroneousResponse ]
|
77
|
+
# If remote service respond with not successful response.
|
78
|
+
#
|
79
|
+
# @see #save
|
80
|
+
#
|
81
|
+
def save!(opts = {})
|
51
82
|
#raise ::Acfs::InvalidResource errors: errors.to_a unless valid?
|
52
83
|
|
53
84
|
opts[:data] = attributes unless opts[:data]
|
@@ -59,19 +90,47 @@ module Acfs
|
|
59
90
|
|
60
91
|
module ClassMethods
|
61
92
|
|
93
|
+
# @api public
|
94
|
+
#
|
62
95
|
# Create a new resource sending given data. If resource cannot be
|
63
96
|
# created an error will be thrown.
|
64
97
|
#
|
98
|
+
# Saving a resource is a synchronous operation.
|
99
|
+
#
|
100
|
+
# @param [ Hash{ Symbol, String => Object }] data Data to send in create request.
|
101
|
+
# @return [ self ] Newly resource object.
|
102
|
+
#
|
103
|
+
# @raise [ Acfs::InvalidResource ]
|
104
|
+
# If remote services respond with 422 response. Will fill errors with data from response
|
105
|
+
# @raise [ Acfs::ErroneousResponse ]
|
106
|
+
# If remote service respond with not successful response.
|
107
|
+
#
|
108
|
+
# @see Acfs::Model::Persistence#save! Available options. `:data` will be overridden with provided data hash.
|
109
|
+
# @see #create
|
110
|
+
#
|
65
111
|
def create!(data, opts = {})
|
66
112
|
new.tap do |model|
|
67
113
|
model.save! opts.merge data: data
|
68
114
|
end
|
69
115
|
end
|
70
116
|
|
117
|
+
# @api public
|
118
|
+
#
|
71
119
|
# Create a new resource sending given data. If resource cannot be
|
72
120
|
# create model will be returned and error hash contains response
|
73
121
|
# errors if available.
|
74
122
|
#
|
123
|
+
# Saving a resource is a synchronous operation.
|
124
|
+
#
|
125
|
+
# @param [ Hash{ Symbol, String => Object }] data Data to send in create request.
|
126
|
+
# @return [ self ] Newly resource object.
|
127
|
+
#
|
128
|
+
# @raise [ Acfs::ErroneousResponse ]
|
129
|
+
# If remote service respond with not successful response.
|
130
|
+
#
|
131
|
+
# @see Acfs::Model::Persistence#save! Available options. `:data` will be overridden with provided data hash.
|
132
|
+
# @see #create!
|
133
|
+
#
|
75
134
|
def create(data, opts = {})
|
76
135
|
model = new
|
77
136
|
model.save! opts.merge data: data
|
@@ -2,7 +2,7 @@ module Acfs::Model
|
|
2
2
|
|
3
3
|
# Methods providing the query interface for finding resouces.
|
4
4
|
#
|
5
|
-
#
|
5
|
+
# @example
|
6
6
|
# class MyUser
|
7
7
|
# include Acfs::Model
|
8
8
|
# end
|
@@ -18,13 +18,41 @@ module Acfs::Model
|
|
18
18
|
|
19
19
|
module ClassMethods
|
20
20
|
|
21
|
-
#
|
21
|
+
# @api public
|
22
22
|
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
23
|
+
# @overload find(id, opts = {})
|
24
|
+
# Find a single resource by given ID.
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# user = User.find(5) # Will query `http://base.url/users/5`
|
28
|
+
#
|
29
|
+
# @param [ Fixnum ] id Resource IDs to fetch from remote service.
|
30
|
+
# @param [ Hash ] opts Additional options.
|
31
|
+
# @option opts [ Hash ] :params Additional parameters added to request. `:id` will be overridden
|
32
|
+
# with given ID.
|
33
|
+
#
|
34
|
+
# @yield [ resource ] Callback block to be executed after resource was fetched successfully.
|
35
|
+
# @yieldparam resource [ self ] Fetched resources.
|
36
|
+
#
|
37
|
+
# @return [ self ] Resource object if only one ID was given.
|
38
|
+
#
|
39
|
+
# @overload find(*ids, opts = {})
|
40
|
+
# Load collection of specified resources by given IDs.
|
41
|
+
#
|
42
|
+
# @example
|
43
|
+
# User.find(1, 2, 5) # Will return collection and will request
|
44
|
+
# # `http://base.url/users/1`, `http://base.url/users/2`
|
45
|
+
# # and `http://base.url/users/5` parallel
|
46
|
+
#
|
47
|
+
# @param [ Fixnum, ... ] ids One or more resource IDs to fetch from remote service.
|
48
|
+
# @param [ Hash ] opts Additional options.
|
49
|
+
# @option opts [ Hash ] :params Additional parameters added to request. `:id` will be overridden
|
50
|
+
# with individual resource ID.
|
51
|
+
#
|
52
|
+
# @yield [ collection ] Callback block to be executed after collection was fetched successfully.
|
53
|
+
# @yieldparam resource [ Collection ] Collection with fetched resources.
|
54
|
+
#
|
55
|
+
# @return [ Collection ] Collection of requested resources if multiple IDs were given.
|
28
56
|
#
|
29
57
|
def find(*attrs, &block)
|
30
58
|
opts = attrs.extract_options!
|
@@ -32,8 +60,17 @@ module Acfs::Model
|
|
32
60
|
attrs.size > 1 ? find_multiple(attrs, opts, &block) : find_single(attrs[0], opts, &block)
|
33
61
|
end
|
34
62
|
|
63
|
+
# @api public
|
64
|
+
#
|
35
65
|
# Try to load all resources.
|
36
66
|
#
|
67
|
+
# @param [ Hash ] params Request parameters that will be send to remote service.
|
68
|
+
#
|
69
|
+
# @yield [ collection ] Callback block to be executed when resource collection was loaded successfully.
|
70
|
+
# @yieldparam collection [ Collection ] Collection of fetched resources.
|
71
|
+
#
|
72
|
+
# @return [ Collection ] Collection of requested resources.
|
73
|
+
#
|
37
74
|
def all(params = {}, &block)
|
38
75
|
collection = ::Acfs::Collection.new
|
39
76
|
|
data/lib/acfs/model/service.rb
CHANGED
@@ -1,24 +1,36 @@
|
|
1
1
|
module Acfs::Model
|
2
2
|
|
3
|
-
# Included by Acfs::Model. Allows
|
3
|
+
# Included by Acfs::Model. Allows to configure the service
|
4
|
+
# a resource belongs to.
|
4
5
|
#
|
5
6
|
module Service
|
6
7
|
extend ActiveSupport::Concern
|
7
8
|
|
8
9
|
module ClassMethods
|
9
10
|
|
10
|
-
#
|
11
|
-
# are fetched from service. Return assigned service if no arguments are given.
|
11
|
+
# @api public
|
12
12
|
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
# self.base_url = 'http://acc.serv.org'
|
16
|
-
# end
|
13
|
+
# @overload service()
|
14
|
+
# Return service instance.
|
17
15
|
#
|
18
|
-
# class
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
16
|
+
# @return [ Service ] Service class instance.
|
17
|
+
#
|
18
|
+
# @overload service(klass, options = {})
|
19
|
+
# Link to service this model belongs to. Connection settings like base URL
|
20
|
+
# are fetched from service. Return assigned service if no arguments are given.
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# class AccountService < Acfs::Client
|
24
|
+
# self.base_url = 'http://acc.serv.org'
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# class MyUser
|
28
|
+
# service AccountService
|
29
|
+
# end
|
30
|
+
# MyUser.find 5 # Will fetch `http://acc.serv.org/users/5`
|
31
|
+
#
|
32
|
+
# @param [ Class ] klass Service class derived from {Acfs::Service}.
|
33
|
+
# @param [ Object ] options Option delegated to service class initializer.
|
22
34
|
#
|
23
35
|
def service(klass = nil, options = {})
|
24
36
|
return @service unless klass
|
data/lib/acfs/operation.rb
CHANGED
data/lib/acfs/runner.rb
CHANGED
@@ -2,6 +2,8 @@ require 'acfs/service/middleware'
|
|
2
2
|
|
3
3
|
module Acfs
|
4
4
|
|
5
|
+
# @api private
|
6
|
+
#
|
5
7
|
class Runner
|
6
8
|
include Service::Middleware
|
7
9
|
attr_reader :adapter
|
@@ -59,6 +61,7 @@ module Acfs
|
|
59
61
|
def clear
|
60
62
|
queue.clear
|
61
63
|
adapter.abort
|
64
|
+
@running = false
|
62
65
|
end
|
63
66
|
|
64
67
|
private
|
data/lib/acfs/service.rb
CHANGED
@@ -2,21 +2,38 @@ require 'acfs/service/middleware'
|
|
2
2
|
|
3
3
|
module Acfs
|
4
4
|
|
5
|
-
# Service
|
5
|
+
# User {Acfs::Service} to define your services. That includes
|
6
|
+
# an identity used to identify the service in configuration files
|
7
|
+
# and middlewares the service uses.
|
8
|
+
#
|
9
|
+
# Configure your service URLs in a YAML file loaded in an
|
10
|
+
# initializer using the identity as a key:
|
11
|
+
#
|
12
|
+
# production:
|
13
|
+
# services:
|
14
|
+
# user_service_key: "http://users.service.org/base/path"
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
# class UserService < Acfs::Service
|
18
|
+
# identity :user_service_key
|
19
|
+
#
|
20
|
+
# use Acfs::Middleware::MessagePackDecoder
|
21
|
+
# end
|
6
22
|
#
|
7
23
|
class Service
|
8
24
|
attr_accessor :options
|
9
25
|
|
10
26
|
include Service::Middleware
|
11
27
|
|
28
|
+
# @api private
|
29
|
+
#
|
12
30
|
def initialize(options = {})
|
13
31
|
@options = options
|
14
32
|
end
|
15
33
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
34
|
+
# @api private
|
35
|
+
# @return [String]
|
36
|
+
#
|
20
37
|
def url_for(resource_class, options = {})
|
21
38
|
options.reverse_merge! self.options
|
22
39
|
|
@@ -28,11 +45,27 @@ module Acfs
|
|
28
45
|
|
29
46
|
class << self
|
30
47
|
|
48
|
+
# @api public
|
49
|
+
#
|
50
|
+
# @overload identity()
|
51
|
+
# Return configured identity key or derive key from class name.
|
52
|
+
#
|
53
|
+
# @return [Symbol] Service identity key.
|
54
|
+
#
|
55
|
+
# @overload identity(identity)
|
56
|
+
# Set identity key.
|
57
|
+
#
|
58
|
+
# @param [#to_s] identity New identity key.
|
59
|
+
# @return [Symbol] New set identity key.
|
60
|
+
#
|
31
61
|
def identity(identity = nil)
|
32
62
|
@identity = identity.to_s.to_sym unless identity.nil?
|
33
63
|
@identity ||= name.to_sym
|
34
64
|
end
|
35
65
|
|
66
|
+
# @api private
|
67
|
+
# @return [String]
|
68
|
+
#
|
36
69
|
def base_url
|
37
70
|
unless (base = Acfs::Configuration.current.locate identity)
|
38
71
|
raise ArgumentError, "#{identity} not configured. Add `locate '#{identity.to_s.underscore}', 'http://service.url/'` to your configuration."
|