parse-stack 1.7.0 → 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml 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