untied 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +20 -0
- data/.rvmrc +48 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +7 -0
- data/README.md +124 -0
- data/Rakefile +3 -0
- data/bin/untied_worker +16 -0
- data/examples/goliath/.rvmrc +48 -0
- data/examples/goliath/Gemfile +9 -0
- data/examples/goliath/README.mkd +81 -0
- data/examples/goliath/Rakefile +14 -0
- data/examples/goliath/config/srv.rb +21 -0
- data/examples/goliath/models/user.rb +5 -0
- data/examples/goliath/observer.rb +10 -0
- data/examples/goliath/srv.rb +46 -0
- data/examples/rails-consumer/.rvmrc +48 -0
- data/examples/rails-consumer/Gemfile +38 -0
- data/examples/rails-consumer/README.mkd +37 -0
- data/examples/rails-consumer/Rakefile +7 -0
- data/examples/rails-consumer/app/assets/images/rails.png +0 -0
- data/examples/rails-consumer/app/assets/javascripts/application.js +15 -0
- data/examples/rails-consumer/app/assets/javascripts/users.js.coffee +3 -0
- data/examples/rails-consumer/app/assets/stylesheets/application.css +13 -0
- data/examples/rails-consumer/app/assets/stylesheets/scaffolds.css.scss +69 -0
- data/examples/rails-consumer/app/assets/stylesheets/users.css.scss +3 -0
- data/examples/rails-consumer/app/controllers/application_controller.rb +3 -0
- data/examples/rails-consumer/app/controllers/users_controller.rb +83 -0
- data/examples/rails-consumer/app/helpers/application_helper.rb +2 -0
- data/examples/rails-consumer/app/helpers/users_helper.rb +2 -0
- data/examples/rails-consumer/app/mailers/.gitkeep +0 -0
- data/examples/rails-consumer/app/models/.gitkeep +0 -0
- data/examples/rails-consumer/app/models/untied_user_observer.rb +8 -0
- data/examples/rails-consumer/app/models/user.rb +3 -0
- data/examples/rails-consumer/app/views/layouts/application.html.erb +14 -0
- data/examples/rails-consumer/app/views/users/_form.html.erb +21 -0
- data/examples/rails-consumer/app/views/users/edit.html.erb +6 -0
- data/examples/rails-consumer/app/views/users/index.html.erb +23 -0
- data/examples/rails-consumer/app/views/users/new.html.erb +5 -0
- data/examples/rails-consumer/app/views/users/show.html.erb +10 -0
- data/examples/rails-consumer/config/application.rb +62 -0
- data/examples/rails-consumer/config/boot.rb +6 -0
- data/examples/rails-consumer/config/database.yml +25 -0
- data/examples/rails-consumer/config/environment.rb +5 -0
- data/examples/rails-consumer/config/environments/development.rb +37 -0
- data/examples/rails-consumer/config/environments/production.rb +67 -0
- data/examples/rails-consumer/config/environments/test.rb +37 -0
- data/examples/rails-consumer/config/initializers/backtrace_silencers.rb +7 -0
- data/examples/rails-consumer/config/initializers/inflections.rb +15 -0
- data/examples/rails-consumer/config/initializers/mime_types.rb +5 -0
- data/examples/rails-consumer/config/initializers/secret_token.rb +7 -0
- data/examples/rails-consumer/config/initializers/session_store.rb +8 -0
- data/examples/rails-consumer/config/initializers/untied.rb +4 -0
- data/examples/rails-consumer/config/initializers/wrap_parameters.rb +14 -0
- data/examples/rails-consumer/config/locales/en.yml +5 -0
- data/examples/rails-consumer/config/routes.rb +60 -0
- data/examples/rails-consumer/config.ru +4 -0
- data/examples/rails-consumer/db/development.sqlite3 +0 -0
- data/examples/rails-consumer/db/migrate/20121019155057_create_users.rb +9 -0
- data/examples/rails-consumer/db/schema.rb +22 -0
- data/examples/rails-consumer/db/seeds.rb +7 -0
- data/examples/rails-consumer/lib/assets/.gitkeep +0 -0
- data/examples/rails-consumer/lib/tasks/.gitkeep +0 -0
- data/examples/rails-consumer/log/.gitkeep +0 -0
- data/examples/rails-consumer/log/development.log +135 -0
- data/examples/rails-consumer/public/404.html +26 -0
- data/examples/rails-consumer/public/422.html +26 -0
- data/examples/rails-consumer/public/500.html +25 -0
- data/examples/rails-consumer/public/favicon.ico +0 -0
- data/examples/rails-consumer/public/index.html +241 -0
- data/examples/rails-consumer/public/robots.txt +5 -0
- data/examples/rails-consumer/script/rails +6 -0
- data/examples/rails-consumer/test/fixtures/.gitkeep +0 -0
- data/examples/rails-consumer/test/fixtures/users.yml +7 -0
- data/examples/rails-consumer/test/functional/.gitkeep +0 -0
- data/examples/rails-consumer/test/functional/users_controller_test.rb +49 -0
- data/examples/rails-consumer/test/integration/.gitkeep +0 -0
- data/examples/rails-consumer/test/performance/browsing_test.rb +12 -0
- data/examples/rails-consumer/test/test_helper.rb +13 -0
- data/examples/rails-consumer/test/unit/.gitkeep +0 -0
- data/examples/rails-consumer/test/unit/helpers/users_helper_test.rb +4 -0
- data/examples/rails-consumer/test/unit/user_test.rb +7 -0
- data/examples/rails-consumer/vendor/assets/javascripts/.gitkeep +0 -0
- data/examples/rails-consumer/vendor/assets/stylesheets/.gitkeep +0 -0
- data/examples/rails-consumer/vendor/plugins/.gitkeep +0 -0
- data/examples/social_network.rb +1 -0
- data/lib/untied/version.rb +4 -0
- data/lib/untied.rb +20 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/support/setup_ar_and_schema.rb +24 -0
- data/untied.gemspec +25 -0
- metadata +212 -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,7 @@
|
|
1
|
+
Copyright (c) 2012 Redu Educational Technologies
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
# Untied
|
2
|
+
|
3
|
+
Need to register an Observer which observes ActiveRecord models in different applications? Untied Observer for the rescue.
|
4
|
+
|
5
|
+
The publisher application registers which models are able to be observed. The consumers just need to define callbacks that will be fired for certain events. The consumer part uses an API similar to the one provided by ActiveRecord::Observer.
|
6
|
+
|
7
|
+
**Build status**
|
8
|
+
|
9
|
+
- Untied::Consumer [![Build Status](https://travis-ci.org/redu/untied-consumer.png)](https://travis-ci.org/redu/untied-consumer)
|
10
|
+
- Untied::Publisher [![Build Status](https://travis-ci.org/redu/untied-publisher.png)](https://travis-ci.org/redu/untied-publisher)
|
11
|
+
|
12
|
+
### Publisher
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Add this line to your application's Gemfile:
|
17
|
+
|
18
|
+
gem 'untied'
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
$ bundle
|
23
|
+
|
24
|
+
Or install it yourself as:
|
25
|
+
|
26
|
+
$ gem install untied
|
27
|
+
|
28
|
+
The untied Gem relies on RabbitMQ, so it need to be installed in order to work properly. [Here are](http://www.rabbitmq.com/download.html) the instructions.
|
29
|
+
|
30
|
+
## Usage
|
31
|
+
|
32
|
+
### Publisher
|
33
|
+
|
34
|
+
You need to do some configurations on the publisher side:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
Untied::Publisher.configure do |config|
|
38
|
+
config.logger = Logger.new(STDOUT)
|
39
|
+
config.deliver_messages = true # Silent mode when falsy
|
40
|
+
config.service_name = "social-network"
|
41
|
+
config.doorkeeper = MyDoorkeeper
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
The ``service_name`` configuration is very important here. It must be unique across all the services and will be used to uniquely identify the models. The ``deliver_messages``option enable and disable events sending. Disabling it may be useful on test and development environment.
|
46
|
+
|
47
|
+
The ``doorkeeper`` configuration let you specify which class is responsible for telling which ActiveRecord models are capable of be observed.
|
48
|
+
|
49
|
+
You should also define when the ActiveRecord models will be propagated to other services. We can take advantage on the usefulness of ``ActiveRecord::Callbacks``. To keep the things DRY, this job may be done inside what we call the Watcher:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
class MyDoorkeeper
|
53
|
+
include Untied::Doorkeeper
|
54
|
+
|
55
|
+
def initialize
|
56
|
+
watch User, :after_create, :after_update
|
57
|
+
end
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
The watcher defined above will propagate Users instances when they are created or updated.
|
62
|
+
|
63
|
+
### Consumer
|
64
|
+
|
65
|
+
On the consumer side, you just need to define the observer as you would with ActiveRecord::Observer. Remember to subclass Untied::Observer instead.
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
class UserObserver < Untied::Consumer::Observer
|
69
|
+
observe :user, :from => "social-network"
|
70
|
+
|
71
|
+
def after_create(user)
|
72
|
+
puts "A the following user was created on social-network service: #{user}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def after_update(user)
|
76
|
+
puts "A the following user was updated on social-network service: #{user}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
```
|
80
|
+
|
81
|
+
One important step is identify which service models the observer is listening to. That's why we user the ``:from`` option on the ``observe`` method.
|
82
|
+
|
83
|
+
Activating observers:
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
Untied::Consumer.configure do |config|
|
87
|
+
config.observers = [UserObserver]
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
## Internals
|
92
|
+
|
93
|
+
TODO
|
94
|
+
|
95
|
+
## What need to be done?
|
96
|
+
|
97
|
+
- Make it ActiveRecord independent.
|
98
|
+
- Add instructions about how to initialize the worker.
|
99
|
+
- Failsafeness
|
100
|
+
- Do not rely on Pub class name
|
101
|
+
|
102
|
+
|
103
|
+
## Contributing
|
104
|
+
|
105
|
+
1. Fork it
|
106
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
107
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
108
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
109
|
+
5. Create new Pull Request
|
110
|
+
|
111
|
+
|
112
|
+
<img src="https://github.com/downloads/redu/redupy/redutech-marca.png" alt="Redu Educational Technologies" width="300">
|
113
|
+
|
114
|
+
This project is maintained and funded by [Redu Educational Techologies](http://tech.redu.com.br).
|
115
|
+
|
116
|
+
# Copyright
|
117
|
+
|
118
|
+
Copyright (c) 2012 Redu Educational Technologies
|
119
|
+
|
120
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
121
|
+
|
122
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
123
|
+
|
124
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
data/bin/untied_worker
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "amqp"
|
4
|
+
|
5
|
+
require "untied"
|
6
|
+
require "untied/worker"
|
7
|
+
|
8
|
+
module Untied
|
9
|
+
AMQP.start do |connection|
|
10
|
+
channel = AMQP::Channel.new(connection)
|
11
|
+
exchange = channel.topic("untied", :auto_delete => true)
|
12
|
+
# consumer = Consumer.new
|
13
|
+
worker = Consumer::Worker.new(:channel => channel, :exchange => exchange)
|
14
|
+
worker.start
|
15
|
+
end
|
16
|
+
end
|
@@ -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.9.2" > .rvmrc
|
9
|
+
environment_id="ruby-1.9.2-p320"
|
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
|
@@ -0,0 +1,81 @@
|
|
1
|
+
## Using Untied with Goliath
|
2
|
+
|
3
|
+
This example defines both the Producer and the Consumer. The producer is defined inside srv.rb, which is a goliath server. The consumer doesn't depend on the producer in any way.
|
4
|
+
|
5
|
+
### Setup
|
6
|
+
|
7
|
+
```sh
|
8
|
+
$ > git clone git://github.com/redu/untied.git
|
9
|
+
$ > cd untied/examples/goliath
|
10
|
+
$ > bundle install
|
11
|
+
```
|
12
|
+
|
13
|
+
The Untied gem relies on RabbitMQ, so it need to be installed in order to work properly. [Here are](http://www.rabbitmq.com/download.html) the instructions.
|
14
|
+
|
15
|
+
### Structure
|
16
|
+
|
17
|
+
```sh
|
18
|
+
.
|
19
|
+
├── Gemfile
|
20
|
+
├── Gemfile.lock
|
21
|
+
├── README.mkd
|
22
|
+
├── Rakefile # Requires the observer.rb. Listens on the message bus.
|
23
|
+
├── config
|
24
|
+
│ └── srv.rb # ActiveRecord and Untied Publisher configurations
|
25
|
+
├── models
|
26
|
+
│ └── user.rb # ActiveRecord User model
|
27
|
+
├── observer.rb # Untied observer used by consumer
|
28
|
+
└── srv.rb # Goliath server and Untied::Doorkeeper
|
29
|
+
```
|
30
|
+
|
31
|
+
### More info
|
32
|
+
|
33
|
+
If you don't know what some of the components above means, here are some references:
|
34
|
+
|
35
|
+
- [Untied::Observer](https://github.com/redu/untied#consumer)
|
36
|
+
- [Untied publisher configurations](https://github.com/redu/untied#publisher)
|
37
|
+
- [Goliath](http://postrank-labs.github.com/goliath/)
|
38
|
+
- [Goliath configurarions](https://github.com/postrank-labs/goliath/wiki/Configuration)
|
39
|
+
|
40
|
+
### Usage
|
41
|
+
|
42
|
+
#### Publisher
|
43
|
+
|
44
|
+
The publisher consists of a REST API defined as a Goliath server. Every time an User is created using this API the Untied publisher will serialize it and deliver it through the message bus.
|
45
|
+
|
46
|
+
All you need is to initialize goliath server
|
47
|
+
|
48
|
+
```sh
|
49
|
+
goliath (master) > ruby srv.rb -sv
|
50
|
+
I, [2012-10-18T10:06:50.806799 #9274] INFO -- : Untied: Initializing publisher observer
|
51
|
+
[9274:INFO] 2012-10-18 10:06:50 :: Starting server on 0.0.0.0:9000 in development mode. Watch out for stones.
|
52
|
+
-- create_table(:users, {:force=>true})
|
53
|
+
-> 0.0158s
|
54
|
+
```
|
55
|
+
|
56
|
+
And use the REST API:
|
57
|
+
|
58
|
+
```sh
|
59
|
+
goliath (master) > curl http://0.0.0.0:9000?name=guila -X POST
|
60
|
+
"{\"user\":{\"created_at\":\"2012-10-18T09:59:14-03:00\",\"id\":1,\"name\":\"guila\",\"updated_at\":\"2012-10-18T09:59:14-03:00\"}}"
|
61
|
+
```
|
62
|
+
|
63
|
+
#### Consumer
|
64
|
+
|
65
|
+
The consumer listens to the events sent to the message bus and fires defined Untied::Observer methods. In this example we just defined an ``after_create`` method for the User entity (see observer.rb).
|
66
|
+
|
67
|
+
To initialize the cosumer call the following rake task:
|
68
|
+
|
69
|
+
```sh
|
70
|
+
goliath (master) > rake untied:consumer:work
|
71
|
+
I, [2012-10-18T10:09:44.687311 #9327] INFO -- : Worker initialized and listening
|
72
|
+
```
|
73
|
+
|
74
|
+
Everytime an user is created on the publisher side, the consumer will process the event and call the proper callback (defined in observer.rb):
|
75
|
+
|
76
|
+
```
|
77
|
+
# I, [2012-10-18T09:59:14.927815 #9133] INFO -- : Untied::Consumer: processing event after_create from goliath with payload {:user=>{:created_at=>"2012-10-18T09:59:14-03:00", :id=>1, :name=>"guila", :updated_at=>"2012-10-18T09:59:14-03:00"}}
|
78
|
+
# An user was created on Goliath server, yay!
|
79
|
+
```
|
80
|
+
|
81
|
+
Of course you should consume events produced by other server, say a Rails server.
|
@@ -0,0 +1,14 @@
|
|
1
|
+
$: << File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "untied"
|
5
|
+
require "untied-consumer"
|
6
|
+
|
7
|
+
require "./observer"
|
8
|
+
|
9
|
+
# Enabling the observer defined in observer.rb
|
10
|
+
Untied::Consumer.configure do |c|
|
11
|
+
c.observers = [Observer]
|
12
|
+
end
|
13
|
+
|
14
|
+
load "untied-consumer/tasks/untied.tasks"
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'em-synchrony/activerecord'
|
3
|
+
|
4
|
+
CONFIG = { Goliath.env.to_sym => { :adapter => 'sqlite3', :database => ":memory:" } }
|
5
|
+
ActiveRecord::Base.configurations = CONFIG
|
6
|
+
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[Goliath.env])
|
7
|
+
|
8
|
+
ActiveRecord::Schema.define do
|
9
|
+
create_table :users, :force => true do |t|
|
10
|
+
t.string :name
|
11
|
+
t.timestamp :created_at
|
12
|
+
t.timestamp :updated_at
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
Untied::Publisher.configure do |config|
|
17
|
+
config.logger = Logger.new(STDOUT)
|
18
|
+
config.deliver_messages = true
|
19
|
+
config.service_name = "goliath"
|
20
|
+
config.doorkeeper = "Doorkeeper"
|
21
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
# Here you should define the method which are going to be called when the
|
3
|
+
# publisher sends some event.
|
4
|
+
class Observer < Untied::Consumer::Observer
|
5
|
+
observe :user, :from => :goliath
|
6
|
+
|
7
|
+
def after_create(model)
|
8
|
+
puts "An user was created on Goliath server, yay!"
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
$: << File.dirname(__FILE__)
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'goliath'
|
6
|
+
require 'em-synchrony/activerecord'
|
7
|
+
require 'untied'
|
8
|
+
require 'untied-publisher'
|
9
|
+
|
10
|
+
require 'models/user'
|
11
|
+
|
12
|
+
# Defining which ActiveRecord lifecycle events will be observed
|
13
|
+
class Doorkeeper
|
14
|
+
include Untied::Publisher::Doorkeeper
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
# Everytime the User's after_create is fired, it will send the user
|
18
|
+
# through the message bus
|
19
|
+
watch User, :after_create
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Untied::Publisher.config.doorkeeper = Doorkeeper
|
24
|
+
|
25
|
+
# Initializing the publisher observer
|
26
|
+
Untied::Publisher::Observer.instance
|
27
|
+
|
28
|
+
class Srv < Goliath::API
|
29
|
+
use Goliath::Rack::Params
|
30
|
+
use Goliath::Rack::DefaultMimeType
|
31
|
+
use Goliath::Rack::Render, 'json'
|
32
|
+
|
33
|
+
def response(env)
|
34
|
+
if env['REQUEST_METHOD'] == 'GET'
|
35
|
+
begin
|
36
|
+
user = User.find(params['id'])
|
37
|
+
[200, {}, user.to_json]
|
38
|
+
rescue ActiveRecord::RecordNotFound => e
|
39
|
+
[404, {}, {:error => e.message}.to_json]
|
40
|
+
end
|
41
|
+
else
|
42
|
+
user = User.create(params)
|
43
|
+
[200, {}, user.to_json]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -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.9.3" > .rvmrc
|
9
|
+
environment_id="ruby-1.9.3-p194"
|
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
|
@@ -0,0 +1,38 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gem 'rails', '3.2.8'
|
4
|
+
|
5
|
+
# Bundle edge Rails instead:
|
6
|
+
# gem 'rails', :git => 'git://github.com/rails/rails.git'
|
7
|
+
|
8
|
+
gem 'sqlite3'
|
9
|
+
gem 'untied-consumer'
|
10
|
+
|
11
|
+
# Gems used only for assets and not required
|
12
|
+
# in production environments by default.
|
13
|
+
group :assets do
|
14
|
+
gem 'sass-rails', '~> 3.2.3'
|
15
|
+
gem 'coffee-rails', '~> 3.2.1'
|
16
|
+
|
17
|
+
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
18
|
+
# gem 'therubyracer', :platforms => :ruby
|
19
|
+
|
20
|
+
gem 'uglifier', '>= 1.0.3'
|
21
|
+
end
|
22
|
+
|
23
|
+
gem 'jquery-rails'
|
24
|
+
|
25
|
+
# To use ActiveModel has_secure_password
|
26
|
+
# gem 'bcrypt-ruby', '~> 3.0.0'
|
27
|
+
|
28
|
+
# To use Jbuilder templates for JSON
|
29
|
+
# gem 'jbuilder'
|
30
|
+
|
31
|
+
# Use unicorn as the app server
|
32
|
+
# gem 'unicorn'
|
33
|
+
|
34
|
+
# Deploy with Capistrano
|
35
|
+
# gem 'capistrano'
|
36
|
+
|
37
|
+
# To use debugger
|
38
|
+
gem 'debugger'
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Untied::Consumer Rails example
|
2
|
+
|
3
|
+
This project shows how to use the [Untied](http://github.com/redu/untied) gem in a Rails project.
|
4
|
+
|
5
|
+
The untied Gem relies on RabbitMQ, so it need to be installed in order to work properly. [Here are](http://www.rabbitmq.com/download.html) the instructions.
|
6
|
+
|
7
|
+
## Structure
|
8
|
+
|
9
|
+
The relevant files are:
|
10
|
+
|
11
|
+
```
|
12
|
+
Gemfile
|
13
|
+
app/models/
|
14
|
+
├── untied_user_observer.rb
|
15
|
+
└── user.rb
|
16
|
+
config/initializers/
|
17
|
+
└── untied.rb
|
18
|
+
```
|
19
|
+
|
20
|
+
- The Gemfile adds untied-consumer as a dependency;
|
21
|
+
- The ``app/models/untied_user_observer.rb`` listens the Message Bus for User creation events. It inherits from Untied::Consumer::Observer and works in a similar way as the well known ActiveRecord::Observer;
|
22
|
+
- The ``config/initializes/untied.rb`` registers UntiedUserObserver;
|
23
|
+
|
24
|
+
|
25
|
+
## Running the server
|
26
|
+
|
27
|
+
```sh
|
28
|
+
$> bundle install
|
29
|
+
$> bundle exec rails s
|
30
|
+
```
|
31
|
+
|
32
|
+
## Starting the worker
|
33
|
+
|
34
|
+
```sh
|
35
|
+
$> rabbitmq-server
|
36
|
+
$> rake untied:consumer:work
|
37
|
+
```
|
@@ -0,0 +1,7 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
3
|
+
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
4
|
+
|
5
|
+
require File.expand_path('../config/application', __FILE__)
|
6
|
+
|
7
|
+
UntiedRailsExample::Application.load_tasks
|
Binary file
|
@@ -0,0 +1,15 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// the compiled file.
|
9
|
+
//
|
10
|
+
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
|
11
|
+
// GO AFTER THE REQUIRES BELOW.
|
12
|
+
//
|
13
|
+
//= require jquery
|
14
|
+
//= require jquery_ujs
|
15
|
+
//= require_tree .
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
9
|
+
* compiled file, but it's generally better to create a new file per style scope.
|
10
|
+
*
|
11
|
+
*= require_self
|
12
|
+
*= require_tree .
|
13
|
+
*/
|