waffle 0.4.0 → 0.5.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.md CHANGED
@@ -36,27 +36,79 @@ You also can configure Waffle programmatically:
36
36
 
37
37
  or:
38
38
 
39
- Waffle.configure do |config|
40
- config.transport = 'redis'
41
- config.url = 'redis://localhost:6379/0'
42
- config.encoder = 'json'
39
+ Waffle.configure do
40
+ default do |config|
41
+ config.transport = 'redis'
42
+ config.url = 'redis://localhost:6379/0'
43
+ config.encoder = 'json'
44
+ end
43
45
  end
44
46
 
47
+ ### Multitransport config
48
+
49
+ You can use many transports for organising your messaging system. Just add to config file `queues` section:
50
+
51
+ production:
52
+ transport: rabbitmq
53
+ encoder: marshal
54
+ url: amqp://anyhost.com:5678
55
+ queues:
56
+ redis_name:
57
+ transport: redis
58
+ encoder: json
59
+ url: redis://localhost:6379/0
60
+
61
+ or programmatically:
62
+
63
+ Waffle.configure({
64
+ :transport => 'redis',
65
+ :url => 'redis://localhost:6379/0',
66
+ :encoder => 'json',
67
+ :queues => {
68
+ :redis_name => {
69
+ :transport => 'redis',
70
+ :url => 'redis://localhost:6380/0',
71
+ :encoder => 'json',
72
+ }
73
+ }
74
+ })
75
+
76
+ # or
77
+
78
+ Waffle.configure do
79
+ default do |config|
80
+ config.transport = 'redis'
81
+ config.url = 'redis://localhost:6379/0'
82
+ config.encoder = 'json'
83
+ end
84
+
85
+ queue(:redis_name) do |config|
86
+ config.transport = 'redis'
87
+ config.url = 'redis://localhost:6380/0'
88
+ config.encoder = 'json'
89
+ end
90
+ end
91
+
92
+
45
93
  ## Usage
46
94
 
47
95
  ### Event
48
96
 
49
97
  When you want to performan event, just insert this code in place, where it must occur:
50
98
 
51
- Waffle::Event.occurred 'index_page_load'
99
+ Waffle::Event.occurred 'message'
52
100
 
53
101
  You can attach meta data to event like this:
54
102
 
55
- Waffle::Event.occurred 'index_page_load', {'user_id' => 13, 'user_name' => 'Joshua'}
103
+ Waffle::Event.occurred {'user_id' => 13, 'user_name' => 'Joshua'}, :event_name => 'index_page_load'
56
104
 
57
105
  or like this:
58
106
 
59
- Waffle::Event.occurred 'index_page_load', 'bingo!'
107
+ Waffle::Event.occurred 'bingo!', :event_name => 'index_page_load'
108
+
109
+ or:
110
+
111
+ Waffle::Event.occurred 'message', :event_name => 'index_page_load', :queue => :experimental_queue
60
112
 
61
113
  ### Pub/Sub
62
114
 
@@ -67,6 +119,15 @@ or like this:
67
119
  pp message_hash_or_string
68
120
  end
69
121
 
122
+ ### Multitransport usage
123
+
124
+ Waffle.queue(:redis_name).publish('event.name', message_hash_or_string)
125
+
126
+ Waffle.queue(:redis_name).subscribe('event.name') do |message_type, message_hash_or_string|
127
+ pp message_type
128
+ pp message_hash_or_string
129
+ end
130
+
70
131
  ### Reconnect
71
132
 
72
133
  Don't care about any reconnects when transport server is down. Waffle just waits for server ready and reconnects automatically.
@@ -0,0 +1,19 @@
1
+ Waffle.configure do
2
+ default do |config|
3
+ config.transport = 'rabbitmq'
4
+ config.url = 'amqp://localhost:5672'
5
+ config.encoder = 'json'
6
+ end
7
+
8
+ queue(:rabbitmq_name) do |config|
9
+ config.transport = 'rabbitmq'
10
+ config.url = 'amqp://localhost:5673'
11
+ config.encoder = 'json'
12
+ end
13
+
14
+ queue(:redis_name) do |config|
15
+ config.transport = 'redis'
16
+ config.url = 'redis://localhost:6379/0'
17
+ config.encoder = 'json'
18
+ end
19
+ end
@@ -2,6 +2,15 @@ production: &base
2
2
  transport: rabbitmq
3
3
  encoder: json
4
4
  url: 'amqp://localhost:5672'
5
+ queues:
6
+ rabbitmq_name:
7
+ transport: rabbitmq
8
+ encoder: json
9
+ url: 'amqp://localhost:5673'
10
+ redis_name:
11
+ transport: redis
12
+ encoder: json
13
+ url: 'redis://localhost:6379/0'
5
14
  development:
6
15
  <<: *base
7
16
  test:
@@ -7,58 +7,58 @@ require 'waffle/version'
7
7
  require 'time'
8
8
 
9
9
  module Waffle
10
- extend self
10
+ extend self
11
11
  module Transports
12
12
  autoload :Base, 'waffle/transports/base'
13
13
  autoload :Rabbitmq, 'waffle/transports/rabbitmq' if defined?(Bunny)
14
14
  autoload :Redis, 'waffle/transports/redis' if defined?(::Redis)
15
+
16
+ module_function
17
+ def create config
18
+ "Waffle::Transports::#{config.transport.camelize}".constantize.new(config)
19
+ end
15
20
  end
16
21
 
17
22
  autoload :Config, 'waffle/config'
18
23
  autoload :Event, 'waffle/event'
19
24
 
25
+ class Config
26
+ autoload :Node, 'waffle/config'
27
+ end
28
+
20
29
  module Encoders
21
30
  autoload :Json, 'waffle/encoders/json' if defined?(Yajl)
22
31
  autoload :Marshal, 'waffle/encoders/marshal'
23
32
  end
24
33
 
25
34
  def reset_config!
26
- @configured = false
35
+ config.reset_config! if config
27
36
  end
28
37
 
29
- def configure options=nil
30
- @configured ||= begin
38
+ def configure options = nil, &block
39
+ if block_given?
40
+ Config.class_eval(&block)
41
+ else
31
42
  options = {:path => 'config/waffle.yml'} unless options
32
43
  Config.load!(options)
33
- true
34
44
  end
35
- block_given? ? yield(Config) : Config
36
45
  end
37
46
 
38
- def publish flow = 'events', message = ''
39
- transport.publish(flow, message)
40
- rescue *transport.connection_exceptions => e
41
- transport.reconnect && retry if transport.ready_to_connect?
47
+ def config
48
+ Config if Config.configured?
42
49
  end
43
50
 
44
- def subscribe flow = '', &block
45
- transport.subscribe(flow, &block)
46
- rescue *transport.connection_exceptions => e
47
- until transport.reconnect do
48
- sleep(config.connection_attempt_timeout)
49
- end
50
- retry
51
+ def queue name = :default
52
+ config.queues[name] or raise "Transport '#{name}' is not configured"
51
53
  end
52
54
 
53
- def transport
54
- @transport ||= "Waffle::Transports::#{Waffle.config.transport.camelize}".constantize.new
55
- end
56
-
57
- def encoder
58
- @encoder ||= "Waffle::Encoders::#{Waffle.config.encoder.camelize}".constantize
55
+ def method_missing meth, *args
56
+ if Config.configured?
57
+ config.queues[:default].send(meth, *args)
58
+ else
59
+ super
60
+ end
59
61
  end
60
-
61
- alias :config :configure
62
62
  end
63
63
 
64
64
  unless defined?(ActiveSupport::Inflector)
@@ -1,57 +1,111 @@
1
- require 'yaml'
2
-
3
1
  module Waffle
4
- module Config
5
- extend self
6
- attr_accessor :settings, :defaults
2
+ class Config
3
+ class Node
4
+ attr_accessor :settings
7
5
 
8
- @settings = {}
9
- @defaults = {}
6
+ class << self
7
+ def option(name, options = {})
8
+ defaults[name] = options[:default]
10
9
 
11
- def option(name, options = {})
12
- defaults[name] = settings[name] = options[:default]
10
+ class_eval <<-RUBY
11
+ def #{name}
12
+ settings[#{name.inspect}] || defaults[#{name.inspect}]
13
+ end
13
14
 
14
- class_eval <<-RUBY
15
- def #{name}
16
- settings[#{name.inspect}]
17
- end
15
+ def #{name}=(value)
16
+ settings[#{name.inspect}] = value
17
+ end
18
18
 
19
- def #{name}=(value)
20
- settings[#{name.inspect}] = value
19
+ def #{name}?
20
+ #{name}
21
+ end
22
+ RUBY
21
23
  end
22
24
 
23
- def #{name}?
24
- #{name}
25
+ def defaults
26
+ @defaults ||= {}
25
27
  end
26
- RUBY
27
- end
28
+ end
29
+
30
+ option :url, :default => nil
31
+ option :encoder, :default => 'json'
32
+ option :transport, :default => nil
33
+ option :connection_attempt_timeout, :default => 30
34
+ option :options, :default => {'exchange' => 'events', 'queue' => ''}
28
35
 
29
- option :url, :default => nil
30
- option :encoder, :default => 'json'
31
- option :transport, :default => nil
32
- option :connection_attempt_timeout, :default => 30
36
+ def initialize config = {}
37
+ @settings = config ? defaults.merge(config) : {}
38
+ yield self if block_given?
39
+ end
33
40
 
34
- def load! options=nil
35
- options[:path] ? load_from_yaml!(options[:path]) : load_from_hash!(options)
36
- self
41
+ protected
42
+ def defaults
43
+ self.class.defaults
44
+ end
37
45
  end
38
46
 
39
- def load_from_yaml! filename
40
- filename = Rails.root.join(filename) if defined?(Rails)
41
- filename = File.expand_path(filename)
47
+ class << self
48
+ def load! options = nil
49
+ options[:path] ? load_from_yaml!(options[:path]) : load_from_hash!(options)
50
+ self
51
+ end
42
52
 
43
- if File.exists?(filename)
44
- settings_hash = YAML.load_file(filename)[environment]
45
- @settings = defaults.merge(settings_hash.symbolize_keys) if settings_hash
53
+ def configured?
54
+ !!@configured
46
55
  end
47
- end
48
56
 
49
- def load_from_hash! options
50
- @settings = defaults.merge(options)
51
- end
52
-
53
- def environment
54
- defined?(Rails) && Rails.respond_to?(:env) ? Rails.env : (ENV['RACK_ENV'] || 'development')
57
+ def reset_config!
58
+ @queues = {}
59
+ @configured = false
60
+ end
61
+
62
+ def default &block
63
+ queue(:default, &block).tap do
64
+ @configured = true
65
+ end
66
+ end
67
+
68
+ def queue name, &block
69
+ queues[name] = Transports.create(Node.new(&block))
70
+ nil
71
+ end
72
+
73
+ def queues
74
+ @queues ||= {}
75
+ end
76
+
77
+ protected
78
+ def load_from_yaml! filename
79
+ filename = Rails.root.join(filename) if defined?(Rails)
80
+ filename = File.expand_path(filename)
81
+
82
+ if File.exists?(filename)
83
+ settings_hash = YAML.load_file(filename)[environment]
84
+ load_from_hash!(settings_hash) if settings_hash
85
+ end
86
+ end
87
+
88
+ def load_from_hash! settings_hash
89
+ queues_config = settings_hash.delete('queues') || {}
90
+ queues_config['default'] = settings_hash
91
+ queues_config.each do |queue, config|
92
+ config = Node.new(Node.defaults.merge(config.symbolize_keys))
93
+ queues[queue.to_sym] = Transports.create(config)
94
+ end
95
+ @configured = true
96
+ end
97
+
98
+ def method_missing meth, *args
99
+ if configured?
100
+ queues[:default].config.send(meth, *args)
101
+ else
102
+ super
103
+ end
104
+ end
105
+
106
+ def environment
107
+ defined?(Rails) && Rails.respond_to?(:env) ? Rails.env : (ENV['RACK_ENV'] || 'development')
108
+ end
55
109
  end
56
110
  end
57
- end
111
+ end
@@ -1,14 +1,19 @@
1
1
  module Waffle
2
2
  class Event
3
3
  class << self
4
- def occured(event_name = 'event', event_data = nil)
4
+ def occured(event_data, options = {})
5
+ options = {
6
+ :event_name => 'event',
7
+ :queue => :default
8
+ }.merge(options)
9
+
5
10
  unless event_data.is_a?(Hash)
6
11
  event_data = {'body' => event_data.to_s}
7
12
  end
8
13
 
9
14
  event_data.merge!({'occured_at' => Time.now})
10
15
 
11
- Waffle.publish(event_name, event_data)
16
+ Waffle.queue(options[:queue]).publish(options[:event_name], event_data)
12
17
  end
13
18
  alias :occurred :occured
14
19
  end
@@ -1,19 +1,40 @@
1
1
  module Waffle
2
2
  module Transports
3
3
  class Base
4
- def initialize
4
+ attr_reader :config
5
+ def initialize config
6
+ @config = config
5
7
  connect!
6
8
  end
7
9
 
10
+ def publish flow = 'events', message = ''
11
+ publish_impl(flow, message)
12
+ rescue *connection_exceptions => e
13
+ reconnect && retry if ready_to_connect?
14
+ end
15
+
16
+ def subscribe flow = '', &block
17
+ subscribe_impl(flow, &block)
18
+ rescue *connection_exceptions => e
19
+ until reconnect do
20
+ sleep(config.connection_attempt_timeout)
21
+ end
22
+ retry
23
+ end
24
+
25
+ protected
8
26
  def ready_to_connect?
9
- (Time.now - @last_connection_attempt) > Waffle.config.connection_attempt_timeout
27
+ (Time.now - @last_connection_attempt) > config.connection_attempt_timeout
10
28
  end
11
29
 
12
30
  def reconnect
13
31
  connect!
14
32
  end
15
33
 
16
- protected
34
+ def encoder
35
+ @encoder ||= "Waffle::Encoders::#{config.encoder.camelize}".constantize
36
+ end
37
+
17
38
  def connect!
18
39
  @last_connection_attempt = Time.now
19
40
  do_connect
@@ -6,11 +6,12 @@ module Waffle
6
6
  class Rabbitmq < Base
7
7
  EXCHANGE = 'events'
8
8
 
9
- def publish(flow = 'events', message = '')
10
- exchange.publish(Waffle.encoder.encode(message), :key => flow)
9
+ protected
10
+ def publish_impl(flow = 'events', message = '')
11
+ exchange.publish(encoder.encode(message), :key => flow)
11
12
  end
12
13
 
13
- def subscribe(flow = 'events')
14
+ def subscribe_impl(flow = 'events')
14
15
  if flow.is_a?(Array)
15
16
  flow.each{|f| queue.bind(exchange, :key => f)}
16
17
  else
@@ -18,7 +19,7 @@ module Waffle
18
19
  end
19
20
 
20
21
  queue.subscribe do |message|
21
- yield(message[:delivery_details][:routing_key], Waffle.encoder.decode(message[:payload]))
22
+ yield(message[:delivery_details][:routing_key], encoder.decode(message[:payload]))
22
23
  end
23
24
  end
24
25
 
@@ -26,19 +27,18 @@ module Waffle
26
27
  [Bunny::ServerDownError, Bunny::ConnectionError, Errno::ECONNRESET]
27
28
  end
28
29
 
29
- private
30
30
  def exchange
31
- @exchange ||= @bunny.exchange(EXCHANGE)
31
+ @exchange ||= @bunny.exchange(config.options['exchange'] || EXCHANGE)
32
32
  end
33
33
 
34
34
  def queue
35
- @queue ||= @bunny.queue('', :durable => true, :auto_delete => true)
35
+ @queue ||= @bunny.queue(config.options['queue'] || '', :durable => true, :auto_delete => true)
36
36
  end
37
37
 
38
38
  def do_connect
39
39
  @exchange = nil
40
40
  @queue = nil
41
- @bunny = Bunny.new(Waffle.config.url)
41
+ @bunny = Bunny.new(config.url)
42
42
  @bunny.start
43
43
  end
44
44
  end
@@ -3,14 +3,15 @@ module Waffle
3
3
  class Redis < Base
4
4
  attr_reader :db
5
5
 
6
- def publish(flow = 'events', message = '')
7
- db.publish(flow, Waffle.encoder.encode(message))
6
+ protected
7
+ def publish_impl(flow = 'events', message = '')
8
+ db.publish(flow, encoder.encode(message))
8
9
  end
9
10
 
10
- def subscribe(flow = 'events')
11
+ def subscribe_impl(flow = 'events')
11
12
  db.subscribe(*flow) do |on|
12
13
  on.message do |channel, message|
13
- yield(channel, Waffle.encoder.decode(message))
14
+ yield(channel, encoder.decode(message))
14
15
  end
15
16
  end
16
17
  end
@@ -19,9 +20,8 @@ module Waffle
19
20
  [Errno::ECONNREFUSED, Errno::ECONNRESET]
20
21
  end
21
22
 
22
- protected
23
23
  def do_connect
24
- @db = ::Redis.new(:url => Waffle.config.url)
24
+ @db = ::Redis.new(:url => config.url)
25
25
  end
26
26
  end
27
27
  end
@@ -1,3 +1,3 @@
1
1
  module Waffle
2
- VERSION = '0.4.0'
2
+ VERSION = '0.5.0'
3
3
  end
@@ -2,22 +2,25 @@ require 'spec_helper'
2
2
 
3
3
  describe Waffle::Event do
4
4
  describe '.occured' do
5
- let(:flow){'event'}
5
+ let(:flow){'event name'}
6
6
  let(:now){Time.now}
7
+ let(:options){{:event_name => flow, :queue => :default}}
8
+ let(:transport){double(:transport)}
7
9
 
8
10
  before do
9
11
  Time.stub(:now => now)
10
- Waffle.should_receive(:publish).with(flow, message)
12
+ Waffle.should_receive(:queue).with(:default).and_return(transport)
13
+ transport.should_receive(:publish).with(flow, message)
11
14
  end
12
15
 
13
16
  context do
14
17
  let(:message){{"key1" => "value1", "key2" => "value2", "occured_at" => now}}
15
- specify{Waffle::Event.occured(flow, {'key1' => 'value1', 'key2' => 'value2'})}
18
+ specify{Waffle::Event.occured(message, {:event_name => flow})}
16
19
  end
17
20
 
18
21
  context do
19
22
  let(:message){{"body" => "message data", "occured_at" => now}}
20
- specify{Waffle::Event.occured(flow, 'message data')}
23
+ specify{Waffle::Event.occured('message data', {:event_name => flow})}
21
24
  end
22
25
  end
23
26
  end
@@ -1,14 +1,13 @@
1
1
  require 'spec_helper'
2
2
 
3
- class Waffle::Transports::Rabbitmq
4
- def initialize
5
- end
6
- end
3
+ #class Waffle::Transports::Rabbitmq
4
+ #end
7
5
 
8
6
  describe Waffle::Transports::Rabbitmq do
9
- subject{Waffle::Transports::Rabbitmq.new}
7
+ subject{Waffle::Transports::Rabbitmq.new(config)}
10
8
 
11
9
  let(:exchange){mock(:exchange)}
10
+ let(:config){Waffle::Config::Node.new({})}
12
11
 
13
12
  before do
14
13
  subject.stub(:exchange => exchange)
@@ -1,13 +1,12 @@
1
1
  require 'spec_helper'
2
2
 
3
- class Waffle::Transports::Redis
4
- def initialize
5
- end
6
- end
3
+ #class Waffle::Transports::Redis
4
+ #end
7
5
 
8
6
  describe Waffle::Transports::Redis do
9
- subject{Waffle::Transports::Redis.new}
7
+ subject{Waffle::Transports::Redis.new(config)}
10
8
 
9
+ let(:config){Waffle::Config::Node.new({})}
11
10
  let(:redis){mock(:redis)}
12
11
  let(:subscription){mock(:subscription)}
13
12
 
@@ -19,9 +19,9 @@ describe Waffle do
19
19
  YAML.stub(:load_file => config_hash)
20
20
  File.stub(:exists? => true)
21
21
  Waffle::Config.stub(:environment => 'development')
22
+ Waffle.configure
22
23
  end
23
24
 
24
- specify{Waffle.configure.should == Waffle::Config}
25
25
  specify{Waffle.config.should == Waffle::Config}
26
26
  specify{Waffle.config.transport.should == 'redis'}
27
27
  specify{Waffle.config.encoder.should == 'json'}
@@ -31,8 +31,10 @@ describe Waffle do
31
31
 
32
32
  context "when a block is supplied" do
33
33
  before do
34
- Waffle.configure do |config|
35
- config.transport = 'redis'
34
+ Waffle.configure do
35
+ default do |config|
36
+ config.transport = 'redis'
37
+ end
36
38
  end
37
39
  end
38
40
 
@@ -48,7 +50,8 @@ describe Waffle do
48
50
 
49
51
  context do
50
52
  before do
51
- Waffle.stub(:transport => transport)
53
+ Waffle.configure
54
+ Waffle::Config.stub(:queues => {:default => transport})
52
55
  end
53
56
 
54
57
  let(:transport){mock(:transport)}
@@ -65,10 +68,10 @@ describe Waffle do
65
68
 
66
69
  describe '#subscribe' do
67
70
  before do
68
- transport.should_receive(:subscribe).with('flow').and_yield
71
+ transport.should_receive(:subscribe).with('flow')
69
72
  end
70
73
 
71
- specify{Waffle.subscribe('flow'){}}
74
+ specify{Waffle.subscribe('flow')}
72
75
  end
73
76
  end
74
77
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: waffle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-07-06 00:00:00.000000000 Z
13
+ date: 2012-10-05 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rake
@@ -43,6 +43,7 @@ files:
43
43
  - Gemfile.lock
44
44
  - README.md
45
45
  - Rakefile
46
+ - config/config.rb
46
47
  - config/waffle.yml
47
48
  - lib/waffle.rb
48
49
  - lib/waffle/config.rb
@@ -75,7 +76,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
75
76
  version: '0'
76
77
  segments:
77
78
  - 0
78
- hash: 2130519817389681143
79
+ hash: -4396134894466903683
79
80
  required_rubygems_version: !ruby/object:Gem::Requirement
80
81
  none: false
81
82
  requirements: