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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 48e731586682a18f7800110c166f20536c46b717
4
- data.tar.gz: e5cd663b12a7935acb772ff881ca471709de076b
3
+ metadata.gz: 3557dd1c97f0f227d848732708916d3428563d22
4
+ data.tar.gz: b71256968073f1318cb5c88665c551e6e6f7381c
5
5
  SHA512:
6
- metadata.gz: 79a487df290f8e3f7efc843b8f7b079fccd5dee56e52f3821a20f75f3b391675da33ce03e23a01ed9c442305017d2cee5e1d6dcfb52074bca218ee5f8f986044
7
- data.tar.gz: b54958ad9b82dd8fddc10b6c1701e20eea11701482d27403cc92c41e4db883573feb77a9ab17915738092b940ee319a6b6c394f7afa6d7360790d1b916d524b9
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.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- parse-stack (1.7.0)
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.1)
18
- actionview (= 5.1.1)
19
- activesupport (= 5.1.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.1)
25
- activesupport (= 5.1.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.1)
36
- activesupport (= 5.1.1)
37
- activesupport (5.1.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.0)
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.0)
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 enummerated type. see :enum
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
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "bundler/setup"
4
- require "parse/stack"
5
- require 'dotenv'
6
4
  require 'byebug'
5
+ require 'dotenv'
6
+ require "parse/stack"
7
+
7
8
  Dotenv.load
8
9
 
9
10
  def setup
@@ -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
- HTTP_OVERRIDE = 'X-Http-Method-Override'
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 > 2_000
55
- env[:request_headers][HTTP_OVERRIDE] = 'GET'
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
- env[:body] = env[:url].query
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. You can use the methods below to perform these types of atomic
225
- # operations. The operation is performed directly on Parse server
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, :string, field: :GCMSenderId
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
- # @return [String]
97
- property :time_zone
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 "[Parse::SaveAll] Unbounded update detected with id #{cursor.id}."
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 "[Parse::SaveAll] Reached anchor date #{anchor_date} < #{cursor.updated_at}"
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 = [:id, :string, :relation, :integer, :float, :boolean, :date, :array, :file, :geopoint, :bytes, :object, :acl].freeze
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
- @fields ||= {id: :string, created_at: :date, updated_at: :date, acl: :acl}
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 self.fields[key].present? && BASE_FIELD_MAP[key].nil?
131
- warn "Property #{self}##{key} already defined with data type #{data_type}. Will be ignored."
132
- return
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
@@ -90,7 +90,7 @@ module Parse
90
90
 
91
91
  end
92
92
 
93
- # @return [Hash]
93
+ # @return [Hash] attributes for a Parse GeoPoint.
94
94
  def attributes
95
95
  ATTRIBUTES
96
96
  end
@@ -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
- # This class also handles all the relational types of associations in a Parse application and
74
- # handles the main CRUD operations.
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
@@ -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
 
@@ -6,6 +6,6 @@ module Parse
6
6
  # The Parse Server SDK for Ruby
7
7
  module Stack
8
8
  # The current version.
9
- VERSION = "1.7.0"
9
+ VERSION = "1.7.1"
10
10
  end
11
11
  end
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.0
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-06-11 00:00:00.000000000 Z
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