ayl 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +0 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +41 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +44 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/lib/ayl.rb +8 -0
- data/lib/ayl/engine.rb +52 -0
- data/lib/ayl/extensions.rb +74 -0
- data/lib/ayl/logger.rb +24 -0
- data/lib/ayl/logging.rb +14 -0
- data/lib/ayl/message.rb +49 -0
- data/lib/ayl/message_options.rb +32 -0
- data/lib/ayl/unrecoverable_message_exception.rb +6 -0
- data/lib/ayl/worker.rb +19 -0
- data/spec/engine_spec.rb +86 -0
- data/spec/extensions_spec.rb +45 -0
- data/spec/logger_spec.rb +57 -0
- data/spec/message_options_spec.rb +95 -0
- data/spec/message_spec.rb +102 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/worker_spec.rb +26 -0
- metadata +127 -0
data/.document
ADDED
data/.rspec
ADDED
File without changes
|
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "rspec", ">= 2.3.0"
|
10
|
+
gem "bundler", ">= 1.0.0"
|
11
|
+
gem "jeweler", ">= 1.6.4"
|
12
|
+
gem "rcov", ">= 0"
|
13
|
+
gem 'pry'
|
14
|
+
end
|
15
|
+
|
16
|
+
group :test do
|
17
|
+
gem 'pry'
|
18
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
coderay (0.9.8)
|
5
|
+
diff-lcs (1.1.3)
|
6
|
+
git (1.2.5)
|
7
|
+
jeweler (1.6.4)
|
8
|
+
bundler (~> 1.0)
|
9
|
+
git (>= 1.2.5)
|
10
|
+
rake
|
11
|
+
method_source (0.6.7)
|
12
|
+
ruby_parser (>= 2.3.1)
|
13
|
+
pry (0.9.7.4)
|
14
|
+
coderay (~> 0.9.8)
|
15
|
+
method_source (~> 0.6.7)
|
16
|
+
ruby_parser (>= 2.3.1)
|
17
|
+
slop (~> 2.1.0)
|
18
|
+
rake (0.9.2.2)
|
19
|
+
rcov (0.9.11)
|
20
|
+
rspec (2.7.0)
|
21
|
+
rspec-core (~> 2.7.0)
|
22
|
+
rspec-expectations (~> 2.7.0)
|
23
|
+
rspec-mocks (~> 2.7.0)
|
24
|
+
rspec-core (2.7.1)
|
25
|
+
rspec-expectations (2.7.0)
|
26
|
+
diff-lcs (~> 1.1.2)
|
27
|
+
rspec-mocks (2.7.0)
|
28
|
+
ruby_parser (2.3.1)
|
29
|
+
sexp_processor (~> 3.0)
|
30
|
+
sexp_processor (3.0.8)
|
31
|
+
slop (2.1.0)
|
32
|
+
|
33
|
+
PLATFORMS
|
34
|
+
ruby
|
35
|
+
|
36
|
+
DEPENDENCIES
|
37
|
+
bundler (>= 1.0.0)
|
38
|
+
jeweler (>= 1.6.4)
|
39
|
+
pry
|
40
|
+
rcov
|
41
|
+
rspec (>= 2.3.0)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 j0hnds@gmail.com
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
= ayl
|
2
|
+
|
3
|
+
A small framework to support asynchronous method calls in Ruby.
|
4
|
+
|
5
|
+
== Description
|
6
|
+
|
7
|
+
ayl (At Your Leisure or As You Like or ...) is a small framework that simplifies the process of implementing
|
8
|
+
asynchronous method calls in Ruby. Fundamentally, it works by handing off a method call to be executed in a
|
9
|
+
separate processing thread (maybe a thread, maybe a separate process, etc.).
|
10
|
+
|
11
|
+
The actual implementation of the code that executes the methods has
|
12
|
+
been abstracted away from the general framework. You can implement any
|
13
|
+
"Engine" you like to provide the asynchronous functionality. A
|
14
|
+
reference implementation of an engine using beanstalk
|
15
|
+
({beanstalkd}[http://kr.github.com/beanstalkd/],
|
16
|
+
{beanstalk-client}[http://beanstalk.rubyforge.org/]) as a queuing
|
17
|
+
mechanism is provided to give you a leg up:
|
18
|
+
{ayl-beanstalk}[https://github.com/j0hnds/ayl-beanstalk]. If you
|
19
|
+
decide to use a different mechanism, you can simply provide an
|
20
|
+
implementation of the Ayl::Engine interface.
|
21
|
+
|
22
|
+
If you would like to leverage asynchronous processes in your rails
|
23
|
+
application, take a look at
|
24
|
+
{ayl-rails}[https://github.com/j0hnds/ayl-rails]; it provides the
|
25
|
+
ability to asynchronize ActiveRecord callbacks (e.g. after_save,
|
26
|
+
after_update, etc.).
|
27
|
+
|
28
|
+
See the {wiki}[https://github.com/j0hnds/ayl/wiki/ayl-Home] for more detailed documentation.
|
29
|
+
|
30
|
+
== Contributing to ayl
|
31
|
+
|
32
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
33
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
34
|
+
* Fork the project
|
35
|
+
* Start a feature/bugfix branch
|
36
|
+
* Commit and push until you are happy with your contribution
|
37
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
38
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
39
|
+
|
40
|
+
== Copyright
|
41
|
+
|
42
|
+
Copyright (c) 2011 j0hnds@gmail.com. See LICENSE.txt for
|
43
|
+
further details.
|
44
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
# require 'bundler'
|
5
|
+
# begin
|
6
|
+
# Bundler.setup(:default, :development)
|
7
|
+
# rescue Bundler::BundlerError => e
|
8
|
+
# $stderr.puts e.message
|
9
|
+
# $stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
# exit e.status_code
|
11
|
+
# end
|
12
|
+
# require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "ayl"
|
18
|
+
gem.homepage = "http://github.com/j0hnds/ayl"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{Invoke code At Your Leisure}
|
21
|
+
gem.description = %Q{Invoke code At Your Leisure. ayl is a small framework that simplifies the process of implementing asynchronous method calls in Ruby.}
|
22
|
+
gem.email = "j0hnds@gmail.com"
|
23
|
+
gem.authors = ["Dave Sieh"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rspec/core'
|
29
|
+
require 'rspec/core/rake_task'
|
30
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
31
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
32
|
+
end
|
33
|
+
|
34
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
35
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
36
|
+
spec.rcov = true
|
37
|
+
end
|
38
|
+
|
39
|
+
task :default => :spec
|
40
|
+
|
41
|
+
require 'rdoc/task'
|
42
|
+
Rake::RDocTask.new do |rdoc|
|
43
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
44
|
+
|
45
|
+
rdoc.rdoc_dir = 'rdoc'
|
46
|
+
rdoc.title = "ayl #{version}"
|
47
|
+
rdoc.rdoc_files.include('README*')
|
48
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
49
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/lib/ayl.rb
ADDED
data/lib/ayl/engine.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Ayl
|
4
|
+
|
5
|
+
class Engine
|
6
|
+
include Singleton
|
7
|
+
include Ayl::Logging
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
def get_active_engine
|
12
|
+
self.engines ||= []
|
13
|
+
engine = self.engines.detect { |engine| engine.is_connected? }
|
14
|
+
engine ||= self.instance
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_engine(engine)
|
18
|
+
raise "engine must respond to asynchronous?" unless engine.respond_to?(:asynchronous?)
|
19
|
+
raise "engine must respond to is_connected?" unless engine.respond_to?(:is_connected?)
|
20
|
+
self.engines << engine
|
21
|
+
end
|
22
|
+
|
23
|
+
def clear_engines
|
24
|
+
@engines = []
|
25
|
+
end
|
26
|
+
|
27
|
+
def engines
|
28
|
+
@engines ||= []
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
# These methods define the API that must be implemented
|
34
|
+
# by all engines
|
35
|
+
def asynchronous?() false end
|
36
|
+
def is_connected?() true end
|
37
|
+
|
38
|
+
def submit(message)
|
39
|
+
log_call(:submit) do
|
40
|
+
worker.process_message(message)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def worker
|
47
|
+
@worker ||= Worker.new
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Ayl
|
2
|
+
|
3
|
+
module Extensions
|
4
|
+
|
5
|
+
def ayl_send(selector, *args)
|
6
|
+
ayl_send_opts(selector, {}, *args)
|
7
|
+
end
|
8
|
+
|
9
|
+
def ayl_send_opts(selector, opts, *args)
|
10
|
+
engine = Ayl::Engine.get_active_engine
|
11
|
+
message = Message.new(self, selector, MessageOptions.new(opts), *args)
|
12
|
+
engine.submit(message)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
module InstanceExtensions
|
18
|
+
def to_rrepr()
|
19
|
+
method = (respond_to? :get_cache) ? 'get_cache' : 'find'
|
20
|
+
"#{self.class.to_rrepr}.unscoped.#{method}(#{id.to_rrepr})"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
[ Array, Hash, Module, Numeric, Range, String, Symbol ].each { |c| c.send :include, Ayl::Extensions }
|
27
|
+
|
28
|
+
class Symbol
|
29
|
+
def to_rrepr() inspect end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Module
|
33
|
+
def to_rrepr() name end
|
34
|
+
end
|
35
|
+
|
36
|
+
class NilClass
|
37
|
+
def to_rrepr() inspect end
|
38
|
+
end
|
39
|
+
|
40
|
+
class FalseClass
|
41
|
+
def to_rrepr() inspect end
|
42
|
+
end
|
43
|
+
|
44
|
+
class TrueClass
|
45
|
+
def to_rrepr() inspect end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Numeric
|
49
|
+
def to_rrepr() inspect end
|
50
|
+
end
|
51
|
+
|
52
|
+
class String
|
53
|
+
def to_rrepr() inspect end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Array
|
57
|
+
def to_rrepr() '[' + map(&:to_rrepr).join(', ') + ']' end
|
58
|
+
end
|
59
|
+
|
60
|
+
class Hash
|
61
|
+
def to_rrepr() '{' + map{|k,v| k.to_rrepr + '=>' + v.to_rrepr}.join(', ') + '}' end
|
62
|
+
end
|
63
|
+
|
64
|
+
class Range
|
65
|
+
def to_rrepr() "(#{first.to_rrepr}#{exclude_end? ? '...' : '..'}#{last.to_rrepr})" end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Time
|
69
|
+
def to_rrepr() "Time.parse('#{self.inspect}')" end
|
70
|
+
end
|
71
|
+
|
72
|
+
class Date
|
73
|
+
def to_rrepr() "Date.parse('#{self.inspect}')" end
|
74
|
+
end
|
data/lib/ayl/logger.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Ayl
|
4
|
+
|
5
|
+
class Logger
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
attr_accessor :logger
|
9
|
+
|
10
|
+
LOG_METHODS = [ :debug, :error, :fatal, :info, :warn ]
|
11
|
+
|
12
|
+
LOG_METHODS.each do |method|
|
13
|
+
define_method(method) do |message|
|
14
|
+
if logger
|
15
|
+
logger.send(method, message)
|
16
|
+
else
|
17
|
+
Kernel.puts "#{method.upcase}: #{message}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/lib/ayl/logging.rb
ADDED
data/lib/ayl/message.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module Ayl
|
2
|
+
|
3
|
+
class Message
|
4
|
+
|
5
|
+
attr_accessor :object, :selector, :options, :arguments
|
6
|
+
|
7
|
+
def initialize(object, selector, opts, *args)
|
8
|
+
@object = object
|
9
|
+
@selector = selector
|
10
|
+
@options = opts
|
11
|
+
@arguments = args
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.from_hash(message_hash)
|
15
|
+
raise Ayl::UnrecoverableMessageException, "parameter must be a hash" unless message_hash.is_a?(Hash)
|
16
|
+
raise Ayl::UnrecoverableMessageException, "not a valid message hash" if message_hash[:type] != :ayl || message_hash[:code].nil?
|
17
|
+
raise Ayl::UnrecoverableMessageException, "No code provided in job: #{job.body}" if message_hash[:code].nil?
|
18
|
+
|
19
|
+
code = message_hash[:code]
|
20
|
+
|
21
|
+
Message.new(nil, nil, MessageOptions.new).tap do | m |
|
22
|
+
m.send(:message_hash=, message_hash)
|
23
|
+
m.send(:code=, code)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_rrepr
|
29
|
+
@code ||= %Q{#{@object.to_rrepr}.#{@selector}(#{@arguments.to_rrepr[1...-1]})}
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_hash
|
33
|
+
@message_hash ||= {
|
34
|
+
:type => :ayl,
|
35
|
+
:code => to_rrepr
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def evaluate(top_binding)
|
40
|
+
code_to_eval = to_rrepr
|
41
|
+
eval(code_to_eval, top_binding, code_to_eval, 1)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_writer :message_hash, :code
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Ayl
|
2
|
+
|
3
|
+
class MessageOptions
|
4
|
+
|
5
|
+
OPTIONS = [ :priority, :fuzz, :delay, :time_to_run, :queue_name ]
|
6
|
+
|
7
|
+
attr_accessor *OPTIONS
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_accessor :default_priority, :default_fuzz, :default_delay, :default_time_to_run, :default_queue_name
|
11
|
+
end
|
12
|
+
|
13
|
+
# Set the default options
|
14
|
+
self.default_priority = 512
|
15
|
+
self.default_fuzz = 0
|
16
|
+
self.default_delay = 0
|
17
|
+
self.default_time_to_run = 120
|
18
|
+
self.default_queue_name = 'default'
|
19
|
+
|
20
|
+
def initialize(opts=nil)
|
21
|
+
opts ||= {}
|
22
|
+
raise "parameter must be a hash" unless opts.is_a?(Hash)
|
23
|
+
unknown_options = opts.keys - OPTIONS
|
24
|
+
raise "unknown options specified: #{unknown_options}" unless unknown_options.empty?
|
25
|
+
OPTIONS.each do |o|
|
26
|
+
send("#{o}=".to_sym, opts.fetch(o, self.class.send("default_#{o}".to_sym)))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
data/lib/ayl/worker.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Ayl
|
2
|
+
|
3
|
+
class Worker
|
4
|
+
include Ayl::Logging
|
5
|
+
|
6
|
+
attr_accessor :eval_binding
|
7
|
+
|
8
|
+
def process_messages
|
9
|
+
logger.error "Attempt to invoke #{self.class.name}.process_messages failed"
|
10
|
+
raise "synchronous worker cannot receive messages"
|
11
|
+
end
|
12
|
+
|
13
|
+
def process_message(message)
|
14
|
+
message.evaluate(@eval_binding)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
data/spec/engine_spec.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ayl::Engine do
|
4
|
+
|
5
|
+
context "Engine Selection" do
|
6
|
+
|
7
|
+
it "should return the default (synchronous) engine if no other engines are configured" do
|
8
|
+
engine = Ayl::Engine.get_active_engine
|
9
|
+
|
10
|
+
engine.should_not be_nil
|
11
|
+
engine.should be_an_instance_of(Ayl::Engine)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should allow a different engine to be configured and selected if active" do
|
15
|
+
mock_engine = mock("FakeEngine")
|
16
|
+
mock_engine.should_receive(:respond_to?).with(:asynchronous?).and_return(true)
|
17
|
+
mock_engine.should_receive(:respond_to?).with(:is_connected?).and_return(true)
|
18
|
+
mock_engine.should_receive(:is_connected?).and_return(true)
|
19
|
+
|
20
|
+
Ayl::Engine.clear_engines
|
21
|
+
Ayl::Engine.add_engine(mock_engine)
|
22
|
+
|
23
|
+
Ayl::Engine.get_active_engine.should == mock_engine
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should allow a different engine to be configured but select the default if the new one is not active" do
|
27
|
+
mock_engine = mock("FakeEngine")
|
28
|
+
mock_engine.should_receive(:respond_to?).with(:asynchronous?).and_return(true)
|
29
|
+
mock_engine.should_receive(:respond_to?).with(:is_connected?).and_return(true)
|
30
|
+
mock_engine.should_receive(:is_connected?).and_return(false)
|
31
|
+
|
32
|
+
Ayl::Engine.clear_engines
|
33
|
+
Ayl::Engine.add_engine(mock_engine)
|
34
|
+
|
35
|
+
Ayl::Engine.get_active_engine.should == Ayl::Engine.instance
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should raise an exception if a new engine is added that doesn't respond to :asynchronous" do
|
39
|
+
mock_engine = mock("FakeEngine")
|
40
|
+
mock_engine.should_receive(:respond_to?).with(:asynchronous?).and_return(false)
|
41
|
+
|
42
|
+
lambda { Ayl::Engine.add_engine(mock_engine) }.should raise_error
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should raise an exception if a new engine is added that doesn't respond to :is_connected" do
|
46
|
+
mock_engine = mock("FakeEngine")
|
47
|
+
mock_engine.should_receive(:respond_to?).with(:asynchronous?).and_return(true)
|
48
|
+
mock_engine.should_receive(:respond_to?).with(:is_connected?).and_return(false)
|
49
|
+
|
50
|
+
lambda { Ayl::Engine.add_engine(mock_engine) }.should raise_error
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
context "Default Engine Behavior" do
|
56
|
+
|
57
|
+
before(:each) do
|
58
|
+
@default_engine = Ayl::Engine.instance
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should identify itself as synchronous" do
|
62
|
+
@default_engine.asynchronous?.should be_false
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should respond that it is connected" do
|
66
|
+
@default_engine.is_connected?.should be_true
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should simply execute the code provided in the message submission" do
|
70
|
+
mock_logger = mock("Ayl::Logger")
|
71
|
+
mock_logger.stub(:info)
|
72
|
+
|
73
|
+
Ayl::Logger.instance.logger = mock_logger
|
74
|
+
|
75
|
+
mock_message = mock("Ayl::Message")
|
76
|
+
|
77
|
+
mock_worker = mock("Ayl::Worker")
|
78
|
+
mock_worker.should_receive(:process_message).with(mock_message).and_return(8)
|
79
|
+
Ayl::Worker.should_receive(:new).and_return(mock_worker)
|
80
|
+
|
81
|
+
@default_engine.submit(mock_message).should == 8
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ayl::Extensions do
|
4
|
+
|
5
|
+
class ExtendedClass
|
6
|
+
include Ayl::Extensions
|
7
|
+
end
|
8
|
+
|
9
|
+
context "Send methods" do
|
10
|
+
|
11
|
+
before(:each) do
|
12
|
+
@cut = ExtendedClass.new
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should delegate the call to ayl_send to ayl_send_opts" do
|
16
|
+
@cut.should_receive(:ayl_send_opts).with(:something, {}, "arg1", "arg2")
|
17
|
+
@cut.ayl_send(:something, "arg1", "arg2")
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should have the message submit the call from ayl_send_opts" do
|
21
|
+
mock_engine = mock("Ayl::Engine")
|
22
|
+
|
23
|
+
Ayl::Engine.should_receive(:get_active_engine).and_return(mock_engine)
|
24
|
+
|
25
|
+
mock_message_opts = mock("Ayl::MessageOptions")
|
26
|
+
Ayl::MessageOptions.should_receive(:new).with({}).and_return(mock_message_opts)
|
27
|
+
|
28
|
+
mock_message = mock("Ayl::Message")
|
29
|
+
|
30
|
+
Ayl::Message.should_receive(:new).with(@cut, :something, mock_message_opts, "arg1", "arg2").and_return(mock_message)
|
31
|
+
mock_engine.should_receive(:submit).with(mock_message)
|
32
|
+
|
33
|
+
@cut.ayl_send_opts(:something, {}, "arg1", "arg2")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should extend a common set up classes" do
|
37
|
+
[ Array, Hash, Module, Numeric, Range, String, Symbol ].each do |c|
|
38
|
+
c.should respond_to :ayl_send
|
39
|
+
c.should respond_to :ayl_send_opts
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/spec/logger_spec.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ayl::Logger do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
Ayl::Logger.instance.logger = nil
|
7
|
+
end
|
8
|
+
|
9
|
+
context "Logger Configuration" do
|
10
|
+
|
11
|
+
it "should respond with nil if a logger isn't configured" do
|
12
|
+
Ayl::Logger.instance.logger.should be_nil
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should respond with the logger set if the logger is configured" do
|
16
|
+
mock_logger = mock("Logger")
|
17
|
+
|
18
|
+
Ayl::Logger.instance.logger = mock_logger
|
19
|
+
Ayl::Logger.instance.logger.should == mock_logger
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
context "Logging" do
|
25
|
+
|
26
|
+
it "should log calls to the logger through std out when no logger is configured" do
|
27
|
+
Kernel.should_receive(:puts).with("DEBUG: Debug message")
|
28
|
+
Kernel.should_receive(:puts).with("ERROR: Error message")
|
29
|
+
Kernel.should_receive(:puts).with("FATAL: Fatal message")
|
30
|
+
Kernel.should_receive(:puts).with("INFO: Info message")
|
31
|
+
Kernel.should_receive(:puts).with("WARN: Warn message")
|
32
|
+
|
33
|
+
Ayl::Logger.instance.logger = nil
|
34
|
+
|
35
|
+
[ :debug, :error, :fatal, :info, :warn ].each do |method|
|
36
|
+
Ayl::Logger.instance.send(method, "#{method.to_s.capitalize} message")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should log calls to the configured logger" do
|
41
|
+
mock_logger = mock("Logger")
|
42
|
+
mock_logger.should_receive(:debug).with("Debug message")
|
43
|
+
mock_logger.should_receive(:error).with("Error message")
|
44
|
+
mock_logger.should_receive(:fatal).with("Fatal message")
|
45
|
+
mock_logger.should_receive(:info).with("Info message")
|
46
|
+
mock_logger.should_receive(:warn).with("Warn message")
|
47
|
+
|
48
|
+
Ayl::Logger.instance.logger = mock_logger
|
49
|
+
|
50
|
+
[ :debug, :error, :fatal, :info, :warn ].each do |method|
|
51
|
+
Ayl::Logger.instance.send(method, "#{method.to_s.capitalize} message")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ayl::MessageOptions do
|
4
|
+
|
5
|
+
context "Option Default Accessors" do
|
6
|
+
|
7
|
+
it "should have the correct defaults" do
|
8
|
+
Ayl::MessageOptions.default_priority.should == 512
|
9
|
+
Ayl::MessageOptions.default_fuzz.should == 0
|
10
|
+
Ayl::MessageOptions.default_delay.should == 0
|
11
|
+
Ayl::MessageOptions.default_time_to_run.should == 120
|
12
|
+
Ayl::MessageOptions.default_queue_name.should == 'default'
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should reflect the changes if changes have been made" do
|
16
|
+
Ayl::MessageOptions.default_priority = 256
|
17
|
+
Ayl::MessageOptions.default_fuzz = 18
|
18
|
+
Ayl::MessageOptions.default_delay = 2300
|
19
|
+
Ayl::MessageOptions.default_time_to_run = 1
|
20
|
+
Ayl::MessageOptions.default_queue_name = 'different'
|
21
|
+
|
22
|
+
Ayl::MessageOptions.default_priority.should == 256
|
23
|
+
Ayl::MessageOptions.default_fuzz.should == 18
|
24
|
+
Ayl::MessageOptions.default_delay.should == 2300
|
25
|
+
Ayl::MessageOptions.default_time_to_run.should == 1
|
26
|
+
Ayl::MessageOptions.default_queue_name.should == 'different'
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
context "Initialization" do
|
32
|
+
|
33
|
+
it "should respond with the correct defaults when initialized with an empty hash" do
|
34
|
+
mo = Ayl::MessageOptions.new({})
|
35
|
+
|
36
|
+
mo.priority.should == Ayl::MessageOptions.default_priority
|
37
|
+
mo.fuzz.should == Ayl::MessageOptions.default_fuzz
|
38
|
+
mo.delay.should == Ayl::MessageOptions.default_delay
|
39
|
+
mo.time_to_run.should == Ayl::MessageOptions.default_time_to_run
|
40
|
+
mo.queue_name.should == Ayl::MessageOptions.default_queue_name
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should respond with the correct defaults when initialized with nil" do
|
44
|
+
mo = Ayl::MessageOptions.new(nil)
|
45
|
+
|
46
|
+
mo.priority.should == Ayl::MessageOptions.default_priority
|
47
|
+
mo.fuzz.should == Ayl::MessageOptions.default_fuzz
|
48
|
+
mo.delay.should == Ayl::MessageOptions.default_delay
|
49
|
+
mo.time_to_run.should == Ayl::MessageOptions.default_time_to_run
|
50
|
+
mo.queue_name.should == Ayl::MessageOptions.default_queue_name
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should respond with the correct defaults when initialized with no parameter" do
|
54
|
+
mo = Ayl::MessageOptions.new()
|
55
|
+
|
56
|
+
mo.priority.should == Ayl::MessageOptions.default_priority
|
57
|
+
mo.fuzz.should == Ayl::MessageOptions.default_fuzz
|
58
|
+
mo.delay.should == Ayl::MessageOptions.default_delay
|
59
|
+
mo.time_to_run.should == Ayl::MessageOptions.default_time_to_run
|
60
|
+
mo.queue_name.should == Ayl::MessageOptions.default_queue_name
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should respond with the correct values when all are specified in the options" do
|
64
|
+
opts = { :priority => 22, :fuzz => 88, :delay => 99, :time_to_run => 11, :queue_name => 'stub' }
|
65
|
+
mo = Ayl::MessageOptions.new(opts)
|
66
|
+
|
67
|
+
mo.priority.should == opts[:priority]
|
68
|
+
mo.fuzz.should == opts[:fuzz]
|
69
|
+
mo.delay.should == opts[:delay]
|
70
|
+
mo.time_to_run.should == opts[:time_to_run]
|
71
|
+
mo.queue_name.should == opts[:queue_name]
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should respond with the correct values when some values are specified in the options" do
|
75
|
+
opts = { :priority => 22, :fuzz => 88, :delay => 99 }
|
76
|
+
mo = Ayl::MessageOptions.new(opts)
|
77
|
+
|
78
|
+
mo.priority.should == opts[:priority]
|
79
|
+
mo.fuzz.should == opts[:fuzz]
|
80
|
+
mo.delay.should == opts[:delay]
|
81
|
+
mo.time_to_run.should == Ayl::MessageOptions.default_time_to_run
|
82
|
+
mo.queue_name.should == Ayl::MessageOptions.default_queue_name
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should raise an exception when an invalid option is provided in the hash" do
|
86
|
+
lambda { Ayl::MessageOptions.new({:bob => 'something' }) }.should raise_error
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should raise an exception when a non-hash is provided as a parameter" do
|
90
|
+
lambda { Ayl::MessageOptions.new(14) }.should raise_error
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ayl::Message do
|
4
|
+
|
5
|
+
context "Initialization" do
|
6
|
+
|
7
|
+
it "should accept an object, selector options and a set of arguments" do
|
8
|
+
options = Ayl::MessageOptions.new
|
9
|
+
m = Ayl::Message.new("object", :method_name, options, "arg1", "arg2")
|
10
|
+
|
11
|
+
m.object.should == "object"
|
12
|
+
m.selector.should == :method_name
|
13
|
+
m.options.should == options
|
14
|
+
m.arguments.should == [ "arg1", "arg2" ]
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should accept an object, selector options and no arguments" do
|
18
|
+
options = Ayl::MessageOptions.new
|
19
|
+
m = Ayl::Message.new("object", :method_name, options)
|
20
|
+
|
21
|
+
m.object.should == "object"
|
22
|
+
m.selector.should == :method_name
|
23
|
+
m.options.should == options
|
24
|
+
m.arguments.should == [ ]
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
context "Code generation" do
|
30
|
+
|
31
|
+
it "should generate code when arguments are present" do
|
32
|
+
options = Ayl::MessageOptions.new
|
33
|
+
m = Ayl::Message.new("object", :method_name, options, "arg1", "arg2")
|
34
|
+
m.to_rrepr.should == "\"object\".method_name(\"arg1\", \"arg2\")"
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should generate code when no arguments are present" do
|
38
|
+
options = Ayl::MessageOptions.new
|
39
|
+
m = Ayl::Message.new("object", :method_name, options)
|
40
|
+
m.to_rrepr.should == "\"object\".method_name()"
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
context "Message Packaging" do
|
46
|
+
|
47
|
+
it "should package up the message into a hash" do
|
48
|
+
options = Ayl::MessageOptions.new
|
49
|
+
m = Ayl::Message.new("object", :method_name, options, "arg1", "arg2")
|
50
|
+
m.to_hash.should == { :type => :ayl, :code => "\"object\".method_name(\"arg1\", \"arg2\")" }
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should be able to create a message from a hash with code that has arguments" do
|
54
|
+
m_hash = { :type => :ayl, :code => "\"object\".method_name(\"arg1\", \"arg2\")" }
|
55
|
+
m = Ayl::Message.from_hash(m_hash)
|
56
|
+
m.options.is_a?(Ayl::MessageOptions).should be_true
|
57
|
+
m.to_hash.should === m_hash
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should be able to create a message from a hash with code that has no arguments" do
|
61
|
+
m_hash = { :type => :ayl, :code => "\"object\".method_name()" }
|
62
|
+
m = Ayl::Message.from_hash(m_hash)
|
63
|
+
m.options.is_a?(Ayl::MessageOptions).should be_true
|
64
|
+
m.to_hash.should === m_hash
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should be able to create a message from a hash with code that has no arguments and no parens" do
|
68
|
+
m_hash = { :type => :ayl, :code => "\"object\".method_name" }
|
69
|
+
m = Ayl::Message.from_hash(m_hash)
|
70
|
+
m.options.is_a?(Ayl::MessageOptions).should be_true
|
71
|
+
m.to_hash.should === m_hash
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should be able to create a message from a hash with code that has one arguments with multiple parens" do
|
75
|
+
m_hash = { :type => :ayl, :code => "\"object\".method_name('string'.length())" }
|
76
|
+
m = Ayl::Message.from_hash(m_hash)
|
77
|
+
m.options.is_a?(Ayl::MessageOptions).should be_true
|
78
|
+
m.to_hash.should === m_hash
|
79
|
+
end
|
80
|
+
|
81
|
+
# Sample._ayl_after_create(Sample.find(106))
|
82
|
+
it "should be able to create a message from a hash with code that has one arguments with multiple parens" do
|
83
|
+
m_hash = { :type => :ayl, :code => "String._ayl_after_create(2.to_s(2))" }
|
84
|
+
m = Ayl::Message.from_hash(m_hash)
|
85
|
+
m.options.is_a?(Ayl::MessageOptions).should be_true
|
86
|
+
m.to_hash.should === m_hash
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
context "Code Evaluation" do
|
93
|
+
|
94
|
+
it "should evaluate the code associated with the message" do
|
95
|
+
array_of_nums = [ 3, 2, 8, 1 ]
|
96
|
+
m = Ayl::Message.new(array_of_nums, :length, Ayl::MessageOptions.new)
|
97
|
+
m.evaluate(binding).should == 4
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'ayl'
|
5
|
+
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
7
|
+
# in ./support/ and its subdirectories.
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
|
12
|
+
end
|
data/spec/worker_spec.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ayl::Worker do
|
4
|
+
|
5
|
+
context "Messages Processing" do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
Ayl::Logger.instance.logger = nil
|
9
|
+
@worker = Ayl::Worker.new
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should raise an exception if receive is called on an engine that is not asynchronous" do
|
13
|
+
Kernel.stub(:puts)
|
14
|
+
lambda { @worker.process_messages }.should raise_error(RuntimeError, "synchronous worker cannot receive messages")
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
context "Message Processing" do
|
20
|
+
|
21
|
+
it "should be able to invoke the code for a message" do
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ayl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Dave Sieh
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-11-20 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &84028120 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.3.0
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *84028120
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: bundler
|
27
|
+
requirement: &84027830 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.0.0
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *84027830
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: jeweler
|
38
|
+
requirement: &83964270 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.6.4
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *83964270
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rcov
|
49
|
+
requirement: &83964020 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *83964020
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: pry
|
60
|
+
requirement: &83963730 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *83963730
|
69
|
+
description: Invoke code At Your Leisure. ayl is a small framework that simplifies
|
70
|
+
the process of implementing asynchronous method calls in Ruby.
|
71
|
+
email: j0hnds@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files:
|
75
|
+
- LICENSE.txt
|
76
|
+
- README.rdoc
|
77
|
+
files:
|
78
|
+
- .document
|
79
|
+
- .rspec
|
80
|
+
- Gemfile
|
81
|
+
- Gemfile.lock
|
82
|
+
- LICENSE.txt
|
83
|
+
- README.rdoc
|
84
|
+
- Rakefile
|
85
|
+
- VERSION
|
86
|
+
- lib/ayl.rb
|
87
|
+
- lib/ayl/engine.rb
|
88
|
+
- lib/ayl/extensions.rb
|
89
|
+
- lib/ayl/logger.rb
|
90
|
+
- lib/ayl/logging.rb
|
91
|
+
- lib/ayl/message.rb
|
92
|
+
- lib/ayl/message_options.rb
|
93
|
+
- lib/ayl/unrecoverable_message_exception.rb
|
94
|
+
- lib/ayl/worker.rb
|
95
|
+
- spec/engine_spec.rb
|
96
|
+
- spec/extensions_spec.rb
|
97
|
+
- spec/logger_spec.rb
|
98
|
+
- spec/message_options_spec.rb
|
99
|
+
- spec/message_spec.rb
|
100
|
+
- spec/spec_helper.rb
|
101
|
+
- spec/worker_spec.rb
|
102
|
+
homepage: http://github.com/j0hnds/ayl
|
103
|
+
licenses:
|
104
|
+
- MIT
|
105
|
+
post_install_message:
|
106
|
+
rdoc_options: []
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
111
|
+
requirements:
|
112
|
+
- - ! '>='
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
117
|
+
requirements:
|
118
|
+
- - ! '>='
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
requirements: []
|
122
|
+
rubyforge_project:
|
123
|
+
rubygems_version: 1.8.10
|
124
|
+
signing_key:
|
125
|
+
specification_version: 3
|
126
|
+
summary: Invoke code At Your Leisure
|
127
|
+
test_files: []
|