parse-ruby-client 0.1.14 → 0.1.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/.travis.yml +3 -2
  2. data/Gemfile +12 -12
  3. data/Gemfile.lock +26 -12
  4. data/OLD_README.md +255 -0
  5. data/README.md +1038 -144
  6. data/Rakefile +2 -10
  7. data/VERSION +1 -1
  8. data/features.md +11 -11
  9. data/fixtures/vcr_cassettes/test_acls_arent_objects.yml +180 -0
  10. data/fixtures/vcr_cassettes/test_array_add.yml +20 -20
  11. data/fixtures/vcr_cassettes/test_array_add_pointerizing.yml +239 -0
  12. data/fixtures/vcr_cassettes/test_array_add_unique.yml +180 -0
  13. data/fixtures/vcr_cassettes/test_batch_create_object.yml +53 -80
  14. data/fixtures/vcr_cassettes/test_batch_delete_object.yml +303 -428
  15. data/fixtures/vcr_cassettes/test_batch_run.yml +53 -74
  16. data/fixtures/vcr_cassettes/test_batch_update_object.yml +303 -432
  17. data/fixtures/vcr_cassettes/test_circular_save.yml +121 -0
  18. data/fixtures/vcr_cassettes/test_created_at.yml +53 -74
  19. data/fixtures/vcr_cassettes/test_decrement.yml +121 -0
  20. data/fixtures/vcr_cassettes/test_deep_parse.yml +154 -218
  21. data/fixtures/vcr_cassettes/test_destroy.yml +104 -144
  22. data/fixtures/vcr_cassettes/test_eq_pointerize.yml +239 -0
  23. data/fixtures/vcr_cassettes/test_equality.yml +180 -0
  24. data/fixtures/vcr_cassettes/test_get.yml +104 -146
  25. data/fixtures/vcr_cassettes/test_get_missing.yml +60 -0
  26. data/fixtures/vcr_cassettes/test_include.yml +27 -27
  27. data/fixtures/vcr_cassettes/test_new_model.yml +53 -146
  28. data/fixtures/vcr_cassettes/test_new_object.yml +53 -74
  29. data/fixtures/vcr_cassettes/test_nils_delete_keys.yml +155 -216
  30. data/fixtures/vcr_cassettes/test_parse_delete.yml +204 -284
  31. data/fixtures/vcr_cassettes/test_pointer.yml +53 -74
  32. data/fixtures/vcr_cassettes/test_save_with_sub_objects.yml +298 -0
  33. data/fixtures/vcr_cassettes/test_saving_boolean_values.yml +121 -0
  34. data/fixtures/vcr_cassettes/test_server_update.yml +257 -358
  35. data/fixtures/vcr_cassettes/test_simple_save.yml +53 -74
  36. data/fixtures/vcr_cassettes/test_text_file_save.yml +53 -161
  37. data/fixtures/vcr_cassettes/test_update.yml +104 -144
  38. data/fixtures/vcr_cassettes/test_updated_at.yml +104 -144
  39. data/fixtures/vcr_cassettes/test_user_save.yml +10 -10
  40. data/lib/parse/client.rb +43 -6
  41. data/lib/parse/datatypes.rb +14 -35
  42. data/lib/parse/error.rb +8 -0
  43. data/lib/parse/object.rb +25 -20
  44. data/lib/parse/protocol.rb +3 -4
  45. data/lib/parse/query.rb +10 -9
  46. data/lib/parse/util.rb +36 -2
  47. data/parse-ruby-client.gemspec +22 -18
  48. data/test/helper.rb +33 -5
  49. data/test/test_batch.rb +1 -4
  50. data/test/test_client.rb +59 -7
  51. data/test/test_cloud.rb +3 -7
  52. data/test/test_datatypes.rb +6 -7
  53. data/test/test_file.rb +9 -10
  54. data/test/test_model.rb +1 -4
  55. data/test/test_object.rb +148 -6
  56. data/test/test_push.rb +1 -5
  57. data/test/test_query.rb +13 -3
  58. data/test/test_throttle.rb +1 -3
  59. data/test/test_user.rb +1 -5
  60. metadata +23 -19
  61. data/fixtures/vcr_cassettes/test_array_add_relation.yml +0 -321
  62. data/fixtures/vcr_cassettes/test_cloud_function.yml +0 -81
  63. data/fixtures/vcr_cassettes/test_file_save.yml +0 -87
  64. data/fixtures/vcr_cassettes/test_image_file_associate_with_object.yml +0 -2034
  65. data/fixtures/vcr_cassettes/test_image_file_save.yml +0 -1957
  66. data/fixtures/vcr_cassettes/test_object_id.yml +0 -83
  67. data/fixtures/vcr_cassettes/test_request_batch.yml +0 -62
@@ -1,7 +1,8 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.9.2
3
+ - 2.0.0
4
4
  - 1.9.3
5
+ - 1.9.2
5
6
 
6
- env:
7
+ env:
7
8
  - PARSE_APPLICATION_ID=Slw1ACyMSVguo79pWvfIq15pkUjfwTLNPpL4984b PARSE_REST_API_KEY=qJKM8CGOAn70WOyR16f16YbyKWM0nBJCEbbtAMOm
data/Gemfile CHANGED
@@ -1,20 +1,20 @@
1
- source "http://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
  # Add dependencies required to use your gem here.
3
3
  # Example:
4
- # gem "activesupport", ">= 2.3.5"
4
+ # gem 'activesupport', '>= 2.3.5'
5
5
 
6
6
  # Add dependencies to develop your gem here.
7
7
  # Include everything needed to run rake, tests, features, etc.
8
8
  group :development do
9
- gem "bundler", "~> 1.1.5"
10
- gem "shoulda", ">= 0"
11
- gem "test-unit", '= 2.5.0'
12
- gem "mocha", '= 0.12.0', :require => false
13
- gem "jeweler", "~> 1.6.4"
14
- gem "rcov", ">= 0"
15
- gem "webmock"
16
- gem "vcr"
9
+ gem 'bundler'
10
+ gem 'shoulda', '>= 0'
11
+ gem 'test-unit', '= 2.5.0'
12
+ gem 'mocha', '= 0.12.0', :require => false
13
+ gem 'jeweler', :git => 'https://github.com/foxnewsnetwork/jeweler.git', :branch => 'ruby-2.0.0-ifying'
14
+ gem 'simplecov', :require => false
15
+ gem 'webmock'
16
+ gem 'vcr'
17
17
  end
18
18
 
19
- gem "patron"
20
- gem "iron_mq"
19
+ gem 'patron'
20
+ gem 'iron_mq'
@@ -1,5 +1,16 @@
1
+ GIT
2
+ remote: https://github.com/foxnewsnetwork/jeweler.git
3
+ revision: f05c62e168cfc29bd82cebe06df8fd11e1ef09ee
4
+ branch: ruby-2.0.0-ifying
5
+ specs:
6
+ jeweler (1.8.4)
7
+ bundler (~> 1.3.0.pre)
8
+ git (>= 1.2.5)
9
+ rake
10
+ rdoc
11
+
1
12
  GEM
2
- remote: http://rubygems.org/
13
+ remote: https://rubygems.org/
3
14
  specs:
4
15
  addressable (2.3.2)
5
16
  crack (0.3.1)
@@ -8,26 +19,29 @@ GEM
8
19
  rest (>= 2.2.0)
9
20
  iron_mq (2.1.3)
10
21
  iron_core (>= 0.4.2)
11
- jeweler (1.6.4)
12
- bundler (~> 1.0)
13
- git (>= 1.2.5)
14
- rake
22
+ json (1.8.0)
15
23
  metaclass (0.0.1)
16
24
  mime-types (1.20.1)
17
25
  mocha (0.12.0)
18
26
  metaclass (~> 0.0.1)
27
+ multi_json (1.7.3)
19
28
  net-http-persistent (2.8)
20
- patron (0.4.17)
21
- rake (0.9.2.2)
22
- rcov (0.9.11)
29
+ patron (0.4.18)
30
+ rake (10.0.4)
31
+ rdoc (4.0.1)
32
+ json (~> 1.4)
23
33
  rest (2.2.0)
24
34
  net-http-persistent
25
35
  rest-client (>= 0.3.0)
26
36
  rest-client (1.6.7)
27
37
  mime-types (>= 1.16)
28
38
  shoulda (2.11.3)
39
+ simplecov (0.7.1)
40
+ multi_json (~> 1.0)
41
+ simplecov-html (~> 0.7.1)
42
+ simplecov-html (0.7.1)
29
43
  test-unit (2.5.0)
30
- vcr (2.0.1)
44
+ vcr (2.4.0)
31
45
  webmock (1.8.10)
32
46
  addressable (>= 2.2.7)
33
47
  crack (>= 0.1.7)
@@ -36,13 +50,13 @@ PLATFORMS
36
50
  ruby
37
51
 
38
52
  DEPENDENCIES
39
- bundler (~> 1.1.5)
53
+ bundler
40
54
  iron_mq
41
- jeweler (~> 1.6.4)
55
+ jeweler!
42
56
  mocha (= 0.12.0)
43
57
  patron
44
- rcov
45
58
  shoulda
59
+ simplecov
46
60
  test-unit (= 2.5.0)
47
61
  vcr
48
62
  webmock
@@ -0,0 +1,255 @@
1
+ **Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)*
2
+
3
+ - [Ruby Client for parse.com REST API](#ruby-client-for-parsecom-rest-api)
4
+ - [Dependencies](#dependencies)
5
+ - [Getting Started](#getting-started)
6
+ - [Installation](#installation)
7
+ - [Load API keys from `global.json` created by Cloud Code](#load-api-keys-from-globaljson-created-by-cloud-code)
8
+ - [Creating and Saving Objects](#creating-and-saving-objects)
9
+ - [ActiveRecord-style Models](#activerecord-style-models)
10
+ - [Retrieving Objects](#retrieving-objects)
11
+ - [Queries](#queries)
12
+ - [Create some simple objects to query](#create-some-simple-objects-to-query)
13
+ - [Retrieve all scores between 10 & 20 inclusive](#retrieve-all-scores-between-10-&-20-inclusive)
14
+ - [Retrieve a set of specific scores](#retrieve-a-set-of-specific-scores)
15
+ - [Relational Data](#relational-data)
16
+ - [Push Notifications](#push-notifications)
17
+ - [Batch Requests](#batch-requests)
18
+ - [Cloud Code](#cloud-code)
19
+ - [assumes you have a function named "trivial"](#assumes-you-have-a-function-named-"trivial")
20
+ - [TODO](#todo)
21
+ - [Resources](#resources)
22
+ - [](#)
23
+
24
+ ## Warning
25
+
26
+ There is an undocumented feature I've been working on, where if you provide IronMQ API keys, you will get a rate-limited rest client. This feature is broken and you should not provide such keys and/or use such a feature until this warning is removed. The feature only existed in 0.1.13 and has been removed in 0.1.14. If you don't provide the keys when calling Parse.init, you will be fine. I'm posting this out of extra caution.
27
+
28
+ # Ruby Client for parse.com REST API
29
+
30
+ This file implements a simple Ruby client library for using Parse's REST API.
31
+ Rather than try to implement an ActiveRecord-compatible library, it tries to
32
+ match the structure of the iOS and Android SDKs from Parse.
33
+
34
+ So far it supports simple GET, PUT, and POST operations on objects. Enough
35
+ to read & write simple data.
36
+
37
+ ## Dependencies
38
+
39
+ This currently depends on the gems 'json' and 'patron' for JSON support and HTTP, respectively.
40
+
41
+ # Getting Started
42
+
43
+ ## Installation
44
+
45
+ [![Gem Version](https://badge.fury.io/rb/parse-ruby-client.png)](http://badge.fury.io/rb/parse-ruby-client)
46
+
47
+ `gem "parse-ruby-client"`
48
+
49
+ ---
50
+
51
+ To get started, load the parse.rb file and call Parse.init to initialize the client object with
52
+ your application ID and API key values, which you can obtain from the parse.com dashboard.
53
+
54
+ ```ruby
55
+ require 'parse-ruby-client'
56
+
57
+ Parse.init :application_id => "<your_app_id>",
58
+ :api_key => "<your_api_key>"
59
+ ```
60
+
61
+ If you don't like pasting this stuff in every time you fire up irb, save your api keys to `.bash_profile` or similar:
62
+
63
+ ```bash
64
+ export PARSE_APPLICATION_ID="12345678"
65
+ export PARSE_REST_API_KEY="ABCDEFGH"
66
+ ```
67
+
68
+ Now you can just do this:
69
+
70
+ ```ruby
71
+ Parse.init
72
+ ```
73
+
74
+ The test folder assumes this naming convention for environment variables, so if you want to run the tests, you *must* do this. But it's easy. And good for you, too.
75
+
76
+ ### Load API keys from `global.json` created by Cloud Code
77
+
78
+ ```ruby
79
+ Parse.init_from_cloud_code("path/to/global.json")
80
+ ```
81
+
82
+ The path defaults to `"../config/global.json"`. So if you create a folder inside the root of a Cloud Code directory, and in that folder is a `.rb` file, just call `Parse.init_from_cloud_code` with no arguments and you're set.
83
+
84
+ With `Parse::init_from_cloud_code`, you can easily write Ruby [tests for Cloud Code functions](https://github.com/adelevie/ParseCloudTest).
85
+
86
+ ## Creating and Saving Objects
87
+
88
+ Create an instance of ```Parse::Object``` with your class name supplied as a string, set some keys, and call ```save()```.
89
+
90
+ ```ruby
91
+ game_score = Parse::Object.new "GameScore"
92
+ game_score["score"] = 1337
93
+ game_score["playerName"] = "Sean Plott"
94
+ game_score["cheatMode"] = false
95
+ game_score.save
96
+ ```
97
+
98
+ Alternatively, you can initialize the object's initial values with a hash.
99
+
100
+ ```ruby
101
+ game_score = Parse::Object.new "GameScore", {
102
+ "score" => 1337, "playerName" => "Sean Plott", "cheatMode" => false
103
+ }
104
+ ```
105
+
106
+ Or if you prefer, you can use symbols for the hash keys - they will be converted to strings
107
+ by ```Parse::Object.initialize()```.
108
+
109
+ ```ruby
110
+ game_score = Parse::Object.new "GameScore", {
111
+ :score => 1337, :playerName => "Sean Plott", :cheatMode => false
112
+ }
113
+ ```
114
+
115
+ ### ActiveRecord-style Models
116
+
117
+ I like ActiveRecord-style models, but I also want to avoid ActiveRecord-style model bloat. `Parse::Model` is just a subclass of `Parse::Object` that passes the class name into the `initialize` method.
118
+
119
+ ```ruby
120
+ class Post < Parse::Model
121
+ end
122
+ ```
123
+
124
+ The entire source for `Parse::Model` is just seven lines of simple Ruby:
125
+
126
+ ```ruby
127
+ module Parse
128
+ class Model < Parse::Object
129
+ def initialize
130
+ super(self.class.to_s)
131
+ end
132
+ end
133
+ end
134
+ ```
135
+
136
+ ## Retrieving Objects
137
+
138
+ Individual objects can be retrieved with a single call to ```Parse.get()``` supplying the class and object id.
139
+
140
+ ```ruby
141
+ game_score = Parse.get "GameScore", "xWMyZ4YEGZ"
142
+ ```
143
+
144
+ All the objects in a given class can be retrieved by omitting the object ID. Use caution if you have lots
145
+ and lots of objects of that class though.
146
+
147
+ ```ruby
148
+ all_scores = Parse.get "GameScore"
149
+ ```
150
+
151
+ ## Queries
152
+
153
+ Queries are supported by the ```Parse::Query``` class.
154
+
155
+ ```ruby
156
+ # Create some simple objects to query
157
+ (1..100).each { |i|
158
+ score = Parse::Object.new "GameScore"
159
+ score["score"] = i
160
+ score.save
161
+ }
162
+
163
+ # Retrieve all scores between 10 & 20 inclusive
164
+ Parse::Query.new("GameScore") \
165
+ .greater_eq("score", 10) \
166
+ .less_eq("score", 20) \
167
+ .get
168
+
169
+ # Retrieve a set of specific scores
170
+ Parse::Query.new("GameScore") \
171
+ .value_in("score", [10, 20, 30, 40]) \
172
+ .get
173
+
174
+ ```
175
+
176
+ ### Relational Data
177
+
178
+ ```ruby
179
+ post = Parse::Object.new "Post"
180
+ post.save
181
+
182
+ comment = Parse::Object.new "Comment"
183
+ comment.save
184
+
185
+ post.array_add_relation("comments", comment.pointer)
186
+ post.save
187
+
188
+ q = Parse::Query.new("Comment")
189
+ q.related_to("comments", post.pointer)
190
+ comments = q.get
191
+ assert_equal comments.first["objectId"], comment["objectId"]
192
+ ```
193
+
194
+ ## Push Notifications
195
+
196
+ ```ruby
197
+ push = Parse::Push.new({"alert" => "I'm sending this push to all my app users!"})
198
+ push.save
199
+ ```
200
+
201
+ ## Batch Requests
202
+
203
+ Automagic:
204
+
205
+ ```ruby
206
+ posts = Parse::Query.new("Post").get
207
+ update_batch = Parse::Batch.new
208
+ posts.each_slice(20) do |posts_slice|
209
+ posts_slice.each do |post|
210
+ post["title"] = "Updated title!"
211
+ update_batch.update_object(post)
212
+ end
213
+ end
214
+ update_batch.run!
215
+ ```
216
+
217
+ `Parse::Batch` also has `#create_object` and `delete_object` methods. Both take instances of `Parse::Object` as the only argument.
218
+
219
+ Manual:
220
+
221
+ ```ruby
222
+ batch = Parse::Batch.new
223
+ batch.add_request({
224
+ "method" => "POST",
225
+ "path" => "/1/classes/GameScore",
226
+ "body" => {
227
+ "score" => 1337,
228
+ "playerName" => "Sean Plott"
229
+ }
230
+ })
231
+ resp = batch.run!
232
+ ```
233
+
234
+ ## Cloud Code
235
+
236
+ ```ruby
237
+ # assumes you have a function named "trivial"
238
+ function = Parse::Cloud::Function.new("trivial")
239
+ params = {"foo" => "bar"}
240
+ function.call(params)
241
+ ```
242
+
243
+ # TODO
244
+
245
+ - Add some form of debug logging
246
+ - ~~Support for Date, Pointer, and Bytes API [data types](https://www.parse.com/docs/rest#objects-types)~~
247
+ - ACLs
248
+ - Login
249
+
250
+
251
+ # Resources
252
+
253
+ - parse.com [REST API documentation](https://parse.com/docs/rest)
254
+ - [parse_resource](https://github.com/adelevie/parse_resource) , an ActiveRecord-compatible wrapper
255
+ for the API for seamless integration into Rails.
data/README.md CHANGED
@@ -1,251 +1,1145 @@
1
- **Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)*
1
+ ## Summary
2
2
 
3
- - [Ruby Client for parse.com REST API](#ruby-client-for-parsecom-rest-api)
4
- - [Dependencies](#dependencies)
5
- - [Getting Started](#getting-started)
6
- - [Installation](#installation)
7
- - [Load API keys from `global.json` created by Cloud Code](#load-api-keys-from-globaljson-created-by-cloud-code)
8
- - [Creating and Saving Objects](#creating-and-saving-objects)
9
- - [ActiveRecord-style Models](#activerecord-style-models)
10
- - [Retrieving Objects](#retrieving-objects)
11
- - [Queries](#queries)
12
- - [Create some simple objects to query](#create-some-simple-objects-to-query)
13
- - [Retrieve all scores between 10 & 20 inclusive](#retrieve-all-scores-between-10-&-20-inclusive)
14
- - [Retrieve a set of specific scores](#retrieve-a-set-of-specific-scores)
15
- - [Relational Data](#relational-data)
16
- - [Push Notifications](#push-notifications)
17
- - [Batch Requests](#batch-requests)
18
- - [Cloud Code](#cloud-code)
19
- - [assumes you have a function named "trivial"](#assumes-you-have-a-function-named-"trivial")
20
- - [TODO](#todo)
21
- - [Resources](#resources)
22
- - [](#)
3
+ parse-ruby-client lets you interact with Parse using Ruby. There are many uses. For example:
23
4
 
24
- # Ruby Client for parse.com REST API
5
+ - A webserver can show data from Parse on a website.
6
+ - You can upload large amounts of data that will later be consumed in a mobile app.
7
+ - You can download recent data to run your own custom analytics.
8
+ - You can export all of your data if you no longer want to use Parse.
25
9
 
26
- This file implements a simple Ruby client library for using Parse's REST API.
27
- Rather than try to implement an ActiveRecord-compatible library, it tries to
28
- match the structure of the iOS and Android SDKs from Parse.
10
+ ### Quick Reference
29
11
 
30
- So far it supports simple GET, PUT, and POST operations on objects. Enough
31
- to read & write simple data.
12
+ #### Installation
32
13
 
33
- ## Dependencies
14
+ `gem install parse-ruby-client` or add `gem "parse-ruby-client"` to your `Gemfile.`
34
15
 
35
- This currently depends on the gems 'json' and 'patron' for JSON support and HTTP, respectively.
16
+ #### Configuration
36
17
 
37
- # Getting Started
18
+ ```ruby
19
+ require 'parse-ruby-client'
38
20
 
39
- ## Installation
21
+ Parse.init :application_id => "<your_app_id>",
22
+ :api_key => "<your_api_key>"
23
+ ```
40
24
 
41
25
  [![Gem Version](https://badge.fury.io/rb/parse-ruby-client.png)](http://badge.fury.io/rb/parse-ruby-client)
42
26
 
43
- `gem "parse-ruby-client"`
27
+ [![Build Status](https://travis-ci.org/adelevie/parse-ruby-client.png?branch=master)](https://travis-ci.org/adelevie/parse-ruby-client)
44
28
 
45
- ---
29
+ **Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)*
46
30
 
47
- To get started, load the parse.rb file and call Parse.init to initialize the client object with
48
- your application ID and API key values, which you can obtain from the parse.com dashboard.
31
+ - [Summary](#summary)
32
+ - [Quick Reference](#quick-reference)
33
+ - [Installation](#installation)
34
+ - [Configuration](#configuration)
35
+ - [Objects](#objects)
36
+ - [Creating Objects](#creating-objects)
37
+ - [Retrieving Objects](#retrieving-objects)
38
+ - [Updating Objects](#updating-objects)
39
+ - [Counters](#counters)
40
+ - [Arrays](#arrays)
41
+ - [Relations](#relations)
42
+ - [TODO: This method is not yet implemented.](#todo:-this-method-is-not-yet-implemented)
43
+ - [Deleting Objects](#deleting-objects)
44
+ - [TODO: This method is not yet implemented.](#todo:-this-method-is-not-yet-implemented)
45
+ - [Batch Operations](#batch-operations)
46
+ - [making a few GameScore objects](#making-a-few-gamescore-objects)
47
+ - [Data Types](#data-types)
48
+ - [Dates](#dates)
49
+ - [Bytes](#bytes)
50
+ - [Pointers](#pointers)
51
+ - [Relation](#relation)
52
+ - [TODO: There is no Ruby object representation of this type, yet.](#todo:-there-is-no-ruby-object-representation-of-this-type-yet)
53
+ - [Future data types and namespacing](#future-data-types-and-namespacing)
54
+ - [Queries](#queries)
55
+ - [Basic Queries](#basic-queries)
56
+ - [Query Contraints](#query-contraints)
57
+ - [Queries on Array Values](#queries-on-array-values)
58
+ - [Relational Queries](#relational-queries)
59
+ - [Counting Objects](#counting-objects)
60
+ - [Compound Queries](#compound-queries)
61
+ - [Users](#users)
62
+ - [Signing Up](#signing-up)
63
+ - [Logging In](#logging-in)
64
+ - [Verifying Emails](#verifying-emails)
65
+ - [Requesting A Password Reset](#requesting-a-password-reset)
66
+ - [Retrieving Users](#retrieving-users)
67
+ - [Updating Users](#updating-users)
68
+ - [Querying](#querying)
69
+ - [Deleting Users](#deleting-users)
70
+ - [Linking Users](#linking-users)
71
+ - [Signing Up and Logging In](#signing-up-and-logging-in)
72
+ - [should look something like this:](#should-look-something-like-this:)
73
+ - [Linking](#linking)
74
+ - [should look something like this:](#should-look-something-like-this:)
75
+ - [or](#or)
76
+ - [Unlinking](#unlinking)
77
+ - [should look something like this:](#should-look-something-like-this:)
78
+ - [Security](#security)
79
+ - [Roles](#roles)
80
+ - [Files](#files)
81
+ - [Uploading Files](#uploading-files)
82
+ - [Associating with Objects](#associating-with-objects)
83
+ - [Deleting Files](#deleting-files)
84
+ - [Push Notifications](#push-notifications)
85
+ - [Installations](#installations)
86
+ - [GeoPoints](#geopoints)
87
+ - [GeoPoint](#geopoint)
88
+ - [GeoQueries](#geoqueries)
89
+ - [should look something like this:](#should-look-something-like-this:)
90
+ - [Caveats](#caveats)
91
+
92
+ ## Objects
93
+
94
+ The design philosophy behind parse-ruby-client is to stay out of the way as much as possible. Parse Objects, at the most basic level, act like Ruby `Hash` objects with Parse-specific methods tacked on.
95
+
96
+ ### Creating Objects
49
97
 
50
98
  ```ruby
51
- require 'parse-ruby-client'
99
+ game_score = Parse::Object.new("GameScore")
100
+ game_score["score"] = 1337
101
+ game_score["playerName"] = "Sean Plott"
102
+ game_score["cheatMode"] = false
103
+ result = game_score.save
104
+ puts result
105
+ ```
52
106
 
53
- Parse.init :application_id => "<your_app_id>",
54
- :api_key => "<your_api_key>"
107
+ This will return:
108
+
109
+ ```ruby
110
+ {"score"=>1337,
111
+ "playerName"=>"Sean Plott",
112
+ "cheatMode"=>false,
113
+ "createdAt"=>"2013-01-19T21:01:33.562Z",
114
+ "objectId"=>"GeqPWJdNqp"}
115
+ ```
116
+
117
+ ### Retrieving Objects
118
+
119
+ The easiest way to retrieve Objects is with `Parse::Query`:
120
+
121
+ ```ruby
122
+ game_score_query = Parse::Query.new("GameScore")
123
+ game_score_query.eq("objectId", "GeqPWJdNqp")
124
+ game_score = game_score_query.get
125
+ puts game_score
126
+ ```
127
+
128
+ This will return:
129
+
130
+ ```ruby
131
+ [{"score"=>1337,
132
+ "playerName"=>"Sean Plott",
133
+ "createdAt"=>"2013-01-19T21:01:33.562Z",
134
+ "updatedAt"=>"2013-01-19T21:01:33.562Z",
135
+ "objectId"=>"GeqPWJdNqp"}]
136
+ ```
137
+
138
+ Notice that this is an `Array` of results. For more information on queries, see TODO.
139
+
140
+ When retrieving objects that have pointers to children, you can fetch child objects by setting the `include` attribute. For instance, to fetch the object pointed to by the "game" key:
141
+
142
+ ```ruby
143
+ game_score_query = Parse::Query.new("GameScore")
144
+ game_score_query.eq("objectId", "GeqPWJdNqp")
145
+ game_score_query.include = "game"
146
+ game_score = game_score_query.get
55
147
  ```
56
148
 
57
- If you don't like pasting this stuff in every time you fire up irb, save your api keys to `.bash_profile` or similar:
149
+ You can include multiple children pointers by separating key names by commas:
58
150
 
59
- ```bash
60
- export PARSE_APPLICATION_ID="12345678"
61
- export PARSE_REST_API_KEY="ABCDEFGH"
151
+ ```ruby
152
+ game_score_query.include = "game,genre"
62
153
  ```
63
154
 
64
- Now you can just do this:
155
+ ### Updating Objects
156
+
157
+ To change the data on an object that already exists, just call `Parse::Object#save` on it. Any keys you don't specify will remain unchanged, so you can update just a subset of the object's data. For example, if we wanted to change the score field of our object:
65
158
 
66
159
  ```ruby
67
- Parse.init
160
+ game_score = Parse::Query.new("GameScore").eq("objectId", "GeqPWJdNqp").get.first
161
+ game_score["score"] = 73453
162
+ result = game_score.save
163
+ puts result
68
164
  ```
69
165
 
70
- The test folder assumes this naming convention for environment variables, so if you want to run the tests, you *must* do this. But it's easy. And good for you, too.
166
+ This will return:
167
+
168
+ ```ruby
169
+ {"score"=>73453,
170
+ "playerName"=>"Sean Plott",
171
+ "createdAt"=>"2013-01-19T21:01:33.562Z",
172
+ "updatedAt"=>"2013-01-19T21:16:34.395Z",
173
+ "objectId"=>"GeqPWJdNqp"}
174
+ ```
71
175
 
72
- ### Load API keys from `global.json` created by Cloud Code
176
+ #### Counters
177
+
178
+ To help with storing counter-type data, Parse provides the ability to atomically increment (or decrement) any number field. So, we can increment the score field like so:
73
179
 
74
180
  ```ruby
75
- Parse.init_from_cloud_code("path/to/global.json")
181
+ game_score = Parse::Query.new("GameScore").eq("objectId", "GeqPWJdNqp").get.first
182
+ game_score["score"] = Parse::Increment.new(1)
183
+ game_score.save
76
184
  ```
77
185
 
78
- The path defaults to `"../config/global.json"`. So if you create a folder inside the root of a Cloud Code directory, and in that folder is a `.rb` file, just call `Parse.init_from_cloud_code` with no arguments and you're set.
186
+ You can also use a negative amount to decrement.
187
+
188
+ #### Arrays
79
189
 
80
- With `Parse::init_from_cloud_code`, you can easily write Ruby [tests for Cloud Code functions](https://github.com/adelevie/ParseCloudTest).
190
+ To help with storing array data, there are three operations that can be used to atomically change an array field:
81
191
 
82
- ## Creating and Saving Objects
192
+ 1. `Parse::Object#array_add(field, value)` appends the given array of objects to the end of an array field.
193
+ 2. `Parse::Object#array_add_unique(field, value)` adds only the given objects which aren't already contained in an array field to that field. The position of the insert is not guaranteed.
194
+ 3. `Parse::Object#array_remove(field, value)` removes all instances of each given object from an array field.
83
195
 
84
- Create an instance of ```Parse::Object``` with your class name supplied as a string, set some keys, and call ```save()```.
196
+ Each method takes an array of objects to add or remove in the "objects" key. For example, we can add items to the set-like "skills" field like so:
85
197
 
86
198
  ```ruby
87
- game_score = Parse::Object.new "GameScore"
88
- game_score["score"] = 1337
89
- game_score["playerName"] = "Sean Plott"
90
- game_score["cheatMode"] = false
199
+ game_score = Parse::Query.new("GameScore").eq("objectId", "5iEEIxM4MW").get.first
200
+ game_score.array_add_unique("skills", ["flying", "kungfu"])
91
201
  game_score.save
202
+ puts game_score["skills"]
92
203
  ```
93
204
 
94
- Alternatively, you can initialize the object's initial values with a hash.
205
+ This will return:
206
+
207
+ ```ruby
208
+ [["flying", "kungfu"]]
209
+ ```
210
+
211
+ #### Relations
212
+
213
+ In order to update Relation types, Parse provides special operators to atomically add and remove objects to a relation. So, we can add an object to a relation like so:
214
+
215
+ ```ruby
216
+ game_score = Parse::Query.new("GameScore").eq("objectId", "5iEEIxM4MW").get.first
217
+ player = Parse::Query.new("Player").eq("objectId", "GLtvtEaGKa").get.first
218
+ game_score.array_add_relation("opponents", player.pointer)
219
+ game_score.save
220
+ game_score["opponents"] #=> #<Parse::ArrayOp:0x007fbe98931508 @operation="AddRelation", @objects=[Player:GLtvtEaGKa]>
221
+ game_score["opponents"].objects.first #=> Player:GLtvtEaGKa
222
+ ```
223
+
224
+ To remove an object from a relation, you can do:
225
+
226
+ ```ruby
227
+ # TODO: This method is not yet implemented.
228
+ ```
229
+
230
+ ### Deleting Objects
231
+
232
+ To delete an object from the Parse Cloud, call `Parse::Object#parse_delete`. For example:
95
233
 
96
234
  ```ruby
97
- game_score = Parse::Object.new "GameScore", {
98
- "score" => 1337, "playerName" => "Sean Plott", "cheatMode" => false
235
+ game_score = Parse::Query.new("GameScore").eq("objectId", "5iEEIxM4MW").get.first
236
+ game_score.parse_delete
237
+ Parse::Query.new("GameScore").eq("objectId", "5iEEIxM4MW").get.length #=> 0
238
+ ```
239
+
240
+ You can delete a single field from an object by using the `Parse::Object#delete_field` operation:
241
+
242
+ ```ruby
243
+ # TODO: This method is not yet implemented.
244
+ ```
245
+
246
+ ### Batch Operations
247
+
248
+ 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.
249
+
250
+ 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.
251
+
252
+ ```ruby
253
+ batch = Parse::Batch.new
254
+ batch.add_request({
255
+ "method" => "POST",
256
+ "path" => "/1/classes/GameScore"
257
+ "body" => {
258
+ "score" => 1337,
259
+ "playerName" => "Sean Plott"
260
+ }
261
+ })
262
+ batch.add_request({
263
+ "method" => "POST",
264
+ "path" => "/1/classes/GameScore"
265
+ "body" => {
266
+ "score" => 1338,
267
+ "playerName" => "ZeroCool"
268
+ }
269
+ })
270
+ batch.run!
271
+ ```
272
+
273
+ Because manually constructing `"path"` values is repetitive, you can use `Parse::Batch#create_object`, `Parse::Batch#update_object`, and `Parse::Batch#delete_object`. Each of these methods takes an instance of `Parse::Object` as the only argument. Then you just call `Parse::Batch#run!`. For example:
274
+
275
+ ```ruby
276
+ # making a few GameScore objects
277
+ game_scores = [1, 2, 3, 4, 5].map do |i|
278
+ gs = Parse::Object.new("GameScore")
279
+ gs["score"] = "#{i}"
280
+ gs
281
+ end
282
+
283
+ batch = Parse::Batch.new
284
+ game_scores.each do |gs|
285
+ batch.create_object(gs)
286
+ end
287
+ batch.run!
288
+ ```
289
+
290
+ The response from batch will be an `Array` with the same number of elements as the input list. Each item in the `Array` with be a `Hash` with either the `"success"` or `"error"` field set. The value of success will be the normal response to the equivalent REST command:
291
+
292
+ ```ruby
293
+ {
294
+ "success" => {
295
+ "createdAt" => "2012-06-15T16:59:11.276Z",
296
+ "objectId" => "YAfSAWwXbL"
297
+ }
99
298
  }
100
299
  ```
101
300
 
102
- Or if you prefer, you can use symbols for the hash keys - they will be converted to strings
103
- by ```Parse::Object.initialize()```.
301
+ The value of `"error"` will be a `Hash` with a numeric code and error string:
104
302
 
105
303
  ```ruby
106
- game_score = Parse::Object.new "GameScore", {
107
- :score => 1337, :playerName => "Sean Plott", :cheatMode => false
304
+ {
305
+ "error" => {
306
+ "code" => 101,
307
+ "error" => "object not found for delete"
308
+ }
108
309
  }
109
310
  ```
110
311
 
111
- ### ActiveRecord-style Models
312
+ ### Data Types
112
313
 
113
- I like ActiveRecord-style models, but I also want to avoid ActiveRecord-style model bloat. `Parse::Model` is just a subclass of `Parse::Object` that passes the class name into the `initialize` method.
314
+ So far we have only used values that can be encoded with standard JSON. The Parse mobile client libraries also support dates, binary data, and relational data. In parse-ruby-client, these values are encoded as JSON hashes with the __type field set to indicate their type, so you can read or write these fields if you use the correct encoding. See https://github.com/adelevie/parse-ruby-client/blob/master/lib/parse/datatypes.rb for implementation details of several common data types.
315
+
316
+ #### Dates
317
+
318
+ Use `Parse::Date::new` to create a date object:
114
319
 
115
320
  ```ruby
116
- class Post < Parse::Model
117
- end
321
+ date_time = DateTime.now
322
+ parse_date = Parse::Date.new(date_time)
118
323
  ```
119
324
 
120
- The entire source for `Parse::Model` is just seven lines of simple Ruby:
325
+ Dates are useful in combination with the built-in createdAt and updatedAt fields. For example, to retrieve objects created since a particular time, just encode a Date in a comparison query:
121
326
 
122
327
  ```ruby
123
- module Parse
124
- class Model < Parse::Object
125
- def initialize
126
- super(self.class.to_s)
127
- end
128
- end
129
- end
328
+ game_score = Parse::Query.new("GameScore").tap do |q|
329
+ g.greater_than("createdAt", Parse::Object.new(DateTime.now)) # query options explained in more detail later in this document
330
+ end.get.first
331
+ ```
332
+
333
+ `Parse::Date::new` can take a `DateTime`, iso `Hash`, or a `String` that can be parsed by `DateTime#parse` as the sole argument.
334
+
335
+ The `Parse::Date` API is not set in stone and will likely change following the suggestions discussed here: https://github.com/adelevie/parse-ruby-client/issues/35. The current methods probably will not go away, but some newer, easier methods will be added.
336
+
337
+ #### Bytes
338
+
339
+ `Parse::Bytes` contains an attribute, `base64`, which contains a base64 encoding of binary data. The specific base64 encoding is the one used by MIME, and does not contain whitespace.
340
+
341
+ ```ruby
342
+ data = "TG9va3MgbGlrZSB5b3UgZm91bmQgYW4gZWFzdGVyIEVnZy4gTWF5YmUgaXQn\ncyB0aW1lIHlvdSB0b29rIGEgTWluZWNyYWZ0IGJyZWFrPw==\n" # base64 encoded data
343
+ bytes = Parse::Bytes.new(data)
344
+ ```
345
+
346
+ #### Pointers
347
+
348
+ The `Pointer` type is used when mobile code sets a `PFObject` (iOS SDK) or `ParseObject` (Android SDK) as the value of another object. It contains the `className` and `objectId` of the referred-to value.
349
+
350
+ ```ruby
351
+ pointer = Parse::Pointer.new({"className => gameScore", "objectId" => "GeqPWJdNqp"})
130
352
  ```
131
353
 
132
- ## Retrieving Objects
354
+ Pointers to `user` objects have a `className` of `_User`. Prefixing with an underscore is forbidden for developer-defined classes and signifies the class is a special built-in.
133
355
 
134
- Individual objects can be retrieved with a single call to ```Parse.get()``` supplying the class and object id.
356
+ If you already have a `Parse::Object`, you can get its `Pointer` very easily:
135
357
 
136
358
  ```ruby
137
- game_score = Parse.get "GameScore", "xWMyZ4YEGZ"
359
+ game_score.pointer
138
360
  ```
139
361
 
140
- All the objects in a given class can be retrieved by omitting the object ID. Use caution if you have lots
141
- and lots of objects of that class though.
362
+ #### Relation
363
+
364
+ The `Relation` type is used for many-to-many relations when the mobile uses `PFRelation` (iOS SDK) or `ParseRelation` (Android SDK) as a value. It has a `className` that is the class name of the target objects.
142
365
 
143
366
  ```ruby
144
- all_scores = Parse.get "GameScore"
367
+ # TODO: There is no Ruby object representation of this type, yet.
145
368
  ```
146
369
 
370
+ #### Future data types and namespacing
371
+
372
+ Though this is something parse-ruby-client will take care for you, it's worth noting:
373
+
374
+ When more data types are added, they will also be represented as hashes with a `__type` field set, so you may not use this field yourself on JSON objects.
375
+
376
+
147
377
  ## Queries
148
378
 
149
- Queries are supported by the ```Parse::Query``` class.
379
+ Queries are created like so:
380
+
381
+ ```ruby
382
+ query = Parse::Query.new("GameScore")
383
+ ```
384
+
385
+
386
+
387
+ ### Basic Queries
388
+
389
+ You can retrieve multiple objects at once by calling `#get`:
390
+
391
+ ```ruby
392
+ query.get
393
+ ```
394
+
395
+ The return value is an `Array` of `Parse::Object` instances:
396
+
397
+ ```ruby
398
+ [{"score"=>100,
399
+ "player"=>player:qPHDUbBbjj,
400
+ "createdAt"=>"2012-10-10T00:16:10.846Z",
401
+ "updatedAt"=>"2012-10-10T00:16:10.846Z",
402
+ "objectId"=>"6ff54A5OCY"},
403
+ {"score"=>1337,
404
+ "playerName"=>"Sean Plott",
405
+ "createdAt"=>"2013-01-05T22:51:56.033Z",
406
+ "updatedAt"=>"2013-01-05T22:51:56.033Z",
407
+ "objectId"=>"MpPBAHsqNg"},
408
+ {"score"=>1337,
409
+ "playerName"=>"Sean Plott",
410
+ "createdAt"=>"2013-01-05T22:53:22.609Z",
411
+ "updatedAt"=>"2013-01-05T22:53:22.609Z",
412
+ "objectId"=>"T1dj8cWwYJ"}]]
413
+ ```
414
+
415
+ ### Query Contraints
416
+
417
+ There are several ways to put constraints on the objects found, using various methods of `Parse::Query`. The most basic is `Parse::Query#eq`:
418
+
419
+ ```ruby
420
+ query = Parse::Query.new("GameScore").eq("playerName", "Sean Plott")
421
+ ```
422
+
423
+ Other constraint methods include:
424
+
425
+ <table>
426
+ <tr>
427
+ <td>`Parse::Query#less_than(field, value)`</td>
428
+ <td>Less Than</td>
429
+ </tr>
430
+ <tr>
431
+ <td>`Parse::Query#less_eq(field, value)`</td>
432
+ <td>Less Than or Equal To</td>
433
+ </tr>
434
+ <tr>
435
+ <td>`Parse::Query#greater_than(field, value)`</td>
436
+ <td>Greater Than</td>
437
+ </tr>
438
+ <tr>
439
+ <td>`Parse::Query#greater_eq(field, value)`</td>
440
+ <td>Greater Than Or Equal To</td>
441
+ </tr>
442
+ <tr>
443
+ <td>`Parse::Query#not_eq(field, value)`</td>
444
+ <td>Not Equal To</td>
445
+ </tr>
446
+ <tr>
447
+ <td>`Parse::Query#value_in(field, values)`</td>
448
+ <td>Contained In</td>
449
+ </tr>
450
+ <tr>
451
+ <td>`Parse::Query#value_not_in(field, values)`</td>
452
+ <td>Not Contained in</td>
453
+ </tr>
454
+ <tr>
455
+ <td>`Parse::Query#exists(field, value=true)`</td>
456
+ <td>A value is set for the key</td>
457
+ </tr>
458
+ <tr>
459
+ <td>`Parse::Query#select`</td>
460
+ <td>TODO: `$select` not yet implemented. This matches a value for a key in the result of a different query</td>
461
+ </tr>
462
+ </table>
463
+
464
+ For example, to retrieve scores between 1000 and 3000, including the endpoints, we could issue:
465
+
466
+ ```ruby
467
+ scores = Parse::Query.new("GameScore").tap do |q|
468
+ q.greater_eq("score", 1000)
469
+ q.less_eq("score", 3000)
470
+ end.get
471
+ ```
472
+
473
+ To retrieve scores equal to an odd number below 10, we could issue:
474
+
475
+ ```ruby
476
+ scores = Parse::Query.new("GameScore").tap do |q|
477
+ q.value_in("score", [1,3,5,7,9])
478
+ end.get
479
+ ```
480
+
481
+ To retrieve scores not by a given list of players we could issue:
482
+
483
+ ```ruby
484
+ scores = Parse::Query.new("GameScore").tap do |q|
485
+ q.value_not_in("playerName", ["Jonathan Walsh","Dario Wunsch","Shawn Simon"])
486
+ end.get
487
+ ```
488
+
489
+ To retrieve documents with the score set, we could issue:
150
490
 
151
491
  ```ruby
152
- # Create some simple objects to query
153
- (1..100).each { |i|
154
- score = Parse::Object.new "GameScore"
155
- score["score"] = i
156
- score.save
492
+ scores = Parse::Query.new("GameScore").tap do |q|
493
+ q.exists("score") # defaults to `true`
494
+ end.get
495
+ ```
496
+
497
+ To retrieve documents without the score set, we could issue:
498
+
499
+ ```ruby
500
+ scores = Parse::Query.new("GameScore").tap do |q|
501
+ q.exists("score", false)
502
+ end.get
503
+ ```
504
+
505
+ If you have a class containing sports teams and you store a user's hometown in the user class, you can issue one query to find the list of users whose hometown teams have winning records. The query would look like:
506
+
507
+ ```ruby
508
+ users = Parse::Query.new("_User").tap do |users_query|
509
+ users_query.eq("hometown", {
510
+ "$select" => {
511
+ "query" => {
512
+ "className" => "Team",
513
+ "where" => {
514
+ "winPct" => {"$gt" => 0.5}
515
+ }
516
+ },
517
+ "key" => "city"
518
+ }
519
+ })
520
+ end.get
521
+ ```
522
+
523
+ Currently, there is no convenience method provided for `$select` queries. However, they are still possible. This is a good example of the flexibility of parse-ruby-client. You usually do not need to wait for a feature to be added in order to user it. If you have a good idea on what a convencience method for this should look like, please file an issue, or even better, submit a pull request.
524
+
525
+ You can use the `Parse::Query#order_by` method to specify a field to sort by. By default, everything is ordered ascending. Thus, to retrieve scores in ascending order:
526
+
527
+ ```ruby
528
+ scores = Parse::Query.new("GameScore").tap do |q|
529
+ q.order_by = "score"
530
+ end.get
531
+ ```
532
+
533
+ And to retrieve scores in descending order:
534
+
535
+ ```ruby
536
+ scores = Parse::Query.new("GameScore").tap do |q|
537
+ q.order_by = "score"
538
+ q.order = :descending
539
+ end.get
540
+ ```
541
+
542
+ You can sort by multiple fields by passing order a comma-separated list. Currently, there is no convenience method to accomplish this. However, you can still manually construct an `order` string. To retrieve documents that are ordered by scores in ascending order and the names in descending order:
543
+
544
+ ```ruby
545
+ scores = Parse::Query.new("GameScore").tap do |q|
546
+ q.order_by = "score,-name"
547
+ end.get
548
+ ```
549
+
550
+ You can use the `limit` and `skip` parameters for pagination. `limit` defaults to 100, but anything from 1 to 1000 is a valid limit. Thus, to retrieve 200 objects after skipping the first 400:
551
+
552
+ ```ruby
553
+ scores = Parse::Query.new("GameScore").tap do |q|
554
+ q.limit = 200
555
+ q.skip = 400
556
+ end.get
557
+ ```
558
+
559
+ All of these parameters can be used in combination with each other.
560
+
561
+ ### Queries on Array Values
562
+
563
+ For keys with an array type, you can find objects where the key's array value contains 2 by:
564
+
565
+ ```ruby
566
+ randos = Parse::Query.new("RandomObject").eq("arrayKey", 2).get
567
+ ```
568
+
569
+ ### Relational Queries
570
+
571
+ There are several ways to issue queries for relational data. For example, if each `Comment` has a `Post` object in its `post` field, you can fetch comments for a particular `Post`:
572
+
573
+ ```ruby
574
+ comments = Parse::Query.new("Comment").tap do |q|
575
+ q.eq("post", Parse::Pointer.new({
576
+ "className" => "Post",
577
+ "objectId" => "8TOXdXf3tz"
578
+ }))
579
+ end.get
580
+ ```
581
+
582
+ If you want to retrieve objects where a field contains an object that matches another query, you can use the `Parse::Query#in_query(field, query=nil)` method. Note that the default limit of 100 and maximum limit of 1000 apply to the inner query as well, so with large data sets you may need to construct queries carefully to get the desired behavior. For example, imagine you have `Post` class and a `Comment` class, where each `Comment` has a relation to its parent `Post`. You can find comments on posts with images by doing:
583
+
584
+ ```ruby
585
+ comments = Parse::Query.new("Comment").tap do |comments_query|
586
+ comments_query.in_query("post", Parse::Query.new("Post").tap do |posts_query|
587
+ posts_query.exists("image")
588
+ end)
589
+ end.get
590
+ ```
591
+
592
+ Note: You must pass an instance of `Parse::Query` as the second argument for `Parse::Query#query_in`. You cannot manually construct queries for this.
593
+
594
+ TODO: Implement this:
595
+ ```
596
+ If you want to retrieve objects where a field contains an object that does not match another query, you can use the $notInQuery operator. Imagine you have Post class and a Comment class, where each Comment has a relation to its parent Post. You can find comments on posts without images by doing:
597
+ ```
598
+
599
+ If you want to retrieve objects that are members of `Relation` field of a parent object, you can use the `Parse::Query#related_to(field, value)` method. Imagine you have a `Post `class and `User` class, where each `Post` can be liked by many users. If the `Users` that liked a Post was stored in a `Relation` on the post under the key likes, you, can the find the users that liked a particular post by:
600
+
601
+ ```ruby
602
+ users = Parse::Query.new("_User").tap do |q|
603
+ q.related_to("likes", Parse::Pointer.new({
604
+ "className" => "Post",
605
+ "objectId" => "8TOXdXf3tz"
606
+ }))
607
+ end.get
608
+ ```
609
+
610
+ In some situations, you want to return multiple types of related objects in one query. You can do this by passing the field to include in the `include` parameter. For example, let's say you are retrieving the last ten comments, and you want to retrieve their related posts at the same time:
611
+
612
+ ```ruby
613
+ comments = Parse::Query.new("Comment").tap do |q|
614
+ q.order_by = "createdAt"
615
+ q.order = :descending
616
+ q.limit = 10
617
+ q.include = "post"
618
+ end.get
619
+ ```
620
+
621
+ Instead of being represented as a `Pointer`, the `post` field is now expanded into the whole object. `__type` is set to `Object` and `className` is provided as well. For example, a `Pointer` to a `Post` could be represented as:
622
+
623
+ ```ruby
624
+ {
625
+ "__type" => "Pointer",
626
+ "className" => "Post",
627
+ "objectId" => "8TOXdXf3tz"
157
628
  }
629
+ ```
630
+
631
+ When the query is issued with an `include` parameter for the key holding this pointer, the pointer will be expanded to:
158
632
 
159
- # Retrieve all scores between 10 & 20 inclusive
160
- Parse::Query.new("GameScore") \
161
- .greater_eq("score", 10) \
162
- .less_eq("score", 20) \
163
- .get
633
+ ```ruby
634
+ {
635
+ "__type" => "Object",
636
+ "className" => "Post",
637
+ "objectId" => "8TOXdXf3tz",
638
+ "createdAt" => "2011-12-06T20:59:34.428Z",
639
+ "updatedAt" => "2011-12-06T20:59:34.428Z",
640
+ "otherFields" => "willAlsoBeIncluded"
641
+ }
642
+ ```
164
643
 
165
- # Retrieve a set of specific scores
166
- Parse::Query.new("GameScore") \
167
- .value_in("score", [10, 20, 30, 40]) \
168
- .get
644
+ You can also do multi level includes using dot notation. If you wanted to include the post for a comment and the post's author as well you can do:
169
645
 
646
+ ```ruby
647
+ comments = Parse::Query.new("Comment").tap do |q|
648
+ q.order_by = "createdAt"
649
+ q.order = :descending
650
+ q.limit = 10
651
+ q.include = "post.author"
652
+ end.get
170
653
  ```
171
654
 
172
- ### Relational Data
655
+ You can issue a query with multiple fields included by passing a comma-separated list of keys as the include parameter:
173
656
 
174
657
  ```ruby
175
- post = Parse::Object.new "Post"
176
- post.save
658
+ comments = Parse::Query.new("Comment").tap do |q|
659
+ q.include("post,author")
660
+ end.get
661
+ ```
177
662
 
178
- comment = Parse::Object.new "Comment"
179
- comment.save
663
+ ### Counting Objects
180
664
 
181
- post.array_add_relation("comments", comment.pointer)
182
- post.save
665
+ If you are limiting your query, or if there are a very large number of results, and you want to know how many total results there are without returning them all, you can use the `count` parameter. For example, if you only care about the number of games played by a particular player:
183
666
 
184
- q = Parse::Query.new("Comment")
185
- q.related_to("comments", post.pointer)
186
- comments = q.get
187
- assert_equal comments.first["objectId"], comment["objectId"]
667
+ ```ruby
668
+ count = Parse::Query.new("GameScore").tap do |q|
669
+ q.eq("playerName", "Jonathan Walsh")
670
+ q.limit = 0
671
+ q.count
672
+ end.get
188
673
  ```
189
674
 
190
- ## Push Notifications
675
+ With a nonzero limit, that request would return results as well as the count.
676
+
677
+ ### Compound Queries
678
+
679
+ If you want to find objects that match one of several queries, you can use `Parse::Quer#or` method, with an `Array` as its value. For instance, if you want to find players with either have a lot of wins or a few wins, you can do:
191
680
 
192
681
  ```ruby
193
- push = Parse::Push.new({"alert" => "I'm sending this push to all my app users!"})
194
- push.save
682
+
683
+ players = Parse::Query.new("Player").tap do |q|
684
+ q.greater_than("wins", 150)
685
+ q.or(Parse::Query.new("Player").tap do |or_query|
686
+ or_query.less_than("wins, 5")
687
+ end)
688
+ end.get
195
689
  ```
196
690
 
197
- ## Batch Requests
691
+ ## Users
692
+
693
+ Many apps have a unified login that works across the mobile app and other systems. Accessing user accounts through parse-ruby-client lets you build this functionality on top of Parse.
694
+
695
+ In general, users have the same features as other objects, such as the flexible schema. The differences are that user objects must have a username and password, the password is automatically encrypted and stored securely, and Parse enforces the uniqueness of the `username` and `email` fields.
696
+
697
+ ### Signing Up
698
+
699
+ Signing up a new user differs from creating a generic object in that the `username` and `password` fields are required. The password field is handled differently than the others; it is encrypted when stored in the Parse Cloud and never returned to any client request.
700
+
701
+ You can ask Parse to verify user email addresses in your application settings page. With this setting enabled, all new user registrations with an `email` field will generate an email confirmation at that address. You can check whether the user has verified their `email` with the `emailVerified` field.
198
702
 
199
- Automagic:
703
+ To sign up a new user, create a new `Parse::User` object and then call `#save` on it:
200
704
 
201
705
  ```ruby
202
- posts = Parse::Query.new("Post").get
203
- update_batch = Parse::Batch.new
204
- posts.each_slice(20) do |posts_slice|
205
- posts_slice.each do |post|
206
- post["title"] = "Updated title!"
207
- update_batch.update_object(post)
208
- end
209
- end
210
- update_batch.run!
706
+ user = Parse::User.new({
707
+ :username => "cooldude6",
708
+ :password => "p_n7!-e8",
709
+ :phone => "415-392-0202"
710
+ })
711
+ user.save
712
+ ```
713
+
714
+ The response body is a `Parse::User` object containing the `objectId`, the `createdAt` timestamp of the newly-created object, and the `sessionToken` which can be used to authenticate subsequent requests as this user:
715
+
716
+ ```ruby
717
+ {"username"=>"cooldude6",
718
+ "phone"=>"415-392-0202",
719
+ "createdAt"=>"2013-01-31T15:22:40.339Z",
720
+ "objectId"=>"2bMfWZQ9Ob",
721
+ "sessionToken"=>"zrGuvs3psdndaqswhf0smupsodflkqbFdwRs"}
211
722
  ```
212
723
 
213
- `Parse::Batch` also has `#create_object` and `delete_object` methods. Both take instances of `Parse::Object` as the only argument.
724
+ ### Logging In
214
725
 
215
- Manual:
726
+ After you allow users to sign up, you need to let them log in to their account with a username and password in the future. To do this, call `Parse::User#authenticate(username, password)`:
216
727
 
217
728
  ```ruby
218
- batch = Parse::Batch.new
219
- batch.add_request({
220
- "method" => "POST",
221
- "path" => "/1/classes/GameScore",
222
- "body" => {
223
- "score" => 1337,
224
- "playerName" => "Sean Plott"
729
+ user = Parse::User.authenticate("cooldude6", "p_n7!-e8")
730
+ ```
731
+
732
+ The response body is a `Parse::User` object containing all the user-provided fields except `password`. It also contains the `createdAt`, `updatedAt`, `objectId`, and `sessionToken` fields:
733
+
734
+ ```ruby
735
+ {"username"=>"cooldude6",
736
+ "phone"=>"415-392-0202",
737
+ "createdAt"=>"2013-01-31T15:22:40.339Z",
738
+ "updatedAt"=>"2013-01-31T15:22:40.339Z",
739
+ "objectId"=>"2bMfWZQ9Ob",
740
+ "sessionToken"=>"uvs3aspasdnlksdasqu178qaq0smupso"}
741
+ ```
742
+
743
+ ### Verifying Emails
744
+
745
+ Enabling email verification in an application's settings allows the application to reserve part of its experience for users with confirmed email addresses. Email verification adds the `emailVerified` field to the `User` object. When a `User`'s `email` is set or modified, `emailVerified` is set to false. Parse then emails the user a link which will set `emailVerified` to `true`.
746
+
747
+ There are three `emailVerified` states to consider:
748
+
749
+ 1. `true` - the user confirmed his or her email address by clicking on the link Parse emailed them. `Users` can never have a `true` value when the user account is first created.
750
+
751
+ 2. `false` - at the time the `User` object was last refreshed, the user had not confirmed his or her email address. If `emailVerified` is `false`, consider refreshing the `User` object.
752
+
753
+ 3. *missing* - the `User` was created when email verification was off or the `User` does not have an `email`.
754
+
755
+ ### Requesting A Password Reset
756
+
757
+ You can initiate password resets for users who have emails associated with their account. To do this, use `Parse::User::reset_password`:
758
+
759
+ ```ruby
760
+ resp = Parse::User.reset_password("coolguy@iloveapps.com")
761
+ puts resp #=> {}
762
+ ```
763
+
764
+ If successful, the response body is an empty `Hash` object.
765
+
766
+ ### Retrieving Users
767
+
768
+ You can also retrieve the contents of a user object by using `Parse::Query`. For example, to retrieve the user created above:
769
+
770
+ ```ruby
771
+ user = Parse::Query.new("_User").eq("objectId", "2bMfWZQ9Ob").get.first
772
+ ```
773
+
774
+ The response body is a `Parse::User` object containing all the user-provided fields except `password`. It also contains the `createdAt`, `updatedAt`, and `objectId` fields:
775
+
776
+ ```ruby
777
+ {"username"=>"cooldude6",
778
+ "phone"=>"415-392-0202",
779
+ "createdAt"=>"2013-01-31T15:22:40.339Z",
780
+ "updatedAt"=>"2013-01-31T15:22:40.339Z",
781
+ "objectId"=>"2bMfWZQ9Ob"}
782
+ ```
783
+
784
+ ### Updating Users
785
+
786
+ TODO: Implement this!
787
+
788
+ In normal usage, nobody except the user is allowed to modify their own data. To authenticate themselves, the user must add a `X-Parse-Session-Token` header to the request with the session token provided by the signup or login method.
789
+
790
+ To change the data on a user that already exists, send a PUT request to the user URL. Any keys you don't specify will remain unchanged, so you can update just a subset of the user's data. `username` and `password` may be changed, but the new username must not already be in use.
791
+
792
+ For example, if we wanted to change the phone number for cooldude6:
793
+
794
+ ```ruby
795
+ user = Parse::Query.new("_User").eq("objectId", "2bMfWZQ9Ob").get.first
796
+ user["phone"] = "415-369-6201"
797
+ user.save
798
+ ```
799
+
800
+ Currently returns the following error:
801
+
802
+ ```
803
+ Parse::ParseProtocolError: 206: Parse::UserCannotBeAlteredWithoutSessionError
804
+ ```
805
+
806
+ ### Querying
807
+
808
+ You can retrieve multiple users at once by using `Parse::Query`:
809
+
810
+ ```ruby
811
+ users = Parse::Query.new("_User").get
812
+ ```
813
+
814
+ The return value is an `Array` of `Parse::User` objects:
815
+
816
+ ```ruby
817
+ [{"username"=>"fake_person",
818
+ "createdAt"=>"2012-04-20T20:07:32.295Z",
819
+ "updatedAt"=>"2012-04-20T20:07:32.295Z",
820
+ "objectId"=>"AAVwfClOx9"},
821
+ {"username"=>"fake_person222",
822
+ "createdAt"=>"2012-04-20T20:07:32.946Z",
823
+ "updatedAt"=>"2012-04-20T20:07:32.946Z",
824
+ "objectId"=>"0W1Gj1CXqU"}]
825
+ ```
826
+
827
+ All of the options for queries that work for regular objects also work for user objects, so check the section on Querying Objects for more details.
828
+
829
+ ### Deleting Users
830
+
831
+ TODO: Implement this!
832
+
833
+ Proposed api:
834
+
835
+ To delete a user from the Parse Cloud, call `#parse_delete` on it:
836
+
837
+ ```ruby
838
+ user.parse_delete
839
+ ```
840
+
841
+ ### Linking Users
842
+
843
+ TODO: Implement this! See https://parse.com/docs/rest#users-linking
844
+
845
+ Parse allows you to link your users with services like Twitter and Facebook, enabling your users to sign up or log into your application using their existing identities. This is accomplished through the sign-up and update REST endpoints by providing authentication data for the service you wish to link to a user in the authData field. Once your user is associated with a service, the authData for the service will be stored with the user and is retrievable by logging in.
846
+
847
+ authData is a JSON object with keys for each linked service containing the data below. In each case, you are responsible for completing the authentication flow (e.g. OAuth 1.0a) to obtain the information the the service requires for linking.
848
+
849
+ Facebook authData contents:
850
+
851
+ ```ruby
852
+ {
853
+ "facebook" => {
854
+ "id" => "user's Facebook id number as a string",
855
+ "access_token" => "an authorized Facebook access token for the user",
856
+ "expiration_date" => "token expiration date of the format: yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
857
+ }
858
+ }
859
+ ```
860
+
861
+ Twitter authData contents:
862
+
863
+ ```ruby
864
+ {
865
+ "twitter" => {
866
+ "id" => "user's Twitter id number as a string",
867
+ "screen_name" => "user's Twitter screen name",
868
+ "consumer_key" => "your application's consumer key",
869
+ "consumer_secret" => "your application's consumer secret",
870
+ "auth_token" => "an authorized Twitter token for the user with your application",
871
+ "auth_token_secret" => "the secret associated with the auth_token"
872
+ }
873
+ }
874
+ ```
875
+
876
+ Anonymous user authData contents:
877
+
878
+ ```ruby
879
+ {
880
+ "anonymous" => {
881
+ "id" => "random UUID with lowercase hexadecimal digits"
225
882
  }
883
+ }
884
+ ```
885
+
886
+ #### Signing Up and Logging In
887
+
888
+ Todo: Implement this!
889
+
890
+ Signing a user up with a linked service and logging them in with that service uses the same POST request, in which the authData for the user is specified. For example, to sign up or log in with a user's Twitter account:
891
+
892
+ ```ruby
893
+ # should look something like this:
894
+ twitter_user = Parse::User::Twitter.new({
895
+ "id" => "12345678",
896
+ "screen_name" => "ParseIt",
897
+ "consumer_key" => "SaMpLeId3X7eLjjLgWEw",
898
+ "consumer_secret" => "SaMpLew55QbMR0vTdtOACfPXa5UdO2THX1JrxZ9s3c",
899
+ "auth_token" => "12345678-SaMpLeTuo3m2avZxh5cjJmIrAfx4ZYyamdofM7IjU",
900
+ "auth_token_secret" => "SaMpLeEb13SpRzQ4DAIzutEkCE2LBIm2ZQDsP3WUU"
901
+ })
902
+ twitter_user.save
903
+ ```
904
+
905
+ Parse then verifies that the provided authData is valid and checks to see if a user is already associated with this data. If so, it returns a status code of 200 OK and the details (including a sessionToken for the user).
906
+
907
+ With a response body like:
908
+
909
+ ```ruby
910
+ {
911
+ "username" => "Parse",
912
+ "createdAt" => "2012-02-28T23:49:36.353Z",
913
+ "updatedAt" => "2012-02-28T23:49:36.353Z",
914
+ "objectId" => "uMz0YZeAqc",
915
+ "sessionToken" => "samplei3l83eerhnln0ecxgy5",
916
+ "authData" => {
917
+ "twitter" => {
918
+ "id" => "12345678",
919
+ "screen_name" => "ParseIt",
920
+ "consumer_key" => "SaMpLeId3X7eLjjLgWEw",
921
+ "consumer_secret" => "SaMpLew55QbMR0vTdtOACfPXa5UdO2THX1JrxZ9s3c",
922
+ "auth_token" => "12345678-SaMpLeTuo3m2avZxh5cjJmIrAfx4ZYyamdofM7IjU",
923
+ "auth_token_secret" => "SaMpLeEb13SpRzQ4DAIzutEkCE2LBIm2ZQDsP3WUU"
924
+ }
925
+ }
926
+ }
927
+ ```
928
+
929
+ If the user has never been linked with this account, you will instead receive a status code of 201 Created, indicating that a new user was created.
930
+
931
+ The body of the response will contain the objectId, createdAt, sessionToken, and an automatically-generated unique username. For example:
932
+
933
+ ```ruby
934
+ {
935
+ "username" => "iwz8sna7sug28v4eyu7t89fij",
936
+ "createdAt" => "2012-02-28T23:49:36.353Z",
937
+ "objectId" => "uMz0YZeAqc",
938
+ "sessionToken" => "samplei3l83eerhnln0ecxgy5"
939
+ }
940
+ ```
941
+
942
+ #### Linking
943
+
944
+ TODO: Implement this!
945
+
946
+ Linking an existing user with a service like Facebook or Twitter uses a PUT request to associate authData with the user. For example, linking a user with a Facebook account would use a request like this:
947
+
948
+ ```ruby
949
+ # should look something like this:
950
+
951
+ user = Parse::Query.new("_User").eq("objectId", "2bMfWZQ9Ob").get.first
952
+ user.link_to_facebook!({
953
+ "id" => "123456789",
954
+ "access_token" => "SaMpLeAAibS7Q55FSzcERWIEmzn6rosftAr7pmDME10008bWgyZAmv7mziwfacNOhWkgxDaBf8a2a2FCc9Hbk9wAsqLYZBLR995wxBvSGNoTrEaL",
955
+ "expiration_date" => "2012-02-28T23:49:36.353Z"
956
+ })
957
+
958
+ # or
959
+
960
+ user.link_to_twitter!({...})
961
+ ```
962
+
963
+ After linking your user to a service, you can authenticate them using matching authData.
964
+
965
+
966
+ #### Unlinking
967
+
968
+ TODO: Implement this!
969
+
970
+ Unlinking an existing user with a service also uses a PUT request to clear authData from the user by setting the authData for the service to null. For example, unlinking a user with a Facebook account would use a request like this:
971
+
972
+ ```ruby
973
+ # should look something like this:
974
+
975
+ user = Parse::Query.new("_User").eq("objectId", "2bMfWZQ9Ob").get.first
976
+ user.unlink_from_facebook!
977
+ ```
978
+
979
+ ### Security
980
+
981
+ TODO: Implement this!
982
+
983
+ When you access Parse via the REST API key, access can be restricted by ACL just like in the iOS and Android SDKs. You can still read and modify acls via the REST API, just be accessing the "ACL" key of an object.
984
+
985
+ The ACL is formatted as a JSON object where the keys are either object ids or the special key "*" to indicate public access permissions. The values of the ACL are "permission objects", JSON objects whose keys are the permission name and the value is always true.
986
+
987
+ For example, if you want the user with id "3KmCvT7Zsb" to have read and write access to an object, plus the object should be publicly readable, that corresponds to an ACL of:
988
+
989
+ ```json
990
+ {
991
+ "3KmCvT7Zsb": {
992
+ "read": true,
993
+ "write": true
994
+ },
995
+ "*": {
996
+ "read": true
997
+ }
998
+ }
999
+ ```
1000
+
1001
+ If you want to access your data ignoring all ACLs, you can use the master key provided on the Dashboard. Instead of the X-Parse-REST-API-Key header, set the X-Parse-Master-Key header. For backward compatibility, you can also do master-level authentication using HTTP Basic Auth, passing the application id as the username and the master key as the password. For security, the master key should not be distributed to end users, but if you are running code in a trusted environment, feel free to use the master key for authentication.
1002
+
1003
+ ## Roles
1004
+
1005
+ TODO: Implement this!
1006
+
1007
+ See https://parse.com/docs/rest#roles
1008
+
1009
+ ## Files
1010
+
1011
+ ### Uploading Files
1012
+
1013
+ To upload a file to Parse, use `Parse::File`. You must include the `"Content-Type"` parameter when instantiating. Keep in mind that files are limited to 10 megabytes. Here's a simple example that'll create a file named `hello.txt` containing a string:
1014
+
1015
+ ```ruby
1016
+ file = Parse::File.new({
1017
+ :body => "Hello World!",
1018
+ :local_filename => "hello.txt",
1019
+ :content_type => "text/plain"
226
1020
  })
227
- resp = batch.run!
1021
+ file.save
1022
+ ```
1023
+
1024
+ The response body is a `Hash` object containing the name of the file, which is the original file name prefixed with a unique identifier in order to prevent name collisions. This means, you can save files by the same name, and the files will not overwrite one another.
1025
+
1026
+ ```ruby
1027
+ {"url"=>
1028
+ "http://files.parse.com/372fcbb9-7eae-4b9a-abc8-6da97fcac50d/98f06e15-d6e6-42a9-a9cd-7d28ec98052c-hello.txt",
1029
+ "name"=>"98f06e15-d6e6-42a9-a9cd-7d28ec98052c-hello.txt"}
228
1030
  ```
229
1031
 
230
- ## Cloud Code
1032
+ To upload an image, the syntax is a little bit different. Here's an example that will upload the image parsers.jpg from the current directory:
231
1033
 
232
1034
  ```ruby
233
- # assumes you have a function named "trivial"
234
- function = Parse::Cloud::Function.new("trivial")
235
- params = {"foo" => "bar"}
236
- function.call(params)
1035
+ photo = Parse::File.new({
1036
+ :body => IO.read("test/parsers.jpg"),
1037
+ :local_filename => "parsers.jpg",
1038
+ :content_type => "image/jpeg"
1039
+ })
1040
+ photo.save
237
1041
  ```
238
1042
 
239
- # TODO
1043
+ ### Associating with Objects
1044
+
1045
+ After files are uploaded, you can associate them with Parse objects:
1046
+
1047
+ ```ruby
1048
+ photo = Parse::File.new({
1049
+ :body => IO.read("test/parsers.jpg"),
1050
+ :local_filename => "parsers.jpg",
1051
+ :content_type => "image/jpeg"
1052
+ })
1053
+ photo.save
1054
+ player_profile = Parse::Object.new("PlayerProfile").tap do |p|
1055
+ p["name"] = "All the Parsers"
1056
+ p["picture"] = photo
1057
+ end.save
1058
+ ```
1059
+
1060
+ ### Deleting Files
1061
+
1062
+ TODO: Implement this!
1063
+
1064
+ ## Push Notifications
1065
+
1066
+ Parse allows you send Push Notifications to iOS and Android devices.
1067
+
1068
+ To send a notification to the "user_1" channel
1069
+
1070
+ ```ruby
1071
+ data = { :alert => "This is a notification from Parse" }
1072
+ push = Parse::Push.new(data, "user_1")
1073
+ push.type = "ios"
1074
+ push.save
1075
+ ```
1076
+
1077
+ Notifications by default are set for iOS and Android. You can set certain notifications to only be sent to iOS or Android by setting the `type` to `ios` or `android`.
1078
+
1079
+ For config/installation: https://parse.com/docs/rest#push and https://parse.com/docs/push_guide#top/REST
1080
+
1081
+ ## Installations
1082
+
1083
+ TODO: Implement this!
1084
+
1085
+ ## GeoPoints
1086
+
1087
+ Parse allows you to associate real-world latitude and longitude coordinates with an object. Adding a GeoPoint data type to a class allows queries to take into account the proximity of an object to a reference point. This allows you to easily do things like find out what user is closest to another user or which places are closest to a user.
1088
+
1089
+ ### GeoPoint
1090
+
1091
+ To associate a point with an object you will need to embed a GeoPoint data type into your object. This is done by using a JSON object with __type set to the string GeoPoint and numeric values being set for the latitude and longitude keys. For example, to create an object containing a point under the "location" key with a latitude of 40.0 degrees and -30.0 degrees longitude:
1092
+
1093
+ ```ruby
1094
+ place = Parse::Object.new("PlaceObject").tap do |p|
1095
+ p["location"] = Parse::GeoPoint.new({
1096
+ "latitude" => 40.0,
1097
+ "longitude" => -30.0
1098
+ })
1099
+ end.save
1100
+ ```
1101
+
1102
+ ### GeoQueries
1103
+
1104
+ TODO: Implement this!
1105
+
1106
+ Now that you have a bunch of objects with spatial coordinates, it would be nice to find out which objects are closest to a point. This can be done by using a GeoPoint data type with query on the field using $nearSphere. Getting a list of ten places that are closest to a user may look something like:
1107
+
1108
+ ```ruby
1109
+ # should look something like this:
1110
+ places = Parse::Query.new("PlaceObject").tap do |q|
1111
+ q.near("location", {
1112
+ "latitude" => 30.0,
1113
+ "longitude" => -20.0
1114
+ })
1115
+ end.get
1116
+ ```
1117
+
1118
+ See https://parse.com/docs/rest#geo-query for the rest of the geo query types to implement.
1119
+
1120
+ ### Caveats
1121
+
1122
+ At the moment there are a couple of things to watch out for:
1123
+
1124
+ 1. Each PFObject class may only have one key with a PFGeoPoint object.
240
1125
 
241
- - Add some form of debug logging
242
- - ~~Support for Date, Pointer, and Bytes API [data types](https://www.parse.com/docs/rest#objects-types)~~
243
- - ACLs
244
- - Login
1126
+ 2. Points should not equal or exceed the extreme ends of the ranges. Latitude should not be -90.0 or 90.0. Longitude should not be -180.0 or 180.0. Attempting to use GeoPoint's with latitude and/or longitude outside these ranges will cause an error.
245
1127
 
1128
+ # Contributors
246
1129
 
247
- # Resources
1130
+ This project would not be where it is today without the generous help provided by its many contributors:
248
1131
 
249
- - parse.com [REST API documentation](https://parse.com/docs/rest)
250
- - [parse_resource](https://github.com/adelevie/parse_resource) , an ActiveRecord-compatible wrapper
251
- for the API for seamless integration into Rails.
1132
+ 1. Adam Alpern (created this project) (https://github.com/aalpern) (https://www.gittip.com/on/github/aalpern/)
1133
+ 2. Eric Jensen (https://github.com/ericcj) (https://www.gittip.com/on/github/ericcj/)
1134
+ 3. Ben Cherry (https://github.com/bcherry) (https://www.gittip.com/bcherry/)
1135
+ 4. Tikhon Bernstam (https://github.com/tikhon) (https://www.gittip.com/on/github/tikhon/)
1136
+ 5. Tony Amoyal (https://github.com/tamoyal) (https://www.gittip.com/on/github/tamoyal/)
1137
+ 6. Glenn Goodrich (https://github.com/ruprict) (https://www.gittip.com/on/github/ruprict/)
1138
+ 7. Jeremy Schoenherr (https://github.com/jeremyschoenherr) (https://www.gittip.com/on/github/jeremyschoenherr/)
1139
+ 8. Dean Perry (https://github.com/deanperry) (https://www.gittip.com/deanperry/)
1140
+ 9. [vircheck](https://github.com/vircheck) (https://www.gittip.com/on/github/vircheck/)
1141
+ 10. Jacob Eiting (https://github.com/jeiting) (https://www.gittip.com/on/github/jeiting/)
1142
+ 11. Guy Maliar (https://github.com/gmaliar) (https://www.gittip.com/on/github/gmaliar/)
1143
+ 12. Ivan Fuller (https://github.com/ifuller) (https://www.gittip.com/on/github/ifuller/)
1144
+ 13. Leandro S. (https://github.com/lsiqueira) (https://www.gittip.com/on/github/lsiqueira/)
1145
+ 14. Brian Hammond (https://github.com/fictorial) (https://www.gittip.com/on/github/fictorial/)