parse-stack 1.4.3 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changes.md +52 -39
- data/Gemfile.lock +2 -2
- data/README.md +609 -124
- data/bin/console +0 -9
- data/lib/parse/api/all.rb +3 -0
- data/lib/parse/api/analytics.rb +2 -2
- data/lib/parse/api/apps.rb +15 -17
- data/lib/parse/api/batch.rb +4 -1
- data/lib/parse/api/cloud_functions.rb +2 -0
- data/lib/parse/api/config.rb +14 -2
- data/lib/parse/api/files.rb +6 -3
- data/lib/parse/api/hooks.rb +4 -4
- data/lib/parse/api/objects.rb +14 -11
- data/lib/parse/api/push.rb +4 -2
- data/lib/parse/api/schemas.rb +6 -5
- data/lib/parse/api/sessions.rb +11 -1
- data/lib/parse/api/users.rb +65 -15
- data/lib/parse/client/authentication.rb +4 -2
- data/lib/parse/client/body_builder.rb +11 -3
- data/lib/parse/client/caching.rb +17 -6
- data/lib/parse/client/protocol.rb +14 -8
- data/lib/parse/client/request.rb +4 -1
- data/lib/parse/client/response.rb +59 -6
- data/lib/parse/client.rb +72 -42
- data/lib/parse/model/acl.rb +22 -4
- data/lib/parse/model/associations/belongs_to.rb +22 -10
- data/lib/parse/model/associations/collection_proxy.rb +14 -1
- data/lib/parse/model/associations/has_many.rb +76 -15
- data/lib/parse/model/associations/has_one.rb +69 -0
- data/lib/parse/model/associations/pointer_collection_proxy.rb +13 -6
- data/lib/parse/model/associations/relation_collection_proxy.rb +5 -2
- data/lib/parse/model/bytes.rb +6 -2
- data/lib/parse/model/classes/installation.rb +27 -0
- data/lib/parse/model/classes/role.rb +20 -0
- data/lib/parse/model/classes/session.rb +26 -0
- data/lib/parse/model/classes/user.rb +185 -0
- data/lib/parse/model/core/actions.rb +40 -26
- data/lib/parse/model/core/properties.rb +126 -20
- data/lib/parse/model/core/querying.rb +63 -3
- data/lib/parse/model/core/schema.rb +9 -6
- data/lib/parse/model/date.rb +5 -1
- data/lib/parse/model/file.rb +12 -9
- data/lib/parse/model/geopoint.rb +6 -4
- data/lib/parse/model/model.rb +29 -21
- data/lib/parse/model/object.rb +29 -76
- data/lib/parse/model/pointer.rb +8 -6
- data/lib/parse/model/push.rb +4 -1
- data/lib/parse/query/constraint.rb +3 -0
- data/lib/parse/query/constraints.rb +6 -3
- data/lib/parse/query/operation.rb +3 -0
- data/lib/parse/query/ordering.rb +3 -0
- data/lib/parse/query.rb +85 -38
- data/lib/parse/stack/generators/rails.rb +3 -0
- data/lib/parse/stack/railtie.rb +2 -0
- data/lib/parse/stack/tasks.rb +4 -1
- data/lib/parse/stack/version.rb +4 -1
- data/lib/parse/stack.rb +3 -0
- data/lib/parse/webhooks/payload.rb +14 -8
- data/lib/parse/webhooks/registration.rb +11 -8
- data/lib/parse/webhooks.rb +11 -8
- data/lib/parse-stack.rb +3 -0
- data/parse-stack.gemspec +10 -8
- metadata +16 -4
data/README.md
CHANGED
@@ -19,20 +19,15 @@ Or install it yourself as:
|
|
19
19
|
|
20
20
|
$ gem install parse-stack
|
21
21
|
|
22
|
+
### Rack / Sinatra
|
23
|
+
Parse-Stack API, models and webhooks easily integrate in your existing Rack/Sinatra based applications. For more details see [Parse-Stack Rack Example](https://github.com/modernistik/parse-stack-example).
|
24
|
+
|
22
25
|
### Rails
|
23
26
|
Parse-Stack comes with support for Rails by adding additional rake tasks and generators. After adding `parse-stack` as a gem dependency in your Gemfile and running `bundle`, you should run the install script:
|
24
27
|
|
25
28
|
$ rails g parse_stack:install
|
26
29
|
|
27
|
-
|
28
|
-
|
29
|
-
$ rails g parse_stack:model Song name:string released:date genres:array
|
30
|
-
|
31
|
-
This would create a `song.rb` file in `app/models` with the provided properties. Once you are ready to update your schema, you can run the `parse:upgrade` task to upgrade the remote Parse-Server schema to match your new models.
|
32
|
-
|
33
|
-
$ rails parse:upgrade
|
34
|
-
|
35
|
-
That should create the new collection `Song` in your Parse-Server backend. For a more full featured example, see [Parse-Server-Rails-Example](https://github.com/modernistik/parse-server-rails-example).
|
30
|
+
For a more details on the rails integration see [Parse-Stack Rails Example](https://github.com/modernistik/parse-stack-rails-example).
|
36
31
|
|
37
32
|
## Table Of Contents
|
38
33
|
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
@@ -40,7 +35,6 @@ That should create the new collection `Song` in your Parse-Server backend. For a
|
|
40
35
|
|
41
36
|
|
42
37
|
- [Overview](#overview)
|
43
|
-
- [Main Features](#main-features)
|
44
38
|
- [Architecture](#architecture)
|
45
39
|
- [Parse::Client](#parseclient)
|
46
40
|
- [Parse::Query](#parsequery)
|
@@ -57,17 +51,40 @@ That should create the new collection `Song` in your Parse-Server backend. For a
|
|
57
51
|
- [Parse::GeoPoint](#parsegeopoint)
|
58
52
|
- [Calculating Distances between locations](#calculating-distances-between-locations)
|
59
53
|
- [Parse::Bytes](#parsebytes)
|
60
|
-
- [Parse::
|
61
|
-
- [Parse::Installation](#parseinstallation)
|
54
|
+
- [Parse::ACL](#parseacl)
|
62
55
|
- [Parse::Session](#parsesession)
|
56
|
+
- [Parse::Installation](#parseinstallation)
|
63
57
|
- [Parse::Role](#parserole)
|
58
|
+
- [Parse::User](#parseuser)
|
59
|
+
- [Signup](#signup)
|
60
|
+
- [Third-Party Services](#third-party-services)
|
61
|
+
- [Login and Sessions](#login-and-sessions)
|
62
|
+
- [Linking and Unlinking Users](#linking-and-unlinking-users)
|
63
|
+
- [Request Password Reset](#request-password-reset)
|
64
64
|
- [Modeling and Subclassing](#modeling-and-subclassing)
|
65
65
|
- [Defining Properties](#defining-properties)
|
66
66
|
- [Accessor Aliasing](#accessor-aliasing)
|
67
67
|
- [Property Options](#property-options)
|
68
|
+
- [`:required`](#required)
|
69
|
+
- [`:field`](#field)
|
70
|
+
- [`:default`](#default)
|
71
|
+
- [`:alias`](#alias)
|
72
|
+
- [`:symbolize`](#symbolize)
|
73
|
+
- [`:enum`](#enum)
|
74
|
+
- [`:scope`](#scope)
|
68
75
|
- [Associations](#associations)
|
69
76
|
- [Belongs To](#belongs-to)
|
70
|
-
|
77
|
+
- [Options](#options)
|
78
|
+
- [`:required`](#required-1)
|
79
|
+
- [`:as`](#as)
|
80
|
+
- [`:field`](#field-1)
|
81
|
+
- [Has One](#has-one)
|
82
|
+
- [Has Many](#has-many)
|
83
|
+
- [Query](#query)
|
84
|
+
- [Array](#array)
|
85
|
+
- [Parse Relation](#parse-relation)
|
86
|
+
- [Options](#options-1)
|
87
|
+
- [`:through`](#through)
|
71
88
|
- [Creating, Saving and Deleting Records](#creating-saving-and-deleting-records)
|
72
89
|
- [Create](#create)
|
73
90
|
- [Saving](#saving)
|
@@ -114,9 +131,10 @@ That should create the new collection `Song` in your Parse-Server backend. For a
|
|
114
131
|
- [Bounding Box Constraint](#bounding-box-constraint)
|
115
132
|
- [Relational Queries](#relational-queries)
|
116
133
|
- [Compound Queries](#compound-queries)
|
134
|
+
- [Query Scopes](#query-scopes)
|
117
135
|
- [Calling Cloud Code Functions](#calling-cloud-code-functions)
|
118
136
|
- [Calling Background Jobs](#calling-background-jobs)
|
119
|
-
- [Model Callbacks](#model-callbacks)
|
137
|
+
- [Active Model Callbacks](#active-model-callbacks)
|
120
138
|
- [Schema Upgrades and Migrations](#schema-upgrades-and-migrations)
|
121
139
|
- [Push Notifications](#push-notifications)
|
122
140
|
- [Cloud Code Webhooks](#cloud-code-webhooks)
|
@@ -140,9 +158,13 @@ require 'parse/stack'
|
|
140
158
|
|
141
159
|
Parse.setup app_id: APP_ID,
|
142
160
|
api_key: REST_API_KEY,
|
161
|
+
master_key: YOUR_MASTER_KEY,
|
143
162
|
server_url: 'https://api.parse.com/1/'
|
144
163
|
|
145
|
-
#
|
164
|
+
# login
|
165
|
+
user = Parse::User.login(username, passwd)
|
166
|
+
|
167
|
+
# Custom Subclasses
|
146
168
|
class Song < Parse::Object
|
147
169
|
property :name
|
148
170
|
property :play, :integer
|
@@ -157,12 +179,17 @@ end
|
|
157
179
|
class Artist < Parse::Object
|
158
180
|
property :name
|
159
181
|
property :genres, :array
|
182
|
+
has_many :fans, as: :user
|
183
|
+
has_one :manager, as: :user
|
184
|
+
|
185
|
+
scope :recent, ->(x) { query(:created_at.after => x) }
|
160
186
|
end
|
161
187
|
|
162
188
|
# updates schemas for your Parse app based on your models (non-destructive)
|
163
189
|
Parse.auto_upgrade!
|
164
190
|
|
165
191
|
artist = Artist.new(name: "Frank Sinatra", genres: ["swing", "jazz"])
|
192
|
+
artist.fans << user
|
166
193
|
artist.save
|
167
194
|
|
168
195
|
# Query
|
@@ -192,21 +219,6 @@ result = Parse.call_function :myFunctionName, {param: value}
|
|
192
219
|
|
193
220
|
```
|
194
221
|
|
195
|
-
## Main Features
|
196
|
-
While there are many additional features of the framework, these are the main points.
|
197
|
-
|
198
|
-
- Support for all Parse Query constraints.
|
199
|
-
- Object Relational Mapping with dirty tracking.
|
200
|
-
- Easy management of Parse GeoPoints, Files and ACLs.
|
201
|
-
- Parse Queries support with caching middleware. (Reduces API usage)
|
202
|
-
- Support for all Parse data types.
|
203
|
-
- One-to-One, One-to-Many and Many-to-Many relations.
|
204
|
-
- Integration with Parse Cloud Code Webhooks.
|
205
|
-
- Send Push notifications with advanced targeting.
|
206
|
-
- Schema upgrades and migrations.
|
207
|
-
- Rake tasks for webhook registrations.
|
208
|
-
- Some special magic with :save_all and :max limit fetch.
|
209
|
-
|
210
222
|
## Architecture
|
211
223
|
The architecture of `Parse::Stack` is broken into four main components.
|
212
224
|
|
@@ -220,7 +232,7 @@ This class implements the [Parse REST Querying](https://parse.com/docs/rest/guid
|
|
220
232
|
This component is main class for all object relational mapping subclasses for your application. It provides features in order to map your remote Parse records to a local ruby object. It implements the Active::Model interface to provide a lot of additional features, CRUD operations, querying, including dirty tracking, JSON serialization, save/destroy callbacks and others. While we are overlooking some functionality, for simplicity, you will mainly be working with Parse::Object as your superclass. While not required, it is highly recommended that you define a model (Parse::Object subclass) for all the Parse classes in your application.
|
221
233
|
|
222
234
|
### Parse::Webhooks
|
223
|
-
Parse provides a feature called [Cloud Code Webhooks](http://blog.parse.com/announcements/introducing-cloud-code-webhooks/). For most applications, save/delete triggers and cloud functions tend to be implemented by Parse's own hosted Javascript solution called Cloud Code. However, Parse provides the ability to have these hooks utilize your hosted solution instead of their own, since their environment is limited in terms of resources and tools.
|
235
|
+
Parse provides a feature called [Cloud Code Webhooks](http://blog.parse.com/announcements/introducing-cloud-code-webhooks/). For most applications, save/delete triggers and cloud functions tend to be implemented by Parse's own hosted Javascript solution called Cloud Code. However, Parse provides the ability to have these hooks utilize your hosted solution instead of their own, since their environment is limited in terms of resources and tools.
|
224
236
|
|
225
237
|
## Field Naming Conventions
|
226
238
|
By convention in Ruby (see [Style Guide](https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars)), symbols and variables are expressed in lower_snake_case form. Parse, however, prefers column names in **lower-first camel case** (ex. `objectId`, `createdAt` and `updatedAt`). To keep in line with the style guides between the languages, we do the automatic conversion of the field names when compiling the query. As an additional exception to this rule, the field key of `id` will automatically be converted to the `objectId` field when used. If you do not want this to happen, you can turn off or change the value `Parse::Query.field_formatter` as shown below. Though we recommend leaving the default `:columnize` if possible.
|
@@ -266,8 +278,8 @@ If you wish to add additional connection middleware to the stack, you may do so
|
|
266
278
|
Calling `setup` will create the default `Parse::Client` session object that will be used for all models and requests in the stack. You may retrive this client by calling the class `session()` method. It is possible to create different client connections and have different models point to different Parse applications and endpoints at the same time.
|
267
279
|
|
268
280
|
```ruby
|
269
|
-
default_client = Parse::Client.
|
270
|
-
|
281
|
+
default_client = Parse::Client.client(:default)
|
282
|
+
# alias Parse::Client.client
|
271
283
|
```
|
272
284
|
|
273
285
|
### Connection Options
|
@@ -301,7 +313,7 @@ Sets the default cache expiration time (in seconds) for successful non-empty `GE
|
|
301
313
|
You may pass a hash of options that will be passed to the `Faraday` constructor.
|
302
314
|
|
303
315
|
## Parse Config
|
304
|
-
Getting your configuration variables once you have a default client setup can be done with `Parse.config`. The first time this method is called, Parse-Stack will get the configuration from Parse-Server, and cache it. To force a reload of the config, use `config!`.
|
316
|
+
Getting your configuration variables once you have a default client setup can be done with `Parse.config`. The first time this method is called, Parse-Stack will get the configuration from Parse-Server, and cache it. To force a reload of the config, use `config!`. You
|
305
317
|
|
306
318
|
```ruby
|
307
319
|
Parse.setup( ... )
|
@@ -309,6 +321,12 @@ Getting your configuration variables once you have a default client setup can be
|
|
309
321
|
val = Parse.config["myKey"]
|
310
322
|
val = Parse.config["myKey"] # cached
|
311
323
|
|
324
|
+
# update a config with Parse
|
325
|
+
Parse.set_config "myKey", "someValue"
|
326
|
+
|
327
|
+
# batch update several
|
328
|
+
Parse.update_config({fieldEnabled: true, searchMiles: 50})
|
329
|
+
|
312
330
|
# Force fetch of config!
|
313
331
|
val = Parse.config!["myKey"]
|
314
332
|
|
@@ -327,12 +345,16 @@ end
|
|
327
345
|
|
328
346
|
class Commentary < Parse::Object
|
329
347
|
parse_class "Comment"
|
330
|
-
belongs_to :post
|
348
|
+
belongs_to :post
|
331
349
|
#'Author' class not defined locally
|
332
350
|
belongs_to :author
|
333
351
|
end
|
334
352
|
|
335
353
|
comment = Commentary.first
|
354
|
+
comment.post? # true because it is non-nil
|
355
|
+
comment.artist? # true because it is non-nil
|
356
|
+
|
357
|
+
# both are true because they are in a Pointer state
|
336
358
|
comment.post.pointer? # true
|
337
359
|
comment.author.pointer? # true
|
338
360
|
|
@@ -341,6 +363,10 @@ comment.post # <Post @parse_class="Post", @id="xdqcCqfngz">
|
|
341
363
|
|
342
364
|
# we have not defined an Author class handler
|
343
365
|
comment.author # <Parse::Pointer @parse_class="Author", @id="hZLbW6ofKC">
|
366
|
+
|
367
|
+
|
368
|
+
comment.post.fetch # fetch the relation
|
369
|
+
comment.post.pointer? # false, it is now a full object.
|
344
370
|
```
|
345
371
|
|
346
372
|
The effect is that for any unknown classes that the framework encounters, it will generate Parse::Pointer instances until you define those classes with valid properties and associations. While this might be ok for some classes you do not use, we still recommend defining all your Parse classes locally in the framework.
|
@@ -368,7 +394,7 @@ This class represents a Parse file pointer. `Parse::File` has helper methods to
|
|
368
394
|
|
369
395
|
```
|
370
396
|
|
371
|
-
The default MIME type for all files is `
|
397
|
+
The default MIME type for all files is `image/jpeg`. This can be default can be changed by setting a value to `Parse::File.default_mime_type`. Other ways of creating a `Parse::File` are provided below. The created Parse::File is not uploaded until you call `save`.
|
372
398
|
|
373
399
|
```ruby
|
374
400
|
# urls
|
@@ -418,15 +444,15 @@ This class manages the GeoPoint data type that Parse provides to support geo-que
|
|
418
444
|
We include helper methods to calculate distances between GeoPoints: `distance_in_miles` and `distance_in_km`.
|
419
445
|
|
420
446
|
```ruby
|
421
|
-
san_diego = Parse::GeoPoint.new(32.8233, -117.6542)
|
422
|
-
los_angeles = Parse::GeoPoint.new [34.0192341, -118.970792]
|
447
|
+
san_diego = Parse::GeoPoint.new(32.8233, -117.6542)
|
448
|
+
los_angeles = Parse::GeoPoint.new [34.0192341, -118.970792]
|
423
449
|
|
424
|
-
# Haversine calculations
|
425
|
-
san_diego.distance_in_miles(los_angeles)
|
426
|
-
# ~112.33 miles
|
450
|
+
# Haversine calculations
|
451
|
+
san_diego.distance_in_miles(los_angeles)
|
452
|
+
# ~112.33 miles
|
427
453
|
|
428
|
-
san_diego.distance_in_km(los_angeles)
|
429
|
-
# ~180.793 km
|
454
|
+
san_diego.distance_in_km(los_angeles)
|
455
|
+
# ~180.793 km
|
430
456
|
```
|
431
457
|
|
432
458
|
### Parse::Bytes
|
@@ -441,18 +467,72 @@ The `Bytes` data type represents the storage format for binary content in a Pars
|
|
441
467
|
decoded = bytes.decoded # same as Base64.decode64
|
442
468
|
```
|
443
469
|
|
444
|
-
### Parse::
|
445
|
-
|
470
|
+
### Parse::ACL
|
471
|
+
The `ACL` class represents the access control lists for each record. An ACL is represented by a JSON object with the keys being `Parse::User` object ids or the special key of `*`, which indicates the public access permissions.
|
472
|
+
The value of each key in the hash is a `Parse::ACL::Permission` object which defines the boolean permission state for `read` and `write`.
|
473
|
+
|
474
|
+
The example below illustrates a Parse ACL JSON object where there is a public read permission, but public write is prevented. In addition, the user with id `3KmCvT7Zsb`, is allowed to both read and write this record.
|
475
|
+
|
476
|
+
```json
|
477
|
+
{
|
478
|
+
"*": { "read": true },
|
479
|
+
"3KmCvT7Zsb": { "read": true, "write": true }
|
480
|
+
}
|
481
|
+
```
|
482
|
+
|
483
|
+
All `Parse::Object` subclasses have an `acl` property by default. With this property, you can apply and delete permissions for this particular Parse object record.
|
446
484
|
|
447
485
|
```ruby
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
486
|
+
user = Parse::User.first
|
487
|
+
artist = Artist.first
|
488
|
+
|
489
|
+
artist.acl # "*": { "read": true, "write": true }
|
490
|
+
|
491
|
+
# apply public read, but no public write
|
492
|
+
artist.acl.everyone true, false
|
493
|
+
|
494
|
+
|
495
|
+
# allow user to have read and write access
|
496
|
+
artist.acl.apply user.id, true, true
|
497
|
+
|
498
|
+
# remove all permissions for this user id
|
499
|
+
artist.acl.delete user.id
|
500
|
+
|
501
|
+
# allow the 'Admins' role read and write
|
502
|
+
artist.acl.apply_role "Admins", true, true
|
503
|
+
|
504
|
+
artist.save
|
505
|
+
```
|
506
|
+
|
507
|
+
For more information about Parse record ACLs, see the documentation at [Security](https://parseplatform.github.io/docs/rest/guide/#security)
|
508
|
+
|
509
|
+
### Parse::Session
|
510
|
+
This class represents the data and columns contained in the standard Parse `_Session` collection. You may add additional properties and methods to this class. It is defined as follows:
|
511
|
+
|
512
|
+
```ruby
|
513
|
+
class Parse::Session < Parse::Object
|
514
|
+
property :created_with, :object
|
515
|
+
property :expires_at, :date
|
516
|
+
property :installation_id
|
517
|
+
property :restricted, :boolean
|
518
|
+
property :session_token
|
519
|
+
|
520
|
+
belongs_to :user
|
453
521
|
end
|
454
522
|
```
|
455
523
|
|
524
|
+
You can get a specific `Parse::Session` given a session_token by using the `session` method. You can also find the user tied to a specific Parse session or session token with `Parse::User.session`.
|
525
|
+
|
526
|
+
```ruby
|
527
|
+
session = Parse::Session.session(token)
|
528
|
+
|
529
|
+
session.user # the Parse user for this session
|
530
|
+
|
531
|
+
# or fetch user with a session token
|
532
|
+
user = Parse::User.session(token)
|
533
|
+
|
534
|
+
```
|
535
|
+
|
456
536
|
### Parse::Installation
|
457
537
|
This class represents the data and columns contained in the standard Parse `_Installation` collection. You may add additional properties and methods to this class. It is defined as follows:
|
458
538
|
|
@@ -475,21 +555,6 @@ class Parse::Installation < Parse::Object
|
|
475
555
|
end
|
476
556
|
```
|
477
557
|
|
478
|
-
### Parse::Session
|
479
|
-
This class represents the data and columns contained in the standard Parse `_Session` collection. You may add additional properties and methods to this class. It is defined as follows:
|
480
|
-
|
481
|
-
```ruby
|
482
|
-
class Parse::Session < Parse::Object
|
483
|
-
property :created_with, :object
|
484
|
-
property :expires_at, :date
|
485
|
-
property :installation_id
|
486
|
-
property :restricted, :boolean
|
487
|
-
property :session_token
|
488
|
-
|
489
|
-
belongs_to :user
|
490
|
-
end
|
491
|
-
```
|
492
|
-
|
493
558
|
### Parse::Role
|
494
559
|
This class represents the data and columns contained in the standard Parse `_Role` collection. You may add additional properties and methods to this class. It is defined as follows:
|
495
560
|
|
@@ -502,6 +567,115 @@ class Parse::Role < Parse::Object
|
|
502
567
|
end
|
503
568
|
```
|
504
569
|
|
570
|
+
### Parse::User
|
571
|
+
This class represents the data and columns contained in the standard Parse `_User` collection. You may add additional properties and methods to this class. It is defined as follows:
|
572
|
+
|
573
|
+
```ruby
|
574
|
+
class Parse::User < Parse::Object
|
575
|
+
property :auth_data, :object
|
576
|
+
property :email
|
577
|
+
property :username
|
578
|
+
|
579
|
+
end
|
580
|
+
```
|
581
|
+
|
582
|
+
While `:password` is a property on the User class, which will generally be empty whenever fetching User records.
|
583
|
+
|
584
|
+
#### Signup
|
585
|
+
You can signup new users in two ways. You can either use a class method `Parse::User.signup` to create a new user with the minimum fields of username, password and email, or create a `Parse::User` object can call the `signup!` method. If signup fails, it will raise the corresponding exception.
|
586
|
+
|
587
|
+
```ruby
|
588
|
+
user = Parse::User.signup(username, password, email)
|
589
|
+
|
590
|
+
#or
|
591
|
+
user = Parse::User.new username: "user", password: "s3cret"
|
592
|
+
user.signup!
|
593
|
+
```
|
594
|
+
|
595
|
+
##### Third-Party Services
|
596
|
+
You can signup users using third-party services like Facebook and Twitter as described in: [Signing Up and Logging In](https://parseplatform.github.io/docs/rest/guide/#signing-up-and-logging-in). To do this with Parse-Stack, you can call the `Parse::User.autologin_service` method by passing the service name and the corresponding authentication hash data. For a listing of supported third-party authentication services, see [OAuth](https://github.com/ParsePlatform/parse-server/wiki/OAuth).
|
597
|
+
|
598
|
+
```ruby
|
599
|
+
fb_auth = {}
|
600
|
+
fb_auth[:id] = "123456789"
|
601
|
+
fb_auth[:access_token] = "SaMpLeAAiZBLR995wxBvSGNoTrEaL"
|
602
|
+
fb_auth[:expiration_date] = "2025-02-21T23:49:36.353Z"
|
603
|
+
|
604
|
+
# signup or login a user with this auth data.
|
605
|
+
user = Parse::User.autologin_service(:facebook, fb_auth)
|
606
|
+
```
|
607
|
+
|
608
|
+
You may also combine both approaches of signing up a new user with a third-party service and set additional custom fields. For this, use the method `Parse::User.create`.
|
609
|
+
|
610
|
+
```ruby
|
611
|
+
# or to signup a user with additional data, but linked to Facebook
|
612
|
+
data = {
|
613
|
+
username: "johnsmith",
|
614
|
+
name: "John",
|
615
|
+
email: "user@example.com",
|
616
|
+
authData: { facebook: fb_auth }
|
617
|
+
}
|
618
|
+
user = Parse::User.create data
|
619
|
+
```
|
620
|
+
|
621
|
+
#### Login and Sessions
|
622
|
+
With the `Parse::User` class, you can also perform login and logout functionality. The class special accessors for `session_token` and `session` to manage its authentication state. This will allow you to authenticate users as well as perform Parse queries as a specific user using their session token. To login a user, use the `Parse::User.login` method by supplying the corresponding username and password, or if you already have a user record, use `login!` with the proper password.
|
623
|
+
|
624
|
+
```ruby
|
625
|
+
user = Parse::User.login(username,password)
|
626
|
+
user.session_token # session token from a Parse::Session
|
627
|
+
user.session # Parse::Session tied to the token
|
628
|
+
|
629
|
+
# You can login user records
|
630
|
+
user = Parse::User.first
|
631
|
+
user.session_token # nil
|
632
|
+
|
633
|
+
passwd = 'p_n7!-e8' # corresponding password
|
634
|
+
user.login!(passwd) # true
|
635
|
+
|
636
|
+
user.session_token # 'r:pnktnjyb996sj4p156gjtp4im'
|
637
|
+
|
638
|
+
# logout to delete the session
|
639
|
+
user.logout
|
640
|
+
```
|
641
|
+
|
642
|
+
If you happen to already have a valid session token, you can use it to retrieve the corresponding Parse::User.
|
643
|
+
|
644
|
+
```ruby
|
645
|
+
# finds user with session token
|
646
|
+
user = Parse::User.session(session_token)
|
647
|
+
|
648
|
+
user.logout # deletes the corresponding session
|
649
|
+
```
|
650
|
+
|
651
|
+
#### Linking and Unlinking Users
|
652
|
+
You can signup or login uses with third-party services like Facebook and Twitter as described in: [Linking and Unlinking Users](https://parseplatform.github.io/docs/rest/guide/#linking). To do this, you must first get the corresponding authentication data for the specific service, and then apply it to the user using the linking and unlinking methods. Each method returns true or false if the action was successful. For a listing of supported third-party authentication services, see [OAuth](https://github.com/ParsePlatform/parse-server/wiki/OAuth).
|
653
|
+
|
654
|
+
```ruby
|
655
|
+
|
656
|
+
user = Parse::User.first
|
657
|
+
|
658
|
+
fb_auth = { ... } # Facebook auth data
|
659
|
+
|
660
|
+
# Link this user's Facebook account with Parse
|
661
|
+
user.link_auth_data! :facebook, fb_auth
|
662
|
+
|
663
|
+
# Unlinks this user's Facebook account from Parse
|
664
|
+
user.unlink_auth_data! :facebook
|
665
|
+
```
|
666
|
+
|
667
|
+
#### Request Password Reset
|
668
|
+
You can reset a user's password using the `Parse::User.request_password_reset` method.
|
669
|
+
|
670
|
+
```ruby
|
671
|
+
user = Parse::User.first
|
672
|
+
|
673
|
+
# pass a user object
|
674
|
+
Parse::User.request_password_reset user
|
675
|
+
# or email
|
676
|
+
Parse::User.request_password_reset("user@example.com")
|
677
|
+
```
|
678
|
+
|
505
679
|
|
506
680
|
## Modeling and Subclassing
|
507
681
|
For the general case, your Parse classes should inherit from `Parse::Object`. `Parse::Object` utilizes features from `ActiveModel` to add several features to each instance of your subclass. These include `Dirty`, `Conversion`, `Callbacks`, `Naming` and `Serializers::JSON`.
|
@@ -524,19 +698,21 @@ end
|
|
524
698
|
### Defining Properties
|
525
699
|
Properties are considered a literal-type of association. This means that a defined local property maps directly to a column name for that remote Parse class which contain the value. **All properties are implicitly formatted to map to a lower-first camelcase version in Parse (remote).** Therefore a local property defined as `like_count`, would be mapped to the remote column of `likeCount` automatically. The only special behavior to this rule is the `:id` property which maps to `objectId` in Parse. This implicit conversion mapping is the default behavior, but can be changed on a per-property basis. All Parse data types are supported and all Parse::Object subclasses already provide definitions for `:id` (objectId), `:created_at` (createdAt), `:updated_at` (updatedAt) and `:acl` (ACL) properties.
|
526
700
|
|
527
|
-
- **:string** (_default_) - a generic string.
|
528
|
-
- **:integer** - basic number.
|
701
|
+
- **:string** (_default_) - a generic string. Can be used as an enum field, see [Enum](#enum).
|
702
|
+
- **:integer** (alias **:int**) - basic number.
|
529
703
|
- **:float** - a floating numeric value.
|
530
|
-
- **:boolean** - true/false value. (
|
531
|
-
- **:date** - a Parse date type.
|
532
|
-
- **:array** - a
|
533
|
-
- **:file** - a Parse file type.
|
534
|
-
- **:geopoint** - a GeoPoint type.
|
535
|
-
- **:bytes** - a Parse bytes data type managed as base64.
|
536
|
-
- **:object** - an object
|
704
|
+
- **:boolean** (alias **:bool**) - true/false value. This will also generate a class scope helper. See [Query Scopes](#query-scopes).
|
705
|
+
- **:date** - a Parse date type. See [Parse::Date](#parsedate).
|
706
|
+
- **:array** - a heterogeneous list with dirty tracking. See [Parse::CollectionProxy](https://github.com/modernistik/parse-stack/blob/master/lib/parse/model/associations/collection_proxy.rb).
|
707
|
+
- **:file** - a Parse file type. See [Parse::File](#parsefile).
|
708
|
+
- **:geopoint** - a GeoPoint type. See [Parse::GeoPoint](#parsegeopoint).
|
709
|
+
- **:bytes** - a Parse bytes data type managed as base64. See [Parse::Bytes](#parsebytes).
|
710
|
+
- **:object** - an object "hash" data type. See [ActiveSupport::HashWithIndifferentAccess](http://apidock.com/rails/ActiveSupport/HashWithIndifferentAccess).
|
537
711
|
|
538
712
|
For completeness, the `:id` and `:acl` data types are also defined in order to handle the Parse `objectId` field and the `ACL` object. Those are special and should not be used in your class (unless you know what you are doing). New data types can be implemented through the internal `typecast` interface. **TODO: discuss `typecast` interface in the future**
|
539
713
|
|
714
|
+
In addition, `:boolean` data types create a special method that uses the `?` convention. As an example, if you have a property named `approved`, the normal getter `obj.approved` can return true, false or nil based on the value in Parse. However with the `obj.approved?` method, it will return true if it set to true, false for any other value.
|
715
|
+
|
540
716
|
Using the example above, we can add the base properties to our classes.
|
541
717
|
|
542
718
|
```ruby
|
@@ -562,6 +738,9 @@ class Post < Parse::Object
|
|
562
738
|
# a list using
|
563
739
|
property :tags, :array
|
564
740
|
|
741
|
+
# string column as enummerated type. see :enum
|
742
|
+
property :status, enum: [:active, :archived]
|
743
|
+
|
565
744
|
# Maps to "featuredImage" column representing a File.
|
566
745
|
property :featured_image, :file
|
567
746
|
|
@@ -612,13 +791,13 @@ post.SEO # the alias method since 'field: "SEO"'
|
|
612
791
|
#### Property Options
|
613
792
|
These are the supported options when defining properties. Parse::Objects are backed by `ActiveModel`, which means you can add additional validations and features supported by that library.
|
614
793
|
|
615
|
-
##### `:required
|
616
|
-
This option provides information to the property builder that it is a required property. The requirement is not strongly enforced for a save, which means even though the value for the property may not be present, saves and updates can be successfully performed. However, the setting `required` to true, it will set some ActiveModel validations on the property to be used when calling `valid?`. By default it will add a `validates_presence_of` for the property key. If the data type of the property is either `:integer` or `:float`, it will also add a `validates_numericality_of` validation. Default `false`.
|
794
|
+
##### `:required`
|
795
|
+
A boolean property. This option provides information to the property builder that it is a required property. The requirement is not strongly enforced for a save, which means even though the value for the property may not be present, saves and updates can be successfully performed. However, the setting `required` to true, it will set some ActiveModel validations on the property to be used when calling `valid?`. By default it will add a `validates_presence_of` for the property key. If the data type of the property is either `:integer` or `:float`, it will also add a `validates_numericality_of` validation. Default `false`.
|
617
796
|
|
618
|
-
##### `:field
|
797
|
+
##### `:field`
|
619
798
|
This option allows you to set the name of the remote column for the Parse table. Using this will explicitly set the remote property name to the value of this option. The value provided for this option will affect the name of the alias method that is generated when `alias` option is used. **By default, the name of the remote column is the lower-first camelcase version of the property name. As an example, for a property with key `:my_property_name`, the framework will implicitly assume that the remote column is `myPropertyName`.**
|
620
799
|
|
621
|
-
##### `:default
|
800
|
+
##### `:default`
|
622
801
|
This option provides you to set a default value for a specific property when the getter accessor method is used and the internal value of the instance object's property is nil. It can either take a literal value or a Proc/lambda.
|
623
802
|
|
624
803
|
```ruby
|
@@ -630,11 +809,11 @@ class SomeClass < Parse::Object
|
|
630
809
|
end
|
631
810
|
```
|
632
811
|
|
633
|
-
##### `:alias
|
634
|
-
It is highly recommended that this is set to true, which is the default. This option allows for the generation of the additional accessors with the value of `:field`. By allowing two accessors methods, aliased to each other, allows for easier importing and automatic object instantiation based on Parse object JSON data into the Parse::Object subclass.
|
812
|
+
##### `:alias`
|
813
|
+
A boolean property. It is highly recommended that this is set to true, which is the default. This option allows for the generation of the additional accessors with the value of `:field`. By allowing two accessors methods, aliased to each other, allows for easier importing and automatic object instantiation based on Parse object JSON data into the Parse::Object subclass.
|
635
814
|
|
636
|
-
##### `:symbolize
|
637
|
-
This option is only available for fields with data type of `:string`. This allows you to utilize the values for this property as symbols instead of the literal strings, which is Parse's storage format. This feature is useful if a particular property represents a set of enumerable states described in string form. As an example, if you have a `Post` object which has a set of publish states stored in Parse as "draft","scheduled", and "published" - we can use ruby symbols to make our code easier.
|
815
|
+
##### `:symbolize`
|
816
|
+
A boolean property. This option is only available for fields with data type of `:string`. This allows you to utilize the values for this property as symbols instead of the literal strings, which is Parse's storage format. This feature is useful if a particular property represents a set of enumerable states described in string form. As an example, if you have a `Post` object which has a set of publish states stored in Parse as "draft","scheduled", and "published" - we can use ruby symbols to make our code easier.
|
638
817
|
|
639
818
|
```ruby
|
640
819
|
class Post < Parse::Object
|
@@ -650,14 +829,78 @@ if post.state == :draft
|
|
650
829
|
end
|
651
830
|
```
|
652
831
|
|
653
|
-
#####
|
654
|
-
|
832
|
+
##### `:enum`
|
833
|
+
The enum option allows you to define an array of possible values that the particular `:string` property should hold. This feature has similarities in the methods and accessors generated for you as described in [ActiveRecord::Enum](http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html). Using the example in that documentation:
|
834
|
+
|
835
|
+
```ruby
|
836
|
+
class Conversation < Parse::Object
|
837
|
+
property :status, enum: [ :active, :archived ]
|
838
|
+
end
|
839
|
+
|
840
|
+
Conversation.statuses # => [ :active, :archived ]
|
841
|
+
|
842
|
+
# named scopes
|
843
|
+
Conversation.active # where status: :active
|
844
|
+
Conversation.archived(limit: 10) # where status: :archived, limit 10
|
845
|
+
|
846
|
+
conversation.active! # sets status to active!
|
847
|
+
conversation.active? # => true
|
848
|
+
conversation.status # => :active
|
849
|
+
|
850
|
+
conversation.archived!
|
851
|
+
conversation.archived? # => true
|
852
|
+
conversation.status # => :archived
|
853
|
+
|
854
|
+
# equivalent
|
855
|
+
conversation.status = "archived"
|
856
|
+
conversation.status = :archived
|
857
|
+
|
858
|
+
# allowed by the setter
|
859
|
+
conversation.status = :banana
|
860
|
+
conversation.status_valid? # => false
|
861
|
+
|
862
|
+
```
|
863
|
+
|
864
|
+
Similar to [ActiveRecord::Enum](http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html), you can use the `:_prefix` or `:_suffix` options when you need to define multiple enums with same values. If the passed value is true, the methods are prefixed/suffixed with the name of the enum. It is also possible to supply a custom value:
|
865
|
+
|
866
|
+
```ruby
|
867
|
+
class Conversation < Parse::Object
|
868
|
+
property :status, enum: [:active, :archived], _suffix: true
|
869
|
+
property :comments_status, enum: [:active, :inactive], _prefix: :comments
|
870
|
+
# combined
|
871
|
+
property :discussion, enum: [:casual, :business], _prefix: :talk, _suffix: true
|
872
|
+
end
|
873
|
+
|
874
|
+
Conversation.statuses # => [:active, :archived]
|
875
|
+
Conversation.comments # => [:active, :inactive]
|
876
|
+
Conversation.talks # => [:casual, :business]
|
877
|
+
|
878
|
+
# affects scopes names
|
879
|
+
Conversation.archived_status
|
880
|
+
Conversation.comments_inactive
|
881
|
+
Conversation.business_talk
|
882
|
+
|
883
|
+
conversation.active_status!
|
884
|
+
conversation.archived_status? # => false
|
885
|
+
|
886
|
+
conversation.status = :banana
|
887
|
+
conversation.valid_status? # => false
|
888
|
+
|
889
|
+
conversation.comments_inactive!
|
890
|
+
conversation.comments_active? # => false
|
891
|
+
|
892
|
+
conversation.casual_talk!
|
893
|
+
conversation.business_talk? # => false
|
894
|
+
```
|
895
|
+
|
896
|
+
##### `:scope`
|
897
|
+
A boolean property. For some data types like `:boolean` and enums, some [query scopes](#query-scopes) are generated to more easily query data. To prevent generating these scopes for a particular property, set this value to `false`.
|
655
898
|
|
656
899
|
### Associations
|
657
900
|
Parse supports a three main types of relational associations. One type of relation is the `One-to-One` association. This is implemented through a specific column in Parse with a Pointer data type. This pointer column, contains a local value that refers to a different record in a separate Parse table. This association is implemented using the `:belongs_to` feature. The second association is of `One-to-Many`. This is implemented is in Parse as a Array type column that contains a list of of Parse pointer objects. It is recommended by Parse that this array does not exceed 100 items for performance reasons. This feature is implemented using the `:has_many` operation with the plural name of the local Parse class. The last association type is a Parse Relation. These can be used to implement a large `Many-to-Many` association without requiring an explicit intermediary Parse table or class. This feature is also implemented using the `:has_many` method but passing the option of `:relation`.
|
658
901
|
|
659
902
|
#### Belongs To
|
660
|
-
Utilizing the `belongs_to` method in defining a property in a Parse::Object subclass sets up an association between the local table and a foreign table. Specifying the `belongs_to` in the class, tells the framework that the Parse table contains a local column in its schema that has a reference to a record in a foreign table. The argument to `belongs_to` should be the singularized version of the foreign Parse::Object class. you should specify the foreign table as the snake_case singularized version of the foreign table class. It is important to note that the reverse relationship is not generated automatically.
|
903
|
+
This association creates a one-to-one association with another Parse model. This association says that this class contains a foreign pointer column which references a different class. Utilizing the `belongs_to` method in defining a property in a Parse::Object subclass sets up an association between the local table and a foreign table. Specifying the `belongs_to` in the class, tells the framework that the Parse table contains a local column in its schema that has a reference to a record in a foreign table. The argument to `belongs_to` should be the singularized version of the foreign Parse::Object class. you should specify the foreign table as the snake_case singularized version of the foreign table class. It is important to note that the reverse relationship is not generated automatically.
|
661
904
|
|
662
905
|
```ruby
|
663
906
|
class Author < Parse::Object
|
@@ -683,12 +926,12 @@ post.save
|
|
683
926
|
```
|
684
927
|
|
685
928
|
##### Options
|
686
|
-
You can override some of the default functionality when creating both `belongs_to` and `has_many` associations.
|
929
|
+
You can override some of the default functionality when creating both `belongs_to`, `has_one` and `has_many` associations.
|
687
930
|
|
688
|
-
###### `:required
|
689
|
-
Setting the requirement, automatically creates an ActiveModel validation of `validates_presence_of` for the association. This will not prevent the save, but affects the validation check when `valid?` is called on an instance. Default is false.
|
931
|
+
###### `:required`
|
932
|
+
A boolean property. Setting the requirement, automatically creates an ActiveModel validation of `validates_presence_of` for the association. This will not prevent the save, but affects the validation check when `valid?` is called on an instance. Default is false.
|
690
933
|
|
691
|
-
###### `:as
|
934
|
+
###### `:as`
|
692
935
|
This option allows you to override the foreign Parse class that this association refers while allowing you to have a different accessor name. As an example, you may have a class `Band` which has a `manager` who is of type `Parse::User` and a set of band members, represented by the class `Artist`. You can override the default casting class as follows:
|
693
936
|
|
694
937
|
```ruby
|
@@ -708,64 +951,226 @@ band.lead_singer # Artist object
|
|
708
951
|
band.drummer # Artist object
|
709
952
|
```
|
710
953
|
|
711
|
-
###### `:field
|
954
|
+
###### `:field`
|
712
955
|
This option allows you to set the name of the remote Parse column for this property. Using this will explicitly set the remote property name to the value of this option. The value provided for this option will affect the name of the alias method that is generated when `alias` option is used. **By default, the name of the remote column is the lower-first camel case version of the property name. As an example, for a property with key `:my_property_name`, the framework will implicitly assume that the remote column is `myPropertyName`.**
|
713
956
|
|
714
|
-
#### Has
|
715
|
-
|
957
|
+
#### Has One
|
958
|
+
The `has_one` creates a one-to-one association with another Parse class. This association says that the other class in the association contains a foreign pointer column which references instances of this class. If your model contains a column that is a Parse pointer to another class, you should use `belongs_to` for that association instead.
|
716
959
|
|
717
|
-
|
718
|
-
2. Querying the relation is actually performed against the implicit join table, not the local one.
|
719
|
-
3. Applying query constraints for a set of records within a relation is performed against the foreign table class, not the class having the relational column.
|
960
|
+
Defining a `has_one` property generates a helper query method to fetch a particular record from a foreign class. This is useful for setting up the inverse relationship accessors of a `belongs_to`. In the case of the `has_one` relationship, the `:field` option represents the name of the column of the foreign class where the Parse pointer is stored. By default, the lower-first camel case version of the Parse class name is used.
|
720
961
|
|
721
|
-
|
962
|
+
In the example below, a `Band` has a local column named `manager` which has a pointer to a `Parse::User` record. This setups up the accessor for `Band` objects to access the band's manager.
|
722
963
|
|
723
|
-
|
964
|
+
```ruby
|
965
|
+
# every band has a manager
|
966
|
+
class Band < Parse::Object
|
967
|
+
belongs_to :manager, as: :user
|
968
|
+
end
|
969
|
+
|
970
|
+
band = Band.first id: '12345'
|
971
|
+
# the user represented by this manager
|
972
|
+
user = band.manger
|
973
|
+
|
974
|
+
```
|
975
|
+
|
976
|
+
Since we know there is a column named `manager` in the `Band` class that points to a single `Parse::User`, you can setup the inverse association read accessor in the `Parse::User` class. Note, that to change the association, you need to modify the `manager` property on the band instance since it contains the `belongs_to` property.
|
724
977
|
|
725
978
|
```ruby
|
979
|
+
# every user manages a band
|
980
|
+
class Parse::User
|
981
|
+
# inverse relationship to `Band.belongs_to :manager`
|
982
|
+
has_one :band, field: :manager
|
983
|
+
end
|
984
|
+
|
985
|
+
user = Parse::User.first
|
986
|
+
# use the generated has_one accessor `band`.
|
987
|
+
user.band # similar to query: Band.first(:manager => user)
|
988
|
+
|
989
|
+
```
|
990
|
+
|
991
|
+
You may optionally use `has_one` with scopes, in order to fine tune the query result. Using the example above, you can customize the query with a scope that only fetches the association if the band is approved. If the association cannot be fetched, `nil` is returned.
|
992
|
+
|
993
|
+
```ruby
|
994
|
+
# adding to previous example
|
995
|
+
class Band < Parse::Object
|
996
|
+
property :approved, :boolean
|
997
|
+
property :approved_date, :date
|
998
|
+
end
|
999
|
+
|
1000
|
+
# every user manages a band
|
1001
|
+
class Parse::User
|
1002
|
+
has_one :recently_approved, ->{ where(order: :approved_date.desc) }, field: :manager, as: :band
|
1003
|
+
has_one :band_by_status, ->(status) { where(approved: status) }, field: :manager, as: :band
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
# gets the band most recently approved
|
1007
|
+
user.recently_approved
|
1008
|
+
# equivalent: Band.first(manager: user, order: :approved_date.desc)
|
1009
|
+
|
1010
|
+
# fetch the managed band that is not approved
|
1011
|
+
user.band_by_status(false)
|
1012
|
+
# equivalent: Band.first(manager: user, approved: false)
|
1013
|
+
|
1014
|
+
```
|
1015
|
+
|
1016
|
+
#### Has Many
|
1017
|
+
Parse has many ways to implement one-to-many and many-to-many associations: `Array`, `Parse Relation` or through a `Query`. How you decide to implement your associations, will affect how `has_many` works in Parse-Stack. Parse natively supports one-to-many and many-to-many relationships using `Array` and `Relations`, as described in [Relational Data](https://parseplatform.github.io/docs/js/guide/#relational-data). Both of these methods require you define a specific column type in your Parse table that will be used to store information about the association.
|
1018
|
+
|
1019
|
+
In addition to `Array` and `Relation`, Parse-Stack also implements the standard `has_many` behavior prevalent in other frameworks through a query where the associated class contains a foreign pointer to the local class, usually the inverse of a `belongs_to`. This requires that the associated class
|
1020
|
+
|
1021
|
+
##### Query
|
1022
|
+
In this implementation, a `has_many` association for a Parse class requires that another Parse class will have a foreign pointer that refers to instances of this class. This is the standard way that `has_many` relationships work in most databases systems. This is usually the case when you have a class that has a `belongs_to` relationship to instances of the local class.
|
1023
|
+
|
1024
|
+
In the example below, many songs belong to a specific artist. We set this association by setting `:belongs_to` relationship from `Song` to `Artist`. Knowing there is a column in `Song` that points to instances of an `Artist`, we can setup a `has_many` association to `Song` instances in the `Artist` class. Doing so will generate a helper query method on the `Artist` instance objects.
|
1025
|
+
|
1026
|
+
```ruby
|
1027
|
+
class Song < Parse::Object
|
1028
|
+
property :released, :date
|
1029
|
+
# this class will have a pointer column to an Artist
|
1030
|
+
belongs_to :artist
|
1031
|
+
end
|
726
1032
|
|
727
1033
|
class Artist < Parse::Object
|
1034
|
+
has_many :songs
|
728
1035
|
end
|
729
1036
|
|
730
|
-
|
731
|
-
|
1037
|
+
artist = Artist.first
|
1038
|
+
|
1039
|
+
artist.songs # => [all songs belonging to artist]
|
1040
|
+
# equivalent: Song.all(artist: artist)
|
1041
|
+
|
1042
|
+
# filter also by release date
|
1043
|
+
artist.songs(:released.after => 1.year.ago)
|
1044
|
+
# equivalent: Song.all(artist: artist, :released.after => 1.year.ago)
|
1045
|
+
|
1046
|
+
```
|
1047
|
+
|
1048
|
+
In order to modify the associated objects (ex. `songs`), you must modify their corresponding `belongs_to` field (in this case `song.artist`), to another record and save it.
|
1049
|
+
|
1050
|
+
Options for `has_many` using this approach are `:as` and `:field`. The `:as` option behaves similarly to the `:belongs_to` counterpart. The `:field` option can be used to override the derived column name located in the foreign class. The default value for `:field` is the columnized version of the Parse subclass `parse_class` method.
|
1051
|
+
|
1052
|
+
```ruby
|
1053
|
+
class Parse::User
|
1054
|
+
# since the foreign column name is :agent
|
1055
|
+
has_many :artists, field: :agent
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
class Artist < Parse::Object
|
1059
|
+
belongs_to :manager, as: :user, field: :agent
|
1060
|
+
end
|
1061
|
+
|
1062
|
+
artist.manager # => Parse::User object
|
1063
|
+
|
1064
|
+
user.artists # => [artists where :agent column is user]
|
1065
|
+
```
|
1066
|
+
|
1067
|
+
When using this approach, you may also employ the use of scopes to filter the particular data from the `has_many` association.
|
1068
|
+
|
1069
|
+
```ruby
|
1070
|
+
class Artist
|
1071
|
+
has_many :songs, ->(timeframe) { where(:created_at.after => timeframe) }
|
1072
|
+
end
|
1073
|
+
|
1074
|
+
artist.songs(6.months.ago)
|
1075
|
+
# => [artist's songs created in the last 6 months]
|
1076
|
+
|
1077
|
+
```
|
1078
|
+
|
1079
|
+
You may also call property methods in your scopes related to the local class. You also have access to the instance object for the local class through a special `:i` method in the scope.
|
1080
|
+
|
1081
|
+
```ruby
|
1082
|
+
class Concert
|
1083
|
+
property :city
|
1084
|
+
belongs_to :artist
|
1085
|
+
end
|
1086
|
+
|
1087
|
+
class Artist
|
1088
|
+
property :hometown
|
1089
|
+
has_many :local_concerts, -> { where(:city => hometown) }, as: :concerts
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
# assume
|
1093
|
+
artist.hometown = "San Diego"
|
1094
|
+
|
1095
|
+
# artist's concerts in their hometown of 'San Diego'
|
1096
|
+
artist.local_concerts
|
1097
|
+
# equivalent: Concert.all(artist: artist, city: artist.hometown)
|
1098
|
+
|
1099
|
+
```
|
1100
|
+
|
1101
|
+
##### Array
|
1102
|
+
In this implementation, you can designate a column to be of `Array` type that contains a list of Parse pointers. Parse-Stack supports this by passing the option `through: :array` to the `has_many` method. If you use this approach, it is recommended that this is used for associations where the quantity is less than 100 in order to maintain query and fetch performance. You would be in charge of maintaining the array with the proper list of Parse pointers that are associated to the object. Parse-Stack does help by wrapping the array in a [Parse::PointerCollectionProxy](https://github.com/modernistik/parse-stack/blob/master/lib/parse/model/associations/pointer_collection_proxy.rb) which provides dirty tracking.
|
1103
|
+
|
1104
|
+
```ruby
|
1105
|
+
class Artist < Parse::Object
|
732
1106
|
end
|
733
1107
|
|
734
1108
|
class Band < Parse::Object
|
735
|
-
|
736
|
-
# assume any band as < 100 members
|
737
|
-
has_many :artists # assumes `through: :array`
|
738
|
-
# bands can have millions of fans (Parse::User objects),
|
739
|
-
# we use relations instead
|
740
|
-
has_many :fans, as: :user, through: :relation
|
1109
|
+
has_many :artists, through: :array
|
741
1110
|
end
|
742
1111
|
|
743
|
-
|
744
|
-
|
1112
|
+
artist = Artist.first
|
1113
|
+
|
1114
|
+
# find all bands that contain this artist
|
1115
|
+
bands = Band.all( :artists.in => [artist.pointer] )
|
745
1116
|
|
746
|
-
# Find all bands which have Joe as an artist.
|
747
|
-
banjoe = Artist.first name: "Joe Banjoe"
|
748
|
-
bands = Band.all( :artists.in => [banjoe.pointer] )
|
749
1117
|
band = bands.first
|
1118
|
+
band.artists # => [array of Artist pointers]
|
1119
|
+
|
1120
|
+
# remove artists
|
1121
|
+
band.artists.remove artist
|
1122
|
+
|
1123
|
+
# add artist
|
1124
|
+
band.artists.add artist
|
1125
|
+
|
1126
|
+
# save changes
|
1127
|
+
band.save
|
1128
|
+
```
|
1129
|
+
|
1130
|
+
##### Parse Relation
|
1131
|
+
Other than the use of arrays, Parse supports native one-to-many and many-to-many associations through what is referred to as a [Parse Relation](https://parseplatform.github.io/docs/js/guide/#many-to-many-relationships). This is implemented by defining a column to be of type `Relation` which refers to a foreign class. Parse-Stack supports this by passing the `through: :relation` option to the `has_many` method. Designating a column as a Parse relation to another class type, will create a one-way intermediate "join-list" between the local class and the foreign class. One important distinction of this compared to other types of data stores (ex. PostgresSQL) is that:
|
1132
|
+
|
1133
|
+
1. The inverse relationship association is not available automatically. Therefore, having a column of `artists` in a `Band` class that relates to members of the band (as `Artist` class), does not automatically make a set of `Band` records available to `Artist` records for which they have been related. If you need to maintain both the inverse relationship between a foreign class to its associations, you will need to manually manage that by adding two Parse relation columns in each class, or by creating a separate class (ex. `ArtistBands`) that is used as a join table.
|
1134
|
+
2. Querying the relation is actually performed against the implicit join table, not the local one.
|
1135
|
+
3. Applying query constraints for a set of records within a relation is performed against the foreign table class, not the class having the relational column.
|
1136
|
+
|
1137
|
+
The Parse documentation provides more details on associations, see [Parse Relations Guide](https://parse.com/docs/ios/guide#relations). Parse-Stack will handle the work for (2) and (3) automatically.
|
1138
|
+
|
1139
|
+
In the example below, a `Band` can have thousands of `Fans`. We setup a `Relation<Fan>` column in the `Band` class that references the `Fan` class. Parse-Stack provides methods to manage the relationship under the [Parse::RelationCollectionProxy](https://github.com/modernistik/parse-stack/blob/master/lib/parse/model/associations/relation_collection_proxy.rb) class.
|
1140
|
+
|
1141
|
+
```ruby
|
1142
|
+
|
1143
|
+
class Fan < Parse::Object
|
1144
|
+
# .. lots of properties ...
|
1145
|
+
property :location, :geopoint
|
1146
|
+
end
|
1147
|
+
|
1148
|
+
class Band < Parse::Object
|
1149
|
+
has_many :fans, through: :relation
|
1150
|
+
end
|
1151
|
+
|
1152
|
+
band = Band.first
|
750
1153
|
|
751
1154
|
# the number of fans in the relation
|
752
1155
|
band.fans.count
|
753
1156
|
|
754
1157
|
# get the first object in relation
|
755
|
-
fan = bands.fans.first
|
1158
|
+
fan = bands.fans.first # => Parse::User object
|
756
1159
|
|
757
1160
|
# use `add` or `remove` to modify relations
|
758
|
-
band.fans.add
|
1161
|
+
band.fans.add user
|
1162
|
+
bands.fans.remove user
|
1163
|
+
|
759
1164
|
# updates the relation as well as changes to `band`
|
760
1165
|
band.fans.save
|
761
1166
|
|
762
|
-
|
1167
|
+
# Find 50 fans who are near San Diego, CA
|
763
1168
|
downtown = Parse::GeoPoint.new(32.82, -117.23)
|
764
|
-
fans = band.fans.all
|
1169
|
+
fans = band.fans.all :location.near => downtown
|
765
1170
|
|
766
1171
|
```
|
767
1172
|
|
768
|
-
You can perform atomic additions and removals of objects from `has_many` relations. Parse allows this by providing a specific atomic operation request. You can use the methods below to perform these types of atomic operations. __Note: The operation is performed directly on Parse server and not on your
|
1173
|
+
You can perform atomic additions and removals of objects from `has_many` relations. Parse allows this by providing a specific atomic operation request. You can use the methods below to perform these types of atomic operations. __Note: The operation is performed directly on Parse server and not on your instance object.__
|
769
1174
|
|
770
1175
|
```ruby
|
771
1176
|
|
@@ -787,10 +1192,31 @@ band.op_destroy!("category") # { __op: :Delete }
|
|
787
1192
|
```
|
788
1193
|
|
789
1194
|
##### Options
|
790
|
-
Options for `has_many` are the same as the `belongs_to` counterpart with support for `:required`, `:as` and `:field`. It has
|
1195
|
+
Options for `has_many` are the same as the `belongs_to` counterpart with support for `:required`, `:as` and `:field`. It has these additional options.
|
1196
|
+
|
1197
|
+
###### `:through`
|
1198
|
+
This sets the type of the `has_many` relation whose possible values are `:array`, `:relation` or `:query` (implicit default). If set to `:array`, it defines the column in Parse as being an array of Parse pointer objects and will be managed locally using a `Parse::PointerCollectionProxy`. If set to `:relation`, it defines a column of type Parse Relation with the foreign class and will be managed locally using a `Parse::RelationCollectionProxy`. If set to `:query`, no storage is required on the local class as the associated records will be fetched using a Parse query.
|
1199
|
+
|
1200
|
+
###### `:scope_only`
|
1201
|
+
Setting this option to `true`, makes the association fetch based only on the scope provided and does not use the local instance object as a foreign pointer in the query. This allows for cases where another property of the local class, is needed to match the resulting records in the association.
|
791
1202
|
|
792
|
-
|
793
|
-
|
1203
|
+
In the example below, the `Post` class does not have a `:belongs_to` association to `Author`, but using the author's name, we can find related posts.
|
1204
|
+
|
1205
|
+
```ruby
|
1206
|
+
|
1207
|
+
class Author < Parse::Object
|
1208
|
+
property :name
|
1209
|
+
has_many :posts, ->{ where :tags.in => name.downcase }, scope_only: true
|
1210
|
+
end
|
1211
|
+
|
1212
|
+
class Post < Parse::Object
|
1213
|
+
property :tags, :array
|
1214
|
+
end
|
1215
|
+
|
1216
|
+
author.posts # => Posts where author's name is a tag
|
1217
|
+
# equivalent: Post.all( :tags.in => artist.name.downcase )
|
1218
|
+
|
1219
|
+
```
|
794
1220
|
|
795
1221
|
## Creating, Saving and Deleting Records
|
796
1222
|
This section provides some of the basic methods when creating, updating and deleting objects from Parse. To illustrate the various methods available for saving Parse records, we use this example class:
|
@@ -1536,6 +1962,59 @@ query.or_where(:wins.lt => 5)
|
|
1536
1962
|
results = query.results
|
1537
1963
|
```
|
1538
1964
|
|
1965
|
+
## Query Scopes
|
1966
|
+
This feature is a small subset of the [ActiveRecord named scopes](http://guides.rubyonrails.org/active_record_querying.html#scopes) feature. Scoping allows you to specify commonly-used queries which can be referenced as class method calls and are chainable with other scopes. You can use every `Parse::Query` method previously covered such as `where`, `includes` and `limit`.
|
1967
|
+
|
1968
|
+
```ruby
|
1969
|
+
|
1970
|
+
class Article < Parse::Object
|
1971
|
+
property :published, :boolean
|
1972
|
+
scope :published, -> { query(published: true) }
|
1973
|
+
end
|
1974
|
+
```
|
1975
|
+
|
1976
|
+
This is the same as defining your own class method for the query.
|
1977
|
+
|
1978
|
+
```ruby
|
1979
|
+
class Article < Parse::Object
|
1980
|
+
def self.published
|
1981
|
+
query(published: true)
|
1982
|
+
end
|
1983
|
+
end
|
1984
|
+
```
|
1985
|
+
|
1986
|
+
You can also chain scopes and pass parameters. In addition, boolean and enumerated properties have automatically generated scopes for you to use.
|
1987
|
+
|
1988
|
+
```ruby
|
1989
|
+
|
1990
|
+
class Article < Parse::Object
|
1991
|
+
scope :published, -> { query(published: true) }
|
1992
|
+
|
1993
|
+
property :comment_count, :integer
|
1994
|
+
property :category
|
1995
|
+
property :approved, :boolean
|
1996
|
+
|
1997
|
+
scope :published_and_commented, -> { published.where :comment_count.gt => 0 }
|
1998
|
+
scope :popular_topics, ->(name) { published_and_commented.where category: name }
|
1999
|
+
end
|
2000
|
+
|
2001
|
+
# simple scope
|
2002
|
+
Article.published # => where published is true
|
2003
|
+
|
2004
|
+
# chained scope
|
2005
|
+
Article.published_and_commented # published is true and comment_count > 0
|
2006
|
+
|
2007
|
+
# scope with parameters
|
2008
|
+
Article.popular_topic("music") # => popular music articles
|
2009
|
+
# equivalent: where(published: true, :comment_count.gt => 0, category: name)
|
2010
|
+
|
2011
|
+
# automatically generated scope
|
2012
|
+
Article.approved(category: "tour") # => where approved: true, category: 'tour'
|
2013
|
+
|
2014
|
+
```
|
2015
|
+
|
2016
|
+
If you would like to turn off automatic scope generation for property types, set the option `:scope` to false when declaring the property.
|
2017
|
+
|
1539
2018
|
## Calling Cloud Code Functions
|
1540
2019
|
You can call on your defined Cloud Code functions using the `call_function()` method. The result will be `nil` in case of errors or the value of the `result` field in the Parse response.
|
1541
2020
|
|
@@ -1562,8 +2041,8 @@ You can trigger background jobs that you have configured in your Parse applicati
|
|
1562
2041
|
response.result unless response.error?
|
1563
2042
|
```
|
1564
2043
|
|
1565
|
-
## Model Callbacks
|
1566
|
-
All `Parse::Object` subclasses extend [`ActiveModel::Callbacks`](http://api.rubyonrails.org/classes/ActiveModel/Callbacks.html) for `#save` and `#destroy` operations. You can setup internal hooks for `before
|
2044
|
+
## Active Model Callbacks
|
2045
|
+
All `Parse::Object` subclasses extend [`ActiveModel::Callbacks`](http://api.rubyonrails.org/classes/ActiveModel/Callbacks.html) for `#save` and `#destroy` operations. You can setup internal hooks for `before` and `after`.
|
1567
2046
|
|
1568
2047
|
```ruby
|
1569
2048
|
|
@@ -1575,6 +2054,10 @@ class Song < Parse::Object
|
|
1575
2054
|
acl.everyone(true, false) if new?
|
1576
2055
|
end
|
1577
2056
|
|
2057
|
+
after_create do
|
2058
|
+
puts "New object successfully saved."
|
2059
|
+
end
|
2060
|
+
|
1578
2061
|
end
|
1579
2062
|
|
1580
2063
|
song = Song.new name: "my title"
|
@@ -1584,6 +2067,8 @@ puts song.name # 'My Title'
|
|
1584
2067
|
|
1585
2068
|
```
|
1586
2069
|
|
2070
|
+
There are also a special `:create` callback. A `before_create` will be called whenever a unsaved object will be saved, and `after_create` will be called when a previously unsaved object successfully saved for the first time.
|
2071
|
+
|
1587
2072
|
## Schema Upgrades and Migrations
|
1588
2073
|
You may change your local Parse ruby classes by adding new properties. To easily propagate the changes to your Parse application (MongoDB), you can call `auto_upgrade!` on the class to perform an non-destructive additive schema change. This will create the new columns in Parse for the properties you have defined in your models. Parse Stack will calculate the changes and only modify the tables which need new columns to be added. *It will not destroy columns or data*
|
1589
2074
|
|
@@ -1646,7 +2131,7 @@ end
|
|
1646
2131
|
# Parse::Webhooks.route :function, :myFunc, MyClass.method(:my_func)
|
1647
2132
|
```
|
1648
2133
|
|
1649
|
-
If you have registered this webhook (see instructions below), you should be able to test it out by running curl using the command below.
|
2134
|
+
If you have registered this webhook (see instructions below), you should be able to test it out by running curl using the command below.
|
1650
2135
|
|
1651
2136
|
```bash
|
1652
2137
|
curl -X POST \
|
@@ -1816,8 +2301,8 @@ If you are already have setup a client that is being used by your defined models
|
|
1816
2301
|
client = Song.client
|
1817
2302
|
|
1818
2303
|
# you can also have multiple clients
|
1819
|
-
client = Parse::Client.
|
1820
|
-
client = Parse::Client.
|
2304
|
+
client = Parse::Client.client #default client session
|
2305
|
+
client = Parse::Client.client(:other_session)
|
1821
2306
|
|
1822
2307
|
```
|
1823
2308
|
|
@@ -1843,7 +2328,7 @@ same_user = Parse::User.first # cached result
|
|
1843
2328
|
|
1844
2329
|
# you may clear the cache at any time
|
1845
2330
|
# clear the cache for the default session
|
1846
|
-
Parse::Client.
|
2331
|
+
Parse::Client.client.clear_cache!
|
1847
2332
|
|
1848
2333
|
# or through the client accessor of a model
|
1849
2334
|
Song.client.clear_cache!
|