blindgaenger-sinatra-rest 0.2 → 0.3.0
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/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
|