simultaneous 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ gem 'rack-async', :git => "git://github.com/matsadler/rack-async.git"
6
+ # gem 'thin'
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+ Copyright (c) 2011 Garry Hill <garry@magnetised.net>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
data/README ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,152 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'date'
4
+
5
+
6
+ #############################################################################
7
+ #
8
+ # Helper functions
9
+ #
10
+ #############################################################################
11
+
12
+ def name
13
+ @name ||= Dir['*.gemspec'].first.split('.').first
14
+ end
15
+
16
+ def version
17
+ line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
18
+ line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
19
+ end
20
+
21
+ def date
22
+ Date.today.to_s
23
+ end
24
+
25
+ def rubyforge_project
26
+ name
27
+ end
28
+
29
+ def gemspec_file
30
+ "#{name}.gemspec"
31
+ end
32
+
33
+ def gem_file
34
+ "#{name}-#{version}.gem"
35
+ end
36
+
37
+ def replace_header(head, header_name)
38
+ head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
39
+ end
40
+
41
+ #############################################################################
42
+ #
43
+ # Standard tasks
44
+ #
45
+ #############################################################################
46
+
47
+ task :default => :test
48
+
49
+ require 'rake/testtask'
50
+
51
+ Rake::TestTask.new(:test) do |test|
52
+ test.libs << 'lib' << 'test'
53
+ test.pattern = 'test/**/test_*.rb'
54
+ test.verbose = false
55
+ end
56
+
57
+ desc "Generate RCov test coverage and open in your browser"
58
+ task :coverage do
59
+ require 'rcov'
60
+ sh "rm -fr coverage"
61
+ sh "rcov test/test_*.rb"
62
+ sh "open coverage/index.html"
63
+ end
64
+
65
+ require 'rdoc/task'
66
+ Rake::RDocTask.new do |rdoc|
67
+ rdoc.rdoc_dir = 'rdoc'
68
+ rdoc.title = "#{name} #{version}"
69
+ rdoc.rdoc_files.include('README*')
70
+ rdoc.rdoc_files.include('lib/**/*.rb')
71
+ end
72
+
73
+ desc "Open an irb session preloaded with this library"
74
+ task :console do
75
+ sh "irb -rubygems -r ./lib/#{name}.rb"
76
+ end
77
+
78
+ #############################################################################
79
+ #
80
+ # Custom tasks (add your own tasks here)
81
+ #
82
+ #############################################################################
83
+
84
+
85
+
86
+ #############################################################################
87
+ #
88
+ # Packaging tasks
89
+ #
90
+ #############################################################################
91
+
92
+ desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
93
+ task :release => :build do
94
+ unless `git branch` =~ /^\* master$/
95
+ puts "You must be on the master branch to release!"
96
+ exit!
97
+ end
98
+ sh "git commit --allow-empty -a -m 'Release #{version}'"
99
+ sh "git tag v#{version}"
100
+ sh "git push origin master"
101
+ sh "git push origin v#{version}"
102
+ sh "gem push pkg/#{name}-#{version}.gem"
103
+ end
104
+
105
+ desc "Build #{gem_file} into the pkg directory"
106
+ task :build => :gemspec do
107
+ sh "mkdir -p pkg"
108
+ sh "gem build #{gemspec_file}"
109
+ sh "mv #{gem_file} pkg"
110
+ end
111
+
112
+ desc "Generate #{gemspec_file}"
113
+ task :gemspec => :validate do
114
+ # read spec file and split out manifest section
115
+ spec = File.read(gemspec_file)
116
+ head, manifest, tail = spec.split(" # = MANIFEST =\n")
117
+
118
+ # replace name version and date
119
+ replace_header(head, :name)
120
+ replace_header(head, :version)
121
+ replace_header(head, :date)
122
+ #comment this out if your rubyforge_project has a different name
123
+ replace_header(head, :rubyforge_project)
124
+
125
+ # determine file list from git ls-files
126
+ files = `git ls-files`.
127
+ split("\n").
128
+ sort.
129
+ reject { |file| file =~ /^\./ }.
130
+ reject { |file| file =~ /^(rdoc|pkg)/ }.
131
+ map { |file| " #{file}" }.
132
+ join("\n")
133
+
134
+ # piece file back together and write
135
+ manifest = " s.files = %w[\n#{files}\n ]\n"
136
+ spec = [head, manifest, tail].join(" # = MANIFEST =\n")
137
+ File.open(gemspec_file, 'w') { |io| io.write(spec) }
138
+ puts "Updated #{gemspec_file}"
139
+ end
140
+
141
+ desc "Validate #{gemspec_file}"
142
+ task :validate do
143
+ libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
144
+ unless libfiles.empty?
145
+ puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
146
+ exit!
147
+ end
148
+ unless Dir['VERSION*'].empty?
149
+ puts "A `VERSION` file at root level violates Gem best practices."
150
+ exit!
151
+ end
152
+ end
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ faf_path = File.expand_path('../../lib', __FILE__)
4
+ $:.unshift(faf_path) if File.directory?(faf_path) && !$:.include?(faf_path)
5
+
6
+ require 'eventmachine'
7
+ require 'simultaneous'
8
+
9
+ module Console
10
+ PROMPT = "\n>> ".freeze
11
+
12
+ def post_init
13
+ send_data PROMPT
14
+ send_data "\0"
15
+ end
16
+
17
+ def receive_data data
18
+ return close_connection if data =~ /exit|quit/
19
+
20
+ begin
21
+ @ret, @out, $stdout = :exception, $stdout, StringIO.new
22
+ @ret = eval(data, scope, '(rirb)')
23
+ rescue StandardError, ScriptError, Exception, SyntaxError
24
+ $! = RuntimeError.new("unknown exception raised") unless $!
25
+ print $!.class, ": ", $!, "\n"
26
+
27
+ trace = []
28
+ $!.backtrace.each do |line|
29
+ trace << "\tfrom #{line}"
30
+ break if line =~ /\(rirb\)/
31
+ end
32
+
33
+ puts trace
34
+ ensure
35
+ $stdout, @out = @out, $stdout
36
+ @out.rewind
37
+ @out = @out.read
38
+ end
39
+
40
+ send_data @out unless @out.empty?
41
+ send_data "=> #{@ret.inspect}" unless @ret == :exception
42
+ send_data "\0\n>> \0"
43
+ end
44
+
45
+ # def send_data data
46
+ # p ['server send', data]
47
+ # super
48
+ # end
49
+
50
+ def scope
51
+ @scope ||= instance_eval{ binding }
52
+ end
53
+
54
+ def handle_error
55
+ $! = RuntimeError.new("unknown exception raised") unless $!
56
+ print $!.class, ": ", $!, "\n"
57
+
58
+ trace = []
59
+ $!.backtrace.each do |line|
60
+ trace << "\\tfrom \#{line}"
61
+ break if line =~ /\(rirb\)/
62
+ end
63
+
64
+ puts trace
65
+ end
66
+
67
+ def self.start port = 7331
68
+ EM.run{
69
+ @server ||= EM.start_server '127.0.0.1', port, self
70
+ }
71
+ end
72
+
73
+ def self.stop
74
+ @server.close_connection if @server
75
+ @server = nil
76
+ end
77
+ end
78
+
79
+ module RIRB
80
+ def connection_completed
81
+ p 'connected to console'
82
+ end
83
+
84
+ def receive_data data
85
+ # p ['receive', data]
86
+ (@buffer ||= BufferedTokenizer.new("\0")).extract(data).each do |d|
87
+ process(d)
88
+ end
89
+ end
90
+
91
+ def process data
92
+ if data.strip == '>>'
93
+ while l = Readline.readline('>> ')
94
+ unless l.nil? or l.strip.empty?
95
+ Readline::HISTORY.push(l)
96
+ send_data l
97
+ break
98
+ end
99
+ end
100
+ else
101
+ puts data
102
+ end
103
+ end
104
+
105
+ def unbind
106
+ p 'disconnected'
107
+ EM.stop_event_loop
108
+ end
109
+
110
+ def self.connect host = 'localhost', port = 7331
111
+ require 'readline'
112
+ EM.run{
113
+ trap('INT'){ exit }
114
+ @connection.close_connection if @connection
115
+ @connection = EM.connect host, port, self do |c|
116
+ c.instance_eval{ @host, @port = host, port }
117
+ end
118
+ }
119
+ end
120
+ attr_reader :host, :port
121
+ end
122
+
123
+ EM.run{
124
+ Console.start
125
+ RIRB.connect
126
+ }
127
+
128
+ # if __FILE__ == $0
129
+ # EM.run{
130
+ # if ARGV[0] == 'server'
131
+ # Console.start
132
+ # elsif ARGV[0] == 'client'
133
+ # RIRB.connect
134
+ # else
135
+ # puts "#{$0} <server|client>"
136
+ # EM.stop
137
+ # end
138
+ # }
139
+ # end
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ faf_path = File.expand_path('../../lib', __FILE__)
4
+ $:.unshift(faf_path) if File.directory?(faf_path) && !$:.include?(faf_path)
5
+
6
+ require 'optparse'
7
+ require 'eventmachine'
8
+ require 'etc'
9
+ require 'fileutils'
10
+
11
+ require 'simultaneous'
12
+
13
+
14
+ options = {}
15
+ options[:gid] = Process.egid
16
+
17
+ connection = nil
18
+
19
+ socket = port = host = nil
20
+
21
+ OptionParser.new do |opts|
22
+ opts.on("-d", "--debug", "Turn on debugging") { $debug = true }
23
+ opts.on("-s", "--socket SOCKET", "Socket") { |v| socket = v }
24
+ opts.on("-c", "--connection CONNECTION", "Connection") { |v| connection = v }
25
+ opts.on("-p", "--port PORT", "Port") { |v| port = v }
26
+ opts.on("-h", "--host HOST", "Host") { |v| host = v }
27
+ opts.on("-g", "--group GROUPNAME", "Socket owning group") { |v|
28
+ options[:gid] = nil; options[:group_name] = v }
29
+ end.parse!
30
+
31
+
32
+ unless options[:gid]
33
+ if (group_name = options.delete(:group_name))
34
+ options[:gid] = Etc.getgrnam(group_name).gid
35
+ end
36
+ end
37
+
38
+ if connection.nil?
39
+ if host or port
40
+ host ||= Simultaneous::DEFAULT_HOST
41
+ port ||= Simultaneous::DEFAULT_PORT
42
+ connection = Simultaneous::Connection.tcp(host, port)
43
+ else
44
+ connection = (socket ||= Simultaneous::DEFAULT_CONNECTION)
45
+ raise Errno::EADDRINUSE if File.exists?(socket) and File.socket?(socket)
46
+ end
47
+ end
48
+
49
+ terminate = proc {
50
+ puts "Terminating..."
51
+ EM.stop
52
+ }
53
+
54
+ %w(TERM INT QUIT).each { |signal| trap(signal, terminate) }
55
+
56
+ puts "Simultaneous server PID:#{$$} listening on #{connection}"
57
+
58
+ EventMachine.run {
59
+ Simultaneous::Server.start(connection, options)
60
+ }
@@ -0,0 +1,63 @@
1
+ # encoding: UTF-8
2
+
3
+ module Simultaneous
4
+ class BroadcastMessage
5
+ attr_accessor :domain
6
+ attr_reader :event
7
+
8
+ def initialize(values = {})
9
+ @domain = (values[:domain] || values["domain"])
10
+ @event = nil
11
+ if (event = (values[:event] || values["event"])) and !event.empty?
12
+ self.event = event
13
+ end
14
+ self.data = (values[:data] || values["data"] || "")
15
+ end
16
+
17
+ def event=(event)
18
+ @event = event.to_s
19
+ end
20
+
21
+ def data=(data)
22
+ @data = data.split(/\r\n?|\n\r?/)
23
+ end
24
+
25
+ def data
26
+ @data.join("\n").chomp
27
+ end
28
+
29
+ def <<(line)
30
+ data = line.chomp
31
+ case data
32
+ when /^domain: *(.+)/
33
+ self.domain = $1
34
+ when /^event: *(.+)/
35
+ self.event = $1
36
+ when /^data: *(.*)/
37
+ @data << $1
38
+ when /^:/
39
+ # comment
40
+ else
41
+ # malformed request
42
+ end
43
+ end
44
+
45
+ def valid?
46
+ @domain && @event && !@data.empty?
47
+ end
48
+
49
+ def to_event
50
+ lines = ["domain: #{domain}", "event: #{event}"]
51
+ lines.concat(@data.map { |l| "data: #{l}" })
52
+ lines.push("\n")
53
+ lines.join("\n")
54
+ end
55
+
56
+ def to_sse
57
+ lines = ["event: #{event}"]
58
+ lines.concat(@data.map { |l| "data: #{l}" })
59
+ lines.push("\n")
60
+ lines.join("\n")
61
+ end
62
+ end
63
+ end