sinatra_resource 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.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
- raise "Unexpected Category count" unless Category.count == 0
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
@@ -7,7 +7,9 @@ class CategoriesSourcesGetManyResourceTest < ResourceTestCase
7
7
  def app; Categories end
8
8
 
9
9
  before do
10
- raise "Unexpected Source count" unless Source.count == 0
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 SOURCES, actual.sort
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
- raise "Unexpected Source count" unless Source.count == 0
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 SOURCES, actual.sort
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
- raise "Unexpected Source count" unless User.count == 3
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}",
@@ -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][to_r_or_w(action)]
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
- invalid << property if !authorized?(action, role, resource_config, property.intern)
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 all +model+ documents.
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.find(:all)
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
 
@@ -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.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-26}
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.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-26 00:00:00 -04:00
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