untied-consumer 0.0.1
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/.gitignore +20 -0
- data/.rvmrc +48 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +32 -0
- data/Rakefile +1 -0
- data/lib/untied-consumer/config.rb +25 -0
- data/lib/untied-consumer/event.rb +27 -0
- data/lib/untied-consumer/observer.rb +86 -0
- data/lib/untied-consumer/processor.rb +47 -0
- data/lib/untied-consumer/railtie.rb +10 -0
- data/lib/untied-consumer/tasks/untied.tasks +16 -0
- data/lib/untied-consumer/tasks/untied_rails.tasks +17 -0
- data/lib/untied-consumer/version.rb +5 -0
- data/lib/untied-consumer/worker.rb +23 -0
- data/lib/untied-consumer.rb +11 -0
- data/spec/event_spec.rb +50 -0
- data/spec/observer_spec.rb +115 -0
- data/spec/processor_spec.rb +70 -0
- data/spec/spec_helper.rb +22 -0
- data/untied-consumer.gemspec +35 -0
- metadata +207 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# This is an RVM Project .rvmrc file, used to automatically load the ruby
|
4
|
+
# development environment upon cd'ing into the directory
|
5
|
+
|
6
|
+
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
|
7
|
+
# Only full ruby name is supported here, for short names use:
|
8
|
+
# echo "rvm use 1.8.7" > .rvmrc
|
9
|
+
environment_id="ruby-1.8.7-p370"
|
10
|
+
|
11
|
+
# Uncomment the following lines if you want to verify rvm version per project
|
12
|
+
# rvmrc_rvm_version="1.14.5 (master)" # 1.10.1 seams as a safe start
|
13
|
+
# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
|
14
|
+
# echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
|
15
|
+
# return 1
|
16
|
+
# }
|
17
|
+
|
18
|
+
# First we attempt to load the desired environment directly from the environment
|
19
|
+
# file. This is very fast and efficient compared to running through the entire
|
20
|
+
# CLI and selector. If you want feedback on which environment was used then
|
21
|
+
# insert the word 'use' after --create as this triggers verbose mode.
|
22
|
+
if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
|
23
|
+
&& -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
|
24
|
+
then
|
25
|
+
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
|
26
|
+
[[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] &&
|
27
|
+
\. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true
|
28
|
+
else
|
29
|
+
# If the environment file has not yet been created, use the RVM CLI to select.
|
30
|
+
rvm --create "$environment_id" || {
|
31
|
+
echo "Failed to create RVM environment '${environment_id}'."
|
32
|
+
return 1
|
33
|
+
}
|
34
|
+
fi
|
35
|
+
|
36
|
+
# If you use bundler, this might be useful to you:
|
37
|
+
# if [[ -s Gemfile ]] && {
|
38
|
+
# ! builtin command -v bundle >/dev/null ||
|
39
|
+
# builtin command -v bundle | GREP_OPTIONS= \grep $rvm_path/bin/bundle >/dev/null
|
40
|
+
# }
|
41
|
+
# then
|
42
|
+
# printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
|
43
|
+
# gem install bundler
|
44
|
+
# fi
|
45
|
+
# if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null
|
46
|
+
# then
|
47
|
+
# bundle install | GREP_OPTIONS= \grep -vE '^Using|Your bundle is complete'
|
48
|
+
# fi
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Guilherme Cavalcanti
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Untied::Consumer
|
2
|
+
|
3
|
+
This is the consumer part of Untied. Untied is an observer pattern implementation to distributed systems.
|
4
|
+
|
5
|
+
For usage information, please visit the [Untied](http://github.com.br/redu/untied) page.
|
6
|
+
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
gem 'untied-consumer'
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install untied-consumer
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
TODO: Write usage instructions here
|
25
|
+
|
26
|
+
## Contributing
|
27
|
+
|
28
|
+
1. Fork it
|
29
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
30
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
31
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
32
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'configurable'
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module Untied
|
6
|
+
module Consumer
|
7
|
+
def self.configure(&block)
|
8
|
+
yield(config) if block_given?
|
9
|
+
Processor.observers = config.observers
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.config
|
13
|
+
@config ||= Config.new
|
14
|
+
end
|
15
|
+
|
16
|
+
class Config
|
17
|
+
include Configurable
|
18
|
+
|
19
|
+
config :logger, Logger.new(STDOUT)
|
20
|
+
config :observers, []
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'active_model'
|
3
|
+
|
4
|
+
module Untied
|
5
|
+
class Event
|
6
|
+
include ActiveModel::Serializers::JSON
|
7
|
+
attr_accessor :name, :payload, :origin
|
8
|
+
|
9
|
+
def initialize(attrs)
|
10
|
+
@config = {
|
11
|
+
:name => "after_create",
|
12
|
+
:payload => nil,
|
13
|
+
:origin => nil
|
14
|
+
}.merge(attrs)
|
15
|
+
|
16
|
+
raise "You should inform the origin service" unless @config[:origin]
|
17
|
+
|
18
|
+
@name = @config.delete(:name)
|
19
|
+
@payload = @config.delete(:payload)
|
20
|
+
@origin = @config.delete(:origin)
|
21
|
+
end
|
22
|
+
|
23
|
+
def attributes
|
24
|
+
{ "name" => @name, "origin" => @origin, "payload" => @payload }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
module Untied
|
5
|
+
module Consumer
|
6
|
+
class Observer
|
7
|
+
include Singleton
|
8
|
+
CALLBACKS = [
|
9
|
+
:after_initialize, :after_find, :after_touch, :before_validation,
|
10
|
+
:after_validation, :before_save, :around_save, :after_save, :before_create,
|
11
|
+
:around_create, :after_create, :before_update, :around_update,
|
12
|
+
:after_update, :before_destroy, :around_destroy, :after_destroy,
|
13
|
+
:after_commit, :after_rollback
|
14
|
+
]
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def observe(*args)
|
18
|
+
from, classes = deal_with_args(*args)
|
19
|
+
define_observed_classes(classes)
|
20
|
+
define_observed_service(from)
|
21
|
+
end
|
22
|
+
|
23
|
+
def define_observed_classes(classes)
|
24
|
+
remove_possible_method(:observed_classes)
|
25
|
+
define_method(:observed_classes, Proc.new { classes })
|
26
|
+
end
|
27
|
+
|
28
|
+
def define_observed_service(service)
|
29
|
+
remove_possible_method(:observed_service)
|
30
|
+
define_method(:observed_service, Proc.new { service })
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def deal_with_args(*args)
|
36
|
+
if args.last.is_a? Hash # calling deal_with_args(User, Post, :from =>...
|
37
|
+
from = args.delete_at(-1).fetch(:from, :core).to_sym
|
38
|
+
else # calling deal_with_args(User, Post..)
|
39
|
+
from = :core
|
40
|
+
end
|
41
|
+
|
42
|
+
classes = args.collect(&:to_sym)
|
43
|
+
|
44
|
+
[from, classes]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Calls the proper callback method if the current observer is configured
|
49
|
+
# to observe the event_name from service to the klass.
|
50
|
+
#
|
51
|
+
# class MyObserver < Untied::Consumer::Observer
|
52
|
+
# observe User, :from => :core
|
53
|
+
#
|
54
|
+
# def after_create(model); end
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# MyObserver.instance.notify(:after_create, :user, :core, { :user => { } })
|
58
|
+
# # => calls after create method
|
59
|
+
#
|
60
|
+
# MyObserver.instance.notify(:after_update, :user, :core, { :user => { } })
|
61
|
+
# # => doesn't calls after create method
|
62
|
+
def notify(*args)
|
63
|
+
return nil unless args.length == 4
|
64
|
+
|
65
|
+
event_name = args.shift
|
66
|
+
klass = args.shift
|
67
|
+
service = args.shift
|
68
|
+
entity = args.shift
|
69
|
+
|
70
|
+
return nil unless CALLBACKS.include? event_name
|
71
|
+
return nil unless service == observed_service
|
72
|
+
return nil unless observed_classes.include? klass
|
73
|
+
|
74
|
+
self.send(event_name, entity)
|
75
|
+
end
|
76
|
+
|
77
|
+
def observed_classes
|
78
|
+
self.class.observed_classes
|
79
|
+
end
|
80
|
+
|
81
|
+
def observed_service
|
82
|
+
self.class.observed_service
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Untied
|
3
|
+
module Consumer
|
4
|
+
class Processor
|
5
|
+
attr_reader :observers
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@observers = \
|
9
|
+
self.class.observers.collect { |o| o.to_s.camelize.constantize.instance }
|
10
|
+
end
|
11
|
+
|
12
|
+
def process(headers, message)
|
13
|
+
begin
|
14
|
+
message = JSON.parse(message, :symbolize_names => true)
|
15
|
+
rescue JSON::ParserError => e
|
16
|
+
Consumer.config.logger "Untied::Processor: Parsing error #{e}"
|
17
|
+
return
|
18
|
+
end
|
19
|
+
|
20
|
+
message = message.fetch(:event, {})
|
21
|
+
payload = message.fetch(:payload, {})
|
22
|
+
service = message[:origin].try(:to_sym)
|
23
|
+
event_name = message[:name].try(:to_sym)
|
24
|
+
klass = payload.keys.first
|
25
|
+
|
26
|
+
Consumer.config.logger.info \
|
27
|
+
"Untied::Processor: processing event #{event_name} from #{service} with " + \
|
28
|
+
"payload #{payload}"
|
29
|
+
|
30
|
+
observers.each do |observer|
|
31
|
+
observer.notify(event_name, klass, service, payload)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class << self
|
36
|
+
def observers=(*obs)
|
37
|
+
@observers = obs.flatten
|
38
|
+
end
|
39
|
+
|
40
|
+
def observers
|
41
|
+
@observers ||= []
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'untied-consumer/worker'
|
2
|
+
require 'amqp'
|
3
|
+
|
4
|
+
namespace :untied do
|
5
|
+
namespace :consumer do
|
6
|
+
desc "Starts untied's worker"
|
7
|
+
task :work do
|
8
|
+
AMQP.start do |connection|
|
9
|
+
channel = AMQP::Channel.new(connection)
|
10
|
+
exchange = channel.topic("untied", :auto_delete => true)
|
11
|
+
worker = Untied::Consumer::Worker.new(:channel => channel, :exchange => exchange)
|
12
|
+
worker.start
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'untied-consumer/worker'
|
2
|
+
require 'amqp'
|
3
|
+
|
4
|
+
namespace :untied do
|
5
|
+
namespace :consumer do
|
6
|
+
desc "Starts untied's worker"
|
7
|
+
task :work => :environment do
|
8
|
+
AMQP.start do |connection|
|
9
|
+
channel = AMQP::Channel.new(connection)
|
10
|
+
exchange = channel.topic("untied", :auto_delete => true)
|
11
|
+
worker = Untied::Consumer::Worker.new(:channel => channel, :exchange => exchange)
|
12
|
+
worker.start
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Untied
|
3
|
+
module Consumer
|
4
|
+
class Worker
|
5
|
+
def initialize(opts)
|
6
|
+
@channel = opts[:channel]
|
7
|
+
@queue_name = opts[:queue_name] || ""
|
8
|
+
@consumer = opts[:consumer] || Processor.new
|
9
|
+
@exchange = opts[:exchange]
|
10
|
+
|
11
|
+
Consumer.config.logger.info "Worker initialized and listening"
|
12
|
+
end
|
13
|
+
|
14
|
+
def start
|
15
|
+
@channel.queue(@queue_name, :exclusive => true) do |queue|
|
16
|
+
queue.bind(@exchange, :routing_key => "untied.#").subscribe do |h,p|
|
17
|
+
@consumer.process(h,p)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/spec/event_spec.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Untied
|
5
|
+
describe Event do
|
6
|
+
before do
|
7
|
+
class Person
|
8
|
+
include ActiveModel::Serializers::JSON
|
9
|
+
attr_accessor :name
|
10
|
+
|
11
|
+
def initialize(attrs)
|
12
|
+
@name = attrs[:name]
|
13
|
+
end
|
14
|
+
|
15
|
+
def attributes
|
16
|
+
{ :name => name }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
let(:person) { Person.new(:name => "Guila") }
|
21
|
+
|
22
|
+
context ".new" do
|
23
|
+
it "should accept an event name and a payload" do
|
24
|
+
Event.
|
25
|
+
new(:name => :after_create, :payload => double('User'), :origin => "core").
|
26
|
+
should be_a Event
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should include the origin service name" do
|
30
|
+
Event.new(:name => :after_create, :payload => person, :origin => "core").
|
31
|
+
origin.should == "core"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "#to_json" do
|
36
|
+
it "should generte the correct json representation" do
|
37
|
+
event = {
|
38
|
+
:event => {
|
39
|
+
:name => :after_create,
|
40
|
+
:payload => person,
|
41
|
+
:origin => :core
|
42
|
+
}
|
43
|
+
}
|
44
|
+
Event.new(:name => :after_create, :payload => person, :origin => :core).
|
45
|
+
to_json == event.to_json
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Untied
|
5
|
+
module Consumer
|
6
|
+
describe Observer do
|
7
|
+
before do
|
8
|
+
class ::UserObserver < Untied::Consumer::Observer
|
9
|
+
end
|
10
|
+
end
|
11
|
+
after do
|
12
|
+
obsc = ::UserObserver
|
13
|
+
%w(observed_classes observed_service).each do |method|
|
14
|
+
if ::UserObserver.respond_to?(method)
|
15
|
+
::UserObserver.send(:remove_method, method)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
let(:subject) { ::UserObserver.instance }
|
20
|
+
|
21
|
+
context ".instance" do
|
22
|
+
it "should return a valid instance of the observer" do
|
23
|
+
subject.should be_a Untied::Consumer::Observer
|
24
|
+
end
|
25
|
+
it "should be a singleton" do
|
26
|
+
subject.should == UserObserver.instance
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context ".observe" do
|
31
|
+
context ".observed_classes" do
|
32
|
+
it "should define .observed_classes" do
|
33
|
+
::UserObserver.observe(:user, :from => :core)
|
34
|
+
subject.observed_classes.should == [:user]
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should accept multiple classes" do
|
38
|
+
::UserObserver.observe(:user, :post, :from => :core)
|
39
|
+
subject.observed_classes.should == [:user, :post]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context ".observed_services" do
|
44
|
+
it "should define the observed services" do
|
45
|
+
::UserObserver.observe(:user, :from => :core)
|
46
|
+
subject.observed_service == :core
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should define the observed services as string" do
|
50
|
+
::UserObserver.observe(:user, :from => "core")
|
51
|
+
subject.observed_service == :core
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when omiting service name" do
|
55
|
+
it "should default to core" do
|
56
|
+
::UserObserver.observe(:user)
|
57
|
+
subject.observed_service == :core
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should define observed classes" do
|
61
|
+
::UserObserver.observe(:user)
|
62
|
+
subject.observed_classes.should == [:user]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "#notify" do
|
69
|
+
before do
|
70
|
+
::UserObserver.observe(:user)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should respont to #notify" do
|
74
|
+
subject.should respond_to :notify
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should call the correct method based on event_name" do
|
78
|
+
subject.stub(:after_create)
|
79
|
+
subject.should_receive(:after_create).with(an_instance_of(Hash))
|
80
|
+
subject.notify(:after_create, :user, :core, { :user => { :name => "há!" }})
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should not call non callback methods" do
|
84
|
+
subject.stub(:after_jump)
|
85
|
+
subject.should_not_receive(:after_jump)
|
86
|
+
subject.notify(:after_jump, :user, :core, { :user => { :name => "há!" }})
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should pass through when the entity comes from other service" do
|
90
|
+
subject.stub(:after_create)
|
91
|
+
subject.should_not_receive(:after_create)
|
92
|
+
subject.notify(:after_create, :user, :foo, { :user => { :name => "há!" }})
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should pass trhough when entity is not observed" do
|
96
|
+
subject.stub(:after_create)
|
97
|
+
subject.should_not_receive(:after_create)
|
98
|
+
subject.notify(:after_create, :post, :core, { :user => { :name => "há!" }})
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should not raise error when passing incorrect arguments" do
|
102
|
+
expect {
|
103
|
+
subject.notify
|
104
|
+
}.to_not raise_error(ArgumentError)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should pass through when there is incorrect arguments" do
|
108
|
+
subject.stub(:after_create)
|
109
|
+
subject.should_not_receive(:after_create)
|
110
|
+
subject.notify
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Untied
|
5
|
+
module Consumer
|
6
|
+
describe Processor do
|
7
|
+
before do
|
8
|
+
class ::SomeObserver < Observer
|
9
|
+
observe :user, :from => :core
|
10
|
+
end
|
11
|
+
class MyProcessor < Processor
|
12
|
+
end
|
13
|
+
end
|
14
|
+
after do
|
15
|
+
MyProcessor.observers = []
|
16
|
+
end
|
17
|
+
let(:message) do
|
18
|
+
{ :event => {
|
19
|
+
:name => :after_create, :origin => :core,
|
20
|
+
:payload => { :user => { :name => "Guila" } }
|
21
|
+
} }
|
22
|
+
end
|
23
|
+
|
24
|
+
context ".observers=" do
|
25
|
+
it "should accept a list of observers" do
|
26
|
+
MyProcessor.observers = [:my_observer, :other_observers]
|
27
|
+
MyProcessor.observers.should == [:my_observer, :other_observers]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context ".new" do
|
32
|
+
before { MyProcessor.observers = [:some_observer] }
|
33
|
+
it "should initialize observers" do
|
34
|
+
SomeObserver.should_receive(:instance)
|
35
|
+
MyProcessor.new
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should assing observer to a instance variable" do
|
39
|
+
MyProcessor.new.observers.should include SomeObserver.instance
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "#process" do
|
44
|
+
before { MyProcessor.observers = [:some_observer] }
|
45
|
+
it "should call Observer#notify" do
|
46
|
+
SomeObserver.instance.should_receive(:notify)
|
47
|
+
MyProcessor.new.process({}, message.to_json)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should call Observer#notify with correct arguments" do
|
51
|
+
SomeObserver.instance.should_receive(:notify).
|
52
|
+
with(:after_create, :user, :core, { :user => { :name => "Guila" } })
|
53
|
+
MyProcessor.new.process({}, message.to_json)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should not fail if there is parsing error" do
|
57
|
+
expect {
|
58
|
+
MyProcessor.new.process({}, "guila")
|
59
|
+
}.to_not raise_error(JSON::ParserError)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should not fail if the message doesn't meet the protocol" do
|
63
|
+
expect {
|
64
|
+
MyProcessor.new.process({}, {})
|
65
|
+
}.to_not raise_error(NoMethodError)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
3
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
4
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
5
|
+
# loaded once.
|
6
|
+
#
|
7
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
8
|
+
|
9
|
+
require 'untied-consumer'
|
10
|
+
require 'ruby-debug'
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
14
|
+
config.run_all_when_everything_filtered = true
|
15
|
+
config.filter_run :focus
|
16
|
+
|
17
|
+
# Run specs in random order to surface order dependencies. If you find an
|
18
|
+
# order dependency and want to debug it, you can fix the order by providing
|
19
|
+
# the seed, which is printed after each run.
|
20
|
+
# --seed 1234
|
21
|
+
config.order = 'random'
|
22
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'untied-consumer/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "untied-consumer"
|
8
|
+
gem.version = Untied::Consumer::VERSION
|
9
|
+
gem.authors = ["Guilherme Cavalcanti"]
|
10
|
+
gem.email = ["guiocavalcanti@gmail.com"]
|
11
|
+
gem.description = "Provides the Consumer part of the Untied gem."
|
12
|
+
gem.summary = "Untied is a Observer Pattern implementation for distributed systems. Think as a cross-application ActiveRecord::Observer. This gem handles the listening of events"
|
13
|
+
gem.homepage = "http://github.com/redu/untied"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(spec)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_development_dependency "rspec"
|
21
|
+
gem.add_development_dependency "sqlite3"
|
22
|
+
gem.add_development_dependency "rake"
|
23
|
+
|
24
|
+
gem.add_runtime_dependency "activemodel"
|
25
|
+
gem.add_runtime_dependency "amqp"
|
26
|
+
gem.add_runtime_dependency "configurable"
|
27
|
+
gem.add_runtime_dependency "json"
|
28
|
+
|
29
|
+
if RUBY_VERSION < "1.9"
|
30
|
+
gem.add_runtime_dependency "system_timer"
|
31
|
+
gem.add_development_dependency "ruby-debug"
|
32
|
+
else
|
33
|
+
gem.add_development_dependency "debugger"
|
34
|
+
end
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: untied-consumer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Guilherme Cavalcanti
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-10-20 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: sqlite3
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: activemodel
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: amqp
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: configurable
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: json
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: debugger
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
description: Provides the Consumer part of the Untied gem.
|
143
|
+
email:
|
144
|
+
- guiocavalcanti@gmail.com
|
145
|
+
executables: []
|
146
|
+
extensions: []
|
147
|
+
extra_rdoc_files: []
|
148
|
+
files:
|
149
|
+
- .gitignore
|
150
|
+
- .rvmrc
|
151
|
+
- .travis.yml
|
152
|
+
- Gemfile
|
153
|
+
- LICENSE.txt
|
154
|
+
- README.md
|
155
|
+
- Rakefile
|
156
|
+
- lib/untied-consumer.rb
|
157
|
+
- lib/untied-consumer/config.rb
|
158
|
+
- lib/untied-consumer/event.rb
|
159
|
+
- lib/untied-consumer/observer.rb
|
160
|
+
- lib/untied-consumer/processor.rb
|
161
|
+
- lib/untied-consumer/railtie.rb
|
162
|
+
- lib/untied-consumer/tasks/untied.tasks
|
163
|
+
- lib/untied-consumer/tasks/untied_rails.tasks
|
164
|
+
- lib/untied-consumer/version.rb
|
165
|
+
- lib/untied-consumer/worker.rb
|
166
|
+
- spec/event_spec.rb
|
167
|
+
- spec/observer_spec.rb
|
168
|
+
- spec/processor_spec.rb
|
169
|
+
- spec/spec_helper.rb
|
170
|
+
- untied-consumer.gemspec
|
171
|
+
homepage: http://github.com/redu/untied
|
172
|
+
licenses: []
|
173
|
+
post_install_message:
|
174
|
+
rdoc_options: []
|
175
|
+
require_paths:
|
176
|
+
- lib
|
177
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
178
|
+
none: false
|
179
|
+
requirements:
|
180
|
+
- - ! '>='
|
181
|
+
- !ruby/object:Gem::Version
|
182
|
+
version: '0'
|
183
|
+
segments:
|
184
|
+
- 0
|
185
|
+
hash: 2730721219158462455
|
186
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
187
|
+
none: false
|
188
|
+
requirements:
|
189
|
+
- - ! '>='
|
190
|
+
- !ruby/object:Gem::Version
|
191
|
+
version: '0'
|
192
|
+
segments:
|
193
|
+
- 0
|
194
|
+
hash: 2730721219158462455
|
195
|
+
requirements: []
|
196
|
+
rubyforge_project:
|
197
|
+
rubygems_version: 1.8.24
|
198
|
+
signing_key:
|
199
|
+
specification_version: 3
|
200
|
+
summary: Untied is a Observer Pattern implementation for distributed systems. Think
|
201
|
+
as a cross-application ActiveRecord::Observer. This gem handles the listening of
|
202
|
+
events
|
203
|
+
test_files:
|
204
|
+
- spec/event_spec.rb
|
205
|
+
- spec/observer_spec.rb
|
206
|
+
- spec/processor_spec.rb
|
207
|
+
- spec/spec_helper.rb
|