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
@@ -0,0 +1,119 @@
|
|
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
|
data/lib/acfs/model.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
module Acfs::Model
|
2
2
|
|
3
|
-
#
|
3
|
+
# = Acfs Attributes
|
4
4
|
#
|
5
5
|
# Allows to specify attributes of a class with default values and type safety.
|
6
6
|
#
|
7
|
+
# @example
|
7
8
|
# class User
|
8
9
|
# include Acfs::Model
|
9
10
|
# attribute :name, :string, default: 'Anon'
|
@@ -18,13 +19,23 @@ module Acfs::Model
|
|
18
19
|
extend ActiveSupport::Concern
|
19
20
|
include ActiveModel::AttributeMethods
|
20
21
|
|
21
|
-
|
22
|
+
# @api public
|
23
|
+
#
|
24
|
+
# Write default attributes defined in resource class.
|
25
|
+
#
|
26
|
+
# @see #write_attributes
|
27
|
+
# @see ClassMethods#attributes
|
28
|
+
#
|
29
|
+
def initialize(*attrs)
|
22
30
|
self.write_attributes self.class.attributes, change: false
|
23
31
|
super
|
24
32
|
end
|
25
33
|
|
34
|
+
# @api public
|
35
|
+
#
|
26
36
|
# Returns ActiveModel compatible list of attributes and values.
|
27
37
|
#
|
38
|
+
# @example
|
28
39
|
# class User
|
29
40
|
# include Acfs::Model
|
30
41
|
# attribute :name, type: String, default: 'Anon'
|
@@ -32,24 +43,58 @@ module Acfs::Model
|
|
32
43
|
# user = User.new(name: 'John')
|
33
44
|
# user.attributes # => { "name" => "John" }
|
34
45
|
#
|
46
|
+
# @return [ HashWithIndifferentAccess{ Symbol => Object } ] Attributes and their values.
|
47
|
+
#
|
35
48
|
def attributes
|
36
49
|
HashWithIndifferentAccess.new self.class.attributes.keys.inject({}) { |h, k| h[k.to_sym] = public_send k; h }
|
37
50
|
end
|
38
51
|
|
39
|
-
#
|
52
|
+
# @api public
|
53
|
+
#
|
54
|
+
# Update all attributes with given hash. Attribute values will be casted
|
55
|
+
# to defined attribute type.
|
56
|
+
#
|
57
|
+
# @example
|
58
|
+
# user.attributes = { :name => 'Adam' }
|
59
|
+
# user.name # => 'Adam'
|
60
|
+
#
|
61
|
+
# @param [ Hash{ String, Symbol => Object }, #each{|key, value|} ] attributes to set in resource.
|
62
|
+
# @see #write_attributes Delegates attributes hash to `#write_attributes`.
|
40
63
|
#
|
41
64
|
def attributes=(attributes)
|
42
65
|
write_attributes attributes
|
43
66
|
end
|
44
67
|
|
45
|
-
#
|
68
|
+
# @api public
|
69
|
+
#
|
70
|
+
# Read an attribute from instance variable.
|
71
|
+
#
|
72
|
+
# @param [ Symbol, String ] name Attribute name.
|
73
|
+
# @return [ Object ] Attribute value.
|
46
74
|
#
|
47
75
|
def read_attribute(name)
|
48
76
|
instance_variable_get :"@#{name}"
|
49
77
|
end
|
50
78
|
|
79
|
+
# @api public
|
80
|
+
#
|
51
81
|
# Write a hash of attributes and values.
|
52
82
|
#
|
83
|
+
# If attribute value is a `Proc` it will be evaluated in the context
|
84
|
+
# of the resource after all non-proc attribute values are set. Values
|
85
|
+
# will be casted to defined attribute type.
|
86
|
+
#
|
87
|
+
# The behavior is used to apply default attributes from resource
|
88
|
+
# class definition.
|
89
|
+
#
|
90
|
+
# @example
|
91
|
+
# user.write_attributes { :name => 'john', :email => lambda{ "#{name}@example.org" } }
|
92
|
+
# user.name # => 'john'
|
93
|
+
# user.email # => 'john@example.org'
|
94
|
+
#
|
95
|
+
# @param [ Hash{ String, Symbol => Object, Proc }, #each{|key, value|} ] attributes to write.
|
96
|
+
# @see #write_attribute Delegates attribute values to `#write_attribute`.
|
97
|
+
#
|
53
98
|
def write_attributes(attributes, opts = {})
|
54
99
|
return false unless attributes.respond_to? :each
|
55
100
|
|
@@ -66,37 +111,58 @@ module Acfs::Model
|
|
66
111
|
procs.each do |key, proc|
|
67
112
|
write_attribute key, instance_exec(&proc), opts
|
68
113
|
end
|
114
|
+
|
69
115
|
true
|
70
116
|
end
|
71
117
|
|
72
|
-
#
|
118
|
+
# @api public
|
119
|
+
#
|
120
|
+
# Write single attribute with given value. Value will be casted
|
121
|
+
# to defined attribute type.
|
122
|
+
#
|
123
|
+
# @param [ String, Symbol ] name Attribute name.
|
124
|
+
# @param [ Object ] value Value to write.
|
125
|
+
# @raise [ ArgumentError ] If no attribute with given name is defined.
|
73
126
|
#
|
74
127
|
def write_attribute(name, value, opts = {})
|
75
128
|
if (type = self.class.attribute_types[name.to_sym]).nil?
|
76
|
-
raise "Unknown attribute `#{name}`."
|
129
|
+
raise ArgumentError.new "Unknown attribute `#{name}`."
|
77
130
|
end
|
78
131
|
|
79
132
|
write_raw_attribute name, value.nil? ? nil : type.cast(value), opts
|
80
133
|
end
|
81
134
|
|
82
|
-
#
|
83
|
-
#
|
135
|
+
# @api private
|
136
|
+
#
|
137
|
+
# Write an attribute without checking type or existence or casting
|
138
|
+
# value to attributes type. Value be stored in an instance variable
|
139
|
+
# named after attribute name.
|
140
|
+
#
|
141
|
+
# @param [ String, Symbol ] name Attribute name.
|
142
|
+
# @param [ Object ] value Attribute value.
|
84
143
|
#
|
85
144
|
def write_raw_attribute(name, value, _ = {})
|
86
145
|
instance_variable_set :"@#{name}", value
|
87
146
|
end
|
88
147
|
|
89
|
-
module ClassMethods
|
148
|
+
module ClassMethods
|
90
149
|
|
150
|
+
# @api public
|
151
|
+
#
|
91
152
|
# Define a model attribute by name and type. Will create getter and
|
92
153
|
# setter for given attribute name. Existing methods will be overridden.
|
93
154
|
#
|
155
|
+
# Available types can be found in `Acfs::Model::Attributes::*`.
|
156
|
+
#
|
157
|
+
# @example
|
94
158
|
# class User
|
95
159
|
# include Acfs::Model
|
96
160
|
# attribute :name, :string, default: 'Anon'
|
161
|
+
# attribute :email, :string, default: lambda{ "#{name}@example.org"}
|
97
162
|
# end
|
98
163
|
#
|
99
|
-
#
|
164
|
+
# @param [ #to_sym ] name Attribute name.
|
165
|
+
# @param [ Symbol, String, Class ] type Attribute type identifier or type class.
|
100
166
|
#
|
101
167
|
def attribute(name, type, opts = {})
|
102
168
|
if type.is_a? Symbol or type.is_a? String
|
@@ -106,8 +172,11 @@ module Acfs::Model
|
|
106
172
|
define_attribute name.to_sym, type, opts
|
107
173
|
end
|
108
174
|
|
175
|
+
# @api public
|
176
|
+
#
|
109
177
|
# Return list of possible attributes and default values for this model class.
|
110
178
|
#
|
179
|
+
# @example
|
111
180
|
# class User
|
112
181
|
# include Acfs::Model
|
113
182
|
# attribute :name, :string
|
@@ -115,20 +184,36 @@ module Acfs::Model
|
|
115
184
|
# end
|
116
185
|
# User.attributes # => { "name": nil, "age": 25 }
|
117
186
|
#
|
187
|
+
# @return [ Hash{ String => Object, Proc } ] Attributes with default values.
|
188
|
+
#
|
118
189
|
def attributes
|
119
190
|
@attributes ||= {}
|
120
191
|
end
|
121
192
|
|
193
|
+
# @api public
|
194
|
+
#
|
122
195
|
# Return hash of attributes and there types.
|
123
196
|
#
|
197
|
+
# @example
|
198
|
+
# class User
|
199
|
+
# include Acfs::Model
|
200
|
+
# attribute :name, :string
|
201
|
+
# attribute :age, :integer, default: 25
|
202
|
+
# end
|
203
|
+
# User.attributes # => { "name": Acfs::Model::Attributes::String, "age": Acfs::Model::Attributes::Integer }
|
204
|
+
#
|
205
|
+
# @return [ Hash{ Symbol => Class } ] Attributes and their types.
|
206
|
+
#
|
124
207
|
def attribute_types
|
125
208
|
@attribute_types ||= {}
|
126
209
|
end
|
127
210
|
|
128
211
|
private
|
129
|
-
def define_attribute(name, type, opts = {})
|
212
|
+
def define_attribute(name, type, opts = {})
|
213
|
+
name = name.to_s
|
130
214
|
default_value = opts.has_key?(:default) ? opts[:default] : nil
|
131
215
|
default_value = type.cast default_value unless default_value.is_a? Proc or default_value.nil?
|
216
|
+
|
132
217
|
attributes[name] = default_value
|
133
218
|
attribute_types[name.to_sym] = type
|
134
219
|
define_attribute_method name
|
@@ -147,7 +232,7 @@ end
|
|
147
232
|
|
148
233
|
# Load attribute type classes.
|
149
234
|
#
|
150
|
-
Dir[File.dirname(__FILE__) +
|
235
|
+
Dir[File.dirname(__FILE__) + '/attributes/*.rb'].sort.each do |path|
|
151
236
|
filename = File.basename(path)
|
152
237
|
require "acfs/model/attributes/#{filename}"
|
153
238
|
end
|
@@ -1,8 +1,11 @@
|
|
1
1
|
module Acfs::Model
|
2
2
|
module Attributes
|
3
3
|
|
4
|
+
# @api public
|
5
|
+
#
|
4
6
|
# Boolean attribute type. Use it in your model as an attribute type:
|
5
7
|
#
|
8
|
+
# @example
|
6
9
|
# class User
|
7
10
|
# include Acfs::Model
|
8
11
|
# attribute :name, :boolean
|
@@ -13,10 +16,17 @@ module Acfs::Model
|
|
13
16
|
#
|
14
17
|
# true, on, yes
|
15
18
|
#
|
16
|
-
module Boolean
|
19
|
+
module Boolean
|
17
20
|
|
18
21
|
TRUE_VALUES = %w(true on yes)
|
19
22
|
|
23
|
+
# @api public
|
24
|
+
#
|
25
|
+
# Cast given object to boolean.
|
26
|
+
#
|
27
|
+
# @param [Object] obj Object to cast.
|
28
|
+
# @return [TrueClass, FalseClass] Casted boolean.
|
29
|
+
#
|
20
30
|
def self.cast(obj)
|
21
31
|
return true if obj.is_a? TrueClass
|
22
32
|
return false if obj.is_a? FalseClass
|
@@ -1,15 +1,25 @@
|
|
1
1
|
module Acfs::Model
|
2
2
|
module Attributes
|
3
3
|
|
4
|
+
# @api public
|
5
|
+
#
|
4
6
|
# Integer attribute type. Use it in your model as an attribute type:
|
5
7
|
#
|
8
|
+
# @example
|
6
9
|
# class User
|
7
10
|
# include Acfs::Model
|
8
11
|
# attribute :name, :integer
|
9
12
|
# end
|
10
13
|
#
|
11
|
-
module Integer
|
14
|
+
module Integer
|
12
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
|
+
#
|
13
23
|
def self.cast(obj)
|
14
24
|
obj.to_i
|
15
25
|
end
|
@@ -1,15 +1,25 @@
|
|
1
1
|
module Acfs::Model
|
2
2
|
module Attributes
|
3
3
|
|
4
|
+
# @api public
|
5
|
+
#
|
4
6
|
# String attribute type. Use it in your model as an attribute type:
|
5
7
|
#
|
8
|
+
# @example
|
6
9
|
# class User
|
7
10
|
# include Acfs::Model
|
8
11
|
# attribute :name, :string
|
9
12
|
# end
|
10
13
|
#
|
11
|
-
module String
|
14
|
+
module String
|
12
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
|
+
#
|
13
23
|
def self.cast(obj)
|
14
24
|
obj.to_s
|
15
25
|
end
|
data/lib/acfs/model/dirty.rb
CHANGED
@@ -7,12 +7,16 @@ module Acfs
|
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
include ActiveModel::Dirty
|
9
9
|
|
10
|
-
#
|
10
|
+
# @api private
|
11
|
+
#
|
12
|
+
# Resets all changes. Does not touch previous changes.
|
11
13
|
#
|
12
14
|
def reset_changes
|
13
15
|
changed_attributes.clear
|
14
16
|
end
|
15
17
|
|
18
|
+
# @api private
|
19
|
+
#
|
16
20
|
# Save current changes as previous changes and reset
|
17
21
|
# current one.
|
18
22
|
#
|
@@ -21,15 +25,21 @@ module Acfs
|
|
21
25
|
reset_changes
|
22
26
|
end
|
23
27
|
|
24
|
-
|
25
|
-
|
28
|
+
# @api private
|
29
|
+
#
|
30
|
+
def save!(*)
|
31
|
+
super.tap { |_| swap_changes }
|
26
32
|
end
|
27
33
|
|
28
|
-
|
34
|
+
# @api private
|
35
|
+
#
|
36
|
+
def loaded!
|
29
37
|
reset_changes
|
30
38
|
super
|
31
39
|
end
|
32
40
|
|
41
|
+
# @api private
|
42
|
+
#
|
33
43
|
def write_raw_attribute(name, value, opts = {}) # :nodoc:
|
34
44
|
attribute_will_change! name if opts[:change].nil? or opts[:change]
|
35
45
|
super
|