lackie 0.1.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 +7 -0
- data/Gemfile.lock +57 -0
- data/README.rdoc +65 -0
- data/Rakefile +13 -0
- data/features/remote_control.feature +19 -0
- data/features/step_definitions/lackie_steps.rb +23 -0
- data/features/support/config.ru +7 -0
- data/features/support/env.rb +54 -0
- data/features/support/example_app/app.html +13 -0
- data/features/support/example_app.rb +9 -0
- data/lackie.gemspec +27 -0
- data/lib/lackie/javascript/json2.js +483 -0
- data/lib/lackie/javascript/surrender.js +58 -0
- data/lib/lackie/javascript/surrender.rb +15 -0
- data/lib/lackie/javascript.rb +1 -0
- data/lib/lackie/poller.rb +22 -0
- data/lib/lackie/rack/middleware.rb +85 -0
- data/lib/lackie/rack.rb +2 -0
- data/lib/lackie/remote_control.rb +51 -0
- data/lib/lackie.rb +8 -0
- data/spec/lackie/javascript/surrender_spec.rb +16 -0
- data/spec/lackie/poller_spec.rb +34 -0
- data/spec/lackie/rack/middleware_spec.rb +77 -0
- data/spec/lackie/remote_control_spec.rb +46 -0
- data/spec/spec_helper.rb +6 -0
- metadata +197 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'restclient'
|
2
|
+
require 'json'
|
3
|
+
require 'lackie/poller'
|
4
|
+
|
5
|
+
module Lackie
|
6
|
+
class RemoteControl
|
7
|
+
def initialize(host, port, poller=Poller.new)
|
8
|
+
@host, @port, @poller = host, port, poller
|
9
|
+
end
|
10
|
+
|
11
|
+
def log(message)
|
12
|
+
exec("Lackie.log(#{message.to_json})")
|
13
|
+
end
|
14
|
+
|
15
|
+
def exec(command)
|
16
|
+
send_command(command)
|
17
|
+
poll_for_result(command)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def send_command(command)
|
23
|
+
RestClient.post("http://#{@host}:#{@port}/lackie/eval", command)
|
24
|
+
end
|
25
|
+
|
26
|
+
def poll_for_result(command)
|
27
|
+
body = nil
|
28
|
+
@poller.await("result of command:\n#{command}") do
|
29
|
+
body = retrieve_result_body
|
30
|
+
end
|
31
|
+
parse_result(body)
|
32
|
+
end
|
33
|
+
|
34
|
+
def retrieve_result_body
|
35
|
+
begin
|
36
|
+
RestClient.get("http://#{@host}:#{@port}/lackie/result").body
|
37
|
+
rescue RestClient::ResourceNotFound
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse_result(body)
|
43
|
+
parsed_body = JSON.parse(body)
|
44
|
+
raise RemoteExecutionError.new(parsed_body["error"]) if parsed_body.include?("error")
|
45
|
+
return parsed_body["value"]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class RemoteExecutionError < RuntimeError
|
50
|
+
end
|
51
|
+
end
|
data/lib/lackie.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'lackie/javascript'
|
3
|
+
|
4
|
+
module Lackie
|
5
|
+
module JavaScript
|
6
|
+
describe Surrender do
|
7
|
+
it "produces a javascript to zombify a remote browser" do
|
8
|
+
Surrender.new.script.should =~ /window\.setInterval/
|
9
|
+
end
|
10
|
+
|
11
|
+
it "includes json2.js" do
|
12
|
+
Surrender.new.script.should include("http://www.JSON.org/json2.js")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'lackie/poller'
|
3
|
+
|
4
|
+
module Lackie
|
5
|
+
describe Poller do
|
6
|
+
it "sleeps for a sepecified number of seconds between yields, until the block returns true" do
|
7
|
+
Kernel.should_receive(:sleep).with(0.666).exactly(:twice)
|
8
|
+
return_values = [false, false, true]
|
9
|
+
Poller.new(:interval_seconds => 0.666).await("foo") do
|
10
|
+
return_values.shift
|
11
|
+
end
|
12
|
+
return_values.should be_empty
|
13
|
+
end
|
14
|
+
|
15
|
+
it "gives up after a specified number of seconds" do
|
16
|
+
Kernel.stub!(:sleep)
|
17
|
+
poller = Poller.new(:timeout_seconds => 2.5)
|
18
|
+
lambda {
|
19
|
+
poller.await("foo") do
|
20
|
+
false
|
21
|
+
end
|
22
|
+
}.should raise_error("Timed out after 2.5 seconds awaiting foo")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "uses a different sleeper when Kernel is not convenient" do
|
26
|
+
sleeper = mock("sleeper")
|
27
|
+
sleeper.should_receive(:sleep).with(0.999).exactly(:twice)
|
28
|
+
return_values = [false, false, true]
|
29
|
+
Poller.new(:interval_seconds => 0.999, :sleeper => sleeper).await("foo") do
|
30
|
+
return_values.shift
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'lackie/rack'
|
3
|
+
|
4
|
+
module Lackie
|
5
|
+
module Rack
|
6
|
+
describe Middleware do
|
7
|
+
before do
|
8
|
+
Lackie::JavaScript::Surrender.should_receive(:new).and_return(mock("surrender", :script => "yowzer"))
|
9
|
+
@app = mock("app")
|
10
|
+
@env = mock("env")
|
11
|
+
@middleware = Middleware.new(@app)
|
12
|
+
end
|
13
|
+
|
14
|
+
def request(options)
|
15
|
+
@request = mock("Request: #{options.inspect}", options)
|
16
|
+
::Rack::Request.stub!(:new).with(@env).and_return(@request)
|
17
|
+
@middleware.call(@env)
|
18
|
+
end
|
19
|
+
|
20
|
+
def get(path)
|
21
|
+
request(:path => path, :get? => true)
|
22
|
+
end
|
23
|
+
|
24
|
+
def post(path, body)
|
25
|
+
request(:path => path, :get? => false, :body => mock("io", :read => body))
|
26
|
+
end
|
27
|
+
|
28
|
+
it "ignores GET requests with paths not beginning with /lackie/" do
|
29
|
+
@app.should_receive(:call).with(@env).and_return(:result)
|
30
|
+
get("else/where").should == :result
|
31
|
+
end
|
32
|
+
|
33
|
+
it "ignores POST requests with paths not beginning with /lackie/" do
|
34
|
+
@app.should_receive(:call).with(@env).and_return(:result)
|
35
|
+
post("over/yonder", "body").should == :result
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "GET /lackie/surrender" do
|
39
|
+
it "returns a javascript to zombify a remote browser" do
|
40
|
+
get("/lackie/surrender").should == [200, {'Content-Type' => 'text/javascript'}, ["yowzer"]]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "POST /lackie/eval with the body 'foo()'" do
|
45
|
+
describe "followed by GET /lackie/yield" do
|
46
|
+
it "returns 'foo()' as the response body" do
|
47
|
+
post("/lackie/eval", "foo()")
|
48
|
+
get("/lackie/yield").should == [200, {"Content-Type"=>"text/javascript"}, ["foo()"]]
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "followed by GET /lackie/yield" do
|
52
|
+
it "returns '' as the response body" do
|
53
|
+
post("/lackie/eval", "foo()")
|
54
|
+
get("/lackie/yield")
|
55
|
+
get("/lackie/yield").should == [200, {"Content-Type"=>"text/javascript"}, [""]]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "GET /lackie/result (before POST /lackie/result)" do
|
62
|
+
it "returns a 404 Not Found" do
|
63
|
+
get("/lackie/result").should == [404, {'Content-Type' => 'text/plain'}, ["Not Found"]]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "POST /lackie/result with 'yippee'" do
|
68
|
+
describe "followed by GET /lackie/result" do
|
69
|
+
it "gets 'bar' as the response body" do
|
70
|
+
post("/lackie/result", "'bar'")
|
71
|
+
get("/lackie/result").should == [200, {"Content-Type"=>"application/json"}, ["'bar'"]]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'lackie/javascript'
|
3
|
+
|
4
|
+
module Lackie
|
5
|
+
describe RemoteControl do
|
6
|
+
before(:each) do
|
7
|
+
@poller = Poller.new(:sleeper => mock("sleeper", :sleep => true))
|
8
|
+
Poller.stub!(:new).and_return(@poller)
|
9
|
+
@rc = RemoteControl.new("host", 555, @poller)
|
10
|
+
RestClient.stub!(:post)
|
11
|
+
RestClient.stub!(:get).and_return(mock("response", :body => '{"value":"123"}'))
|
12
|
+
end
|
13
|
+
|
14
|
+
it "sends log commands as POST /lackie/eval" do
|
15
|
+
RestClient.should_receive(:post).with("http://host:555/lackie/eval", 'Lackie.log("OK")')
|
16
|
+
@rc.log("OK")
|
17
|
+
end
|
18
|
+
|
19
|
+
it "sends execute 'X' commands as POST /lackie/eval with the body 'X'" do
|
20
|
+
RestClient.should_receive(:post).with("http://host:555/lackie/eval", 'X')
|
21
|
+
@rc.exec("X")
|
22
|
+
end
|
23
|
+
|
24
|
+
it "polls for results via GET /lackie/result" do
|
25
|
+
responses = [
|
26
|
+
lambda { raise RestClient::ResourceNotFound },
|
27
|
+
lambda { mock("poll_response", :body => '{"value":"bar"}') }
|
28
|
+
]
|
29
|
+
RestClient.stub!(:get).with("http://host:555/lackie/result").and_return {
|
30
|
+
responses.shift.call
|
31
|
+
}
|
32
|
+
@rc.exec("foo").should == "bar"
|
33
|
+
responses.should be_empty
|
34
|
+
end
|
35
|
+
|
36
|
+
it "deserializes json results" do
|
37
|
+
RestClient.should_receive(:get).and_return(mock("response", :body => '{"value":666}'))
|
38
|
+
@rc.log("oops").should == 666
|
39
|
+
end
|
40
|
+
|
41
|
+
it "rethrows json errors" do
|
42
|
+
RestClient.should_receive(:get).and_return(mock("response", :body => '{"error":"oops"}'))
|
43
|
+
lambda { @rc.log("oops") }.should raise_error("oops")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lackie
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Josh Chisholm
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-12-31 00:00:00 +00:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rack
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 29
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 2
|
33
|
+
- 1
|
34
|
+
version: 1.2.1
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rest-client
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 3
|
46
|
+
segments:
|
47
|
+
- 1
|
48
|
+
- 4
|
49
|
+
- 2
|
50
|
+
version: 1.4.2
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: rspec
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 7
|
62
|
+
segments:
|
63
|
+
- 2
|
64
|
+
- 2
|
65
|
+
- 0
|
66
|
+
version: 2.2.0
|
67
|
+
type: :development
|
68
|
+
version_requirements: *id003
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: cucumber
|
71
|
+
prerelease: false
|
72
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
hash: 55
|
78
|
+
segments:
|
79
|
+
- 0
|
80
|
+
- 10
|
81
|
+
- 0
|
82
|
+
version: 0.10.0
|
83
|
+
type: :development
|
84
|
+
version_requirements: *id004
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: mongrel
|
87
|
+
prerelease: false
|
88
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
hash: 25
|
94
|
+
segments:
|
95
|
+
- 1
|
96
|
+
- 1
|
97
|
+
- 5
|
98
|
+
version: 1.1.5
|
99
|
+
type: :development
|
100
|
+
version_requirements: *id005
|
101
|
+
- !ruby/object:Gem::Dependency
|
102
|
+
name: relevance-rcov
|
103
|
+
prerelease: false
|
104
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
hash: 13
|
110
|
+
segments:
|
111
|
+
- 0
|
112
|
+
- 9
|
113
|
+
- 2
|
114
|
+
- 1
|
115
|
+
version: 0.9.2.1
|
116
|
+
type: :development
|
117
|
+
version_requirements: *id006
|
118
|
+
description: Automates remote applications using an HTTP middleman
|
119
|
+
email: joshuachisholm@gmail.com
|
120
|
+
executables: []
|
121
|
+
|
122
|
+
extensions: []
|
123
|
+
|
124
|
+
extra_rdoc_files:
|
125
|
+
- README.rdoc
|
126
|
+
files:
|
127
|
+
- Gemfile
|
128
|
+
- Gemfile.lock
|
129
|
+
- README.rdoc
|
130
|
+
- Rakefile
|
131
|
+
- features/remote_control.feature
|
132
|
+
- features/step_definitions/lackie_steps.rb
|
133
|
+
- features/support/config.ru
|
134
|
+
- features/support/env.rb
|
135
|
+
- features/support/example_app.rb
|
136
|
+
- features/support/example_app/app.html
|
137
|
+
- lackie.gemspec
|
138
|
+
- lib/lackie.rb
|
139
|
+
- lib/lackie/javascript.rb
|
140
|
+
- lib/lackie/javascript/json2.js
|
141
|
+
- lib/lackie/javascript/surrender.js
|
142
|
+
- lib/lackie/javascript/surrender.rb
|
143
|
+
- lib/lackie/poller.rb
|
144
|
+
- lib/lackie/rack.rb
|
145
|
+
- lib/lackie/rack/middleware.rb
|
146
|
+
- lib/lackie/remote_control.rb
|
147
|
+
- spec/lackie/javascript/surrender_spec.rb
|
148
|
+
- spec/lackie/poller_spec.rb
|
149
|
+
- spec/lackie/rack/middleware_spec.rb
|
150
|
+
- spec/lackie/remote_control_spec.rb
|
151
|
+
- spec/spec_helper.rb
|
152
|
+
has_rdoc: true
|
153
|
+
homepage: http://github.com/joshski/lackie
|
154
|
+
licenses: []
|
155
|
+
|
156
|
+
post_install_message:
|
157
|
+
rdoc_options: []
|
158
|
+
|
159
|
+
require_paths:
|
160
|
+
- lib
|
161
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
162
|
+
none: false
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
hash: 3
|
167
|
+
segments:
|
168
|
+
- 0
|
169
|
+
version: "0"
|
170
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
171
|
+
none: false
|
172
|
+
requirements:
|
173
|
+
- - ">="
|
174
|
+
- !ruby/object:Gem::Version
|
175
|
+
hash: 3
|
176
|
+
segments:
|
177
|
+
- 0
|
178
|
+
version: "0"
|
179
|
+
requirements: []
|
180
|
+
|
181
|
+
rubyforge_project:
|
182
|
+
rubygems_version: 1.3.7
|
183
|
+
signing_key:
|
184
|
+
specification_version: 3
|
185
|
+
summary: lackie-0.1.0
|
186
|
+
test_files:
|
187
|
+
- features/remote_control.feature
|
188
|
+
- features/step_definitions/lackie_steps.rb
|
189
|
+
- features/support/config.ru
|
190
|
+
- features/support/env.rb
|
191
|
+
- features/support/example_app.rb
|
192
|
+
- features/support/example_app/app.html
|
193
|
+
- spec/lackie/javascript/surrender_spec.rb
|
194
|
+
- spec/lackie/poller_spec.rb
|
195
|
+
- spec/lackie/rack/middleware_spec.rb
|
196
|
+
- spec/lackie/remote_control_spec.rb
|
197
|
+
- spec/spec_helper.rb
|