vuderacha-orq 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.rdoc ADDED
@@ -0,0 +1,43 @@
1
+ = Introduction
2
+ ORQ (Object Relational Queueing) provides an abstraction for working with message queues directly
3
+ from your objects.
4
+
5
+ = Getting Started
6
+ Install ORQ with the following:
7
+ git clone git://github.com/vuderacha/orq.git
8
+ cd orq
9
+ rake install
10
+
11
+ Create a simple impulse:
12
+ # mymessage.rb
13
+ class MyMessage < ORQ::Impulse
14
+ field :text
15
+ field :interesting
16
+ end
17
+
18
+ Create your configuration file (assuming that you are running an AMQP broker on the default port):
19
+ # messaging.yml
20
+ development:
21
+ default:
22
+ driver: amqp
23
+
24
+ Create your application:
25
+ # testapp.rb
26
+ require 'rubygems'
27
+ require 'eventmachine'
28
+ require 'orq/impulse'
29
+ require 'mymessage'
30
+
31
+ ORQ::Impulse.configure File.join(File.dirname(__FILE__), 'messaging.yml'), 'development'
32
+
33
+ EM.run {
34
+ ORQ::Impulse.start
35
+
36
+ MyMessage.subscribe do |m|
37
+ puts "Got message: #{m.text} (#{m.interesting})"
38
+ end
39
+
40
+ EM.add_periodic_timer(1) do
41
+ MyMessage.new({:text => 'Text', :interesting => 'Cheese'}).fire!
42
+ end
43
+ }
data/Rakefile ADDED
@@ -0,0 +1,67 @@
1
+ require 'rake'
2
+ require 'spec/rake/spectask'
3
+
4
+ desc "Run all specs"
5
+ Spec::Rake::SpecTask.new('spec') do |t|
6
+ t.spec_opts = ["-cfs"]
7
+ t.spec_files = FileList['spec/**/*_spec.rb']
8
+ t.libs = ['lib', 'spec/support']
9
+ end
10
+
11
+ desc "Print specdocs"
12
+ Spec::Rake::SpecTask.new(:doc) do |t|
13
+ t.spec_opts = ["--format", "specdoc", "--dry-run"]
14
+ t.spec_files = FileList['spec/*_spec.rb']
15
+ end
16
+
17
+ desc "Generate RCov code coverage report"
18
+ Spec::Rake::SpecTask.new('rcov') do |t|
19
+ t.spec_files = FileList['spec/*_spec.rb']
20
+ t.rcov = true
21
+ t.rcov_opts = ['--exclude', 'examples']
22
+ end
23
+
24
+ task :default => :spec
25
+
26
+ ######################################################
27
+
28
+ require 'rake'
29
+ require 'rake/testtask'
30
+ require 'rake/clean'
31
+ require 'rake/gempackagetask'
32
+ require 'rake/rdoctask'
33
+ require 'fileutils'
34
+ include FileUtils
35
+ # $:.push File.join(File.dirname(__FILE__), 'lib')
36
+
37
+ begin
38
+ require 'jeweler'
39
+ Jeweler::Tasks.new do |s|
40
+ s.name = "orq"
41
+ s.summary = "Ruby Object Relational Queueing."
42
+ s.description = "ORQ is a queueing abstraction layer allowing messaging to be done directly from object."
43
+ s.author = "Paul Jones"
44
+ s.email = "pauljones23@gmail.com"
45
+ s.homepage = "http://github.com/vuderacha/orq/"
46
+
47
+ s.files = %w(Rakefile README.rdoc) + Dir.glob("{bin,lib,spec}/**/*")
48
+ s.require_path = "lib"
49
+ s.add_dependency('activesupport', '>= 2.2.2')
50
+ end
51
+ rescue LoadError
52
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
53
+ end
54
+
55
+ task :test => [ :spec ]
56
+
57
+ Rake::RDocTask.new do |t|
58
+ t.rdoc_dir = 'rdoc'
59
+ t.title = "ORQ -- Object Relational Queueing"
60
+ t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
61
+ t.options << '--charset' << 'utf-8'
62
+ t.rdoc_files.include('README.rdoc')
63
+ t.rdoc_files.include('lib/orq/*.rb')
64
+ end
65
+
66
+ CLEAN.include [ 'build/*', '**/*.o', '**/*.so', '**/*.a', 'lib/*-*', '**/*.log', 'pkg', 'lib/*.bundle', '*.gem', '.config' ]
67
+
@@ -0,0 +1,33 @@
1
+ require 'mq'
2
+
3
+ # Adapter providing access to AMQP based message systems from ORQ
4
+ class AmqpAdapter
5
+ def initialize(config)
6
+ @config = config
7
+ end
8
+
9
+ def start
10
+ # Do connection
11
+ @connection = AMQP.connect :host => (@config['host'] || 'localhost'), :port => (@config['port'] || 5672).to_i
12
+ @channel = MQ.new @connection
13
+ end
14
+
15
+ def subscribe(impulseType, &block)
16
+ raise "No block provided - a block must be provided to the subscribe method" unless block_given?
17
+
18
+ queue = MQ::Queue.new @channel, impulseType.uri
19
+ exchange = MQ::Exchange.new @channel, :direct, impulseType.uri
20
+ queue.bind(exchange)
21
+
22
+ queue.subscribe :ack => true do |headers, msg|
23
+ yield impulseType.load(msg)
24
+ headers.ack
25
+ end
26
+ end
27
+
28
+ def fire(impulseUri, content)
29
+ exchange = MQ::Exchange.new @channel, :direct, impulseUri
30
+ exchange.publish content, :content_type => 'application/json'
31
+ end
32
+ end
33
+
@@ -0,0 +1,115 @@
1
+ require 'rubygems'
2
+ require 'yaml'
3
+ require 'active_support'
4
+
5
+ module ORQ
6
+ # Base class inherited by domain objects describing a message that can be sent
7
+ class Impulse
8
+ def initialize(config = {})
9
+ apply_hash!(config)
10
+ end
11
+
12
+ # Instructs the impulse to send itself to the message queue
13
+ def fire!
14
+ self.class.get_target(self.class.target_name).fire self.class.uri, self.to_json
15
+ end
16
+
17
+ # Instructs the impulse to apply the properties in the given hash
18
+ def apply_hash!(props)
19
+ props.each do |k, v|
20
+ send("#{k}=", v) if respond_to? "#{k}="
21
+ end
22
+ end
23
+
24
+ # Class Methods
25
+ class << self
26
+ #
27
+ # Configuration Methods
28
+ #
29
+
30
+ # Sets the uri for this Impulse if a uri is provided; returns the value for the uri
31
+ # (or the class name if not overriden)
32
+ def uri(uri = nil)
33
+ @uri = uri unless uri.nil?
34
+
35
+ return self.name if @uri.nil?
36
+ @uri
37
+ end
38
+
39
+ # Sets the target name for this Impulse if a uri is provided; returns the value for the target name
40
+ # (or 'default' if not overriden)
41
+ def target_name(target_name = nil)
42
+ @target_name = target_name unless target_name.nil?
43
+
44
+ return 'default' if @target_name.nil?
45
+ @target_name
46
+ end
47
+
48
+ # Declares a field that should be available on an impulse.
49
+ def field(name)
50
+ attr_accessor name
51
+ end
52
+
53
+ #
54
+ # Control Methods
55
+ #
56
+ def subscribe(&block)
57
+ target = get_target self.target_name
58
+ target.subscribe self, &block
59
+ end
60
+
61
+ #
62
+ # Loading Methods
63
+ #
64
+ def load(text)
65
+ vals = ActiveSupport::JSON.decode(text)
66
+ result = new(vals)
67
+ result
68
+ end
69
+ end
70
+
71
+ # Static Methods
72
+
73
+ # Configures the ORQ infrastructure using the file on the given path.
74
+ def self.configure(path, environment)
75
+ # Load the given YAML file
76
+ config = YAML::load_file(path)
77
+
78
+ # Get the configuration for the profile
79
+ env_config = config[environment.to_s]
80
+ raise "No configuration for environment #{environment} in #{path}" unless env_config
81
+
82
+ # Clear the list of targets, and then process each
83
+ @@targets = {}
84
+ if env_config.is_a? Hash
85
+ env_config.each do |target_name, target_config|
86
+
87
+ @@targets[target_name] = load_target target_name, target_config
88
+ end
89
+ end
90
+ end
91
+
92
+ # Requests that the ORQ infrastructure start any connections
93
+ def self.start
94
+ @@targets.each do |name, target|
95
+ target.start
96
+ end
97
+ end
98
+
99
+ private
100
+ def self.load_target(name, config)
101
+ driver = config.delete 'driver'
102
+ raise "No driver specified for target #{name}" unless driver
103
+
104
+ # Load the adapter for the given driver
105
+ require "orq/adapters/#{driver}_adapter"
106
+ driver_class = Kernel.const_get("#{driver}_adapter".classify).new config
107
+ end
108
+
109
+ # Retrieves a target with the given name
110
+ def self.get_target(name)
111
+ raise "No target #{name} available" unless @@targets[name]
112
+ @@targets[name]
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,124 @@
1
+ require 'orq/impulse'
2
+
3
+ describe ORQ::Impulse do
4
+ it "should be inheritable with default details" do
5
+ class TrivialObj < ORQ::Impulse
6
+ end
7
+
8
+ TrivialObj.uri.should == 'TrivialObj'
9
+ end
10
+
11
+ it "should get fully qualified details by default" do
12
+ module TrivialNS
13
+ class TrivialObj < ORQ::Impulse
14
+ end
15
+ end
16
+
17
+ TrivialNS::TrivialObj.uri.should == 'TrivialNS::TrivialObj'
18
+ end
19
+
20
+ it "should allow multiple different types to be created with overidden uri" do
21
+ class FirstTrivialNamedObj < ORQ::Impulse
22
+ uri 'http://vuderacha.com/orq/trivial_named/first'
23
+ end
24
+ class SecondTrivialNamedObj < ORQ::Impulse
25
+ uri 'http://vuderacha.com/orq/trivial_named/second'
26
+ end
27
+
28
+ FirstTrivialNamedObj.uri.should == 'http://vuderacha.com/orq/trivial_named/first'
29
+ SecondTrivialNamedObj.uri.should == 'http://vuderacha.com/orq/trivial_named/second'
30
+ end
31
+
32
+ it "should support the declaration of fields" do
33
+ class FieldedImpulse < ORQ::Impulse
34
+ field :first_val
35
+ field :second_val
36
+ end
37
+
38
+ f = FieldedImpulse.new
39
+ f.first_val = 'a'
40
+ f.second_val = 'b'
41
+ f.first_val.should == 'a'
42
+ f.second_val.should == 'b'
43
+ end
44
+
45
+ it "should support restoring from a hash" do
46
+ class RestorableImpulse < ORQ::Impulse
47
+ field :first_val
48
+ field :second_val
49
+ end
50
+
51
+ f = RestorableImpulse.new({:first_val => 1, :second_val => 'a'})
52
+ f.first_val.should == 1
53
+ f.second_val.should == 'a'
54
+ end
55
+
56
+ it "should support restoring from a hash with too many values" do
57
+ class AnotherRestorableImpulse < ORQ::Impulse
58
+ field :first_val
59
+ field :second_val
60
+ end
61
+
62
+ f = AnotherRestorableImpulse.new({:first_val => 1, :second_val => 'a', :third_val => [1,2,3]})
63
+ f.first_val.should == 1
64
+ f.second_val.should == 'a'
65
+ end
66
+
67
+ it "should support configuration from a YAML file" do
68
+ ORQ::Impulse.configure 'spec/simple_config.yml', :development
69
+ end
70
+
71
+ it "should return default as a target name when not overriden" do
72
+ class DefaultTargetImpulse < ORQ::Impulse; end
73
+ DefaultTargetImpulse.target_name.should == 'default'
74
+ end
75
+
76
+ it "should return the configured target name" do
77
+ class SpecialTargetImpulse < ORQ::Impulse
78
+ target_name 'special'
79
+ end
80
+ SpecialTargetImpulse.target_name.should == 'special'
81
+ end
82
+ end
83
+
84
+ describe "configured #{ORQ::Impulse}" do
85
+ before(:all) do
86
+ ORQ::Impulse.configure 'spec/stub_config.yml', :development
87
+ end
88
+
89
+ it "should provide its uri and content to the adapter when being fired" do
90
+ class AnotherFireableImpulse < ORQ::Impulse
91
+ field :simple
92
+ end
93
+ StubAdapter.instance.should_receive(:fire).with('AnotherFireableImpulse', '{"simple": "a"}')
94
+
95
+ i = AnotherFireableImpulse.new({:simple => 'a'})
96
+ i.fire!
97
+ end
98
+
99
+ it "should support subscribing with a block" do
100
+ # Declare our subscribable impulse
101
+ class SubscribableImpulse < ORQ::Impulse
102
+ field :a
103
+ end
104
+
105
+ # Create our receiver
106
+ r = []
107
+
108
+ # Extend the stub adapter to respond the way that way we want it to
109
+ class StubAdapter
110
+ def subscribe(type, &block)
111
+ type.should == SubscribableImpulse
112
+ yield SubscribableImpulse.new({:a => 1})
113
+ end
114
+ end
115
+
116
+ # Perform the subscription
117
+ SubscribableImpulse.subscribe do |imp|
118
+ r << imp
119
+ end
120
+
121
+ r.length.should == 1
122
+ r[0].a.should == 1
123
+ end
124
+ end
@@ -0,0 +1 @@
1
+ development:
@@ -0,0 +1,3 @@
1
+ development:
2
+ default:
3
+ driver: stub
@@ -0,0 +1,9 @@
1
+ class StubAdapter
2
+ def initialize(config = {})
3
+ @@instance = self
4
+ end
5
+
6
+ def self.instance
7
+ @@instance
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vuderacha-orq
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Paul Jones
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-22 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.2.2
24
+ version:
25
+ description: ORQ is a queueing abstraction layer allowing messaging to be done directly from object.
26
+ email: pauljones23@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README.rdoc
33
+ files:
34
+ - Rakefile
35
+ - README.rdoc
36
+ - lib/orq
37
+ - lib/orq/adapters
38
+ - lib/orq/adapters/amqp_adapter.rb
39
+ - lib/orq/impulse.rb
40
+ - spec/impulse_spec.rb
41
+ - spec/simple_config.yml
42
+ - spec/stub_config.yml
43
+ - spec/support
44
+ - spec/support/orq
45
+ - spec/support/orq/adapters
46
+ - spec/support/orq/adapters/stub_adapter.rb
47
+ has_rdoc: true
48
+ homepage: http://github.com/vuderacha/orq/
49
+ post_install_message:
50
+ rdoc_options:
51
+ - --inline-source
52
+ - --charset=UTF-8
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "0"
60
+ version:
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0"
66
+ version:
67
+ requirements: []
68
+
69
+ rubyforge_project:
70
+ rubygems_version: 1.2.0
71
+ signing_key:
72
+ specification_version: 2
73
+ summary: Ruby Object Relational Queueing.
74
+ test_files: []
75
+