parse-stack 1.5.1 → 1.5.2

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +15 -1
  3. data/Gemfile.lock +10 -10
  4. data/README.md +23 -9
  5. data/bin/console +3 -0
  6. data/lib/parse/api/analytics.rb +1 -1
  7. data/lib/parse/api/objects.rb +1 -1
  8. data/lib/parse/api/users.rb +1 -1
  9. data/lib/parse/client.rb +77 -40
  10. data/lib/parse/client/caching.rb +9 -5
  11. data/lib/parse/client/protocol.rb +47 -0
  12. data/lib/parse/client/request.rb +66 -37
  13. data/lib/parse/client/response.rb +39 -21
  14. data/lib/parse/model/acl.rb +4 -9
  15. data/lib/parse/model/associations/belongs_to.rb +97 -9
  16. data/lib/parse/model/associations/collection_proxy.rb +89 -29
  17. data/lib/parse/model/associations/has_many.rb +301 -28
  18. data/lib/parse/model/associations/has_one.rb +98 -4
  19. data/lib/parse/model/associations/pointer_collection_proxy.rb +48 -16
  20. data/lib/parse/model/associations/relation_collection_proxy.rb +61 -36
  21. data/lib/parse/model/bytes.rb +11 -5
  22. data/lib/parse/model/classes/installation.rb +50 -3
  23. data/lib/parse/model/classes/role.rb +7 -2
  24. data/lib/parse/model/classes/session.rb +21 -4
  25. data/lib/parse/model/classes/user.rb +122 -22
  26. data/lib/parse/model/core/actions.rb +7 -3
  27. data/lib/parse/model/core/properties.rb +14 -13
  28. data/lib/parse/model/core/querying.rb +16 -10
  29. data/lib/parse/model/core/schema.rb +2 -3
  30. data/lib/parse/model/date.rb +18 -12
  31. data/lib/parse/model/file.rb +77 -19
  32. data/lib/parse/model/geopoint.rb +70 -12
  33. data/lib/parse/model/model.rb +84 -8
  34. data/lib/parse/model/object.rb +225 -94
  35. data/lib/parse/model/pointer.rb +94 -13
  36. data/lib/parse/model/push.rb +76 -4
  37. data/lib/parse/query.rb +356 -41
  38. data/lib/parse/query/constraints.rb +399 -29
  39. data/lib/parse/query/ordering.rb +21 -8
  40. data/lib/parse/stack.rb +1 -0
  41. data/lib/parse/stack/version.rb +2 -1
  42. data/lib/parse/webhooks.rb +0 -24
  43. data/lib/parse/webhooks/payload.rb +54 -1
  44. data/lib/parse/webhooks/registration.rb +13 -2
  45. metadata +2 -2
@@ -3,12 +3,17 @@
3
3
  require_relative '../object'
4
4
  require_relative 'user'
5
5
  module Parse
6
-
6
+ # This class represents the data and columns contained in the standard Parse `_Role` collection.
7
7
  class Role < Parse::Object
8
+
8
9
  parse_class Parse::Model::CLASS_ROLE
10
+ # @return [String] the name of this role.
9
11
  property :name
10
-
12
+ # The roles Parse relation provides a mechanism to create a hierarchical inheritable types of permissions
13
+ # by assigning child roles.
14
+ # @return [RelationCollectionProxy<Role>] a collection of Roles.
11
15
  has_many :roles, through: :relation
16
+ # @return [RelationCollectionProxy<User>] a Parse relation of users belonging to this role.
12
17
  has_many :users, through: :relation
13
18
 
14
19
  before_save do
@@ -3,20 +3,37 @@
3
3
  require_relative '../object'
4
4
 
5
5
  module Parse
6
+ # This class represents the data and columns contained in the standard Parse
7
+ # `_Session` collection. The Session class maintains per-device (or website) authentication
8
+ # information for a particular user. Whenever a User object is logged in, a new Session record, with
9
+ # a session token is generated. You may use a known active session token to find the corresponding
10
+ # user for that session. Deleting a Session record (and session token), effectively logs out the user, when making Parse requests
11
+ # on behalf of the user using the session token.
6
12
  class Session < Parse::Object
13
+
7
14
  parse_class Parse::Model::CLASS_SESSION
15
+ # @return [Hash] data on how this Session was created.
8
16
  property :created_with, :object
17
+ # @return [Parse::Date] when the session token expires.
9
18
  property :expires_at, :date
19
+ # @return [String] The installation id from the Installation table.
20
+ # @see Installation#installation_id
10
21
  property :installation_id
22
+ # @return [Boolean] whether this session token is restricted.
11
23
  property :restricted, :boolean
24
+ # @return [String] the session token for this installation and user pair.
12
25
  property :session_token
13
-
26
+ # @return [User] the user corresponding to this session.
27
+ # @see User
14
28
  belongs_to :user
15
29
 
16
- def self.session(token)
17
- response = client.fetch_session(token)
30
+ # Return the Session record for this session token.
31
+ # @param token [String] the session token
32
+ # @return [Session] the session for this token, otherwise nil.
33
+ def self.session(token, **opts)
34
+ response = client.fetch_session(token, opts)
18
35
  if response.success?
19
- reutrn Parse::Session.build response.result
36
+ return Parse::Session.build response.result
20
37
  end
21
38
  nil
22
39
  end
@@ -1,24 +1,52 @@
1
1
  # encoding: UTF-8
2
2
  # frozen_string_literal: true
3
3
 
4
- # The User class provided by Parse with the required fields. You may
5
- # add mixings to this class to add the app specific properties
6
4
  require_relative '../object'
7
5
  module Parse
8
-
9
- class UsernameMissingError < StandardError; end; # 200
10
- class PasswordMissingError < StandardError; end; # 201
11
- class UsernameTakenError < StandardError; end; # 202
12
- class EmailTakenError < StandardError; end; # 203
13
- class EmailMissing < StandardError; end; # 204
14
-
6
+ # 200 Error code indicating that the username is missing or empty.
7
+ class UsernameMissingError < StandardError; end;
8
+ # 201 Error code indicating that the password is missing or empty.
9
+ class PasswordMissingError < StandardError; end;
10
+ # Error code 202: indicating that the username has already been taken.
11
+ class UsernameTakenError < StandardError; end;
12
+ # 203 Error code indicating that the email has already been taken.
13
+ class EmailTakenError < StandardError; end;
14
+ # 204 Error code indicating that the email is missing, but must be specified.
15
+ class EmailMissing < StandardError; end;
16
+ # 205 Error code indicating that a user with the specified email was not found.
17
+ class EmailNotFound < StandardError; end;
18
+ # 125 Error code indicating that the email address was invalid.
19
+ class InvalidEmailAddress < StandardError; end;
20
+
21
+ # The main class representing the _User table in Parse. A user can either be signed up or anonymous.
22
+ # All users need to have a username and a password, with email being optional but globally unique if set.
23
+ # You may add additional properties by redeclaring the class to match your specific schema.
15
24
  class User < Parse::Object
16
25
 
17
26
  parse_class Parse::Model::CLASS_USER
27
+ # @return [String] The session token if this user is logged in.
18
28
  attr_accessor :session_token
29
+
30
+ # The auth data for this Parse::User. Depending on how this user is authenticated or
31
+ # logged in, the contents may be different, especially if you are using another third-party
32
+ # authentication mechanism like Facebook/Twitter.
33
+ # @return [Hash] Auth data hash object.
19
34
  property :auth_data, :object
35
+
36
+ # Emails are optional in Parse, but if set, they must be unique.
37
+ # @return [String] The email field.
20
38
  property :email
39
+
40
+ # @overload password=(value)
41
+ # You may set a password for this user when you are creating them. Parse never returns a
42
+ # Parse::User's password when a record is fetched. Therefore, normally this getter is nil.
43
+ # While this API exists, it is recommended you use either the #login! or #signup! methods.
44
+ # (see #login!)
45
+ # @return [String] The password you set.
21
46
  property :password
47
+
48
+ # All Parse users have a username and must be globally unique.
49
+ # @return [String] The user's username.
22
50
  property :username
23
51
 
24
52
  before_save do
@@ -26,45 +54,71 @@ module Parse
26
54
  self.clear_attribute_change!(:acl)
27
55
  end
28
56
 
57
+ # True if this user is anonymous.
29
58
  def anonymous?
30
59
  anonymous_id.nil?
31
60
  end
32
61
 
62
+ # Returns the anonymous identifier only if this user is anonymous.
63
+ # @see #anonymous?
64
+ # @return [String] The anonymous identifier for this anonymous user.
33
65
  def anonymous_id
34
66
  auth_data['anonymous']['id'] if auth_data.present? && auth_data["anonymous"].is_a?(Hash)
35
67
  end
36
68
 
69
+ # Adds the third-party authentication data to for a given service.
70
+ # @param service_name [Symbol] The name of the service (ex. :facebook)
71
+ # @param data [Hash] The body of the OAuth data. Dependent on each service.
72
+ # @raise [ResponseError] If user was not successfully linked
37
73
  def link_auth_data!(service_name, **data)
38
74
  response = client.set_service_auth_data(id, service_name, data)
39
- apply_attributes!(response.result) if response.success?
40
- response.success?
75
+ raise Parse::ResponseError, response if response.error?
76
+ apply_attributes!(response.result)
41
77
  end
42
78
 
79
+ # Removes third-party authentication data for this user
80
+ # @param service_name [Symbol] The name of the third-party service (ex. :facebook)
81
+ # @raise [ResponseError] If user was not successfully unlinked
82
+ # @return [Boolean] True/false if successful.
43
83
  def unlink_auth_data!(service_name)
44
84
  response = client.set_service_auth_data(id, service_name, nil)
45
- apply_attributes!(response.result) if response.success?
46
- response.success?
85
+ raise Parse::ResponseError, response if response.error?
86
+ apply_attributes!(response.result)
47
87
  end
48
88
 
89
+
90
+ # @!visibility private
49
91
  # So that apply_attributes! works with session_token for login
50
92
  def session_token_set_attribute!(token, track = false)
51
93
  @session_token = token.to_s
52
94
  end
53
95
  alias_method :sessionToken_set_attribute!, :session_token_set_attribute!
54
96
 
97
+ # @return [Boolean] true if this user has a session token.
55
98
  def logged_in?
56
99
  self.session_token.present?
57
100
  end
58
101
 
102
+ # Request a password reset for this user
103
+ # @return [Boolean] true if it was successful requested. false otherwise.
59
104
  def request_password_reset
60
105
  return false if email.nil?
61
106
  Parse::User.request_password_reset(email)
62
107
  end
63
108
 
109
+ # You may set a password for this user when you are creating them. Parse never returns a
110
+ # @param passwd The user's password to be used for signing up.
111
+ # @raise [Parse::UsernameMissingError] If username is missing.
112
+ # @raise [Parse::PasswordMissingError] If password is missing.
113
+ # @raise [Parse::UsernameTakenError] If the username has already been taken.
114
+ # @raise [Parse::EmailTakenError] If the email has already been taken (or exists in the system).
115
+ # @raise [Parse::InvalidEmailAddress] If the email is invalid.
116
+ # @raise [Parse::ResponseError] An unknown error occurred.
117
+ # @return [Boolean] True if signup it was successful. If it fails an exception is thrown.
64
118
  def signup!(passwd = nil)
65
119
  self.password = passwd || password
66
120
  if username.blank?
67
- raise Parse::UsernameMissingError, "Signup requires an username."
121
+ raise Parse::UsernameMissingError, "Signup requires a username."
68
122
  end
69
123
 
70
124
  if password.blank?
@@ -91,10 +145,15 @@ module Parse
91
145
  raise Parse::UsernameTakenError, response
92
146
  when Parse::Response::ERROR_EMAIL_TAKEN
93
147
  raise Parse::EmailTakenError, response
148
+ when Parse::Response::ERROR_EMAIL_INVALID
149
+ raise Parse::InvalidEmailAddress, response
94
150
  end
95
- raise response
151
+ raise Parse::ResponseError, response
96
152
  end
97
153
 
154
+ # Login and get a session token for this user.
155
+ # @param passwd [String] The password for this user.
156
+ # @return [Boolean] True/false if we received a valid session token.
98
157
  def login!(passwd = nil)
99
158
  self.password = passwd || self.password
100
159
  response = client.login(username.to_s, password.to_s)
@@ -102,6 +161,8 @@ module Parse
102
161
  self.session_token.present?
103
162
  end
104
163
 
164
+ # Invalid the current session token for this logged in user.
165
+ # @return [Boolean] True/false if successful
105
166
  def logout
106
167
  return true if self.session_token.blank?
107
168
  client.logout session_token
@@ -111,11 +172,13 @@ module Parse
111
172
  false
112
173
  end
113
174
 
175
+ # @!visibility private
114
176
  def session_token=(token)
115
177
  @session = nil
116
178
  @session_token = token
117
179
  end
118
180
 
181
+ # @return [Session] the session corresponding to the user's session token.
119
182
  def session
120
183
  if @session.blank? && @session_token.present?
121
184
  response = client.fetch_session(@session_token)
@@ -124,6 +187,16 @@ module Parse
124
187
  @session
125
188
  end
126
189
 
190
+ # Creates a new Parse::User given a hash that maps to the fields defined in your Parse::User collection.
191
+ # @param body [Hash] The hash containing the Parse::User fields. The field `username` and `password` are required.
192
+ # @option opts [Boolean] :master_key Whether the master key should be used for this request.
193
+ # @raise [UsernameMissingError] If username is missing.
194
+ # @raise [PasswordMissingError] If password is missing.
195
+ # @raise [UsernameTakenError] If the username has already been taken.
196
+ # @raise [EmailTakenError] If the email has already been taken (or exists in the system).
197
+ # @raise [InvalidEmailAddress] If the email is invalid.
198
+ # @raise [ResponseError] An unknown error occurred.
199
+ # @return [User] Returns a successfully created Parse::User.
127
200
  def self.create(body, **opts)
128
201
  response = client.create_user(body, opts: opts)
129
202
  if response.success?
@@ -140,43 +213,70 @@ module Parse
140
213
  when Parse::Response::ERROR_EMAIL_TAKEN
141
214
  raise Parse::EmailTakenError, response
142
215
  end
143
- raise response
216
+ raise Parse::ResponseError, response
144
217
  end
145
218
 
146
- # method will signup or login a user given the third-party authentication data
219
+ # Automatically and implicitly signup a user if it did not already exists and
220
+ # authenticates them (login) using third-party authentication data. May raise exceptions
221
+ # similar to `create` depending on what you provide the _body_ parameter.
222
+ # @param service_name [Symbol] the name of the service key (ex. :facebook)
223
+ # @param auth_data [Hash] the specific service data to place in the user's auth-data for this service.
224
+ # @param body [Hash] any additional User related fields or properties when signing up this User record.
225
+ # @return [User] a logged in user, or nil.
226
+ # @see User.create
147
227
  def self.autologin_service(service_name, auth_data, body: {})
148
228
  body = body.merge({authData: {service_name => auth_data} })
149
229
  self.create(body)
150
230
  end
151
231
 
232
+ # This method will signup a new user using the parameters below. The required fields
233
+ # to create a user in Parse is the _username_ and _password_ fields. The _email_ field is optional.
234
+ # Both _username_ and _email_ (if provided), must be unique. At a minimum, it is recommended you perform
235
+ # a query using the supplied _username_ first to verify do not already have an account with that username.
236
+ # This method will raise all the exceptions from the similar `create` method.
237
+ # @see User.create
152
238
  def self.signup(username, password, email = nil, body: {})
153
239
  body = body.merge({username: username, password: password })
154
240
  body[:email] = email if email.present?
155
241
  self.create(body)
156
242
  end
157
243
 
244
+ # Login and return a Parse::User with this username/password combination.
245
+ # @param username [String] the user's username
246
+ # @param password [String] the user's password
247
+ # @return [User] a logged in user for the provided username. Returns nil otherwise.
158
248
  def self.login(username, password)
159
249
  response = client.login(username.to_s, password.to_s)
160
250
  response.success? ? Parse::User.build(response.result) : nil
161
251
  end
162
252
 
253
+ # Request a password reset for a registered email.
254
+ # @param email [String] The user's email address.
255
+ # @return [Boolean] True/false if successful.
163
256
  def self.request_password_reset(email)
164
257
  email = email.email if email.is_a?(Parse::User)
165
258
  return false if email.blank?
166
- response = client.reset_password(email)
259
+ response = client.request_password_reset(email)
167
260
  response.success?
168
261
  end
169
262
 
170
- def self.session(token)
171
- self.session! token
263
+ # Same as `session!` but returns nil if a user was not found or sesion token was invalid.
264
+ # @return [User] the user matching this active token, otherwise nil.
265
+ # @see #session!
266
+ def self.session(token, opts = {})
267
+ self.session! token, opts
172
268
  rescue InvalidSessionTokenError => e
173
269
  nil
174
270
  end
175
271
 
176
- def self.session!(token)
272
+ # Return a Parse::User for this active session token.
273
+ # @raise [InvalidSessionTokenError] Invalid session token.
274
+ # @return [User] the user matching this active token
275
+ # @see #session
276
+ def self.session!(token, opts = {})
177
277
  # support Parse::Session objects
178
278
  token = token.session_token if token.respond_to?(:session_token)
179
- response = client.current_user(token)
279
+ response = client.current_user(token, opts)
180
280
  response.success? ? Parse::User.build(response.result) : nil
181
281
  end
182
282
 
@@ -9,7 +9,6 @@ require 'time'
9
9
  require 'parallel'
10
10
  require_relative '../../client/request'
11
11
 
12
- #This module provides many of the CRUD operations on Parse::Object.
13
12
 
14
13
  # A Parse::RelationAction is special operation that adds one object to a relational
15
14
  # table as to another. Depending on the polarity of the action, the objects are
@@ -46,8 +45,13 @@ end
46
45
  # we use temporary Request objects have contain the operation to be performed (in some cases).
47
46
  # This allows to group a list of Request methods, into a batch for sending all at once to Parse.
48
47
  module Parse
48
+
49
+ # An error raised when a save failure occurs.
49
50
  class SaveFailureError < StandardError
51
+ # @return [Parse::Object] the Parse::Object that failed to save.
50
52
  attr_reader :object
53
+
54
+ # @param object [Parse::Object] the object that failed.
51
55
  def initialize(object)
52
56
  @object = object
53
57
  end
@@ -455,8 +459,8 @@ module Parse
455
459
  module Fetching
456
460
 
457
461
  # force fetches the current object with the data contained in Parse.
458
- def fetch!
459
- response = client.fetch_object(parse_class, id)
462
+ def fetch!(opts = {})
463
+ response = client.fetch_object(parse_class, id, opts)
460
464
  if response.error?
461
465
  puts "[Fetch Error] #{response.code}: #{response.error}"
462
466
  end
@@ -13,17 +13,16 @@ require 'active_model_serializers'
13
13
  require 'active_support/hash_with_indifferent_access'
14
14
  require 'time'
15
15
 
16
- =begin
17
- This module provides support for handling all the different types of column data types
18
- supported in Parse and mapping them between their remote names with their local ruby named attributes.
19
- By default, the convention used for naming parameters is that the remote column should be in lower-first-camelcase, (ex. myField, eventAddress), except for
20
- a few special columns like "id" and "acl".
21
- Properties are defined when creating subclasses of Parse::Object and using the `property` class method.
22
16
 
23
- By defining properties, dynamic methods are created in order to allow getters and setters to be used. We will go into detail below.
24
-
25
- Each class will have a different copy of attribute mapping and field mappings.
26
- =end
17
+ # This module provides support for handling all the different types of column data types
18
+ # supported in Parse and mapping them between their remote names with their local ruby named attributes.
19
+ # By default, the convention used for naming parameters is that the remote column should be in lower-first-camelcase, (ex. myField, eventAddress), except for
20
+ # a few special columns like "id" and "acl".
21
+ # Properties are defined when creating subclasses of Parse::Object and using the `property` class method.
22
+ #
23
+ # By defining properties, dynamic methods are created in order to allow getters and setters to be used. We will go into detail below.
24
+ #
25
+ # Each class will have a different copy of attribute mapping and field mappings.
27
26
 
28
27
  module Parse
29
28
 
@@ -206,7 +205,7 @@ module Parse
206
205
 
207
206
  method_name = add_suffix ? :"valid_#{prefix_or_key}?" : :"#{prefix_or_key}_valid?"
208
207
  define_method(method_name) do
209
- value = instance_variable_get(ivar)
208
+ value = send(key) # call default getter
210
209
  return true if allow_nil && value.nil?
211
210
  enum_values.include?(value.to_s.to_sym)
212
211
  end
@@ -220,8 +219,10 @@ module Parse
220
219
  method_name = :"#{prefix}_#{enum}"
221
220
  end
222
221
  self.scope method_name, ->(ex = {}){ ex.merge!(key => enum); query( ex ) }
223
- define_method("#{method_name}!") { instance_variable_set(ivar, enum) }
224
- define_method("#{method_name}?") { enum == instance_variable_get(ivar).to_s.to_sym }
222
+
223
+
224
+ define_method("#{method_name}!") { send set_attribute_method, enum, true }
225
+ define_method("#{method_name}?") { enum == send(key).to_s.to_sym }
225
226
  end
226
227
  end # unless scopes
227
228
 
@@ -3,8 +3,6 @@
3
3
 
4
4
  require_relative '../../query'
5
5
 
6
- # This module provides most of the querying methods for Parse Objects.
7
- # It proxies much of the query methods to the Parse::Query object.
8
6
  module Parse
9
7
 
10
8
  module Querying
@@ -38,17 +36,21 @@ module Parse
38
36
 
39
37
  if _q.is_a?(Parse::Query)
40
38
  klass = self
41
- _q.define_singleton_method(:method_missing) do |m, *args, &block|
39
+ _q.define_singleton_method(:method_missing) do |m, *args, &chained_block|
42
40
  if klass.respond_to?(m, true)
43
- klass_scope = klass.send(m, *args, &block)
41
+ # must be a scope
42
+ klass_scope = klass.send(m, *args)
44
43
  if klass_scope.is_a?(Parse::Query)
45
- return self.add_constraints( klass_scope.constraints )
46
- end
44
+ # merge constraints
45
+ add_constraints( klass_scope.constraints )
46
+ # if a block was passed, execute the query, otherwise return the query
47
+ return chained_block.present? ? results(&chained_block) : self
48
+ end # if
47
49
  klass = nil # help clean up ruby gc
48
50
  return klass_scope
49
51
  end
50
52
  klass = nil # help clean up ruby gc
51
- return self.results.send(m, *args, &block)
53
+ return results.send(m, *args, &chained_block)
52
54
  end
53
55
  end
54
56
  return _q if block.nil?
@@ -61,6 +63,7 @@ module Parse
61
63
  # This query method helper returns a Query object tied to a parse class.
62
64
  # The parse class should be the name of the one that will be sent in the query
63
65
  # request pointing to the remote table.
66
+
64
67
  def query(constraints = {})
65
68
  Parse::Query.new self.parse_class, constraints
66
69
  end; alias_method :where, :query
@@ -71,6 +74,7 @@ module Parse
71
74
 
72
75
  # Most common method to use when querying a class. This takes a hash of constraints
73
76
  # and conditions and returns the results.
77
+
74
78
  def all(constraints = {})
75
79
  constraints = {limit: :max}.merge(constraints)
76
80
  prepared_query = query(constraints)
@@ -82,6 +86,7 @@ module Parse
82
86
  # then we treat it as a count.
83
87
  # Ex. Object.first( :name => "Anthony" ) (returns single object)
84
88
  # Ex. Object.first(3) # first 3 objects (array of 3 objects)
89
+
85
90
  def first(constraints = {})
86
91
  fetch_count = 1
87
92
  if constraints.is_a?(Numeric)
@@ -95,18 +100,19 @@ module Parse
95
100
  end
96
101
 
97
102
  # creates a count request (which is more performant when counting objects)
98
- def count(**constraints)
103
+
104
+ def count(constraints = {})
99
105
  query(constraints).count
100
106
  end
101
107
 
102
- def newest(**constraints)
108
+ def newest(constraints = {})
103
109
  constraints.merge!(order: :created_at.desc)
104
110
  _q = query(constraints)
105
111
  _q.define_singleton_method(:method_missing) { |m, *args, &block| self.results.send(m, *args, &block) }
106
112
  _q
107
113
  end
108
114
 
109
- def oldest(**constraints)
115
+ def oldest(constraints = {})
110
116
  constraints.merge!(order: :created_at.asc)
111
117
  _q = query(constraints)
112
118
  _q.define_singleton_method(:method_missing) { |m, *args, &block| self.results.send(m, *args, &block) }