spectate 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,23 @@
1
+ require 'cgi'
2
+
3
+ module Spectate
4
+ # A convenience module to provide URI-safe versions of spectator names. It essentially
5
+ # just uses URI::Escape on the strings, but with the additional conversion of spaces to
6
+ # underscores (and underscores to double underscores) because having lots of '%20' blobs
7
+ # in a URL is painful on the eyes.
8
+ module Encode
9
+ # Makes a name URI-safe, with the minor cosmetic tweak of underscoring spaces.
10
+ def encode(name)
11
+ name.gsub!(/_/,'__')
12
+ name.tr!(' ','_')
13
+ CGI::escape(name)
14
+ end
15
+
16
+ # Turns a URI-safe name generated by 'encode' back into a reasonable string.
17
+ def decode(name)
18
+ name.gsub!(/_(?!_)/,' ')
19
+ name.gsub!(/_ /,'_')
20
+ CGI::unescape(name)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,8 @@
1
+ module Spectate
2
+ module Ping
3
+ def ping
4
+ c = Spectate::Client.new
5
+ c.get
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'sinatra/base'
3
+ require 'rufus-tokyo'
4
+ require 'json'
5
+
6
+ module Spectate
7
+ class Server < Sinatra::Base
8
+
9
+ def initialize
10
+ Spectate::Config.load_configuration
11
+ Rufus::Tokyo::Cabinet.open(File.join(Spectate::Config.basedir, 'spectate.tcb'), :opts => 'b') do |db|
12
+ nil
13
+ end
14
+ super
15
+ end
16
+
17
+ get '/' do
18
+ {:summary => "Spectate v#{VERSION}"}.to_json
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,43 @@
1
+ module Spectate
2
+ class Status
3
+ include Encode
4
+ attr_reader :server, :source
5
+ def initialize(server, source, properties = {})
6
+ @server, @source = server, source
7
+ @status = properties.inject({}) do |status, (key, value)| # Shamelessly lifted from ActiveSupport
8
+ status[(key.to_sym rescue key) || key] = value
9
+ status
10
+ end
11
+ end
12
+
13
+ # An array of all the properties contained in this Status object.
14
+ def properties
15
+ @status.keys
16
+ end
17
+
18
+ # Returns the path of the spectator that owns this source. In practical
19
+ # terms, this simply truncates to the next-to-last element on the path; so
20
+ # if the source is '/examples/foo/re_bar' the parent would be
21
+ # '/examples/foo'.
22
+ def parent
23
+ @source.slice %r{^/.*(?=/)}
24
+ end
25
+
26
+ # The English representation of the name of the spectator belonging to this source.
27
+ def name
28
+ decode @source.slice(%r{/[^/]+$}).delete('/') # Ruby 1.8 doesn't support lookbehind
29
+ end
30
+
31
+ def [](key)
32
+ @status[key]
33
+ end
34
+
35
+ def method_missing(method, *args, &block)
36
+ if @status.has_key?(method)
37
+ @status[method]
38
+ else
39
+ super
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,150 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'spectate/client'
3
+
4
+ describe Spectate::Client do
5
+ include Spectate::Spec::ServerHelpers
6
+
7
+ before(:all) do
8
+ start_server
9
+ end
10
+ describe "configuration" do
11
+ it "loads configuration if it has to" do
12
+ Spectate::Config.clear!
13
+ Spectate::Config.expects(:load_configuration).returns(true)
14
+ @this = Spectate::Client.new
15
+ end
16
+
17
+ it "doesn't reload the configuration if it doesn't have to" do
18
+ Spectate::Config.expects(:load_configuration).never
19
+ Spectate::Config.expects(:loaded?).returns(true)
20
+ @this = Spectate::Client.new
21
+ end
22
+
23
+ it "knows its host" do
24
+ @this = Spectate::Client.new
25
+ @this.host.should == 'localhost'
26
+ end
27
+
28
+ it "can override its host on creation" do
29
+ @this = Spectate::Client.new(:host => 'example.com')
30
+ @this.host.should == 'example.com'
31
+ end
32
+
33
+ it "can override its host at any time" do
34
+ @this = Spectate::Client.new
35
+ @this.host = 'example.org'
36
+ @this.host.should == 'example.org'
37
+ end
38
+
39
+ it "knows its port" do
40
+ @this = Spectate::Client.new
41
+ @this.port.should == 47502
42
+ end
43
+
44
+ it "can override its port on creation" do
45
+ @this = Spectate::Client.new(:port => 8888)
46
+ @this.port.should == 8888
47
+ end
48
+
49
+ it "can override its port at any time" do
50
+ @this = Spectate::Client.new
51
+ @this.port = 9999
52
+ @this.port.should == 9999
53
+ end
54
+
55
+ it "knows its protocol" do
56
+ @this = Spectate::Client.new
57
+ @this.protocol.should == 'http'
58
+ end
59
+
60
+ it "can override its protocol on creation" do
61
+ @this = Spectate::Client.new(:protocol => 'https')
62
+ @this.protocol.should == 'https'
63
+ end
64
+
65
+ it "can override its protocol at any time" do
66
+ @this = Spectate::Client.new
67
+ @this.protocol = 'ftp'
68
+ @this.protocol.should == 'ftp'
69
+ end
70
+
71
+ it "defaults to accepting JSON" do
72
+ @this = Spectate::Client.new
73
+ @this.accept.should == 'application/json'
74
+ end
75
+
76
+ it "can override its accept on creation" do
77
+ @this = Spectate::Client.new(:accept => 'text/html')
78
+ @this.accept.should == 'text/html'
79
+ end
80
+
81
+ it "can override its accept at any time" do
82
+ @this = Spectate::Client.new
83
+ @this.accept = 'application/xml'
84
+ @this.accept.should == 'application/xml'
85
+ end
86
+
87
+ it "knows its root with a port" do
88
+ @this = Spectate::Client.new(:protocol => 'https', :host => 'example.org', :port => 777)
89
+ @this.root.should == 'https://example.org:777'
90
+ end
91
+
92
+ it "knows its root without a port or protocol" do
93
+ @this = Spectate::Client.new(:host => '10.0.1.5', :port => 0)
94
+ @this.root.should == 'http://10.0.1.5'
95
+ end
96
+
97
+ end
98
+
99
+ shared_examples_for "a Rest-Client proxy method" do
100
+ STATUS_JSON = '{"uptime":"23h 14m 11s","summary":"Spectate v0.0.0.0"}'
101
+ before(:each) do
102
+ method = @method
103
+ @dummy = stub('Rest Client') do
104
+ stubs(method).returns(STATUS_JSON)
105
+ stubs(:[]).returns(@dummy)
106
+ end
107
+ @this.instance_variable_set(:@driver, @dummy)
108
+ end
109
+ it "calls the method in the RestClient resource" do
110
+ @dummy.expects(@method).returns(STATUS_JSON)
111
+ @this.send(@method).should_not be_nil
112
+ end
113
+ it "parses the result when accepting JSON" do
114
+ @this.send(@method).should be_a_kind_of(Spectate::Status)
115
+ end
116
+ it "includes the appropriate fields" do
117
+ s = @this.send(@method)
118
+ s[:summary].should =~ /Spectate/
119
+ end
120
+ it "returns nil on a Resource Not Found error" do
121
+ @dummy.expects(@method).raises(RestClient::ResourceNotFound)
122
+ @this.send(@method).should be_nil
123
+ end
124
+ it "returns nil on a Connection Refused error" do
125
+ @dummy.expects(@method).raises(Errno::ECONNREFUSED)
126
+ @this.send(@method).should be_nil
127
+ end
128
+ it "raises any other exception" do
129
+ @dummy.expects(@method).raises(RestClient::RequestFailed)
130
+ lambda{@this.send(@method)}.should raise_error(RestClient::RequestFailed)
131
+ end
132
+ end
133
+
134
+ describe 'calling method' do
135
+ before(:each) do
136
+ @this = Spectate::Client.new
137
+ end
138
+
139
+ describe "get" do
140
+ before(:each) do
141
+ @method = :get
142
+ end
143
+ it_should_behave_like "a Rest-Client proxy method"
144
+ end
145
+ end
146
+
147
+ after(:all) do
148
+ stop_server
149
+ end
150
+ end
@@ -0,0 +1,86 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Command 'spectate'" do
4
+ include Spectate::Spec::ConfigHelpers
5
+
6
+ before(:all) do
7
+ create_config
8
+ end
9
+
10
+ it "gets its base directory from the SPECTATE_DIR environment variable if specified" do
11
+ unmade_dir = File.expand_path("~/blahhhhh")
12
+ ENV['SPECTATE_DIR'] = unmade_dir
13
+ `spectate`.should =~ /Directory #{Regexp.escape(unmade_dir)} not found!/
14
+ ENV['SPECTATE_DIR'] = @tempdir
15
+ end
16
+
17
+ it "executes" do
18
+ `spectate --help`.should =~ /Spectate/i
19
+ end
20
+
21
+ # We have several options for displaying help
22
+ %w{-h -? --help --usage}.each do |option|
23
+ it "displays its usage when called with #{option}" do
24
+ `spectate #{option}`.should =~ /Usage:/
25
+ end
26
+
27
+ it "does not attempt to start the server if called with #{option}" do
28
+ `spectate #{option}`.should_not =~ /Starting Spectate/
29
+ end
30
+
31
+ end
32
+
33
+ it "displays its usage when given parameters it doesn't understand" do
34
+ `spectate --blah1234`.should =~ /invalid option: --blah1234.*Usage:/m
35
+ end
36
+
37
+
38
+ %w{-d --directory}.each do |option|
39
+ it "requires a directory parameter" do
40
+ `spectate #{option}`.should =~ /missing argument: #{option}.*Usage:/m
41
+ end
42
+
43
+ it "sets a different base directory when called with #{option} dirname" do
44
+ tmpdir = Dir.mktmpdir
45
+ `spectate #{option} #{tmpdir}`.should =~ /Directory set to #{Regexp.escape(tmpdir)}/
46
+ # Ensure cleanup
47
+ FileUtils.rm_r tmpdir, :force => true, :secure => true
48
+ end
49
+ end
50
+
51
+ describe "starting" do
52
+ it "attempts to start Spectate if not already running" do
53
+ `spectate`.should =~ /Starting Spectate/
54
+ end
55
+ it "complains if Spectate is already running" do
56
+ `spectate`
57
+ `spectate`.should =~ /already running/
58
+ end
59
+
60
+ it "creates a PID file in the base directory" do
61
+ `spectate`
62
+ File.exists?(File.join(@tempdir,'spectate.pid')).should be_true
63
+ end
64
+ after(:each) do
65
+ `spectate --stop`
66
+ end
67
+ end
68
+
69
+ describe "--stop" do
70
+ before(:each) do
71
+ `spectate`
72
+ end
73
+ it "stops the server" do
74
+ `spectate --stop`.should =~ /stopped./
75
+ end
76
+ it "complains if the server wasn't running" do
77
+ `spectate --stop`
78
+ `spectate --stop`.should =~ /wasn't running!/
79
+ end
80
+ end
81
+
82
+
83
+ after(:all) do
84
+ remove_config
85
+ end
86
+ end
@@ -0,0 +1,247 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Configuration" do
4
+ describe "version" do
5
+ it "is a proper major.minor.build number" do
6
+ Spectate::VERSION.should =~ /\d+\.\d+\.\d+/
7
+ end
8
+ end
9
+
10
+ describe "variables" do
11
+ it "can be set" do
12
+ Spectate::Config['foo'] = 'bar'
13
+ Spectate::Config['foo'].should == 'bar'
14
+ Spectate::Config.delete('foo')
15
+ end
16
+
17
+ it "can be displayed as a hash" do
18
+ Spectate::Config['bar'] = 'foo'
19
+ hash = Spectate::Config.to_hash
20
+ hash['bar'].should == 'foo'
21
+ Spectate::Config.delete('bar')
22
+ end
23
+
24
+ it "can be deleted" do
25
+ Spectate::Config['foo'] = 'bar'
26
+ Spectate::Config.delete('foo')
27
+ Spectate::Config.to_hash.should_not have_key('foo')
28
+ end
29
+
30
+ it "can be cleared" do
31
+ Spectate::Config['something'] = 'something else'
32
+ Spectate::Config.clear!
33
+ Spectate::Config.to_hash.size.should == 0
34
+ end
35
+ end
36
+
37
+ describe "defaults" do
38
+ before(:each) do
39
+ @defhash = {'aloha' => 'hello'}
40
+ end
41
+
42
+ it "will merge in if they don't exist" do
43
+ Spectate::Config.default(@defhash)
44
+ Spectate::Config['aloha'].should == 'hello'
45
+ end
46
+
47
+ it "will not merge in if they do exist" do
48
+ Spectate::Config['aloha'] = "goodbye"
49
+ Spectate::Config.default(@defhash)
50
+ Spectate::Config['aloha'].should == 'goodbye'
51
+ end
52
+
53
+ after(:each) do
54
+ Spectate::Config.delete('aloha')
55
+ end
56
+ end
57
+
58
+ describe "file setup" do
59
+ include Spectate::Spec::ConfigHelpers
60
+
61
+ # We don't want to mess up anyone's actual configuration
62
+ before(:all) do
63
+ set_tempdir # Sets @tempparent, @tempdir, @configfile
64
+ end
65
+
66
+ describe "without a .spectate directory" do
67
+ before(:each) do
68
+ FileUtils.rm_r @tempdir, :force => true, :secure => true
69
+ end
70
+
71
+ it "tells the user to run spectate --setup" do
72
+ `spectate -d #{@tempdir}`.should =~ /Directory #{Regexp.escape(@tempdir)} not found!\nRun spectate --setup to initialize things\./m
73
+ end
74
+ end
75
+
76
+ describe "without a config.yml file" do
77
+ before(:each) do
78
+ FileUtils.mkdir @tempdir
79
+ File.delete(@configfile) if File.exists?(@configfile)
80
+ end
81
+
82
+ it "tells the user to run spectate --setup" do
83
+ `spectate -d #{@tempdir}`.should =~ /File #{Regexp.escape(@tempdir)}\/config\.yml not found!\nRun spectate --setup to initialize things\./m
84
+ end
85
+ end
86
+
87
+
88
+ shared_examples_for "a valid setup option" do
89
+ it "complains if config.yml already exists" do
90
+ FileUtils.mkdir @tempdir
91
+ FileUtils.touch @configfile
92
+ `spectate -d #{@tempdir} --setup #{@option}`.should =~ /You already have a config\.yml file! We don't want to mess with a good thing\.\nIf you really want to start over, delete #{Regexp.escape(@tempdir)}\/config\.yml and run spectate --setup again\./m
93
+
94
+ end
95
+
96
+ it "tells the user a config.yml file is being made" do
97
+ `spectate -d #{@tempdir} --setup #{@option}`.should =~ /Creating config.yml file/
98
+ end
99
+
100
+ it "creates the file" do
101
+ `spectate -d #{@tempdir} --setup #{@option}`
102
+ File.exists?(@configfile).should be_true
103
+ end
104
+
105
+ it "writes the server value to the file" do
106
+ `spectate -d #{@tempdir} --setup #{@option}`
107
+ File.read(@configfile).should =~ /server: #{@option}/
108
+ end
109
+
110
+ it "writes the host if given" do
111
+ `spectate -d #{@tempdir} -h foo.bar.local --setup #{@option}`
112
+ File.read(@configfile).should =~ /host: foo\.bar\.local/
113
+ end
114
+
115
+ it "writes the port if given" do
116
+ `spectate -d #{@tempdir} -p 55555 --setup #{@option}`
117
+ File.read(@configfile).should =~ /port: 55555/
118
+ end
119
+
120
+ it "creates a rackup file" do
121
+ `spectate -d #{@tempdir} --setup #{@option}`
122
+ File.read(File.join(@tempdir,'config.ru')).should =~ /run Spectate::Server/
123
+ end
124
+ end
125
+
126
+ describe "with spectate --setup" do
127
+
128
+ describe "(no parameters)" do
129
+ it "tells the user to specify a parameter" do
130
+ `spectate -d #{@tempdir} --setup`.should =~ /missing argument/
131
+ end
132
+ end
133
+
134
+ describe "passenger" do
135
+ before(:each) do
136
+ @option = "passenger"
137
+ end
138
+ it_should_behave_like "a valid setup option"
139
+
140
+ it "sets rackup to false" do
141
+ `spectate -d #{@tempdir} --setup #{@option}`
142
+ File.read(@configfile).should =~ /rackup: false/
143
+ end
144
+
145
+ it "doesn't set a port" do
146
+ `spectate -d #{@tempdir} --setup #{@option}`
147
+ File.read(@configfile).should_not =~ /port/
148
+ end
149
+
150
+ it "defaults the host to spectate.local" do
151
+ `spectate -d #{@tempdir} --setup #{@option}`
152
+ File.read(@configfile).should =~ /host: spectate\.local/
153
+ end
154
+ end
155
+
156
+ %w[thin webrick mongrel].each do |server|
157
+ describe "#{server}" do
158
+ before(:each) do
159
+ @option = "#{server}"
160
+ end
161
+
162
+ it_should_behave_like "a valid setup option"
163
+
164
+ it "sets rackup to true" do
165
+ `spectate -d #{@tempdir} --setup #{@option}`
166
+ File.read(@configfile).should =~ /rackup: true/
167
+ end
168
+
169
+ it "sets the port to the default of 20574" do
170
+ `spectate -d #{@tempdir} --setup #{@option}`
171
+ File.read(@configfile).should =~ /port: 20574/
172
+ end
173
+
174
+ it "defaults the host to localhost" do
175
+ `spectate -d #{@tempdir} --setup #{@option}`
176
+ File.read(@configfile).should =~ /host: localhost/
177
+ end
178
+ end
179
+ end
180
+ after(:each) do
181
+ FileUtils.rm_r @tempdir, :force => true, :secure => true
182
+ end
183
+ end
184
+
185
+
186
+ after(:all) do
187
+ FileUtils.rm_r @tempparent, :force => true, :secure => true
188
+ end
189
+ end
190
+
191
+ describe "loading" do
192
+ include Spectate::Spec::ConfigHelpers
193
+ before(:each) do
194
+ Spectate::Config.clear!
195
+ create_config
196
+ ENV["SPECTATE_DIR"] = nil
197
+ end
198
+
199
+ it "gets its values from the environment variable if set" do
200
+ ENV["SPECTATE_DIR"] = @tempdir
201
+ Spectate::Config.load_configuration
202
+ Spectate::Config['port'].should == 47502
203
+ end
204
+
205
+ it "gets its values from the method parameter if given" do
206
+ Spectate::Config.load_configuration(@tempdir)
207
+ Spectate::Config['port'].should == 47502
208
+ end
209
+
210
+ it "gets its values from Spectate::Config['basedir'] if given" do
211
+ Spectate::Config['basedir'] = @tempdir
212
+ Spectate::Config.load_configuration
213
+ Spectate::Config['port'].should == 47502
214
+ end
215
+
216
+ it "gets its values from ROOT_DIR as a last resort" do
217
+ Spectate.send(:remove_const, :ROOT_DIR) # To suppress warnings
218
+ Spectate.const_set(:ROOT_DIR,@tempdir)
219
+ Spectate::Config.load_configuration
220
+ Spectate::Config['port'].should == 47502
221
+ Spectate.send(:remove_const, :ROOT_DIR) # To suppress warnings
222
+ Spectate.const_set(:ROOT_DIR,nil)
223
+ end
224
+
225
+ it "complains if no base directory can be found" do
226
+ lambda{Spectate::Config.load_configuration}.should raise_error(RuntimeError, /could not find/i)
227
+ end
228
+
229
+ it "knows when it hasn't been loaded yet" do
230
+ Spectate::Config.loaded?.should be_false
231
+ end
232
+
233
+ it "knows when it has been loaded" do
234
+ Spectate::Config.load_configuration(@tempdir)
235
+ Spectate::Config.loaded?.should be_true
236
+ end
237
+
238
+ it "knows its basedir" do
239
+ Spectate::Config.load_configuration(@tempdir)
240
+ Spectate::Config.basedir.should == @tempdir
241
+ end
242
+
243
+ after(:each) do
244
+ remove_config
245
+ end
246
+ end
247
+ end