map_restfully 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +9 -0
- data/README.rdoc +135 -0
- data/Rakefile +35 -0
- data/VERSION +1 -0
- data/lib/map_restfully.rb +17 -0
- data/test/helper.rb +69 -0
- data/test/test_rack.rb +54 -0
- data/test/test_routes.rb +23 -0
- metadata +124 -0
data/Gemfile
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
= map_restfully
|
2
|
+
|
3
|
+
The map_restfully plugin creates a more RESTful controller than the default in Rails. This facilitates
|
4
|
+
applications that are much closer to the architectural style REST, which leads to performance and convention
|
5
|
+
advantages.
|
6
|
+
|
7
|
+
== Example
|
8
|
+
|
9
|
+
In your routes.rb file, simply add:
|
10
|
+
|
11
|
+
map_restfully :chair
|
12
|
+
|
13
|
+
where 'chair' is the name of your resource.
|
14
|
+
|
15
|
+
This will automatically provide routes and named path helpers for GETS, POSTS, PUTS, DELETES, GET, POST, PUT, DELETE.
|
16
|
+
The grammatical number corresponds to the url generated and the path helpers.
|
17
|
+
|
18
|
+
Using the above example, we have the following.
|
19
|
+
*convenience helper* \*example paths* \*controller method*
|
20
|
+
chair_path(@chair) \chair, chair.html, chair/1, chair/1.html \ChairsController#get
|
21
|
+
chair_path(@chair, :method => :post) \chair, chair.xml, chair/1, chair/1.xml \ChairsController#post
|
22
|
+
chair_path(@chair, :method => :put) \chair, chair.js, chair/1, chair/1.js \ChairsController#put
|
23
|
+
chair_path(@chair, :method => :delete) \chair, chair.html, chair/1, chair/1.html \ChairsController#delete
|
24
|
+
chairs_path(@chairs) \chairs, chairs.html, chairs/1,2,3, chairs/1,2,3.html \ChairsController#gets
|
25
|
+
chairs_path(@chairs, :method => :post) \chairs, chairs.xml, chairs/1,2,3, chairs/1,2,3.xml \ChairsController#posts
|
26
|
+
chairs_path(@chairs, :method => :put) \chairs, chairs.js, chairs/1,2,3, chairs/1,2,3.js \ChairsController#puts
|
27
|
+
chairs_path(@chairs, :method => :delete) \chairs, chairs.html, chairs/1,2,3, chairs/1,2,3.html \ChairsController#deletes
|
28
|
+
Note the ‘s’ on the end of the controller method names in the plural case. The above are only examples. The paths take
|
29
|
+
the usual formats .xml, .js, etc. The id number in the singular case and the ids in the plural case are optional.
|
30
|
+
|
31
|
+
|
32
|
+
== Rationale
|
33
|
+
|
34
|
+
When Rails began flirting with ‘RESTful’ resources, there wasn’t much RESTful about Rails’ implementation other than
|
35
|
+
an added conversion layer between REST verbs and the default Rails actions. Those actions are Index (formerly List),
|
36
|
+
Show, New, Edit, Create, Update, and Destroy. We take the position that this added conversion layer is both
|
37
|
+
unnecessary and counter-productive.
|
38
|
+
|
39
|
+
We have demonstrated that this conversion layer, the current implementation of map.resource, is unnecessary
|
40
|
+
in this plugin. We suggest that the current implementation in Rails is counter-productive, for the reasons elaborated below.
|
41
|
+
|
42
|
+
== Breaking the Origin Barrier
|
43
|
+
|
44
|
+
RESTful architecture provides scalability and fault-tolerance in a network application. A default Rails
|
45
|
+
implementation overrides this with the response header “Cach-Control: private, max-age=0, must-revalidate.”
|
46
|
+
This tells every node between the server and the client that every request requires the entire page
|
47
|
+
to be sent on each request.
|
48
|
+
|
49
|
+
Imagine a situation where the index page to your site fairly static. Rather than sending the header above,
|
50
|
+
the server responds to the root request with “Cach-Control: max-age=28800.” Boom. What was that sound?
|
51
|
+
That was sound of your index page being served so fast that it moved faster than your server can spit
|
52
|
+
the bits out. Let’s call it ‘Breaking the Origin Barrier.’
|
53
|
+
|
54
|
+
Your Rails web site now responds much faster, because the client web browser knows not to bother the
|
55
|
+
origin server if 8 hours haven’t passed since the last time you looked at the index page. Client-side
|
56
|
+
caching is the best possible performance solution in applications that transfer state representations
|
57
|
+
across a network.
|
58
|
+
|
59
|
+
The next-best performance solution is if an intermediate server caches the web page. You now have that
|
60
|
+
advantage as well. Any caching servers set up between the origin server and the clients requesting that
|
61
|
+
resource (the index page) won’t bother passing the request along and waiting for a response. Instead,
|
62
|
+
they will simply and quickly reply with the cached page.
|
63
|
+
|
64
|
+
As you can see, this could drastically decrease your server load and bandwidth needs. This is how HTTP
|
65
|
+
was designed to work. This is one benefit of using a RESTful approach.
|
66
|
+
|
67
|
+
To get this caching benefit, you could add ‘expires_in’ to the example above:
|
68
|
+
|
69
|
+
class ChairsController < ApplicationController
|
70
|
+
def gets
|
71
|
+
…code…
|
72
|
+
expires_in 8.hours, :private => false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
(Gets is the method that corresponds to the Index action in traditional Rails apps.)
|
77
|
+
|
78
|
+
== Tips
|
79
|
+
|
80
|
+
Rails typically treated Show, New, and Edit actions as get requests. In our experience, we found that
|
81
|
+
the edit view was always redundant with either the Show or the New view, so we discard it. In many cases,
|
82
|
+
we find that a properly scoped controller would have a form behind HTTP_AUTH (the RESTful alternative to
|
83
|
+
sessions). We also found that the form helpers obviate the New view, since we can just call
|
84
|
+
@record.new_record? in the view if we need to distinguish. So rather than maintain show(), edit(),
|
85
|
+
and new(), we recommend combining edit() and new() into one get.html.* form.
|
86
|
+
|
87
|
+
You can distinguish the editable representation of a resource either by the subject of the RESTful
|
88
|
+
sentence (normally the logged-in user), or by another parameter (like appending ?edit=true to the url).
|
89
|
+
In the former case we recommend putting the logged-in views and controllers under a separate namespace
|
90
|
+
in order to make it convenient to disable the caching for those GET requests. In the latter case you
|
91
|
+
can render a different template based on the presence of the edit parameter.
|
92
|
+
|
93
|
+
In typical ActiveRecord implementations, the id starts with the integer 1 and increments with each
|
94
|
+
record created. We use the id number 0 to represent a new ActiveRecord instance. Using that convention,
|
95
|
+
we can obviate the separate view and controller method for New.
|
96
|
+
|
97
|
+
== Contributing to map_restfully
|
98
|
+
|
99
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
100
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
101
|
+
* Fork the project
|
102
|
+
* Start a feature/bugfix branch
|
103
|
+
* Commit and push until you are happy with your contribution
|
104
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
105
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
106
|
+
|
107
|
+
== Turn Off Session
|
108
|
+
|
109
|
+
If you adhere to strick RESTful principles, you can separate your application controller by the subject
|
110
|
+
of the request: “I get the book…” or “Anonymous gets the book…” In this example, you can see that the
|
111
|
+
former case has the subject “I,” which is sometimes stored in the session cookie. You will get a
|
112
|
+
significant performance boost by turning sessions off. If you cannot identify the subject via a URL
|
113
|
+
parameter, then you are probably dealing with an issue of authentication. Consider using HTTP
|
114
|
+
authentication. This will populate your parameters with a ‘user’ and ‘password’ on every request,
|
115
|
+
which is automatically handled by the client web browser.
|
116
|
+
|
117
|
+
To turn off sessions, change the following default line in config/environment.rb from:
|
118
|
+
|
119
|
+
config.action_controller.session = {
|
120
|
+
:session_key => '_dummy_session',
|
121
|
+
:secret => 'someveryveryverylooooooooongrandomstring'
|
122
|
+
}
|
123
|
+
|
124
|
+
to:
|
125
|
+
|
126
|
+
config.action_controller.session = false
|
127
|
+
|
128
|
+
And remove the following line in ‘app/controllers/application.rb’:
|
129
|
+
|
130
|
+
protect_from_forgery #:secret => 'somerandomstring
|
131
|
+
|
132
|
+
== Copyright
|
133
|
+
|
134
|
+
Copyright (c) 2011 CLR. See LICENSE.txt for
|
135
|
+
further details.
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
require 'jeweler'
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
|
+
gem.name = "map_restfully"
|
16
|
+
gem.homepage = "http://github.com/clr/map_restfully"
|
17
|
+
gem.license = "MIT"
|
18
|
+
gem.summary = %Q{Provide HTTP-verb style routing to Rails.}
|
19
|
+
gem.description = %Q{Convenience method to provide controller actions that correspond to the HTTP verbs.}
|
20
|
+
gem.email = "clr@port49.com"
|
21
|
+
gem.authors = ["CLR"]
|
22
|
+
gem.add_dependency 'rails', '~> 3.0.10'
|
23
|
+
gem.add_dependency 'rack-test'
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
task :default => :test
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.5.0
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ActionDispatch
|
2
|
+
module Routing
|
3
|
+
class Mapper
|
4
|
+
module MapRestfully
|
5
|
+
def map_restfully(resource_name, options={})
|
6
|
+
singular = resource_name.to_s
|
7
|
+
plural = options[:plural] || singular.pluralize
|
8
|
+
%w(get post put delete).each do |verb|
|
9
|
+
match({"#{singular}(/:id(.:format))" => "#{plural}##{verb}", :via => verb, :as => singular}.merge(options))
|
10
|
+
match "#{plural}(/:ids(.:format))" => "#{plural}##{verb}s", :via => verb, :as => plural
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
include MapRestfully
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
# Set up gems listed in the Gemfile.
|
3
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
|
4
|
+
require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
|
5
|
+
|
6
|
+
require "action_controller/railtie"
|
7
|
+
require "rails/test_unit/railtie"
|
8
|
+
require 'rails/test_help'
|
9
|
+
require File.expand_path('../../lib/map_restfully', __FILE__)
|
10
|
+
|
11
|
+
module Test
|
12
|
+
class Application < Rails::Application
|
13
|
+
end
|
14
|
+
end
|
15
|
+
Test::Application.config.secret_token = ("testingframework" * 2)
|
16
|
+
|
17
|
+
def app
|
18
|
+
Test::Application.routes.draw do
|
19
|
+
map_restfully :chair
|
20
|
+
match("/url" => "controller#action", :as => :nothing)
|
21
|
+
end
|
22
|
+
Test::Application
|
23
|
+
end
|
24
|
+
|
25
|
+
# helper for a common assertion
|
26
|
+
def assert_ok
|
27
|
+
assert last_response.ok?, "Response was not ok: #{last_response.status}"
|
28
|
+
end
|
29
|
+
|
30
|
+
# helper for looping through the HTTP verbs
|
31
|
+
def each_http_verb(&block)
|
32
|
+
%w(GET POST PUT DELETE).each do |verb|
|
33
|
+
yield verb
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class ChairsController < ActionController::Base
|
38
|
+
include Test::Application.routes.url_helpers
|
39
|
+
|
40
|
+
# singular actions
|
41
|
+
each_http_verb do |verb|
|
42
|
+
define_method verb.downcase.to_sym do
|
43
|
+
response = params[:id].nil? ? "new chair" : "chair number #{params[:id]}"
|
44
|
+
respond_to do |format|
|
45
|
+
format.html{render :text => "HTML #{verb} #{response}"}
|
46
|
+
format.js{ render :text => "JSON #{verb} #{response}"}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
# plural actions
|
51
|
+
each_http_verb do |verb|
|
52
|
+
define_method "#{verb.downcase}s".to_sym do
|
53
|
+
response = params[:ids].nil? ? "all chairs" : "chairs numbered #{params[:ids].split(',').join(" and ")}"
|
54
|
+
respond_to do |format|
|
55
|
+
format.html{render :text => "HTML #{verb}S #{response}"}
|
56
|
+
format.js{ render :text => "JSON #{verb}S #{response}"}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class ActiveSupport::TestCase
|
63
|
+
# Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
|
64
|
+
#
|
65
|
+
# Note: You'll currently still have to declare fixtures explicitly in integration tests
|
66
|
+
# -- they do not yet inherit this setting
|
67
|
+
|
68
|
+
# Add more helper methods to be used by all tests here...
|
69
|
+
end
|
data/test/test_rack.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
|
+
require 'rack/test'
|
3
|
+
|
4
|
+
class TestRack < ActiveSupport::TestCase
|
5
|
+
include Rack::Test::Methods
|
6
|
+
|
7
|
+
each_http_verb do |verb|
|
8
|
+
test "#{verb} request to singular resource" do
|
9
|
+
self.send(verb.downcase.to_sym, "/chair")
|
10
|
+
assert_ok
|
11
|
+
assert_equal "HTML #{verb} new chair", last_response.body
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
each_http_verb do |verb|
|
16
|
+
test "#{verb} request to singular resource by resources ID" do
|
17
|
+
self.send(verb.downcase.to_sym, "/chair/42")
|
18
|
+
assert_ok
|
19
|
+
assert_equal "HTML #{verb} chair number 42", last_response.body
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
each_http_verb do |verb|
|
24
|
+
test "#{verb} request to singular resource by resources ID in another format" do
|
25
|
+
self.send(verb.downcase.to_sym, "/chair/42.js")
|
26
|
+
assert_ok
|
27
|
+
assert_equal "JSON #{verb} chair number 42", last_response.body
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
each_http_verb do |verb|
|
32
|
+
test "#{verb} request to plural resource" do
|
33
|
+
self.send(verb.downcase.to_sym, "/chairs")
|
34
|
+
assert_ok
|
35
|
+
assert_equal "HTML #{verb}S all chairs", last_response.body
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
each_http_verb do |verb|
|
40
|
+
test "#{verb} request to plural resource by resources IDs" do
|
41
|
+
self.send(verb.downcase.to_sym, "/chairs/42,37")
|
42
|
+
assert_ok
|
43
|
+
assert_equal "HTML #{verb}S chairs numbered 42 and 37", last_response.body
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
each_http_verb do |verb|
|
48
|
+
test "#{verb} request to plural resource by resources IDs in another format" do
|
49
|
+
self.send(verb.downcase.to_sym, "/chairs/42,37.js")
|
50
|
+
assert_ok
|
51
|
+
assert_equal "JSON #{verb}S chairs numbered 42 and 37", last_response.body
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/test/test_routes.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
|
+
|
3
|
+
class TestRoutes < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
test "that a named routes exist for singular and plural resource" do
|
6
|
+
assert app.routes.named_routes[:chair].is_a?(ActionDispatch::Routing::Route), "Singular named route not found."
|
7
|
+
assert app.routes.named_routes[:chairs].is_a?(ActionDispatch::Routing::Route), "Plural named route not found."
|
8
|
+
end
|
9
|
+
|
10
|
+
each_http_verb do |verb|
|
11
|
+
test "that a map exists in the router for #{verb} resource singular" do
|
12
|
+
assert saved_route = app.routes.routes.detect{|r| r.verb == verb && r.path == "/chair(/:id(.:format))"}, "Route not found for #{verb} resource"
|
13
|
+
assert_equal({:controller => 'chairs', :action => verb.downcase}, saved_route.requirements)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
each_http_verb do |verb|
|
18
|
+
test "that a map exists in the router for #{verb} resource plural" do
|
19
|
+
assert saved_route = app.routes.routes.detect{|r| r.verb == verb && r.path == "/chairs(/:ids(.:format))"}, "Route not found for #{verb} resource"
|
20
|
+
assert_equal({:controller => 'chairs', :action => "#{verb.downcase}s"}, saved_route.requirements)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: map_restfully
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- CLR
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-10-19 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: &18942540 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.0.10
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *18942540
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rack-test
|
27
|
+
requirement: &18941980 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *18941980
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: bundler
|
38
|
+
requirement: &18941480 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.0.0
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *18941480
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: jeweler
|
49
|
+
requirement: &18940980 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.6.4
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *18940980
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: rails
|
60
|
+
requirement: &18940480 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ~>
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: 3.0.10
|
66
|
+
type: :runtime
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *18940480
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rack-test
|
71
|
+
requirement: &18939960 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :runtime
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *18939960
|
80
|
+
description: Convenience method to provide controller actions that correspond to the
|
81
|
+
HTTP verbs.
|
82
|
+
email: clr@port49.com
|
83
|
+
executables: []
|
84
|
+
extensions: []
|
85
|
+
extra_rdoc_files:
|
86
|
+
- README.rdoc
|
87
|
+
files:
|
88
|
+
- Gemfile
|
89
|
+
- README.rdoc
|
90
|
+
- Rakefile
|
91
|
+
- VERSION
|
92
|
+
- lib/map_restfully.rb
|
93
|
+
- test/helper.rb
|
94
|
+
- test/test_rack.rb
|
95
|
+
- test/test_routes.rb
|
96
|
+
homepage: http://github.com/clr/map_restfully
|
97
|
+
licenses:
|
98
|
+
- MIT
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options: []
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ! '>='
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
segments:
|
110
|
+
- 0
|
111
|
+
hash: 4251634958389231380
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
requirements: []
|
119
|
+
rubyforge_project:
|
120
|
+
rubygems_version: 1.8.10
|
121
|
+
signing_key:
|
122
|
+
specification_version: 3
|
123
|
+
summary: Provide HTTP-verb style routing to Rails.
|
124
|
+
test_files: []
|