parse-stack 1.7.0 → 1.7.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 +18 -0
- data/Gemfile.lock +11 -11
- data/README.md +81 -4
- data/bin/console +3 -2
- data/lib/parse/client/body_builder.rb +9 -6
- data/lib/parse/model/associations/has_many.rb +34 -6
- data/lib/parse/model/classes/installation.rb +36 -5
- data/lib/parse/model/classes/product.rb +15 -0
- data/lib/parse/model/classes/role.rb +15 -5
- data/lib/parse/model/classes/session.rb +25 -0
- data/lib/parse/model/classes/user.rb +22 -0
- data/lib/parse/model/core/actions.rb +14 -50
- data/lib/parse/model/core/properties.rb +34 -11
- data/lib/parse/model/core/querying.rb +65 -0
- data/lib/parse/model/core/schema.rb +2 -0
- data/lib/parse/model/geopoint.rb +1 -1
- data/lib/parse/model/object.rb +22 -3
- data/lib/parse/model/time_zone.rb +143 -0
- data/lib/parse/stack.rb +0 -2
- data/lib/parse/stack/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3557dd1c97f0f227d848732708916d3428563d22
|
4
|
+
data.tar.gz: b71256968073f1318cb5c88665c551e6e6f7381c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 78d5ee17c4c2a9b8488ef6e37a8e6eeedf82e195b4369423e2694a784c4ff8cd41ba73d945cbdaa863ec094f6b1a1f4df5b26a6b8e28ae56401dfaf988b138d1
|
7
|
+
data.tar.gz: 86e596121613d31532aee9cd67fa8e5691ccbdcba7de01f11cfc0e3731b61e43cfde29a91137665e537aa448897af37dc21ea1b83bdc7908d7617065de1a8b93
|
data/Changes.md
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
## Parse-Stack Changelog
|
2
2
|
|
3
|
+
### 1.7.1
|
4
|
+
- NEW: `:timezone` datatype that maps to `Parse::TimeZone` (which mimics `ActiveSupport::TimeZone`)
|
5
|
+
- NEW: Installation `:time_zone` field is now a `Parse::TimeZone` instance.
|
6
|
+
- Any properties named `time_zone` or `timezone` with a string data type set will be converted to use `Parse::TimeZone` as the data class.
|
7
|
+
- FIXED: Fixes issues with HTTP Method Override for long url queries.
|
8
|
+
- FIXED: Fixes issue with Parse::Object.each method signature.
|
9
|
+
- FIXED: Removed `:id` from the Parse::Properties::TYPES list.
|
10
|
+
- FIXED: Parse::Object subclasses will not be allowed to redefine core properties.
|
11
|
+
- Parse::Object save_all() and each() methods raise ArgumentError for
|
12
|
+
invalid constraint arguments.
|
13
|
+
- Removes deprecated function `Role.apply_default_acls`. If you need the previous
|
14
|
+
behavior, you should set your own :before_save callback that modifies the role
|
15
|
+
object with the ACLs that you want or use the new `Role.set_default_acl`.
|
16
|
+
- Parse::Object.property returns true/false whether creating the property was successful.
|
17
|
+
- Parse::Session now has a `has_one` association to Installation through `:installation`
|
18
|
+
- Parse::User now has a `has_many` association to Sessions through `:active_sessions`
|
19
|
+
- Parse::Installation now has a `has_one` association to Session through `:session`
|
20
|
+
|
3
21
|
### 1.7.0
|
4
22
|
- NEW: You can use `set_default_acl` to set default ACLs for your subclasses.
|
5
23
|
- NEW: Support for `withinPolygon` query constraint.
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
parse-stack (1.7.
|
4
|
+
parse-stack (1.7.1)
|
5
5
|
active_model_serializers (>= 0.9, < 1)
|
6
6
|
activemodel (>= 4.2.1, < 6)
|
7
7
|
activesupport (>= 4.2.1, < 6)
|
@@ -14,15 +14,15 @@ PATH
|
|
14
14
|
GEM
|
15
15
|
remote: https://rubygems.org/
|
16
16
|
specs:
|
17
|
-
actionpack (5.1.
|
18
|
-
actionview (= 5.1.
|
19
|
-
activesupport (= 5.1.
|
17
|
+
actionpack (5.1.2)
|
18
|
+
actionview (= 5.1.2)
|
19
|
+
activesupport (= 5.1.2)
|
20
20
|
rack (~> 2.0)
|
21
21
|
rack-test (~> 0.6.3)
|
22
22
|
rails-dom-testing (~> 2.0)
|
23
23
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
24
|
-
actionview (5.1.
|
25
|
-
activesupport (= 5.1.
|
24
|
+
actionview (5.1.2)
|
25
|
+
activesupport (= 5.1.2)
|
26
26
|
builder (~> 3.1)
|
27
27
|
erubi (~> 1.4)
|
28
28
|
rails-dom-testing (~> 2.0)
|
@@ -32,9 +32,9 @@ GEM
|
|
32
32
|
activemodel (>= 4.1, < 6)
|
33
33
|
case_transform (>= 0.2)
|
34
34
|
jsonapi-renderer (>= 0.1.1.beta1, < 0.2)
|
35
|
-
activemodel (5.1.
|
36
|
-
activesupport (= 5.1.
|
37
|
-
activesupport (5.1.
|
35
|
+
activemodel (5.1.2)
|
36
|
+
activesupport (= 5.1.2)
|
37
|
+
activesupport (5.1.2)
|
38
38
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
39
39
|
i18n (~> 0.7)
|
40
40
|
minitest (~> 5.1)
|
@@ -50,7 +50,7 @@ GEM
|
|
50
50
|
daemons (1.2.4)
|
51
51
|
debug_inspector (0.0.3)
|
52
52
|
dotenv (2.2.1)
|
53
|
-
erubi (1.6.
|
53
|
+
erubi (1.6.1)
|
54
54
|
eventmachine (1.2.3)
|
55
55
|
faraday (0.12.1)
|
56
56
|
multipart-post (>= 1.2, < 3)
|
@@ -89,7 +89,7 @@ GEM
|
|
89
89
|
redcarpet (3.4.0)
|
90
90
|
redis (3.3.3)
|
91
91
|
slop (3.6.0)
|
92
|
-
thin (1.7.
|
92
|
+
thin (1.7.1)
|
93
93
|
daemons (~> 1.0, >= 1.0.9)
|
94
94
|
eventmachine (~> 1.0, >= 1.0.4)
|
95
95
|
rack (>= 1, < 3)
|
data/README.md
CHANGED
@@ -153,6 +153,7 @@ result = Parse.call_function :myFunctionName, {param: value}
|
|
153
153
|
- [Parse::GeoPoint](#parsegeopoint)
|
154
154
|
- [Calculating Distances between locations](#calculating-distances-between-locations)
|
155
155
|
- [Parse::Bytes](#parsebytes)
|
156
|
+
- [Parse::TimeZone](#parsetimezone)
|
156
157
|
- [Parse::ACL](#parseacl)
|
157
158
|
- [Parse::Session](#parsesession)
|
158
159
|
- [Parse::Installation](#parseinstallation)
|
@@ -517,7 +518,7 @@ This class manages dates in the special JSON format it requires for properties o
|
|
517
518
|
|
518
519
|
One important note with dates, is that `created_at` and `updated_at` columns do not follow this convention all the time. Depending on the Cloud Code SDK, they can be the Parse ISO hash date format or the `iso8601` string format. By default, these are serialized as `iso8601` when sent as responses to Parse for backwards compatibility with some clients. To use the Parse ISO hash format for these fields instead, set `Parse::Object.disable_serialized_string_date = true`.
|
519
520
|
|
520
|
-
### Parse::GeoPoint
|
521
|
+
### [Parse::GeoPoint](https://www.modernistik.com/gems/parse-stack/Parse/GeoPoint.html)
|
521
522
|
This class manages the GeoPoint data type that Parse provides to support geo-queries. To define a GeoPoint property, use the `:geopoint` data type. Please note that latitudes should not be between -90.0 and 90.0, and longitudes should be between -180.0 and 180.0.
|
522
523
|
|
523
524
|
```ruby
|
@@ -561,6 +562,29 @@ The `Bytes` data type represents the storage format for binary content in a Pars
|
|
561
562
|
decoded = bytes.decoded # same as Base64.decode64
|
562
563
|
```
|
563
564
|
|
565
|
+
### [Parse::TimeZone](https://www.modernistik.com/gems/parse-stack/Parse/TimeZone.html)
|
566
|
+
While Parse does not provide a native time zone data type, Parse-Stack provides a class to make it easier to manage time zone attributes, usually stored IANA string identifiers, with your ruby code. This is done by utilizing the features provided by [`ActiveSupport::TimeZone`](http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html). In addition to setting a column as a time zone field, we also add special validations to verify it is of the right IANA identifier.
|
567
|
+
|
568
|
+
```ruby
|
569
|
+
class Event < Parse::Object
|
570
|
+
# an event occurs in a time zone.
|
571
|
+
property :time_zone, :timezone, default: 'America/Los_Angeles'
|
572
|
+
end
|
573
|
+
|
574
|
+
event = Event.new
|
575
|
+
event.time_zone.name # => 'America/Los_Angeles'
|
576
|
+
event.time_zone.valid? # => true
|
577
|
+
|
578
|
+
event.time_zone.zone # => ActiveSupport::TimeZone
|
579
|
+
event.time_zone.formatted_offset # => "-08:00"
|
580
|
+
|
581
|
+
event.time_zone = 'Europe/Paris'
|
582
|
+
event.time_zone.formatted_offset # => +01:00"
|
583
|
+
|
584
|
+
event.time_zone = 'Galaxy/Andromeda'
|
585
|
+
event.time_zone.valid? # => false
|
586
|
+
```
|
587
|
+
|
564
588
|
### [Parse::ACL](https://www.modernistik.com/gems/parse-stack/Parse/ACL.html)
|
565
589
|
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.
|
566
590
|
The value of each key in the hash is a `Parse::ACL::Permission` object which defines the boolean permission state for `read` and `write`.
|
@@ -771,6 +795,7 @@ Properties are considered a literal-type of association. This means that a defin
|
|
771
795
|
- **:float** - a floating numeric value. Will also generate atomic `_increment!` helper method.
|
772
796
|
- **:boolean** (alias **:bool**) - true/false value. This will also generate a class scope helper. See [Query Scopes](#query-scopes).
|
773
797
|
- **:date** - a Parse date type. See [Parse::Date](#parsedate).
|
798
|
+
- **:timezone** - a time zone object. See [Parse::TimeZone](#parsetimezone).
|
774
799
|
- **: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).
|
775
800
|
- **:file** - a Parse file type. See [Parse::File](#parsefile).
|
776
801
|
- **:geopoint** - a GeoPoint type. See [Parse::GeoPoint](#parsegeopoint).
|
@@ -810,7 +835,7 @@ class Post < Parse::Object
|
|
810
835
|
# a list using
|
811
836
|
property :tags, :array
|
812
837
|
|
813
|
-
# string column as
|
838
|
+
# string column as enumerated type. see :enum
|
814
839
|
property :status, enum: [:active, :archived]
|
815
840
|
|
816
841
|
# Maps to "featuredImage" column representing a File.
|
@@ -821,6 +846,9 @@ class Post < Parse::Object
|
|
821
846
|
# Support bytes
|
822
847
|
property :data, :bytes
|
823
848
|
|
849
|
+
# A field that contains time zone information (ex. 'America/Los_Angeles')
|
850
|
+
property :time_zone, :timezone
|
851
|
+
|
824
852
|
# store SEO information. Make sure we map it to the column
|
825
853
|
# "SEO", otherwise it would have implicitly used "seo"
|
826
854
|
# as the remote column name
|
@@ -1264,6 +1292,38 @@ band.op_destroy!("category") # { __op: :Delete }
|
|
1264
1292
|
|
1265
1293
|
```
|
1266
1294
|
|
1295
|
+
You can also perform queries against class entities to find related objects. Assume
|
1296
|
+
that users can like a band. The `Band` class can have a `likes` column that is
|
1297
|
+
a Parse relation to the `Parse::User` class containing the users who have liked a
|
1298
|
+
specific band.
|
1299
|
+
|
1300
|
+
```ruby
|
1301
|
+
# assume the schema
|
1302
|
+
class Band < Parse::Object
|
1303
|
+
# likes is a Parse relation column of user objects.
|
1304
|
+
has_many :likes, through: :relation, as: :user
|
1305
|
+
end
|
1306
|
+
```
|
1307
|
+
|
1308
|
+
You can now find all `Parse::User` records who have liked a specific band. *In the
|
1309
|
+
example below, the `:likes` key refers to the `likes` column defined in the `Band`
|
1310
|
+
collection which contains the set of user records.*
|
1311
|
+
|
1312
|
+
```ruby
|
1313
|
+
band = Band.first # get a band
|
1314
|
+
# find all users who have liked this band, where :likes is a column
|
1315
|
+
# in the Band collection - NOT in the User collection.
|
1316
|
+
users = Parse::User.all :likes.related_to => band
|
1317
|
+
```
|
1318
|
+
You can also find all bands that a specific user has liked.
|
1319
|
+
|
1320
|
+
```ruby
|
1321
|
+
user = Parse::User.first
|
1322
|
+
# find all bands where this user is contained in the `likes` Parse relation column
|
1323
|
+
# of the Band collection
|
1324
|
+
bands_liked_by_user = Band.all :likes => user
|
1325
|
+
```
|
1326
|
+
|
1267
1327
|
##### Options
|
1268
1328
|
Options for `has_many` are the same as the `belongs_to` counterpart with support for `:required`, `:as` and `:field`. It has these additional options.
|
1269
1329
|
|
@@ -2044,11 +2104,28 @@ Equivalent to the `$relatedTo` Parse query operation. If you want to retrieve ob
|
|
2044
2104
|
```ruby
|
2045
2105
|
q.where :field.related_to => pointer
|
2046
2106
|
q.where :field.rel => pointer # alias
|
2107
|
+
```
|
2108
|
+
|
2109
|
+
In the example below, imagine you have a `Post` collection that has a Parse relation column `likes`
|
2110
|
+
which has the set of users who have liked a certain post. You would use the `Parse::Users` class to query
|
2111
|
+
against the `post` record of interest against the `likes` column of the `Post` collection.
|
2112
|
+
|
2113
|
+
```ruby
|
2114
|
+
# assume Post class definition
|
2115
|
+
class Post < Parse::Object
|
2116
|
+
# Parse relation to Parse::User records who've liked a post
|
2117
|
+
has_many :likes, through: :relation, as: :user
|
2118
|
+
end
|
2047
2119
|
|
2048
|
-
# example
|
2049
|
-
# find all Users who have liked this post object
|
2050
2120
|
post = Post.first
|
2121
|
+
# find all Users who have liked this post object,
|
2122
|
+
# where likes is a column on the Post class.
|
2051
2123
|
users = Parse::User.all :likes.rel => post
|
2124
|
+
|
2125
|
+
# or find posts that a certain user has liked
|
2126
|
+
user = Parse::User.first
|
2127
|
+
# likes is a Parse relation in the Post collection that contains User records
|
2128
|
+
liked_posts_by_user = Post.all :likes => user
|
2052
2129
|
```
|
2053
2130
|
|
2054
2131
|
### Compound Queries
|
data/bin/console
CHANGED
@@ -22,7 +22,7 @@ module Parse
|
|
22
22
|
def self.logging=(value)
|
23
23
|
Parse::Middleware::BodyBuilder.logging = value
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
# Namespace for Parse-Stack related middleware.
|
27
27
|
module Middleware
|
28
28
|
# This middleware takes an incoming Parse response, after an outgoing request,
|
@@ -30,8 +30,9 @@ module Parse
|
|
30
30
|
class BodyBuilder < Faraday::Middleware
|
31
31
|
include Parse::Protocol
|
32
32
|
# Header sent when a GET requests exceeds the limit.
|
33
|
-
|
34
|
-
|
33
|
+
HTTP_METHOD_OVERRIDE = 'X-Http-Method-Override'
|
34
|
+
# Maximum url length for most server requests before HTTP Method Override is used.
|
35
|
+
MAX_URL_LENGTH = 2_000.freeze
|
35
36
|
class << self
|
36
37
|
# Allows logging. Set to `true` to enable logging, `false` to disable.
|
37
38
|
# You may specify `:debug` for additional verbosity.
|
@@ -51,10 +52,12 @@ module Parse
|
|
51
52
|
# (which is most likely a very complicated query), we need to override the request method
|
52
53
|
# to be POST instead of GET and send the query parameters in the body of the POST request.
|
53
54
|
# The standard maximum POST request (which is a server setting), is usually set to 20MBs
|
54
|
-
if env[:method] == :get && env[:url].to_s.length
|
55
|
-
env[:request_headers][
|
55
|
+
if env[:method] == :get && env[:url].to_s.length >= MAX_URL_LENGTH
|
56
|
+
env[:request_headers][HTTP_METHOD_OVERRIDE] = 'GET'
|
56
57
|
env[:request_headers][CONTENT_TYPE] = 'application/x-www-form-urlencoded'
|
57
|
-
|
58
|
+
# parse-sever looks for method overrides in the body under the `_method` param.
|
59
|
+
# so we will add it to the query string, which will now go into the body.
|
60
|
+
env[:body] = "_method=GET&" + env[:url].query
|
58
61
|
env[:url].query = nil
|
59
62
|
#override
|
60
63
|
env[:method] = :post
|
@@ -95,7 +95,7 @@ module Parse
|
|
95
95
|
# # => [artist's songs created in the last 6 months]
|
96
96
|
#
|
97
97
|
# You may also call property methods in your scopes related to the instance.
|
98
|
-
# You also have access to the instance object for the local class
|
98
|
+
# *Note:* You also have access to the instance object for the local class
|
99
99
|
# through a special "*i*" method in the scope.
|
100
100
|
#
|
101
101
|
# class Concert
|
@@ -220,10 +220,9 @@ module Parse
|
|
220
220
|
# fans = band.fans.all :location.near => downtown
|
221
221
|
#
|
222
222
|
# You can perform atomic additions and removals of objects from `has_many`
|
223
|
-
# relations. Parse allows this by providing a specific atomic operation
|
224
|
-
# request.
|
225
|
-
#
|
226
|
-
# and not on your instance object.
|
223
|
+
# relations using the methods below. Parse allows this by providing a specific atomic operation
|
224
|
+
# request. The operation is performed directly on Parse server
|
225
|
+
# and *NOT* on your instance object.
|
227
226
|
#
|
228
227
|
# # atomically add/remove
|
229
228
|
# band.artists.add! objects # { __op: :AddUnique }
|
@@ -240,6 +239,35 @@ module Parse
|
|
240
239
|
# # this should set it as `undefined`.
|
241
240
|
# band.op_destroy!("category") # { __op: :Delete }
|
242
241
|
#
|
242
|
+
# You can also perform queries against class entities to find related objects. Assume
|
243
|
+
# that users can like a band. The `Band` class can have a `likes` column that is
|
244
|
+
# a Parse relation to the {Parse::User} class containing the users who have liked a
|
245
|
+
# specific band.
|
246
|
+
#
|
247
|
+
#
|
248
|
+
# class Band < Parse::Object
|
249
|
+
# # likes is a Parse relation column of user objects.
|
250
|
+
# has_many :likes, through: :relation, as: :user
|
251
|
+
# end
|
252
|
+
#
|
253
|
+
# You can now find all {Parse::User} records who have liked a specific band. In the
|
254
|
+
# example below, the `:likes` key refers to the `likes` column defined in the `Band`
|
255
|
+
# collection which contains the set of user records.
|
256
|
+
#
|
257
|
+
# band = Band.first # get a band
|
258
|
+
#
|
259
|
+
# # find all users who have liked this band, where :likes is a column
|
260
|
+
# # in the Band collection - NOT in the User collection.
|
261
|
+
# users = Parse::User.all :likes.related_to => band
|
262
|
+
#
|
263
|
+
# You can also find all bands that a specific user has liked.
|
264
|
+
#
|
265
|
+
# user = Parse::User.first
|
266
|
+
#
|
267
|
+
# # find all bands where this user
|
268
|
+
# # is in the `likes` column of the Band collection
|
269
|
+
# bands_liked_by_user = Band.all :likes => user
|
270
|
+
#
|
243
271
|
module HasMany
|
244
272
|
|
245
273
|
# @!attribute [rw] self.relations
|
@@ -367,7 +395,7 @@ module Parse
|
|
367
395
|
end
|
368
396
|
|
369
397
|
Parse::Query.apply_auto_introspection!(query)
|
370
|
-
|
398
|
+
|
371
399
|
return query if block.nil?
|
372
400
|
query.results(&block)
|
373
401
|
end
|
@@ -10,7 +10,31 @@ module Parse
|
|
10
10
|
# An installation object represents an instance of your app being installed
|
11
11
|
# on a device. These objects are used to store subscription data for
|
12
12
|
# installations which have subscribed to one or more push notification channels.
|
13
|
+
#
|
14
|
+
# The default schema for {Installation} is as follows:
|
15
|
+
#
|
16
|
+
# class Parse::Installation < Parse::Object
|
17
|
+
# # See Parse::Object for inherited properties...
|
18
|
+
#
|
19
|
+
# property :gcm_sender_id, field: :GCMSenderId
|
20
|
+
# property :app_identifier
|
21
|
+
# property :app_name
|
22
|
+
# property :app_version
|
23
|
+
# property :badge, :integer
|
24
|
+
# property :channels, :array
|
25
|
+
# property :device_token
|
26
|
+
# property :device_token_last_modified, :integer
|
27
|
+
# property :device_type, enum: [:ios, :android, :winrt, :winphone, :dotnet]
|
28
|
+
# property :installation_id
|
29
|
+
# property :locale_identifier
|
30
|
+
# property :parse_version
|
31
|
+
# property :push_type
|
32
|
+
# property :time_zone, :timezone
|
33
|
+
#
|
34
|
+
# has_one :session, ->{ where(installation_id: i.installation_id) }, scope_only: true
|
35
|
+
# end
|
13
36
|
# @see Push
|
37
|
+
# @see Parse::Object
|
14
38
|
class Installation < Parse::Object
|
15
39
|
|
16
40
|
parse_class Parse::Model::CLASS_INSTALLATION
|
@@ -22,7 +46,7 @@ module Parse
|
|
22
46
|
# provider. If you set this field, then you must set the GCM API key
|
23
47
|
# corresponding to this GCM sender ID in your Parse application’s push settings.
|
24
48
|
# @return [String]
|
25
|
-
property :gcm_sender_id,
|
49
|
+
property :gcm_sender_id, field: :GCMSenderId
|
26
50
|
|
27
51
|
# @!attribute app_identifier
|
28
52
|
# A unique identifier for this installation’s client application. In iOS, this is the Bundle Identifier.
|
@@ -92,10 +116,17 @@ module Parse
|
|
92
116
|
property :push_type
|
93
117
|
|
94
118
|
# @!attribute time_zone
|
95
|
-
# The current time zone where the target device is located. This should be an IANA time zone identifier
|
96
|
-
#
|
97
|
-
|
98
|
-
|
119
|
+
# The current time zone where the target device is located. This should be an IANA time zone identifier
|
120
|
+
# or a {Parse::TimeZone} instance.
|
121
|
+
# @return [Parse::TimeZone]
|
122
|
+
property :time_zone, :timezone
|
123
|
+
|
124
|
+
# @!attribute session
|
125
|
+
# Returns the corresponding {Parse::Session} associated with this installation, if any exists.
|
126
|
+
# This is implemented as a has_one association to the Session class using the {installation_id}.
|
127
|
+
# @version 1.7.1
|
128
|
+
# @return [Parse::Session] The associated {Parse::Session} that might be tied to this installation
|
129
|
+
has_one :session, ->{ where(installation_id: i.installation_id) }, scope_only: true
|
99
130
|
end
|
100
131
|
|
101
132
|
end
|
@@ -6,6 +6,21 @@ require_relative 'user'
|
|
6
6
|
module Parse
|
7
7
|
# This class represents the data and columns contained in the standard Parse `_Product` collection.
|
8
8
|
# These records are usually used when implementing in-app purchases in mobile applications.
|
9
|
+
#
|
10
|
+
# The default schema for {Product} is as follows:
|
11
|
+
#
|
12
|
+
# class Parse::Product < Parse::Object
|
13
|
+
# # See Parse::Object for inherited properties...
|
14
|
+
#
|
15
|
+
# property :download, :file
|
16
|
+
# property :icon, :file, required: true
|
17
|
+
# property :order, :integer, required: true
|
18
|
+
# property :subtitle, required: true
|
19
|
+
# property :title, required: true
|
20
|
+
# property :product_identifier, required: true
|
21
|
+
#
|
22
|
+
# end
|
23
|
+
# @see Parse::Object
|
9
24
|
class Product < Parse::Object
|
10
25
|
|
11
26
|
parse_class Parse::Model::CLASS_PRODUCT
|
@@ -7,6 +7,21 @@ module Parse
|
|
7
7
|
# Roles allow the an application to group a set of {Parse::User} records with the same set of
|
8
8
|
# permissions, so that specific records in the database can have {Parse::ACL}s related to a role
|
9
9
|
# than trying to add all the users in a group.
|
10
|
+
#
|
11
|
+
# The default schema for {Role} is as follows:
|
12
|
+
#
|
13
|
+
# class Parse::Role < Parse::Object
|
14
|
+
# # See Parse::Object for inherited properties...
|
15
|
+
#
|
16
|
+
# property :name
|
17
|
+
#
|
18
|
+
# # A role may have child roles.
|
19
|
+
# has_many :roles, through: :relation
|
20
|
+
#
|
21
|
+
# # The set of users who belong to this role.
|
22
|
+
# has_many :users, through: :relation
|
23
|
+
# end
|
24
|
+
# @see Parse::Object
|
10
25
|
class Role < Parse::Object
|
11
26
|
|
12
27
|
parse_class Parse::Model::CLASS_ROLE
|
@@ -22,11 +37,6 @@ module Parse
|
|
22
37
|
# This attribute is mapped as a `has_many` Parse relation association with the {Parse::User} class.
|
23
38
|
# @return [RelationCollectionProxy<User>] a Parse relation of users belonging to this role.
|
24
39
|
has_many :users, through: :relation
|
25
|
-
|
26
|
-
# A method to set acls for Roles to public read, and deny public write.
|
27
|
-
def apply_default_acls
|
28
|
-
acl.everyone true, false
|
29
|
-
end
|
30
40
|
|
31
41
|
end
|
32
42
|
|
@@ -9,6 +9,24 @@ module Parse
|
|
9
9
|
# a session token is generated. You may use a known active session token to find the corresponding
|
10
10
|
# user for that session. Deleting a Session record (and session token), effectively logs out the user, when making Parse requests
|
11
11
|
# on behalf of the user using the session token.
|
12
|
+
#
|
13
|
+
# The default schema for the {Session} class is as follows:
|
14
|
+
# class Parse::Session < Parse::Object
|
15
|
+
# # See Parse::Object for inherited properties...
|
16
|
+
#
|
17
|
+
# property :session_token
|
18
|
+
# property :created_with, :object
|
19
|
+
# property :expires_at, :date
|
20
|
+
# property :installation_id
|
21
|
+
# property :restricted, :boolean
|
22
|
+
#
|
23
|
+
# belongs_to :user
|
24
|
+
#
|
25
|
+
# # Installation where the installation_id matches.
|
26
|
+
# has_one :installation, ->{ where(installation_id: i.installation_id) }, scope_only: true
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# @see Parse::Object
|
12
30
|
class Session < Parse::Object
|
13
31
|
|
14
32
|
parse_class Parse::Model::CLASS_SESSION
|
@@ -40,6 +58,13 @@ module Parse
|
|
40
58
|
# @see User
|
41
59
|
belongs_to :user
|
42
60
|
|
61
|
+
# @!attribute [r] installation
|
62
|
+
# Returns the {Parse::Installation} where the sessions installation_id field matches the installation_id field
|
63
|
+
# in the {Parse::Installation} collection. This is implemented as a has_one scope.
|
64
|
+
# @version 1.7.1
|
65
|
+
# @return [Parse::Installation] The associated {Parse::Installation} tied to this session
|
66
|
+
has_one :installation, ->{ where(installation_id: i.installation_id) }, scope_only: true
|
67
|
+
|
43
68
|
# Return the Session record for this session token.
|
44
69
|
# @param token [String] the session token
|
45
70
|
# @return [Session] the session for this token, otherwise nil.
|
@@ -24,6 +24,19 @@ module Parse
|
|
24
24
|
# All users need to have a username and a password, with email being optional but globally unique if set.
|
25
25
|
# You may add additional properties by redeclaring the class to match your specific schema.
|
26
26
|
#
|
27
|
+
# The default schema for the {User} class is as follows:
|
28
|
+
#
|
29
|
+
# class Parse::User < Parse::Object
|
30
|
+
# # See Parse::Object for inherited properties...
|
31
|
+
#
|
32
|
+
# property :auth_data, :object
|
33
|
+
# property :username
|
34
|
+
# property :password
|
35
|
+
# property :email
|
36
|
+
#
|
37
|
+
# has_many :active_sessions, as: :session
|
38
|
+
# end
|
39
|
+
#
|
27
40
|
# *Signup*
|
28
41
|
#
|
29
42
|
# You can signup new users in two ways. You can either use a class method
|
@@ -122,6 +135,7 @@ module Parse
|
|
122
135
|
# # Unlinks this user's Facebook account from Parse
|
123
136
|
# user.unlink_auth_data! :facebook
|
124
137
|
#
|
138
|
+
# @see Parse::Object
|
125
139
|
class User < Parse::Object
|
126
140
|
|
127
141
|
parse_class Parse::Model::CLASS_USER
|
@@ -153,6 +167,14 @@ module Parse
|
|
153
167
|
# @return [String] The user's username.
|
154
168
|
property :username
|
155
169
|
|
170
|
+
# @!attribute active_sessions
|
171
|
+
# A has_many relationship to all {Parse::Session} instances for this user. This
|
172
|
+
# will query the _Session collection for all sessions which have this user in it's `user`
|
173
|
+
# column.
|
174
|
+
# @version 1.7.1
|
175
|
+
# @return [Array<Parse::Session>] A list of active Parse::Session objects.
|
176
|
+
has_many :active_sessions, as: :session
|
177
|
+
|
156
178
|
before_save do
|
157
179
|
# You cannot specify user ACLs.
|
158
180
|
self.clear_attribute_change!(:acl)
|
@@ -169,58 +169,12 @@ module Parse
|
|
169
169
|
obj
|
170
170
|
end
|
171
171
|
|
172
|
-
# This methods allow you to efficiently iterate over all the records in the collection
|
173
|
-
# (lower memory cost) at a minor cost of performance. This method utilizes
|
174
|
-
# the `created_at` field of Parse records to order and iterate over all records,
|
175
|
-
# therefore you should not use this method if you want to perform a query
|
176
|
-
# with constraints against `created_at` field or need specific type of ordering.
|
177
|
-
# @param constraints [Hash] a set of query constraints.
|
178
|
-
# @yield a block which will iterate through each matching record.
|
179
|
-
# @example
|
180
|
-
#
|
181
|
-
# post = Post.first
|
182
|
-
# # iterate over all comments matching conditions
|
183
|
-
# Comments.each( post: post) do |comment|
|
184
|
-
# # ...
|
185
|
-
# end
|
186
|
-
# @return [Parse::Object] the last Parse::Object record processed.
|
187
|
-
def each(constraints = {}, **opts, &block)
|
188
|
-
#anchor_date = opts[:anchor_date] || Parse::Date.now
|
189
|
-
batch_size = 250
|
190
|
-
start_cursor = first( order: :created_at.asc, keys: :created_at )
|
191
|
-
constraints.merge! cache: false, limit: batch_size, order: :created_at.asc
|
192
|
-
all_query = query(constraints)
|
193
|
-
cursor = start_cursor
|
194
|
-
# the exclusion set is a set of ids not to include the next query.
|
195
|
-
exclusion_set = []
|
196
|
-
loop do
|
197
|
-
_q = query(constraints.dup)
|
198
|
-
_q.where(:created_at.on_or_after => cursor.created_at)
|
199
|
-
# set of ids not to include in the next query. non-performant, but accurate.
|
200
|
-
_q.where(:id.nin => exclusion_set) unless exclusion_set.empty?
|
201
|
-
results = _q.results # get results
|
202
|
-
|
203
|
-
break cursor if results.empty? # break if no results
|
204
|
-
results.each(&block)
|
205
|
-
next_cursor = results.last
|
206
|
-
# break if we got less than the maximum requested
|
207
|
-
break next_cursor if results.count < batch_size
|
208
|
-
# break if the next object is the same as the current object.
|
209
|
-
break next_cursor if cursor.id == next_cursor.id
|
210
|
-
# The exclusion set is used in the case where multiple records have the exact
|
211
|
-
# same created_at date (down to the microsecond). This prevents getting the same
|
212
|
-
# record in the next query request.
|
213
|
-
exclusion_set = results.select { |r| r.created_at == next_cursor.created_at }.map(&:id)
|
214
|
-
results = nil
|
215
|
-
cursor = next_cursor
|
216
|
-
end
|
217
|
-
|
218
|
-
end
|
219
|
-
|
220
172
|
# Auto save all objects matching the query constraints. This method is
|
221
173
|
# meant to be used with a block. Any objects that are modified in the block
|
222
174
|
# will be batched for a save operation. This uses the `updated_at` field to
|
223
175
|
# continue to query for all matching objects that have not been updated.
|
176
|
+
# If you need to use `:updated_at` in your constraints, consider using {Parse::Core::Querying#all} or
|
177
|
+
# {Parse::Core::Querying#each}
|
224
178
|
# @param constraints [Hash] a set of query constraints.
|
225
179
|
# @yield a block which will iterate through each matching object.
|
226
180
|
# @example
|
@@ -230,8 +184,18 @@ module Parse
|
|
230
184
|
# # .. modify comment ...
|
231
185
|
# # it will automatically be saved
|
232
186
|
# end
|
187
|
+
# @note You cannot use *:updated_at* as a constraint.
|
233
188
|
# @return [Boolean] whether there were any errors.
|
234
189
|
def save_all(constraints = {})
|
190
|
+
invalid_constraints = constraints.keys.any? do |k|
|
191
|
+
(k == :updated_at || k == :updatedAt) ||
|
192
|
+
( k.is_a?(Parse::Operation) && (k.operand == :updated_at || k.operand == :updatedAt) )
|
193
|
+
end
|
194
|
+
if invalid_constraints
|
195
|
+
raise ArgumentError,
|
196
|
+
"[#{self}] Special method save_all() cannot be used with an :updated_at constraint."
|
197
|
+
end
|
198
|
+
|
235
199
|
force = false
|
236
200
|
batch_size = 250
|
237
201
|
iterator_block = nil
|
@@ -262,7 +226,7 @@ module Parse
|
|
262
226
|
|
263
227
|
# verify we didn't get duplicates fetches
|
264
228
|
if cursor.is_a?(Parse::Object) && results.any? { |x| x.id == cursor.id }
|
265
|
-
warn "[
|
229
|
+
warn "[#{self}.save_all] Unbounded update detected with id #{cursor.id}."
|
266
230
|
has_errors = true
|
267
231
|
break cursor
|
268
232
|
end
|
@@ -284,7 +248,7 @@ module Parse
|
|
284
248
|
update_query.where :updated_at.gte => cursor.updated_at
|
285
249
|
|
286
250
|
if cursor.updated_at.present? && cursor.updated_at > anchor_date
|
287
|
-
warn "[
|
251
|
+
warn "[#{self}.save_all] Reached anchor date #{anchor_date} < #{cursor.updated_at}"
|
288
252
|
break cursor
|
289
253
|
end
|
290
254
|
|
@@ -20,7 +20,7 @@ module Parse
|
|
20
20
|
# supported in Parse and mapping them between their remote names with their local ruby named attributes.
|
21
21
|
module Properties
|
22
22
|
# These are the base types supported by Parse.
|
23
|
-
TYPES = [:
|
23
|
+
TYPES = [:string, :relation, :integer, :float, :boolean, :date, :array, :file, :geopoint, :bytes, :object, :acl, :timezone].freeze
|
24
24
|
# These are the base mappings of the remote field name types.
|
25
25
|
BASE = {objectId: :string, createdAt: :date, updatedAt: :date, ACL: :acl}.freeze
|
26
26
|
# The list of properties that are part of all objects
|
@@ -28,8 +28,9 @@ module Parse
|
|
28
28
|
# Default hash map of local attribute name to remote column name
|
29
29
|
BASE_FIELD_MAP = {id: :objectId, created_at: :createdAt, updated_at: :updatedAt, acl: :ACL}.freeze
|
30
30
|
# The delete operation hash.
|
31
|
+
CORE_FIELDS = {id: :string, created_at: :date, updated_at: :date, acl: :acl}.freeze
|
32
|
+
# The delete operation hash.
|
31
33
|
DELETE_OP = {"__op"=>"Delete"}.freeze
|
32
|
-
|
33
34
|
# @!visibility private
|
34
35
|
def self.included(base)
|
35
36
|
base.extend(ClassMethods)
|
@@ -44,7 +45,8 @@ module Parse
|
|
44
45
|
# @param type [Symbol] a property type.
|
45
46
|
# @return [Hash] the defined fields for this Parse collection with their data type.
|
46
47
|
def fields(type = nil)
|
47
|
-
|
48
|
+
# if it's Parse::Object, then only use the initial set, otherwise add the other base fields.
|
49
|
+
@fields ||= (self == Parse::Object ? CORE_FIELDS : Parse::Object.fields).dup
|
48
50
|
if type.present?
|
49
51
|
type = type.to_sym
|
50
52
|
return @fields.select { |k,v| v == type }
|
@@ -107,10 +109,16 @@ module Parse
|
|
107
109
|
if data_type.is_a?(Hash)
|
108
110
|
opts.merge!(data_type)
|
109
111
|
data_type = :string
|
112
|
+
# future: automatically use :timezone datatype for timezone-like fields.
|
113
|
+
# when the data_type was not specifically set.
|
114
|
+
# data_type = :timezone if key == :time_zone || key == :timezone
|
110
115
|
end
|
111
116
|
|
117
|
+
data_type = :timezone if data_type == :string && (key == :time_zone || key == :timezone)
|
118
|
+
|
112
119
|
# allow :bool for :boolean
|
113
120
|
data_type = :boolean if data_type == :bool
|
121
|
+
data_type = :timezone if data_type == :time_zone
|
114
122
|
data_type = :geopoint if data_type == :geo_point
|
115
123
|
data_type = :integer if data_type == :int || data_type == :number
|
116
124
|
|
@@ -127,14 +135,16 @@ module Parse
|
|
127
135
|
#By default, the remote field name is a lower-first-camelcase version of the key
|
128
136
|
# it can be overriden by the :field parameter
|
129
137
|
parse_field = opts[:field].to_sym
|
130
|
-
if
|
131
|
-
|
132
|
-
|
138
|
+
# if this is a custom property that is already defined, OR it is a subclass trying to define a core property
|
139
|
+
# then warn and exit.
|
140
|
+
if (self.fields[key].present? && BASE_FIELD_MAP[key].nil?) || ( self < Parse::Object && BASE_FIELD_MAP.has_key?(key) )
|
141
|
+
warn "Property #{self}##{key} already defined with data type :#{data_type}. Will be ignored."
|
142
|
+
return false
|
133
143
|
end
|
134
144
|
# We keep the list of fields that are on the remote Parse store
|
135
|
-
if self.fields[parse_field].present?
|
145
|
+
if self.fields[parse_field].present? || ( self < Parse::Object && BASE.has_key?(parse_field) )
|
136
146
|
warn "Alias property #{self}##{parse_field} conflicts with previously defined property. Will be ignored."
|
137
|
-
return
|
147
|
+
return false
|
138
148
|
# raise ArgumentError
|
139
149
|
end
|
140
150
|
#dirty tracking. It is declared to use with ActiveModel DirtyTracking
|
@@ -159,6 +169,15 @@ module Parse
|
|
159
169
|
validates_presence_of key
|
160
170
|
end
|
161
171
|
|
172
|
+
# timezone datatypes are basically enums based on IANA time zone identifiers.
|
173
|
+
if data_type == :timezone
|
174
|
+
validates_each key do |record, attribute, value|
|
175
|
+
# Parse::TimeZone objects have a `valid?` method to determine if the timezone is valid.
|
176
|
+
unless value.nil? || value.valid?
|
177
|
+
record.errors.add(attribute, "field :#{attribute} must be a valid IANA time zone identifier.")
|
178
|
+
end
|
179
|
+
end # validates_each
|
180
|
+
end # data_type == :timezone
|
162
181
|
|
163
182
|
is_enum_type = opts[:enum].nil? == false
|
164
183
|
|
@@ -227,7 +246,9 @@ module Parse
|
|
227
246
|
end
|
228
247
|
end # unless scopes
|
229
248
|
|
230
|
-
end
|
249
|
+
end # if is enum
|
250
|
+
|
251
|
+
|
231
252
|
|
232
253
|
symbolize_value = opts[:symbolize]
|
233
254
|
|
@@ -391,7 +412,7 @@ module Parse
|
|
391
412
|
# if both the local name matches the calculated/provided remote column name, don't create
|
392
413
|
# an alias method since it is the same thing. Ex. attribute 'username' would probably have the
|
393
414
|
# remote column name also called 'username'.
|
394
|
-
return if parse_field == key
|
415
|
+
return true if parse_field == key
|
395
416
|
|
396
417
|
# we will now create the aliases, however if the method is already defined
|
397
418
|
# we warn the user unless the field is :objectId since we are in charge of that one.
|
@@ -405,7 +426,7 @@ module Parse
|
|
405
426
|
elsif parse_field.to_sym != :objectId
|
406
427
|
warn "Alias property method #{self}##{parse_field} already defined."
|
407
428
|
end
|
408
|
-
|
429
|
+
true
|
409
430
|
end # property
|
410
431
|
|
411
432
|
end #ClassMethods
|
@@ -572,6 +593,8 @@ module Parse
|
|
572
593
|
# pus "[Parse::Stack] Invalid date value '#{val}' assigned to #{self.class}##{key}, it should be a Parse::Date or DateTime."
|
573
594
|
# raise ValueError, "Invalid date value '#{val}' assigned to #{self.class}##{key}, it should be a Parse::Date or DateTime."
|
574
595
|
end
|
596
|
+
when :timezone
|
597
|
+
val = Parse::TimeZone.new(val) if val.present?
|
575
598
|
else
|
576
599
|
# You can provide a specific class instead of a symbol format
|
577
600
|
if data_type.respond_to?(:typecast)
|
@@ -128,6 +128,68 @@ module Parse
|
|
128
128
|
query.where(conditions)
|
129
129
|
end
|
130
130
|
|
131
|
+
# This methods allow you to efficiently iterate over all the records in the collection
|
132
|
+
# (lower memory cost) at a minor cost of performance. This method utilizes
|
133
|
+
# the `created_at` field of Parse records to order and iterate over *all* matching records,
|
134
|
+
# therefore you should not use this method if you want to perform a query
|
135
|
+
# with constraints against the `created_at` field or need specific type of ordering.
|
136
|
+
# If you need to use `:created_at` in your constraints, consider using {Parse::Core::Querying#all} or
|
137
|
+
# {Parse::Core::Actions::ClassMethods#save_all}
|
138
|
+
# @param constraints [Hash] a set of query constraints.
|
139
|
+
# @yield a block which will iterate through each matching record.
|
140
|
+
# @example
|
141
|
+
#
|
142
|
+
# post = Post.first
|
143
|
+
# # iterate over all comments matching conditions
|
144
|
+
# Comment.each(post: post) do |comment|
|
145
|
+
# # ...
|
146
|
+
# end
|
147
|
+
# @return [Parse::Object] the last Parse::Object record processed.
|
148
|
+
# @note You cannot use *:created_at* as a constraint.
|
149
|
+
# @raise ArgumentError if :created_at is detected in the constraints argument.
|
150
|
+
# @see Parse::Core::Querying.all
|
151
|
+
# @see Parse::Core::Actions.save_all
|
152
|
+
def each(constraints = {}, &block)
|
153
|
+
# verify we don't hvae created at as a constraint, otherwise this will not work
|
154
|
+
invalid_constraints = constraints.keys.any? do |k|
|
155
|
+
(k == :created_at || k == :createdAt) ||
|
156
|
+
( k.is_a?(Parse::Operation) && (k.operand == :created_at || k.operand == :createdAt) )
|
157
|
+
end
|
158
|
+
if invalid_constraints
|
159
|
+
raise ArgumentError, "[#{self.class}.each] Special method each()" \
|
160
|
+
"cannot be used with a :created_at constraint."
|
161
|
+
end
|
162
|
+
batch_size = 250
|
163
|
+
start_cursor = first( order: :created_at.asc, keys: :created_at )
|
164
|
+
constraints.merge! cache: false, limit: batch_size, order: :created_at.asc
|
165
|
+
all_query = query(constraints)
|
166
|
+
cursor = start_cursor
|
167
|
+
# the exclusion set is a set of ids not to include the next query.
|
168
|
+
exclusion_set = []
|
169
|
+
loop do
|
170
|
+
_q = query(constraints.dup)
|
171
|
+
_q.where(:created_at.on_or_after => cursor.created_at)
|
172
|
+
# set of ids not to include in the next query. non-performant, but accurate.
|
173
|
+
_q.where(:id.nin => exclusion_set) unless exclusion_set.empty?
|
174
|
+
results = _q.results # get results
|
175
|
+
|
176
|
+
break cursor if results.empty? # break if no results
|
177
|
+
results.each(&block)
|
178
|
+
next_cursor = results.last
|
179
|
+
# break if we got less than the maximum requested
|
180
|
+
break next_cursor if results.count < batch_size
|
181
|
+
# break if the next object is the same as the current object.
|
182
|
+
break next_cursor if cursor.id == next_cursor.id
|
183
|
+
# The exclusion set is used in the case where multiple records have the exact
|
184
|
+
# same created_at date (down to the microsecond). This prevents getting the same
|
185
|
+
# record in the next query request.
|
186
|
+
exclusion_set = results.select { |r| r.created_at == next_cursor.created_at }.map(&:id)
|
187
|
+
results = nil
|
188
|
+
cursor = next_cursor
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
131
193
|
# Fetch all matching objects in this collection matching the constraints.
|
132
194
|
# This will be the most common way when querying Parse objects for a subclass.
|
133
195
|
# When no block is passed, all objects are returned. Using a block is more memory
|
@@ -143,6 +205,9 @@ module Parse
|
|
143
205
|
# # ... do something with song..
|
144
206
|
# end
|
145
207
|
#
|
208
|
+
# @note This method will continually query for records by automatically
|
209
|
+
# incrementing the *:skip* parameter until no more results are returned
|
210
|
+
# by the server.
|
146
211
|
# @return [Array<Parse::Object>] an array of matching objects. If a block is passed,
|
147
212
|
# an empty array is returned.
|
148
213
|
def all(constraints = {limit: :max})
|
@@ -29,6 +29,8 @@ module Parse
|
|
29
29
|
result = { type: Parse::Model::TYPE_POINTER, targetClass: references[k] }
|
30
30
|
when :acl
|
31
31
|
result[:type] = Parse::Model::ACL
|
32
|
+
when :timezone, :time_zone
|
33
|
+
result[:type] = "String" # no TimeZone native in Parse
|
32
34
|
else
|
33
35
|
result[:type] = v.to_s.camelize
|
34
36
|
end
|
data/lib/parse/model/geopoint.rb
CHANGED
data/lib/parse/model/object.rb
CHANGED
@@ -18,6 +18,7 @@ require_relative 'geopoint'
|
|
18
18
|
require_relative 'file'
|
19
19
|
require_relative 'bytes'
|
20
20
|
require_relative 'date'
|
21
|
+
require_relative 'time_zone'
|
21
22
|
require_relative 'acl'
|
22
23
|
require_relative 'push'
|
23
24
|
require_relative 'core/actions'
|
@@ -69,9 +70,27 @@ module Parse
|
|
69
70
|
|
70
71
|
# This is the core class for all app specific Parse table subclasses. This class
|
71
72
|
# in herits from Parse::Pointer since an Object is a Parse::Pointer with additional fields,
|
72
|
-
# at a minimum, created_at, updated_at and ACLs.
|
73
|
-
#
|
74
|
-
#
|
73
|
+
# at a minimum, created_at, updated_at and ACLs. This class also handles all
|
74
|
+
# the relational types of associations in a Parse application and handles the main CRUD operations.
|
75
|
+
#
|
76
|
+
# As the parent class to all custom subclasses, this class provides the default property schema:
|
77
|
+
#
|
78
|
+
# class Parse::Object
|
79
|
+
# # All subclasses will inherit these properties by default.
|
80
|
+
#
|
81
|
+
# # the objectId column of a record.
|
82
|
+
# property :id, :string, field: :objectId
|
83
|
+
#
|
84
|
+
# # The the last updated date for a record (Parse::Date)
|
85
|
+
# property :updated_at, :date
|
86
|
+
#
|
87
|
+
# # The original creation date of a record (Parse::Date)
|
88
|
+
# property :created_at, :date
|
89
|
+
#
|
90
|
+
# # The Parse::ACL field
|
91
|
+
# property :acl, :acl, field: :ACL
|
92
|
+
#
|
93
|
+
# end
|
75
94
|
#
|
76
95
|
# Most Pointers and Object subclasses are treated the same. Therefore, defining a class Artist < Parse::Object
|
77
96
|
# that only has `id` set, will be treated as a pointer. Therefore a Parse::Object can be in a "pointer" state
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'active_support'
|
5
|
+
require 'active_support/values/time_zone'
|
6
|
+
require_relative "model"
|
7
|
+
|
8
|
+
module Parse
|
9
|
+
# This class a wrapper around ActiveSupport::TimeZone when using Parse columns that
|
10
|
+
# store IANA time zone identifiers (ex. Installation collection). Parse does not have a
|
11
|
+
# native time zone data type, but this class is provided to manage and perform timezone-like
|
12
|
+
# operation on those properties which you have marked as type _:timezone_.
|
13
|
+
#
|
14
|
+
# When declaring a property of type :timezone, you may also define a default just like
|
15
|
+
# any other property. In addition, the framework will automatically add a validation
|
16
|
+
# to make sure that your property is either nil or one of the valid IANA time zone identifiers.
|
17
|
+
#
|
18
|
+
# Each instance of {Parse::TimeZone} has a {Parse::TimeZone#zone} attribute that provides access to
|
19
|
+
# the underlying {http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html ActiveSupport::TimeZone}
|
20
|
+
# instance, which you can use to perform time zone operations.
|
21
|
+
# @example
|
22
|
+
# class Event < Parse::Object
|
23
|
+
# # an event occurs in a time zone.
|
24
|
+
# property :time_zone, :timezone, default: 'America/Los_Angeles'
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# event = Event.new
|
28
|
+
# event.time_zone.name # => 'America/Los_Angeles'
|
29
|
+
# event.time_zone.valid? # => true
|
30
|
+
#
|
31
|
+
# event.time_zone.zone # => ActiveSupport::TimeZone
|
32
|
+
# event.time_zone.formatted_offset # => "-08:00"
|
33
|
+
#
|
34
|
+
# event.time_zone = 'Europe/Paris'
|
35
|
+
# event.time_zone.formatted_offset # => +01:00"
|
36
|
+
#
|
37
|
+
# event.time_zone = 'Galaxy/Andromeda'
|
38
|
+
# event.time_zone.valid? # => false
|
39
|
+
# @version 1.7.1
|
40
|
+
class TimeZone
|
41
|
+
# The mapping of TimeZones
|
42
|
+
MAPPING = ActiveSupport::TimeZone::MAPPING
|
43
|
+
|
44
|
+
# Create methods based on the allowable public methods on ActiveSupport::TimeZone.
|
45
|
+
# Basically sets up sending forwarding calls to the `zone` object for a Parse::TimeZone object.
|
46
|
+
(ActiveSupport::TimeZone.public_instance_methods(false) - [:to_s, :name, :as_json]).each do |meth|
|
47
|
+
Parse::TimeZone.class_eval do
|
48
|
+
define_method meth do |*args|
|
49
|
+
zone.send meth, *args
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Creates a new instance given the IANA identifier (ex. America/Los_Angeles)
|
55
|
+
# @overload new(iana)
|
56
|
+
# @param iana [String] the IANA identifier (ex. America/Los_Angeles)
|
57
|
+
# @return [Parse::TimeZone]
|
58
|
+
# @overload new(timezone)
|
59
|
+
# You can instantiate a new instance with either a {Parse::TimeZone} or an {http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html ActiveSupport::TimeZone}
|
60
|
+
# object.
|
61
|
+
# @param timezone [Parse::TimeZone|ActiveSupport::TimeZone] an instance of either timezone class.
|
62
|
+
# @return [Parse::TimeZone]
|
63
|
+
def initialize(iana)
|
64
|
+
if iana.is_a?(String)
|
65
|
+
@name = iana
|
66
|
+
@zone = nil
|
67
|
+
elsif iana.is_a?(::Parse::TimeZone)
|
68
|
+
@zone = iana.zone
|
69
|
+
@name = nil
|
70
|
+
elsif iana.is_a?(::ActiveSupport::TimeZone)
|
71
|
+
@zone = iana
|
72
|
+
@name = nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# @!attribute [rw] name
|
77
|
+
# @raise ArgumentError if value is not a string type.
|
78
|
+
# @return [String] the IANA identifier for this time zone.
|
79
|
+
def name
|
80
|
+
@zone.present? ? zone.name : @name
|
81
|
+
end
|
82
|
+
|
83
|
+
def name=(iana)
|
84
|
+
unless iana.nil? || iana.is_a?(String)
|
85
|
+
raise ArgumentError, "Parse::TimeZone#name should be an IANA time zone identifier."
|
86
|
+
end
|
87
|
+
@name = iana
|
88
|
+
@zone = nil
|
89
|
+
end
|
90
|
+
|
91
|
+
# @!attribute [rw] zone
|
92
|
+
# Returns an instance of {http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html ActiveSupport::TimeZone}
|
93
|
+
# based on the IANA identifier. The setter may allow usign an IANA string identifier,
|
94
|
+
# a {Parse::TimeZone} or an
|
95
|
+
# {http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html ActiveSupport::TimeZone}
|
96
|
+
# object.
|
97
|
+
# @see #name
|
98
|
+
# @raise ArgumentError
|
99
|
+
# @return [ActiveSupport::TimeZone]
|
100
|
+
def zone
|
101
|
+
# lazy load the TimeZone object only when the user requests it, otherwise
|
102
|
+
# just keep the name of the string around. Makes encoding/decoding faster.
|
103
|
+
if @zone.nil? && @name.present?
|
104
|
+
@zone = ::ActiveSupport::TimeZone.new(@name)
|
105
|
+
@name = nil # clear out the cache
|
106
|
+
end
|
107
|
+
@zone
|
108
|
+
end
|
109
|
+
|
110
|
+
def zone=(timezone)
|
111
|
+
if timezone.is_a?(::ActiveSupport::TimeZone)
|
112
|
+
@zone = timezone
|
113
|
+
@name = nil
|
114
|
+
elsif timezone.is_a?(Parse::TimeZone)
|
115
|
+
@name = timezone.name
|
116
|
+
@zone = nil
|
117
|
+
elsif timezone.nil? || timezone.is_a?(String)
|
118
|
+
@name = timezone
|
119
|
+
@zone = nil
|
120
|
+
else
|
121
|
+
raise ArgumentError, 'Invalid value passed to Parse::TimeZone#zone.'
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# (see #to_s)
|
126
|
+
def as_json(*args)
|
127
|
+
name
|
128
|
+
end
|
129
|
+
|
130
|
+
# @return [String] the IANA identifier for this timezone or nil.
|
131
|
+
def to_s
|
132
|
+
name
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns true or false whether the time zone exists in the {http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html ActiveSupport::TimeZone} mapping.
|
136
|
+
# @return [Bool] true if it contains a valid time zone
|
137
|
+
def valid?
|
138
|
+
ActiveSupport::TimeZone[to_s].present?
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
data/lib/parse/stack.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
exit if ENV['MERCURY_URL'] # don't use master branch
|
5
4
|
require_relative "stack/version"
|
6
5
|
require_relative 'client'
|
7
6
|
require_relative 'query'
|
@@ -13,7 +12,6 @@ module Parse
|
|
13
12
|
class Error < StandardError; end;
|
14
13
|
module Stack
|
15
14
|
|
16
|
-
# Your code goes here...
|
17
15
|
end
|
18
16
|
end
|
19
17
|
|
data/lib/parse/stack/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: parse-stack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.7.
|
4
|
+
version: 1.7.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anthony Persaud
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-07-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -233,6 +233,7 @@ files:
|
|
233
233
|
- lib/parse/model/pointer.rb
|
234
234
|
- lib/parse/model/push.rb
|
235
235
|
- lib/parse/model/shortnames.rb
|
236
|
+
- lib/parse/model/time_zone.rb
|
236
237
|
- lib/parse/query.rb
|
237
238
|
- lib/parse/query/constraint.rb
|
238
239
|
- lib/parse/query/constraints.rb
|