acfs 0.16.0 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|