screw-unit 0.3.3 → 0.5.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.
- data/CHANGES +10 -0
- data/Rakefile +27 -25
- data/VERSION.yml +4 -0
- data/bin/screw_unit_server +14 -4
- data/core/EXAMPLE.html +1 -1
- data/core/example/spec/suite.html +1 -1
- data/core/lib/jquery-1.3.2.js +4376 -0
- data/core/lib/jquery.fn.js +2 -1
- data/core/lib/screw.behaviors.js +8 -7
- data/core/lib/screw.builder.js +34 -19
- data/core/lib/screw.css +1 -1
- data/core/lib/screw.events.js +5 -5
- data/core/lib/screw.matchers.js +59 -2
- data/core/spec/suite.html +2 -1
- data/core/spec/with_screw_context_spec.js +9 -0
- data/lib/screw_unit.rb +13 -16
- data/lib/screw_unit/representations.rb +2 -0
- data/lib/screw_unit/{resources/spec.rb → representations/spec.html.rb} +55 -39
- data/spec/functional/functional_spec.rb +1 -1
- data/spec/functional/functional_spec_helper.rb +21 -33
- data/spec/functional/functional_spec_server_starter.rb +68 -0
- data/spec/unit/js_test_core/specs/spec_file_spec.rb +52 -18
- data/spec/unit/unit_spec_helper.rb +24 -91
- data/vendor/js-test-core/CHANGES +8 -0
- data/vendor/js-test-core/Rakefile +1 -0
- data/vendor/js-test-core/lib/js_test_core.rb +22 -14
- data/vendor/js-test-core/lib/js_test_core/app.rb +12 -0
- data/vendor/js-test-core/lib/js_test_core/client.rb +21 -9
- data/vendor/js-test-core/lib/js_test_core/configuration.rb +35 -0
- data/vendor/js-test-core/lib/js_test_core/extensions.rb +1 -1
- data/vendor/js-test-core/lib/js_test_core/extensions/selenium/client/driver.rb +7 -0
- data/vendor/js-test-core/lib/js_test_core/models.rb +8 -0
- data/vendor/js-test-core/lib/js_test_core/models/selenium_session.rb +80 -0
- data/vendor/js-test-core/lib/js_test_core/representations.rb +11 -0
- data/vendor/js-test-core/lib/js_test_core/representations/dir.html.rb +24 -0
- data/vendor/js-test-core/lib/js_test_core/representations/not_found.html.rb +15 -0
- data/vendor/js-test-core/lib/js_test_core/representations/page.html.rb +41 -0
- data/vendor/js-test-core/lib/js_test_core/representations/spec.html.rb +24 -0
- data/vendor/js-test-core/lib/js_test_core/resources.rb +13 -7
- data/vendor/js-test-core/lib/js_test_core/resources/core_file.rb +19 -0
- data/vendor/js-test-core/lib/js_test_core/resources/file.rb +42 -20
- data/vendor/js-test-core/lib/js_test_core/resources/implementations_deprecation.rb +12 -0
- data/vendor/js-test-core/lib/js_test_core/resources/not_found.rb +35 -0
- data/vendor/js-test-core/lib/js_test_core/resources/resource.rb +16 -0
- data/vendor/js-test-core/lib/js_test_core/resources/selenium_session.rb +104 -0
- data/vendor/js-test-core/lib/js_test_core/resources/spec_file.rb +63 -0
- data/vendor/js-test-core/lib/js_test_core/resources/web_root.rb +5 -60
- data/vendor/js-test-core/{vendor/thin-rest/README → spec/example_core/subdir/SubDirFile.js} +0 -0
- data/vendor/js-test-core/spec/example_specs/custom_dir_and_suite/passing_spec.js +6 -0
- data/vendor/js-test-core/spec/example_specs/custom_suite.html +8 -0
- data/vendor/js-test-core/spec/example_specs/foo/failing_spec.js +1 -1
- data/vendor/js-test-core/spec/example_specs/foo/passing_spec.js +1 -1
- data/vendor/js-test-core/spec/spec_helpers/be_http.rb +32 -0
- data/vendor/js-test-core/spec/spec_helpers/example_group.rb +36 -0
- data/vendor/js-test-core/spec/spec_helpers/fake_selenium_driver.rb +27 -0
- data/vendor/js-test-core/spec/spec_helpers/show_test_exceptions.rb +22 -0
- data/vendor/js-test-core/spec/unit/js_test_core/client_spec.rb +35 -10
- data/vendor/js-test-core/spec/unit/js_test_core/configuration_spec.rb +44 -0
- data/vendor/js-test-core/spec/unit/js_test_core/models/selenium_session_spec.rb +85 -0
- data/vendor/js-test-core/spec/unit/js_test_core/resources/core_file_spec.rb +60 -0
- data/vendor/js-test-core/spec/unit/js_test_core/resources/file_spec.rb +54 -63
- data/vendor/js-test-core/spec/unit/js_test_core/resources/implementations_deprecation_spec.rb +18 -0
- data/vendor/js-test-core/spec/unit/js_test_core/resources/not_found_spec.rb +51 -0
- data/vendor/js-test-core/spec/unit/js_test_core/resources/selenium_session_spec.rb +362 -0
- data/vendor/js-test-core/spec/unit/js_test_core/resources/spec_file_spec.rb +120 -0
- data/vendor/js-test-core/spec/unit/js_test_core/resources/web_root_spec.rb +15 -19
- data/vendor/js-test-core/spec/unit/unit_spec_helper.rb +16 -149
- data/vendor/js-test-core/vendor/lucky-luciano/README.markdown +7 -0
- data/vendor/js-test-core/vendor/lucky-luciano/lib/lucky_luciano.rb +4 -0
- data/vendor/js-test-core/vendor/lucky-luciano/lib/lucky_luciano/resource.rb +135 -0
- data/vendor/js-test-core/vendor/lucky-luciano/lib/lucky_luciano/rspec.rb +4 -0
- data/vendor/js-test-core/vendor/lucky-luciano/lib/lucky_luciano/rspec/be_http.rb +32 -0
- data/vendor/js-test-core/vendor/lucky-luciano/spec/lucky_luciano/resource_spec.rb +231 -0
- data/vendor/js-test-core/vendor/lucky-luciano/spec/spec_helper.rb +48 -0
- data/vendor/js-test-core/vendor/{thin-rest → lucky-luciano}/spec/spec_suite.rb +1 -2
- metadata +134 -145
- data/core/lib/jquery-1.2.6.js +0 -3549
- data/lib/screw_unit/resources.rb +0 -2
- data/spec/unit/js_test_core/specs/spec_dir_spec.rb +0 -38
- data/vendor/js-test-core/lib/js_test_core/rack.rb +0 -2
- data/vendor/js-test-core/lib/js_test_core/rack/commonlogger.rb +0 -5
- data/vendor/js-test-core/lib/js_test_core/rails_server.rb +0 -22
- data/vendor/js-test-core/lib/js_test_core/resources/dir.rb +0 -67
- data/vendor/js-test-core/lib/js_test_core/resources/file_not_found.rb +0 -11
- data/vendor/js-test-core/lib/js_test_core/resources/runner.rb +0 -107
- data/vendor/js-test-core/lib/js_test_core/resources/session.rb +0 -44
- data/vendor/js-test-core/lib/js_test_core/resources/session_finish.rb +0 -17
- data/vendor/js-test-core/lib/js_test_core/resources/specs/spec_dir.rb +0 -46
- data/vendor/js-test-core/lib/js_test_core/resources/specs/spec_file.rb +0 -17
- data/vendor/js-test-core/lib/js_test_core/selenium.rb +0 -2
- data/vendor/js-test-core/lib/js_test_core/selenium/selenium_driver.rb +0 -5
- data/vendor/js-test-core/lib/js_test_core/server.rb +0 -50
- data/vendor/js-test-core/lib/js_test_core/thin.rb +0 -3
- data/vendor/js-test-core/lib/js_test_core/thin/backends/js_test_core_server.rb +0 -9
- data/vendor/js-test-core/lib/js_test_core/thin/js_test_core_connection.rb +0 -8
- data/vendor/js-test-core/spec/unit/js_test_core/rails_server_spec.rb +0 -45
- data/vendor/js-test-core/spec/unit/js_test_core/resources/dir_spec.rb +0 -52
- data/vendor/js-test-core/spec/unit/js_test_core/resources/file_not_found_spec.rb +0 -16
- data/vendor/js-test-core/spec/unit/js_test_core/resources/runners/runner_spec.rb +0 -303
- data/vendor/js-test-core/spec/unit/js_test_core/resources/session_finish_spec.rb +0 -79
- data/vendor/js-test-core/spec/unit/js_test_core/resources/session_spec.rb +0 -82
- data/vendor/js-test-core/spec/unit/js_test_core/resources/specs/spec_dir_spec.rb +0 -105
- data/vendor/js-test-core/spec/unit/js_test_core/resources/specs/spec_file_spec.rb +0 -42
- data/vendor/js-test-core/spec/unit/js_test_core/server_spec.rb +0 -117
- data/vendor/js-test-core/spec/unit/thin/js_test_core_connection_spec.rb +0 -6
- data/vendor/js-test-core/vendor/thin-rest/CHANGES +0 -2
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest.rb +0 -9
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/connection.rb +0 -116
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/extensions.rb +0 -3
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/extensions/object.rb +0 -21
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/resource.rb +0 -108
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/resource_invalid.rb +0 -4
- data/vendor/js-test-core/vendor/thin-rest/lib/thin_rest/routing_error.rb +0 -5
- data/vendor/js-test-core/vendor/thin-rest/spec/thin_rest/connection_spec.rb +0 -207
- data/vendor/js-test-core/vendor/thin-rest/spec/thin_rest/resource_spec.rb +0 -127
- data/vendor/js-test-core/vendor/thin-rest/spec/thin_rest_spec_helper.rb +0 -124
@@ -3,29 +3,25 @@ require File.expand_path("#{File.dirname(__FILE__)}/../../unit_spec_helper")
|
|
3
3
|
module JsTestCore
|
4
4
|
module Resources
|
5
5
|
describe WebRoot do
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
connection.receive_data("GET /stylesheets HTTP/1.1\r\nHost: _\r\n\r\n")
|
6
|
+
macro("includes a link to the spec suite") do |relative_path|
|
7
|
+
it "includes a link to the spec suite" do
|
8
|
+
response = get(relative_path)
|
9
|
+
response.should be_http(
|
10
|
+
200,
|
11
|
+
{},
|
12
|
+
""
|
13
|
+
)
|
14
|
+
doc = Nokogiri::HTML(response.body)
|
15
|
+
doc.css("a[href='/specs']").should_not be_nil
|
17
16
|
end
|
18
17
|
end
|
19
18
|
|
20
|
-
describe "GET
|
21
|
-
|
22
|
-
|
23
|
-
mock(connection).send_head(200, 'Content-Type' => "text/css", 'Content-Length' => ::File.size(path), 'Last-Modified' => ::File.mtime(path).rfc822)
|
24
|
-
mock(connection).send_data(::File.read(path))
|
25
|
-
stub(EventMachine).close_connection
|
19
|
+
describe "GET " do
|
20
|
+
send("includes a link to the spec suite", "")
|
21
|
+
end
|
26
22
|
|
27
|
-
|
28
|
-
|
23
|
+
describe "GET /" do
|
24
|
+
send("includes a link to the spec suite", "/")
|
29
25
|
end
|
30
26
|
end
|
31
27
|
end
|
@@ -1,161 +1,28 @@
|
|
1
1
|
require "rubygems"
|
2
2
|
require "spec"
|
3
|
+
require "spec/autorun"
|
4
|
+
require "rack/test"
|
5
|
+
ARGV.push("-b")
|
3
6
|
|
4
7
|
dir = File.dirname(__FILE__)
|
5
|
-
|
8
|
+
LIBRARY_ROOT_DIR = File.expand_path("#{dir}/../..")
|
9
|
+
$LOAD_PATH.unshift File.expand_path("#{LIBRARY_ROOT_DIR}/lib")
|
6
10
|
require "js_test_core"
|
7
|
-
require "
|
11
|
+
require "nokogiri"
|
8
12
|
require "guid"
|
13
|
+
require "#{LIBRARY_ROOT_DIR}/spec/spec_helpers/be_http"
|
14
|
+
require "#{LIBRARY_ROOT_DIR}/spec/spec_helpers/example_group"
|
15
|
+
require "#{LIBRARY_ROOT_DIR}/spec/spec_helpers/fake_selenium_driver"
|
16
|
+
require "#{LIBRARY_ROOT_DIR}/spec/spec_helpers/show_test_exceptions"
|
9
17
|
|
10
18
|
Spec::Runner.configure do |config|
|
11
19
|
config.mock_with :rr
|
12
20
|
end
|
13
21
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
!@actual.nil?
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class JsTestCoreTestDir < JsTestCore::Resources::Dir
|
26
|
-
def get
|
27
|
-
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
class Spec::ExampleGroup
|
32
|
-
class << self
|
33
|
-
def thin_logging
|
34
|
-
@thin_logging = true if @thin_logging.nil?
|
35
|
-
@thin_logging
|
36
|
-
end
|
37
|
-
|
38
|
-
attr_writer :thin_logging
|
39
|
-
end
|
40
|
-
|
41
|
-
attr_reader :core_path, :spec_root_path, :implementation_root_path, :public_path, :server, :connection
|
42
|
-
before(:all) do
|
43
|
-
dir = File.dirname(__FILE__)
|
44
|
-
@core_path = File.expand_path("#{dir}/../example_core")
|
45
|
-
JsTestCore.core_path = core_path
|
46
|
-
@spec_root_path = File.expand_path("#{dir}/../example_specs")
|
47
|
-
@implementation_root_path = File.expand_path("#{dir}/../example_public/javascripts")
|
48
|
-
@public_path = File.expand_path("#{dir}/../example_public")
|
49
|
-
stub(Thread).start.yields
|
50
|
-
end
|
51
|
-
|
52
|
-
before(:each) do
|
53
|
-
JsTestCore::Server.instance = JsTestCore::Server.new(spec_root_path, implementation_root_path, public_path)
|
54
|
-
stub(EventMachine).run do
|
55
|
-
raise "You need to mock calls to EventMachine.run or the process will hang"
|
56
|
-
end
|
57
|
-
stub(EventMachine).start_server do
|
58
|
-
raise "You need to mock calls to EventMachine.start_server or the process will hang"
|
59
|
-
end
|
60
|
-
stub(EventMachine).send_data do
|
61
|
-
raise "Calls to EventMachine.send_data must be mocked or stubbed"
|
62
|
-
end
|
63
|
-
@connection = create_connection
|
64
|
-
stub(EventMachine).send_data {raise "EventMachine.send_data must be handled"}
|
65
|
-
stub(EventMachine).close_connection {raise "EventMachine.close_connection must be handled"}
|
66
|
-
@server = JsTestCore::Server.instance
|
67
|
-
Thin::Logging.silent = !self.class.thin_logging
|
68
|
-
Thin::Logging.debug = self.class.thin_logging
|
69
|
-
end
|
70
|
-
|
71
|
-
after(:each) do
|
72
|
-
JsTestCore::Resources::WebRoot.dispatch_strategy = nil
|
73
|
-
Thin::Logging.silent = true
|
74
|
-
Thin::Logging.debug = false
|
75
|
-
end
|
76
|
-
|
77
|
-
def create_connection(guid=Guid.new)
|
78
|
-
Thin::JsTestCoreConnection.new(Guid.new)
|
79
|
-
end
|
80
|
-
|
81
|
-
def get(url, params={})
|
82
|
-
request(:get, url, params)
|
83
|
-
end
|
84
|
-
|
85
|
-
def post(url, params={})
|
86
|
-
request(:post, url, params)
|
87
|
-
end
|
88
|
-
|
89
|
-
def put(url, params={})
|
90
|
-
request(:put, url, params)
|
91
|
-
end
|
92
|
-
|
93
|
-
def delete(url, params={})
|
94
|
-
request(:delete, url, params)
|
95
|
-
end
|
96
|
-
|
97
|
-
def env_for(method, url, params)
|
98
|
-
Rack::MockRequest.env_for(url, params.merge({:method => method.to_s.upcase, 'js_test_core.connection' => connection}))
|
99
|
-
end
|
22
|
+
Sinatra::Application.use ShowTestExceptions
|
23
|
+
Sinatra::Application.set :raise_errors, true
|
24
|
+
Sinatra::Application.set :show_exceptions, false
|
25
|
+
JsTestCore::App.set :raise_errors, true
|
26
|
+
JsTestCore::App.set :show_exceptions, false
|
100
27
|
|
101
|
-
|
102
|
-
body = params.map do |key, value|
|
103
|
-
"#{URI.escape(key)}=#{URI.escape(value)}"
|
104
|
-
end.join("&")
|
105
|
-
connection.receive_data "#{method.to_s.upcase} #{path} HTTP/1.1\r\nHost: _\r\nContent-Length: #{body.length}\r\n\r\n#{body}"
|
106
|
-
connection.response
|
107
|
-
end
|
108
|
-
alias_method :request, :create_request
|
109
|
-
|
110
|
-
def spec_dir(relative_path="")
|
111
|
-
absolute_path = spec_root_path + relative_path
|
112
|
-
JsTestCore::Resources::Specs::SpecDir.new(:connection => connection, :absolute_path => absolute_path, :relative_path => "/specs#{relative_path}")
|
113
|
-
end
|
114
|
-
|
115
|
-
def contain_spec_file_with_correct_paths(path_relative_to_spec_root)
|
116
|
-
expected_absolute_path = spec_root_path + path_relative_to_spec_root
|
117
|
-
expected_relative_path = "/specs" + path_relative_to_spec_root
|
118
|
-
|
119
|
-
::Spec::Matchers::SimpleMatcher.new(expected_relative_path) do |globbed_files|
|
120
|
-
file = globbed_files.find do |file|
|
121
|
-
file.absolute_path == expected_absolute_path
|
122
|
-
end
|
123
|
-
raise "Did not find file with absolute path of #{expected_absolute_path.inspect}" unless file
|
124
|
-
file.relative_path == expected_relative_path
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def stub_send_data
|
129
|
-
stub(EventMachine).send_data do |signature, data, data_length|
|
130
|
-
data_length
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
class FakeSeleniumDriver
|
136
|
-
SESSION_ID = "DEADBEEF"
|
137
|
-
attr_reader :session_id
|
138
|
-
|
139
|
-
def initialize
|
140
|
-
@session_id = nil
|
141
|
-
end
|
142
|
-
|
143
|
-
def start
|
144
|
-
@session_id = SESSION_ID
|
145
|
-
end
|
146
|
-
|
147
|
-
def stop
|
148
|
-
@session_id = nil
|
149
|
-
end
|
150
|
-
|
151
|
-
def open(url)
|
152
|
-
end
|
153
|
-
|
154
|
-
def create_cookie(key_value, options="")
|
155
|
-
|
156
|
-
end
|
157
|
-
|
158
|
-
def session_started?
|
159
|
-
!!@session_id
|
160
|
-
end
|
161
|
-
end
|
28
|
+
Sinatra::Application.use(JsTestCore::App)
|
@@ -0,0 +1,7 @@
|
|
1
|
+
Lucky Luciano, is a library that makes it easy to organize your Sinatra routes into Restful Resources.
|
2
|
+
|
3
|
+
## Who was this Lucky Luciano person?
|
4
|
+
|
5
|
+
Lucky Luciano was a mob boss and an alleged friend of Frank Sinatra.
|
6
|
+
|
7
|
+
http://news.bbc.co.uk/2/hi/special_report/1998/05/98/sinatra/94360.stm
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module LuckyLuciano
|
2
|
+
class Resource
|
3
|
+
class << self
|
4
|
+
attr_reader :base_path_definition
|
5
|
+
|
6
|
+
def map(base_path_definition)
|
7
|
+
@base_path_definition = base_path_definition
|
8
|
+
end
|
9
|
+
|
10
|
+
def recorded_http_handlers
|
11
|
+
@recorded_http_handlers ||= []
|
12
|
+
end
|
13
|
+
|
14
|
+
def route_handler
|
15
|
+
create_sinatra_handler
|
16
|
+
end
|
17
|
+
|
18
|
+
["get", "put", "post", "delete"].each do |http_verb|
|
19
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
20
|
+
def #{http_verb}(relative_path, opts={}, &block)
|
21
|
+
recorded_http_handlers << [:#{http_verb}, relative_path, opts, block]
|
22
|
+
end
|
23
|
+
RUBY
|
24
|
+
end
|
25
|
+
|
26
|
+
def path(*sub_paths)
|
27
|
+
params = sub_paths.last.is_a?(Hash) ? sub_paths.pop : {}
|
28
|
+
|
29
|
+
sub_path_definition = sub_paths.join("/")
|
30
|
+
|
31
|
+
full_path = "#{base_path(params)}/#{path_from_definition(sub_path_definition, params)}".
|
32
|
+
gsub("//", "/").gsub(/\/$/, "")
|
33
|
+
|
34
|
+
base_path_param_keys = param_keys_from(base_path_definition)
|
35
|
+
query_params = params.delete_if do |key, value|
|
36
|
+
base_path_param_keys.include?(key)
|
37
|
+
end
|
38
|
+
|
39
|
+
sub_path_param_keys = param_keys_from(sub_path_definition)
|
40
|
+
query_params = params.delete_if do |key, value|
|
41
|
+
sub_path_param_keys.include?(key)
|
42
|
+
end
|
43
|
+
|
44
|
+
if query_params.empty?
|
45
|
+
full_path
|
46
|
+
else
|
47
|
+
query = build_query(query_params)
|
48
|
+
"#{full_path}?#{query}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def base_path(params={})
|
53
|
+
path_from_definition(base_path_definition, params)
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
def path_from_definition(definition, params={})
|
59
|
+
param_keys = param_keys_from(definition)
|
60
|
+
if param_keys.empty?
|
61
|
+
definition.dup
|
62
|
+
else
|
63
|
+
param_keys.each do |base_path_param|
|
64
|
+
unless params.include?(base_path_param.to_sym)
|
65
|
+
raise ArgumentError, "Expected #{base_path_param.inspect} to have a value"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
definition.split("/").map do |segment|
|
69
|
+
if param_key = segment_param_key(segment)
|
70
|
+
params[param_key]
|
71
|
+
else
|
72
|
+
segment
|
73
|
+
end
|
74
|
+
end.join("/")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def param_keys_from(definition)
|
79
|
+
definition.split("/").find_all do |segment|
|
80
|
+
segment_param_key(segment)
|
81
|
+
end.map do |param|
|
82
|
+
param[1..-1].to_sym
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def segment_param_key(segment)
|
87
|
+
segment[0..0] == ':' ? segment[1..-1].to_sym : nil
|
88
|
+
end
|
89
|
+
|
90
|
+
def build_query(params)
|
91
|
+
params.to_a.inject([]) do |splatted_params, (key, value)|
|
92
|
+
[value].flatten.each do |value_in_param|
|
93
|
+
splatted_params << "#{URI.escape(key.to_s)}=#{URI.escape(value_in_param.to_s)}"
|
94
|
+
end
|
95
|
+
splatted_params
|
96
|
+
end.join("&")
|
97
|
+
end
|
98
|
+
|
99
|
+
def create_sinatra_handler
|
100
|
+
handlers = recorded_http_handlers
|
101
|
+
resource_class = self
|
102
|
+
Module.new do
|
103
|
+
(class << self; self; end).class_eval do
|
104
|
+
define_method(:registered) do |app|
|
105
|
+
handlers.each do |handler|
|
106
|
+
verb, relative_path, opts, block = handler
|
107
|
+
full_path = "#{resource_class.base_path_definition}/#{relative_path}".
|
108
|
+
gsub("//", "/").gsub(%r{^.+/$}) do |match|
|
109
|
+
match.gsub(%r{/$}, "")
|
110
|
+
end
|
111
|
+
app.send(verb, full_path, opts) do
|
112
|
+
resource_class.new(self).instance_eval(&block)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
attr_reader :app
|
122
|
+
|
123
|
+
def initialize(app)
|
124
|
+
@app = app
|
125
|
+
end
|
126
|
+
|
127
|
+
def method_missing(method_name, *args, &block)
|
128
|
+
if app.respond_to?(method_name)
|
129
|
+
app.send(method_name, *args, &block)
|
130
|
+
else
|
131
|
+
super
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module BeHttp
|
2
|
+
include Spec::Matchers
|
3
|
+
def be_http(status, headers, body)
|
4
|
+
SimpleMatcher.new(nil) do |given, matcher|
|
5
|
+
description = (<<-DESC).gsub(/^ +/, "")
|
6
|
+
be an http of
|
7
|
+
expected status: #{status.inspect}
|
8
|
+
actual status : #{given.status.inspect}
|
9
|
+
|
10
|
+
expected headers containing: #{headers.inspect}
|
11
|
+
actual headers : #{given.headers.inspect}
|
12
|
+
|
13
|
+
expected body containing: #{body.inspect}
|
14
|
+
actual body : #{given.body.inspect}
|
15
|
+
DESC
|
16
|
+
matcher.failure_message = description
|
17
|
+
matcher.negative_failure_message = "not #{description}"
|
18
|
+
|
19
|
+
passed = true
|
20
|
+
unless given.status == status
|
21
|
+
passed = false
|
22
|
+
end
|
23
|
+
unless headers.all?{|k, v| given.headers[k] == headers[k]}
|
24
|
+
passed = false
|
25
|
+
end
|
26
|
+
unless body.is_a?(Regexp) ? given.body =~ body : given.body.include?(body)
|
27
|
+
passed = false
|
28
|
+
end
|
29
|
+
passed
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,231 @@
|
|
1
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
|
2
|
+
|
3
|
+
module LuckyLuciano
|
4
|
+
module ResourceSpec
|
5
|
+
class Root < Resource
|
6
|
+
map "/"
|
7
|
+
end
|
8
|
+
|
9
|
+
class ResourceFixture < Resource
|
10
|
+
map "/foobar"
|
11
|
+
end
|
12
|
+
|
13
|
+
class ResourceFixtureWithSubPaths < Resource
|
14
|
+
map "/foobar"
|
15
|
+
|
16
|
+
get "/baz" do
|
17
|
+
"Response from /foobar/baz"
|
18
|
+
end
|
19
|
+
|
20
|
+
get "/users/:user_id" do
|
21
|
+
"User id is #{params[:user_id]}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class User < Resource
|
26
|
+
map "/users/:user_id"
|
27
|
+
|
28
|
+
get "/friends" do
|
29
|
+
"User #{params['user_id']} friends"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe Resource do
|
34
|
+
include ResourceSpec
|
35
|
+
|
36
|
+
before do
|
37
|
+
ResourceFixture.recorded_http_handlers.clear
|
38
|
+
end
|
39
|
+
|
40
|
+
macro("http verb") do |verb|
|
41
|
+
describe ".#{verb}" do
|
42
|
+
context "" do
|
43
|
+
before do
|
44
|
+
ResourceFixture.send(verb, "/") do
|
45
|
+
"He sleeps with the fishes"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it "creates a route to #{verb.upcase} the given path that executes the given block" do
|
50
|
+
app.register(ResourceFixture.route_handler)
|
51
|
+
response = send(verb, "/foobar")
|
52
|
+
response.should be_http(
|
53
|
+
200,
|
54
|
+
{},
|
55
|
+
"He sleeps with the fishes"
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "when the base path is blank and the relative path is blank" do
|
61
|
+
before do
|
62
|
+
Root.send(verb, "") do
|
63
|
+
"Response from /"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it "creates a route to /" do
|
68
|
+
app.register(Root.route_handler)
|
69
|
+
response = send(verb, "/")
|
70
|
+
response.should be_http(
|
71
|
+
200,
|
72
|
+
{},
|
73
|
+
"Response from /"
|
74
|
+
)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "when the relative path does not have a leading slash" do
|
79
|
+
before do
|
80
|
+
ResourceFixtureWithSubPaths.send(verb, "no_leading_slash") do
|
81
|
+
"Response from /foobar/no_leading_slash"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it "creates a route to #{verb.upcase} the given path that executes the given block" do
|
86
|
+
app.register(ResourceFixtureWithSubPaths.route_handler)
|
87
|
+
response = send(verb, "/foobar/no_leading_slash")
|
88
|
+
response.should be_http(
|
89
|
+
200,
|
90
|
+
{},
|
91
|
+
"Response from /foobar/no_leading_slash"
|
92
|
+
)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
it "does not respond to another type of http request" do
|
98
|
+
ResourceFixture.send(verb, "/") do
|
99
|
+
""
|
100
|
+
end
|
101
|
+
app.register(ResourceFixture.route_handler)
|
102
|
+
get("/foobar").status.should == 404 unless verb == "get"
|
103
|
+
put("/foobar").status.should == 404 unless verb == "put"
|
104
|
+
post("/foobar").status.should == 404 unless verb == "post"
|
105
|
+
delete("/foobar").status.should == 404 unless verb == "delete"
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "Instance behavior" do
|
109
|
+
attr_reader :evaluation_target
|
110
|
+
before do
|
111
|
+
evaluation_target = nil
|
112
|
+
ResourceFixture.send(verb, "/") do
|
113
|
+
evaluation_target = self
|
114
|
+
""
|
115
|
+
end
|
116
|
+
app.register(ResourceFixture.route_handler)
|
117
|
+
|
118
|
+
send(verb, "/foobar")
|
119
|
+
@evaluation_target = evaluation_target
|
120
|
+
end
|
121
|
+
|
122
|
+
it "evaluates the block in as a Resource" do
|
123
|
+
evaluation_target.class.should == ResourceFixture
|
124
|
+
end
|
125
|
+
|
126
|
+
it "sets app to be the Sinatra context" do
|
127
|
+
evaluation_target.app.class.should == Sinatra::Application
|
128
|
+
end
|
129
|
+
|
130
|
+
it "delegates methods to the #app" do
|
131
|
+
return_value = nil
|
132
|
+
mock.proxy(evaluation_target.app).params {|val| return_value = val}
|
133
|
+
evaluation_target.params.should == return_value
|
134
|
+
end
|
135
|
+
|
136
|
+
context "when the #app does not respond to the method" do
|
137
|
+
it "raises a NoMethodError from the Resource's perspective" do
|
138
|
+
lambda do
|
139
|
+
evaluation_target.i_dont_exist
|
140
|
+
end.should raise_error(NoMethodError, /ResourceFixture/)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
send("http verb", "get")
|
149
|
+
send("http verb", "put")
|
150
|
+
send("http verb", "post")
|
151
|
+
send("http verb", "delete")
|
152
|
+
|
153
|
+
describe ".path" do
|
154
|
+
context "when passed nothing" do
|
155
|
+
it "returns the base_path" do
|
156
|
+
ResourceFixture.path.should == "/foobar"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context "when passed a sub path" do
|
161
|
+
it "merges the base_path into the sub path, regardless of a / in front" do
|
162
|
+
ResourceFixtureWithSubPaths.path("/baz").should == "/foobar/baz"
|
163
|
+
ResourceFixtureWithSubPaths.path("baz").should == "/foobar/baz"
|
164
|
+
end
|
165
|
+
|
166
|
+
context "when passed a multiple sub paths" do
|
167
|
+
it "joins the sub paths with '/'" do
|
168
|
+
ResourceFixtureWithSubPaths.path("users", 99).should == "/foobar/users/99"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context "when passed a hash as the last argument" do
|
173
|
+
context "when using a single path argument" do
|
174
|
+
it "creates url params from the hash" do
|
175
|
+
path = ResourceFixtureWithSubPaths.path(
|
176
|
+
"/users/:user_id", {:user_id => 99, :single_value_param => "single_value_param_value", 'multiple_value_param[]' => [1,2,3]}
|
177
|
+
)
|
178
|
+
uri = URI.parse(path)
|
179
|
+
uri.path.should == "/foobar/users/99"
|
180
|
+
query_parts = uri.query.split("&")
|
181
|
+
query_parts.should =~ [
|
182
|
+
"multiple_value_param[]=1",
|
183
|
+
"multiple_value_param[]=2",
|
184
|
+
"multiple_value_param[]=3",
|
185
|
+
"single_value_param=single_value_param_value",
|
186
|
+
]
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
context "when using multiple path arguments" do
|
191
|
+
it "creates url params from the hash" do
|
192
|
+
path = ResourceFixtureWithSubPaths.path(
|
193
|
+
"users", 99, {:single_value_param => "single_value_param_value", 'multiple_value_param[]' => [1,2,3]}
|
194
|
+
)
|
195
|
+
uri = URI.parse(path)
|
196
|
+
uri.path.should == "/foobar/users/99"
|
197
|
+
query_parts = uri.query.split("&")
|
198
|
+
query_parts.should =~ [
|
199
|
+
"multiple_value_param[]=1",
|
200
|
+
"multiple_value_param[]=2",
|
201
|
+
"multiple_value_param[]=3",
|
202
|
+
"single_value_param=single_value_param_value",
|
203
|
+
]
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context "when the base_path contains a parameter" do
|
210
|
+
context "when not passed" do
|
211
|
+
it "raises an ArgumentError" do
|
212
|
+
lambda do
|
213
|
+
User.path
|
214
|
+
end.should raise_error(
|
215
|
+
ArgumentError,
|
216
|
+
%r{Expected :user_id to have a value}
|
217
|
+
)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
context "when first argument is a hash" do
|
222
|
+
it "returns the full path with the base path param value" do
|
223
|
+
User.path("friends", :user_id => 99).should == "/users/99/friends"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|