parse-stack 1.5.2 → 1.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +5 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +40 -80
  5. data/lib/parse/api/all.rb +7 -0
  6. data/lib/parse/api/analytics.rb +8 -3
  7. data/lib/parse/api/apps.rb +29 -1
  8. data/lib/parse/api/batch.rb +14 -129
  9. data/lib/parse/api/cloud_functions.rb +9 -0
  10. data/lib/parse/api/config.rb +10 -1
  11. data/lib/parse/api/files.rb +7 -2
  12. data/lib/parse/api/hooks.rb +45 -2
  13. data/lib/parse/api/objects.rb +43 -6
  14. data/lib/parse/api/push.rb +6 -1
  15. data/lib/parse/api/schemas.rb +15 -1
  16. data/lib/parse/api/sessions.rb +5 -0
  17. data/lib/parse/api/users.rb +64 -5
  18. data/lib/parse/client/authentication.rb +25 -8
  19. data/lib/parse/client/batch.rb +206 -0
  20. data/lib/parse/client/body_builder.rb +12 -6
  21. data/lib/parse/client/caching.rb +42 -10
  22. data/lib/parse/client/protocol.rb +51 -46
  23. data/lib/parse/client/response.rb +1 -47
  24. data/lib/parse/client.rb +171 -42
  25. data/lib/parse/model/acl.rb +184 -39
  26. data/lib/parse/model/associations/belongs_to.rb +1 -0
  27. data/lib/parse/model/classes/role.rb +7 -1
  28. data/lib/parse/model/classes/session.rb +7 -3
  29. data/lib/parse/model/classes/user.rb +107 -0
  30. data/lib/parse/model/core/actions.rb +166 -115
  31. data/lib/parse/model/core/fetching.rb +105 -0
  32. data/lib/parse/model/core/properties.rb +40 -13
  33. data/lib/parse/model/core/querying.rb +123 -39
  34. data/lib/parse/model/core/schema.rb +22 -32
  35. data/lib/parse/model/object.rb +26 -20
  36. data/lib/parse/model/pointer.rb +1 -0
  37. data/lib/parse/query/constraint.rb +65 -27
  38. data/lib/parse/query/constraints.rb +0 -3
  39. data/lib/parse/query/operation.rb +33 -22
  40. data/lib/parse/query/ordering.rb +10 -5
  41. data/lib/parse/stack/generators/rails.rb +5 -1
  42. data/lib/parse/stack/generators/templates/model_installation.rb +1 -1
  43. data/lib/parse/stack/generators/templates/model_role.rb +1 -1
  44. data/lib/parse/stack/generators/templates/model_session.rb +2 -2
  45. data/lib/parse/stack/generators/templates/model_user.rb +1 -1
  46. data/lib/parse/stack/generators/templates/parse.rb +0 -1
  47. data/lib/parse/stack/railtie.rb +1 -0
  48. data/lib/parse/stack/tasks.rb +3 -1
  49. data/lib/parse/stack/version.rb +3 -1
  50. data/lib/parse/webhooks/registration.rb +3 -3
  51. data/lib/parse/webhooks.rb +88 -7
  52. metadata +5 -3
@@ -5,58 +5,101 @@ module Parse
5
5
 
6
6
 
7
7
  module API
8
+ # Defines the Parse webhooks interface for the Parse REST API
8
9
  module Hooks
9
10
  HOOKS_PREFIX = "hooks/"
11
+ # The allowed set of Parse triggers.
10
12
  TRIGGER_NAMES = [:beforeSave, :afterSave, :beforeDelete, :afterDelete].freeze
13
+
14
+ # @!visibility private
11
15
  def _verify_trigger(triggerName)
12
16
  triggerName = triggerName.to_s.camelize(:lower).to_sym
13
17
  raise ArgumentError, "Invalid trigger name #{triggerName}" unless TRIGGER_NAMES.include?(triggerName)
14
18
  triggerName
15
19
  end
16
20
 
21
+ # Fetch all defined cloud code functions.
22
+ # @return [Parse::Response]
17
23
  def functions
18
24
  request :get, "#{HOOKS_PREFIX}functions"
19
25
  end
20
26
 
27
+ # Fetch information about a specific registered cloud function.
28
+ # @param functionName [String] the name of the cloud code function.
29
+ # @return [Parse::Response]
21
30
  def fetch_function(functionName)
22
31
  request :get, "#{HOOKS_PREFIX}functions/#{functionName}"
23
32
  end
24
33
 
34
+ # Register a cloud code webhook function pointing to a endpoint url.
35
+ # @param functionName [String] the name of the cloud code function.
36
+ # @param url [String] the url endpoint for this cloud code function.
37
+ # @return [Parse::Response]
25
38
  def create_function(functionName, url)
26
39
  request :post, "#{HOOKS_PREFIX}functions", body: {functionName: functionName, url: url}
27
40
  end
28
41
 
42
+ # Updated the endpoint url for a registered cloud code webhook function.
43
+ # @param functionName [String] the name of the cloud code function.
44
+ # @param url [String] the new url endpoint for this cloud code function.
45
+ # @return [Parse::Response]
29
46
  def update_function(functionName, url)
30
- # interesting trick. If you add _method => "PUT" to the JSON body,
47
+ # If you add _method => "PUT" to the JSON body,
31
48
  # and send it as a POST request and parse will accept it as a PUT.
32
- # They must do this because it is easier to send POST with Ajax.
33
49
  request :put, "#{HOOKS_PREFIX}functions/#{functionName}", body: { url: url }
34
50
  end
35
51
 
52
+ # Remove a registered cloud code webhook function.
53
+ # @param functionName [String] the name of the cloud code function.
54
+ # @return [Parse::Response]
36
55
  def delete_function(functionName)
37
56
  request :put, "#{HOOKS_PREFIX}functions/#{functionName}", body: { __op: "Delete" }
38
57
  end
39
58
 
59
+ # Get the set of registered triggers.
60
+ # @return [Parse::Response]
40
61
  def triggers
41
62
  request :get, "#{HOOKS_PREFIX}triggers"
42
63
  end
43
64
 
65
+ # Fetch information about a registered webhook trigger.
66
+ # @param triggerName [String] the name of the trigger. (ex. beforeSave, afterSave)
67
+ # @param className [String] the name of the Parse collection for the trigger.
68
+ # @return [Parse::Response]
69
+ # @see TRIGGER_NAMES
44
70
  def fetch_trigger(triggerName, className)
45
71
  triggerName = _verify_trigger(triggerName)
46
72
  request :get, "#{HOOKS_PREFIX}triggers/#{className}/#{triggerName}"
47
73
  end
48
74
 
75
+ # Register a new cloud code webhook trigger with an endpoint url.
76
+ # @param triggerName [String] the name of the trigger. (ex. beforeSave, afterSave)
77
+ # @param className [String] the name of the Parse collection for the trigger.
78
+ # @param url [String] the url endpoint for this webhook trigger.
79
+ # @return [Parse::Response]
80
+ # @see Parse::API::Hooks::TRIGGER_NAMES
49
81
  def create_trigger(triggerName, className, url)
50
82
  triggerName = _verify_trigger(triggerName)
51
83
  body = {className: className, triggerName: triggerName, url: url }
52
84
  request :post, "#{HOOKS_PREFIX}triggers", body: body
53
85
  end
54
86
 
87
+ # Updated the registered endpoint for this cloud code webhook trigger.
88
+ # @param triggerName [String] the name of the trigger. (ex. beforeSave, afterSave)
89
+ # @param className [String] the name of the Parse collection for the trigger.
90
+ # @param url [String] the new url endpoint for this webhook trigger.
91
+ # @return [Parse::Response]
92
+ # @see Parse::API::Hooks::TRIGGER_NAMES
55
93
  def update_trigger(triggerName, className, url)
56
94
  triggerName = _verify_trigger(triggerName)
57
95
  request :put, "#{HOOKS_PREFIX}triggers/#{className}/#{triggerName}", body: { url: url }
58
96
  end
59
97
 
98
+ # Remove a registered cloud code webhook trigger.
99
+ # @param triggerName [String] the name of the trigger. (ex. beforeSave, afterSave)
100
+ # @param className [String] the name of the Parse collection for the trigger.
101
+ # @return [Parse::Response]
102
+ # @see Parse::API::Hooks::TRIGGER_NAMES
60
103
  def delete_trigger(triggerName, className)
61
104
  triggerName = _verify_trigger(triggerName)
62
105
  request :put, "#{HOOKS_PREFIX}triggers/#{className}/#{triggerName}", body: { __op: "Delete" }
@@ -9,19 +9,25 @@ module Parse
9
9
  module API
10
10
  # REST API methods for fetching CRUD operations on Parse objects.
11
11
  module Objects
12
-
12
+ # The class prefix for fetching objects.
13
13
  CLASS_PATH_PREFIX = "classes/"
14
+ # The class prefix mapping for fetching objects.
14
15
  PREFIX_MAP = { installation: "installations", _installation: "installations",
15
16
  user: "users", _user: "users",
16
17
  role: "roles", _role: "roles",
17
18
  session: "sessions", _session: "sessions"
18
19
  }.freeze
19
20
 
21
+ # @!visibility private
20
22
  def self.included(base)
21
23
  base.extend(ClassMethods)
22
24
  end
23
25
 
24
26
  module ClassMethods
27
+ # Get the API path for this class.
28
+ # @param className [String] the name of the Parse collection.
29
+ # @param id [String] optional objectId to add at the end of the path.
30
+ # @return [String] the API uri path
25
31
  def uri_path(className, id = nil)
26
32
  if className.is_a?(Parse::Pointer)
27
33
  id = className.id
@@ -37,39 +43,70 @@ module Parse
37
43
 
38
44
  end
39
45
 
46
+ # Get the API path for this class.
47
+ # @param className [String] the name of the Parse collection.
48
+ # @param id [String] optional objectId to add at the end of the path.
49
+ # @return [String] the API uri path
40
50
  def uri_path(className, id = nil)
41
51
  self.class.uri_path(className, id)
42
52
  end
43
53
 
44
- # /1/classes/<className> POST Creating Objects
54
+ # Create an object in a collection.
55
+ # @param className [String] the name of the Parse collection.
56
+ # @param body [Hash] the body of the request.
57
+ # @param opts [Hash] additional options to pass to the {Parse::Client} request.
58
+ # @param headers [Hash] additional HTTP headers to send with the request.
59
+ # @return [Parse::Response]
45
60
  def create_object(className, body = {}, headers: {}, **opts)
46
61
  response = request :post, uri_path(className) , body: body, headers: headers, opts: opts
47
62
  response.parse_class = className if response.present?
48
63
  response
49
64
  end
50
65
 
51
- # /1/classes/<className>/<objectId> DELETE Deleting Objects
66
+ # Delete an object in a collection.
67
+ # @param className [String] the name of the Parse collection.
68
+ # @param id [String] The objectId of the record in the collection.
69
+ # @param opts [Hash] additional options to pass to the {Parse::Client} request.
70
+ # @param headers [Hash] additional HTTP headers to send with the request.
71
+ # @return [Parse::Response]
52
72
  def delete_object(className, id, headers: {}, **opts)
53
73
  response = request :delete, uri_path(className, id), headers: headers, opts: opts
54
74
  response.parse_class = className if response.present?
55
75
  response
56
76
  end
57
77
 
58
- # /1/classes/<className>/<objectId> GET Retrieving Objects
78
+ # Fetch a specific object from a collection.
79
+ # @param className [String] the name of the Parse collection.
80
+ # @param id [String] The objectId of the record in the collection.
81
+ # @param opts [Hash] additional options to pass to the {Parse::Client} request.
82
+ # @param headers [Hash] additional HTTP headers to send with the request.
83
+ # @return [Parse::Response]
59
84
  def fetch_object(className, id, headers: {}, **opts)
60
85
  response = request :get, uri_path(className, id), headers: headers, opts: opts
61
86
  response.parse_class = className if response.present?
62
87
  response
63
88
  end
64
89
 
65
- # /1/classes/<className> GET Queries
90
+ # Fetch a set of matching objects for a query.
91
+ # @param className [String] the name of the Parse collection.
92
+ # @param query [Hash] The set of query constraints.
93
+ # @param opts [Hash] additional options to pass to the {Parse::Client} request.
94
+ # @param headers [Hash] additional HTTP headers to send with the request.
95
+ # @return [Parse::Response]
96
+ # @see Parse::Query
66
97
  def find_objects(className, query = {}, headers: {}, **opts)
67
98
  response = request :get, uri_path(className), query: query, headers: headers, opts: opts
68
99
  response.parse_class = className if response.present?
69
100
  response
70
101
  end
71
102
 
72
- # /1/classes/<className>/<objectId> PUT Updating Objects
103
+ # Update an object in a collection.
104
+ # @param className [String] the name of the Parse collection.
105
+ # @param id [String] The objectId of the record in the collection.
106
+ # @param body [Hash] The key value pairs to update.
107
+ # @param opts [Hash] additional options to pass to the {Parse::Client} request.
108
+ # @param headers [Hash] additional HTTP headers to send with the request.
109
+ # @return [Parse::Response]
73
110
  def update_object(className, id, body = {}, headers: {}, **opts)
74
111
  response = request :put, uri_path(className,id) , body: body, headers: headers, opts: opts
75
112
  response.parse_class = className if response.present?
@@ -4,9 +4,14 @@
4
4
  module Parse
5
5
 
6
6
  module API
7
- #object fetch methods
7
+ # Defines the Parse Push notification service interface for the Parse REST API
8
8
  module Push
9
9
  PUSH_PATH = "push"
10
+
11
+ # Update the schema for a collection.
12
+ # @param payload [Hash] the paylod for the Push notification.
13
+ # @return [Parse::Response]
14
+ # @see https://parseplatform.github.io/docs/rest/guide/#sending-pushes Sending Pushes
10
15
  def push(payload = {})
11
16
  request :post, PUSH_PATH, body: payload.as_json
12
17
  end
@@ -4,17 +4,31 @@
4
4
  module Parse
5
5
 
6
6
  module API
7
- #object fetch methods
7
+ # Defines the Schema interface for the Parse REST API
8
8
  module Schema
9
9
  SCHEMAS_PATH = "schemas"
10
+
11
+ # Get the schema for a collection.
12
+ # @param className [String] the name of the remote Parse collection.
13
+ # @return [Parse::Response]
10
14
  def schema(className)
11
15
  request :get, "#{SCHEMAS_PATH}/#{className}"
12
16
  end
13
17
 
18
+ # Create a new collection with the specific schema.
19
+ # @param className [String] the name of the remote Parse collection.
20
+ # @param schema [Hash] the schema hash. This is a specific format specified by
21
+ # Parse.
22
+ # @return [Parse::Response]
14
23
  def create_schema(className, schema)
15
24
  request :post, "#{SCHEMAS_PATH}/#{className}", body: schema
16
25
  end
17
26
 
27
+ # Update the schema for a collection.
28
+ # @param className [String] the name of the remote Parse collection.
29
+ # @param schema [Hash] the schema hash. This is a specific format specified by
30
+ # Parse.
31
+ # @return [Parse::Response]
18
32
  def update_schema(className, schema)
19
33
  request :put, "#{SCHEMAS_PATH}/#{className}", body: schema
20
34
  end
@@ -4,9 +4,14 @@
4
4
  module Parse
5
5
 
6
6
  module API
7
+ # Defines the Session class interface for the Parse REST API
7
8
  module Sessions
8
9
  SESSION_PATH_PREFIX = "sessions"
9
10
 
11
+ # Fetch a session record for a given session token.
12
+ # @param session_token [String] an active session token.
13
+ # @param opts [Hash] additional options to pass to the {Parse::Client} request.
14
+ # @return [Parse::Response]
10
15
  def fetch_session(session_token, **opts)
11
16
  opts.merge!({use_master_key: false, cache: false})
12
17
  headers = {Parse::Protocol::SESSION_TOKEN => session_token}
@@ -6,24 +6,38 @@ require 'open-uri'
6
6
  module Parse
7
7
 
8
8
  module API
9
+ # Defines the User class interface for the Parse REST API
9
10
  module Users
10
- # Note that Parse::Objects mainly use the objects.rb API since we can
11
- # detect class names to proper URI handlers
11
+
12
12
  USER_PATH_PREFIX = "users"
13
13
  LOGOUT_PATH = "logout"
14
14
  LOGIN_PATH = "login"
15
15
  REQUEST_PASSWORD_RESET = "requestPasswordReset"
16
16
 
17
+ # Fetch a {Parse::User} for a given objectId.
18
+ # @param id [String] the user objectid
19
+ # @param opts [Hash] additional options to pass to the {Parse::Client} request.
20
+ # @param headers [Hash] additional HTTP headers to send with the request.
21
+ # @return [Parse::Response]
17
22
  def fetch_user(id, headers: {}, **opts)
18
23
  request :get, "#{USER_PATH_PREFIX}/#{id}", headers: headers, opts: opts
19
24
  end
20
25
 
26
+ # Find users matching a set of constraints.
27
+ # @param query [Hash] query parameters.
28
+ # @param opts [Hash] additional options to pass to the {Parse::Client} request.
29
+ # @param headers [Hash] additional HTTP headers to send with the request.
30
+ # @return [Parse::Response]
21
31
  def find_users(query = {}, headers: {}, **opts)
22
32
  response = request :get, USER_PATH_PREFIX, query: query, headers: headers, opts: opts
23
33
  response.parse_class = Parse::Model::CLASS_USER
24
34
  response
25
35
  end
26
36
 
37
+ # Find user matching this active session token.
38
+ # @param opts [Hash] additional options to pass to the {Parse::Client} request.
39
+ # @param headers [Hash] additional HTTP headers to send with the request.
40
+ # @return [Parse::Response]
27
41
  def current_user(session_token, headers: {}, **opts)
28
42
  headers.merge!({Parse::Protocol::SESSION_TOKEN => session_token})
29
43
  response = request :get, "#{USER_PATH_PREFIX}/me", headers: headers, opts: opts
@@ -31,6 +45,11 @@ module Parse
31
45
  response
32
46
  end
33
47
 
48
+ # Create a new user.
49
+ # @param body [Hash] a hash of values related to your _User schema.
50
+ # @param opts [Hash] additional options to pass to the {Parse::Client} request.
51
+ # @param headers [Hash] additional HTTP headers to send with the request.
52
+ # @return [Parse::Response]
34
53
  def create_user(body, headers: {}, **opts)
35
54
  headers.merge!({ Parse::Protocol::REVOCABLE_SESSION => '1'})
36
55
  if opts[:session_token].present?
@@ -41,27 +60,56 @@ module Parse
41
60
  response
42
61
  end
43
62
 
63
+ # Update a {Parse::User} record given an objectId.
64
+ # @param id [String] the Parse user objectId.
65
+ # @param body [Hash] the body of the API request.
66
+ # @param opts [Hash] additional options to pass to the {Parse::Client} request.
67
+ # @param headers [Hash] additional HTTP headers to send with the request.
68
+ # @return [Parse::Response]
44
69
  def update_user(id, body = {}, headers: {}, **opts)
45
70
  response = request :put, "#{USER_PATH_PREFIX}/#{id}", body: body, opts: opts
46
71
  response.parse_class = Parse::Model::CLASS_USER
47
72
  response
48
73
  end
49
74
 
50
- # deleting or unlinking is done by setting the authData of the service name to nil
75
+ # Set the authentication service OAUth data for a user. Deleting or unlinking
76
+ # is done by setting the authData of the service name to nil.
77
+ # @param id [String] the Parse user objectId.
78
+ # @param service_name [Symbol] the name of the OAuth service.
79
+ # @param auth_data [Hash] the hash data related to the third-party service.
80
+ # @param opts [Hash] additional options to pass to the {Parse::Client} request.
81
+ # @param headers [Hash] additional HTTP headers to send with the request.
82
+ # @return [Parse::Response]
51
83
  def set_service_auth_data(id, service_name, auth_data, headers: {}, **opts)
52
84
  body = { authData: { service_name => auth_data } }
53
85
  update_user(id, body, opts)
54
86
  end
55
87
 
88
+ # Delete a {Parse::User} record given an objectId.
89
+ # @param id [String] the Parse user objectId.
90
+ # @param opts [Hash] additional options to pass to the {Parse::Client} request.
91
+ # @param headers [Hash] additional HTTP headers to send with the request.
92
+ # @return [Parse::Response]
56
93
  def delete_user(id, headers: {}, **opts)
57
94
  request :delete, "#{USER_PATH_PREFIX}/#{id}", headers: headers, opts: opts
58
95
  end
59
96
 
60
- def request_password_reset(email, **opts)
97
+ # Request a password reset for a registered email.
98
+ # @param email [String] the Parse user email.
99
+ # @param opts [Hash] additional options to pass to the {Parse::Client} request.
100
+ # @param headers [Hash] additional HTTP headers to send with the request.
101
+ # @return [Parse::Response]
102
+ def request_password_reset(email, headers: {}, **opts)
61
103
  body = {email: email}
62
- request :post, REQUEST_PASSWORD_RESET, body: body, opts: opts
104
+ request :post, REQUEST_PASSWORD_RESET, body: body, opts: opts, headers: headers
63
105
  end
64
106
 
107
+ # Login a user.
108
+ # @param username [String] the Parse user username.
109
+ # @param password [String] the Parse user's associated password.
110
+ # @param headers [Hash] additional HTTP headers to send with the request.
111
+ # @param opts [Hash] additional options to pass to the {Parse::Client} request.
112
+ # @return [Parse::Response]
65
113
  def login(username, password, headers: {}, **opts)
66
114
  # Probably pass Installation-ID as header
67
115
  query = { username: username, password: password }
@@ -72,6 +120,11 @@ module Parse
72
120
  response
73
121
  end
74
122
 
123
+ # Logout a user by deleting the associated session.
124
+ # @param session_token [String] the Parse user session token to delete.
125
+ # @param headers [Hash] additional HTTP headers to send with the request.
126
+ # @param opts [Hash] additional options to pass to the {Parse::Client} request.
127
+ # @return [Parse::Response]
75
128
  def logout(session_token, headers: {}, **opts)
76
129
  headers.merge!({ Parse::Protocol::SESSION_TOKEN => session_token})
77
130
  opts.merge!({use_master_key: false, session_token: session_token})
@@ -79,6 +132,12 @@ module Parse
79
132
  end
80
133
 
81
134
  # Signup a user given a username, password and, optionally, their email.
135
+ # @param username [String] the Parse user username.
136
+ # @param password [String] the Parse user's associated password.
137
+ # @param email [String] the desired Parse user's email.
138
+ # @param body [Hash] additional property values to pass when creating the user record.
139
+ # @param opts [Hash] additional options to pass to the {Parse::Client} request.
140
+ # @return [Parse::Response]
82
141
  def signup(username, password, email = nil, body: {}, **opts)
83
142
  body = body.merge({ username: username, password: password })
84
143
  body[:email] = email || body[:email]
@@ -7,22 +7,37 @@ require 'active_support'
7
7
  require 'active_support/core_ext'
8
8
 
9
9
  require_relative 'protocol'
10
- # All Parse requests require authentication with specific header values.
11
- # This middleware takes all outgoing requests and adds the proper header values
12
- # base on the client configuration.
10
+
13
11
  module Parse
14
12
 
15
13
  module Middleware
16
-
14
+ # This middleware handles sending the proper authentication headers to the
15
+ # Parse REST API endpoint.
17
16
  class Authentication < Faraday::Middleware
18
17
  include Parse::Protocol
19
- DISABLE_MASTER_KEY = "X-Disable-Parse-Master-Key"
18
+ DISABLE_MASTER_KEY = "X-Disable-Parse-Master-Key".freeze
19
+ # @return [String] the application id for this Parse endpoint.
20
20
  attr_accessor :application_id
21
+ # @return [String] the REST API Key for this Parse endpoint.
21
22
  attr_accessor :api_key
23
+ # The Master key API Key for this Parse endpoint. This is optional. If
24
+ # provided, it will be sent in every request.
25
+ # @return [String]
22
26
  attr_accessor :master_key
23
- # The options hash should contain the proper keys to be added to the request.
24
- def initialize(app, options = {})
25
- super(app)
27
+
28
+ #
29
+ # @param adapter [Faraday::Adapter] An instance of the Faraday adapter
30
+ # used for the connection. Defaults Faraday::Adapter::NetHttp.
31
+ # @param options [Hash] the options containing Parse authentication data.
32
+ # @option options [String] :application_id the application id.
33
+ # @option options [String] :api_key the REST API key.
34
+ # @option options [String] :master_key the Master Key for this application.
35
+ # If it is set, it will be sent on every request unless this middleware sees
36
+ # {DISABLE_MASTER_KEY} as an entry in the headers section.
37
+ # @option options [String] :content_type the content type format header. Defaults to
38
+ # {Parse::Protocol::CONTENT_TYPE_FORMAT}.
39
+ def initialize(adapter, options = {})
40
+ super(adapter)
26
41
  @application_id = options[:application_id]
27
42
  @api_key = options[:api_key]
28
43
  @master_key = options[:master_key]
@@ -30,10 +45,12 @@ module Parse
30
45
  end
31
46
 
32
47
  # we dup the call for thread-safety
48
+ # @!visibility private
33
49
  def call(env)
34
50
  dup.call!(env)
35
51
  end
36
52
 
53
+ # @!visibility private
37
54
  def call!(env)
38
55
  # We add the main Parse protocol headers
39
56
  headers = {}
@@ -0,0 +1,206 @@
1
+
2
+ require_relative 'request'
3
+ require_relative 'response'
4
+
5
+
6
+ module Parse
7
+ # Create a new batch operation.
8
+ # @param reqs [Array<Parse::Request>] a set of requests to batch.
9
+ # @return [BatchOperation] a new {BatchOperation} with the given change requests.
10
+ def self.batch(reqs = nil)
11
+ BatchOperation.new(reqs)
12
+ end
13
+
14
+ # This class provides a standard way to submit, manage and process batch operations
15
+ # for Parse::Objects and associations.
16
+ #
17
+ # Batch requests are supported implicitly and intelligently through an
18
+ # extension of array. When an array of Parse::Object subclasses is saved,
19
+ # Parse-Stack will batch all possible save operations for the objects in the
20
+ # array that have changed. It will also batch save 50 at a time until all items
21
+ # in the array are saved. Note: Parse does not allow batch saving Parse::User objects.
22
+ #
23
+ # songs = Songs.first 1000 #first 1000 songs
24
+ # songs.each do |song|
25
+ # # ... modify song ...
26
+ # end
27
+ #
28
+ # # will batch save 50 items at a time until all are saved.
29
+ # songs.save
30
+ #
31
+ # The objects do not have to be of the same collection in order to be supported in the
32
+ # batch request.
33
+ # @see Array.save
34
+ # @see Array.destroy
35
+ class BatchOperation
36
+
37
+ attr_accessor :requests, :responses
38
+ include Enumerable
39
+
40
+ # @return [Parse::Client] the client to be used for the request.
41
+ def client
42
+ @client ||= Parse::Client.client
43
+ end
44
+
45
+ # @param reqs [Array<Parse::Request>] an array of requests.
46
+ def initialize(reqs = nil)
47
+ @requests = []
48
+ @responses = []
49
+ reqs = [reqs] unless reqs.is_a?(Enumerable)
50
+ reqs.each { |r| add(r) } if reqs.is_a?(Enumerable)
51
+ end
52
+
53
+ # Add an additional request to this batch.
54
+ # @overload add(req)
55
+ # @param req [Parse::Request] the request to append.
56
+ # @return [Array<Parse::Request>] the set of requests.
57
+ # @overload add(batch)
58
+ # @param req [Parse::BatchOperation] add all the requests from this batch operation.
59
+ # @return [Array<Parse::Request>] the set of requests.
60
+ def add(req)
61
+ if req.respond_to?(:change_requests)
62
+ requests = req.change_requests.select { |r| r.is_a?(Parse::Request) }
63
+ @requests += requests
64
+ elsif req.is_a?(Array)
65
+ requests = req.select { |r| r.is_a?(Parse::Request) }
66
+ @requests += requests
67
+ elsif req.is_a?(BatchOperation)
68
+ @requests += req.requests if req.is_a?(BatchOperation)
69
+ else
70
+ @requests.push(req) if req.is_a?(Parse::Request)
71
+ end
72
+ @requests
73
+ end
74
+
75
+ # This method is for interoperability with Parse::Object instances.
76
+ # @see Parse::Object#change_requests
77
+ def change_requests
78
+ @requests
79
+ end
80
+
81
+ # @return [Array]
82
+ def each
83
+ return enum_for(:each) unless block_given?
84
+ @requests.each(&Proc.new)
85
+ end
86
+
87
+ # @return [Hash] a formatted payload for the batch request.
88
+ def as_json(*args)
89
+ { requests: requests }.as_json
90
+ end
91
+
92
+ # @return [Integer] the number of requests in the batch.
93
+ def count
94
+ @requests.count
95
+ end
96
+
97
+ # Remove all requests in this batch.
98
+ # @return [Array]
99
+ def clear!
100
+ @requests.clear
101
+ end
102
+
103
+ # @return [Boolean] true if the request was successful.
104
+ def success?
105
+ return false if @responses.empty?
106
+ @responses.compact.all?(&:success?)
107
+ end
108
+
109
+ # @return [Boolean] true if the request had an error.
110
+ def error?
111
+ return false if @responses.empty?
112
+ ! success?
113
+ end
114
+
115
+ # Submit the batch operation in chunks until they are all complete. In general,
116
+ # Parse limits requests in each batch to 50 and it is possible that a {BatchOperation}
117
+ # instance contains more than 50 requests. This method will slice up the array of
118
+ # request and send them based on the `segment` amount until they have all been submitted.
119
+ # @param segment [Integer] the number of requests to send in each batch. Default 50.
120
+ # @return [Array<Parse::Response>] the corresponding set of responses for
121
+ # each request in the batch.
122
+ def submit(segment = 50)
123
+ @responses = []
124
+ @requests.uniq!(&:signature)
125
+ @requests.each_slice(segment) do |slice|
126
+ @responses << client.batch_request( BatchOperation.new(slice) )
127
+ #throttle
128
+ # sleep (slice.count.to_f / MAX_REQ_SEC.to_f )
129
+ end
130
+ @responses.flatten!
131
+ #puts "Requests: #{@requests.count} == Response: #{@responses.count}"
132
+ @requests.zip(@responses).each(&Proc.new) if block_given?
133
+ @responses
134
+ end
135
+ alias_method :save, :submit
136
+
137
+
138
+ end
139
+ end
140
+
141
+ class Array
142
+
143
+ # Submit a batch request for deleting a set of Parse::Objects.
144
+ # @example
145
+ # # assume Post and Author are Parse models
146
+ # author = Author.first
147
+ # posts = Post.all author: author
148
+ # posts.destroy # batch destroy request
149
+ # @return [Parse::BatchOperation] the batch operation performed.
150
+ # @see Parse::BatchOperation
151
+ def destroy
152
+ batch = Parse::BatchOperation.new
153
+ each do |o|
154
+ next unless o.respond_to?(:destroy_request)
155
+ r = o.destroy_request
156
+ batch.add(r) unless r.nil?
157
+ end
158
+ batch.submit
159
+ batch
160
+ end
161
+
162
+ # Submit a batch request for deleting a set of Parse::Objects.
163
+ # Batch requests are supported implicitly and intelligently through an
164
+ # extension of array. When an array of Parse::Object subclasses is saved,
165
+ # Parse-Stack will batch all possible save operations for the objects in the
166
+ # array that have changed. It will also batch save 50 at a time until all items
167
+ # in the array are saved. Note: Parse does not allow batch saving Parse::User objects.
168
+ # @note The objects of the array to be saved do not all have to be of the same collection.
169
+ # @param merge [Boolean] whether to merge the updated changes to the series of
170
+ # objects back to the original ones submitted. If you don't need the original objects
171
+ # to be updated with the changes, set this to false for improved performance.
172
+ # @param force [Boolean] Do not skip objects that do not have pending changes (dirty tracking).
173
+ # @example
174
+ # # assume Post and Author are Parse models
175
+ # author = Author.first
176
+ # posts = Post.first 100
177
+ # posts.each { |post| post.author = author }
178
+ # posts.save # batch save
179
+ # @return [Parse::BatchOperation] the batch operation performed.
180
+ # @see Parse::BatchOperation
181
+ def save(merge: true, force: false)
182
+ batch = Parse::BatchOperation.new
183
+ objects = {}
184
+ each do |o|
185
+ next unless o.is_a?(Parse::Object)
186
+ objects[o.object_id] = o
187
+ batch.add o.change_requests(force)
188
+ end
189
+ if merge == false
190
+ batch.submit
191
+ return batch
192
+ end
193
+ #rebind updates
194
+ batch.submit do |request, response|
195
+ next unless request.tag.present? && response.present? && response.success?
196
+ o = objects[request.tag]
197
+ next unless o.is_a?(Parse::Object)
198
+ result = response.result
199
+ o.id = result['objectId'] if o.id.blank?
200
+ o.set_attributes!(result)
201
+ o.clear_changes!
202
+ end
203
+ batch
204
+ end #save!
205
+
206
+ end