blindgaenger-sinatra-rest 0.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +4 -5
- data/lib/sinatra/rest.rb +153 -0
- data/lib/sinatra/rest/adapters.rb +24 -0
- data/lib/sinatra/rest/controller.tpl.rb +65 -0
- data/lib/sinatra/rest/helpers.tpl.rb +49 -0
- data/lib/sinatra/rest/routes.tpl.rb +78 -0
- data/test/call_order_spec.rb +111 -0
- data/test/crud_spec.rb +99 -0
- data/test/helper.rb +151 -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 +19 -9
- data/lib/rest.rb +0 -278
- data/spec/rest_spec.rb +0 -448
- data/spec/views/people/edit.erb +0 -4
- data/spec/views/people/index.erb +0 -10
- data/spec/views/people/new.erb +0 -4
- data/spec/views/people/show.erb +0 -4
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,151 @@
|
|
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 {
|
33
|
+
rest model, options
|
34
|
+
}
|
35
|
+
instance = @app.new
|
36
|
+
instance.instance_eval(&block) if block_given?
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
#
|
41
|
+
# normalize for easier testing
|
42
|
+
def normalized_response
|
43
|
+
return status, body.gsub(/>\s+</, '><').strip
|
44
|
+
end
|
45
|
+
|
46
|
+
# index GET /models
|
47
|
+
def index(url)
|
48
|
+
get url
|
49
|
+
normalized_response
|
50
|
+
end
|
51
|
+
|
52
|
+
# new GET /models/new
|
53
|
+
def new(url)
|
54
|
+
get url
|
55
|
+
normalized_response
|
56
|
+
end
|
57
|
+
|
58
|
+
# create POST /models
|
59
|
+
def create(url, params={})
|
60
|
+
post url, params
|
61
|
+
normalized_response
|
62
|
+
end
|
63
|
+
|
64
|
+
# show GET /models/1
|
65
|
+
def show(url)
|
66
|
+
get url
|
67
|
+
normalized_response
|
68
|
+
end
|
69
|
+
|
70
|
+
# edit GET /models/1/edit
|
71
|
+
def edit(url)
|
72
|
+
get url
|
73
|
+
normalized_response
|
74
|
+
end
|
75
|
+
|
76
|
+
# update PUT /models/1
|
77
|
+
def update(url, params={})
|
78
|
+
put url, params
|
79
|
+
normalized_response
|
80
|
+
end
|
81
|
+
|
82
|
+
# destroy DELETE /models/1
|
83
|
+
def destroy(url)
|
84
|
+
delete url
|
85
|
+
normalized_response
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
##
|
90
|
+
## kind of a 'minimal model'
|
91
|
+
class Person
|
92
|
+
attr_accessor :id
|
93
|
+
attr_accessor :name
|
94
|
+
|
95
|
+
def initialize(*args)
|
96
|
+
#puts "new #{args.inspect}"
|
97
|
+
if args.size == 0
|
98
|
+
@id = nil
|
99
|
+
@name = nil
|
100
|
+
elsif args.size == 2
|
101
|
+
@id = args[0].to_i
|
102
|
+
@name = args[1]
|
103
|
+
else args.size == 1
|
104
|
+
update_attributes(args[0])
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def save
|
109
|
+
#puts "save #{@id}"
|
110
|
+
@@people << self
|
111
|
+
self.id = @@people.size
|
112
|
+
end
|
113
|
+
|
114
|
+
def update_attributes(hash)
|
115
|
+
#puts "update_attributes #{hash.inspect}"
|
116
|
+
unless hash.empty?
|
117
|
+
@id = hash['id'].to_i if hash.include?('id')
|
118
|
+
@name = hash['name'] if hash.include?('name')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.delete(id)
|
123
|
+
#puts "delete #{id}"
|
124
|
+
@@people.delete_if {|person| person.id == id.to_i}
|
125
|
+
end
|
126
|
+
|
127
|
+
@@people = []
|
128
|
+
|
129
|
+
def self.all(criteria={})
|
130
|
+
#puts 'all'
|
131
|
+
return @@people
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.find_by_id(id)
|
135
|
+
#puts "find_by_id #{id}"
|
136
|
+
all.find {|f| f.id == id.to_i}
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.clear!
|
140
|
+
@@people = []
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.reset!
|
144
|
+
clear!
|
145
|
+
Person.new(1, 'one').save
|
146
|
+
Person.new(2, 'two').save
|
147
|
+
Person.new(3, 'three').save
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
|
@@ -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
|