blindgaenger-sinatra-rest 0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile ADDED
@@ -0,0 +1,84 @@
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 '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>
51
+ <code>
52
+ # simply add this line
53
+
54
+ rest Person, :renderer => :erb
55
+
56
+ # and this is generated for you
57
+
58
+ get '/people' do
59
+ @people = Person.all
60
+ erb :"people/index", options
61
+ end
62
+
63
+ put '/people/:id' do
64
+ @person = Person.find_by_id(params[:id])
65
+ redirect url_for_people_show(@person), 'person updated'
66
+ end
67
+
68
+ # further restful routes for Person ...
69
+ </code>
70
+ </pre>
71
+
72
+ That's only half the truth! The routes are generated dynamically, so all
73
+ defaults can be overridden (the behaviour, after/before callbacks, used renderer,
74
+ which routes are added).
75
+
76
+ For more details and options, please have a look at the pages in the
77
+ "Sinatra-REST Wiki":http://github.com/blindgaenger/sinatra-rest/wikis on Github.
78
+
79
+
80
+ h2. Contact
81
+
82
+ You can contact me via mail at blindgaenger at gmail dot com, or leave me a
83
+ message on my "Github profile":http://github.com/blindgaenger.
84
+
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'spec/rake/spectask'
2
+
3
+ task :default => [:test]
4
+ task :test => :spec
5
+
6
+ desc "Run specs"
7
+ Spec::Rake::SpecTask.new :spec do |t|
8
+ t.spec_opts = %w(--format specdoc --color)
9
+ t.spec_files = FileList['test/*_spec.rb']
10
+ end
11
+
data/lib/rest.rb ADDED
@@ -0,0 +1,278 @@
1
+ require 'english/inflect'
2
+ require 'cgi'
3
+
4
+ module Stone
5
+ module Resource
6
+ def find_by_id(id)
7
+ get(id)
8
+ end
9
+ end
10
+ end
11
+
12
+ module DataMapper
13
+ module Model
14
+ def find_by_id(id)
15
+ get(id)
16
+ end
17
+ end
18
+ end
19
+
20
+ module ActiveRecord
21
+ module Base
22
+ def find_by_id(id)
23
+ first(id)
24
+ end
25
+ end
26
+ end
27
+
28
+ module Sinatra
29
+ module REST
30
+
31
+ #
32
+ # adds restful routes and url helpers for the model
33
+ def rest(model_class, options={}, &block)
34
+ model, singular, plural = conjugate(model_class)
35
+
36
+ renderer = options.delete(:renderer)
37
+ renderer ||= :haml
38
+
39
+ # @false@ will remove the @POST@, @PUT@ and @DELETE@ routes
40
+ # by dfault this is enabled
41
+ # :editable => true
42
+ editable = options.delete(:editable)
43
+ editable = true if editable.nil?
44
+
45
+ # @true@ will add the @/models/new@ and @/models/edit@ routes
46
+ # by default this is disabled
47
+ # :inputable => false
48
+ inputable = options.delete(:inputable)
49
+ inputable = true if inputable.nil?
50
+
51
+
52
+ # add some url_for_* helpers
53
+ Sinatra::Default.class_eval <<-XXX
54
+ public
55
+
56
+ # index GET /models
57
+ def url_for_#{plural}_index
58
+ '/#{plural}'
59
+ end
60
+
61
+ # new GET /models/new
62
+ if editable && inputable
63
+ def url_for_#{plural}_new
64
+ '/#{plural}/new'
65
+ end
66
+ end
67
+
68
+ # create POST /models
69
+ if editable
70
+ def url_for_#{plural}_create
71
+ '/#{plural}'
72
+ end
73
+ end
74
+
75
+ # show GET /models/1
76
+ def url_for_#{plural}_show(model)
77
+ "/#{plural}/\#{escape_model_id(model)}"
78
+ end
79
+
80
+ # edit GET /models/1/edit
81
+ if editable && inputable
82
+ def url_for_#{plural}_edit(model)
83
+ "/#{plural}/\#{escape_model_id(model)}/edit"
84
+ end
85
+ end
86
+
87
+ # update PUT /models/1
88
+ if editable
89
+ def url_for_#{plural}_update(model)
90
+ "/#{plural}/\#{escape_model_id(model)}"
91
+ end
92
+ end
93
+
94
+ # destroy DELETE /models/1
95
+ if editable
96
+ def url_for_#{plural}_destroy(model)
97
+ "/#{plural}/\#{escape_model_id(model)}"
98
+ end
99
+ end
100
+
101
+ private
102
+
103
+ def escape_model_id(model)
104
+ if model.nil?
105
+ raise 'can not generate url for nil'
106
+ elsif model.kind_of?(String)
107
+ CGI.escape(model)
108
+ elsif model.kind_of?(Fixnum)
109
+ model
110
+ elsif model.id.kind_of? String
111
+ CGI.escape(model.id)
112
+ else
113
+ model.id
114
+ end
115
+ end
116
+
117
+ public
118
+ XXX
119
+
120
+ # create an own module and fill it with the template
121
+ controller_template = Module.new
122
+ controller_template.class_eval <<-XXX
123
+ def call(name)
124
+ before name
125
+ send name
126
+ after name
127
+ end
128
+
129
+ def after(name)
130
+ # should be overridden
131
+ end
132
+
133
+ def before(name)
134
+ # should be overridden
135
+ end
136
+
137
+
138
+ # index GET /models
139
+ def index
140
+ @#{plural} = #{model}.all
141
+ end
142
+
143
+ # new GET /models/new
144
+ if editable && inputable
145
+ def new
146
+ @#{singular} = #{model}.new
147
+ end
148
+ end
149
+
150
+ # create POST /models
151
+ if editable
152
+ def create
153
+ @#{singular} = #{model}.new(params)
154
+ @#{singular}.save
155
+ end
156
+ end
157
+
158
+ # show GET /models/1
159
+ def show
160
+ @#{singular} = #{model}.find_by_id(params[:id])
161
+ end
162
+
163
+ # edit GET /models/1/edit
164
+ if editable && inputable
165
+ def edit
166
+ @#{singular} = #{model}.find_by_id(params[:id])
167
+ end
168
+ end
169
+
170
+ # update PUT /models/1
171
+ if editable
172
+ def update
173
+ @#{singular} = #{model}.find_by_id(params[:id])
174
+ @#{singular}.update_attributes(params)
175
+ end
176
+ end
177
+
178
+ # destroy DELETE /models/1
179
+ if editable
180
+ def destroy
181
+ #{model}.delete(params[:id])
182
+ end
183
+ end
184
+ XXX
185
+
186
+ # create an own module, to override the template with custom methods
187
+ # this way, you can still use #super# in the overridden methods
188
+ if block_given?
189
+ controller_custom = Module.new &block
190
+ end
191
+
192
+ # create the restful routes
193
+ Sinatra::Application.instance_eval <<-XXX
194
+ # add the correct modules to the EventContext
195
+ # use a metaclass so it isn't included again next time
196
+ before do
197
+ if request.path_info =~ /^\\/#{plural}\\b/
198
+ metaclass = class << self; self; end
199
+ metaclass.send(:include, controller_template)
200
+ metaclass.send(:include, controller_custom) if controller_custom
201
+ end
202
+ end
203
+
204
+ # index GET /models
205
+ get '/#{plural}' do
206
+ call :index
207
+ #{renderer.to_s} :"#{plural}/index", options
208
+ end
209
+
210
+ # new GET /models/new
211
+ if editable && inputable
212
+ get '/#{plural}/new' do
213
+ call :new
214
+ #{renderer.to_s} :"#{plural}/new", options
215
+ end
216
+ end
217
+
218
+ # create POST /models
219
+ if editable
220
+ post '/#{plural}' do
221
+ call :create
222
+ redirect url_for_#{plural}_show(@#{singular}), '#{singular} created'
223
+ end
224
+ end
225
+
226
+ # show GET /models/1
227
+ get '/#{plural}/:id' do
228
+ call :show
229
+ if @#{singular}.nil?
230
+ throw :halt, [404, '#{singular} not found']
231
+ else
232
+ #{renderer.to_s} :"#{plural}/show", options
233
+ end
234
+ end
235
+
236
+ # edit GET /models/1/edit
237
+ if editable && inputable
238
+ get '/#{plural}/:id/edit' do
239
+ call :edit
240
+ #{renderer.to_s} :"#{plural}/edit", options
241
+ end
242
+ end
243
+
244
+ # update PUT /models/1
245
+ if editable
246
+ put '/#{plural}/:id' do
247
+ call :update
248
+ redirect url_for_#{plural}_show(@#{singular}), '#{singular} updated'
249
+ end
250
+ end
251
+
252
+ # destroy DELETE /models/1
253
+ if editable
254
+ delete '/#{plural}/:id' do
255
+ call :destroy
256
+ redirect url_for_#{plural}_index, '#{singular} deleted'
257
+ end
258
+ end
259
+ XXX
260
+
261
+ end
262
+
263
+ protected
264
+ #
265
+ # creates the necessary forms of the model name
266
+ # pretty much like ActiveSupport's inflections, but don't like to depend on
267
+ def conjugate(model_class)
268
+ model = model_class.to_s.match(/(\w+)$/)[0]
269
+ singular = model.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
270
+ return model, singular, singular.pluralize
271
+ end
272
+
273
+ end # REST
274
+ end # Sinatra
275
+
276
+ include Sinatra::REST
277
+
278
+
data/spec/rest_spec.rb ADDED
@@ -0,0 +1,448 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'sinatra'
4
+ require 'sinatra/test/rspec'
5
+ require 'lib/rest'
6
+ require "rexml/document"
7
+ require 'ruby-debug'
8
+
9
+ #
10
+ # kind of a 'minimal model'
11
+ class Person
12
+ attr_accessor :id
13
+ attr_accessor :name
14
+
15
+ def initialize(*args)
16
+ #puts "new #{args.inspect}"
17
+ if args.size == 0
18
+ @id = nil
19
+ @name = nil
20
+ elsif args.size == 2
21
+ @id = args[0].to_i
22
+ @name = args[1]
23
+ else args.size == 1
24
+ update_attributes(args[0])
25
+ end
26
+ end
27
+
28
+ def save
29
+ #puts "save #{@id}"
30
+ @@people << self
31
+ self.id = @@people.size
32
+ end
33
+
34
+ def update_attributes(hash)
35
+ #puts "update_attributes #{hash.inspect}"
36
+ unless hash.empty?
37
+ @id = hash['id'].to_i if hash.include?('id')
38
+ @name = hash['name'] if hash.include?('name')
39
+ end
40
+ end
41
+
42
+ def self.delete(id)
43
+ #puts "delete #{id}"
44
+ @@people.delete_if {|person| person.id == id.to_i}
45
+ end
46
+
47
+ @@people = nil
48
+
49
+ def self.all
50
+ #puts 'all'
51
+ return @@people
52
+ end
53
+
54
+ def self.find_by_id(id)
55
+ #puts "find_by_id #{id}"
56
+ all.find {|f| f.id == id.to_i}
57
+ end
58
+
59
+ def self.reset!
60
+ #puts 'reset!'
61
+ @@people = [
62
+ Person.new(1, 'one'),
63
+ Person.new(2, 'two'),
64
+ Person.new(3, 'three')
65
+ ]
66
+ end
67
+ end
68
+
69
+
70
+ def doc(xml)
71
+ REXML::Document.new(xml.gsub(/>\s+</, '><').strip)
72
+ end
73
+
74
+ def response_should_be(status, body)
75
+ @response.status.should == status
76
+ doc(@response.body).to_s.should == body
77
+ end
78
+
79
+ def model_should_be(size)
80
+ Person.all.size.should == size
81
+ end
82
+
83
+ def call_order_should_be(order, &block)
84
+ Person.call_order = []
85
+ block.call
86
+ Person.call_order.map {|c| c[:method]}.should == order
87
+ end
88
+
89
+ def called_should_be(name, &block)
90
+ Person.call_order = []
91
+ block.call
92
+ called = Person.call_order.map {|c| c[:called]}
93
+ called.should_not be_nil
94
+ called.each {|c| c.should == name }
95
+ end
96
+
97
+
98
+ describe Sinatra::REST do
99
+
100
+ describe 'as inflection generator' do
101
+ it "should conjugate a simple model name" do
102
+ Sinatra::REST.conjugate(Person).should eql(%w(Person person people))
103
+ end
104
+
105
+ it "should conjugate a String as model name" do
106
+ Sinatra::REST.conjugate('Person').should eql(%w(Person person people))
107
+ end
108
+
109
+ it "should conjugate a model name in camel cases" do
110
+ Sinatra::REST.conjugate('SomePerson').should eql(%w(SomePerson some_person some_people))
111
+ end
112
+
113
+ it "should conjugate a model name without module" do
114
+ Sinatra::REST.conjugate('MyModule::ModulePerson').should eql(%w(ModulePerson module_person module_people))
115
+ end
116
+ end
117
+
118
+
119
+ describe 'as route generator' do
120
+
121
+ before(:each) do
122
+ Sinatra.application = nil
123
+ @app = Sinatra.application
124
+
125
+ response_mock = mock "Response"
126
+ response_mock.should_receive(:"body=").with(nil).and_return(nil)
127
+ @context = Sinatra::EventContext.new(nil, response_mock, nil)
128
+ end
129
+
130
+
131
+ it 'should not add editable url_for_* helpers' do
132
+ rest Person, :editable => false
133
+
134
+ methods = Sinatra::EventContext.instance_methods.grep /^url_for_people_/
135
+ methods.size.should == 2
136
+
137
+ @person = Person.new
138
+ @person.id = 99
139
+
140
+ @context.url_for_people_index.should == '/people'
141
+ @context.url_for_people_show(@person).should == '/people/99'
142
+ end
143
+
144
+ it 'should not add editable restful routes' do
145
+ @app.events.clear
146
+ rest Person, :editable => false
147
+ @app.events[:get].map {|r| r.path}.should == ["/people", "/people/:id"]
148
+ @app.events[:post].map {|r| r.path}.should == []
149
+ @app.events[:put].map {|r| r.path}.should == []
150
+ @app.events[:delete].map {|r| r.path}.should == []
151
+ end
152
+
153
+
154
+ it 'should not add inputable url_for_* helpers' do
155
+ rest Person, :inputable => false
156
+
157
+ methods = Sinatra::EventContext.instance_methods.grep /^url_for_people_/
158
+ methods.size.should == 5
159
+
160
+ @person = Person.new
161
+ @person.id = 99
162
+
163
+ @context.url_for_people_index.should == '/people'
164
+ @context.url_for_people_create.should == '/people'
165
+ @context.url_for_people_show(@person).should == '/people/99'
166
+ @context.url_for_people_update(@person).should == '/people/99'
167
+ @context.url_for_people_destroy(@person).should == '/people/99'
168
+ end
169
+
170
+ it 'should not add inputable restful routes' do
171
+ @app.events.clear
172
+ rest Person, :inputable => false
173
+ @app.events[:get].map {|r| r.path}.should == ["/people", "/people/:id"]
174
+ @app.events[:post].map {|r| r.path}.should == ["/people"]
175
+ @app.events[:put].map {|r| r.path}.should == ["/people/:id"]
176
+ @app.events[:delete].map {|r| r.path}.should == ["/people/:id"]
177
+ end
178
+
179
+
180
+ it 'should add all url_for_* helpers' do
181
+ rest Person
182
+
183
+ methods = Sinatra::EventContext.instance_methods.grep /^url_for_people_/
184
+ methods.size.should == 7
185
+
186
+ @person = Person.new
187
+ @person.id = 99
188
+
189
+ @context.url_for_people_index.should == '/people'
190
+ @context.url_for_people_new.should == '/people/new'
191
+ @context.url_for_people_create.should == '/people'
192
+ @context.url_for_people_show(@person).should == '/people/99'
193
+ @context.url_for_people_edit(@person).should == '/people/99/edit'
194
+ @context.url_for_people_update(@person).should == '/people/99'
195
+ @context.url_for_people_destroy(@person).should == '/people/99'
196
+ end
197
+
198
+ it 'should add all restful routes' do
199
+ @app.events.clear
200
+ rest Person
201
+ @app.events[:get].map {|r| r.path}.should == ["/people", "/people/new", "/people/:id", "/people/:id/edit"]
202
+ @app.events[:post].map {|r| r.path}.should == ["/people"]
203
+ @app.events[:put].map {|r| r.path}.should == ["/people/:id"]
204
+ @app.events[:delete].map {|r| r.path}.should == ["/people/:id"]
205
+ end
206
+
207
+ it 'should support models, strings and integers' do
208
+ @person = Person.new('id' => 99)
209
+
210
+ @context.url_for_people_show(@person).should == '/people/99'
211
+ @context.url_for_people_show(99).should == '/people/99'
212
+ @context.url_for_people_show('99').should == '/people/99'
213
+ lambda {@context.url_for_people_show(nil)}.should raise_error('can not generate url for nil')
214
+
215
+ @context.url_for_people_edit(@person).should == '/people/99/edit'
216
+ @context.url_for_people_edit(99).should == '/people/99/edit'
217
+ @context.url_for_people_edit('99').should == '/people/99/edit'
218
+ lambda {@context.url_for_people_edit(nil)}.should raise_error('can not generate url for nil')
219
+
220
+ @context.url_for_people_update(@person).should == '/people/99'
221
+ @context.url_for_people_update(99).should == '/people/99'
222
+ @context.url_for_people_update('99').should == '/people/99'
223
+ lambda {@context.url_for_people_update(nil)}.should raise_error('can not generate url for nil')
224
+
225
+ @context.url_for_people_destroy(@person).should == '/people/99'
226
+ @context.url_for_people_destroy(99).should == '/people/99'
227
+ @context.url_for_people_destroy('99').should == '/people/99'
228
+ lambda {@context.url_for_people_destroy(nil)}.should raise_error('can not generate url for nil')
229
+ end
230
+ end
231
+
232
+
233
+ describe 'as restful service' do
234
+
235
+ before(:each) do
236
+ Sinatra.application = nil
237
+ @app = Sinatra.application
238
+ @app.configure :test do
239
+ set :views, File.join(File.dirname(__FILE__), "views")
240
+ end
241
+
242
+ Person.reset!
243
+ rest Person, :renderer => 'erb'
244
+ end
245
+
246
+ describe 'each method' do
247
+
248
+ # index GET /models
249
+ it 'should list all people on index by their id' do
250
+ get '/people'
251
+ response_should_be 200, '<people><person><id>1</id></person><person><id>2</id></person><person><id>3</id></person></people>'
252
+ end
253
+
254
+ # new GET /models/new
255
+ it 'should prepare an empty item on new' do
256
+ get '/people/new'
257
+ response_should_be 200, '<person><id/><name/></person>'
258
+ end
259
+
260
+ # create POST /models
261
+ it 'should create an item on post' do
262
+ post '/people', :name => 'new resource'
263
+ response_should_be 302, 'person created'
264
+ end
265
+
266
+ # show GET /models/1
267
+ it 'should show an item on get' do
268
+ get '/people/1'
269
+ response_should_be 200, '<person><id>1</id><name>one</name></person>'
270
+ end
271
+
272
+ # edit GET /models/1/edit
273
+ it 'should get the item for editing' do
274
+ get '/people/1/edit'
275
+ response_should_be 200, '<person><id>1</id><name>one</name></person>'
276
+ end
277
+
278
+ # update PUT /models/1
279
+ it 'should update an item on put' do
280
+ put '/people/1', :name => 'another name'
281
+ response_should_be 302, 'person updated'
282
+ end
283
+
284
+ # destroy DELETE /models/1
285
+ it 'should destroy an item on delete' do
286
+ delete '/people/1'
287
+ response_should_be 302, 'person deleted'
288
+ end
289
+
290
+ end
291
+
292
+ describe 'some use cases' do
293
+
294
+ it 'should list all persons' do
295
+ get '/people'
296
+ response_should_be 200, '<people><person><id>1</id></person><person><id>2</id></person><person><id>3</id></person></people>'
297
+ model_should_be 3
298
+ end
299
+
300
+ it 'should read all persons' do
301
+ get '/people'
302
+
303
+ el_people = doc(body).elements.to_a("*/person/id")
304
+ el_people.size.should == 3
305
+ model_should_be 3
306
+
307
+ get "/people/#{el_people[0].text}"
308
+ response_should_be 200, '<person><id>1</id><name>one</name></person>'
309
+ model_should_be 3
310
+
311
+ get "/people/#{el_people[1].text}"
312
+ response_should_be 200, '<person><id>2</id><name>two</name></person>'
313
+ model_should_be 3
314
+
315
+ get "/people/#{el_people[2].text}"
316
+ response_should_be 200, '<person><id>3</id><name>three</name></person>'
317
+ model_should_be 3
318
+ end
319
+
320
+ it 'should create a new person' do
321
+ get '/people'
322
+ response_should_be 200, '<people><person><id>1</id></person><person><id>2</id></person><person><id>3</id></person></people>'
323
+ model_should_be 3
324
+
325
+ get '/people/new'
326
+ response_should_be 200, '<person><id/><name/></person>'
327
+ model_should_be 3
328
+
329
+ post '/people', {:name => 'four'}
330
+ response_should_be 302, 'person created'
331
+ model_should_be 4
332
+
333
+ get '/people'
334
+ response_should_be 200, '<people><person><id>1</id></person><person><id>2</id></person><person><id>3</id></person><person><id>4</id></person></people>'
335
+ model_should_be 4
336
+ end
337
+
338
+ it 'should update a person' do
339
+ get '/people/2'
340
+ response_should_be 200, '<person><id>2</id><name>two</name></person>'
341
+ model_should_be 3
342
+
343
+ put '/people/2', {:name => 'tomorrow'}
344
+ response_should_be 302, 'person updated'
345
+ model_should_be 3
346
+
347
+ get '/people/2'
348
+ response_should_be 200, '<person><id>2</id><name>tomorrow</name></person>'
349
+ model_should_be 3
350
+ end
351
+
352
+ it 'should delete a person' do
353
+ get '/people'
354
+ response_should_be 200, '<people><person><id>1</id></person><person><id>2</id></person><person><id>3</id></person></people>'
355
+ model_should_be 3
356
+
357
+ delete '/people/2'
358
+ response_should_be 302, 'person deleted'
359
+ model_should_be 2
360
+
361
+ get '/people'
362
+ response_should_be 200, '<people><person><id>1</id></person><person><id>3</id></person></people>'
363
+ model_should_be 2
364
+
365
+ get '/people/2'
366
+ response_should_be 404, 'person not found'
367
+ model_should_be 2
368
+ end
369
+
370
+ end
371
+
372
+ describe 'life-cycle' do
373
+
374
+ before(:each) do
375
+ Person.class_eval '@call_order = []; def self.call_order; @call_order; end; def self.call_order=(arr); @call_order = arr; end'
376
+ rest Person, :renderer => 'erb' do
377
+ def before(name)
378
+ Person.call_order << {:method => :before, :called => name}
379
+ end
380
+
381
+ def after(name)
382
+ Person.call_order << {:method => :after, :called => name}
383
+ end
384
+
385
+ def index
386
+ Person.call_order << {:method => :index, :called => :index}
387
+ super
388
+ end
389
+
390
+ def new
391
+ Person.call_order << {:method => :new, :called => :new}
392
+ super
393
+ end
394
+
395
+ def create
396
+ Person.call_order << {:method => :create, :called => :create}
397
+ super
398
+ end
399
+
400
+ def show
401
+ Person.call_order << {:method => :show, :called => :show}
402
+ super
403
+ end
404
+
405
+ def edit
406
+ Person.call_order << {:method => :edit, :called => :edit}
407
+ super
408
+ end
409
+
410
+ def update
411
+ Person.call_order << {:method => :update, :called => :update}
412
+ super
413
+ end
414
+
415
+ def destroy
416
+ Person.call_order << {:method => :destroy, :called => :destroy}
417
+ super
418
+ end
419
+ end
420
+ end
421
+
422
+ it 'should call :before and :after in the right order' do
423
+ call_order_should_be [:before, :index, :after] do get '/people' end
424
+ call_order_should_be [:before, :new, :after] do get '/people/new' end
425
+ call_order_should_be [:before, :create, :after] do post '/people', :name => 'initial name' end
426
+ call_order_should_be [:before, :show, :after] do get '/people/1' end
427
+ call_order_should_be [:before, :edit, :after] do get '/people/1/edit' end
428
+ call_order_should_be [:before, :update, :after] do put '/people/1', :name => 'new name' end
429
+ call_order_should_be [:before, :destroy, :after] do delete '/people/1' end
430
+ end
431
+
432
+ it 'should call :before and :after with the name of the called method' do
433
+ called_should_be :index do get '/people' end
434
+ called_should_be :new do get '/people/new' end
435
+ called_should_be :create do post '/people', :name => 'initial name' end
436
+ called_should_be :show do get '/people/1' end
437
+ called_should_be :edit do get '/people/1/edit' end
438
+ called_should_be :update do put '/people/1', :name => 'new name' end
439
+ called_should_be :destroy do delete '/people/1' end
440
+ end
441
+
442
+ end
443
+
444
+ end
445
+
446
+ end
447
+
448
+
@@ -0,0 +1,4 @@
1
+ <person>
2
+ <id><%= @person.id %></id>
3
+ <name><%= @person.name %></name>
4
+ </person>
@@ -0,0 +1,10 @@
1
+ <people>
2
+ <% @people.each do |person| %>
3
+ <person>
4
+ <id><%= person.id %></id>
5
+ </person>
6
+ <% end %>
7
+ </people>
8
+
9
+
10
+
@@ -0,0 +1,4 @@
1
+ <person>
2
+ <id><%= @person.id %></id>
3
+ <name><%= @person.name %></name>
4
+ </person>
@@ -0,0 +1,4 @@
1
+ <person>
2
+ <id><%= @person.id %></id>
3
+ <name><%= @person.name %></name>
4
+ </person>
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: blindgaenger-sinatra-rest
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.2"
5
+ platform: ruby
6
+ authors:
7
+ - blindgaenger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-28 00:00:00 -08: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/rest.rb
47
+ - spec/rest_spec.rb
48
+ - spec/views/people/edit.erb
49
+ - spec/views/people/index.erb
50
+ - spec/views/people/new.erb
51
+ - spec/views/people/show.erb
52
+ has_rdoc: "false"
53
+ homepage: http://github.com/blindgaenger/sinatra-rest
54
+ post_install_message:
55
+ rdoc_options: []
56
+
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: "0"
70
+ version:
71
+ requirements: []
72
+
73
+ rubyforge_project:
74
+ rubygems_version: 1.2.0
75
+ signing_key:
76
+ specification_version: 2
77
+ summary: Generates RESTful routes for the models of a Sinatra application
78
+ test_files: []
79
+