rackjson 0.3.2 → 0.4.1
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/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
|