waffle 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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: