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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.travis.yml +5 -0
  4. data/CHANGELOG.md +5 -0
  5. data/Gemfile +13 -5
  6. data/Guardfile +6 -16
  7. data/README.md +43 -3
  8. data/Rakefile +11 -1
  9. data/acfs.gemspec +7 -6
  10. data/gemfiles/Gemfile.rails-3-0 +8 -0
  11. data/gemfiles/Gemfile.rails-3-1 +8 -0
  12. data/lib/acfs.rb +7 -0
  13. data/lib/acfs/configuration.rb +52 -1
  14. data/lib/acfs/global.rb +14 -0
  15. data/lib/acfs/messaging/client.rb +39 -0
  16. data/lib/acfs/messaging/message.rb +7 -0
  17. data/lib/acfs/messaging/receiver.rb +119 -0
  18. data/lib/acfs/model.rb +3 -0
  19. data/lib/acfs/model/attributes.rb +97 -12
  20. data/lib/acfs/model/attributes/boolean.rb +11 -1
  21. data/lib/acfs/model/attributes/integer.rb +11 -1
  22. data/lib/acfs/model/attributes/string.rb +11 -1
  23. data/lib/acfs/model/dirty.rb +14 -4
  24. data/lib/acfs/model/initialization.rb +7 -2
  25. data/lib/acfs/model/loadable.rb +16 -0
  26. data/lib/acfs/model/locatable.rb +25 -4
  27. data/lib/acfs/model/operational.rb +4 -0
  28. data/lib/acfs/model/persistence.rb +73 -14
  29. data/lib/acfs/model/query_methods.rb +44 -7
  30. data/lib/acfs/model/service.rb +23 -11
  31. data/lib/acfs/operation.rb +2 -0
  32. data/lib/acfs/runner.rb +3 -0
  33. data/lib/acfs/service.rb +38 -5
  34. data/lib/acfs/service/middleware.rb +23 -5
  35. data/lib/acfs/version.rb +1 -1
  36. data/lib/acfs/yard.rb +5 -0
  37. data/rubydoc.png +0 -0
  38. data/spec/acfs/configuration_spec.rb +0 -1
  39. data/spec/acfs/messaging/receiver_spec.rb +55 -0
  40. data/spec/acfs/model/attributes_spec.rb +4 -4
  41. data/spec/acfs/stub_spec.rb +1 -1
  42. data/spec/acfs_messaging_spec.rb +5 -0
  43. data/spec/spec_helper.rb +1 -1
  44. data/spec/support/service.rb +12 -1
  45. metadata +35 -9
@@ -4,8 +4,11 @@ module Acfs::Model
4
4
  #
5
5
  module Initialization
6
6
 
7
- # Initializes a new model with the given +params+.
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
- def initialize(params={})
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
@@ -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!
@@ -2,25 +2,46 @@ module Acfs::Model
2
2
 
3
3
  # Provide methods for generation URLs for resources.
4
4
  #
5
- # Example
5
+ # @example
6
6
  # class User
7
7
  # service AccountService # With base URL `http://acc.svr`
8
8
  # end
9
- # User.url #=> "http://acc.svr/users"
10
- # User.url(5) #=> "http://acc.svr/users/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
- # user = User.new name: "John"
14
- # user.persisted? # => false
15
- # user.save
16
- # user.persisted? # => true
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
- # user2 = User.find 5
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
- # Save the resource.
46
+ # @api public
47
+ #
48
+ # Saves the resource.
37
49
  #
38
- # It will PATCH to the service to update the resource or send
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
- # `#save` return true of operation was successful, otherwise false.
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
- def save!(opts = {}) # :nodoc:
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
- # Example
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
- # Try to load a resource by given id.
21
+ # @api public
22
22
  #
23
- # Example
24
- # User.find(5) # Will query `http://base.url/users/5`
25
- # User.find(1, 2, 5) # Will return collection and will query
26
- # # `http://base.url/users/1`, `http://base.url/users/2`
27
- # # and `http://base.url/users/5` parallel
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
 
@@ -1,24 +1,36 @@
1
1
  module Acfs::Model
2
2
 
3
- # Included by Acfs::Model. Allows a model to belong to a service.
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
- # Link to service this model belongs to. Connection settings like base URL
11
- # are fetched from service. Return assigned service if no arguments are given.
11
+ # @api public
12
12
  #
13
- # Example
14
- # class AccountService < Acfs::Client
15
- # self.base_url = 'http://acc.serv.org'
16
- # end
13
+ # @overload service()
14
+ # Return service instance.
17
15
  #
18
- # class MyUser
19
- # service AccountService
20
- # end
21
- # MyUser.find 5 # Will fetch `http://acc.serv.org/users/5`
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
@@ -1,5 +1,7 @@
1
1
  module Acfs
2
2
 
3
+ # @api private
4
+ #
3
5
  # Describes a CRUD operation. Handle request creation and response
4
6
  # processing as well as error handling and stubbing.
5
7
  #
@@ -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
@@ -2,21 +2,38 @@ require 'acfs/service/middleware'
2
2
 
3
3
  module Acfs
4
4
 
5
- # Service object.
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
- def options
17
- @options
18
- end
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."