diff --git a/README.md b/README.md
index 4fb7015..c55f51f 100644
--- a/README.md
+++ b/README.md
@@ -183,7 +183,7 @@ game_score["score"] = Parse::Increment.new(1)
game_score.save
```
-You can also use `Parse::Decrement.new(amount)`.
+You can also use a negative amount to decrement.
#### Arrays
@@ -247,7 +247,7 @@ You can delete a single field from an object by using the `Parse::Object#delete_
To reduce the amount of time spent on network round trips, you can create, update, or delete several objects in one call, using the batch endpoint.
-parse-ruby-client provides a "manual" way to construct Batch Operations, as well as some convenience methods. The commands are run in the order they are given. For example, to create a couple of GameScore objects using the "manual" style, use `Parse::Batch#add_request`. `#add_request` takes a `Hash` with `"method"`, `"path"`, and `"body"` keys that specify the HTTP command that would normally be used for that command.
+parse-ruby-client provides a "manual" way to construct Batch Operations, as well as some convenience methods. The commands are run in the order they are given. For example, to create a couple of GameScore objects using the "manual" style, use `Parse::Batch#add_request`. `#add_request` takes a `Hash` with `"method"`, `"path"`, and `"body"` keys that specify the HTTP command that would normally be used for that command.
```ruby
batch = Parse::Batch.new
@@ -458,7 +458,7 @@ Other constraint methods include:
`Parse::Query#select` |
TODO: `$select` not yet implemented. This matches a value for a key in the result of a different query |
-
+
For example, to retrieve scores between 1000 and 3000, including the endpoints, we could issue:
@@ -704,7 +704,7 @@ To sign up a new user, create a new `Parse::User` object and then call `#save` o
```ruby
user = Parse::User.new({
- :username => "cooldude6",
+ :username => "cooldude6",
:password => "p_n7!-e8",
:phone => "415-392-0202"
})
@@ -828,7 +828,7 @@ All of the options for queries that work for regular objects also work for user
### Deleting Users
-TODO: Implement this!
+TODO: Implement this!
Proposed api:
@@ -1014,8 +1014,8 @@ To upload a file to Parse, use `Parse::File`. You must include the `"Content-Typ
```ruby
file = Parse::File.new({
- :body => "Hello World!",
- :local_filename => "hello.txt",
+ :body => "Hello World!",
+ :local_filename => "hello.txt",
:content_type => "text/plain"
})
file.save
@@ -1033,8 +1033,8 @@ To upload an image, the syntax is a little bit different. Here's an example that
```ruby
photo = Parse::File.new({
- :body => IO.read("test/parsers.jpg"),
- :local_filename => "parsers.jpg",
+ :body => IO.read("test/parsers.jpg"),
+ :local_filename => "parsers.jpg",
:content_type => "image/jpeg"
})
photo.save
@@ -1046,8 +1046,8 @@ After files are uploaded, you can associate them with Parse objects:
```ruby
photo = Parse::File.new({
- :body => IO.read("test/parsers.jpg"),
- :local_filename => "parsers.jpg",
+ :body => IO.read("test/parsers.jpg"),
+ :local_filename => "parsers.jpg",
:content_type => "image/jpeg"
})
photo.save
diff --git a/features.md b/features.md
index cdb32e3..652a9b0 100644
--- a/features.md
+++ b/features.md
@@ -179,7 +179,7 @@ game_score["score"] = Parse::Increment.new(1)
game_score.save
```
-You can also use `Parse::Decrement.new(amount)`.
+Use a negative amount to decrement.
#### Arrays
@@ -243,7 +243,7 @@ You can delete a single field from an object by using the `Parse::Object#delete_
To reduce the amount of time spent on network round trips, you can create, update, or delete several objects in one call, using the batch endpoint.
-parse-ruby-client provides a "manual" way to construct Batch Operations, as well as some convenience methods. The commands are run in the order they are given. For example, to create a couple of GameScore objects using the "manual" style, use `Parse::Batch#add_request`. `#add_request` takes a `Hash` with `"method"`, `"path"`, and `"body"` keys that specify the HTTP command that would normally be used for that command.
+parse-ruby-client provides a "manual" way to construct Batch Operations, as well as some convenience methods. The commands are run in the order they are given. For example, to create a couple of GameScore objects using the "manual" style, use `Parse::Batch#add_request`. `#add_request` takes a `Hash` with `"method"`, `"path"`, and `"body"` keys that specify the HTTP command that would normally be used for that command.
```ruby
batch = Parse::Batch.new
@@ -454,7 +454,7 @@ Other constraint methods include:
`Parse::Query#select` |
TODO: `$select` not yet implemented. This matches a value for a key in the result of a different query |
-
+
For example, to retrieve scores between 1000 and 3000, including the endpoints, we could issue:
@@ -700,7 +700,7 @@ To sign up a new user, create a new `Parse::User` object and then call `#save` o
```ruby
user = Parse::User.new({
- :username => "cooldude6",
+ :username => "cooldude6",
:password => "p_n7!-e8",
:phone => "415-392-0202"
})
@@ -824,7 +824,7 @@ All of the options for queries that work for regular objects also work for user
### Deleting Users
-TODO: Implement this!
+TODO: Implement this!
Proposed api:
@@ -1010,8 +1010,8 @@ To upload a file to Parse, use `Parse::File`. You must include the `"Content-Typ
```ruby
file = Parse::File.new({
- :body => "Hello World!",
- :local_filename => "hello.txt",
+ :body => "Hello World!",
+ :local_filename => "hello.txt",
:content_type => "text/plain"
})
file.save
@@ -1029,8 +1029,8 @@ To upload an image, the syntax is a little bit different. Here's an example that
```ruby
photo = Parse::File.new({
- :body => IO.read("test/parsers.jpg"),
- :local_filename => "parsers.jpg",
+ :body => IO.read("test/parsers.jpg"),
+ :local_filename => "parsers.jpg",
:content_type => "image/jpeg"
})
photo.save
@@ -1042,8 +1042,8 @@ After files are uploaded, you can associate them with Parse objects:
```ruby
photo = Parse::File.new({
- :body => IO.read("test/parsers.jpg"),
- :local_filename => "parsers.jpg",
+ :body => IO.read("test/parsers.jpg"),
+ :local_filename => "parsers.jpg",
:content_type => "image/jpeg"
})
photo.save
diff --git a/fixtures/vcr_cassettes/test_array_add_unique.yml b/fixtures/vcr_cassettes/test_array_add_unique.yml
new file mode 100644
index 0000000..2d2e4ff
--- /dev/null
+++ b/fixtures/vcr_cassettes/test_array_add_unique.yml
@@ -0,0 +1,180 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://api.parse.com/1/classes/Post
+ body:
+ encoding: UTF-8
+ string: '{}'
+ headers:
+ Content-Type:
+ - application/json
+ Accept:
+ - application/json
+ User-Agent:
+ - Parse for Ruby, 0.0
+ X-Parse-Master-Key:
+ - ''
+ X-Parse-Rest-Api-Key:
+ -
+ X-Parse-Application-Id:
+ -
+ X-Parse-Session-Token:
+ - ''
+ Expect:
+ - ''
+ response:
+ status:
+ code: 201
+ message: Created
+ headers:
+ Access-Control-Allow-Origin:
+ - '*'
+ Access-Control-Request-Method:
+ - '*'
+ Cache-Control:
+ - no-cache
+ Content-Type:
+ - application/json; charset=utf-8
+ Date:
+ - Tue, 21 May 2013 17:44:02 GMT
+ Location:
+ - https://api.parse.com/1/classes/Post/SNcYuo3t5K
+ Server:
+ - nginx/1.2.2
+ Set-Cookie:
+ -
+ Status:
+ - 201 Created
+ X-Runtime:
+ - '0.289539'
+ X-Ua-Compatible:
+ - IE=Edge,chrome=1
+ Content-Length:
+ - '64'
+ Connection:
+ - keep-alive
+ body:
+ encoding: ASCII-8BIT
+ string: '{"createdAt":"2013-05-21T17:44:02.504Z","objectId":"SNcYuo3t5K"}'
+ http_version:
+ recorded_at: Tue, 21 May 2013 17:44:02 GMT
+- request:
+ method: post
+ uri: https://api.parse.com/1/classes/Comment
+ body:
+ encoding: UTF-8
+ string: '{"text":"great post!"}'
+ headers:
+ Content-Type:
+ - application/json
+ Accept:
+ - application/json
+ User-Agent:
+ - Parse for Ruby, 0.0
+ X-Parse-Master-Key:
+ - ''
+ X-Parse-Rest-Api-Key:
+ -
+ X-Parse-Application-Id:
+ -
+ X-Parse-Session-Token:
+ - ''
+ Expect:
+ - ''
+ response:
+ status:
+ code: 201
+ message: Created
+ headers:
+ Access-Control-Allow-Origin:
+ - '*'
+ Access-Control-Request-Method:
+ - '*'
+ Cache-Control:
+ - no-cache
+ Content-Type:
+ - application/json; charset=utf-8
+ Date:
+ - Tue, 21 May 2013 17:44:02 GMT
+ Location:
+ - https://api.parse.com/1/classes/Comment/lJjuzuDd6H
+ Server:
+ - nginx/1.2.2
+ Set-Cookie:
+ -
+ Status:
+ - 201 Created
+ X-Runtime:
+ - '0.107052'
+ X-Ua-Compatible:
+ - IE=Edge,chrome=1
+ Content-Length:
+ - '64'
+ Connection:
+ - keep-alive
+ body:
+ encoding: ASCII-8BIT
+ string: '{"createdAt":"2013-05-21T17:44:02.601Z","objectId":"lJjuzuDd6H"}'
+ http_version:
+ recorded_at: Tue, 21 May 2013 17:44:02 GMT
+- request:
+ method: put
+ uri: https://api.parse.com/1/classes/Post/SNcYuo3t5K
+ body:
+ encoding: UTF-8
+ string: '{"comments":{"__op":"AddUnique","objects":[{"__type":"Pointer","className":"Comment","objectId":"lJjuzuDd6H"}]}}'
+ headers:
+ Content-Type:
+ - application/json
+ Accept:
+ - application/json
+ User-Agent:
+ - Parse for Ruby, 0.0
+ X-Parse-Master-Key:
+ - ''
+ X-Parse-Rest-Api-Key:
+ -
+ X-Parse-Application-Id:
+ -
+ X-Parse-Session-Token:
+ - ''
+ Expect:
+ - ''
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Access-Control-Allow-Origin:
+ - '*'
+ Access-Control-Request-Method:
+ - '*'
+ Cache-Control:
+ - max-age=0, private, must-revalidate
+ Content-Type:
+ - application/json; charset=utf-8
+ Date:
+ - Tue, 21 May 2013 17:44:02 GMT
+ Etag:
+ - '"08c4a7d37dd1d79503cdfba1d649a71b"'
+ Server:
+ - nginx/1.2.2
+ Set-Cookie:
+ -
+ Status:
+ - 200 OK
+ X-Runtime:
+ - '0.109572'
+ X-Ua-Compatible:
+ - IE=Edge,chrome=1
+ Content-Length:
+ - '120'
+ Connection:
+ - keep-alive
+ body:
+ encoding: ASCII-8BIT
+ string: '{"comments":[{"__type":"Pointer","className":"Comment","objectId":"lJjuzuDd6H"}],"updatedAt":"2013-05-21T17:44:02.779Z"}'
+ http_version:
+ recorded_at: Tue, 21 May 2013 17:44:02 GMT
+recorded_with: VCR 2.4.0
diff --git a/fixtures/vcr_cassettes/test_decrement.yml b/fixtures/vcr_cassettes/test_decrement.yml
new file mode 100644
index 0000000..679ebcf
--- /dev/null
+++ b/fixtures/vcr_cassettes/test_decrement.yml
@@ -0,0 +1,121 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: https://api.parse.com/1/classes/Post
+ body:
+ encoding: UTF-8
+ string: '{"count":1}'
+ headers:
+ Content-Type:
+ - application/json
+ Accept:
+ - application/json
+ User-Agent:
+ - Parse for Ruby, 0.0
+ X-Parse-Master-Key:
+ - ''
+ X-Parse-Rest-Api-Key:
+ -
+ X-Parse-Application-Id:
+ -
+ X-Parse-Session-Token:
+ - ''
+ Expect:
+ - ''
+ response:
+ status:
+ code: 201
+ message: Created
+ headers:
+ Access-Control-Allow-Origin:
+ - '*'
+ Access-Control-Request-Method:
+ - '*'
+ Cache-Control:
+ - no-cache
+ Content-Type:
+ - application/json; charset=utf-8
+ Date:
+ - Tue, 21 May 2013 17:44:02 GMT
+ Location:
+ - https://api.parse.com/1/classes/Post/mbi9hgIA8z
+ Server:
+ - nginx/1.2.2
+ Set-Cookie:
+ -
+ Status:
+ - 201 Created
+ X-Runtime:
+ - '0.036852'
+ X-Ua-Compatible:
+ - IE=Edge,chrome=1
+ Content-Length:
+ - '64'
+ Connection:
+ - keep-alive
+ body:
+ encoding: ASCII-8BIT
+ string: '{"createdAt":"2013-05-21T17:44:02.950Z","objectId":"mbi9hgIA8z"}'
+ http_version:
+ recorded_at: Tue, 21 May 2013 17:44:02 GMT
+- request:
+ method: put
+ uri: https://api.parse.com/1/classes/Post/mbi9hgIA8z
+ body:
+ encoding: UTF-8
+ string: '{"count":{"__op":"Increment","amount":-1}}'
+ headers:
+ Content-Type:
+ - application/json
+ Accept:
+ - application/json
+ User-Agent:
+ - Parse for Ruby, 0.0
+ X-Parse-Master-Key:
+ - ''
+ X-Parse-Rest-Api-Key:
+ -
+ X-Parse-Application-Id:
+ -
+ X-Parse-Session-Token:
+ - ''
+ Expect:
+ - ''
+ response:
+ status:
+ code: 200
+ message: OK
+ headers:
+ Access-Control-Allow-Origin:
+ - '*'
+ Access-Control-Request-Method:
+ - '*'
+ Cache-Control:
+ - max-age=0, private, must-revalidate
+ Content-Type:
+ - application/json; charset=utf-8
+ Date:
+ - Tue, 21 May 2013 17:44:03 GMT
+ Etag:
+ - '"801ca6262d69a0dcff8d69e7f078aaca"'
+ Server:
+ - nginx/1.2.2
+ Set-Cookie:
+ -
+ Status:
+ - 200 OK
+ X-Runtime:
+ - '0.104988'
+ X-Ua-Compatible:
+ - IE=Edge,chrome=1
+ Transfer-Encoding:
+ - chunked
+ Connection:
+ - keep-alive
+ body:
+ encoding: ASCII-8BIT
+ string: '{"count":0,"updatedAt":"2013-05-21T17:44:03.131Z"}'
+ http_version:
+ recorded_at: Tue, 21 May 2013 17:44:03 GMT
+recorded_with: VCR 2.4.0
diff --git a/lib/parse/datatypes.rb b/lib/parse/datatypes.rb
index 7a89853..d31de0a 100644
--- a/lib/parse/datatypes.rb
+++ b/lib/parse/datatypes.rb
@@ -202,37 +202,6 @@ def to_json(*a)
end
end
- class Decrement
- # '{"score": {"__op": "Decrement", "amount": 1 } }'
- attr_accessor :amount
-
- def initialize(amount)
- @amount = amount
- end
-
- def eql?(other)
- self.class.equal?(other.class) &&
- amount == other.amount
- end
-
- alias == eql?
-
- def hash
- amount.hash
- end
-
- def as_json(*a)
- {
- Protocol::KEY_OP => Protocol::KEY_DECREMENT,
- Protocol::KEY_AMOUNT => @amount
- }
- end
-
- def to_json(*a)
- as_json.to_json(*a)
- end
- end
-
class ArrayOp
# '{"myArray": {"__op": "Add", "objects": ["something", "something else"] } }'
attr_accessor :operation
diff --git a/lib/parse/object.rb b/lib/parse/object.rb
index d1f753f..a981615 100644
--- a/lib/parse/object.rb
+++ b/lib/parse/object.rb
@@ -116,17 +116,14 @@ def save
method = :post
end
- object_store = Parse.store_objects_by_pointer(self)
-
body = safe_json
data = Parse.client.request(self.uri, method, body)
if data
- parse data
+ # array operations can return mutated view of array which needs to be parsed
+ parse Parse.parse_json(class_name, data)
end
- Parse.restore_objects!(self, object_store)
-
if @class_name == Parse::Protocol::CLASS_USER
self.delete("password")
self.delete(:username)
@@ -209,13 +206,6 @@ def increment(field, amount = 1)
# return nil
#end
- #if amount != 0
- # op = amount > 0 ? Protocol::OP_INCREMENT : Protocol::OP_DECREMENT
- # body = "{\"#{field}\": {\"#{Protocol::KEY_OP}\": \"#{op}\", \"#{Protocol::KEY_AMOUNT}\" : #{amount.abs}}}"
- # data = Parse.client.request( self.uri, :put, body)
- # parse data
- #end
- #self
body = {field => Parse::Increment.new(amount)}.to_json
data = Parse.client.request(self.uri, :put, body)
parse data
@@ -225,11 +215,7 @@ def increment(field, amount = 1)
# Decrement the given field by an amount, which defaults to 1. Saves immediately to reflect decremented
# A synonym for increment(field, -amount).
def decrement(field, amount = 1)
- #increment field, -amount
- body = {field => Parse::Decrement.new(amount)}.to_json
- data = Parse.client.request(self.uri, :put, body)
- parse data
- self
+ increment(field, -amount)
end
private
diff --git a/lib/parse/protocol.rb b/lib/parse/protocol.rb
index 9c62221..83d9340 100644
--- a/lib/parse/protocol.rb
+++ b/lib/parse/protocol.rb
@@ -59,7 +59,6 @@ module Protocol
KEY_OP = "__op"
KEY_INCREMENT = "Increment"
- KEY_DECREMENT = "Decrement"
KEY_DELETE = "Delete"
# array ops
@@ -86,9 +85,6 @@ module Protocol
# Operation name for incrementing an objects field value remotely
OP_INCREMENT = "Increment"
- # Operation name for decrementing an objects field value remotely
- OP_DECREMENT = "Decrement"
-
# The data type name for special JSON objects representing a full object
TYPE_OBJECT = "Object"
diff --git a/lib/parse/util.rb b/lib/parse/util.rb
index 831065a..0eba084 100644
--- a/lib/parse/util.rb
+++ b/lib/parse/util.rb
@@ -84,50 +84,4 @@ def Parse.object_pointer_hash(v)
v.class_name.hash ^ v.id.hash
end
end
-
- def Parse.store_objects_by_pointer(obj, store={})
- if obj.is_a?(Parse::Object) && !obj.new?
- if store[obj.pointer] # don't recurse if you have circular objects
- return store
- end
-
- store[obj.pointer] = obj
- end
-
- if obj.is_a?(Array)
- obj.each do |v|
- Parse.store_objects_by_pointer(v, store)
- end
- elsif obj.is_a?(Hash)
- obj.each do |k, v|
- Parse.store_objects_by_pointer(v, store)
- end
- end
-
- store
- end
-
- def Parse.restore_objects!(obj, store, already_restored={})
- if already_restored[obj]
- return obj
- end
-
- already_restored[obj] = true
-
- if obj.is_a?(Hash) # Parse::Object or Hash, we'll actually modify the object
- obj.each do |k, v|
- obj[k] = Parse.restore_objects!(v, store, already_restored)
- end
-
- obj
- elsif obj.is_a?(Parse::Pointer) && store[obj]
- store[obj]
- elsif obj.is_a?(Array)
- obj.map do |v|
- Parse.restore_objects!(v, store, already_restored)
- end
- else
- obj
- end
- end
end
diff --git a/test/test_datatypes.rb b/test/test_datatypes.rb
index 5f5d671..d917946 100644
--- a/test/test_datatypes.rb
+++ b/test/test_datatypes.rb
@@ -55,13 +55,6 @@ def test_increment
assert_equal increment.to_json, "{\"__op\":\"Increment\",\"amount\":#{amount}}"
end
- def test_decrement
- amount = 5
- increment = Parse::Decrement.new amount
-
- assert_equal increment.to_json, "{\"__op\":\"Decrement\",\"amount\":#{amount}}"
- end
-
def test_geopoint
# '{"location": {"__type":"GeoPoint", "latitude":40.0, "longitude":-30.0}}'
data = {
diff --git a/test/test_object.rb b/test/test_object.rb
index 8355ebc..eb3099e 100644
--- a/test/test_object.rb
+++ b/test/test_object.rb
@@ -198,6 +198,32 @@ def test_array_add_pointerizing
end
end
+ def test_array_add_unique
+ VCR.use_cassette('test_array_add_unique', :record => :new_episodes) do
+ post = Parse::Object.new "Post"
+ post.save
+
+ comment = Parse::Object.new("Comment", "text" => "great post!")
+ comment.save
+
+ post.array_add_unique("comments", comment)
+ assert_equal "great post!", post['comments'][0]['text']
+ post.save
+ assert_equal comment, post['comments'][0]
+ assert post['comments'][0].instance_of?(Parse::Pointer) # save returns array pointerized
+ end
+ end
+
+ def test_decrement
+ VCR.use_cassette('test_decrement', :record => :new_episodes) do
+ post = Parse::Object.new "Post", 'count' => 1
+ post.save
+
+ post.decrement('count')
+ assert_equal 0, post['count']
+ end
+ end
+
def test_array_add_relation
omit("broken test, saving Post results in ParseProtocolError: 111: can't add a relation to an non-relation field")