mikeycgto-sinatra-rest 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +90 -0
- data/Rakefile +10 -0
- data/lib/sinatra/rest/adapters.rb +53 -0
- data/lib/sinatra/rest/rest.yaml +84 -0
- data/lib/sinatra/rest.rb +209 -0
- data/test/adapters_spec.rb +62 -0
- data/test/call_order_spec.rb +111 -0
- data/test/crud_spec.rb +98 -0
- data/test/helper.rb +160 -0
- data/test/helpers_spec.rb +76 -0
- data/test/inflection_spec.rb +29 -0
- data/test/routes_spec.rb +112 -0
- data/test/test_spec.rb +19 -0
- data/test/views/people/edit.haml +4 -0
- data/test/views/people/index.haml +7 -0
- data/test/views/people/new.haml +4 -0
- data/test/views/people/show.haml +4 -0
- data/test/views/words/edit.haml +4 -0
- data/test/views/words/index.haml +7 -0
- data/test/views/words/new.haml +4 -0
- data/test/views/words/show.haml +4 -0
- metadata +121 -0
data/README.textile
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
h1. Sinatra-REST
|
2
|
+
|
3
|
+
Actually it's a set of templates to introduce RESTful routes in Sinatra. The
|
4
|
+
only thing for you to do is to provide the views. The routes and some
|
5
|
+
url helpers will be provided behind the scenes.
|
6
|
+
|
7
|
+
|
8
|
+
h2. Installation
|
9
|
+
|
10
|
+
Guess what!
|
11
|
+
|
12
|
+
sudo gem source --add http://gems.github.com
|
13
|
+
sudo gem install blindgaenger-sinatra-rest
|
14
|
+
|
15
|
+
|
16
|
+
h2. Usage
|
17
|
+
|
18
|
+
Of course you need to require the gem in your Sinatra application:
|
19
|
+
|
20
|
+
require 'rubygems'
|
21
|
+
require 'sinatra'
|
22
|
+
require 'sinatra/rest'
|
23
|
+
|
24
|
+
It's very similar to defining routes in Sinatra (@get@, @post@, ...). But this
|
25
|
+
time you don't define the routes by yourself, but use the model's name for
|
26
|
+
convention.
|
27
|
+
|
28
|
+
For example, if the model's class is called @Person@ you only need to add this
|
29
|
+
line:
|
30
|
+
|
31
|
+
rest Person
|
32
|
+
|
33
|
+
Which will add the following RESTful routes to your application. (Note the
|
34
|
+
pluralization of @Person@ to the @/people/*@ routes.)
|
35
|
+
|
36
|
+
* GET /people
|
37
|
+
* GET /people/new
|
38
|
+
* POST /people
|
39
|
+
* GET /people/:id
|
40
|
+
* GET /people/:id/edit
|
41
|
+
* PUT /people/:id
|
42
|
+
* DELETE /people/:id
|
43
|
+
|
44
|
+
But the real benefit is, that these *routes define a restful standard behaviour*
|
45
|
+
on your model, *appropriate routing and redirecting* and *named url helpers*.
|
46
|
+
|
47
|
+
For instance, you can imagine the following code to be added for the @/people@
|
48
|
+
and @/people/:id@ routes.
|
49
|
+
|
50
|
+
<pre><code>
|
51
|
+
# simply add this line
|
52
|
+
|
53
|
+
rest Person, :renderer => :erb
|
54
|
+
|
55
|
+
# and this is generated for you
|
56
|
+
|
57
|
+
get '/people' do
|
58
|
+
@people = Person.all
|
59
|
+
erb :"people/index", options
|
60
|
+
end
|
61
|
+
|
62
|
+
put '/people/:id' do
|
63
|
+
@person = Person.find_by_id(params[:id])
|
64
|
+
redirect url_for_people_show(@person), 'person updated'
|
65
|
+
end
|
66
|
+
|
67
|
+
# further restful routes for Person ...
|
68
|
+
</code></pre>
|
69
|
+
|
70
|
+
That's only half the truth! The routes are generated dynamically, so all
|
71
|
+
defaults can be overridden (the behaviour, after/before callbacks, used renderer,
|
72
|
+
which routes are added).
|
73
|
+
|
74
|
+
For more details and options, please have a look at the pages in the
|
75
|
+
"Sinatra-REST Wiki":http://github.com/blindgaenger/sinatra-rest/wikis on Github.
|
76
|
+
|
77
|
+
|
78
|
+
h2. Links
|
79
|
+
|
80
|
+
* "Homepage @ GitHub Pages":http://blindgaenger.github.com/sinatra-rest/
|
81
|
+
* "Source Code @ GitHub":http://blindgaenger.github.com/sinatra-rest/
|
82
|
+
* "Documentation @ rdoc.info":http://rdoc.info/projects/blindgaenger/sinatra-rest
|
83
|
+
* "Continuous Integration @ RunCodeRun":http://runcoderun.com/blindgaenger/sinatra-rest
|
84
|
+
* "Gem hosting @ Gemcutter":http://gemcutter.org/gems/sinatra-rest
|
85
|
+
|
86
|
+
h2. Contact
|
87
|
+
|
88
|
+
You can contact me via mail at blindgaenger at gmail dot com, or leave me a
|
89
|
+
message on my "Github profile":http://github.com/blindgaenger.
|
90
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
module Stone
|
2
|
+
module Resource
|
3
|
+
def find_by_id(id)
|
4
|
+
get(id)
|
5
|
+
end
|
6
|
+
|
7
|
+
def delete(id)
|
8
|
+
model = self.find_by_id(id)
|
9
|
+
model.destroy if model
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module DataMapper
|
15
|
+
|
16
|
+
module Resource
|
17
|
+
def update_attributes(opt)
|
18
|
+
update opt
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module Model
|
23
|
+
def find_by_id(id)
|
24
|
+
get(id)
|
25
|
+
end
|
26
|
+
|
27
|
+
def delete(id)
|
28
|
+
model = self.find_by_id(id)
|
29
|
+
model.destroy if model
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# find throws exceptions...
|
35
|
+
# find_by_id returns nil
|
36
|
+
#
|
37
|
+
# find_by_id should already exist but alas it doesn't
|
38
|
+
# because templates are eval'd before that method is.. (i think)
|
39
|
+
module ActiveRecord
|
40
|
+
class Base
|
41
|
+
class << self
|
42
|
+
def find_by_id(id)
|
43
|
+
begin
|
44
|
+
find(id)
|
45
|
+
rescue Exception
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
@@ -0,0 +1,84 @@
|
|
1
|
+
---
|
2
|
+
:index:
|
3
|
+
:verb: GET
|
4
|
+
:url: /PLURAL
|
5
|
+
:control: |-
|
6
|
+
@PLURAL = call_model_method(MODEL, :where, mp)
|
7
|
+
:render: |-
|
8
|
+
if request.xhr?
|
9
|
+
content_type :json
|
10
|
+
@PLURAL.as_json.to_json
|
11
|
+
else
|
12
|
+
RENDERER :'PLURAL/index', options
|
13
|
+
end
|
14
|
+
|
15
|
+
:new:
|
16
|
+
:verb: GET
|
17
|
+
:url: /PLURAL/new
|
18
|
+
:control: |-
|
19
|
+
@SINGULAR = call_model_method(MODEL, :new, mp)
|
20
|
+
:render: |-
|
21
|
+
RENDERER :'PLURAL/new', options
|
22
|
+
|
23
|
+
:create:
|
24
|
+
:verb: POST
|
25
|
+
:url: /PLURAL
|
26
|
+
:control: |-
|
27
|
+
@SINGULAR = call_model_method(MODEL, :new, mp)
|
28
|
+
@SINGULAR.save
|
29
|
+
:render: |-
|
30
|
+
unless @SINGULAR.invalid?
|
31
|
+
redirect url_for_PLURAL_show(@SINGULAR), 'SINGULAR created'
|
32
|
+
else
|
33
|
+
throw :halt, [500, @SINGULAR.errors.full_messages.join("\n")]
|
34
|
+
end
|
35
|
+
|
36
|
+
:show:
|
37
|
+
:verb: GET
|
38
|
+
:url: /PLURAL/:id
|
39
|
+
:control: |-
|
40
|
+
@SINGULAR = call_model_method(MODEL, :find_by_id, mp[:id])
|
41
|
+
:render: |-
|
42
|
+
if @SINGULAR.nil?
|
43
|
+
throw :halt, [404, 'SINGULAR not found']
|
44
|
+
else
|
45
|
+
if request.xhr?
|
46
|
+
content_type :json
|
47
|
+
@SINGULAR.as_json.to_json
|
48
|
+
else
|
49
|
+
RENDERER :'PLURAL/show', options
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
:edit:
|
54
|
+
:verb: GET
|
55
|
+
:url: /PLURAL/:id/edit
|
56
|
+
:control: |-
|
57
|
+
@SINGULAR = call_model_method(MODEL, :find_by_id, mp[:id])
|
58
|
+
:render: |-
|
59
|
+
RENDERER :'PLURAL/edit', options
|
60
|
+
|
61
|
+
:update:
|
62
|
+
:verb: PUT
|
63
|
+
:url: /PLURAL/:id
|
64
|
+
:control: |-
|
65
|
+
@SINGULAR = call_model_method(MODEL, :find_by_id, mp[:id])
|
66
|
+
@SINGULAR.update_attributes(mp) unless @SINGULAR.nil?
|
67
|
+
:render: |-
|
68
|
+
if @SINGULAR.nil?
|
69
|
+
throw :halt, [404, 'SINGULAR not found']
|
70
|
+
else
|
71
|
+
unless @SINGULAR.invalid?
|
72
|
+
redirect url_for_PLURAL_show(@SINGULAR), 'SINGULAR updated'
|
73
|
+
else
|
74
|
+
throw :halt, [500, @SINGULAR.errors.full_messages.join("\n")]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
:destroy:
|
79
|
+
:verb: DELETE
|
80
|
+
:url: /PLURAL/:id
|
81
|
+
:control: |-
|
82
|
+
call_model_method(MODEL, :delete, mp[:id])
|
83
|
+
:render: |-
|
84
|
+
redirect url_for_PLURAL_index, 'SINGULAR destroyed'
|
data/lib/sinatra/rest.rb
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'english/inflect'
|
3
|
+
|
4
|
+
libdir = File.dirname(__FILE__) + "/rest"
|
5
|
+
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
6
|
+
require 'adapters'
|
7
|
+
require 'yaml'
|
8
|
+
|
9
|
+
module Sinatra
|
10
|
+
|
11
|
+
module REST
|
12
|
+
|
13
|
+
#
|
14
|
+
# adds restful routes and url helpers for the model
|
15
|
+
def rest(model_class, options={}, &block)
|
16
|
+
parse_args(model_class, options)
|
17
|
+
read_config('rest/rest.yaml')
|
18
|
+
|
19
|
+
# register model specific helpers
|
20
|
+
helpers generate_helpers
|
21
|
+
|
22
|
+
# create an own module, to override the template with custom methods
|
23
|
+
# this way, you can still use #super# in the overridden methods
|
24
|
+
controller = generate_controller
|
25
|
+
if block_given?
|
26
|
+
custom = CustomController.new(@plural)
|
27
|
+
custom.instance_eval &block
|
28
|
+
custom.module.send :include, controller
|
29
|
+
controller = custom.module
|
30
|
+
end
|
31
|
+
helpers controller
|
32
|
+
|
33
|
+
# register routes as DSL extension
|
34
|
+
instance_eval generate_routes
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
ROUTES = {
|
40
|
+
:all => [:index, :new, :create, :show, :edit, :update, :destroy],
|
41
|
+
:readable => [:index, :show],
|
42
|
+
:writeable => [:index, :show, :create, :update, :destroy],
|
43
|
+
:editable => [:index, :show, :create, :update, :destroy, :new, :edit],
|
44
|
+
}
|
45
|
+
|
46
|
+
def parse_args(model_class, options)
|
47
|
+
@model, @singular, @plural = conjugate(model_class)
|
48
|
+
@renderer = (options.delete(:renderer) || :haml).to_s
|
49
|
+
@route_flags = parse_routes(options.delete(:routes) || :all)
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse_routes(routes)
|
53
|
+
routes = [*routes].map {|route| ROUTES[route] || route}.flatten.uniq
|
54
|
+
# keep the order of :all routes
|
55
|
+
ROUTES[:all].select{|route| routes.include? route}
|
56
|
+
end
|
57
|
+
|
58
|
+
def read_config(filename)
|
59
|
+
file = File.read(File.join(File.dirname(__FILE__), filename))
|
60
|
+
@config = YAML.load file
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# creates the necessary forms of the model name
|
65
|
+
# pretty much like ActiveSupport's inflections, but don't like to depend on
|
66
|
+
def conjugate(model_class)
|
67
|
+
model = model_class.to_s.match(/(\w+)$/)[0]
|
68
|
+
singular = model.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
|
69
|
+
return model, singular, English::Inflect.plural(singular)
|
70
|
+
end
|
71
|
+
|
72
|
+
def replace_variables(t, route=nil)
|
73
|
+
if route
|
74
|
+
t.gsub!('NAME', route.to_s)
|
75
|
+
t.gsub!('VERB', @config[route][:verb].downcase)
|
76
|
+
t.gsub!('URL', @config[route][:url])
|
77
|
+
t.gsub!('CONTROL', @config[route][:control])
|
78
|
+
t.gsub!('RENDER', @config[route][:render])
|
79
|
+
end
|
80
|
+
t.gsub!(/PLURAL/, @plural)
|
81
|
+
t.gsub!(/SINGULAR/, @singular)
|
82
|
+
t.gsub!(/MODEL/, @model)
|
83
|
+
t.gsub!(/RENDERER/, @renderer)
|
84
|
+
t
|
85
|
+
end
|
86
|
+
|
87
|
+
def generate_routes
|
88
|
+
@route_flags.map{|r| route_template(r)}.join("\n\n")
|
89
|
+
end
|
90
|
+
|
91
|
+
def route_template(route)
|
92
|
+
t = <<-RUBY
|
93
|
+
VERB 'URL' do
|
94
|
+
PLURAL_before :NAME
|
95
|
+
PLURAL_NAME
|
96
|
+
PLURAL_after :NAME
|
97
|
+
RENDER
|
98
|
+
end
|
99
|
+
RUBY
|
100
|
+
replace_variables(t, route)
|
101
|
+
end
|
102
|
+
|
103
|
+
def generate_helpers
|
104
|
+
m = Module.new
|
105
|
+
@route_flags.each {|r|
|
106
|
+
m.module_eval helpers_template(r)
|
107
|
+
}
|
108
|
+
m
|
109
|
+
end
|
110
|
+
|
111
|
+
def helpers_template(route)
|
112
|
+
t = <<-RUBY
|
113
|
+
def url_for_PLURAL_NAME(model = nil)
|
114
|
+
"URL"
|
115
|
+
end
|
116
|
+
RUBY
|
117
|
+
helper_route = @config[route][:url].gsub(':id', '#{escape_model_id(model)}')
|
118
|
+
t.gsub!('URL', helper_route)
|
119
|
+
replace_variables(t, route)
|
120
|
+
end
|
121
|
+
|
122
|
+
def generate_controller
|
123
|
+
m = Module.new
|
124
|
+
t = <<-RUBY
|
125
|
+
def PLURAL_before(name); end
|
126
|
+
def PLURAL_after(name); end
|
127
|
+
RUBY
|
128
|
+
m.module_eval replace_variables(t)
|
129
|
+
|
130
|
+
@route_flags.each {|route|
|
131
|
+
m.module_eval controller_template(route)
|
132
|
+
}
|
133
|
+
m
|
134
|
+
end
|
135
|
+
|
136
|
+
def controller_template(route)
|
137
|
+
t = <<-RUBY
|
138
|
+
def PLURAL_NAME(options=params)
|
139
|
+
mp = filter_model_params(options)
|
140
|
+
CONTROL
|
141
|
+
end
|
142
|
+
RUBY
|
143
|
+
replace_variables(t, route)
|
144
|
+
end
|
145
|
+
|
146
|
+
#
|
147
|
+
# model unspecific helpers, will be included once
|
148
|
+
module Helpers
|
149
|
+
# for example _method will be removed
|
150
|
+
def filter_model_params(params)
|
151
|
+
params.reject {|k, v| k =~ /^(_|session_token)/ }
|
152
|
+
end
|
153
|
+
|
154
|
+
def escape_model_id(model)
|
155
|
+
if model.nil?
|
156
|
+
raise 'can not generate url for nil'
|
157
|
+
elsif model.kind_of?(String)
|
158
|
+
Rack::Utils.escape(model)
|
159
|
+
elsif model.kind_of?(Fixnum)
|
160
|
+
model
|
161
|
+
elsif model.id.kind_of? String
|
162
|
+
Rack::Utils.escape(model.id)
|
163
|
+
else
|
164
|
+
model.id
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def call_model_method(model_class, name, options={})
|
169
|
+
if options.nil? || model_class.method(name).arity == 0
|
170
|
+
Kernel.warn "warning: calling #{model_class.to_s}##{name} with args, although it doesn't take args" if options
|
171
|
+
model_class.send(name)
|
172
|
+
else
|
173
|
+
model_class.send(name, options)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
#
|
179
|
+
# used as context to evaluate the controller's module
|
180
|
+
class CustomController
|
181
|
+
attr_reader :module
|
182
|
+
|
183
|
+
def initialize(prefix)
|
184
|
+
@prefix = prefix
|
185
|
+
@module = Module.new
|
186
|
+
end
|
187
|
+
|
188
|
+
def before(options={}, &block) prefix :before, █ end
|
189
|
+
def after(options={}, &block) prefix :after, █ end
|
190
|
+
def index(options={}, &block) prefix :index, █ end
|
191
|
+
def new(options={}, &block) prefix :new, █ end
|
192
|
+
def create(options={}, &block) prefix :create, █ end
|
193
|
+
def show(options={}, &block) prefix :show, █ end
|
194
|
+
def edit(options={}, &block) prefix :edit, █ end
|
195
|
+
def update(options={}, &block) prefix :update, █ end
|
196
|
+
def destroy(options={}, &block) prefix :destroy, █ end
|
197
|
+
|
198
|
+
private
|
199
|
+
def prefix(name, &block)
|
200
|
+
@module.send :define_method, "#{@prefix}_#{name}", &block if block_given?
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
end # REST
|
205
|
+
|
206
|
+
helpers REST::Helpers
|
207
|
+
register REST
|
208
|
+
|
209
|
+
end # Sinatra
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
#require 'dm-core'
|
3
|
+
|
4
|
+
class Word
|
5
|
+
include DataMapper::Resource
|
6
|
+
property :id, Serial
|
7
|
+
property :name, String
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'with datamappper model' do
|
11
|
+
|
12
|
+
before(:all) do
|
13
|
+
DataMapper.setup(:default, 'sqlite3::memory:')
|
14
|
+
DataMapper.auto_migrate!
|
15
|
+
@existing_word = Word.create :name => 'existing'
|
16
|
+
mock_rest Word
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should load new' do
|
20
|
+
get '/words/new'
|
21
|
+
last_response.status.should == 200
|
22
|
+
last_response.body.should_not be_empty
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should create record and redirect to show' do
|
26
|
+
post '/words', {:name => 'paleontologist'}
|
27
|
+
word = Word.first(:name => 'paleontologist')
|
28
|
+
word.should_not be_nil
|
29
|
+
last_response.status.should == 302
|
30
|
+
last_response.headers['Location'].should == "/words/#{word.id}"
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should load show' do
|
34
|
+
get "/words/#{@existing_word.id}"
|
35
|
+
last_response.status.should == 200
|
36
|
+
last_response.body.should match @existing_word.name
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should load edit' do
|
40
|
+
get "/words/#{@existing_word.id}/edit"
|
41
|
+
last_response.status.should == 200
|
42
|
+
last_response.body.should match @existing_word.name
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should update record and redirect to show' do
|
46
|
+
put "/words/#{@existing_word.id}", {:name => 'changed'}
|
47
|
+
changed_word = Word.first(:name => 'changed')
|
48
|
+
changed_word.should_not be_nil
|
49
|
+
last_response.status.should == 302
|
50
|
+
last_response.headers['Location'].should == "/words/#{changed_word.id}"
|
51
|
+
changed_word.update :name => 'existing'
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should delete record' do
|
55
|
+
word = Word.create :name => 'delete_me'
|
56
|
+
delete "/words/#{word.id}"
|
57
|
+
Word.get(:name => 'delete_me').should be_nil
|
58
|
+
last_response.status.should == 302
|
59
|
+
last_response.headers['Location'].should == '/words'
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
describe 'call order' do
|
4
|
+
|
5
|
+
def called_routes
|
6
|
+
@app.call_order.map {|r, m| r}.uniq
|
7
|
+
end
|
8
|
+
|
9
|
+
def called_methods
|
10
|
+
@app.call_order.map {|r, m| m}
|
11
|
+
end
|
12
|
+
|
13
|
+
before(:each) do
|
14
|
+
mock_app {
|
15
|
+
configure do
|
16
|
+
set :call_order, []
|
17
|
+
end
|
18
|
+
|
19
|
+
rest Person do
|
20
|
+
before do |route|
|
21
|
+
options.call_order << [route, :before]
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
after do |route|
|
26
|
+
options.call_order << [route, :after]
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
index do
|
31
|
+
options.call_order << [:index, :index]
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
new do
|
36
|
+
options.call_order << [:new, :new]
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
create do
|
41
|
+
options.call_order << [:create, :create]
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
show do
|
46
|
+
options.call_order << [:show, :show]
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
edit do
|
51
|
+
options.call_order << [:edit, :edit]
|
52
|
+
super
|
53
|
+
end
|
54
|
+
|
55
|
+
update do
|
56
|
+
options.call_order << [:update, :update]
|
57
|
+
super
|
58
|
+
end
|
59
|
+
|
60
|
+
destroy do
|
61
|
+
options.call_order << [:destroy, :destroy]
|
62
|
+
super
|
63
|
+
end
|
64
|
+
end
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should call :index in the right order' do
|
69
|
+
index '/people'
|
70
|
+
called_methods.should == [:before, :index, :after]
|
71
|
+
called_routes.should == [:index]
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should call :new in the right order' do
|
75
|
+
new '/people/new'
|
76
|
+
called_methods.should == [:before, :new, :after]
|
77
|
+
called_routes.should == [:new]
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should call :create in the right order' do
|
81
|
+
create('/people', :name => 'initial name')
|
82
|
+
called_methods.should == [:before, :create, :after]
|
83
|
+
called_routes.should == [:create]
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should call :show in the right order' do
|
87
|
+
show '/people/1'
|
88
|
+
called_methods.should == [:before, :show, :after]
|
89
|
+
called_routes.should == [:show]
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should call :edit in the right order' do
|
93
|
+
edit '/people/1/edit'
|
94
|
+
called_methods.should == [:before, :edit, :after]
|
95
|
+
called_routes.should == [:edit]
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should call :update in the right order' do
|
99
|
+
update '/people/1', :name => 'new name'
|
100
|
+
called_methods.should == [:before, :update, :after]
|
101
|
+
called_routes.should == [:update]
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'should call :destroy in the right order' do
|
105
|
+
destroy '/people/1'
|
106
|
+
called_methods.should == [:before, :destroy, :after]
|
107
|
+
called_routes.should == [:destroy]
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
data/test/crud_spec.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
describe 'some use cases' do
|
4
|
+
|
5
|
+
def total_models
|
6
|
+
Person.all.size
|
7
|
+
end
|
8
|
+
|
9
|
+
require "rexml/document"
|
10
|
+
def doc(xml)
|
11
|
+
REXML::Document.new(xml.gsub(/>\s+</, '><').strip)
|
12
|
+
end
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
Person.reset!
|
16
|
+
mock_rest Person
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should list all persons' do
|
20
|
+
get '/people'
|
21
|
+
normalized_response.should == [200, '<people><person><id>1</id></person><person><id>2</id></person><person><id>3</id></person></people>']
|
22
|
+
total_models.should == 3
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should create a new person' do
|
26
|
+
get '/people'
|
27
|
+
normalized_response.should == [200, '<people><person><id>1</id></person><person><id>2</id></person><person><id>3</id></person></people>']
|
28
|
+
total_models.should == 3
|
29
|
+
|
30
|
+
get '/people/new'
|
31
|
+
normalized_response.should == [200, '<person><id></id><name></name></person>']
|
32
|
+
total_models.should == 3
|
33
|
+
|
34
|
+
post '/people', {:name => 'four'}
|
35
|
+
normalized_response.should == [302, 'person created']
|
36
|
+
total_models.should == 4
|
37
|
+
|
38
|
+
get '/people'
|
39
|
+
normalized_response.should == [200, '<people><person><id>1</id></person><person><id>2</id></person><person><id>3</id></person><person><id>4</id></person></people>']
|
40
|
+
total_models.should == 4
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should read all persons' do
|
44
|
+
get '/people'
|
45
|
+
el_people = doc(last_response.body).elements.to_a("*/person/id")
|
46
|
+
el_people.size.should == 3
|
47
|
+
total_models.should == 3
|
48
|
+
|
49
|
+
get "/people/#{el_people[0].text}"
|
50
|
+
normalized_response.should == [200, '<person><id>1</id><name>one</name></person>']
|
51
|
+
total_models.should == 3
|
52
|
+
|
53
|
+
get "/people/#{el_people[1].text}"
|
54
|
+
normalized_response.should == [200, '<person><id>2</id><name>two</name></person>']
|
55
|
+
total_models.should == 3
|
56
|
+
|
57
|
+
get "/people/#{el_people[2].text}"
|
58
|
+
normalized_response.should == [200, '<person><id>3</id><name>three</name></person>']
|
59
|
+
total_models.should == 3
|
60
|
+
|
61
|
+
get "/people/99"
|
62
|
+
normalized_response.should == [404, 'route not found']
|
63
|
+
total_models.should == 3
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should update a person' do
|
67
|
+
get '/people/2'
|
68
|
+
normalized_response.should == [200, '<person><id>2</id><name>two</name></person>']
|
69
|
+
total_models.should == 3
|
70
|
+
|
71
|
+
put '/people/2', {:name => 'tomorrow'}
|
72
|
+
normalized_response.should == [302, 'person updated']
|
73
|
+
total_models.should == 3
|
74
|
+
|
75
|
+
get '/people/2'
|
76
|
+
normalized_response.should == [200, '<person><id>2</id><name>tomorrow</name></person>']
|
77
|
+
total_models.should == 3
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should destroy a person' do
|
81
|
+
get '/people'
|
82
|
+
normalized_response.should == [200, '<people><person><id>1</id></person><person><id>2</id></person><person><id>3</id></person></people>']
|
83
|
+
total_models.should == 3
|
84
|
+
|
85
|
+
delete '/people/2'
|
86
|
+
normalized_response.should == [302, 'person destroyed']
|
87
|
+
total_models.should == 2
|
88
|
+
|
89
|
+
get '/people'
|
90
|
+
normalized_response.should == [200, '<people><person><id>1</id></person><person><id>3</id></person></people>']
|
91
|
+
total_models.should == 2
|
92
|
+
|
93
|
+
get '/people/2'
|
94
|
+
normalized_response.should == [404, 'route not found']
|
95
|
+
total_models.should == 2
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
require 'spec/interop/test'
|
4
|
+
require 'rack/test'
|
5
|
+
require 'english/inflect'
|
6
|
+
require 'haml'
|
7
|
+
require 'dm-core'
|
8
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
9
|
+
require 'sinatra/base'
|
10
|
+
require 'sinatra/rest'
|
11
|
+
|
12
|
+
Sinatra::Base.set(:environment, :test)
|
13
|
+
Test::Unit::TestCase.send :include, Rack::Test::Methods
|
14
|
+
|
15
|
+
#
|
16
|
+
# Sets up a Sinatra::Base subclass defined with the block
|
17
|
+
# given. Used in setup or individual spec methods to establish
|
18
|
+
# the application.
|
19
|
+
|
20
|
+
def app
|
21
|
+
@app
|
22
|
+
end
|
23
|
+
|
24
|
+
def mock_app(&block)
|
25
|
+
base = Sinatra::Base
|
26
|
+
base.register Sinatra::REST
|
27
|
+
base.helpers Sinatra::REST::Helpers
|
28
|
+
@app = Sinatra.new(base) do
|
29
|
+
set :views, File.dirname(__FILE__) + '/views'
|
30
|
+
not_found do
|
31
|
+
'route not found'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
@app.instance_eval(&block) if block_given?
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# sets rest in a sinatra instance
|
39
|
+
# and returns the block's result, if a block is given
|
40
|
+
def mock_rest(model, options={}, &block)
|
41
|
+
mock_app do
|
42
|
+
rest model, options
|
43
|
+
self.new.instance_eval do
|
44
|
+
self.instance_eval(&block) if block_given?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# normalize for easier testing
|
51
|
+
def normalized_response
|
52
|
+
return last_response.status, last_response.body.gsub(/>\s+</, '><').strip
|
53
|
+
end
|
54
|
+
|
55
|
+
# index GET /models
|
56
|
+
def index(url)
|
57
|
+
get url
|
58
|
+
normalized_response
|
59
|
+
end
|
60
|
+
|
61
|
+
# new GET /models/new
|
62
|
+
def new(url)
|
63
|
+
get url
|
64
|
+
normalized_response
|
65
|
+
end
|
66
|
+
|
67
|
+
# create POST /models
|
68
|
+
def create(url, params={})
|
69
|
+
post url, params
|
70
|
+
normalized_response
|
71
|
+
end
|
72
|
+
|
73
|
+
# show GET /models/1
|
74
|
+
def show(url)
|
75
|
+
get url
|
76
|
+
normalized_response
|
77
|
+
end
|
78
|
+
|
79
|
+
# edit GET /models/1/edit
|
80
|
+
def edit(url)
|
81
|
+
get url
|
82
|
+
normalized_response
|
83
|
+
end
|
84
|
+
|
85
|
+
# update PUT /models/1
|
86
|
+
def update(url, params={})
|
87
|
+
put url, params
|
88
|
+
normalized_response
|
89
|
+
end
|
90
|
+
|
91
|
+
# destroy DELETE /models/1
|
92
|
+
def destroy(url)
|
93
|
+
delete url
|
94
|
+
normalized_response
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
##
|
99
|
+
## kind of a 'minimal model'
|
100
|
+
class Person
|
101
|
+
attr_accessor :id
|
102
|
+
attr_accessor :name
|
103
|
+
|
104
|
+
def initialize(*args)
|
105
|
+
#puts "new #{args.inspect}"
|
106
|
+
if args.size == 0
|
107
|
+
@id = nil
|
108
|
+
@name = nil
|
109
|
+
elsif args.size == 2
|
110
|
+
@id = args[0].to_i
|
111
|
+
@name = args[1]
|
112
|
+
else args.size == 1
|
113
|
+
update_attributes(args[0])
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def save
|
118
|
+
#puts "save #{@id}"
|
119
|
+
@@people << self
|
120
|
+
self.id = @@people.size
|
121
|
+
end
|
122
|
+
|
123
|
+
def update_attributes(hash)
|
124
|
+
#puts "update_attributes #{hash.inspect}"
|
125
|
+
unless hash.empty?
|
126
|
+
@id = hash['id'].to_i if hash.include?('id')
|
127
|
+
@name = hash['name'] if hash.include?('name')
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.delete(id)
|
132
|
+
#puts "delete #{id}"
|
133
|
+
@@people.delete_if {|person| person.id == id.to_i}
|
134
|
+
end
|
135
|
+
|
136
|
+
@@people = []
|
137
|
+
|
138
|
+
def self.all(criteria={})
|
139
|
+
#puts 'all'
|
140
|
+
return @@people
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.find_by_id(id)
|
144
|
+
#puts "find_by_id #{id}"
|
145
|
+
all.find {|f| f.id == id.to_i}
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.clear!
|
149
|
+
@@people = []
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.reset!
|
153
|
+
clear!
|
154
|
+
Person.new(1, 'one').save
|
155
|
+
Person.new(2, 'two').save
|
156
|
+
Person.new(3, 'three').save
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
describe 'url helpers' do
|
4
|
+
|
5
|
+
it 'should generate the correct urls for the model' do
|
6
|
+
mock_rest Person do
|
7
|
+
person = Person.new(99, 'foo')
|
8
|
+
url_for_people_create.should == '/people'
|
9
|
+
url_for_people_destroy(person).should == '/people/99'
|
10
|
+
url_for_people_edit(person).should == '/people/99/edit'
|
11
|
+
url_for_people_index.should == '/people'
|
12
|
+
url_for_people_new.should == '/people/new'
|
13
|
+
url_for_people_show(person).should == '/people/99'
|
14
|
+
url_for_people_update(person).should == '/people/99'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should add :all helpers' do
|
19
|
+
mock_rest(Person) { methods.grep(/^url_for_people_/).sort }.should == [
|
20
|
+
"url_for_people_create",
|
21
|
+
"url_for_people_destroy",
|
22
|
+
"url_for_people_edit",
|
23
|
+
"url_for_people_index",
|
24
|
+
"url_for_people_new",
|
25
|
+
"url_for_people_show",
|
26
|
+
"url_for_people_update",
|
27
|
+
]
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should add :readable helpers' do
|
31
|
+
mock_rest(Person, :routes => :readable) { methods.grep(/^url_for_people_/).sort }.should == [
|
32
|
+
"url_for_people_index",
|
33
|
+
"url_for_people_show",
|
34
|
+
]
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should add :writeable helpers' do
|
38
|
+
mock_rest(Person, :routes => :writeable) { methods.grep(/^url_for_people_/).sort }.should == [
|
39
|
+
"url_for_people_create",
|
40
|
+
"url_for_people_destroy",
|
41
|
+
"url_for_people_index",
|
42
|
+
"url_for_people_show",
|
43
|
+
"url_for_people_update",
|
44
|
+
]
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should add :editable helpers' do
|
48
|
+
mock_rest(Person, :routes => :editable) { methods.grep(/^url_for_people_/).sort }.should == [
|
49
|
+
"url_for_people_create",
|
50
|
+
"url_for_people_destroy",
|
51
|
+
"url_for_people_edit",
|
52
|
+
"url_for_people_index",
|
53
|
+
"url_for_people_new",
|
54
|
+
"url_for_people_show",
|
55
|
+
"url_for_people_update",
|
56
|
+
]
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should add helpers by name' do
|
60
|
+
mock_rest(Person, :routes => [:new, :create, :destroy]) { methods.grep(/^url_for_people_/).sort }.should == [
|
61
|
+
"url_for_people_create",
|
62
|
+
"url_for_people_destroy",
|
63
|
+
"url_for_people_new",
|
64
|
+
]
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should add helpers by mixing aliases and names' do
|
68
|
+
mock_rest(Person, :routes => [:readable, :create, :destroy]) { methods.grep(/^url_for_people_/).sort }.should == [
|
69
|
+
"url_for_people_create",
|
70
|
+
"url_for_people_destroy",
|
71
|
+
"url_for_people_index",
|
72
|
+
"url_for_people_show",
|
73
|
+
]
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
describe 'model inflection' do
|
4
|
+
|
5
|
+
def conjugate(model)
|
6
|
+
mock_app {
|
7
|
+
include Sinatra::REST
|
8
|
+
conjugate(model)
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should conjugate a simple model name" do
|
13
|
+
conjugate(Person).should == %w(Person person people)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should conjugate a String as model name" do
|
17
|
+
conjugate('Person').should == %w(Person person people)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should conjugate a model name in camel cases" do
|
21
|
+
conjugate('SomePerson').should == %w(SomePerson some_person some_people)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should conjugate a model name without module" do
|
25
|
+
conjugate('MyModule::ModulePerson').should == %w(ModulePerson module_person module_people)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
data/test/routes_spec.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
describe 'routes' do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
Person.reset!
|
7
|
+
end
|
8
|
+
|
9
|
+
describe 'one by one' do
|
10
|
+
|
11
|
+
before(:each) do
|
12
|
+
mock_rest Person
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should list all people on index by their id' do
|
16
|
+
index('/people').should == [200, '<people><person><id>1</id></person><person><id>2</id></person><person><id>3</id></person></people>']
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should prepare an empty item on new' do
|
20
|
+
new('/people/new').should == [200, '<person><id></id><name></name></person>']
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should create an item on post' do
|
24
|
+
create('/people', :name => 'new resource').should == [302, 'person created']
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should show an item on get' do
|
28
|
+
show('/people/1').should == [200, '<person><id>1</id><name>one</name></person>']
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should get the item for editing' do
|
32
|
+
edit('/people/1/edit').should == [200, '<person><id>1</id><name>one</name></person>']
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should update an item on put' do
|
36
|
+
update('/people/1', :name => 'another name').should == [302, 'person updated']
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should destroy an item on delete' do
|
40
|
+
destroy('/people/1').should == [302, 'person destroyed']
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'options' do
|
46
|
+
|
47
|
+
it 'should add :all routes' do
|
48
|
+
mock_rest Person
|
49
|
+
|
50
|
+
index('/people').should == [200, '<people><person><id>1</id></person><person><id>2</id></person><person><id>3</id></person></people>']
|
51
|
+
new('/people/new').should == [200, '<person><id></id><name></name></person>']
|
52
|
+
create('/people', :name => 'new person').should == [302, "person created"]
|
53
|
+
show('/people/1').should == [200, '<person><id>1</id><name>one</name></person>']
|
54
|
+
edit('/people/1/edit').should == [200, "<person><id>1</id><name>one</name></person>"]
|
55
|
+
update('/people/1', :name => 'new name').should == [302, "person updated"]
|
56
|
+
destroy('/people/1').should == [302, "person destroyed"]
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should add :readable routes' do
|
60
|
+
mock_rest Person, :routes => :readable
|
61
|
+
|
62
|
+
index('/people').should == [200, '<people><person><id>1</id></person><person><id>2</id></person><person><id>3</id></person></people>']
|
63
|
+
show('/people/1').should == [200, '<person><id>1</id><name>one</name></person>']
|
64
|
+
|
65
|
+
new('/people/new').should == [404, "route not found"]
|
66
|
+
create('/people', :name => 'new person').should == [404, "route not found"]
|
67
|
+
edit('/people/1/edit').should == [404, "route not found"]
|
68
|
+
update('/people/1', :name => 'new name').should == [404, "route not found"]
|
69
|
+
destroy('/people/1').should == [404, "route not found"]
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should add :writeable routes' do
|
73
|
+
mock_rest Person, :routes => :writeable
|
74
|
+
|
75
|
+
index('/people').should == [200, '<people><person><id>1</id></person><person><id>2</id></person><person><id>3</id></person></people>']
|
76
|
+
show('/people/1').should == [200, '<person><id>1</id><name>one</name></person>']
|
77
|
+
create('/people', :name => 'new person').should == [302, "person created"]
|
78
|
+
update('/people/1', :name => 'new name').should == [302, "person updated"]
|
79
|
+
destroy('/people/1').should == [302, "person destroyed"]
|
80
|
+
|
81
|
+
new('/people/new').should == [404, "route not found"]
|
82
|
+
edit('/people/1/edit').should == [404, "route not found"]
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should add :editable routes' do
|
86
|
+
mock_rest Person, :routes => :editable
|
87
|
+
|
88
|
+
index('/people').should == [200, '<people><person><id>1</id></person><person><id>2</id></person><person><id>3</id></person></people>']
|
89
|
+
new('/people/new').should == [200, '<person><id></id><name></name></person>']
|
90
|
+
create('/people', :name => 'new person').should == [302, "person created"]
|
91
|
+
show('/people/1').should == [200, '<person><id>1</id><name>one</name></person>']
|
92
|
+
edit('/people/1/edit').should == [200, "<person><id>1</id><name>one</name></person>"]
|
93
|
+
update('/people/1', :name => 'new name').should == [302, "person updated"]
|
94
|
+
destroy('/people/1').should == [302, "person destroyed"]
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should add routes by name' do
|
98
|
+
mock_rest Person, :routes => [:readable, :new, :create]
|
99
|
+
|
100
|
+
index('/people').should == [200, '<people><person><id>1</id></person><person><id>2</id></person><person><id>3</id></person></people>']
|
101
|
+
show('/people/1').should == [200, '<person><id>1</id><name>one</name></person>']
|
102
|
+
new('/people/new').should == [200, '<person><id></id><name></name></person>']
|
103
|
+
create('/people', :name => 'new person').should == [302, "person created"]
|
104
|
+
|
105
|
+
edit('/people/1/edit').should == [404, "route not found"]
|
106
|
+
update('/people/1', :name => 'new name').should == [404, "route not found"]
|
107
|
+
destroy('/people/1').should == [404, "route not found"]
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
data/test/test_spec.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
describe 'test helpers' do
|
4
|
+
|
5
|
+
it 'should work with mock_app' do
|
6
|
+
Person.clear!
|
7
|
+
mock_app {
|
8
|
+
rest Person
|
9
|
+
}
|
10
|
+
index('/people').should == [200, '<people></people>']
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should work with mock_rest' do
|
14
|
+
Person.clear!
|
15
|
+
mock_rest Person
|
16
|
+
index('/people').should == [200, '<people></people>']
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mikeycgto-sinatra-rest
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 13
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 4
|
9
|
+
- 1
|
10
|
+
version: 0.4.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- blindgaenger
|
14
|
+
- kyledrake
|
15
|
+
- dominiquebrezinski
|
16
|
+
- mikeycgto
|
17
|
+
autorequire:
|
18
|
+
bindir: bin
|
19
|
+
cert_chain: []
|
20
|
+
|
21
|
+
date: 2010-06-08 00:00:00 -04:00
|
22
|
+
default_executable:
|
23
|
+
dependencies:
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: sinatra
|
26
|
+
prerelease: false
|
27
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
hash: 57
|
33
|
+
segments:
|
34
|
+
- 0
|
35
|
+
- 9
|
36
|
+
- 1
|
37
|
+
version: 0.9.1
|
38
|
+
type: :runtime
|
39
|
+
version_requirements: *id001
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: english
|
42
|
+
prerelease: false
|
43
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
hash: 11
|
49
|
+
segments:
|
50
|
+
- 0
|
51
|
+
- 5
|
52
|
+
- 0
|
53
|
+
version: 0.5.0
|
54
|
+
type: :runtime
|
55
|
+
version_requirements: *id002
|
56
|
+
description:
|
57
|
+
email: blindgaenger@gmail.com
|
58
|
+
executables: []
|
59
|
+
|
60
|
+
extensions: []
|
61
|
+
|
62
|
+
extra_rdoc_files: []
|
63
|
+
|
64
|
+
files:
|
65
|
+
- Rakefile
|
66
|
+
- README.textile
|
67
|
+
- lib/sinatra/rest.rb
|
68
|
+
- lib/sinatra/rest/adapters.rb
|
69
|
+
- lib/sinatra/rest/rest.yaml
|
70
|
+
- test/adapters_spec.rb
|
71
|
+
- test/call_order_spec.rb
|
72
|
+
- test/crud_spec.rb
|
73
|
+
- test/helper.rb
|
74
|
+
- test/helpers_spec.rb
|
75
|
+
- test/inflection_spec.rb
|
76
|
+
- test/routes_spec.rb
|
77
|
+
- test/test_spec.rb
|
78
|
+
- test/views/people/edit.haml
|
79
|
+
- test/views/people/index.haml
|
80
|
+
- test/views/people/new.haml
|
81
|
+
- test/views/people/show.haml
|
82
|
+
- test/views/words/edit.haml
|
83
|
+
- test/views/words/index.haml
|
84
|
+
- test/views/words/new.haml
|
85
|
+
- test/views/words/show.haml
|
86
|
+
has_rdoc: true
|
87
|
+
homepage: http://github.com/mikeycgto/sinatra-rest
|
88
|
+
licenses: []
|
89
|
+
|
90
|
+
post_install_message:
|
91
|
+
rdoc_options: []
|
92
|
+
|
93
|
+
require_paths:
|
94
|
+
- lib
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
hash: 3
|
101
|
+
segments:
|
102
|
+
- 0
|
103
|
+
version: "0"
|
104
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
hash: 3
|
110
|
+
segments:
|
111
|
+
- 0
|
112
|
+
version: "0"
|
113
|
+
requirements: []
|
114
|
+
|
115
|
+
rubyforge_project:
|
116
|
+
rubygems_version: 1.3.7
|
117
|
+
signing_key:
|
118
|
+
specification_version: 3
|
119
|
+
summary: Generates RESTful routes for the models of a Sinatra application (ActiveRecord, DataMapper, Stone)
|
120
|
+
test_files: []
|
121
|
+
|