ayl 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
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
@@ -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)
@@ -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.
@@ -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
+
@@ -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
@@ -0,0 +1,8 @@
1
+ require 'ayl/logging'
2
+ require 'ayl/extensions'
3
+ require 'ayl/message'
4
+ require 'ayl/engine'
5
+ require 'ayl/message_options'
6
+ require 'ayl/unrecoverable_message_exception'
7
+ require 'ayl/logger'
8
+ require 'ayl/worker'
@@ -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
@@ -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
@@ -0,0 +1,14 @@
1
+ module Ayl
2
+ module Logging
3
+
4
+ def logger() Logger.instance end
5
+
6
+ def log_call(method)
7
+ logger.info "#{self.class.name} invoking #{method}"
8
+ yield
9
+ ensure
10
+ logger.info "#{self.class.name} done invoking #{method}"
11
+ end
12
+
13
+ end
14
+ end
@@ -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
@@ -0,0 +1,6 @@
1
+ module Ayl
2
+
3
+ class UnrecoverableMessageException < RuntimeError
4
+ end
5
+
6
+ end
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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: []