a13g 0.1.0.beta3 → 0.1.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,68 @@
1
+ module A13g
2
+ # This message class is based on stomp message. Should be used by all
3
+ # adapter to representating of received messages.
4
+ class Message
5
+ attr_accessor :headers
6
+ attr_reader :body, :command, :destination
7
+ attr_reader :original
8
+ attr_reader :id
9
+ attr_accessor :ack
10
+
11
+ # Constructor.
12
+ #
13
+ # @param [Object] original
14
+ # original object of received message
15
+ # @param [String] id
16
+ # message id
17
+ # @param [String] body
18
+ # message content
19
+ # @param [String] command
20
+ # type of received message, eg. MESSAGE, ERROR, etc...
21
+ # @param [A13g::Adapters::AbstractAdapter] connection
22
+ # connection related with message
23
+ #
24
+ # @api public
25
+ def initialize(original, id, headers, body, command='MESSAGE', connection=Base.connection)
26
+ @original = original
27
+ @headers, @body, @command = headers, body, command
28
+ @destination = Destination.new(@headers['destination'])
29
+ @id = id
30
+ @connection = connection
31
+ @ack = false
32
+ end
33
+
34
+ def to_s # :nodoc:
35
+ "<A13g::Message @id='#{id} @body='#{body}' @headers='#{headers.inspect}' @command='#{command}'>"
36
+ end
37
+
38
+ # Shortcut to acknowledge message.
39
+ #
40
+ # @api public
41
+ def ack!
42
+ @connection.ack(@original)
43
+ @ack = true
44
+ @connection.logger.info(">> (#{@connection.url}#{@destination.name}): ACK #{@id}")
45
+ end
46
+
47
+ # Returns true when message has been acknowledged
48
+ #
49
+ # @return [Boolean]
50
+ #
51
+ # @api public
52
+ def ack?
53
+ !!@ack
54
+ end
55
+
56
+ # It says to processor, that message processing should be aborded.
57
+ #
58
+ # @api public
59
+ def abort!
60
+ raise AbortMessage
61
+ end
62
+
63
+ # It says to processor, that message should be ignored.
64
+ def ignore!
65
+ raise IgnoreMessage
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,107 @@
1
+ module A13g
2
+ # Mixin for publishing messages.
3
+ #
4
+ # === Simple publishing:
5
+ #
6
+ # include A13g::Producer
7
+ # publish("/queue/Example", "This is test...")
8
+ #
9
+ # === Publishing to different hosts:
10
+ #
11
+ # A13g.setup(:first, :adapter => 'stomp', {}, :host => 'first.host.com')
12
+ # A13g.setup(:second, :adapter => 'stomp', {}, :host => 'second.host.com')
13
+ #
14
+ # publish("/queue/Example", "My first message", {}, :first) # will be sent to first.host.com
15
+ # publish("/queue/Example", "My second message", {}, :first) # will be sent to second.host.com
16
+ #
17
+ # === Access directly from A13g:
18
+ #
19
+ # A13g.publish("/queue/Example", "Message from A13g..."
20
+ #
21
+ # === Transactions:
22
+ #
23
+ # begin("trans")
24
+ # publish("/queue/Example", "First message...", { :transaction => "trans" })
25
+ # publish("/queue/Example", "Second message...", { :transaction => "trans" })
26
+ # publish("/queue/Example", "Third message...", { :transaction => "trans" })
27
+ # commit("trans")
28
+ module Producer
29
+ # Publishes message to specified destination.
30
+ #
31
+ # @param [String] destination
32
+ # where message should be sent
33
+ # @param [String] message
34
+ # text to send
35
+ # @param [Hash] headers
36
+ # message headers
37
+ # @param [Symbol] context
38
+ # connection name, which should be used
39
+ # @param [Array] args
40
+ # additional arguments
41
+ #
42
+ # @api public
43
+ def publish(destination, message, headers={}, context=nil, *args)
44
+ context ||= a13g_context
45
+ conn = Base.connection(context)
46
+ conn.logger.info(">> " + Utils.format_message("#{conn.url}#{destination}", message, headers))
47
+ conn.publish(destination, message, headers, *args)
48
+ end
49
+
50
+ # Begins transaction.
51
+ #
52
+ # @param [String, Symbol] name
53
+ # transaction id
54
+ # @param [Hash] headers
55
+ # transaction additional headers
56
+ # @param [Symbol] context
57
+ # connection name, which should be used
58
+ # @param [Array] args
59
+ # additional arguments
60
+ #
61
+ # @api public
62
+ def begin(name, headers={}, context=nil, *args)
63
+ context ||= a13g_context
64
+ conn = Base.connection(context)
65
+ conn.logger.info("Begin transaction at #{conn.url}#{destination}")
66
+ conn.begin(name, headers, *args)
67
+ end
68
+
69
+ # Commit specified transaction. Params the same as #begin method.
70
+ #
71
+ # @see A13g::Producer#begin
72
+ #
73
+ # @api public
74
+ def commit(name, headers={}, context=nil, *args)
75
+ context ||= a13g_context
76
+ conn = Base.connection(context)
77
+ conn.logger.info("Commit transaction to #{conn.url}#{destination}")
78
+ conn.commit(name, headers, *args)
79
+ end
80
+
81
+ # Abort specified transaction. Params the same as #begin method.
82
+ #
83
+ # @see A13g::Producer#begin
84
+ #
85
+ # @api public
86
+ def abort(name, headers={}, context=nil, *args)
87
+ context ||= a13g_context
88
+ conn = Base.connection(context)
89
+ conn.logger.info("Abort transaction at #{conn.url}#{destination}")
90
+ conn.abort(name, headers, *args)
91
+ end
92
+
93
+ protected
94
+
95
+ # Current context for publishing.
96
+ #
97
+ # @api semipublic
98
+ def a13g_context
99
+ if respond_to?(:context)
100
+ context
101
+ else
102
+ :default
103
+ end
104
+ end
105
+ end
106
+ end
107
+
@@ -0,0 +1,4 @@
1
+ require "rails"
2
+ #require "a13g"
3
+
4
+ A13g::Base.setup(:default)
@@ -0,0 +1,31 @@
1
+ # Capistrano Recipes for managing active_messaging
2
+ #
3
+ # Add these callbacks to have the active_messaging process restart when the server
4
+ # is restarted:
5
+ #
6
+ # after "deploy:stop", "active_messaging:stop"
7
+ # after "deploy:start", "active_messaging:start"
8
+ # after "deploy:restart", "active_messaging:restart"
9
+
10
+ Capistrano::Configuration.instance.load do
11
+ namespace :delayed_job do
12
+ def rails_env
13
+ fetch(:rails_env, false) ? "RAILS_ENV=#{fetch(:rails_env)}" : ''
14
+ end
15
+
16
+ desc "Stop the active_messaging process"
17
+ task :stop, :roles => :app do
18
+ run "cd #{current_path};#{rails_env} script/messaging stop"
19
+ end
20
+
21
+ desc "Start the active_messaging process"
22
+ task :start, :roles => :app do
23
+ run "cd #{current_path};#{rails_env} script/messaging start"
24
+ end
25
+
26
+ desc "Restart the active_messaging process"
27
+ task :restart, :roles => :app do
28
+ run "cd #{current_path};#{rails_env} script/messaging restart"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,123 @@
1
+ module A13g
2
+ class Subscription
3
+ # Special class for list of subscribed destinations.
4
+ class DestinationsList < Hash; end
5
+
6
+ attr_reader :consumer, :destination, :headers, :connection, :args
7
+
8
+ # List of all defined destinations.
9
+ #
10
+ # @api public
11
+ cattr_accessor :all
12
+ @@all = DestinationsList.new
13
+
14
+ # Constructor.
15
+ #
16
+ # @param [A13g::Consumer] consumer
17
+ # consumer class
18
+ # @param [String] destination
19
+ # subscribed destination
20
+ # @param [Hash] headers
21
+ # additional connection headers
22
+ # @param [A13g::Adapters::AbstractAdapter] connection
23
+ # connection object related with subscription
24
+ # @param [Array] args
25
+ # additional subscription arguments
26
+ #
27
+ # @api public
28
+ def initialize(consumer, destination, connection=nil, headers={}, *args)
29
+ @consumer = consumer
30
+ @args = args
31
+ @connection = connection
32
+ @destination, @headers = destination, headers
33
+ @headers['persistent'] = true
34
+ @headers['id'] = @consumer.class.to_s if has_consumer?
35
+ end
36
+
37
+ # Returns `true` if consumer is assigned to this subscription.
38
+ #
39
+ # @return [Boolean]
40
+ # is there related consumer?
41
+ #
42
+ # @api public
43
+ def has_consumer?
44
+ !!consumer
45
+ end
46
+
47
+ # Subscribes current destination.
48
+ #
49
+ # @api public
50
+ def subscribe
51
+ if has_consumer?
52
+ connection.logger.debug("Destination #{connection.url}#{destination} subscribed by #{consumer.class.to_s}")
53
+ connection.subscribe(destination, headers, *args)
54
+ end
55
+ end
56
+
57
+ # Unsubscribes destination.
58
+ #
59
+ # @api public
60
+ def unsubscribe
61
+ connection.unsubscribe(destination, headers, *args)
62
+ end
63
+
64
+ # Port to publishing in current destination.
65
+ #
66
+ # @param [String] message
67
+ # text to send
68
+ # @param [Hash] headers
69
+ # additional connection headers
70
+ # @param [Array] args
71
+ # additional arguments
72
+ #
73
+ # @api public
74
+ def publish(message, headers={}, *args) # :nodoc:
75
+ headers = self.headers.merge(headers)
76
+ connection.publish(destination, message, headers, *args)
77
+ end
78
+
79
+ # Find destination using specified conditions.
80
+ #
81
+ # @param [Array] conditions
82
+ # key only, or name of destination with connection object
83
+ #
84
+ # @return [A13g::Subscription]
85
+ # matching subscription
86
+ #
87
+ # @api public
88
+ def self.find(*conditions)
89
+ if conditions.size == 1
90
+ klass = conditions[0]
91
+ if all.key?(klass.to_s)
92
+ return all[klass.to_s]
93
+ else
94
+ raise DestinationNotDefinedError, "#{klass.to_s} don't subscribes any destination"
95
+ end
96
+ elsif conditions.size < 3
97
+ destination, connection = conditions[0], conditions[1]
98
+ return all.values.find {|x| x.destination == destination.to_s && x.connection === connection}
99
+ else
100
+ raise ArgumentError, 'To many actual parameters'
101
+ end
102
+
103
+ end
104
+
105
+ # Creates new subscription. Parameters the same as #initialize method.
106
+ #
107
+ # @see A13g::Subscription#initialize
108
+ #
109
+ # @return [A13g::Subscription]
110
+ # created subscription
111
+ #
112
+ # @api public
113
+ def self.create(consumer, destination, headers={}, connection=Base.connection, *args)
114
+ unless subscription = find(destination, connection)
115
+ return all[consumer.class.name] = new(consumer, destination, connection, headers, *args)
116
+ else
117
+ raise DestinationAlreadySubscribedError,
118
+ "Destination #{conneciton.url}#{destination} has been already subscribed by `#{consumer.class.name}`"
119
+ end
120
+ return subscription
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,194 @@
1
+ require "time" # httpdate
2
+ # ==== Public Extlib Logger API
3
+ #
4
+ # To replace an existing logger with a new one:
5
+ # Extlib::Logger.set_log(log{String, IO},level{Symbol, String})
6
+ #
7
+ # Available logging levels are
8
+ # Extlib::Logger::{ Fatal, Error, Warn, Info, Debug }
9
+ #
10
+ # Logging via:
11
+ # Extlib.logger.fatal(message<String>,&block)
12
+ # Extlib.logger.error(message<String>,&block)
13
+ # Extlib.logger.warn(message<String>,&block)
14
+ # Extlib.logger.info(message<String>,&block)
15
+ # Extlib.logger.debug(message<String>,&block)
16
+ #
17
+ # Logging with autoflush:
18
+ # Extlib.logger.fatal!(message<String>,&block)
19
+ # Extlib.logger.error!(message<String>,&block)
20
+ # Extlib.logger.warn!(message<String>,&block)
21
+ # Extlib.logger.info!(message<String>,&block)
22
+ # Extlib.logger.debug!(message<String>,&block)
23
+ #
24
+ # Flush the buffer to
25
+ # Extlib.logger.flush
26
+ #
27
+ # Remove the current log object
28
+ # Extlib.logger.close
29
+ #
30
+ # ==== Private Extlib Logger API
31
+ #
32
+ # To initialize the logger you create a new object, proxies to set_log.
33
+ # Extlib::Logger.new(log{String, IO},level{Symbol, String})
34
+ module A13g
35
+ class Logger
36
+
37
+ attr_accessor :level
38
+ attr_accessor :delimiter
39
+ attr_accessor :auto_flush
40
+ attr_reader :buffer
41
+ attr_reader :log
42
+ attr_reader :init_args
43
+
44
+ # ==== Notes
45
+ # Ruby (standard) logger levels:
46
+ # :fatal:: An unhandleable error that results in a program crash
47
+ # :error:: A handleable error condition
48
+ # :warn:: A warning
49
+ # :info:: generic (useful) information about system operation
50
+ # :debug:: low-level information for developers
51
+ Levels =
52
+ {
53
+ :fatal => 7,
54
+ :error => 6,
55
+ :warn => 4,
56
+ :info => 3,
57
+ :debug => 0
58
+ }
59
+
60
+ private
61
+
62
+ # Readies a log for writing.
63
+ #
64
+ # ==== Parameters
65
+ # log<IO, String>:: Either an IO object or a name of a logfile.
66
+ def initialize_log(log)
67
+ close if @log # be sure that we don't leave open files laying around.
68
+
69
+ if log.respond_to?(:write)
70
+ @log = log
71
+ elsif File.exist?(log)
72
+ @log = open(log, (File::WRONLY | File::APPEND))
73
+ @log.sync = true
74
+ else
75
+ FileUtils.mkdir_p(File.dirname(log)) unless File.directory?(File.dirname(log))
76
+ @log = open(log, (File::WRONLY | File::APPEND | File::CREAT))
77
+ @log.sync = true
78
+ @log.write("#{Time.now.httpdate} #{delimiter} info #{delimiter} Logfile created\n")
79
+ end
80
+ end
81
+
82
+ public
83
+
84
+ # To initialize the logger you create a new object, proxies to set_log.
85
+ #
86
+ # ==== Parameters
87
+ # *args:: Arguments to create the log from. See set_logs for specifics.
88
+ def initialize(*args)
89
+ @init_args = args
90
+ set_log(*args)
91
+ self.auto_flush = true
92
+ A13g.logger = self
93
+ end
94
+
95
+ # Replaces an existing logger with a new one.
96
+ #
97
+ # ==== Parameters
98
+ # log<IO, String>:: Either an IO object or a name of a logfile.
99
+ # log_level<~to_sym>::
100
+ # The log level from, e.g. :fatal or :info. Defaults to :error in the
101
+ # production environment and :debug otherwise.
102
+ # delimiter<String>::
103
+ # Delimiter to use between message sections. Defaults to " ~ ".
104
+ # auto_flush<Boolean>::
105
+ # Whether the log should automatically flush after new messages are
106
+ # added. Defaults to false.
107
+ def set_log(log, log_level = nil, delimiter = " ~ ", auto_flush = false)
108
+ if log_level && Levels[log_level.to_sym]
109
+ @level = Levels[log_level.to_sym]
110
+ else
111
+ @level = Levels[:debug]
112
+ end
113
+ @buffer = []
114
+ @delimiter = delimiter
115
+ @auto_flush = auto_flush
116
+
117
+ initialize_log(log)
118
+ end
119
+
120
+ # Flush the entire buffer to the log object.
121
+ def flush
122
+ return unless @buffer.size > 0
123
+ @log.write(@buffer.slice!(0..-1).join)
124
+ end
125
+
126
+ # Close and remove the current log object.
127
+ def close
128
+ flush
129
+ @log.close if @log.respond_to?(:close) && !@log.tty?
130
+ @log = nil
131
+ end
132
+
133
+ # Appends a message to the log. The methods yield to an optional block and
134
+ # the output of this block will be appended to the message.
135
+ #
136
+ # ==== Parameters
137
+ # string<String>:: The message to be logged. Defaults to nil.
138
+ #
139
+ # ==== Returns
140
+ # String:: The resulting message added to the log file.
141
+ def <<(string = nil)
142
+ message = ""
143
+ message << delimiter
144
+ message << string if string
145
+ message << "\n" unless message[-1] == ?\n
146
+ @buffer << message
147
+ flush if @auto_flush
148
+
149
+ message
150
+ end
151
+ alias :push :<<
152
+
153
+ # Generate the logging methods for Extlib.logger for each log level.
154
+ Levels.each_pair do |name, number|
155
+ class_eval <<-LEVELMETHODS, __FILE__, __LINE__
156
+
157
+ # Appends a message to the log if the log level is at least as high as
158
+ # the log level of the logger.
159
+ #
160
+ # ==== Parameters
161
+ # string<String>:: The message to be logged. Defaults to nil.
162
+ #
163
+ # ==== Returns
164
+ # self:: The logger object for chaining.
165
+ def #{name}(message = nil)
166
+ self << message if #{number} >= level
167
+ self
168
+ end
169
+
170
+ # Appends a message to the log if the log level is at least as high as
171
+ # the log level of the logger. The bang! version of the method also auto
172
+ # flushes the log buffer to disk.
173
+ #
174
+ # ==== Parameters
175
+ # string<String>:: The message to be logged. Defaults to nil.
176
+ #
177
+ # ==== Returns
178
+ # self:: The logger object for chaining.
179
+ def #{name}!(message = nil)
180
+ self << message if #{number} >= level
181
+ flush if #{number} >= level
182
+ self
183
+ end
184
+
185
+ # ==== Returns
186
+ # Boolean:: True if this level will be logged by this logger.
187
+ def #{name}?
188
+ #{number} >= level
189
+ end
190
+ LEVELMETHODS
191
+ end
192
+
193
+ end
194
+ end