rackjson 0.3.2 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +94 -0
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/rackjson/collection.rb +43 -2
- data/lib/rackjson/document.rb +12 -1
- data/lib/rackjson/end_point.rb +9 -1
- data/lib/rackjson/extensions/BSON/object_id.rb +1 -1
- data/lib/rackjson/extensions/core/array.rb +12 -0
- data/lib/rackjson/extensions/core/string.rb +13 -0
- data/lib/rackjson/json_document.rb +2 -2
- data/lib/rackjson/json_query.rb +10 -2
- data/lib/rackjson/mongo_document.rb +1 -1
- data/lib/rackjson/request.rb +69 -9
- data/lib/rackjson/resource.rb +57 -19
- data/lib/rackjson/response.rb +3 -3
- data/lib/rackjson.rb +3 -1
- data/rackjson.gemspec +9 -6
- data/test/test_collection.rb +125 -0
- data/test/test_document.rb +8 -1
- data/test/test_json_document.rb +1 -1
- data/test/test_json_query.rb +6 -0
- data/test/test_mongo_document.rb +1 -1
- data/test/test_resource.rb +164 -1
- data/test/test_response.rb +5 -0
- metadata +10 -7
- data/test/test_resource_modifier.rb +0 -73
data/README.markdown
CHANGED
@@ -53,6 +53,8 @@ When creating resources with either the public or private resources the specifie
|
|
53
53
|
|
54
54
|
### REST API
|
55
55
|
|
56
|
+
#### Collections
|
57
|
+
|
56
58
|
To see what actions are available on the notes resource:
|
57
59
|
|
58
60
|
curl -i -XOPTIONS http://localhost:9292/notes
|
@@ -148,6 +150,98 @@ Finally a resource can be deleted using a DELETE request
|
|
148
150
|
|
149
151
|
{"ok": "true"}
|
150
152
|
|
153
|
+
#### Nested Documents
|
154
|
+
|
155
|
+
Rack::JSON fully supports nested documents. Any element within a document can be accessed directly regardless of how deeply it is nested. For example if the following document exists at the location `/notes/1`
|
156
|
+
|
157
|
+
{
|
158
|
+
"_id": 1,
|
159
|
+
"title": "Nested Document",
|
160
|
+
"author": {
|
161
|
+
"name": "Bob",
|
162
|
+
"contacts": {
|
163
|
+
"email": "bob@mail.com"
|
164
|
+
}
|
165
|
+
},
|
166
|
+
"viewed_by": [1, 5, 12, 87],
|
167
|
+
"comments": [{
|
168
|
+
"user_id": 1
|
169
|
+
"text": "awesome!"
|
170
|
+
}]
|
171
|
+
}
|
172
|
+
|
173
|
+
To get just all the comments we can make a get request to `/notes/1/comments`
|
174
|
+
|
175
|
+
curl -i http://localhost:9292/notes/1/comments
|
176
|
+
|
177
|
+
HTTP/1.1 200 OK
|
178
|
+
Connection: close
|
179
|
+
Date: Sun, 29 Aug 2010 19:43:09 GMT
|
180
|
+
Content-Type: application/json
|
181
|
+
Content-Length: 33
|
182
|
+
|
183
|
+
[{"text":"awesome!","user_id":1}]
|
184
|
+
|
185
|
+
We can also get just the first comment by passing in the index of that comment in the array, to get the first comment make a GET request to `/notes/1/comments/0`
|
186
|
+
|
187
|
+
curl -i http://localhost:9292/notes/1/comments/0
|
188
|
+
|
189
|
+
HTTP/1.1 200 OK
|
190
|
+
Connection: close
|
191
|
+
Date: Sun, 29 Aug 2010 19:45:28 GMT
|
192
|
+
Content-Type: application/json
|
193
|
+
Content-Length: 31
|
194
|
+
|
195
|
+
{"text":"awesome!","user_id":1}
|
196
|
+
|
197
|
+
If we try and get a comment that doesn't exist in the array a 404 is returned.
|
198
|
+
|
199
|
+
curl -i http://localhost:9292/notes/1/comments/1
|
200
|
+
|
201
|
+
HTTP/1.1 404 Not Found
|
202
|
+
Connection: close
|
203
|
+
Date: Sun, 29 Aug 2010 19:46:46 GMT
|
204
|
+
Content-Type: text/plain
|
205
|
+
Content-Length: 15
|
206
|
+
|
207
|
+
field not found
|
208
|
+
|
209
|
+
Any field within the document is accessable in this way, just append the field name or the index of the item within an array to the url.
|
210
|
+
|
211
|
+
As well as providing read access to any field within a document Rack::JSON also allows you to modify or remove any field within a document. To change the value of a field make a PUT request to the fields url and pass the value you want as the body.
|
212
|
+
|
213
|
+
Both simple values, numbers and strings, or JSON structures can be set in this way, however if you want to set a field to contain a JSON structure (array or object) you must set the correct content type for the request, application/json.
|
214
|
+
|
215
|
+
#### Array Modifiers
|
216
|
+
|
217
|
+
Fields within a document that are arrays also support atomic push and pulls for adding and removing items from an array.
|
218
|
+
|
219
|
+
To push a new item onto an array we make a post request to _push
|
220
|
+
|
221
|
+
curl -i -XPOST -d'101' http://localhost:9292/notes/1/viewed_by/_push
|
222
|
+
|
223
|
+
The above will push the value 101 onto the viewed by array within the note with _id 1.
|
224
|
+
|
225
|
+
Similarly an item can be pulled from an array using _pull
|
226
|
+
|
227
|
+
curl -i -XPOST -d'101' http://localhost:9292/notes/1/viewed_by/_pull
|
228
|
+
|
229
|
+
This will remove the value 101 from the viewed_by array if it already exists.
|
230
|
+
To remove or add more than one item from an array we can use either _pull_all or _push_all passing in an array each time.
|
231
|
+
|
232
|
+
Arrays within documents can also be treated like sets and only add items that do not currently exists by using the add_to_set command like below
|
233
|
+
|
234
|
+
curl -i -XPOST -d'101' http://localhost:9292/notes/1/viewed_by/_add_to_set
|
235
|
+
|
236
|
+
This will only add the value 101 to the viewed_by array if it doesn't already exist.
|
237
|
+
|
238
|
+
#### Incrementing & Decrementing
|
239
|
+
|
240
|
+
RackJSON provides a simple means of incrementing and decrementing counters within a document, simply make a post request to either _increment or _decrement as shown below
|
241
|
+
|
242
|
+
curl -i -XPOST http://localhost:9292/notes/1/views/_increment
|
243
|
+
curl -i -XPOST http://localhost:9292/notes/1/views/_decrement
|
244
|
+
|
151
245
|
### JSON Query
|
152
246
|
|
153
247
|
RackJSON supports querying of the resources using JSONQuery style syntax. Pass the JSONQuery as query string parameters when making a get request.
|
data/Rakefile
CHANGED
@@ -10,7 +10,7 @@ begin
|
|
10
10
|
gem.email = "oliver.n@new-bamboo.co.uk"
|
11
11
|
gem.homepage = "http://github.com/olivernn/rackjson"
|
12
12
|
gem.authors = ["Oliver Nightingale"]
|
13
|
-
gem.add_dependency('mongo', '>=1.
|
13
|
+
gem.add_dependency('mongo', '>=1.1.0')
|
14
14
|
gem.add_dependency('mongo_ext', '>=0.19.1')
|
15
15
|
gem.add_dependency('json', '=1.2.3')
|
16
16
|
gem.add_dependency('rack', '>=1.0.1')
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.1
|
data/lib/rackjson/collection.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
|
+
require 'enumerator'
|
2
|
+
|
1
3
|
module Rack::JSON
|
2
4
|
class Collection
|
5
|
+
|
6
|
+
class Rack::JSON::Collection::DataTypeError < TypeError ; end
|
7
|
+
|
3
8
|
def initialize(collection)
|
4
9
|
@collection = collection
|
5
10
|
end
|
@@ -8,6 +13,10 @@ module Rack::JSON
|
|
8
13
|
@collection.remove(prepared(selector))
|
9
14
|
end
|
10
15
|
|
16
|
+
def delete_field(selector, field)
|
17
|
+
_update(prepared(selector), { "$unset" => { dot_notate(field) => 1 }, "$set" => { :updated_at => Time.now }})
|
18
|
+
end
|
19
|
+
|
11
20
|
def exists?(selector)
|
12
21
|
!@collection.find(prepared(selector)).to_a.empty?
|
13
22
|
end
|
@@ -16,8 +25,28 @@ module Rack::JSON
|
|
16
25
|
@collection.find(selector, options).inject([]) {|documents, row| documents << Rack::JSON::Document.create(row)}
|
17
26
|
end
|
18
27
|
|
28
|
+
def find_field(selector, fields, options={})
|
29
|
+
document = find_one(prepared(selector))
|
30
|
+
document ? document.field(fields) : nil
|
31
|
+
end
|
32
|
+
|
19
33
|
def find_one(selector, options={})
|
20
|
-
find(prepared(selector), options).first
|
34
|
+
find(prepared(selector), options.merge(:limit => 0)).first
|
35
|
+
end
|
36
|
+
|
37
|
+
def decrement(selector, field, value=1)
|
38
|
+
_update(prepared(selector), { "$inc" => { dot_notate(field) => -1 * (value || 1) }, "$set" => { :updated_at => Time.now }})
|
39
|
+
end
|
40
|
+
|
41
|
+
def increment(selector, field, value=1)
|
42
|
+
_update(prepared(selector), { "$inc" => { dot_notate(field) => value || 1 }, "$set" => { :updated_at => Time.now }})
|
43
|
+
end
|
44
|
+
|
45
|
+
[:pull, :pull_all, :push, :push_all, :add_to_set].each do |method_name|
|
46
|
+
define_method method_name do |selector, field, value|
|
47
|
+
modifier = "$#{method_name.to_s.split('_').to_enum.each_with_index.map { |w, i| i == 0 ? w : w.capitalize }.join}"
|
48
|
+
_update(prepared(selector), { modifier => { dot_notate(field) => value }, "$set" => { :updated_at => Time.now }})
|
49
|
+
end
|
21
50
|
end
|
22
51
|
|
23
52
|
def save(document)
|
@@ -26,16 +55,28 @@ module Rack::JSON
|
|
26
55
|
|
27
56
|
def update(selector, document, query={})
|
28
57
|
if exists?(prepared(selector).merge(query))
|
29
|
-
|
58
|
+
_update(prepared(selector).merge(query), document.to_h, :upsert => false)
|
30
59
|
else
|
31
60
|
false
|
32
61
|
end
|
33
62
|
end
|
34
63
|
|
64
|
+
def update_field(selector, field, value)
|
65
|
+
_update(prepared(selector), { "$set" => { dot_notate(field) => value, :updated_at => Time.now }})
|
66
|
+
end
|
67
|
+
|
35
68
|
private
|
36
69
|
|
70
|
+
def dot_notate field
|
71
|
+
field.is_a?(Array) ? field.join(".") : field
|
72
|
+
end
|
73
|
+
|
37
74
|
def prepared selector
|
38
75
|
selector.is_a?(Hash) ? selector : {:_id => selector}
|
39
76
|
end
|
77
|
+
|
78
|
+
def _update(query, hash, options={})
|
79
|
+
@collection.update(query, hash, options)
|
80
|
+
end
|
40
81
|
end
|
41
82
|
end
|
data/lib/rackjson/document.rb
CHANGED
@@ -15,11 +15,22 @@ module Rack::JSON
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
18
|
def add_attributes(pair)
|
20
19
|
attributes.merge!(pair)
|
21
20
|
end
|
22
21
|
|
22
|
+
def field(field_names)
|
23
|
+
attrs = attributes
|
24
|
+
Array.wrap(field_names).each do |field_name|
|
25
|
+
if attrs.is_a? Array
|
26
|
+
attrs = attrs[field_name.to_i]
|
27
|
+
else
|
28
|
+
attrs = attrs[field_name]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
attrs
|
32
|
+
end
|
33
|
+
|
23
34
|
def set_id(val)
|
24
35
|
add_attributes('_id' => val) unless attributes.keys.include? '_id'
|
25
36
|
end
|
data/lib/rackjson/end_point.rb
CHANGED
@@ -3,6 +3,10 @@ module Rack::JSON
|
|
3
3
|
|
4
4
|
private
|
5
5
|
|
6
|
+
def bad_request error
|
7
|
+
error_response error, 400
|
8
|
+
end
|
9
|
+
|
6
10
|
def bypass? request
|
7
11
|
request.collection.empty? || !(@collections.include? request.collection.to_sym)
|
8
12
|
end
|
@@ -15,8 +19,12 @@ module Rack::JSON
|
|
15
19
|
!@methods.include?(request.request_method.downcase.to_sym)
|
16
20
|
end
|
17
21
|
|
22
|
+
def error_response error, status_code
|
23
|
+
render (error.class.to_s + " :" + error.message), :status => status_code
|
24
|
+
end
|
25
|
+
|
18
26
|
def invalid_json error
|
19
|
-
|
27
|
+
error_response error, 422
|
20
28
|
end
|
21
29
|
|
22
30
|
def method_not_allowed? request
|
@@ -17,8 +17,8 @@ module Rack::JSON
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def set_attribute_ids
|
20
|
-
attributes["_id"] = BSON::
|
21
|
-
rescue BSON::
|
20
|
+
attributes["_id"] = BSON::ObjectId.from_string(attributes["_id"].to_s)
|
21
|
+
rescue BSON::InvalidObjectId
|
22
22
|
return false
|
23
23
|
end
|
24
24
|
|
data/lib/rackjson/json_query.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
module Rack::JSON
|
2
2
|
class JSONQuery
|
3
3
|
|
4
|
-
attr_accessor :options, :selector
|
4
|
+
attr_accessor :options, :selector, :resource_id
|
5
5
|
|
6
|
-
def initialize(query_string)
|
6
|
+
def initialize(query_string, options={})
|
7
|
+
@resource_id = options[:resource_id] || nil
|
7
8
|
@query_string = query_string
|
8
9
|
@conditions = @query_string.split(/\[|\]/).compact.reject {|s| s.empty? }
|
9
10
|
@options = {}
|
@@ -21,6 +22,7 @@ module Rack::JSON
|
|
21
22
|
end
|
22
23
|
end
|
23
24
|
end
|
25
|
+
add_query_document_id
|
24
26
|
end
|
25
27
|
|
26
28
|
def comparison(symbol)
|
@@ -32,6 +34,12 @@ module Rack::JSON
|
|
32
34
|
}[symbol]
|
33
35
|
end
|
34
36
|
|
37
|
+
def add_query_document_id
|
38
|
+
if resource_id
|
39
|
+
@selector[:_id] = resource_id
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
35
43
|
def set_query_fields(condition)
|
36
44
|
if condition.match /^=\w+$/
|
37
45
|
@options[:fields] = condition.sub('=', '').split(',')
|
data/lib/rackjson/request.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
module Rack::JSON
|
2
2
|
class Request < Rack::Request
|
3
|
+
|
4
|
+
class Rack::JSON::Request::UnrecognisedPathTypeError < StandardError ; end
|
5
|
+
|
3
6
|
include Rack::Utils
|
4
7
|
|
5
8
|
attr_reader :env
|
@@ -21,25 +24,82 @@ module Rack::JSON
|
|
21
24
|
self.path_info.match /^\/[\w-]+$/
|
22
25
|
end
|
23
26
|
|
27
|
+
def field
|
28
|
+
path_info.split('/')[3] || ""
|
29
|
+
end
|
30
|
+
|
31
|
+
def fields
|
32
|
+
path_info.split('/').slice(3..-1).reject { |f| f.match(/(_increment|_decrement|_push|_pull|_push_all|_pull_all|_add_to_set)/)} || []
|
33
|
+
end
|
34
|
+
|
35
|
+
def field_path?
|
36
|
+
path_info.match(/^\/[\w-]+\/[\w-]+\/[\w-]+(\/[\w-]+)*$/) && !modifier_path?
|
37
|
+
end
|
38
|
+
|
24
39
|
def member_path?
|
25
|
-
self.path_info.match
|
40
|
+
self.path_info.match /^\/[\w-]+\/[\w-]+$/
|
41
|
+
end
|
42
|
+
|
43
|
+
def modifier
|
44
|
+
modifier_path? ? path_info.split('/').last : nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def modifier_path?
|
48
|
+
path_info.match /^\/[\w-]+\/[\w-]+\/[\w-]+(\/[\w-]+)*\/(_increment|_decrement|_push|_pull|_push_all|_pull_all|_add_to_set)$/
|
49
|
+
end
|
50
|
+
|
51
|
+
def path_type
|
52
|
+
if member_path?
|
53
|
+
:member
|
54
|
+
elsif field_path?
|
55
|
+
:field
|
56
|
+
elsif collection_path?
|
57
|
+
:collection
|
58
|
+
else
|
59
|
+
raise UnrecognisedPathTypeError
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def payload
|
64
|
+
if content_type == 'application/json'
|
65
|
+
JSON.parse(raw_body)
|
66
|
+
elsif raw_body.empty?
|
67
|
+
nil
|
68
|
+
else
|
69
|
+
raw_body.numeric? ? raw_body.to_number : raw_body
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def property
|
74
|
+
property = path_info.split('/')[4]
|
75
|
+
if property
|
76
|
+
property.match(/^\d+$/)? property.to_i : property
|
77
|
+
else
|
78
|
+
nil
|
79
|
+
end
|
26
80
|
end
|
27
81
|
|
28
82
|
def json
|
29
|
-
|
30
|
-
self.body.read
|
83
|
+
raw_body
|
31
84
|
end
|
32
85
|
|
33
86
|
def query
|
34
|
-
@query ||= Rack::JSON::JSONQuery.new(unescape(query_string))
|
87
|
+
@query ||= Rack::JSON::JSONQuery.new(unescape(query_string), :resource_id => resource_id)
|
88
|
+
end
|
89
|
+
|
90
|
+
def raw_body
|
91
|
+
self.body.rewind
|
92
|
+
self.body.read
|
35
93
|
end
|
36
94
|
|
37
95
|
def resource_id
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
96
|
+
unless collection_path?
|
97
|
+
id_string = self.path_info.split('/')[2].to_s
|
98
|
+
begin
|
99
|
+
BSON::ObjectId.from_string(id_string)
|
100
|
+
rescue BSON::InvalidObjectId
|
101
|
+
id_string.match(/^\d+$/) ? id_string.to_i : id_string
|
102
|
+
end
|
43
103
|
end
|
44
104
|
end
|
45
105
|
|
data/lib/rackjson/resource.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Rack::JSON
|
2
2
|
class Resource
|
3
3
|
include Rack::JSON::EndPoint
|
4
|
-
HTTP_METHODS = [:get, :post, :put, :delete]
|
4
|
+
HTTP_METHODS = [:get, :post, :put, :delete, :options]
|
5
5
|
|
6
6
|
def initialize(app, options)
|
7
7
|
@app = app
|
@@ -24,11 +24,19 @@ module Rack::JSON
|
|
24
24
|
|
25
25
|
private
|
26
26
|
|
27
|
+
def create(request)
|
28
|
+
document = Rack::JSON::Document.create(request.json)
|
29
|
+
@collection.save(document)
|
30
|
+
render document, :status => 201
|
31
|
+
end
|
32
|
+
|
27
33
|
def delete(request)
|
28
|
-
if request.
|
29
|
-
|
30
|
-
|
31
|
-
|
34
|
+
if request.field_path?
|
35
|
+
@collection.delete_field(request.query.selector, request.fields)
|
36
|
+
render "", :status => 204
|
37
|
+
elsif request.member_path?
|
38
|
+
@collection.delete(request.query.selector)
|
39
|
+
render "", :status => 204
|
32
40
|
else
|
33
41
|
render "", :status => 405
|
34
42
|
end
|
@@ -36,12 +44,28 @@ module Rack::JSON
|
|
36
44
|
|
37
45
|
[:get, :head].each do |method|
|
38
46
|
define_method method do |request|
|
39
|
-
|
47
|
+
begin
|
48
|
+
send("get_#{request.path_type}", request, method)
|
49
|
+
rescue Rack::JSON::Request::UnrecognisedPathTypeError => error
|
50
|
+
bad_request error
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_collection(request, method)
|
56
|
+
render @collection.find(request.query.selector, request.query.options)
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_field(request, method)
|
60
|
+
field = @collection.find_field(request.query.selector, request.fields, request.query.options)
|
61
|
+
if field
|
62
|
+
render field, :head => (method == :head)
|
63
|
+
else
|
64
|
+
render "field not found", :status => 404, :head => (method == :head)
|
40
65
|
end
|
41
66
|
end
|
42
67
|
|
43
68
|
def get_member(request, method)
|
44
|
-
request.query.selector.merge!({:_id => request.resource_id})
|
45
69
|
document = @collection.find_one(request.query.selector, request.query.options)
|
46
70
|
if document
|
47
71
|
render document, :head => (method == :head)
|
@@ -50,37 +74,46 @@ module Rack::JSON
|
|
50
74
|
end
|
51
75
|
end
|
52
76
|
|
53
|
-
def get_collection(request, method)
|
54
|
-
render @collection.find(request.query.selector, request.query.options)
|
55
|
-
end
|
56
|
-
|
57
|
-
def not_allowed?(request)
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
77
|
def options(request)
|
62
78
|
if request.collection_path?
|
63
79
|
headers = { "Allow" => "GET, POST" }
|
64
80
|
elsif request.member_path?
|
65
81
|
headers = { "Allow" => "GET, PUT, DELETE" }
|
82
|
+
elsif request.field_path?
|
83
|
+
headers = { "Allow" => "GET, PUT, DELETE" }
|
84
|
+
elsif request.modifier_path?
|
85
|
+
headers = { "Allow" => "POST" }
|
66
86
|
end
|
67
87
|
render "", :headers => headers
|
68
88
|
end
|
69
89
|
|
70
90
|
def post(request)
|
71
|
-
|
72
|
-
|
73
|
-
|
91
|
+
if request.collection_path?
|
92
|
+
create(request)
|
93
|
+
elsif request.modifier_path?
|
94
|
+
@collection.exists?(request.resource_id) ? modify(request) : render("document not found", :status => 404)
|
95
|
+
else
|
96
|
+
render "", :status => 405
|
97
|
+
end
|
74
98
|
rescue JSON::ParserError => error
|
75
99
|
invalid_json error
|
76
100
|
end
|
77
101
|
|
78
102
|
def put(request)
|
79
|
-
|
103
|
+
if request.field_path?
|
104
|
+
@collection.exists?(request.resource_id) ? update_field(request) : render("document not found", :status => 404)
|
105
|
+
else
|
106
|
+
@collection.exists?(request.resource_id) ? update(request) : upsert(request)
|
107
|
+
end
|
80
108
|
rescue JSON::ParserError => error
|
81
109
|
invalid_json error
|
82
110
|
end
|
83
111
|
|
112
|
+
def modify(request)
|
113
|
+
@collection.send(request.modifier[1..-1], request.query.selector, request.fields, request.payload)
|
114
|
+
render "OK", :status => 200
|
115
|
+
end
|
116
|
+
|
84
117
|
def update(request)
|
85
118
|
document = Rack::JSON::Document.create(request.json)
|
86
119
|
document.set_id(request.resource_id)
|
@@ -91,6 +124,11 @@ module Rack::JSON
|
|
91
124
|
end
|
92
125
|
end
|
93
126
|
|
127
|
+
def update_field(request)
|
128
|
+
@collection.update_field(request.query.selector, request.fields, request.payload)
|
129
|
+
render "OK", :status => 200
|
130
|
+
end
|
131
|
+
|
94
132
|
def upsert(request)
|
95
133
|
document = Rack::JSON::Document.create(request.json)
|
96
134
|
document.set_id(request.resource_id)
|
data/lib/rackjson/response.rb
CHANGED
@@ -25,11 +25,11 @@ module Rack::JSON
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def parse_body(body)
|
28
|
-
if body.is_a?(Rack::JSON::Document) || body.is_a?(Array)
|
28
|
+
if body.is_a?(Rack::JSON::Document) || body.is_a?(Array) || body.is_a?(Hash)
|
29
29
|
@body = body.to_json
|
30
30
|
@headers["Content-Type"] = "application/json"
|
31
|
-
elsif body.is_a?
|
32
|
-
@body = body
|
31
|
+
elsif body.is_a?(String) || body.is_a?(Fixnum)
|
32
|
+
@body = body.to_s
|
33
33
|
@headers["Content-Type"] = "text/plain"
|
34
34
|
else
|
35
35
|
raise Rack::JSON::Response::BodyFormatError
|
data/lib/rackjson.rb
CHANGED
@@ -4,6 +4,8 @@ require 'rack'
|
|
4
4
|
require 'mongo'
|
5
5
|
require 'time'
|
6
6
|
require 'rackjson/rack/builder'
|
7
|
+
require 'rackjson/extensions/core/array'
|
8
|
+
require 'rackjson/extensions/core/string'
|
7
9
|
|
8
10
|
module Rack::JSON
|
9
11
|
|
@@ -19,7 +21,7 @@ module Rack::JSON
|
|
19
21
|
autoload :Request, 'rackjson/request'
|
20
22
|
autoload :Resource, 'rackjson/resource'
|
21
23
|
autoload :Response, 'rackjson/response'
|
22
|
-
autoload :
|
24
|
+
autoload :ObjectId, 'rackjson/extensions/bson/object_id'
|
23
25
|
autoload :OrderedHash, 'rackjson/extensions/bson/ordered_hash'
|
24
26
|
|
25
27
|
end
|
data/rackjson.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{rackjson}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.4.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Oliver Nightingale"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-10-06}
|
13
13
|
s.description = %q{A rack end point for storing json documents.}
|
14
14
|
s.email = %q{oliver.n@new-bamboo.co.uk}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -29,6 +29,8 @@ Gem::Specification.new do |s|
|
|
29
29
|
"lib/rackjson/end_point.rb",
|
30
30
|
"lib/rackjson/extensions/BSON/object_id.rb",
|
31
31
|
"lib/rackjson/extensions/BSON/ordered_hash.rb",
|
32
|
+
"lib/rackjson/extensions/core/array.rb",
|
33
|
+
"lib/rackjson/extensions/core/string.rb",
|
32
34
|
"lib/rackjson/filter.rb",
|
33
35
|
"lib/rackjson/json_document.rb",
|
34
36
|
"lib/rackjson/json_query.rb",
|
@@ -40,6 +42,7 @@ Gem::Specification.new do |s|
|
|
40
42
|
"rackjson.gemspec",
|
41
43
|
"test/helper.rb",
|
42
44
|
"test/suite.rb",
|
45
|
+
"test/test_collection.rb",
|
43
46
|
"test/test_document.rb",
|
44
47
|
"test/test_filter.rb",
|
45
48
|
"test/test_json_document.rb",
|
@@ -57,6 +60,7 @@ Gem::Specification.new do |s|
|
|
57
60
|
s.test_files = [
|
58
61
|
"test/helper.rb",
|
59
62
|
"test/suite.rb",
|
63
|
+
"test/test_collection.rb",
|
60
64
|
"test/test_document.rb",
|
61
65
|
"test/test_filter.rb",
|
62
66
|
"test/test_json_document.rb",
|
@@ -64,7 +68,6 @@ Gem::Specification.new do |s|
|
|
64
68
|
"test/test_mongo_document.rb",
|
65
69
|
"test/test_rack_builder.rb",
|
66
70
|
"test/test_resource.rb",
|
67
|
-
"test/test_resource_modifier.rb",
|
68
71
|
"test/test_response.rb"
|
69
72
|
]
|
70
73
|
|
@@ -73,18 +76,18 @@ Gem::Specification.new do |s|
|
|
73
76
|
s.specification_version = 3
|
74
77
|
|
75
78
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
76
|
-
s.add_runtime_dependency(%q<mongo>, [">= 1.
|
79
|
+
s.add_runtime_dependency(%q<mongo>, [">= 1.1.0"])
|
77
80
|
s.add_runtime_dependency(%q<mongo_ext>, [">= 0.19.1"])
|
78
81
|
s.add_runtime_dependency(%q<json>, ["= 1.2.3"])
|
79
82
|
s.add_runtime_dependency(%q<rack>, [">= 1.0.1"])
|
80
83
|
else
|
81
|
-
s.add_dependency(%q<mongo>, [">= 1.
|
84
|
+
s.add_dependency(%q<mongo>, [">= 1.1.0"])
|
82
85
|
s.add_dependency(%q<mongo_ext>, [">= 0.19.1"])
|
83
86
|
s.add_dependency(%q<json>, ["= 1.2.3"])
|
84
87
|
s.add_dependency(%q<rack>, [">= 1.0.1"])
|
85
88
|
end
|
86
89
|
else
|
87
|
-
s.add_dependency(%q<mongo>, [">= 1.
|
90
|
+
s.add_dependency(%q<mongo>, [">= 1.1.0"])
|
88
91
|
s.add_dependency(%q<mongo_ext>, [">= 0.19.1"])
|
89
92
|
s.add_dependency(%q<json>, ["= 1.2.3"])
|
90
93
|
s.add_dependency(%q<rack>, [">= 1.0.1"])
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class CollectionTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@db = Mongo::Connection.new.db("test")
|
7
|
+
@doc = @db['resource_test'].insert({:_id => 1, :count => 1, :field => 'foo', :array => [1,2,3,4], :obj => { :field => 'baz'}})
|
8
|
+
@collection = Rack::JSON::Collection.new(@db['resource_test'])
|
9
|
+
end
|
10
|
+
|
11
|
+
def teardown
|
12
|
+
@db['resource_test'].drop
|
13
|
+
end
|
14
|
+
|
15
|
+
test "should be able to retrieve a specific element from a document in the collection" do
|
16
|
+
assert_equal('foo', @collection.find_field(1, ['field']))
|
17
|
+
end
|
18
|
+
|
19
|
+
test "should return nil if there is no matching field" do
|
20
|
+
assert_nil(@collection.find_field(1, ['non-existant-field']))
|
21
|
+
end
|
22
|
+
|
23
|
+
test "should be able to retrieve a specific element form an array" do
|
24
|
+
assert_equal(2, @collection.find_field(1, ['array', 1]))
|
25
|
+
end
|
26
|
+
|
27
|
+
test "should return nil if asking for an array element that doesn't exist" do
|
28
|
+
assert_nil(@collection.find_field(1, ['array', 100]))
|
29
|
+
end
|
30
|
+
|
31
|
+
test "should be able to retrieve a specific element from an embedded object" do
|
32
|
+
assert_equal('baz', @collection.find_field(1, ['obj', 'field']))
|
33
|
+
end
|
34
|
+
|
35
|
+
test "should return nil if asking for an element that doesn't exist on the embeded object" do
|
36
|
+
assert_nil(@collection.find_field(1, ['obj', 'nonexistant']))
|
37
|
+
end
|
38
|
+
|
39
|
+
test "attomic increment" do
|
40
|
+
@collection.increment(1, 'count')
|
41
|
+
assert_equal(2, @collection.find_field(1, 'count'))
|
42
|
+
end
|
43
|
+
|
44
|
+
test "incrementing by more than 1" do
|
45
|
+
@collection.increment(1, 'count', 2)
|
46
|
+
assert_equal(3, @collection.find_field(1, 'count'))
|
47
|
+
end
|
48
|
+
|
49
|
+
test "attomic decrement" do
|
50
|
+
@collection.decrement(1, 'count')
|
51
|
+
assert_equal(0, @collection.find_field(1, 'count'))
|
52
|
+
end
|
53
|
+
|
54
|
+
test "decrementing by more than 1" do
|
55
|
+
@collection.decrement(1, 'count', 2)
|
56
|
+
assert_equal(-1, @collection.find_field(1, 'count'))
|
57
|
+
end
|
58
|
+
|
59
|
+
test "attomic push" do
|
60
|
+
@collection.push(1, 'array', 'pushed value')
|
61
|
+
assert_equal([1,2,3,4,'pushed value'], @collection.find_field(1, 'array'))
|
62
|
+
end
|
63
|
+
|
64
|
+
test "attomic push on a new list" do
|
65
|
+
@collection.push(1, 'new-array', 'pushed value')
|
66
|
+
assert_equal(['pushed value'], @collection.find_field(1, 'new-array'))
|
67
|
+
end
|
68
|
+
|
69
|
+
# mongo isn't throwing an error when doing this, may need to upgrade
|
70
|
+
# still not throwing an error in 1.6
|
71
|
+
# test "attomic push on a non list field should raise a DataTypeError" do
|
72
|
+
# assert_raises Rack::JSON::Collection::DataTypeError do
|
73
|
+
# @collection.push(1, 'count', 'pushed value')
|
74
|
+
# end
|
75
|
+
# end
|
76
|
+
|
77
|
+
test "attomic push all on an existing list" do
|
78
|
+
@collection.push_all(1, 'array', ["a", "b", "c"])
|
79
|
+
assert_equal([1,2,3,4,'a','b','c'], @collection.find_field(1, 'array'))
|
80
|
+
end
|
81
|
+
|
82
|
+
test "attomic push all on to create a new list" do
|
83
|
+
@collection.push_all(1, 'new-array', ["a", "b", "c"])
|
84
|
+
assert_equal(["a", "b", "c"], @collection.find_field(1, 'new-array'))
|
85
|
+
end
|
86
|
+
|
87
|
+
test "attomic pull item form a list" do
|
88
|
+
@collection.pull(1, 'array', 4)
|
89
|
+
assert_equal([1,2,3], @collection.find_field(1, 'array'))
|
90
|
+
end
|
91
|
+
|
92
|
+
test "attomic pull all to remove more than one item from a list" do
|
93
|
+
@collection.pull_all(1, 'array', [1,2,3])
|
94
|
+
assert_equal([4], @collection.find_field(1, 'array'))
|
95
|
+
end
|
96
|
+
|
97
|
+
# currently failing because add to set is supported in mongo v1.3+
|
98
|
+
test "adding an element to an array only if it doesn't already exist in the array" do
|
99
|
+
@collection.add_to_set(1, 'array', 'pushed value')
|
100
|
+
assert_equal([1,2,3,4,'pushed value'], @collection.find_field(1, 'array'))
|
101
|
+
@collection.add_to_set(1, 'array', 1)
|
102
|
+
assert_equal([1,2,3,4,'pushed value'], @collection.find_field(1, 'array'))
|
103
|
+
end
|
104
|
+
|
105
|
+
test "updating a specific field of a document" do
|
106
|
+
@collection.update_field(1, 'field', 'bar')
|
107
|
+
assert_equal('bar', @collection.find_field(1, 'field'))
|
108
|
+
end
|
109
|
+
|
110
|
+
test "updating a field that doesn't currently exist in a document" do
|
111
|
+
@collection.update_field(1, 'new-field', 'bar')
|
112
|
+
assert_equal('bar', @collection.find_field(1, 'new-field'))
|
113
|
+
end
|
114
|
+
|
115
|
+
test "updating a nested field in a document" do
|
116
|
+
@collection.update_field(1, ['obj', 'field'], 'blah')
|
117
|
+
assert_equal('blah', @collection.find_field(1, ['obj', 'field']))
|
118
|
+
end
|
119
|
+
|
120
|
+
test "deleting a specific field from a document" do
|
121
|
+
@collection.delete_field(1, 'field')
|
122
|
+
assert_nil(@collection.find_field(1, 'field'))
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
data/test/test_document.rb
CHANGED
@@ -29,7 +29,7 @@ class DocumentTest < Test::Unit::TestCase
|
|
29
29
|
def test_creating_from_json_with_id
|
30
30
|
json = '{"_id": "4b9f783ba040140525000001", "test":"hello"}'
|
31
31
|
document = Rack::JSON::Document.create(json)
|
32
|
-
assert_equal(BSON::
|
32
|
+
assert_equal(BSON::ObjectId.from_string('4b9f783ba040140525000001'), document.attributes["_id"])
|
33
33
|
assert_equal("hello", document.attributes["test"])
|
34
34
|
assert_equal(Time.now.to_s, document.attributes["created_at"].to_s)
|
35
35
|
assert_equal(Time.now.to_s, document.attributes["updated_at"].to_s)
|
@@ -82,4 +82,11 @@ class DocumentTest < Test::Unit::TestCase
|
|
82
82
|
assert_equal("01/01/2010", document.attributes["created_at"])
|
83
83
|
assert_equal(Time.now.to_s, document.attributes["updated_at"].to_s)
|
84
84
|
end
|
85
|
+
|
86
|
+
test "accessing getting a single attribute from a document" do
|
87
|
+
json = '{"test":"hello", "nested":{"foo":"bar"}}'
|
88
|
+
document = Rack::JSON::Document.create(json)
|
89
|
+
assert_equal "hello", document.field(["test"])
|
90
|
+
assert_equal "bar", document.field(["nested", "foo"])
|
91
|
+
end
|
85
92
|
end
|
data/test/test_json_document.rb
CHANGED
@@ -24,7 +24,7 @@ class JSONDocumentTest < Test::Unit::TestCase
|
|
24
24
|
def test_parsing_mongo_object_id
|
25
25
|
hash = { "_id" => "4ba7e82ca04014011c000001" }
|
26
26
|
doc = JSON.generate hash
|
27
|
-
assert_equal(BSON::
|
27
|
+
assert_equal(BSON::ObjectId.from_string("4ba7e82ca04014011c000001"), Rack::JSON::JSONDocument.new(doc).attributes["_id"])
|
28
28
|
end
|
29
29
|
|
30
30
|
def test_parsing_non_mongo_object_ids
|
data/test/test_json_query.rb
CHANGED
@@ -61,6 +61,12 @@ class QueryTest < Test::Unit::TestCase
|
|
61
61
|
assert_equal({:price => {'$lt' => 10}}, query.selector)
|
62
62
|
end
|
63
63
|
|
64
|
+
test "automatically merging in the _id from the resource id" do
|
65
|
+
json_query = '[?name=bob!]'
|
66
|
+
query = Rack::JSON::JSONQuery.new(json_query, :resource_id => 1)
|
67
|
+
assert_equal({:_id => 1, :name => 'bob!'}, query.selector)
|
68
|
+
end
|
69
|
+
|
64
70
|
# def test_single_greater_than_or_equal_condition
|
65
71
|
# json_query = '[?price=<10]'
|
66
72
|
# query = Rack::JSON::JSONQuery.new(json_query)
|
data/test/test_mongo_document.rb
CHANGED
@@ -3,7 +3,7 @@ require 'helper'
|
|
3
3
|
class MongoDocumentTest < Test::Unit::TestCase
|
4
4
|
|
5
5
|
def test_stringifying_mongo_object_ids
|
6
|
-
hash = {"_id" => BSON::
|
6
|
+
hash = {"_id" => BSON::ObjectId.from_string("4ba7e82ca04014011c000001")}
|
7
7
|
doc = Rack::JSON::MongoDocument.new(hash).attributes
|
8
8
|
assert_equal("4ba7e82ca04014011c000001", doc["_id"])
|
9
9
|
end
|
data/test/test_resource.rb
CHANGED
@@ -94,6 +94,55 @@ class ResourceTest < Test::Unit::TestCase
|
|
94
94
|
assert_equal "document not found", last_response.body
|
95
95
|
end
|
96
96
|
|
97
|
+
test "finding a field within a specific document" do
|
98
|
+
@collection.save({:testing => true, :rating => 5, :title => 'testing', :_id => 1})
|
99
|
+
get '/testing/1/title'
|
100
|
+
assert last_response.ok?
|
101
|
+
assert_equal "testing", last_response.body
|
102
|
+
end
|
103
|
+
|
104
|
+
test "trying to find a field within a non-existant document" do
|
105
|
+
get '/testing/1/title'
|
106
|
+
assert_equal 404, last_response.status
|
107
|
+
end
|
108
|
+
|
109
|
+
test "finding an array inside a document" do
|
110
|
+
@collection.save({:obj => { :hello => "world"}, :ratings => [5,2], :title => 'testing', :_id => 1})
|
111
|
+
get '/testing/1/ratings'
|
112
|
+
assert last_response.ok?
|
113
|
+
expected = [5,2]
|
114
|
+
assert_equal expected, JSON.parse(last_response.body)
|
115
|
+
end
|
116
|
+
|
117
|
+
test "finding an element of an array from a specific document" do
|
118
|
+
@collection.save({:testing => true, :ratings => [5,2], :title => 'testing', :_id => 1})
|
119
|
+
get '/testing/1/ratings/0'
|
120
|
+
assert last_response.ok?
|
121
|
+
assert_equal "5", last_response.body
|
122
|
+
end
|
123
|
+
|
124
|
+
test "finding an embedded document" do
|
125
|
+
@collection.save({:obj => { :hello => "world"}, :ratings => [5,2], :title => 'testing', :_id => 1})
|
126
|
+
get '/testing/1/obj'
|
127
|
+
assert last_response.ok?
|
128
|
+
expected = { "hello" => "world" }
|
129
|
+
assert_equal expected, JSON.parse(last_response.body)
|
130
|
+
end
|
131
|
+
|
132
|
+
test "finding a property of an embedded document" do
|
133
|
+
@collection.save({:obj => { :hello => "world"}, :ratings => [5,2], :title => 'testing', :_id => 1})
|
134
|
+
get '/testing/1/obj/hello'
|
135
|
+
assert last_response.ok?
|
136
|
+
assert_equal "world", last_response.body
|
137
|
+
end
|
138
|
+
|
139
|
+
test "incrementing a property of an embedded document" do
|
140
|
+
@collection.save({:obj => { :counter => 1}, :_id => 1})
|
141
|
+
post '/testing/1/obj/counter/_increment'
|
142
|
+
assert last_response.ok?
|
143
|
+
assert_equal 2, @collection.find_one(:_id => 1)["obj"]["counter"]
|
144
|
+
end
|
145
|
+
|
97
146
|
test "index method with query parameters" do
|
98
147
|
@collection.save({:testing => true, :rating => 5, :title => 'testing'})
|
99
148
|
get '/testing?[?title=testing]'
|
@@ -144,14 +193,128 @@ class ResourceTest < Test::Unit::TestCase
|
|
144
193
|
assert_nil @collection.find_one(:_id => 1, :user_id => 1)
|
145
194
|
end
|
146
195
|
|
196
|
+
test "updating a field within a document" do
|
197
|
+
@collection.save({:title => 'testing', :_id => 1})
|
198
|
+
put '/testing/1/title', "updated"
|
199
|
+
assert last_response.ok?
|
200
|
+
assert_equal "updated", @collection.find_one(:_id => 1)['title']
|
201
|
+
end
|
202
|
+
|
203
|
+
test "creating a new field within an existing documennt" do
|
204
|
+
@collection.save({:title => 'testing', :_id => 1})
|
205
|
+
put '/testing/1/new_field', "created"
|
206
|
+
assert last_response.ok?
|
207
|
+
assert_equal "created", @collection.find_one(:_id => 1)['new_field']
|
208
|
+
end
|
209
|
+
|
210
|
+
test "trying to create a new field within a non-existant document" do
|
211
|
+
@collection.save({:title => 'testing', :_id => 1})
|
212
|
+
put '/testing/2/title', "updated"
|
213
|
+
assert_equal 404, last_response.status
|
214
|
+
end
|
215
|
+
|
216
|
+
test "incrementing a value within a document" do
|
217
|
+
@collection.save({:counter => 1, :_id => 1})
|
218
|
+
post '/testing/1/counter/_increment'
|
219
|
+
assert last_response.ok?
|
220
|
+
assert_equal 2, @collection.find_one(:_id => 1)["counter"]
|
221
|
+
end
|
222
|
+
|
223
|
+
test "incrementing a non existant field" do
|
224
|
+
@collection.save({:_id => 1})
|
225
|
+
post '/testing/1/counter/_increment'
|
226
|
+
assert last_response.ok?
|
227
|
+
assert_equal 1, @collection.find_one(:_id => 1)["counter"]
|
228
|
+
end
|
229
|
+
|
230
|
+
test "incrementing a value within a document by a custom amount" do
|
231
|
+
@collection.save({:counter => 1, :_id => 1})
|
232
|
+
post '/testing/1/counter/_increment', '10'
|
233
|
+
assert last_response.ok?
|
234
|
+
assert_equal 11, @collection.find_one(:_id => 1)["counter"]
|
235
|
+
end
|
236
|
+
|
237
|
+
test "incrementing a value on a non existent document" do
|
238
|
+
post '/testing/1/counter/_increment'
|
239
|
+
assert_equal 404, last_response.status
|
240
|
+
end
|
241
|
+
|
242
|
+
test "decrementing a value within a document" do
|
243
|
+
@collection.save({:counter => 1, :_id => 1})
|
244
|
+
post '/testing/1/counter/_decrement'
|
245
|
+
assert last_response.ok?
|
246
|
+
assert_equal 0, @collection.find_one(:_id => 1)["counter"]
|
247
|
+
end
|
248
|
+
|
249
|
+
test "push a simple value onto an array within a document" do
|
250
|
+
@collection.save({:list => [1,2,3], :_id => 1})
|
251
|
+
post '/testing/1/list/_push', '4'
|
252
|
+
assert last_response.ok?
|
253
|
+
assert_equal [1,2,3,4], @collection.find_one(:_id => 1)['list']
|
254
|
+
end
|
255
|
+
|
256
|
+
test "push an object onto an array within a document" do
|
257
|
+
@collection.save({:list => [1,2,3], :_id => 1})
|
258
|
+
header 'Content-Type', 'application/json'
|
259
|
+
post '/testing/1/list/_push', '{"foo": "bar"}'
|
260
|
+
assert last_response.ok?
|
261
|
+
assert_equal [1,2,3,{"foo" => "bar"}], @collection.find_one(:_id => 1)['list']
|
262
|
+
end
|
263
|
+
|
264
|
+
test "push more than one item onto an array within a document" do
|
265
|
+
@collection.save({:list => [1,2,3], :_id => 1})
|
266
|
+
header 'Content-Type', 'application/json'
|
267
|
+
post '/testing/1/list/_push_all', '[4,5,6,7]'
|
268
|
+
assert last_response.ok?
|
269
|
+
assert_equal [1,2,3,4,5,6,7], @collection.find_one(:_id => 1)['list']
|
270
|
+
end
|
271
|
+
|
272
|
+
test "pull a simple value from an array within a document" do
|
273
|
+
@collection.save({:list => [1,2,3], :_id => 1})
|
274
|
+
post '/testing/1/list/_pull', '2'
|
275
|
+
assert last_response.ok?
|
276
|
+
assert_equal [1,3], @collection.find_one(:_id => 1)['list']
|
277
|
+
end
|
278
|
+
|
279
|
+
test "pull an object from an array within a document" do
|
280
|
+
@collection.save({:list => [1,2,3,{"foo" => "bar"}], :_id => 1})
|
281
|
+
header 'Content-Type', 'application/json'
|
282
|
+
post '/testing/1/list/_pull', '{ "foo": "bar" }'
|
283
|
+
assert last_response.ok?
|
284
|
+
assert_equal [1,2,3], @collection.find_one(:_id => 1)['list']
|
285
|
+
end
|
286
|
+
|
287
|
+
test "pull more than one item from an array within a document" do
|
288
|
+
@collection.save({:list => [1,2,3,4,5,6,7], :_id => 1})
|
289
|
+
header 'Content-Type', 'application/json'
|
290
|
+
post '/testing/1/list/_pull_all', '[4,5,6,7]'
|
291
|
+
assert last_response.ok?
|
292
|
+
assert_equal [1,2,3], @collection.find_one(:_id => 1)['list']
|
293
|
+
end
|
294
|
+
|
295
|
+
test "adding an item to a set" do
|
296
|
+
@collection.save({:list => [1,2,3], :_id => 1})
|
297
|
+
post '/testing/1/list/_add_to_set', '4'
|
298
|
+
assert last_response.ok?
|
299
|
+
assert_equal [1,2,3,4], @collection.find_one(:_id => 1)['list']
|
300
|
+
end
|
301
|
+
|
147
302
|
test "deleting a document" do
|
148
303
|
@collection.save({:title => 'testing', :_id => 1})
|
149
304
|
assert @collection.find_one({:_id => 1})
|
150
305
|
delete '/testing/1'
|
151
|
-
|
306
|
+
assert_equal 204, last_response.status
|
152
307
|
assert_nil @collection.find_one({:_id => 1})
|
153
308
|
end
|
154
309
|
|
310
|
+
test "deleting a field within a document" do
|
311
|
+
@collection.save({:title => 'testing', :_id => 1})
|
312
|
+
assert @collection.find_one({:_id => 1})
|
313
|
+
delete '/testing/1/title'
|
314
|
+
assert_equal 204, last_response.status
|
315
|
+
assert_nil @collection.find_one({:_id => 1})['title']
|
316
|
+
end
|
317
|
+
|
155
318
|
test "deleting only with member path" do
|
156
319
|
delete '/testing'
|
157
320
|
assert_equal 405, last_response.status
|
data/test/test_response.rb
CHANGED
@@ -38,6 +38,11 @@ class ResponseTest < Test::Unit::TestCase
|
|
38
38
|
assert_match(JSON.parse(response.body)['title'], 'Hello')
|
39
39
|
end
|
40
40
|
|
41
|
+
test "sending a number" do
|
42
|
+
response = Rack::JSON::Response.new(1)
|
43
|
+
assert_equal "1", response.body
|
44
|
+
end
|
45
|
+
|
41
46
|
def test_head_response
|
42
47
|
response = Rack::JSON::Response.new("test", :head => true)
|
43
48
|
assert_equal([""], response.to_a[2])
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 4
|
8
|
+
- 1
|
9
|
+
version: 0.4.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Oliver Nightingale
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-10-06 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -26,9 +26,9 @@ dependencies:
|
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
segments:
|
28
28
|
- 1
|
29
|
+
- 1
|
29
30
|
- 0
|
30
|
-
|
31
|
-
version: 1.0.0
|
31
|
+
version: 1.1.0
|
32
32
|
type: :runtime
|
33
33
|
version_requirements: *id001
|
34
34
|
- !ruby/object:Gem::Dependency
|
@@ -95,6 +95,8 @@ files:
|
|
95
95
|
- lib/rackjson/end_point.rb
|
96
96
|
- lib/rackjson/extensions/BSON/object_id.rb
|
97
97
|
- lib/rackjson/extensions/BSON/ordered_hash.rb
|
98
|
+
- lib/rackjson/extensions/core/array.rb
|
99
|
+
- lib/rackjson/extensions/core/string.rb
|
98
100
|
- lib/rackjson/filter.rb
|
99
101
|
- lib/rackjson/json_document.rb
|
100
102
|
- lib/rackjson/json_query.rb
|
@@ -106,6 +108,7 @@ files:
|
|
106
108
|
- rackjson.gemspec
|
107
109
|
- test/helper.rb
|
108
110
|
- test/suite.rb
|
111
|
+
- test/test_collection.rb
|
109
112
|
- test/test_document.rb
|
110
113
|
- test/test_filter.rb
|
111
114
|
- test/test_json_document.rb
|
@@ -147,6 +150,7 @@ summary: A rack end point for storing json documents.
|
|
147
150
|
test_files:
|
148
151
|
- test/helper.rb
|
149
152
|
- test/suite.rb
|
153
|
+
- test/test_collection.rb
|
150
154
|
- test/test_document.rb
|
151
155
|
- test/test_filter.rb
|
152
156
|
- test/test_json_document.rb
|
@@ -154,5 +158,4 @@ test_files:
|
|
154
158
|
- test/test_mongo_document.rb
|
155
159
|
- test/test_rack_builder.rb
|
156
160
|
- test/test_resource.rb
|
157
|
-
- test/test_resource_modifier.rb
|
158
161
|
- test/test_response.rb
|
@@ -1,73 +0,0 @@
|
|
1
|
-
# require 'helper'
|
2
|
-
#
|
3
|
-
# class ResourceModifierTest < Test::Unit::TestCase
|
4
|
-
# include Rack::Test::Methods
|
5
|
-
# include Rack::Utils
|
6
|
-
#
|
7
|
-
# def setup
|
8
|
-
# @db = Mongo::Connection.new.db("test")
|
9
|
-
# @collection = @db['testing']
|
10
|
-
# @doc = @collection.save(:_id => 1, :count => 0, :likers => [])
|
11
|
-
# end
|
12
|
-
#
|
13
|
-
# def teardown
|
14
|
-
# @collection.drop
|
15
|
-
# end
|
16
|
-
#
|
17
|
-
# def app
|
18
|
-
# Rack::JSON::ResourceModifier.new lambda { |env|
|
19
|
-
# [404, {'Content-Length' => '9', 'Content-Type' => 'text/plain'}, ["Not Found"]]
|
20
|
-
# }, :collections => [:testing], :db => @db
|
21
|
-
# end
|
22
|
-
#
|
23
|
-
# test "ignore non matched resources" do
|
24
|
-
# put '/foo/1'
|
25
|
-
# assert_status_not_found last_response
|
26
|
-
# end
|
27
|
-
#
|
28
|
-
# test "ignore non modifier resources" do
|
29
|
-
# put '/testing/1'
|
30
|
-
# assert_status_not_found last_response
|
31
|
-
# end
|
32
|
-
#
|
33
|
-
# test "ignoring modifier requests with the wrong method" do
|
34
|
-
# post '/testing/1/inc?field=count'
|
35
|
-
# assert_status_not_found last_response
|
36
|
-
# end
|
37
|
-
#
|
38
|
-
# test "incrementing a value" do
|
39
|
-
# put '/testing/1/inc?field=count'
|
40
|
-
# assert_status_ok last_response
|
41
|
-
# assert_equal 1, @collection.find_one({:_id => 1})["count"]
|
42
|
-
# end
|
43
|
-
#
|
44
|
-
# test "decrementing a value" do
|
45
|
-
# put '/testing/1/dec?field=count'
|
46
|
-
# assert_status_ok last_response
|
47
|
-
# assert_equal -1, @collection.find_one({:_id => 1})["count"]
|
48
|
-
# end
|
49
|
-
#
|
50
|
-
# test "incrementing a non-existant field" do
|
51
|
-
# put '/testing/1/inc?field=non_existant'
|
52
|
-
# assert_status_ok last_response
|
53
|
-
# assert_equal 0, @collection.find_one({:_id => 1})["count"]
|
54
|
-
# assert_equal 1, @collection.find_one({:_id => 1})["non_existant"]
|
55
|
-
# end
|
56
|
-
#
|
57
|
-
# test "decrementing a non-existent field" do
|
58
|
-
# put '/testing/1/dec?field=non_existant'
|
59
|
-
# assert_status_ok last_response
|
60
|
-
# assert_equal 0, @collection.find_one({:_id => 1})["count"]
|
61
|
-
# assert_equal -1, @collection.find_one({:_id => 1})["non_existant"]
|
62
|
-
# end
|
63
|
-
#
|
64
|
-
# test "pushing a value onto the end of an array" do
|
65
|
-
# testing/1/counter/_inc
|
66
|
-
# testing/1/counter/_dec
|
67
|
-
# testing/1/likers?operation
|
68
|
-
# put '/testing/1/push?field=asdfa' '{"value": [123] }'
|
69
|
-
# put '/testing/1/push', '{"field": "likers", "value": 1}'
|
70
|
-
# assert_status_ok last_response
|
71
|
-
# assert_equal [1], @collection.find_one({:_id => 1})['likers']
|
72
|
-
# end
|
73
|
-
# end
|