orator 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.
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