sham_rack 1.1.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.
@@ -0,0 +1,2 @@
1
+ pkg
2
+ rdoc
@@ -0,0 +1,8 @@
1
+ ## 3-Jun-2009 [mdub@dogbiscuit.org]
2
+
3
+ * Introduced ShamRack#at to simplify registration of apps.
4
+
5
+ ## 13-May-2009 [mdub@dogbiscuit.org]
6
+
7
+ * Added accessors on HTTP object for address, port and rack_app.
8
+ * Added accessors to imitate "net/https".
@@ -0,0 +1,77 @@
1
+ ShamRack
2
+ ========
3
+
4
+ ShamRack plumbs Net:HTTP into [Rack][rack].
5
+
6
+ What's it for, again?
7
+ ---------------------
8
+
9
+ Well, you can _test your HTTP client code_, using ShamRack to stub out an external web-service. Think of it as [FakeWeb][fakeweb] on steriods.
10
+
11
+ Or, you can _test your Rack application_ (or Sinatra, or Rails, or Merb) using arbitrary HTTP client libraries, to check interoperability. For instance, you could hit a local app using:
12
+
13
+ * [`rest-client`][rest-client]
14
+ * [`httparty`][httparty]
15
+ * [`oauth`][oauth]
16
+
17
+ Installing it
18
+ -------------
19
+
20
+ gem sources -a http://gems.github.com
21
+ sudo gem install mdub-sham_rack
22
+
23
+ Using it
24
+ --------
25
+
26
+ ### A simple inline application
27
+
28
+ require 'sham_rack'
29
+
30
+ ShamRack.at("www.example.com") do |env|
31
+ ["200 OK", { "Content-type" => "text/plain" }, "Hello, world!"]
32
+ end
33
+
34
+ require 'open-uri'
35
+ open("http://www.example.com/").read #=> "Hello, world!"
36
+
37
+ ### Sinatra integration
38
+
39
+ ShamRack.at("sinatra.xyz").sinatra do
40
+ get "/hello/:subject" do
41
+ "Hello, #{params[:subject]}"
42
+ end
43
+ end
44
+
45
+ open("http://sinatra.xyz/hello/stranger").read #=> "Hello, stranger"
46
+
47
+ ### Rackup support
48
+
49
+ ShamRack.at("rackup.xyz").rackup do
50
+ use Some::Middleware
51
+ use Some::Other::Middleware
52
+ run MyApp.new
53
+ end
54
+
55
+ ### Any old app
56
+
57
+ ShamRack.mount(my_google_stub, "google.com")
58
+
59
+ What's the catch?
60
+ -----------------
61
+
62
+ * It's brand new! (there will be dragons)
63
+ * Your Rack request-handling code runs in the same Ruby VM, in fact the same Thread, as your request.
64
+
65
+ Thanks to
66
+ ---------
67
+
68
+ * Blaine Cook for [FakeWeb][fakeweb], which was an inspiration for ShamRack.
69
+ * Perryn Fowler for his efforts plumbing Net::HTTP into ActionController::TestProcess.
70
+ * Christian Neukirchen et al for the chewy goodness that is [Rack][rack].
71
+
72
+ [rack]: http://rack.rubyforge.org/
73
+ [sinatra]: http://www.sinatrarb.com/
74
+ [rest-client]: http://github.com/adamwiggins/rest-client
75
+ [httparty]: http://github.com/jnunemaker/httparty
76
+ [oauth]: http://oauth.rubyforge.org/
77
+ [fakeweb]: http://fakeweb.rubyforge.org/
@@ -0,0 +1,40 @@
1
+ require "rubygems"
2
+ require "rake"
3
+
4
+ require "spec/rake/spectask"
5
+
6
+ task "default" => "spec"
7
+
8
+ Spec::Rake::SpecTask.new do |t|
9
+ t.spec_opts = ["--colour", "--format", "progress"]
10
+ t.spec_files = FileList['spec/**/*_spec.rb']
11
+ end
12
+
13
+ require "jeweler"
14
+
15
+ Jeweler::Tasks.new do |g|
16
+ g.name = "sham_rack"
17
+ g.summary = "Net::HTTP-to-Rack plumbing"
18
+ g.email = "mdub@dogbiscuit.org"
19
+ g.homepage = "http://github.com/mdub/sham_rack"
20
+ g.description = "ShamRack plumbs Net::HTTP directly into Rack, for quick and easy HTTP testing."
21
+ g.authors = ["Mike Williams"]
22
+ g.rubyforge_project = "shamrack"
23
+ end
24
+
25
+ Jeweler::RubyforgeTasks.new
26
+
27
+ require 'rake/rdoctask'
28
+ Rake::RDocTask.new do |rdoc|
29
+ if File.exist?('VERSION.yml')
30
+ config = YAML.load(File.read('VERSION.yml'))
31
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
32
+ else
33
+ version = ""
34
+ end
35
+
36
+ rdoc.rdoc_dir = 'rdoc'
37
+ rdoc.title = "ShamRack #{version}"
38
+ rdoc.main = "ShamRack"
39
+ rdoc.rdoc_files.include('lib/**/*.rb')
40
+ end
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 1
3
+ :major: 1
4
+ :minor: 1
@@ -0,0 +1,7 @@
1
+ # ShamRack allows access to Rack applications using Net::Http, but without network traffic.
2
+ #
3
+ # For more detail, see http://github.com/mdub/sham_rack
4
+ #
5
+ module ShamRack; end
6
+
7
+ require "sham_rack/core_ext/net/http"
@@ -0,0 +1,17 @@
1
+ require "net/http"
2
+ require "sham_rack/registry"
3
+ require "sham_rack/http"
4
+
5
+ module Net
6
+
7
+ def HTTP.new(address, port = nil, *proxy_args)
8
+ port ||= HTTP.default_port
9
+ rack_app = ShamRack.application_for(address, port)
10
+ if rack_app
11
+ ShamRack::HTTP.new(address, port, rack_app)
12
+ else
13
+ super(address, port, *proxy_args)
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,109 @@
1
+ module ShamRack
2
+
3
+ # a sham version of Net::HTTP
4
+ class HTTP
5
+
6
+ def initialize(address, port, rack_app)
7
+ @address = address
8
+ @port = port
9
+ @rack_app = rack_app
10
+ end
11
+
12
+ attr_reader :address, :port, :rack_app
13
+
14
+ attr_accessor :read_timeout, :open_timeout
15
+ attr_accessor :use_ssl,:key, :cert, :ca_file, :ca_path, :verify_mode, :verify_callback, :verify_depth, :cert_store
16
+
17
+ def start
18
+ yield self
19
+ end
20
+
21
+ def request(req, body = nil)
22
+ env = default_env
23
+ env.merge!(path_env(req.path))
24
+ env.merge!(method_env(req))
25
+ env.merge!(header_env(req))
26
+ env.merge!(io_env(req, body))
27
+ response = build_response(@rack_app.call(env))
28
+ yield response if block_given?
29
+ return response
30
+ end
31
+
32
+ private
33
+
34
+ def default_env
35
+ {
36
+ "SCRIPT_NAME" => "",
37
+ "SERVER_NAME" => @address,
38
+ "SERVER_PORT" => @port.to_s,
39
+ "rack.version" => [0,1],
40
+ "rack.url_scheme" => "http",
41
+ "rack.multithread" => true,
42
+ "rack.multiprocess" => true,
43
+ "rack.run_once" => false
44
+ }
45
+ end
46
+
47
+ def method_env(request)
48
+ {
49
+ "REQUEST_METHOD" => request.method
50
+ }
51
+ end
52
+
53
+ def io_env(request, body)
54
+ raise(ArgumentError, "both request.body and body argument were provided") if (request.body && body)
55
+ body ||= request.body || ""
56
+ {
57
+ "rack.input" => StringIO.new(body),
58
+ "rack.errors" => $stderr
59
+ }
60
+ end
61
+
62
+ def path_env(path)
63
+ uri = URI.parse(path)
64
+ {
65
+ "PATH_INFO" => uri.path,
66
+ "QUERY_STRING" => (uri.query || ""),
67
+ }
68
+ end
69
+
70
+ def header_env(request)
71
+ result = {}
72
+ request.each do |header, content|
73
+ result["HTTP_" + header.upcase.gsub('-', '_')] = content
74
+ end
75
+ %w(TYPE LENGTH).each do |x|
76
+ result["CONTENT_#{x}"] = result.delete("HTTP_CONTENT_#{x}") if result.has_key?("HTTP_CONTENT_#{x}")
77
+ end
78
+ return result
79
+ end
80
+
81
+ def build_response(rack_response)
82
+ status, headers, body = rack_response
83
+ code, message = status.to_s.split(" ", 2)
84
+ response = Net::HTTPResponse.send(:response_class, code).new("Sham", code, message)
85
+ response.instance_variable_set(:@body, assemble_body(body))
86
+ response.instance_variable_set(:@read, true)
87
+ response.extend ShamRack::ResponseExtensions
88
+ return response
89
+ end
90
+
91
+ def assemble_body(body)
92
+ content = ""
93
+ body.each { |fragment| content << fragment }
94
+ return content
95
+ end
96
+
97
+ end
98
+
99
+ module ResponseExtensions
100
+
101
+ def read_body(dest = nil)
102
+ yield @body if block_given?
103
+ dest << @body if dest
104
+ return @body
105
+ end
106
+
107
+ end
108
+
109
+ end
@@ -0,0 +1,59 @@
1
+ module ShamRack
2
+
3
+ module Registry
4
+
5
+ def mount(rack_app, address, port = nil)
6
+ port ||= Net::HTTP.default_port
7
+ registry[[address, port]] = rack_app
8
+ end
9
+
10
+ def unmount_all
11
+ registry.clear
12
+ end
13
+
14
+ def at(address, port = nil, &block)
15
+ if block
16
+ mount(block, address, port)
17
+ else
18
+ Registrar.new(address, port)
19
+ end
20
+ end
21
+
22
+ def application_for(address, port = nil)
23
+ port ||= Net::HTTP.default_port
24
+ registry[[address, port]]
25
+ end
26
+
27
+ private
28
+
29
+ def registry
30
+ @registry ||= {}
31
+ end
32
+
33
+ end
34
+
35
+ extend Registry
36
+
37
+ class Registrar
38
+
39
+ def initialize(address, port = nil)
40
+ @address = address
41
+ @port = port
42
+ end
43
+
44
+ def rackup(&block)
45
+ require "rack"
46
+ app = Rack::Builder.new(&block).to_app
47
+ ShamRack.mount(app, @address, @port)
48
+ end
49
+
50
+ def sinatra(&block)
51
+ require "sinatra/base"
52
+ sinatra_app = Class.new(Sinatra::Base)
53
+ sinatra_app.class_eval(&block)
54
+ ShamRack.mount(sinatra_app.new, @address, @port)
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,50 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{sham_rack}
5
+ s.version = "1.1.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Mike Williams"]
9
+ s.date = %q{2009-06-03}
10
+ s.description = %q{ShamRack plumbs Net::HTTP directly into Rack, for quick and easy HTTP testing.}
11
+ s.email = %q{mdub@dogbiscuit.org}
12
+ s.extra_rdoc_files = [
13
+ "README.markdown"
14
+ ]
15
+ s.files = [
16
+ ".gitignore",
17
+ "CHANGES.markdown",
18
+ "README.markdown",
19
+ "Rakefile",
20
+ "VERSION.yml",
21
+ "lib/sham_rack.rb",
22
+ "lib/sham_rack/core_ext/net/http.rb",
23
+ "lib/sham_rack/http.rb",
24
+ "lib/sham_rack/registry.rb",
25
+ "sham_rack.gemspec",
26
+ "spec/sham_rack_spec.rb",
27
+ "spec/spec_helper.rb"
28
+ ]
29
+ s.has_rdoc = true
30
+ s.homepage = %q{http://github.com/mdub/sham_rack}
31
+ s.rdoc_options = ["--charset=UTF-8"]
32
+ s.require_paths = ["lib"]
33
+ s.rubyforge_project = %q{shamrack}
34
+ s.rubygems_version = %q{1.3.2}
35
+ s.summary = %q{Net::HTTP-to-Rack plumbing}
36
+ s.test_files = [
37
+ "spec/sham_rack_spec.rb",
38
+ "spec/spec_helper.rb"
39
+ ]
40
+
41
+ if s.respond_to? :specification_version then
42
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
43
+ s.specification_version = 3
44
+
45
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
46
+ else
47
+ end
48
+ else
49
+ end
50
+ end
@@ -0,0 +1,226 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ require "sham_rack"
4
+ require "open-uri"
5
+ require "restclient"
6
+ require "rack"
7
+
8
+ class PlainTextApp
9
+
10
+ def call(env)
11
+ [
12
+ "200 OK",
13
+ { "Content-Type" => "text/plain", "Content-Length" => message.length.to_s },
14
+ [message]
15
+ ]
16
+ end
17
+
18
+ end
19
+
20
+ class SimpleMessageApp < PlainTextApp
21
+
22
+ def initialize(message)
23
+ @message = message
24
+ end
25
+
26
+ attr_reader :message
27
+
28
+ end
29
+
30
+ class EnvRecordingApp < PlainTextApp
31
+
32
+ def call(env)
33
+ @last_env = env
34
+ super
35
+ end
36
+
37
+ attr_reader :last_env
38
+
39
+ def message
40
+ "env stored for later perusal"
41
+ end
42
+
43
+ end
44
+
45
+ class UpcaseBody
46
+
47
+ def initialize(app)
48
+ @app = app
49
+ end
50
+
51
+ def call(env)
52
+ status, headers, body = @app.call(env)
53
+ upcased_body = Array(body).map { |x| x.upcase }
54
+ [status, headers, upcased_body]
55
+ end
56
+
57
+ end
58
+
59
+ describe ShamRack do
60
+
61
+ after(:each) do
62
+ ShamRack.unmount_all
63
+ end
64
+
65
+ describe "mounted Rack application" do
66
+
67
+ before(:each) do
68
+ ShamRack.mount(SimpleMessageApp.new("Hello, world"), "www.test.xyz")
69
+ end
70
+
71
+ it "can be accessed using Net::HTTP" do
72
+ response = Net::HTTP.start("www.test.xyz") do |http|
73
+ http.request(Net::HTTP::Get.new("/"))
74
+ end
75
+ response.body.should == "Hello, world"
76
+ end
77
+
78
+ it "can be accessed using open-uri" do
79
+ response = open("http://www.test.xyz")
80
+ response.status.should == ["200", "OK"]
81
+ response.read.should == "Hello, world"
82
+ end
83
+
84
+ it "can be accessed using RestClient" do
85
+ response = RestClient.get("http://www.test.xyz")
86
+ response.code.should == 200
87
+ response.to_s.should == "Hello, world"
88
+ end
89
+
90
+ end
91
+
92
+ describe "#at" do
93
+
94
+ describe "with a block" do
95
+
96
+ it "mounts associated block as an app" do
97
+
98
+ ShamRack.at("simple.xyz") do |env|
99
+ ["200 OK", { "Content-type" => "text/plain" }, "Easy, huh?"]
100
+ end
101
+
102
+ open("http://simple.xyz").read.should == "Easy, huh?"
103
+
104
+ end
105
+
106
+ end
107
+
108
+ describe "#rackup" do
109
+
110
+ before do
111
+ @return_value = ShamRack.at("rackup.xyz").rackup do
112
+ use UpcaseBody
113
+ run SimpleMessageApp.new("Racked!")
114
+ end
115
+ end
116
+
117
+ it "mounts an app created using Rack::Builder" do
118
+ open("http://rackup.xyz").read.should == "RACKED!"
119
+ end
120
+
121
+ it "returns the app" do
122
+ @return_value.should respond_to(:call)
123
+ end
124
+
125
+ end
126
+
127
+ describe "#sinatra" do
128
+
129
+ before do
130
+ @return_value = ShamRack.at("sinatra.xyz").sinatra do
131
+ get "/hello/:subject" do
132
+ "Hello, #{params[:subject]}"
133
+ end
134
+ end
135
+ end
136
+
137
+ it "mounts associated block as a Sinatra app" do
138
+ open("http://sinatra.xyz/hello/stranger").read.should == "Hello, stranger"
139
+ end
140
+
141
+ it "returns the app" do
142
+ @return_value.should respond_to(:call)
143
+ end
144
+
145
+ end
146
+
147
+ end
148
+
149
+ describe "Rack environment" do
150
+
151
+ before(:each) do
152
+ @env_recorder = recorder = EnvRecordingApp.new
153
+ ShamRack.at("env.xyz").rackup do
154
+ use Rack::Lint
155
+ run recorder
156
+ end
157
+ end
158
+
159
+ def env
160
+ @env_recorder.last_env
161
+ end
162
+
163
+ it "is valid" do
164
+
165
+ open("http://env.xyz/blah?q=abc")
166
+
167
+ env["REQUEST_METHOD"].should == "GET"
168
+ env["SCRIPT_NAME"].should == ""
169
+ env["PATH_INFO"].should == "/blah"
170
+ env["QUERY_STRING"].should == "q=abc"
171
+ env["SERVER_NAME"].should == "env.xyz"
172
+ env["SERVER_PORT"].should == "80"
173
+
174
+ env["rack.version"].should == [0,1]
175
+ env["rack.url_scheme"].should == "http"
176
+
177
+ env["rack.multithread"].should == true
178
+ env["rack.multiprocess"].should == true
179
+ env["rack.run_once"].should == false
180
+
181
+ end
182
+
183
+ it "provides request headers" do
184
+
185
+ Net::HTTP.start("env.xyz") do |http|
186
+ request = Net::HTTP::Get.new("/")
187
+ request["Foo-bar"] = "baz"
188
+ http.request(request)
189
+ end
190
+
191
+ env["HTTP_FOO_BAR"].should == "baz"
192
+
193
+ end
194
+
195
+ it "supports POST" do
196
+
197
+ RestClient.post("http://env.xyz/resource", "q" => "rack")
198
+
199
+ env["REQUEST_METHOD"].should == "POST"
200
+ env["CONTENT_TYPE"].should == "application/x-www-form-urlencoded"
201
+ env["rack.input"].read.should == "q=rack"
202
+
203
+ end
204
+
205
+ it "supports PUT" do
206
+
207
+ RestClient.put("http://env.xyz/thing1", "stuff", :content_type => "text/plain")
208
+
209
+ env["REQUEST_METHOD"].should == "PUT"
210
+ env["CONTENT_TYPE"].should == "text/plain"
211
+ env["rack.input"].read.should == "stuff"
212
+
213
+ end
214
+
215
+ it "supports DELETE" do
216
+
217
+ RestClient.delete("http://env.xyz/thing/1")
218
+
219
+ env["REQUEST_METHOD"].should == "DELETE"
220
+ env["PATH_INFO"].should == "/thing/1"
221
+
222
+ end
223
+
224
+ end
225
+
226
+ end
@@ -0,0 +1,12 @@
1
+ require "rubygems"
2
+ require "spec"
3
+ require "rr"
4
+ require "ruby-debug"
5
+
6
+ project_root = File.expand_path("#{__FILE__}/../..")
7
+ $LOAD_PATH << "#{project_root}/lib"
8
+ $LOAD_PATH << "#{project_root}/spec/support/lib"
9
+
10
+ Spec::Runner.configure do |config|
11
+ config.mock_with RR::Adapters::Rspec
12
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sham_rack
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Mike Williams
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-03 00:00:00 +10:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: ShamRack plumbs Net::HTTP directly into Rack, for quick and easy HTTP testing.
17
+ email: mdub@dogbiscuit.org
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.markdown
24
+ files:
25
+ - .gitignore
26
+ - CHANGES.markdown
27
+ - README.markdown
28
+ - Rakefile
29
+ - VERSION.yml
30
+ - lib/sham_rack.rb
31
+ - lib/sham_rack/core_ext/net/http.rb
32
+ - lib/sham_rack/http.rb
33
+ - lib/sham_rack/registry.rb
34
+ - sham_rack.gemspec
35
+ - spec/sham_rack_spec.rb
36
+ - spec/spec_helper.rb
37
+ has_rdoc: true
38
+ homepage: http://github.com/mdub/sham_rack
39
+ licenses: []
40
+
41
+ post_install_message:
42
+ rdoc_options:
43
+ - --charset=UTF-8
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ requirements: []
59
+
60
+ rubyforge_project: shamrack
61
+ rubygems_version: 1.3.2
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: Net::HTTP-to-Rack plumbing
65
+ test_files:
66
+ - spec/sham_rack_spec.rb
67
+ - spec/spec_helper.rb