orator 0.0.1 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MDJhNWFlYjk5NjdjZTI1Nzk5MGEzMzVhMjc5N2NhNjRkY2JkMGVlZA==
4
+ M2JlMGJkOWU0OGIzMWEwM2ZmNmRjMTZkZDFhZTlmMGI5ZDUzZjlhZg==
5
5
  data.tar.gz: !binary |-
6
- Zjk5YzBmNjNiMzFhZTNkMGNlYTZiYmMxN2ExMWRkNTAwMDk4OWU1MQ==
6
+ MTI4Yzg5NmZlOTJhNTc4ZmM1MzI4ZTlmNzYxMTVjYWUzOWVhMDIyNQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- ZWE2N2Y4ZTU3ZWM4ZDRiNmZhMWQyNGRlY2RkNzQ5ZWVhMTRhZmUyODc1ZGMy
10
- ZjYwMWY4YWU3Y2FjN2QxOGQ2YzBiMzQ4MzQ1YmVhOWZhMjk0NDNkYzNhYzgx
11
- MTk2ZWQxMjdmMGViZmVlODg3ZTU3MmIwOTFmMThmOGQ4NDE1OGY=
9
+ OWYxOGY0ZDVmNWEzY2JmNTkxZjdkMjlmMTc5OTI4NTgxNDdhMzFlYjQxYWVl
10
+ N2IwOTE4YzlkZjU4ZjQzYmEwNzI0NTIwZmFlYTlhOWUxNWQ4MWFhM2VjNGJk
11
+ YmU5NDg1ZTYxNWFlMzNhYzhjMjM2ODRhYTNjNjRjZTRjNzQyNDU=
12
12
  data.tar.gz: !binary |-
13
- NzJhODQzZWNhZDhkZDU3NGViN2ZiYmYzYTY4NDU3NTVkZWQ0ZGE3YmJjYzc3
14
- ZTkxYTJlOGExZjI5MmQ0NzFhNDI0MGE2YTBhZGNmOTRhOWE0Mjg0YzA0MzU2
15
- YTFjYTM0ZmE1ODcwYTFiZmM1MWI4OTQyNjgxMjUyMGZiNDQ3ODU=
13
+ OTQ3YzgzOTc5YmY5YmY5OTk1MzcyMWM3YzNjNGU1YzI5ZmEzZmI4OTg2NzU4
14
+ ZjNjOTQ4MjkyZTJmNDc3ZGE5MDQ0NjFjN2Q3MmM2OGJiZDUxOGZjZTM4YWJh
15
+ YzFkYWU2OTU3YmFiOWU1MmNlZjBlMzM3MDg5NmE1Nzc4YzEyNWQ=
data/README.md CHANGED
@@ -9,7 +9,7 @@ well as [em-websocket](https://github.com/igrigorik/em-websocket).
9
9
  ## Ruby on Rails Integration
10
10
  Orator integrates easily with Ruby on Rails. Just add
11
11
 
12
- gem 'orator', :github => "redjazz96/orator"
12
+ gem 'orator'
13
13
 
14
14
  to your Gemfile and `bundle install` away. In order to use it, you'll need to
15
15
  add the following line to your `application.js` file:
@@ -19,51 +19,142 @@ add the following line to your `application.js` file:
19
19
  Now you can use the orator client in your JavaScript! If the browser doesn't
20
20
  support JavaScript, it'll fall back to a flash object.
21
21
 
22
+ ## Server Handlers
23
+ We'll start with the server-side stuff. A simple `rails g orator:config` will
24
+ place the file `orator_config.yml` in `config/`, and an example handler in
25
+ `app/handlers`. Feel free to modify the configuration file (I'll add
26
+ documentation to it). Let's set up a handler here:
27
+
28
+ ```Ruby
29
+ class TestHandler < Orator::Handlers::Base
30
+ ```
31
+
32
+ This is important. Orator will not accept the Handler unless it is a child
33
+ of `Orator::Handlers::Base`. `Orator::Handlers::Base` implements methods that
34
+ are used by Orator.
35
+
36
+ ```Ruby
37
+ before do
38
+ self.number_of_messages ||= 0
39
+ self.number_of_messages += 1
40
+
41
+ if self.number_of_messages > 9_000
42
+ prevent_event
43
+ end
44
+ end
45
+ ```
46
+
47
+ Here we have a basic before block that increments a number every time we get
48
+ a message. If the number of messages is over 9,000, it will prevent the event
49
+ from occuring (but any after blocks will still run).
50
+
51
+ ```Ruby
52
+ on :ping
53
+ def ping(data)
54
+ send message('test.pong', message: 'pong')
55
+ end
56
+ ```
57
+
58
+ Here we define a basic event named `ping`. When the server receives it, it
59
+ sends back an event named `test.pong`, with the message `pong`. Cool, eh?
60
+
61
+ ```Ruby
62
+ on :pong
63
+ def pong(data)
64
+ puts data["message"] # => "pong"
65
+ end
66
+ ```
67
+
68
+ In this we're assuming that somewhere else in our code the client sent us a
69
+ `test.pong` event; maybe they did so in response to a ping that we sent.
70
+ Either way, we're outputting a message that we received.
71
+
72
+ ```Ruby
73
+ end
74
+
75
+ Orator.add_handler(TestHandler)
76
+ ```
77
+
78
+ Here we register our handler with Orator, so it can use it for routing events.
79
+ But say we also want to handle a `socket.open` event. Our handler above can't
80
+ handle that, sadly, so we use this method:
81
+
82
+ ```Ruby
83
+ Orator.add_handler do |events|
84
+ events.on('socket.open') do |data|
85
+ self.user = {}
86
+
87
+ send mesasge('test.ping', message: 'ping')
88
+ end
89
+
90
+ events.on('user.alias') do |data|
91
+ self.user[:name] = json["name"]
92
+ end
93
+ end
94
+ ```
95
+
96
+ So when the socket opens in `socket.open`, we set `#user` to an empty hash and
97
+ send a ping message (whose response will be handled by our TestHandler). When
98
+ we receive the `user.alias` message, we set the key-pair `:name => json["name"]`
99
+ on the `#user` hash. Cool, eh?
100
+
22
101
  ## JavaScript Client
23
102
  Let's go through an example together.
24
-
25
- $(document).ready(function() {
26
- Orator.setup("ws://host:port/path?query", function(events){
103
+
104
+ ```JavaScript
105
+ $(document).ready(function() {
106
+ Orator.setup("ws://host:port/path?query", function(events){
107
+ ```
27
108
 
28
109
  `Orator.setup` handles setting up our socket for us. It also sets up the
29
110
  events and routing, as well. There are some locally defined events that are
30
111
  triggered even if the server hadn't sent them, such as `socket.open` and
31
112
  `socket.close`.
32
113
 
33
- events.on('some.event', function() {})
114
+ ```JavaScript
115
+ events.on('some.event', function() {})
116
+ ```
34
117
 
35
118
  Here we're binding some event named `some.event` to an empty function. Boring.
36
119
  Note that the name of the event doesn't matter here.
37
120
 
38
- events.on('test.ping', function() {
39
- this.server.send('test.pong', { message: 'pong' });
40
- });
121
+ ```JavaScript
122
+ events.on('test.ping', function() {
123
+ this.server.send('test.pong', { message: 'pong' });
124
+ });
125
+ ```
41
126
 
42
127
  Here we're responding to a ping that a server might send to this client. We
43
128
  send back a `test.pong` event, with the message 'pong'. The contents of the
44
129
  event can be anything.
45
130
 
46
- events.on('test.pong', function(data) {
47
- console.log(data.message) # => "pong"
48
- });
131
+ ```JavaScript
132
+ events.on('test.pong', function(data) {
133
+ console.log(data.message) // => "pong"
134
+ });
135
+ ```
49
136
 
50
137
  Here we received a pong back from the server - this was triggered by the server
51
138
  in response to our ping, which we could have sent at any time.
52
139
 
53
- events.on('socket.open', function() {
54
- this.user = {}
140
+ ```JavaScript
141
+ events.on('socket.open', function() {
142
+ this.user = {}
55
143
 
56
- this.server.send('user.alias', { name: some_name })
57
- });
144
+ this.server.send('user.alias', { name: some_name })
145
+ });
146
+ ```
58
147
 
59
148
  Here we defined an event that will set up a an empty object on the property
60
149
  user of `this`. This will become important. We then send the event
61
150
  `user.alias` to the server (which probably changes the name of the user) with
62
151
  the new name (assuming `some_name` has a string value).
63
152
 
64
- events.on('user.alias', function(data) {
65
- this.user.name = data.name;
66
- });
153
+ ```JavaScript
154
+ events.on('user.alias', function(data) {
155
+ this.user.name = data.name;
156
+ });
157
+ ```
67
158
 
68
159
  It is common place for the server's response events to be named the same as the
69
160
  request events (although the response events may be sent at any time, with no
@@ -71,15 +162,10 @@ context). Here we see setting the name of the user object to the name the
71
162
  server sent back. This is the same user object that we defined in the event
72
163
  above.
73
164
 
74
- });
165
+ ```JavaScript
166
+ });
75
167
 
76
- });
168
+ });
169
+ ```
77
170
 
78
171
  And we close our braces.
79
-
80
- ## Server Stuff
81
- I'm not entirely sure how to set this part up... any ideas?
82
-
83
- I'm trying to provide support for all types of rails applications, including
84
- those on passenger. I'll probably require a settings file in
85
- `Rails.root/config` for orator...
@@ -0,0 +1,14 @@
1
+ module Orator
2
+ module Generators
3
+ class ConfigGenerator < Rails::Generators::Base
4
+
5
+ source_root File.expand_path('../../templates', __FILE__)
6
+
7
+ def copy_configuration
8
+ template "config.yml", "config/orator_config.yml"
9
+ directory "handlers", "app/handlers"
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ module Orator
2
+ module Generators
3
+ class HandlerGenerator < Rails::Generators::Base
4
+
5
+ source_root File.expand_path('../../templates', __FILE__)
6
+ argument :handler_name, :required => true,
7
+ :desc => "The name of the handler to use."
8
+
9
+ def copy_configuration
10
+ template "template_handler.rb", "app/handlers/#{handler_name}_handler.rb"
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ ---
2
+ :pid_file: tmp/orator.pid
3
+ :log_file: log/orator.log
4
+ :server_options:
5
+ :host: 0.0.0.0
6
+ :port: 8080
7
+ :handlers_path: app/handlers/*
@@ -0,0 +1,31 @@
1
+ class ExampleHandler < Orator::Handlers::Base
2
+
3
+ # This defines the name of the handler. THIS IS REQUIRED. Orator will not
4
+ # be able to correctly route your events to this handler if it is not put
5
+ # here.
6
+ #handler_name 'example'
7
+
8
+ # This defines a before event. It can take a block or a symbol. If it's a
9
+ # symbol, it'll call the method.
10
+ #before do
11
+ # something
12
+ #end
13
+
14
+ # This defines the event `some_event`. Note that the two have to have the
15
+ # same value. If they're different, you need to add a second argument
16
+ # describing the method name (i.e. `on 'some_event', :some_method`).
17
+ #on 'some_event'
18
+ #def some_event(json)
19
+ # do_something
20
+ #end
21
+
22
+ # Inline syntax.
23
+ #on 'other_event' do; end
24
+
25
+ #after do
26
+ # other_thing
27
+ #end
28
+
29
+ end
30
+
31
+ #Orator.add_handler(ExampleHandler)
@@ -0,0 +1,6 @@
1
+ class <%= handler_name.camelcase %>Handler < Orator::Handlers::Base
2
+
3
+ handler_name '<%= handler_name.underscore %>'
4
+
5
+
6
+ end
data/lib/orator/cli.rb CHANGED
@@ -8,7 +8,7 @@ module Orator
8
8
 
9
9
  DEFAULT_OPTIONS = {
10
10
  :command => :help,
11
- :file => "./orator_config.yml",
11
+ :file => "config/orator_config.yml",
12
12
  :daemonize => false
13
13
  }
14
14
 
@@ -27,10 +27,14 @@ module Orator
27
27
  @options[:command] = command
28
28
  end
29
29
 
30
- opts.on('-D', '--[no-]daemonize', "Whether or not to daemonize the process.") do |d|
30
+ opts.on('-d', '--[no-]daemonize', "Whether or not to daemonize the process.") do |d|
31
31
  @options[:daemonize] = d
32
32
  end
33
33
 
34
+ opts.on('-D', '--debug', "Run Orator in debug mode.") do
35
+ Orator.debug = true
36
+ end
37
+
34
38
  opts.on('-h', '--help', "Shows this message.") { puts opts; exit }
35
39
  opts.on('-v', '--version', "Shows the version of orator.") do
36
40
  puts Orator::VERSION
@@ -45,7 +49,6 @@ module Orator
45
49
  end
46
50
 
47
51
  def handle_command
48
- Orator.debug = yaml_options[:debug]
49
52
  send(@options[:command])
50
53
  end
51
54
 
@@ -119,21 +122,9 @@ module Orator
119
122
  #
120
123
  # @return [void]
121
124
  def load_handlers
122
- handlers = []
123
- if yaml_options[:handler]
124
- handlers << yaml_options[:handler]
125
- end
126
-
127
- if yaml_options[:handlers]
128
- handlers.concat(yaml_options[:handlers])
129
- end
130
-
131
- handlers.each do |handler|
132
- begin
133
- require_relative handler
134
- rescue LoadError
135
- require handler
136
- end
125
+ p yaml_options, yaml_options[:handlers_path]
126
+ Dir[yaml_options[:handlers_path]].each do |file|
127
+ load file
137
128
  end
138
129
  end
139
130
 
@@ -20,23 +20,17 @@ module Orator
20
20
  #
21
21
  # @param event [Symbol, String] the event to trigger.
22
22
  # @raise [NoMethodError] if the event isn't defined on the handler.
23
- # @return [Array<Object>] the values of the trigger.
23
+ # @return [Array<Object>, nil] the values of the trigger, or nil if the
24
+ # event was prevented.
24
25
  def __trigger(event, *args)
25
26
  handler, method_name = event.to_s.split('.')
27
+ __check_event(handler, method_name)
26
28
 
27
- if handler != self.class.name
28
- raise NoMethodError,
29
- "Event #{handler} does not match #{self.class.name}"
30
- return
31
- elsif !__method_exists?(method_name)
32
- raise NoMethodError,
33
- "Method #{method_name} does not exist on #{self.class.name}"
34
- return
35
- end
29
+ __run_callbacks(:before)
30
+ out = __run_event(method_name, args) unless @__prevent_event
31
+ __run_callbacks(:after)
36
32
 
37
- self.class.before_list.map { |b| instance_exec &b }
38
-
39
- __run_event(method_name, args) unless @__prevent_event
33
+ out
40
34
  end
41
35
 
42
36
  # Checks the event list to see if this class responds to it.
@@ -86,23 +80,54 @@ module Orator
86
80
 
87
81
  private
88
82
 
83
+ # Run the event.
89
84
  def __run_event(method_name, args)
90
85
  result = __event_list[method_name].map do |responder|
91
86
  responder = method(responder) if responder.is_a? Symbol
92
87
  instance_exec *args, &responder
93
88
  end
94
89
 
95
- self.class.after_list.map { |a| instance_exec &a }
96
-
97
90
  result
98
91
  end
99
92
 
93
+ # Check the event, making sure that this handler can respond to it.
94
+ def __check_event(handler, method)
95
+ if handler != self.class.handler_name
96
+ raise NoMethodError,
97
+ "Event #{handler} does not match #{self.class.name}"
98
+ return
99
+ elsif !__method_exists?(method)
100
+ raise NoMethodError,
101
+ "Method #{method} does not exist on #{self.class.name}"
102
+ return
103
+ end
104
+ end
105
+
106
+ # Runs the callbacks.
107
+ def __run_callbacks(type)
108
+ self.class.send("#{type}_list").map do |cb|
109
+ if cb.is_a? Symbol
110
+ method(cb).call
111
+ else
112
+ instance_exec &cb
113
+ end
114
+ end
115
+ end
116
+
100
117
  module ClassMethods
101
- # The name of the handler. This is used to determine whether or not
102
- # the handler can run it.
118
+
119
+ # This manages the handler's name. If an argument is passed, the name
120
+ # is set to that. Otherwise, it returns the handler's name.
103
121
  #
104
- # @return [String]
105
- attr_accessor :name
122
+ # @param value [String, nil] the new name.
123
+ # @return [String] the handler name.
124
+ def handler_name(value = nil)
125
+ if value
126
+ @_handler_name = value
127
+ end
128
+
129
+ @_handler_name
130
+ end
106
131
 
107
132
  # This registers a method for this Handler.
108
133
  #
@@ -132,7 +157,7 @@ module Orator
132
157
  # @param event_handler [EventHandler]
133
158
  def register_with(event_handler)
134
159
  event_list.keys.each do |event|
135
- event_handler.on("#{self.name}.#{event}", self)
160
+ event_handler.on("#{self.handler_name}.#{event}", self)
136
161
  end
137
162
  end
138
163
 
@@ -150,7 +175,7 @@ module Orator
150
175
  end
151
176
 
152
177
  extend ClassMethods
153
- self.name = ''
178
+ handler_name ''
154
179
 
155
180
  end
156
181
 
@@ -11,6 +11,7 @@ module Orator
11
11
  # Initialize the class.
12
12
  def initialize(client)
13
13
  @client = client
14
+ @struct = OpenStruct.new
14
15
  end
15
16
 
16
17
  # This sends a message to the client.
@@ -34,5 +35,17 @@ module Orator
34
35
 
35
36
  new_data
36
37
  end
38
+
39
+ # This delegates the rest of the requests to the struct.
40
+ def method_missing(method, *args, &block)
41
+ super unless respond_to_missing?(method)
42
+
43
+ @struct.__send__(method, *args, &block)
44
+ end
45
+
46
+ # Lets ruby know we're doing some {#method_missing} magic.
47
+ def respond_to_missing?(method, include_private = false)
48
+ @struct.respond_to?(method, include_private)
49
+ end
37
50
  end
38
51
  end
@@ -1,5 +1,5 @@
1
1
  module Orator
2
2
 
3
3
  # Version of Orator.
4
- VERSION = '0.0.1'
4
+ VERSION = '0.1.1'
5
5
  end
data/lib/orator.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'oj'
4
4
  require 'set'
5
+ require 'ostruct'
5
6
  require 'em-websocket'
6
7
 
7
8
  require 'orator/server'
@@ -17,5 +18,9 @@ module Orator
17
18
 
18
19
  class << self; attr_accessor :debug; end
19
20
  Orator.debug = false
21
+
22
+ def self.add_handler(*args, &block)
23
+ Orator::CLI.add_handler(*args, &block)
24
+ end
20
25
 
21
26
  end
@@ -0,0 +1,3 @@
1
+ namespace :orator do
2
+
3
+ end
@@ -3,7 +3,7 @@ describe Orator::Handlers::Base do
3
3
  something = double('something')
4
4
  something.should_receive(:to_call)
5
5
  klass = Class.new(described_class) do
6
- self.name = 'test'
6
+ handler_name 'test'
7
7
  before { something.to_call }
8
8
  on 'some_event'
9
9
  def some_event; end
@@ -21,7 +21,7 @@ describe Orator::Handlers::Base do
21
21
  something = double('something')
22
22
  something.should_not_receive(:to_call)
23
23
  klass = Class.new(described_class) do
24
- self.name = 'test'
24
+ handler_name 'test'
25
25
  before { prevent_event }
26
26
  on('some_event') { something.to_call }
27
27
  end
@@ -33,7 +33,7 @@ describe Orator::Handlers::Base do
33
33
  event_handler = double('event_handler')
34
34
 
35
35
  klass = Class.new(described_class) do
36
- self.name = 'test'
36
+ handler_name 'test'
37
37
 
38
38
  on('some_event') { }
39
39
  end
@@ -42,4 +42,34 @@ describe Orator::Handlers::Base do
42
42
 
43
43
  klass.register_with(event_handler)
44
44
  end
45
+
46
+ it "should be able to take a before method" do
47
+ something = double('something')
48
+ something.should_not_receive(:to_call)
49
+
50
+ klass = Class.new(described_class) do
51
+ handler_name 'test'
52
+
53
+ before :do_something
54
+
55
+ def do_something; prevent_event; end
56
+
57
+ on 'some_event' do; something.to_call; end
58
+ end
59
+
60
+ klass.new(nil).__trigger('test.some_event')
61
+ end
62
+
63
+ it "should be able to use a different method" do
64
+
65
+ klass = Class.new(described_class) do
66
+ handler_name 'test'
67
+
68
+ on 'some_event', :other_method
69
+ def other_method; end
70
+ def some_event; raise StandardError; end
71
+ end
72
+
73
+ klass.new(nil).__trigger('test.some_event')
74
+ end
45
75
  end
@@ -0,0 +1,2 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: orator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Rodi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-04-07 00:00:00.000000000 Z
11
+ date: 2013-04-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: em-websocket
@@ -84,9 +84,16 @@ files:
84
84
  - lib/orator/handlers/base.rb
85
85
  - lib/orator/engine.rb
86
86
  - lib/orator/event_handler.rb
87
+ - lib/generators/orator/handler_generator.rb
88
+ - lib/generators/orator/config_generator.rb
89
+ - lib/generators/templates/template_handler.rb
90
+ - lib/generators/templates/config.yml
91
+ - lib/generators/templates/handlers/example_handler.rb
87
92
  - lib/orator.rb
93
+ - lib/tasks/orator.rake
88
94
  - bin/orator
89
95
  - spec/client_spec.rb
96
+ - spec/spec_helper.rb
90
97
  - spec/middle_ground_spec.rb
91
98
  - spec/event_handler_spec.rb
92
99
  - spec/base_handler_spec.rb