optionsful2 0.2.4

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,15 @@
1
+ Copyright (c) 2010 Marco Antonio Gonzalez Junior (kayaman@baurets.net).
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation
5
+ the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
6
+ and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7
+
8
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions
9
+ of the Software.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
12
+ TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
13
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
14
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
15
+ DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,91 @@
1
+ h1. *Optionsful*
2
+
3
+ Provide HTTP OPTIONS support for the Ruby on Rails framework.
4
+
5
+ * Note for the impatient: installation instructions below.
6
+ * Note for the dummies: change host names and paths properly.
7
+ * Note for the unfaithful: run @rake routes@ to validate the results.
8
+
9
+ h2. "-No! No! No! *-Show me the code!*"
10
+
11
+ h3. Retrieving an HTTP OPTIONS request via telnet:
12
+
13
+ <pre>
14
+
15
+ $ telnet localhost 3000
16
+ OPTIONS /posts HTTP/1.1
17
+ Host: http://localhost:3000
18
+
19
+ HTTP/1.1 204 No Content
20
+ Allow: GET, POST
21
+ Connection: close
22
+ Date: Thu, 22 Jul 2010 17:20:27 GMT
23
+ Link: "<http://localhost:3000/optionsful/posts>; type=text/html; rel=help"
24
+
25
+ OPTIONS /posts/1 HTTP/1.1
26
+ Host: http://localhost:3000
27
+
28
+ HTTP/1.1 204 No Content
29
+ Allow: GET, PUT, DELETE
30
+ Connection: close
31
+ Date: Thu, 22 Jul 2010 18:14:24 GMT
32
+ Link: "<http://localhost:3000/optionsful/posts/1/>; type=text/html; rel=help"
33
+
34
+ OPTIONS /posts/1/comments HTTP/1.1
35
+ Host: http://localhost:3000
36
+
37
+ HTTP/1.1 204 No Content
38
+ Allow: GET, POST
39
+ Connection: close
40
+ Date: Thu, 22 Jul 2010 18:12:43 GMT
41
+ Link: "<http://localhost:3000/optionsful/posts/1/comments>; type=text/html; rel=help"
42
+
43
+ </pre>
44
+ ~Note the empty line which is part of the HTTP protocol.~
45
+
46
+ h3. Telnet is the geek way. You would like better to use an HTTP client software. I use "HTTP Client":http://ditchnet.org/httpclient/ on Mac OS X.
47
+
48
+ h2. INSTALLATION:
49
+
50
+ # Change directory to your Ruby on Rails web application,
51
+ # Add gem dependency to @config/environment.rb@:
52
+ <pre>
53
+ config.gem "optionsful"
54
+ </pre>
55
+ # To install it, run @rake gems:install@ or:
56
+ <pre>
57
+ $ gem install optionsful
58
+ </pre>
59
+ # Enjoy! And give feedback! :)
60
+
61
+ h2. Link header WARNING
62
+
63
+ To enable the Link URI on your application, check the "Wisdomful":http://github.com/kayaman/wisdomful project!
64
+
65
+ h3. Link base path (Note: _Work in Progress_)
66
+
67
+ * To change the generated URI base path, install and edit the configuration file:
68
+ ** run @rake optionsful:yml@
69
+ ** edit the 'optionsful.yml' file at your application's 'config' folder.
70
+ * Example:
71
+ <pre>
72
+ development:
73
+ http:
74
+ base_path: /blopts
75
+ </pre>
76
+
77
+ * Note: @Allow@ and @Link@ are expected headers on a response to an HTTP OPTIONS request. I will think about some features to add more flexibility on the @Link@ generation pretty soon.
78
+
79
+ h2. KNOWN ISSUES
80
+ * Rails route recognition still need some work
81
+ * Platform: ruby 1.8.7, rails 2.3.8, rack 1.2.1
82
+
83
+ h2. Get involved
84
+ * Mailing list: http://groups.google.com/group/optionsful
85
+ * Bug tracker : http://kayaman.lighthouseapp.com/projects/56438-optionsful/overview
86
+
87
+ h2. CONTRIBUTORS
88
+ * Me, myself and I, so far.
89
+ * You are welcome, do it. ;-)
90
+
91
+ Copyright (c) 2010 Marco Antonio Gonzalez Junior, kayaman@baurets.net, released under the MIT license.
@@ -0,0 +1,11 @@
1
+ require 'metric_fu'
2
+ require 'rubygems'
3
+ require 'spec/rake/spectask'
4
+
5
+ Spec::Rake::SpecTask.new(:spec) do |t|
6
+ t.spec_files = Dir.glob( File.dirname(__FILE__) + '/spec/**/*_spec.rb' )
7
+ t.spec_opts << '--color --format specdoc'
8
+ t.rcov = true
9
+ end
10
+
11
+ task :default => :spec
@@ -0,0 +1,49 @@
1
+ module Baurets
2
+ module Optionsful
3
+ class Config
4
+
5
+ def initialize(options = {})
6
+ @config = configure_options(options)
7
+ setup
8
+ self
9
+ end
10
+
11
+ def base_path
12
+ @config[:http][:base_path]
13
+ end
14
+
15
+ private
16
+
17
+ def configure_options(options = {})
18
+ default_opts = { :http => { :base_path => "/optionsful"}, :file => "", :environment => "development" }
19
+ conf = {}
20
+ if defined? RAILS_ROOT
21
+ conf = default_opts.merge!({:file => (File.join(RAILS_ROOT, 'config', 'optionsful.yml'))})
22
+ else
23
+ conf = default_opts.merge!({ :http => { :base_path => "/optionsful"} })
24
+ end
25
+ conf = conf.merge!(options) unless options.empty?
26
+ conf
27
+ end
28
+
29
+ def setup
30
+ require "yaml"
31
+ yaml_file = @config[:file]
32
+ begin
33
+ if File.exist? yaml_file
34
+ conf = YAML::load_file(yaml_file)[@config[:environment]].symbolize_keys
35
+ configure(conf) if conf
36
+ end
37
+ rescue
38
+ end
39
+ end
40
+
41
+ def configure(conf)
42
+ @config[:http][:base_path] = conf[:http]["base_path"] if (conf[:http] && conf[:http]["base_path"])
43
+ @config[:file] = conf[:file] if conf[:file]
44
+ @config[:environment] = conf[:environment] if conf[:environment]
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,72 @@
1
+ module Baurets
2
+ module Optionsful
3
+ module Introspections
4
+
5
+ def self.is_part_static?(routes, index, value)
6
+ routes.each do |route|
7
+ return true if route[0][index] == value
8
+ end
9
+ return false
10
+
11
+ end
12
+
13
+
14
+ def self.do_routing_introspection
15
+ returning Array.new do |routes|
16
+ route_requirements = nil
17
+ ActionController::Routing::Routes.named_routes.map.each do |named_route|
18
+ name = named_route[0].to_s
19
+ routes << [[name], ["GET", named_route[1].requirements[:action]], {:controller => named_route[1].requirements[:controller], :action => named_route[1].requirements[:action]}]
20
+ #TODO ANY ?!?
21
+ end
22
+ ActionController::Routing::Routes.routes.each do |route|
23
+ static_path = []
24
+ route.segments.each do |segment|
25
+ route_requirements = route.requirements #TODO validate
26
+ if segment.kind_of?(ActionController::Routing::StaticSegment)
27
+ static_path << segment.value if (segment.respond_to?(:value) && segment.value != "/")
28
+ elsif segment.kind_of?(ActionController::Routing::DynamicSegment)
29
+ static_path << :dynamic unless (segment.respond_to?(:key) && segment.key == :format)
30
+ end
31
+ end
32
+ routes << [static_path, [route.conditions[:method].to_s.upcase, route_requirements[:action]], route_requirements] unless route.conditions[:method].nil?
33
+ end
34
+ end
35
+ end
36
+
37
+ def self.guess_route(routes, path)
38
+ guess = []
39
+ parts = prepare_request_path(path)
40
+ index = 0
41
+ parts.each do |part|
42
+ if is_part_static?(routes, index, part)
43
+ guess << part
44
+ else
45
+ guess << :dynamic
46
+ end
47
+ index += 1
48
+ end
49
+ guess
50
+ end
51
+
52
+ def self.do_the_matches(routes, route_guess)
53
+ allow = ""
54
+ routes.each do |route|
55
+ if route.first == route_guess
56
+ allow += (route[1][0].to_s.upcase + "|") unless allow.include?(route[1][0].to_s.upcase)
57
+ end
58
+ end
59
+ allow = allow.split("|").join(", ")
60
+ end
61
+
62
+ def self.prepare_request_path(path)
63
+ path_parts = []
64
+ path = path[0..(path.rindex('.')-1)] if path.include?('.')
65
+ path_parts = path.split("/")
66
+ path_parts.delete("")
67
+ path_parts
68
+ end
69
+
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,52 @@
1
+ module Baurets
2
+ module Optionsful
3
+ class Server
4
+
5
+ def initialize(app)
6
+ @app = app
7
+ @config = ::Baurets::Optionsful::Config.new
8
+ end
9
+
10
+ def call(env)
11
+ unless env["REQUEST_METHOD"] == "OPTIONS"
12
+ @app.call(env)
13
+ else
14
+ extract_options_information(env)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def extract_options_information(env)
21
+ allows = extract_allowed_methods(env)
22
+ if allows.empty?
23
+ [404, {}, "Not found."]
24
+ else
25
+ [204, {"Allow" => allows, "Link" => "\"#{build_help_link(env)}\""}, ""]
26
+ end
27
+ end
28
+
29
+ def extract_allowed_methods(env)
30
+ # do routing introspection:
31
+ routes = ::Baurets::Optionsful::Introspections.do_routing_introspection
32
+ # do request path investigation
33
+ path = env["PATH_INFO"]
34
+ route_guess = ::Baurets::Optionsful::Introspections.guess_route(routes, path)
35
+
36
+ puts "\nPATH: \n #{path}\n"
37
+ puts "\nROUTES: \n #{routes.inspect}\n"
38
+ puts "\nGUESS: \n #{route_guess.inspect}\n"
39
+
40
+ # do the matches:
41
+ allow = ::Baurets::Optionsful::Introspections.do_the_matches(routes, route_guess)
42
+ end
43
+
44
+ def build_help_link(env)
45
+ server_name = env["SERVER_NAME"]
46
+ server_port = env["SERVER_PORT"]
47
+ "<http://#{server_name}:#{server_port}" + @config.base_path + "#{env["PATH_INFO"]}>; type=text/html; rel=help"
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,16 @@
1
+ module Baurets
2
+ module Optionsful
3
+ module Version
4
+
5
+ MAJOR = 0
6
+ MINOR = 2
7
+ TINY = 4
8
+
9
+ def self.to_s
10
+ "#{MAJOR}.#{MINOR}.#{TINY}"
11
+ end
12
+
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__))
2
+
3
+ require 'baurets/optionsful/config'
4
+ require 'baurets/optionsful/introspections'
5
+ require 'baurets/optionsful/server'
6
+
7
+ module Optionsful
8
+
9
+
10
+ end
@@ -0,0 +1,8 @@
1
+ require 'fileutils'
2
+
3
+ namespace :optionsful do
4
+ desc "Install 'config/optionsful.yml'"
5
+ task :yml do
6
+ puts File.dirname(__FILE__)
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ # Adding Optionsful to the Rack middleware stack:
2
+ ActionController::Dispatcher.middleware.use Baurets::Optionsful::Server
3
+
4
+ puts "Gem.searcher.find('optionsful').full_gem_path = #{Gem.searcher.find('optionsful').full_gem_path}"
5
+
6
+ Dir["#{Gem.searcher.find('optionsful').full_gem_path}/**/tasks/*.rake"].each do |ext|
7
+ puts "Loading: #{ext}"
8
+ load ext
9
+ end
@@ -0,0 +1,14 @@
1
+ development:
2
+ introspection: enabled
3
+ http:
4
+ base_path: /blopts
5
+
6
+ test:
7
+ introspection: enabled
8
+ http:
9
+ base_path: /blopts
10
+
11
+ production:
12
+ introspection: enabled
13
+ http:
14
+ base_path: /blopts
@@ -0,0 +1,11 @@
1
+ development:
2
+ http:
3
+ base_path: /blopts
4
+
5
+ test:
6
+ http:
7
+ base_path: /test
8
+
9
+ production:
10
+ http:
11
+ base_path: /blopts
File without changes
@@ -0,0 +1,40 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Baurets::Optionsful::Config do
4
+
5
+ include Rack::Test::Methods
6
+
7
+ context "Config carries specific settings" do
8
+
9
+ describe "try to find the custom configuration file" do
10
+
11
+ it "if the custom configuration file exist, try to load settings from it" do
12
+ config = Baurets::Optionsful::Config.new( {:file => File.join(File.dirname(__FILE__), 'fixtures', 'optionsful.yml'), :environment => "test" })
13
+ config.base_path.should == "/test"
14
+ end
15
+
16
+ it "if the custom configuration file exist and is not valid, keep the default settings" do
17
+ config = Baurets::Optionsful::Config.new( {:file => File.join(File.dirname(__FILE__), 'fixtures', 'optionsful_bug.yml'), :environment => "test" })
18
+ config.base_path.should == "/optionsful"
19
+ end
20
+
21
+ it "if no configuration file is informed or found, do load the default settings" do
22
+ config = Baurets::Optionsful::Config.new
23
+ config.base_path.should == "/optionsful"
24
+ end
25
+
26
+ it "if RAILS_ROOT is defined, look for the custom configuration file on its config folder" do
27
+ Baurets::Optionsful::Config.const_set(:RAILS_ROOT, File.dirname(__FILE__))
28
+ config = Baurets::Optionsful::Config.new
29
+ config.base_path.should == "/optionsful"
30
+ end
31
+
32
+ it "and does not exist, keep default settings" do
33
+ config = Baurets::Optionsful::Config.new( {:file => File.join(File.dirname(__FILE__), 'fixtures', 'optionsful_xxx.yml'), :environment => "test" })
34
+ config.base_path.should == "/optionsful"
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,335 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+
4
+ describe Baurets::Optionsful::Server do
5
+
6
+ include Rack::Test::Methods
7
+
8
+ context "as a Rack middleware" do
9
+
10
+ it "is a Ruby object that responds to call;" do
11
+ assert ::Baurets::Optionsful::Server.new(app).respond_to? :call
12
+ end
13
+
14
+ it "takes exactly one argument, (the environment) and returns an Array;" do
15
+ response = ::Baurets::Optionsful::Server.new(app).call(mock_env({"REQUEST_METHOD" => "OPTIONS", "PATH_INFO" => "/posts"}))
16
+ assert response.kind_of?(Array)
17
+ end
18
+
19
+ it "the returned Array must have exactly three values: the status, the headers and the body;" do
20
+ response = ::Baurets::Optionsful::Server.new(app).call(mock_env({"REQUEST_METHOD" => "OPTIONS", "PATH_INFO" => "/posts"}))
21
+ assert response.size.should == 3
22
+ assert response[0].kind_of? Fixnum
23
+ assert response[1].kind_of? Hash
24
+ assert response[2].kind_of? String
25
+ end
26
+
27
+ before do
28
+ ActionController::Routing::Routes.draw do |map|
29
+ map.resources :posts, :has_many => :comments
30
+ end
31
+ end
32
+
33
+ it "must be nice, acting somewhere on a Rack middleware stack;" do
34
+ response = fake_opts_app.call(mock_env({"REQUEST_METHOD" => "OPTIONS", "PATH_INFO" => "/posts"}))
35
+ assert response.size.should == 3
36
+ assert response[0].kind_of? Fixnum
37
+ assert response[0].should == 204
38
+ assert response[1].kind_of? Hash
39
+ assert response[1]["Allow"]
40
+ end
41
+
42
+ it "must let the request go through the stack, if it has nothing to it!" do
43
+ response = fake_opts_app.call(mock_env({"REQUEST_METHOD" => "GET", "PATH_INFO" => "/lobster"}))
44
+ assert response.size.should == 3
45
+ assert response[0].kind_of? Fixnum
46
+ assert response[0].should == 200
47
+ assert response[1].kind_of? Hash
48
+ end
49
+
50
+ end
51
+
52
+ context "as an interpreter for HTTP OPTIONS requests, MUST recognize the Rails" do
53
+
54
+ describe "default resource routing" do
55
+ #Sample resource route (maps HTTP verbs to controller actions automatically)
56
+
57
+ before(:all) do
58
+ ActionController::Routing::Routes.draw do |map|
59
+ map.resources :posts, :has_many => :comments
60
+ end
61
+ end
62
+
63
+ it "the index action displays a list of all posts in response of a GET request" do
64
+ response = http_options_request("/posts")
65
+ assert response.kind_of?(Array)
66
+ assert allows?(response[1], "GET")
67
+ end
68
+
69
+ it "the new action return from a GET request an HTML form for creating a new post" do
70
+ response = http_options_request("/posts/new")
71
+ assert response.kind_of?(Array)
72
+ assert allows?(response[1], "GET")
73
+ end
74
+
75
+ it "the create action uses POST to create a new post instance" do
76
+ response = http_options_request("/posts")
77
+ assert response.kind_of?(Array)
78
+ assert allows?(response[1], "POST")
79
+ end
80
+
81
+ it "the show action display a specific post in response of a GET request" do
82
+ response = http_options_request("/posts/1")
83
+ assert response.kind_of?(Array)
84
+ assert allows?(response[1], "GET")
85
+ end
86
+
87
+ it "the edit action return an HTML form for editing a post in response of a GET request" do
88
+ response = http_options_request("/posts/1/edit")
89
+ assert response.kind_of?(Array)
90
+ assert allows?(response[1], "GET")
91
+ end
92
+
93
+ it "the update action uses PUT to update a specific post" do
94
+ response = http_options_request("/posts/1")
95
+ assert response.kind_of?(Array)
96
+ assert allows?(response[1], "PUT")
97
+ end
98
+
99
+ it "the destroy action uses DELETE to delete a specific post" do
100
+ response = http_options_request("/posts/1")
101
+ assert response.kind_of?(Array)
102
+ assert allows?(response[1], "DELETE")
103
+ end
104
+
105
+ it "not finding a path, return 404 Not Found" do
106
+ response = http_options_request("/sblingers/sblongers")
107
+ assert response.kind_of?(Array)
108
+ assert response[0].should be 404
109
+ end
110
+
111
+ ##
112
+ # Note that extension relation types are REQUIRED to be absolute URIs
113
+ # in Link headers, and MUST be quoted if they contain a semicolon (";")
114
+ # or comma (",") (as these characters are used as delimiters in the
115
+ # header itself).
116
+ it "the Link header MUST be quoted if it contains a semicolon or comma" do
117
+ response = http_options_request("/posts")
118
+ assert response.kind_of?(Array)
119
+ link = response[1]["Link"]
120
+ assert link.should =~ /\A"{1}.+"\z/
121
+ end
122
+
123
+ end
124
+
125
+ describe "named routes" do
126
+
127
+ before(:each) do
128
+ ActionController::Routing::Routes.draw do |map|
129
+ map.login 'login', :controller => 'accounts', :action => 'login'
130
+ end
131
+ end
132
+
133
+ it "should work" do
134
+ response = http_options_request("/login")
135
+ assert response.kind_of?(Array)
136
+ assert response[0].should be 204
137
+ assert allows?(response[1], "GET") # WTF? return ANY?!? ;p
138
+ end
139
+
140
+ after(:all) do
141
+ ActionController::Routing::Routes.reload!
142
+ end
143
+
144
+ end
145
+
146
+ describe "route globbing" do
147
+
148
+ before(:all) do
149
+ ActionController::Routing::Routes.draw do |map|
150
+ map.connect '*path' , :controller => 'blog' , :action => 'unrecognized?'
151
+ end
152
+ end
153
+
154
+ it "should work" do
155
+ pending "What the hell is that?"
156
+ response = http_options_request("/post/path")
157
+ assert response.kind_of?(Array)
158
+ assert response[0].should_not be 404
159
+ end
160
+
161
+ after(:all) do
162
+ ActionController::Routing::Routes.reload!
163
+ end
164
+
165
+ end
166
+
167
+ describe "route conditions" do
168
+
169
+ before(:all) do
170
+ ActionController::Routing::Routes.draw do |map|
171
+ map.connect 'post/:id', :controller => 'posts', :action => 'show', :conditions => { :method => :get }
172
+ map.connect 'post/:id', :controller => 'posts', :action => 'create_comment', :conditions => { :method => :post }
173
+ end
174
+ end
175
+
176
+ it "should work" do
177
+ response = http_options_request("/post/123")
178
+ assert response.kind_of?(Array)
179
+ assert allows?(response[1], "GET")
180
+ assert allows?(response[1], "POST")
181
+ end
182
+
183
+ after(:all) do
184
+ ActionController::Routing::Routes.reload!
185
+ end
186
+ end
187
+
188
+ describe "pretty URLs" do
189
+
190
+ before(:all) do
191
+ ActionController::Routing::Routes.draw do |map|
192
+ map.connect "articles/:year/:month/:day", :controller => 'articles', :action => 'find_by_date', :requirements => { :year => /\d{4}/, :month => /\d{1,2}/, :day => /\d{1,2}/ }
193
+ end
194
+ end
195
+
196
+ it "should work" do
197
+ response = http_options_request("/articles/2010/07/23")
198
+ assert response.kind_of?(Array)
199
+ assert response[0].should be 204
200
+ end
201
+
202
+ after(:all) do
203
+ ActionController::Routing::Routes.reload!
204
+ end
205
+
206
+ end
207
+
208
+ describe "regular expressions and parameters" do
209
+
210
+ before(:all) do
211
+ ActionController::Routing::Routes.draw do |map|
212
+ map.geocode 'geocode/:postalcode', :controller => 'geocode', :action => 'show', :requirements => { :postalcode => /\d{5}(-\d{4})?/ }
213
+ end
214
+ end
215
+
216
+ it "should work" do
217
+ response = http_options_request("/geocode/20100")
218
+ assert response.kind_of?(Array)
219
+ assert response[0].should be 204
220
+ assert allows?(response[1], "GET")
221
+ end
222
+
223
+ after(:all) do
224
+ ActionController::Routing::Routes.reload!
225
+ end
226
+ end
227
+
228
+ describe "resource route within a namespace" do
229
+
230
+ before(:all) do
231
+ ActionController::Routing::Routes.draw do |map|
232
+ map.namespace :admin do |admin|
233
+ # Directs /admin/posts/* to Admin::PostsController (app/controllers/admin/posts_controller.rb)
234
+ admin.resources :posts
235
+ end
236
+ end
237
+ end
238
+
239
+ it "must understand an namespaced path" do
240
+ response = http_options_request("/admin/posts")
241
+ assert response.kind_of?(Array)
242
+ assert response[0].should be 204
243
+ assert allows?(response[1], "GET")
244
+ assert allows?(response[1], "POST")
245
+ end
246
+
247
+ after(:all) do
248
+ ActionController::Routing::Routes.reload!
249
+ end
250
+
251
+ end
252
+
253
+ describe "deal the catch all" do
254
+
255
+ before(:all) do
256
+ ActionController::Routing::Routes.draw do |map|
257
+ map.resources :posts
258
+ map.connect ':controller/:action/:id'
259
+ end
260
+ end
261
+
262
+ it "must cheat :P" do
263
+ response = http_options_request("/posts")
264
+ assert response.kind_of?(Array)
265
+ assert response[0].should be 204
266
+ assert allows?(response[1], "GET")
267
+ assert allows?(response[1], "POST")
268
+ end
269
+
270
+ after(:all) do
271
+ ActionController::Routing::Routes.reload!
272
+ end
273
+
274
+ end
275
+
276
+ describe "another sample of named route" do
277
+ before(:all) do
278
+ ActionController::Routing::Routes.draw do |map|
279
+ map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase'
280
+ end
281
+ end
282
+
283
+ it "should work" do
284
+ response = http_options_request("/products/123/purchase")
285
+ assert response.kind_of?(Array)
286
+ assert response[0].should be 204
287
+ assert allows?(response[1], "ANY")
288
+ end
289
+
290
+ after(:all) do
291
+ ActionController::Routing::Routes.reload!
292
+ end
293
+
294
+ end
295
+
296
+ describe "Sample resource route with options" do
297
+
298
+ before(:all) do
299
+ ActionController::Routing::Routes.draw do |map|
300
+ map.resources :products, :member => { :short => :get, :toggle => :post }, :collection => { :sold => :get }
301
+ end
302
+ end
303
+
304
+ it "should work" do
305
+ response = http_options_request("/products/123/toggle")
306
+ assert response.kind_of?(Array)
307
+ assert response[0].should be 204
308
+ assert allows?(response[1], "POST")
309
+ end
310
+
311
+ it "should work" do
312
+ response = http_options_request("/products/123/short")
313
+ assert response.kind_of?(Array)
314
+ assert response[0].should be 204
315
+ assert allows?(response[1], "GET")
316
+ end
317
+
318
+ it "should work" do
319
+ response = http_options_request("/products/sold")
320
+ assert response.kind_of?(Array)
321
+ assert response[0].should be 204
322
+ assert allows?(response[1], "GET")
323
+ end
324
+
325
+ after(:all) do
326
+ ActionController::Routing::Routes.reload!
327
+ end
328
+
329
+ end
330
+
331
+ end
332
+
333
+ end
334
+
335
+
@@ -0,0 +1,4 @@
1
+ --color
2
+ --format progress
3
+ --loadby mtime
4
+ --reverse
@@ -0,0 +1,72 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'lib', "baurets", 'optionsful', "config.rb")
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', "baurets", 'optionsful', "server.rb")
3
+ require File.join(File.dirname(__FILE__), '..', 'lib', "baurets", 'optionsful', "introspections.rb")
4
+ # TODO ^ this was ugly?! :-S
5
+
6
+ require 'rubygems'
7
+ require 'sinatra'
8
+ require 'rack/test'
9
+ require 'rack'
10
+ require 'rack/lobster'
11
+ require 'spec'
12
+ require 'spec/autorun'
13
+ require 'spec/interop/test'
14
+ require 'action_controller'
15
+
16
+
17
+ # set test environment
18
+ set :environment, :test
19
+ set :run, false
20
+ set :raise_errors, true
21
+ set :logging, true
22
+
23
+ DEFAULT_ENV = { "rack.version" => Rack::VERSION, "rack.input" => StringIO.new, "rack.errors" => StringIO.new,
24
+ "rack.multithread" => true, "rack.multiprocess" => true, "rack.run_once" => false, }
25
+
26
+ SAMPLE_ENV = {"rack.session"=>{:session_id=>"f66c8af1e89f318775e923159e046e71"}, "SERVER_NAME"=>"localhost",
27
+ "HTTP_HOST"=>"localhost:3332", "REMOTE_HOST"=>"localhost", "HTTP_USER_AGENT"=>"HTTP%20Client0.9.1
28
+ CFNetwork/438.14 Darwin/9.8.0 (i386) (MacBook4%2C1)", "rack.url_scheme"=>"http", "REQUEST_PATH"=>"/",
29
+ "SERVER_PROTOCOL"=>"HTTP/1.1", "rack.errors"=>"", "PATH_INFO"=>"/", "REMOTE_ADDR"=>"127.0.0.1",
30
+ "SERVER_SOFTWARE"=>"WEBrick/1.3.1 (Ruby/1.8.7/2010-06-23)", "rack.run_once"=>false, "rack.version"=>[1, 1],
31
+ "SCRIPT_NAME"=>"", "rack.multithread"=>false, "HTTP_VERSION"=>"HTTP/1.1", "REQUEST_URI"=>"",
32
+ "rack.multiprocess"=>false, "SERVER_PORT"=>"3332", "rack.session.options"=>{:domain=>"nilly", :path=>"/",
33
+ :key=>"_session_id", :httponly=>true, :expire_after=>300, :id=>"f66c8af1e89f318775e923159e046e71"},
34
+ "REQUEST_METHOD"=>"HEAD", "GATEWAY_INTERFACE"=>"CGI/1.1", "QUERY_STRING"=>"", "HTTP_CONNECTION"=>"close"}
35
+
36
+ FAKE_ENV = SAMPLE_ENV.merge!(DEFAULT_ENV)
37
+
38
+ def app
39
+ app = Rack::Builder.new {
40
+ use Rack::CommonLogger
41
+ use Rack::ShowExceptions
42
+ }
43
+ end
44
+
45
+
46
+ def fake_opts_app
47
+ app = Rack::Builder.new {
48
+ use Rack::CommonLogger
49
+ use Rack::ShowExceptions
50
+ use Baurets::Optionsful::Server
51
+ map "/lobster" do
52
+ use Rack::Lint
53
+ run Rack::Lobster.new
54
+ end
55
+ }
56
+ end
57
+
58
+ def mock_env(options = {})
59
+ FAKE_ENV.merge!(options)
60
+ end
61
+
62
+ def http_options_request(path)
63
+ complex_env = mock_env({"REQUEST_METHOD" => "OPTIONS", "PATH_INFO" => path })
64
+ response = Baurets::Optionsful::Server.new(app).call(complex_env)
65
+ end
66
+
67
+ def allows?(headers, method)
68
+ headers["Allow"].include?(method)
69
+ end
70
+
71
+
72
+
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: optionsful2
3
+ version: !ruby/object:Gem::Version
4
+ hash: 31
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 4
10
+ version: 0.2.4
11
+ platform: ruby
12
+ authors:
13
+ - Marco Antonio Gonzalez Junior
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-05 00:00:00 -03:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Help building RESTful web services enabling HTTP OPTIONS verb on Ruby on Rails applications.
23
+ email: kayaman@baurets.net
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - README.textile
32
+ - MIT-LICENSE
33
+ - Rakefile
34
+ - lib/optionsful.rb
35
+ - lib/baurets/optionsful/server.rb
36
+ - lib/baurets/optionsful/introspections.rb
37
+ - lib/baurets/optionsful/config.rb
38
+ - lib/baurets/optionsful/version.rb
39
+ - lib/tasks/optionsful.rake
40
+ - rails/init.rb
41
+ - samples/optionsful.yml
42
+ - spec/fixtures/optionsful.yml
43
+ - spec/fixtures/optionsful_bug.yml
44
+ - spec/optionsful_server_spec.rb
45
+ - spec/optionsful_config_spec.rb
46
+ - spec/spec.opts
47
+ - spec/spec_helper.rb
48
+ has_rdoc: true
49
+ homepage: http://github.com/kayaman/optionsful2
50
+ licenses: []
51
+
52
+ post_install_message:
53
+ rdoc_options: []
54
+
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ hash: 3
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ requirements: []
76
+
77
+ rubyforge_project:
78
+ rubygems_version: 1.3.7
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: Support HTTP OPTIONS verb on your Rails app.
82
+ test_files: []
83
+