simultaneous 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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