sinatra-rest 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.textile +86 -0
- data/Rakefile +10 -0
- data/lib/sinatra/rest.rb +210 -0
- data/lib/sinatra/rest/adapters.rb +37 -0
- data/lib/sinatra/rest/rest.yaml +67 -0
- data/test/call_order_spec.rb +111 -0
- data/test/crud_spec.rb +99 -0
- data/test/helper.rb +153 -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
- metadata +89 -0
data/README.textile
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
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
|
+
h2. Links
|
|
78
|
+
|
|
79
|
+
* "Documentation @ rdoc.info":http://rdoc.info/projects/blindgaenger/sinatra-rest
|
|
80
|
+
* "Continuous Integration @ RunCodeRun":http://runcoderun.com/blindgaenger/sinatra-rest
|
|
81
|
+
|
|
82
|
+
h2. Contact
|
|
83
|
+
|
|
84
|
+
You can contact me via mail at blindgaenger at gmail dot com, or leave me a
|
|
85
|
+
message on my "Github profile":http://github.com/blindgaenger.
|
|
86
|
+
|
data/Rakefile
ADDED
data/lib/sinatra/rest.rb
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
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, singular.pluralize
|
|
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 =~ /^_/}
|
|
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
|
+
method = model_class.method(name)
|
|
170
|
+
if options.nil? || method.arity == 0
|
|
171
|
+
Kernel.warn "warning: calling #{model_class.to_s}##{name} with args, although it doesn't take args" if options
|
|
172
|
+
method.call
|
|
173
|
+
else
|
|
174
|
+
method.call(options)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
#
|
|
180
|
+
# used as context to evaluate the controller's module
|
|
181
|
+
class CustomController
|
|
182
|
+
attr_reader :module
|
|
183
|
+
|
|
184
|
+
def initialize(prefix)
|
|
185
|
+
@prefix = prefix
|
|
186
|
+
@module = Module.new
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def before(options={}, &block) prefix :before, █ end
|
|
190
|
+
def after(options={}, &block) prefix :after, █ end
|
|
191
|
+
def index(options={}, &block) prefix :index, █ end
|
|
192
|
+
def new(options={}, &block) prefix :new, █ end
|
|
193
|
+
def create(options={}, &block) prefix :create, █ end
|
|
194
|
+
def show(options={}, &block) prefix :show, █ end
|
|
195
|
+
def edit(options={}, &block) prefix :edit, █ end
|
|
196
|
+
def update(options={}, &block) prefix :update, █ end
|
|
197
|
+
def destroy(options={}, &block) prefix :destroy, █ end
|
|
198
|
+
|
|
199
|
+
private
|
|
200
|
+
def prefix(name, &block)
|
|
201
|
+
@module.send :define_method, "#{@prefix}_#{name}", &block if block_given?
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
end # REST
|
|
206
|
+
|
|
207
|
+
helpers REST::Helpers
|
|
208
|
+
register REST
|
|
209
|
+
|
|
210
|
+
end # Sinatra
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
module Model
|
|
16
|
+
def find_by_id(id)
|
|
17
|
+
get(id)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def delete(id)
|
|
21
|
+
model = self.find_by_id(id)
|
|
22
|
+
model.destroy if model
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module ActiveRecord
|
|
28
|
+
class Base
|
|
29
|
+
class << self
|
|
30
|
+
def find_by_id(id)
|
|
31
|
+
find(id)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
:index:
|
|
3
|
+
:verb: GET
|
|
4
|
+
:url: /PLURAL
|
|
5
|
+
:control: |-
|
|
6
|
+
@PLURAL = call_model_method(MODEL, :all, mp)
|
|
7
|
+
:render: |-
|
|
8
|
+
RENDERER :'PLURAL/index', options
|
|
9
|
+
|
|
10
|
+
:new:
|
|
11
|
+
:verb: GET
|
|
12
|
+
:url: /PLURAL/new
|
|
13
|
+
:control: |-
|
|
14
|
+
@SINGULAR = call_model_method(MODEL, :new, mp)
|
|
15
|
+
:render: |-
|
|
16
|
+
RENDERER :'PLURAL/new', options
|
|
17
|
+
|
|
18
|
+
:create:
|
|
19
|
+
:verb: POST
|
|
20
|
+
:url: /PLURAL
|
|
21
|
+
:control: |-
|
|
22
|
+
@SINGULAR = call_model_method(MODEL, :new, mp)
|
|
23
|
+
@SINGULAR.save
|
|
24
|
+
:render: |-
|
|
25
|
+
redirect url_for_PLURAL_show(@SINGULAR), 'SINGULAR created'
|
|
26
|
+
|
|
27
|
+
:show:
|
|
28
|
+
:verb: GET
|
|
29
|
+
:url: /PLURAL/:id
|
|
30
|
+
:control: |-
|
|
31
|
+
@SINGULAR = call_model_method(MODEL, :find_by_id, mp[:id])
|
|
32
|
+
:render: |-
|
|
33
|
+
if @SINGULAR.nil?
|
|
34
|
+
throw :halt, [404, 'SINGULAR not found']
|
|
35
|
+
else
|
|
36
|
+
RENDERER :'PLURAL/show', options
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
:edit:
|
|
40
|
+
:verb: GET
|
|
41
|
+
:url: /PLURAL/:id/edit
|
|
42
|
+
:control: |-
|
|
43
|
+
@SINGULAR = call_model_method(MODEL, :find_by_id, mp[:id])
|
|
44
|
+
:render: |-
|
|
45
|
+
RENDERER :'PLURAL/edit', options
|
|
46
|
+
|
|
47
|
+
:update:
|
|
48
|
+
:verb: PUT
|
|
49
|
+
:url: /PLURAL/:id
|
|
50
|
+
:control: |-
|
|
51
|
+
@SINGULAR = call_model_method(MODEL, :find_by_id, mp[:id])
|
|
52
|
+
@SINGULAR.update_attributes(mp) unless @SINGULAR.nil?
|
|
53
|
+
:render: |-
|
|
54
|
+
if @SINGULAR.nil?
|
|
55
|
+
throw :halt, [404, 'SINGULAR not found']
|
|
56
|
+
else
|
|
57
|
+
redirect url_for_PLURAL_show(@SINGULAR), 'SINGULAR updated'
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
:destroy:
|
|
61
|
+
:verb: DELETE
|
|
62
|
+
:url: /PLURAL/:id
|
|
63
|
+
:control: |-
|
|
64
|
+
call_model_method(MODEL, :delete, mp[:id])
|
|
65
|
+
:render: |-
|
|
66
|
+
redirect url_for_PLURAL_index, 'SINGULAR destroyed'
|
|
67
|
+
|
|
@@ -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,99 @@
|
|
|
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
|
+
|
|
46
|
+
el_people = doc(body).elements.to_a("*/person/id")
|
|
47
|
+
el_people.size.should == 3
|
|
48
|
+
total_models.should == 3
|
|
49
|
+
|
|
50
|
+
get "/people/#{el_people[0].text}"
|
|
51
|
+
normalized_response.should == [200, '<person><id>1</id><name>one</name></person>']
|
|
52
|
+
total_models.should == 3
|
|
53
|
+
|
|
54
|
+
get "/people/#{el_people[1].text}"
|
|
55
|
+
normalized_response.should == [200, '<person><id>2</id><name>two</name></person>']
|
|
56
|
+
total_models.should == 3
|
|
57
|
+
|
|
58
|
+
get "/people/#{el_people[2].text}"
|
|
59
|
+
normalized_response.should == [200, '<person><id>3</id><name>three</name></person>']
|
|
60
|
+
total_models.should == 3
|
|
61
|
+
|
|
62
|
+
get "/people/99"
|
|
63
|
+
normalized_response.should == [404, 'route not found']
|
|
64
|
+
total_models.should == 3
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'should update a person' do
|
|
68
|
+
get '/people/2'
|
|
69
|
+
normalized_response.should == [200, '<person><id>2</id><name>two</name></person>']
|
|
70
|
+
total_models.should == 3
|
|
71
|
+
|
|
72
|
+
put '/people/2', {:name => 'tomorrow'}
|
|
73
|
+
normalized_response.should == [302, 'person updated']
|
|
74
|
+
total_models.should == 3
|
|
75
|
+
|
|
76
|
+
get '/people/2'
|
|
77
|
+
normalized_response.should == [200, '<person><id>2</id><name>tomorrow</name></person>']
|
|
78
|
+
total_models.should == 3
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'should destroy a person' do
|
|
82
|
+
get '/people'
|
|
83
|
+
normalized_response.should == [200, '<people><person><id>1</id></person><person><id>2</id></person><person><id>3</id></person></people>']
|
|
84
|
+
total_models.should == 3
|
|
85
|
+
|
|
86
|
+
delete '/people/2'
|
|
87
|
+
normalized_response.should == [302, 'person destroyed']
|
|
88
|
+
total_models.should == 2
|
|
89
|
+
|
|
90
|
+
get '/people'
|
|
91
|
+
normalized_response.should == [200, '<people><person><id>1</id></person><person><id>3</id></person></people>']
|
|
92
|
+
total_models.should == 2
|
|
93
|
+
|
|
94
|
+
get '/people/2'
|
|
95
|
+
normalized_response.should == [404, 'route not found']
|
|
96
|
+
total_models.should == 2
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
end
|
data/test/helper.rb
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
require 'spec'
|
|
2
|
+
require 'spec/interop/test'
|
|
3
|
+
require 'sinatra/test'
|
|
4
|
+
|
|
5
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
|
6
|
+
require 'sinatra/base'
|
|
7
|
+
require 'sinatra/rest'
|
|
8
|
+
|
|
9
|
+
Sinatra::Default.set(:environment, :test)
|
|
10
|
+
Test::Unit::TestCase.send :include, Sinatra::Test
|
|
11
|
+
|
|
12
|
+
#
|
|
13
|
+
# Sets up a Sinatra::Base subclass defined with the block
|
|
14
|
+
# given. Used in setup or individual spec methods to establish
|
|
15
|
+
# the application.
|
|
16
|
+
def mock_app(&block)
|
|
17
|
+
base = Sinatra::Application
|
|
18
|
+
@app = Sinatra.new(base) do
|
|
19
|
+
set :views, File.dirname(__FILE__) + '/views'
|
|
20
|
+
|
|
21
|
+
not_found do
|
|
22
|
+
'route not found'
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
@app.instance_eval(&block) if block_given?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
#
|
|
29
|
+
# sets rest in a sinatra instance
|
|
30
|
+
# and returns the block's result, if a block is given
|
|
31
|
+
def mock_rest(model, options={}, &block)
|
|
32
|
+
mock_app do
|
|
33
|
+
rest model, options
|
|
34
|
+
|
|
35
|
+
self.new.instance_eval do
|
|
36
|
+
@app.instance_eval(&block) if block_given?
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
#
|
|
43
|
+
# normalize for easier testing
|
|
44
|
+
def normalized_response
|
|
45
|
+
return status, body.gsub(/>\s+</, '><').strip
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# index GET /models
|
|
49
|
+
def index(url)
|
|
50
|
+
get url
|
|
51
|
+
normalized_response
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# new GET /models/new
|
|
55
|
+
def new(url)
|
|
56
|
+
get url
|
|
57
|
+
normalized_response
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# create POST /models
|
|
61
|
+
def create(url, params={})
|
|
62
|
+
post url, params
|
|
63
|
+
normalized_response
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# show GET /models/1
|
|
67
|
+
def show(url)
|
|
68
|
+
get url
|
|
69
|
+
normalized_response
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# edit GET /models/1/edit
|
|
73
|
+
def edit(url)
|
|
74
|
+
get url
|
|
75
|
+
normalized_response
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# update PUT /models/1
|
|
79
|
+
def update(url, params={})
|
|
80
|
+
put url, params
|
|
81
|
+
normalized_response
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# destroy DELETE /models/1
|
|
85
|
+
def destroy(url)
|
|
86
|
+
delete url
|
|
87
|
+
normalized_response
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
##
|
|
92
|
+
## kind of a 'minimal model'
|
|
93
|
+
class Person
|
|
94
|
+
attr_accessor :id
|
|
95
|
+
attr_accessor :name
|
|
96
|
+
|
|
97
|
+
def initialize(*args)
|
|
98
|
+
#puts "new #{args.inspect}"
|
|
99
|
+
if args.size == 0
|
|
100
|
+
@id = nil
|
|
101
|
+
@name = nil
|
|
102
|
+
elsif args.size == 2
|
|
103
|
+
@id = args[0].to_i
|
|
104
|
+
@name = args[1]
|
|
105
|
+
else args.size == 1
|
|
106
|
+
update_attributes(args[0])
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def save
|
|
111
|
+
#puts "save #{@id}"
|
|
112
|
+
@@people << self
|
|
113
|
+
self.id = @@people.size
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def update_attributes(hash)
|
|
117
|
+
#puts "update_attributes #{hash.inspect}"
|
|
118
|
+
unless hash.empty?
|
|
119
|
+
@id = hash['id'].to_i if hash.include?('id')
|
|
120
|
+
@name = hash['name'] if hash.include?('name')
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def self.delete(id)
|
|
125
|
+
#puts "delete #{id}"
|
|
126
|
+
@@people.delete_if {|person| person.id == id.to_i}
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
@@people = []
|
|
130
|
+
|
|
131
|
+
def self.all(criteria={})
|
|
132
|
+
#puts 'all'
|
|
133
|
+
return @@people
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def self.find_by_id(id)
|
|
137
|
+
#puts "find_by_id #{id}"
|
|
138
|
+
all.find {|f| f.id == id.to_i}
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def self.clear!
|
|
142
|
+
@@people = []
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def self.reset!
|
|
146
|
+
clear!
|
|
147
|
+
Person.new(1, 'one').save
|
|
148
|
+
Person.new(2, 'two').save
|
|
149
|
+
Person.new(3, 'three').save
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
|
|
@@ -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,89 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: sinatra-rest
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.3.3
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- blindgaenger
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2009-09-25 00:00:00 +02:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies:
|
|
15
|
+
- !ruby/object:Gem::Dependency
|
|
16
|
+
name: sinatra
|
|
17
|
+
type: :runtime
|
|
18
|
+
version_requirement:
|
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
20
|
+
requirements:
|
|
21
|
+
- - ">="
|
|
22
|
+
- !ruby/object:Gem::Version
|
|
23
|
+
version: 0.9.0.5
|
|
24
|
+
version:
|
|
25
|
+
- !ruby/object:Gem::Dependency
|
|
26
|
+
name: english
|
|
27
|
+
type: :runtime
|
|
28
|
+
version_requirement:
|
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 0.3.1
|
|
34
|
+
version:
|
|
35
|
+
description:
|
|
36
|
+
email: blindgaenger@gmail.com
|
|
37
|
+
executables: []
|
|
38
|
+
|
|
39
|
+
extensions: []
|
|
40
|
+
|
|
41
|
+
extra_rdoc_files: []
|
|
42
|
+
|
|
43
|
+
files:
|
|
44
|
+
- Rakefile
|
|
45
|
+
- README.textile
|
|
46
|
+
- lib/sinatra/rest.rb
|
|
47
|
+
- lib/sinatra/rest/adapters.rb
|
|
48
|
+
- lib/sinatra/rest/rest.yaml
|
|
49
|
+
- test/call_order_spec.rb
|
|
50
|
+
- test/crud_spec.rb
|
|
51
|
+
- test/helper.rb
|
|
52
|
+
- test/helpers_spec.rb
|
|
53
|
+
- test/inflection_spec.rb
|
|
54
|
+
- test/routes_spec.rb
|
|
55
|
+
- test/test_spec.rb
|
|
56
|
+
- test/views/people/edit.haml
|
|
57
|
+
- test/views/people/index.haml
|
|
58
|
+
- test/views/people/new.haml
|
|
59
|
+
- test/views/people/show.haml
|
|
60
|
+
has_rdoc: true
|
|
61
|
+
homepage: http://github.com/blindgaenger/sinatra-rest
|
|
62
|
+
licenses: []
|
|
63
|
+
|
|
64
|
+
post_install_message:
|
|
65
|
+
rdoc_options: []
|
|
66
|
+
|
|
67
|
+
require_paths:
|
|
68
|
+
- lib
|
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: "0"
|
|
74
|
+
version:
|
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
76
|
+
requirements:
|
|
77
|
+
- - ">="
|
|
78
|
+
- !ruby/object:Gem::Version
|
|
79
|
+
version: "0"
|
|
80
|
+
version:
|
|
81
|
+
requirements: []
|
|
82
|
+
|
|
83
|
+
rubyforge_project:
|
|
84
|
+
rubygems_version: 1.3.5
|
|
85
|
+
signing_key:
|
|
86
|
+
specification_version: 3
|
|
87
|
+
summary: Generates RESTful routes for the models of a Sinatra application (ActiveRecord, DataMapper, Stone)
|
|
88
|
+
test_files: []
|
|
89
|
+
|