sinatra_resource 0.2.2 → 0.2.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/VERSION +1 -1
- data/examples/datacatalog/test/helpers/shared/common_body_responses.rb +12 -0
- data/examples/datacatalog/test/helpers/shared/status_codes.rb +13 -0
- data/examples/datacatalog/test/resources/categories/categories_get_many_test.rb +3 -1
- data/examples/datacatalog/test/resources/categories_sources/categories_sources_get_many_test.rb +5 -4
- data/examples/datacatalog/test/resources/sources/sources_get_many_search_test.rb +92 -0
- data/examples/datacatalog/test/resources/sources/sources_get_many_test.rb +3 -4
- data/examples/datacatalog/test/resources/users/users_get_many_test.rb +3 -1
- data/lib/builder/helpers.rb +14 -5
- data/lib/builder/mongo_helpers.rb +46 -3
- data/sinatra_resource.gemspec +4 -2
- metadata +4 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.3
|
@@ -11,5 +11,17 @@ class ResourceTestCase
|
|
11
11
|
assert_equal [], parsed_response_body
|
12
12
|
end
|
13
13
|
end
|
14
|
+
|
15
|
+
shared "content type header indicates JSON" do
|
16
|
+
test "should have JSON content type" do
|
17
|
+
assert_equal "application/json", last_response.headers["Content-Type"]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
shared "content type header not set" do
|
22
|
+
test "should not have Content-Type set" do
|
23
|
+
assert_equal nil, last_response.headers["Content-Type"]
|
24
|
+
end
|
25
|
+
end
|
14
26
|
|
15
27
|
end
|
@@ -4,6 +4,8 @@ class ResourceTestCase
|
|
4
4
|
test "status should be 200 Ok" do
|
5
5
|
assert_equal 200, last_response.status
|
6
6
|
end
|
7
|
+
|
8
|
+
use "content type header indicates JSON"
|
7
9
|
end
|
8
10
|
|
9
11
|
shared "return 201 Created" do
|
@@ -16,6 +18,8 @@ class ResourceTestCase
|
|
16
18
|
generic_uri = %r{^http://localhost}
|
17
19
|
assert_match generic_uri, last_response.headers["Location"]
|
18
20
|
end
|
21
|
+
|
22
|
+
use "content type header indicates JSON"
|
19
23
|
end
|
20
24
|
|
21
25
|
shared "return 204 No Content" do
|
@@ -24,30 +28,39 @@ class ResourceTestCase
|
|
24
28
|
end
|
25
29
|
|
26
30
|
use "return an empty response body"
|
31
|
+
use "content type header not set"
|
27
32
|
end
|
28
33
|
|
29
34
|
shared "return 400 Bad Request" do
|
30
35
|
test "status should be 400 Bad Request" do
|
31
36
|
assert_equal 400, last_response.status
|
32
37
|
end
|
38
|
+
|
39
|
+
use "content type header indicates JSON"
|
33
40
|
end
|
34
41
|
|
35
42
|
shared "return 401 Unauthorized" do
|
36
43
|
test "status should be 401 Unauthorized" do
|
37
44
|
assert_equal 401, last_response.status
|
38
45
|
end
|
46
|
+
|
47
|
+
use "content type header indicates JSON"
|
39
48
|
end
|
40
49
|
|
41
50
|
shared "return 403 Forbidden" do
|
42
51
|
test "status should be 403 Forbidden" do
|
43
52
|
assert_equal 403, last_response.status
|
44
53
|
end
|
54
|
+
|
55
|
+
use "content type header indicates JSON"
|
45
56
|
end
|
46
57
|
|
47
58
|
shared "return 404 Not Found" do
|
48
59
|
test "status should be 404 Not Found" do
|
49
60
|
assert_equal 404, last_response.status
|
50
61
|
end
|
62
|
+
|
63
|
+
use "content type header indicates JSON"
|
51
64
|
end
|
52
65
|
|
53
66
|
shared "return 404 Not Found with empty response body" do
|
@@ -7,7 +7,9 @@ class CategoriesGetManyResourceTest < ResourceTestCase
|
|
7
7
|
def app; Categories end
|
8
8
|
|
9
9
|
before do
|
10
|
-
|
10
|
+
unless 0 == (c = Category.count)
|
11
|
+
raise "Expected 0 for Category.count, found #{c}"
|
12
|
+
end
|
11
13
|
@categories = 3.times.map do |i|
|
12
14
|
create_category(:name => "Category #{i}")
|
13
15
|
end
|
data/examples/datacatalog/test/resources/categories_sources/categories_sources_get_many_test.rb
CHANGED
@@ -7,7 +7,9 @@ class CategoriesSourcesGetManyResourceTest < ResourceTestCase
|
|
7
7
|
def app; Categories end
|
8
8
|
|
9
9
|
before do
|
10
|
-
|
10
|
+
unless 0 == (c = Source.count)
|
11
|
+
raise "Expected 0 for Source.count, found #{c}"
|
12
|
+
end
|
11
13
|
@category = create_category
|
12
14
|
@sources = 3.times.map do |i|
|
13
15
|
create_source(:title => "Source #{i}")
|
@@ -29,6 +31,7 @@ class CategoriesSourcesGetManyResourceTest < ResourceTestCase
|
|
29
31
|
:category_id => @other_category.id
|
30
32
|
)
|
31
33
|
end
|
34
|
+
@source_titles = ["Source 0", "Source 1", "Source 2"].sort
|
32
35
|
end
|
33
36
|
|
34
37
|
after do
|
@@ -40,8 +43,6 @@ class CategoriesSourcesGetManyResourceTest < ResourceTestCase
|
|
40
43
|
@category.destroy
|
41
44
|
end
|
42
45
|
|
43
|
-
SOURCES = ["Source 0", "Source 1", "Source 2"].sort
|
44
|
-
|
45
46
|
context "get /:id/sources/" do
|
46
47
|
context "anonymous" do
|
47
48
|
before do
|
@@ -82,7 +83,7 @@ class CategoriesSourcesGetManyResourceTest < ResourceTestCase
|
|
82
83
|
|
83
84
|
test "body should have correct source titles" do
|
84
85
|
actual = parsed_response_body.map { |e| e["title"] }
|
85
|
-
assert_equal
|
86
|
+
assert_equal @source_titles, actual.sort
|
86
87
|
end
|
87
88
|
|
88
89
|
docs_properties %w(title url raw id created_at updated_at)
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../helpers/resource_test_helper')
|
2
|
+
|
3
|
+
class SourcesGetManyResourceTest < ResourceTestCase
|
4
|
+
|
5
|
+
include DataCatalog
|
6
|
+
|
7
|
+
def app; Sources end
|
8
|
+
|
9
|
+
before do
|
10
|
+
Source.destroy_all unless Source.count == 0
|
11
|
+
@sources = 3.times.map do |i|
|
12
|
+
create_source(:title => "Source #{i}")
|
13
|
+
end
|
14
|
+
@source_titles = ["Source 0", "Source 1", "Source 2"].sort
|
15
|
+
end
|
16
|
+
|
17
|
+
after do
|
18
|
+
@sources.each { |x| x.destroy } if @sources
|
19
|
+
end
|
20
|
+
|
21
|
+
context "title='Source 2'" do
|
22
|
+
before do
|
23
|
+
@search_params = { :filter => "title='Source 2'" }
|
24
|
+
end
|
25
|
+
|
26
|
+
context "get /" do
|
27
|
+
context "anonymous" do
|
28
|
+
before do
|
29
|
+
get "/", @search_params
|
30
|
+
end
|
31
|
+
|
32
|
+
use "return 401 because the API key is missing"
|
33
|
+
end
|
34
|
+
|
35
|
+
context "incorrect API key" do
|
36
|
+
before do
|
37
|
+
get "/", @search_params.merge(:api_key => BAD_API_KEY)
|
38
|
+
end
|
39
|
+
|
40
|
+
use "return 401 because the API key is invalid"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
%w(basic curator admin).each do |role|
|
45
|
+
context "#{role} : get /" do
|
46
|
+
before do
|
47
|
+
get "/", @search_params.merge(:api_key => api_key_for(role))
|
48
|
+
end
|
49
|
+
|
50
|
+
use "return 200 Ok"
|
51
|
+
|
52
|
+
test "body should have 1 source" do
|
53
|
+
assert_equal 1, parsed_response_body.length
|
54
|
+
end
|
55
|
+
|
56
|
+
test "body should have correct source" do
|
57
|
+
assert_equal 'Source 2', parsed_response_body[0]['title']
|
58
|
+
end
|
59
|
+
|
60
|
+
docs_properties %w(title url raw categories id created_at updated_at)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "title:Source" do
|
66
|
+
before do
|
67
|
+
@search_params = { :filter => "title:Source" }
|
68
|
+
end
|
69
|
+
|
70
|
+
%w(basic curator admin).each do |role|
|
71
|
+
context "#{role} : get /" do
|
72
|
+
before do
|
73
|
+
get "/", @search_params.merge(:api_key => api_key_for(role))
|
74
|
+
end
|
75
|
+
|
76
|
+
use "return 200 Ok"
|
77
|
+
|
78
|
+
test "body should have 3 sources" do
|
79
|
+
assert_equal 3, parsed_response_body.length
|
80
|
+
end
|
81
|
+
|
82
|
+
test "body should have correct source titles" do
|
83
|
+
actual = parsed_response_body.map { |e| e["title"] }
|
84
|
+
assert_equal @source_titles, actual.sort
|
85
|
+
end
|
86
|
+
|
87
|
+
docs_properties %w(title url raw categories id created_at updated_at)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
@@ -7,18 +7,17 @@ class SourcesGetManyResourceTest < ResourceTestCase
|
|
7
7
|
def app; Sources end
|
8
8
|
|
9
9
|
before do
|
10
|
-
|
10
|
+
Source.destroy_all unless Source.count == 0
|
11
11
|
@sources = 3.times.map do |i|
|
12
12
|
create_source(:title => "Source #{i}")
|
13
13
|
end
|
14
|
+
@source_titles = ["Source 0", "Source 1", "Source 2"].sort
|
14
15
|
end
|
15
16
|
|
16
17
|
after do
|
17
18
|
@sources.each { |x| x.destroy } if @sources
|
18
19
|
end
|
19
20
|
|
20
|
-
SOURCES = ["Source 0", "Source 1", "Source 2"].sort
|
21
|
-
|
22
21
|
context "get /" do
|
23
22
|
context "anonymous" do
|
24
23
|
before do
|
@@ -51,7 +50,7 @@ class SourcesGetManyResourceTest < ResourceTestCase
|
|
51
50
|
|
52
51
|
test "body should have correct source titles" do
|
53
52
|
actual = parsed_response_body.map { |e| e["title"] }
|
54
|
-
assert_equal
|
53
|
+
assert_equal @source_titles, actual.sort
|
55
54
|
end
|
56
55
|
|
57
56
|
docs_properties %w(title url raw categories id created_at updated_at)
|
@@ -7,7 +7,9 @@ class UsersGetManyResourceTest < ResourceTestCase
|
|
7
7
|
def app; Users end
|
8
8
|
|
9
9
|
before do
|
10
|
-
|
10
|
+
unless 3 == (c = User.count)
|
11
|
+
raise "Expected 3 for User.count, found #{c}"
|
12
|
+
end
|
11
13
|
@users = 3.times.map do |i|
|
12
14
|
create_user(
|
13
15
|
:name => "User #{i}",
|
data/lib/builder/helpers.rb
CHANGED
@@ -4,6 +4,8 @@ module SinatraResource
|
|
4
4
|
|
5
5
|
module Helpers
|
6
6
|
|
7
|
+
FILTER_KEY = "filter"
|
8
|
+
|
7
9
|
# Build a resource, based on +document+, appropriate for +role+.
|
8
10
|
#
|
9
11
|
# @param [Symbol] role
|
@@ -159,9 +161,13 @@ module SinatraResource
|
|
159
161
|
# a role (such as :anonymous, :basic, or :admin)
|
160
162
|
def minimum_role(action, resource_config, property=nil)
|
161
163
|
if property.nil?
|
162
|
-
resource_config[:permission][to_read_or_modify(action)]
|
164
|
+
hash = resource_config[:permission][to_read_or_modify(action)]
|
163
165
|
else
|
164
|
-
resource_config[:properties][property]
|
166
|
+
hash = resource_config[:properties][property]
|
167
|
+
unless hash
|
168
|
+
raise Error, "bad configuration for #{property.inspect}"
|
169
|
+
end
|
170
|
+
hash[to_r_or_w(action)]
|
165
171
|
end || :anonymous
|
166
172
|
end
|
167
173
|
|
@@ -246,7 +252,7 @@ module SinatraResource
|
|
246
252
|
def lookup_role(document=nil)
|
247
253
|
raise NotImplementedError
|
248
254
|
end
|
249
|
-
|
255
|
+
|
250
256
|
# Are the params suitable for +action+? Raise 400 Bad Request if not.
|
251
257
|
#
|
252
258
|
# @param [Symbol] action
|
@@ -260,7 +266,7 @@ module SinatraResource
|
|
260
266
|
def params_check_action(action)
|
261
267
|
case action
|
262
268
|
when :read
|
263
|
-
unless params.empty?
|
269
|
+
unless params.reject { |k, v| k == FILTER_KEY }.empty?
|
264
270
|
error 400, convert(body_for(:non_empty_params))
|
265
271
|
end
|
266
272
|
when :create
|
@@ -292,7 +298,10 @@ module SinatraResource
|
|
292
298
|
def params_check_action_and_role(action, role, resource_config)
|
293
299
|
invalid = []
|
294
300
|
params.each_pair do |property, value|
|
295
|
-
|
301
|
+
next if property == FILTER_KEY
|
302
|
+
if !authorized?(action, role, resource_config, property.intern)
|
303
|
+
invalid << property
|
304
|
+
end
|
296
305
|
end
|
297
306
|
unless invalid.empty?
|
298
307
|
error 400, convert(body_for(:invalid_params, invalid))
|
@@ -1,10 +1,12 @@
|
|
1
|
+
gem 'query_string_filter', '>= 0.1.1'
|
2
|
+
require 'query_string_filter'
|
3
|
+
|
1
4
|
module SinatraResource
|
2
5
|
|
3
6
|
class Builder
|
4
7
|
|
5
8
|
module MongoHelpers
|
6
9
|
|
7
|
-
|
8
10
|
# Make sure that +parent+ document is related to the +child+ document
|
9
11
|
# by way of +association+. If not, return 404 Not Found.
|
10
12
|
#
|
@@ -59,14 +61,16 @@ module SinatraResource
|
|
59
61
|
document
|
60
62
|
end
|
61
63
|
|
62
|
-
# Find
|
64
|
+
# Find +model+ documents: find all documents if no params, otherwise
|
65
|
+
# find selected documents.
|
63
66
|
#
|
64
67
|
# @param [Class] model
|
65
68
|
# a class that includes MongoMapper::Document
|
66
69
|
#
|
67
70
|
# @return [Array<MongoMapper::Document>]
|
68
71
|
def find_documents!(model)
|
69
|
-
model.
|
72
|
+
return(model.all) if params.empty?
|
73
|
+
model.all(make_conditions(params, model))
|
70
74
|
end
|
71
75
|
|
72
76
|
# Delegates to application, who should use custom logic to related
|
@@ -105,6 +109,10 @@ module SinatraResource
|
|
105
109
|
|
106
110
|
# Update a document with +id+ from params. If not valid, returns 400.
|
107
111
|
#
|
112
|
+
# @param [Hash] model
|
113
|
+
#
|
114
|
+
# @param [String] id
|
115
|
+
#
|
108
116
|
# @return [MongoMapper::Document]
|
109
117
|
def update_document!(model, id)
|
110
118
|
document = model.update(id, params)
|
@@ -113,6 +121,41 @@ module SinatraResource
|
|
113
121
|
end
|
114
122
|
document
|
115
123
|
end
|
124
|
+
|
125
|
+
protected
|
126
|
+
|
127
|
+
PATTERNS = [
|
128
|
+
[ %r{^<=(.*)} , '$lte' ],
|
129
|
+
[ %r{^<(.*)} , '$lt' ],
|
130
|
+
[ %r{^>=(.*)} , '$gte' ],
|
131
|
+
[ %r{^>(.*)} , '$gt' ]
|
132
|
+
]
|
133
|
+
|
134
|
+
QS_FILTER = QueryStringFilter.new
|
135
|
+
|
136
|
+
# Build conditions hash based on +params+.
|
137
|
+
#
|
138
|
+
# @param [Hash] params
|
139
|
+
#
|
140
|
+
# @param [Class] model
|
141
|
+
# a class that includes MongoMapper::Document
|
142
|
+
#
|
143
|
+
# @return [Hash]
|
144
|
+
def make_conditions(params, model)
|
145
|
+
filter_string = params["filter"]
|
146
|
+
return {} unless filter_string
|
147
|
+
QS_FILTER.parse(filter_string)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Returns a typecasted +value+. (Uses +model+ and +key_string+ to
|
151
|
+
# figure out how to typecast it.)
|
152
|
+
#
|
153
|
+
# @return [Object]
|
154
|
+
def typecast(model, key_string, value)
|
155
|
+
dummy = model.new
|
156
|
+
dummy.send(:"#{key_string}=", value)
|
157
|
+
dummy.send(:"#{key_string}")
|
158
|
+
end
|
116
159
|
|
117
160
|
end
|
118
161
|
|
data/sinatra_resource.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{sinatra_resource}
|
8
|
-
s.version = "0.2.
|
8
|
+
s.version = "0.2.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["David James"]
|
12
|
-
s.date = %q{2009-10-
|
12
|
+
s.date = %q{2009-10-27}
|
13
13
|
s.description = %q{A DSL for creating RESTful actions with Sinatra and MongoMapper. It embraces the Resource Oriented Architecture as explained by Leonard Richardson and Sam Ruby.}
|
14
14
|
s.email = %q{djames@sunlightfoundation.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -70,6 +70,7 @@ Gem::Specification.new do |s|
|
|
70
70
|
"examples/datacatalog/test/resources/categories_sources/categories_sources_post_test.rb",
|
71
71
|
"examples/datacatalog/test/resources/categories_sources/categories_sources_put_test.rb",
|
72
72
|
"examples/datacatalog/test/resources/sources/sources_delete_test.rb",
|
73
|
+
"examples/datacatalog/test/resources/sources/sources_get_many_search_test.rb",
|
73
74
|
"examples/datacatalog/test/resources/sources/sources_get_many_test.rb",
|
74
75
|
"examples/datacatalog/test/resources/sources/sources_get_one_test.rb",
|
75
76
|
"examples/datacatalog/test/resources/sources/sources_post_test.rb",
|
@@ -150,6 +151,7 @@ Gem::Specification.new do |s|
|
|
150
151
|
"examples/datacatalog/test/resources/categories_sources/categories_sources_post_test.rb",
|
151
152
|
"examples/datacatalog/test/resources/categories_sources/categories_sources_put_test.rb",
|
152
153
|
"examples/datacatalog/test/resources/sources/sources_delete_test.rb",
|
154
|
+
"examples/datacatalog/test/resources/sources/sources_get_many_search_test.rb",
|
153
155
|
"examples/datacatalog/test/resources/sources/sources_get_many_test.rb",
|
154
156
|
"examples/datacatalog/test/resources/sources/sources_get_one_test.rb",
|
155
157
|
"examples/datacatalog/test/resources/sources/sources_post_test.rb",
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sinatra_resource
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David James
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-10-
|
12
|
+
date: 2009-10-27 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -155,6 +155,7 @@ files:
|
|
155
155
|
- examples/datacatalog/test/resources/categories_sources/categories_sources_post_test.rb
|
156
156
|
- examples/datacatalog/test/resources/categories_sources/categories_sources_put_test.rb
|
157
157
|
- examples/datacatalog/test/resources/sources/sources_delete_test.rb
|
158
|
+
- examples/datacatalog/test/resources/sources/sources_get_many_search_test.rb
|
158
159
|
- examples/datacatalog/test/resources/sources/sources_get_many_test.rb
|
159
160
|
- examples/datacatalog/test/resources/sources/sources_get_one_test.rb
|
160
161
|
- examples/datacatalog/test/resources/sources/sources_post_test.rb
|
@@ -257,6 +258,7 @@ test_files:
|
|
257
258
|
- examples/datacatalog/test/resources/categories_sources/categories_sources_post_test.rb
|
258
259
|
- examples/datacatalog/test/resources/categories_sources/categories_sources_put_test.rb
|
259
260
|
- examples/datacatalog/test/resources/sources/sources_delete_test.rb
|
261
|
+
- examples/datacatalog/test/resources/sources/sources_get_many_search_test.rb
|
260
262
|
- examples/datacatalog/test/resources/sources/sources_get_many_test.rb
|
261
263
|
- examples/datacatalog/test/resources/sources/sources_get_one_test.rb
|
262
264
|
- examples/datacatalog/test/resources/sources/sources_post_test.rb
|