parse-stack 1.7.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|