orator 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MDJhNWFlYjk5NjdjZTI1Nzk5MGEzMzVhMjc5N2NhNjRkY2JkMGVlZA==
5
+ data.tar.gz: !binary |-
6
+ Zjk5YzBmNjNiMzFhZTNkMGNlYTZiYmMxN2ExMWRkNTAwMDk4OWU1MQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ ZWE2N2Y4ZTU3ZWM4ZDRiNmZhMWQyNGRlY2RkNzQ5ZWVhMTRhZmUyODc1ZGMy
10
+ ZjYwMWY4YWU3Y2FjN2QxOGQ2YzBiMzQ4MzQ1YmVhOWZhMjk0NDNkYzNhYzgx
11
+ MTk2ZWQxMjdmMGViZmVlODg3ZTU3MmIwOTFmMThmOGQ4NDE1OGY=
12
+ data.tar.gz: !binary |-
13
+ NzJhODQzZWNhZDhkZDU3NGViN2ZiYmYzYTY4NDU3NTVkZWQ0ZGE3YmJjYzc3
14
+ ZTkxYTJlOGExZjI5MmQ0NzFhNDI0MGE2YTBhZGNmOTRhOWE0Mjg0YzA0MzU2
15
+ YTFjYTM0ZmE1ODcwYTFiZmM1MWI4OTQyNjgxMjUyMGZiNDQ3ODU=
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2013 Jeremy Rodi
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # Orator [![Code Climate](https://codeclimate.com/github/redjazz96/orator.png)](https://codeclimate.com/github/redjazz96/orator) [![Build Status](https://travis-ci.org/redjazz96/orator.png?branch=master)](https://travis-ci.org/redjazz96/orator)
2
+
3
+ Orator is a websocket client/server combination that uses events in order to
4
+ talk to each other. It is still early in its development.
5
+
6
+ This project uses [web-socket-js](https://github.com/gimite/web-socket-js/) as
7
+ well as [em-websocket](https://github.com/igrigorik/em-websocket).
8
+
9
+ ## Ruby on Rails Integration
10
+ Orator integrates easily with Ruby on Rails. Just add
11
+
12
+ gem 'orator', :github => "redjazz96/orator"
13
+
14
+ to your Gemfile and `bundle install` away. In order to use it, you'll need to
15
+ add the following line to your `application.js` file:
16
+
17
+ //= require orator
18
+
19
+ Now you can use the orator client in your JavaScript! If the browser doesn't
20
+ support JavaScript, it'll fall back to a flash object.
21
+
22
+ ## JavaScript Client
23
+ Let's go through an example together.
24
+
25
+ $(document).ready(function() {
26
+ Orator.setup("ws://host:port/path?query", function(events){
27
+
28
+ `Orator.setup` handles setting up our socket for us. It also sets up the
29
+ events and routing, as well. There are some locally defined events that are
30
+ triggered even if the server hadn't sent them, such as `socket.open` and
31
+ `socket.close`.
32
+
33
+ events.on('some.event', function() {})
34
+
35
+ Here we're binding some event named `some.event` to an empty function. Boring.
36
+ Note that the name of the event doesn't matter here.
37
+
38
+ events.on('test.ping', function() {
39
+ this.server.send('test.pong', { message: 'pong' });
40
+ });
41
+
42
+ Here we're responding to a ping that a server might send to this client. We
43
+ send back a `test.pong` event, with the message 'pong'. The contents of the
44
+ event can be anything.
45
+
46
+ events.on('test.pong', function(data) {
47
+ console.log(data.message) # => "pong"
48
+ });
49
+
50
+ Here we received a pong back from the server - this was triggered by the server
51
+ in response to our ping, which we could have sent at any time.
52
+
53
+ events.on('socket.open', function() {
54
+ this.user = {}
55
+
56
+ this.server.send('user.alias', { name: some_name })
57
+ });
58
+
59
+ Here we defined an event that will set up a an empty object on the property
60
+ user of `this`. This will become important. We then send the event
61
+ `user.alias` to the server (which probably changes the name of the user) with
62
+ the new name (assuming `some_name` has a string value).
63
+
64
+ events.on('user.alias', function(data) {
65
+ this.user.name = data.name;
66
+ });
67
+
68
+ It is common place for the server's response events to be named the same as the
69
+ request events (although the response events may be sent at any time, with no
70
+ context). Here we see setting the name of the user object to the name the
71
+ server sent back. This is the same user object that we defined in the event
72
+ above.
73
+
74
+ });
75
+
76
+ });
77
+
78
+ 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...
data/Rakefile ADDED
File without changes
data/bin/orator ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ library_path = File.expand_path("../../lib", __FILE__)
4
+ $LOAD_PATH.unshift library_path unless $LOAD_PATH.include? library_path
5
+ require "orator"
6
+ require "orator/cli"
7
+
8
+ cli = Orator::CLI.new
9
+ cli.parse_arguments ARGV
10
+ cli.handle_command
data/lib/orator/cli.rb ADDED
@@ -0,0 +1,163 @@
1
+ require 'optparse'
2
+ require 'yaml'
3
+
4
+ module Orator
5
+
6
+ # Handles the command line interface for Orator.
7
+ class CLI
8
+
9
+ DEFAULT_OPTIONS = {
10
+ :command => :help,
11
+ :file => "./orator_config.yml",
12
+ :daemonize => false
13
+ }
14
+
15
+ def initialize
16
+ @options = {}.merge DEFAULT_OPTIONS
17
+ end
18
+
19
+ def parse_arguments(args)
20
+ OptionParser.new do |opts|
21
+
22
+ opts.on('--config FILE', "Loads the configuration settings from FILE.") do |file|
23
+ @options[:file] = file
24
+ end
25
+
26
+ opts.on('-cCOMMAND', '--command COMMAND', "The command to run.") do |command|
27
+ @options[:command] = command
28
+ end
29
+
30
+ opts.on('-D', '--[no-]daemonize', "Whether or not to daemonize the process.") do |d|
31
+ @options[:daemonize] = d
32
+ end
33
+
34
+ opts.on('-h', '--help', "Shows this message.") { puts opts; exit }
35
+ opts.on('-v', '--version', "Shows the version of orator.") do
36
+ puts Orator::VERSION
37
+ exit
38
+ end
39
+
40
+ opts.separator ""
41
+ opts.separator "Valid Commands:"
42
+ opts.separator "\tstart: start the orator server."
43
+ opts.separator "\tstop: stop the orator server."
44
+ end.parse!(args)
45
+ end
46
+
47
+ def handle_command
48
+ Orator.debug = yaml_options[:debug]
49
+ send(@options[:command])
50
+ end
51
+
52
+ def start
53
+ daemonize? do
54
+ load_handlers
55
+ server = Orator::Server.new(yaml_options[:server_options])
56
+ handlers = self.class.handlers
57
+
58
+ puts "Starting server..." if Orator.debug
59
+ server.run do
60
+ handlers.each do |handler|
61
+ if handler.is_a? Proc
62
+ handler.call(self)
63
+ else
64
+ handler.register_with(self)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ def stop
72
+ if File.exists? yaml_options[:pid_file]
73
+ Process.kill 1, File.open(yaml_options[:pid_file], 'r').read.to_i
74
+ puts "Stopped Orator."
75
+ File.unlink yaml_options[:pid_file]
76
+ end
77
+ end
78
+
79
+ # Add a handler to be used by the server.
80
+ #
81
+ # @param klass [Class] the class to use as a handler.
82
+ # @return [Array<Class>] a list of handlers that will be used for the
83
+ # server.
84
+ def self.add_handler(klass = nil, &block)
85
+ handlers << (klass || block)
86
+ end
87
+
88
+ # A list of handlers.
89
+ #
90
+ # @return [Array<Class>] the handlers.
91
+ def self.handlers
92
+ @handlers ||= []
93
+ end
94
+
95
+ private
96
+
97
+ # Grab the options from the options file.
98
+ #
99
+ # @returns [Hash]
100
+ def yaml_options
101
+ @yaml_options ||= YAML::load_file @options[:file]
102
+ end
103
+
104
+ # Checks the PID file (assuming it exists) to see if the process is still
105
+ # running.
106
+ #
107
+ # @raises [ProcessExistsError] if the process is still running.
108
+ def check_pid_file
109
+ pid = File.open(yaml_options[:pid_file], 'r').read.to_i
110
+
111
+ begin
112
+ Process.kill 0, pid
113
+ raise ProcessExistsError
114
+ rescue Errno::ESRCH
115
+ end
116
+ end
117
+
118
+ # Loads the handlers from the YAML file by requiring each file.
119
+ #
120
+ # @return [void]
121
+ 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
137
+ end
138
+ end
139
+
140
+ # Daemonizes the block, if it's applicable.
141
+ #
142
+ # @yields [] in the new process, if daemonized; otherwise, a normal yield.
143
+ def daemonize?
144
+ if @options[:daemonize]
145
+ check_pid_file if File.exists? yaml_options[:pid_file]
146
+ child_process = fork do
147
+ $stdout = $stderr = File.open(yaml_options[:log_file], 'a')
148
+ $stdout.sync = true
149
+
150
+ yield
151
+ end
152
+
153
+ File.open(yaml_options[:pid_file], 'w') { |f| f.write child_process }
154
+ Process.detach child_process
155
+ else
156
+ yield
157
+ end
158
+ end
159
+
160
+ class ProcessExistsError < StandardError; end
161
+
162
+ end
163
+ end
@@ -0,0 +1,44 @@
1
+ module Orator
2
+
3
+ # A representation of a client.
4
+ class Client
5
+
6
+ # The event handler for this client.
7
+ #
8
+ # @return [EventHandler]
9
+ attr_accessor :event_handler
10
+
11
+ # The context to be used by the client.
12
+ #
13
+ # @return [Object]
14
+ attr_accessor :context
15
+
16
+ # The socket to be used to talk to the client.
17
+ #
18
+ # @return [#send]
19
+ attr_accessor :socket
20
+
21
+ # Initialize.
22
+ #
23
+ # @param data [Hash] the keys are used to set data on the client to the
24
+ # values of the hash. If the attribute doesn't exist on the client,
25
+ # it is ignored.
26
+ # @example
27
+ # client = Client.new :something => "foo"
28
+ # client.something # => "foo"
29
+ def initialize(data)
30
+ data.each do |k, v|
31
+ send "#{k}=", v if respond_to? "#{k}="
32
+ end
33
+ end
34
+
35
+ # This forwards the method call to [EventHandler#trigger], but removes an
36
+ # argument: the context.
37
+ #
38
+ # @see [EventHandler#trigger]
39
+ def trigger(event, *args)
40
+ event_handler.trigger(event, context, *args)
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,5 @@
1
+ module Orator
2
+ class Engine < Rails::Engine
3
+
4
+ end
5
+ end
@@ -0,0 +1,97 @@
1
+ module Orator
2
+
3
+ # This handles events and calls the callbacks for them.
4
+ class EventHandler
5
+
6
+ # Initialize the event handler.
7
+ #
8
+ # @yields [] in the current instance. Mainly for setting up the events.
9
+ # @return [void]
10
+ def initialize(&block)
11
+ @events = []
12
+ @_number = 0
13
+ instance_exec &block if block_given?
14
+ end
15
+
16
+ # This adds an event to the set. It can accept a block or a class. If
17
+ # it's a class, the class should inherit from [Handlers::Base]. If both
18
+ # a class and a block is given, the block is preferred.
19
+ #
20
+ # @param event [String, Symbol] the event to bind it to.
21
+ # @param klass [Handlers::Base, nil] the class that contains the method to
22
+ # use for the event.
23
+ # @param count [Numeric, nil] the number of times to run this event. No
24
+ # value is an infinite number of times.
25
+ # @raise [ArgumentError] if klass isn't a subclass of Handlers::Base.
26
+ # @return [void]
27
+ def on(event, klass = nil, count = nil, &block)
28
+ puts "Binding #{event} to #{klass || block}..." if Orator.debug
29
+ data = { :event => event.to_s, :count => count }
30
+
31
+ if block_given?
32
+ data[:block] = block
33
+ elsif klass and klass < Handlers::Base
34
+ data[:class] = klass
35
+ else
36
+ raise ArgumentError, "No class or block was given."
37
+ end
38
+
39
+ @events << data unless @events.include?(data)
40
+ end
41
+
42
+ # This triggers the events. The events aren't triggered in any particular
43
+ # order, since they are stored in a [Set]. Extra arguments are passed to
44
+ # the block/class.
45
+ #
46
+ # @param event [String, Symbol] the event to trigger.
47
+ # @param context [Object] the context to run in. If the event is mapped to
48
+ # a block, the block is executed in the context. In all cases, the
49
+ # context is appended to the end of the arguments.
50
+ # @return [Object, nil]
51
+ def trigger(event, context, *args)
52
+ puts "Running event #{event}..." if Orator.debug
53
+ events(event).map do |event|
54
+ puts "Found responder #{event[:block] || event[:class]}" if Orator.debug
55
+ if event[:block]
56
+ context.instance_exec *args, context, &event[:block]
57
+ elsif event[:class]
58
+ event[:class].new(context).__trigger(event[:event], *args)
59
+ end
60
+
61
+ if event[:count] then event[:count] -= 1 end
62
+ end
63
+
64
+ clear_events
65
+ end
66
+
67
+ # Accessor for events. If the first parameter is passed, it checks for
68
+ # events that match that event.
69
+ #
70
+ # @param matching [String, Symbol] the event to find.
71
+ # @return [Set<Hash>]
72
+ def events(matching = nil)
73
+ if matching
74
+ @events.select { |e| e[:event] == matching.to_s }
75
+ else
76
+ @events.dup
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ # This removes events where their [:count] is less than or equal to zero.
83
+ #
84
+ # @return [void]
85
+ def clear_events
86
+ @events.delete_if do |event|
87
+ if event[:count]
88
+ event[:count] <= 0
89
+ else
90
+ false
91
+ end
92
+ end
93
+ end
94
+
95
+ end
96
+
97
+ end
@@ -0,0 +1,158 @@
1
+ module Orator
2
+ module Handlers
3
+
4
+ # A base for the handlers to split off of. It handles contexts and running
5
+ # methods. Most of the methods defined in this class are prefixed with
6
+ # two dashes to prevent polluting the namespace.
7
+ #
8
+ # @abstract
9
+ class Base
10
+
11
+ # Initialize the handler with the context.
12
+ #
13
+ # @param context [Object]
14
+ def initialize(context)
15
+ @__context = context
16
+ end
17
+
18
+ # This triggers an event if it's defined on this handler. If it is not
19
+ # defined, it raises an error.
20
+ #
21
+ # @param event [Symbol, String] the event to trigger.
22
+ # @raise [NoMethodError] if the event isn't defined on the handler.
23
+ # @return [Array<Object>] the values of the trigger.
24
+ def __trigger(event, *args)
25
+ handler, method_name = event.to_s.split('.')
26
+
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
36
+
37
+ self.class.before_list.map { |b| instance_exec &b }
38
+
39
+ __run_event(method_name, args) unless @__prevent_event
40
+ end
41
+
42
+ # Checks the event list to see if this class responds to it.
43
+ #
44
+ # @return [Set<Symbol, Proc>, nil]
45
+ def __method_exists?(method)
46
+ __event_list[method]
47
+ end
48
+
49
+ # A map of the events that determine how this class will respond.
50
+ #
51
+ # @return [Hash]
52
+ # @see [ClassMethods::event_list]
53
+ def __event_list
54
+ self.class.event_list
55
+ end
56
+
57
+ # This tells the handler to not execute the events in the class. This
58
+ # should only be called in a `before` block.
59
+ def prevent_event
60
+ @__prevent_event = true
61
+ end
62
+
63
+ # Handles missing methods by delegating them to the context. Should
64
+ # never be called directly.
65
+ #
66
+ # @return [Object]
67
+ def method_missing(method, *args, &block)
68
+ super unless respond_to_missing?(method)
69
+
70
+ @__context.public_send(method, *args, &block)
71
+ end
72
+
73
+ # Lets Ruby know that there are some undefined methods on this class.
74
+ #
75
+ # @return [Boolean]
76
+ def respond_to_missing?(method, include_private = false)
77
+ @__context.respond_to?(method, include_private)
78
+ end
79
+
80
+ # This forwards {#send} on to the context.
81
+ #
82
+ # @return [Object]
83
+ def send(*args, &block)
84
+ @__context.send(*args, &block)
85
+ end
86
+
87
+ private
88
+
89
+ def __run_event(method_name, args)
90
+ result = __event_list[method_name].map do |responder|
91
+ responder = method(responder) if responder.is_a? Symbol
92
+ instance_exec *args, &responder
93
+ end
94
+
95
+ self.class.after_list.map { |a| instance_exec &a }
96
+
97
+ result
98
+ end
99
+
100
+ module ClassMethods
101
+ # The name of the handler. This is used to determine whether or not
102
+ # the handler can run it.
103
+ #
104
+ # @return [String]
105
+ attr_accessor :name
106
+
107
+ # This registers a method for this Handler.
108
+ #
109
+ # @param event [Symbol] the event to register it to.
110
+ # @return [void]
111
+ def on(event, method = nil, &block)
112
+ event_list[event.to_s] ||= []
113
+
114
+ if block_given?
115
+ event_list[event.to_s] << block
116
+ else
117
+ event_list[event.to_s] << (method || event).to_sym
118
+ end
119
+ end
120
+
121
+ # The event matchings. The keys are the event name, the values can be
122
+ # an array of mappings or a the mapping itself.
123
+ #
124
+ # @return [Hash] the event list.
125
+ def event_list
126
+ @event_list ||= {}
127
+ end
128
+
129
+ # This registers this handler with the event handler by telling it what
130
+ # methods it responds to.
131
+ #
132
+ # @param event_handler [EventHandler]
133
+ def register_with(event_handler)
134
+ event_list.keys.each do |event|
135
+ event_handler.on("#{self.name}.#{event}", self)
136
+ end
137
+ end
138
+
139
+ [:before, :after].each do |type|
140
+ module_eval <<-METHOD, __FILE__, __LINE__
141
+ def #{type}(method = nil, &block)
142
+ #{type}_list << (method || block)
143
+ end
144
+
145
+ def #{type}_list
146
+ @#{type}_list ||= Set.new
147
+ end
148
+ METHOD
149
+ end
150
+ end
151
+
152
+ extend ClassMethods
153
+ self.name = ''
154
+
155
+ end
156
+
157
+ end
158
+ end
@@ -0,0 +1,9 @@
1
+ require 'orator/handlers/base'
2
+
3
+ module Orator
4
+
5
+ # Handlers to be used by orator.
6
+ module Handlers
7
+
8
+ end
9
+ end
@@ -0,0 +1,38 @@
1
+ module Orator
2
+
3
+ # This is used as a context for the client execution.
4
+ class MiddleGround
5
+
6
+ # Provides access to the client.
7
+ #
8
+ # @return [Client]
9
+ attr_reader :client
10
+
11
+ # Initialize the class.
12
+ def initialize(client)
13
+ @client = client
14
+ end
15
+
16
+ # This sends a message to the client.
17
+ #
18
+ # @param data [Hash] the data to be sent to the client.
19
+ # @return [void]
20
+ def send(data)
21
+ @client.socket.send Oj.dump(data, :mode => :null)
22
+ end
23
+
24
+ # This builds a message from a hash and an event.
25
+ #
26
+ # @param event [String, Symbol] the event for the message.
27
+ # @param data [Hash] the data for the message.
28
+ def message(event, data)
29
+ new_data = { "event" => event.to_s }
30
+
31
+ data.each do |k, v|
32
+ new_data[k.to_s] = v
33
+ end
34
+
35
+ new_data
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,70 @@
1
+ module Orator
2
+
3
+ # Handles the server connections and passing stuff to the event handler.
4
+ class Server
5
+
6
+ # The options defined for the server.
7
+ #
8
+ # @return [Hash]
9
+ attr_reader :options
10
+
11
+ # The list of clients that are connected.
12
+ #
13
+ # @return [Set<Client>]
14
+ attr_reader :clients
15
+
16
+ # The event handler that is duplicated for every client. This is to reduce
17
+ # the overhead of reinitializing the event handler for every client.
18
+ #
19
+ # @return [EventHandler]
20
+ attr_reader :event_handler
21
+
22
+ # Initialize the server.
23
+ #
24
+ # @return [void]
25
+ def initialize(options)
26
+ @options = options
27
+ @clients = Set.new
28
+ end
29
+
30
+ # Runs the server with the given options.
31
+ def run(&block)
32
+ @event_handler = EventHandler.new(&block)
33
+ EM::WebSocket.start(@options, &method(:handle_socket))
34
+ end
35
+
36
+ private
37
+
38
+ # Handles setting up the socket. This binds the [#onopen], [#onclose],
39
+ # [#onerror], and [#onmessage] events.
40
+ #
41
+ # @param [Object]
42
+ def handle_socket(socket)
43
+ client = Client.new :event_handler => @event_handler.dup,
44
+ :socket => socket, :context => nil
45
+ client.context = MiddleGround.new(client)
46
+ clients << client
47
+
48
+ socket.onopen do |handshake|
49
+ client.trigger 'socket.open', handshake
50
+ end
51
+
52
+ socket.onerror do |error|
53
+ client.trigger 'socket.error', error
54
+ end
55
+
56
+ socket.onclose do |message|
57
+ client.trigger 'socket.close', message
58
+ clients.delete client
59
+ end
60
+
61
+ socket.onmessage do |data|
62
+ json_data = Oj.load data, :mode => :null
63
+ client.trigger json_data["event"], json_data
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,5 @@
1
+ module Orator
2
+
3
+ # Version of Orator.
4
+ VERSION = '0.0.1'
5
+ end
data/lib/orator.rb ADDED
@@ -0,0 +1,21 @@
1
+ # A websocket server.
2
+
3
+ require 'oj'
4
+ require 'set'
5
+ require 'em-websocket'
6
+
7
+ require 'orator/server'
8
+ require 'orator/client'
9
+ require 'orator/version'
10
+ require 'orator/handlers'
11
+ require 'orator/event_handler'
12
+ require 'orator/middle_ground'
13
+
14
+ require 'orator/engine' if defined? Rails
15
+
16
+ module Orator
17
+
18
+ class << self; attr_accessor :debug; end
19
+ Orator.debug = false
20
+
21
+ end
@@ -0,0 +1,45 @@
1
+ describe Orator::Handlers::Base do
2
+ it "should call before blocks" do
3
+ something = double('something')
4
+ something.should_receive(:to_call)
5
+ klass = Class.new(described_class) do
6
+ self.name = 'test'
7
+ before { something.to_call }
8
+ on 'some_event'
9
+ def some_event; end
10
+ end
11
+
12
+ klass.new(nil).__trigger('test.some_event')
13
+ end
14
+
15
+ it "should raise error when it doesn't match the event" do
16
+ expect { described_class.new(nil).__trigger('other.thing') }.to raise_error(NoMethodError)
17
+ expect { described_class.new(nil).__trigger('.thing') }.to raise_error(NoMethodError)
18
+ end
19
+
20
+ it "should not run the event if the before callback prevents it" do
21
+ something = double('something')
22
+ something.should_not_receive(:to_call)
23
+ klass = Class.new(described_class) do
24
+ self.name = 'test'
25
+ before { prevent_event }
26
+ on('some_event') { something.to_call }
27
+ end
28
+
29
+ klass.new(nil).__trigger('test.some_event')
30
+ end
31
+
32
+ it "should register itself with an event handler" do
33
+ event_handler = double('event_handler')
34
+
35
+ klass = Class.new(described_class) do
36
+ self.name = 'test'
37
+
38
+ on('some_event') { }
39
+ end
40
+
41
+ event_handler.should_receive(:on).with("test.some_event", klass)
42
+
43
+ klass.register_with(event_handler)
44
+ end
45
+ end
@@ -0,0 +1,14 @@
1
+ describe Orator::Client do
2
+ it "should accept a hash" do
3
+ client = described_class.new :context => 42, :other_thing => 124
4
+
5
+ client.context.should be 42
6
+ end
7
+
8
+ it "should trigger event with context" do
9
+ client = described_class.new :context => 42,
10
+ :event_handler => double('event_handler')
11
+ client.event_handler.should_receive(:trigger).with('some.event', 42, 54)
12
+ client.trigger('some.event', 54)
13
+ end
14
+ end
@@ -0,0 +1,62 @@
1
+ describe Orator::EventHandler do
2
+ it "should initalize with block" do
3
+ expect {
4
+ EventHandler.new { raise StandardError }
5
+ }.to raise_error(StandardError)
6
+ end
7
+
8
+ it "should add an event with a block" do
9
+ subject.events.length.should be 0
10
+ subject.on(:some_event) { something }
11
+ subject.events.length.should be 1
12
+ subject.events.first[:event].should eq "some_event"
13
+ subject.events.first[:block].should be_kind_of Proc
14
+ end
15
+
16
+ class SomeClass < Orator::Handlers::Base; end
17
+
18
+ it "should accept a class for an event" do
19
+ subject.on(:some_event, SomeClass)
20
+ subject.events.first[:class].should be SomeClass
21
+ end
22
+
23
+ it "should accept a count" do
24
+ subject.on(:some_event, SomeClass, 4)
25
+ subject.events.first[:count].should be 4
26
+ end
27
+
28
+ it "should decrease the count by one" do
29
+ something = double("something")
30
+ something.should_receive(:to_call)
31
+ subject.on(:some_event, nil, 4) { something.to_call }
32
+ subject.events.first[:count].should be 4
33
+ subject.trigger(:some_event, self)
34
+ subject.events.first[:count].should be 3
35
+ end
36
+
37
+ it "shouldn't add the same event twice" do
38
+ subject.on(:some_event, SomeClass)
39
+ subject.events.length.should be 1
40
+ subject.on(:some_event, SomeClass)
41
+ subject.events.length.should be 1
42
+ end
43
+
44
+ it "should remove events with a count of 0" do
45
+ something = double("something")
46
+ something.should_receive(:to_call)
47
+ subject.on(:some_event, nil, 1) { something.to_call }
48
+ subject.on(:another_event) { }
49
+ subject.events.length.should be 2
50
+ subject.trigger(:some_event, self)
51
+ subject.events.length.should be 1
52
+ end
53
+
54
+ it "should select events with the same event" do
55
+ subject.on(:some_event) { }
56
+ subject.on(:another_event) { }
57
+ subject.on(:some_event) { do_something }
58
+
59
+ subject.events.length.should be 3
60
+ subject.events(:some_event).length.should be 2
61
+ end
62
+ end
@@ -0,0 +1,19 @@
1
+ describe Orator::MiddleGround do
2
+ #it "should build a message" do
3
+ # subje
4
+ #end
5
+
6
+ subject { described_class.new(double('client')) }
7
+
8
+ it "should build a message" do
9
+ subject.message('some.thing', :data => 'value').should eq(
10
+ "event" => "some.thing", "data" => "value")
11
+ end
12
+
13
+ it "should send a message to the client" do
14
+ socket = double('socket')
15
+ socket.should_receive(:send).with('{"event":"some.thing","data":"value"}')
16
+ subject.client.should_receive(:socket).and_return(socket)
17
+ subject.send("event" => "some.thing", "data" => "value")
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: orator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jeremy Rodi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-04-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: em-websocket
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.5.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.5.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: oj
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 2.0.10
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 2.0.10
41
+ - !ruby/object:Gem::Dependency
42
+ name: swf_fu
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 2.0.4
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 2.0.4
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 2.13.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 2.13.0
69
+ description: ! ' An implementation of an evented websocket system for rails.
70
+
71
+ '
72
+ email: redjazz96@gmail.com
73
+ executables:
74
+ - orator
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - lib/orator/middle_ground.rb
79
+ - lib/orator/version.rb
80
+ - lib/orator/handlers.rb
81
+ - lib/orator/server.rb
82
+ - lib/orator/cli.rb
83
+ - lib/orator/client.rb
84
+ - lib/orator/handlers/base.rb
85
+ - lib/orator/engine.rb
86
+ - lib/orator/event_handler.rb
87
+ - lib/orator.rb
88
+ - bin/orator
89
+ - spec/client_spec.rb
90
+ - spec/middle_ground_spec.rb
91
+ - spec/event_handler_spec.rb
92
+ - spec/base_handler_spec.rb
93
+ - README.md
94
+ - Rakefile
95
+ - LICENSE
96
+ homepage: http://github.com/redjazz96/orator
97
+ licenses: []
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ! '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ! '>='
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 2.0.2
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: Magic for your browser.
119
+ test_files: []
120
+ has_rdoc: false