parse-stack 1.5.1 → 1.5.2

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