parse-stack 1.0.0

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 (59) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +6 -0
  3. data/Gemfile.lock +77 -0
  4. data/LICENSE +20 -0
  5. data/README.md +1281 -0
  6. data/Rakefile +12 -0
  7. data/bin/console +20 -0
  8. data/bin/server +10 -0
  9. data/bin/setup +7 -0
  10. data/lib/parse/api/all.rb +13 -0
  11. data/lib/parse/api/analytics.rb +16 -0
  12. data/lib/parse/api/apps.rb +37 -0
  13. data/lib/parse/api/batch.rb +148 -0
  14. data/lib/parse/api/cloud_functions.rb +18 -0
  15. data/lib/parse/api/config.rb +22 -0
  16. data/lib/parse/api/files.rb +21 -0
  17. data/lib/parse/api/hooks.rb +68 -0
  18. data/lib/parse/api/objects.rb +77 -0
  19. data/lib/parse/api/push.rb +16 -0
  20. data/lib/parse/api/schemas.rb +25 -0
  21. data/lib/parse/api/sessions.rb +11 -0
  22. data/lib/parse/api/users.rb +43 -0
  23. data/lib/parse/client.rb +225 -0
  24. data/lib/parse/client/authentication.rb +59 -0
  25. data/lib/parse/client/body_builder.rb +69 -0
  26. data/lib/parse/client/caching.rb +103 -0
  27. data/lib/parse/client/protocol.rb +15 -0
  28. data/lib/parse/client/request.rb +43 -0
  29. data/lib/parse/client/response.rb +116 -0
  30. data/lib/parse/model/acl.rb +182 -0
  31. data/lib/parse/model/associations/belongs_to.rb +121 -0
  32. data/lib/parse/model/associations/collection_proxy.rb +202 -0
  33. data/lib/parse/model/associations/has_many.rb +218 -0
  34. data/lib/parse/model/associations/pointer_collection_proxy.rb +71 -0
  35. data/lib/parse/model/associations/relation_collection_proxy.rb +134 -0
  36. data/lib/parse/model/bytes.rb +50 -0
  37. data/lib/parse/model/core/actions.rb +499 -0
  38. data/lib/parse/model/core/properties.rb +377 -0
  39. data/lib/parse/model/core/querying.rb +100 -0
  40. data/lib/parse/model/core/schema.rb +92 -0
  41. data/lib/parse/model/date.rb +50 -0
  42. data/lib/parse/model/file.rb +127 -0
  43. data/lib/parse/model/geopoint.rb +98 -0
  44. data/lib/parse/model/model.rb +120 -0
  45. data/lib/parse/model/object.rb +347 -0
  46. data/lib/parse/model/pointer.rb +106 -0
  47. data/lib/parse/model/push.rb +99 -0
  48. data/lib/parse/query.rb +378 -0
  49. data/lib/parse/query/constraint.rb +130 -0
  50. data/lib/parse/query/constraints.rb +176 -0
  51. data/lib/parse/query/operation.rb +66 -0
  52. data/lib/parse/query/ordering.rb +49 -0
  53. data/lib/parse/stack.rb +11 -0
  54. data/lib/parse/stack/version.rb +5 -0
  55. data/lib/parse/webhooks.rb +228 -0
  56. data/lib/parse/webhooks/payload.rb +115 -0
  57. data/lib/parse/webhooks/registration.rb +139 -0
  58. data/parse-stack.gemspec +45 -0
  59. metadata +340 -0
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs << 'lib/parse/stack'
8
+ t.test_files = FileList['test/lib/**/*_test.rb']
9
+ t.verbose = true
10
+ end
11
+
12
+ task :default => :test
data/bin/console ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "parse/stack"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ ENV["PARSE_APP_ID"] ||= "vesNFfsUUjbiWtbFljEV41RwXVJ2vw7HPFwLdisL".freeze
11
+ ENV["PARSE_API_KEY"] ||= "FndxgK7oGnjsRznFbMs8r7TG5WHwFSOmgWQr6Qui".freeze
12
+ ENV["PARSE_MASTER_KEY"] ||= "oZs3AlBMKg6JDe82XVm7NJpO5GmvibiXbpMtwEHi".freeze
13
+
14
+ require "pry"
15
+ Pry.start
16
+
17
+ #
18
+ # require "irb"
19
+ # IRB.start
20
+ #Rack::Server.start :app => HelloWorldApp
data/bin/server ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "parse/stack"
5
+
6
+ require "rack"
7
+ require "rack/server"
8
+ require "puma"
9
+ Rack::Handler::WEBrick = Rack::Handler.get(:puma)
10
+ Rack::Server.start :app => Parse::Webhooks
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,13 @@
1
+ require_relative '../client'
2
+ require_relative "analytics"
3
+ require_relative "apps"
4
+ require_relative "batch"
5
+ require_relative "config"
6
+ require_relative "files"
7
+ require_relative "cloud_functions"
8
+ require_relative "hooks"
9
+ require_relative "objects"
10
+ require_relative "push"
11
+ require_relative "schemas"
12
+ require_relative "sessions"
13
+ require_relative "users"
@@ -0,0 +1,16 @@
1
+
2
+
3
+
4
+ module Parse
5
+
6
+ module API
7
+ module Analytics
8
+
9
+ def send_analytics(event_name, data = {})
10
+ request :post, "/1/events/#{event_name}", body: data
11
+ end
12
+
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,37 @@
1
+
2
+ module Parse
3
+
4
+ module API
5
+
6
+ module Apps
7
+
8
+
9
+ def fetch_app_keys(appid, email, password)
10
+ headers = {}
11
+ headers.merge!( { 'X-Parse-Email' => email, 'X-Parse-Password' => password } )
12
+ request :get, "/1/apps/#{appid}", headers: headers
13
+ end
14
+
15
+ def fetch_apps(email, password)
16
+ headers = {}
17
+ headers.merge!( { 'X-Parse-Email' => email, 'X-Parse-Password' => password } )
18
+ request :get, "/1/apps", headers: headers
19
+ end
20
+
21
+ def create_app(opts, email, password)
22
+ headers = {}
23
+ headers.merge!( { 'X-Parse-Email' => email, 'X-Parse-Password' => password } )
24
+ request :post, "/1/apps", body: opts, headers: headers
25
+ end
26
+
27
+ def update_app(appid, opts, email, password)
28
+ headers = {}
29
+ headers.merge!( { 'X-Parse-Email' => email, 'X-Parse-Password' => password } )
30
+ request :put, "/1/apps/#{appid}", body: opts, headers: headers
31
+ end
32
+
33
+
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,148 @@
1
+ require 'parallel'
2
+
3
+ class Array
4
+
5
+ def destroy
6
+ batch = Parse::BatchOperation.new
7
+ each do |o|
8
+ next unless o.respond_to?(:destroy_request)
9
+ r = o.destroy_request
10
+ batch.add(r) unless r.nil?
11
+ end
12
+ batch.submit
13
+ batch
14
+ end
15
+
16
+ def save(merge: true, force: false)
17
+ batch = Parse::BatchOperation.new
18
+ objects = {}
19
+ each do |o|
20
+ next unless o.is_a?(Parse::Object)
21
+ objects[o.object_id] = o
22
+ batch.add o.change_requests(force)
23
+ end
24
+ if merge == false
25
+ batch.submit
26
+ return batch
27
+ end
28
+ #rebind updates
29
+ batch.submit do |request, response|
30
+ next unless request.tag.present? && response.present? && response.success?
31
+ o = objects[request.tag]
32
+ next unless o.is_a?(Parse::Object)
33
+ result = response.result
34
+ o.id = result['objectId'] if o.id.blank?
35
+ o.set_attributes!(result)
36
+ o.clear_changes!
37
+ end
38
+ batch
39
+ end #save!
40
+
41
+ end
42
+
43
+ module Parse
44
+
45
+ def self.batch(reqs = nil)
46
+ BatchOperation.new(reqs)
47
+ end
48
+
49
+ class BatchOperation
50
+ MAX_REQ_SEC = 30
51
+
52
+ attr_accessor :requests, :responses
53
+ include Enumerable
54
+
55
+ def client
56
+ @client ||= Parse::Client.session
57
+ end
58
+
59
+ def initialize(reqs = nil)
60
+ @requests = []
61
+ @responses = []
62
+ reqs = [reqs] unless reqs.is_a?(Enumerable)
63
+ reqs.each { |r| add(r) } if reqs.is_a?(Enumerable)
64
+ end
65
+
66
+ def add(req)
67
+ if req.respond_to?(:change_requests)
68
+ requests = req.change_requests.select { |r| r.is_a?(Parse::Request) }
69
+ @requests += requests
70
+ elsif req.is_a?(Array)
71
+ requests = req.select { |r| r.is_a?(Parse::Request) }
72
+ @requests += requests
73
+ elsif req.is_a?(BatchOperation)
74
+ @requests += req.requests if req.is_a?(BatchOperation)
75
+ else
76
+ @requests.push(req) if req.is_a?(Parse::Request)
77
+ end
78
+ @requests
79
+ end
80
+
81
+ # make Batching interoperable with object methods. This allows adding a batch
82
+ # to another batch.
83
+ def change_requests
84
+ @requests
85
+ end
86
+
87
+ def each
88
+ return enum_for(:each) unless block_given?
89
+ @requests.each(&Proc.new)
90
+ self
91
+ end
92
+
93
+ def as_json(*args)
94
+ { requests: requests }.as_json
95
+ end
96
+
97
+ def count
98
+ @requests.count
99
+ end
100
+
101
+ def clear!
102
+ @requests.clear
103
+ end
104
+
105
+ def success?
106
+ return false if @responses.empty?
107
+ @responses.compact.all?(&:success?)
108
+ end
109
+
110
+ def error?
111
+ return false if @responses.empty?
112
+ ! success?
113
+ end
114
+ # Note that N requests sent in a batch will still count toward
115
+ # your request limit as N requests.
116
+ def submit(segment = 50)
117
+ @responses = []
118
+ @requests.uniq!(&:signature)
119
+ @requests.each_slice(segment) do |slice|
120
+ @responses << client.batch_request( BatchOperation.new(slice) )
121
+ #throttle
122
+ sleep (slice.count.to_f / MAX_REQ_SEC.to_f )
123
+ end
124
+ @responses.flatten!
125
+ #puts "Requests: #{@requests.count} == Response: #{@responses.count}"
126
+ @requests.zip(@responses).each(&Proc.new) if block_given?
127
+ @responses
128
+ end
129
+ alias_method :save, :submit
130
+
131
+
132
+ end
133
+
134
+ module API
135
+ #object fetch methods
136
+
137
+ module Batch
138
+ def batch_request(batch_operations)
139
+ unless batch_operations.is_a?(Parse::BatchOperation)
140
+ batch_operations = Parse::BatchOperation.new batch_operations
141
+ end
142
+ response = request(:post, "/1/batch", body: batch_operations.as_json)
143
+ response.success? && response.batch? ? response.batch_responses : response
144
+ end
145
+
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,18 @@
1
+
2
+ module Parse
3
+
4
+ module API
5
+ module CloudFunctions
6
+
7
+ def call_function(name, body = {})
8
+ request :post, "/1/functions/#{name}", body: body
9
+ end
10
+
11
+ def trigger_job(name, body = {})
12
+ request :post, "/1/jobs/#{name}", body: body
13
+ end
14
+
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,22 @@
1
+
2
+ module Parse
3
+
4
+ module API
5
+ #object fetch methods
6
+ module Config
7
+ attr_accessor :config
8
+
9
+ def config
10
+ if @config.nil?
11
+ response = request :get, "/1/config".freeze
12
+ unless response.error?
13
+ @config = response.result["params"]
14
+ end
15
+ end
16
+ @config
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,21 @@
1
+
2
+ module Parse
3
+
4
+ module API
5
+ #object fetch methods
6
+ module Files
7
+
8
+ # /1/classes/<className> POST Creating Objects
9
+ def create_file(fileName, data = {}, content_type = nil)
10
+ headers = {}
11
+ headers.merge!( { Parse::Protocol::CONTENT_TYPE => content_type.to_s } ) if content_type.present?
12
+ response = request :post, "/1/files/#{fileName}", body: data, headers: headers
13
+ response.parse_class = "_File".freeze
14
+ response
15
+ end
16
+
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,68 @@
1
+
2
+
3
+
4
+ module Parse
5
+
6
+
7
+ module API
8
+ module Hooks
9
+ HOOKS_PREFIX = "/1/hooks/".freeze
10
+ TRIGGER_NAMES = [:beforeSave, :afterSave, :beforeDelete, :afterDelete].freeze
11
+ def _verify_trigger(triggerName)
12
+ triggerName = triggerName.to_s.camelize(:lower).to_sym
13
+ raise "Invalid trigger name #{triggerName}" unless TRIGGER_NAMES.include?(triggerName)
14
+ triggerName
15
+ end
16
+
17
+ def functions
18
+ request :get, "#{HOOKS_PREFIX}functions"
19
+ end
20
+
21
+ def fetch_function(functionName)
22
+ request :get, "#{HOOKS_PREFIX}functions/#{functionName}"
23
+ end
24
+
25
+ def create_function(functionName, url)
26
+ request :post, "#{HOOKS_PREFIX}functions", body: {functionName: functionName, url: url}
27
+ end
28
+
29
+ def update_function(functionName, url)
30
+ # interesting trick. If you add _method => "PUT" to the JSON body,
31
+ # 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
+ request :put, "#{HOOKS_PREFIX}functions/#{functionName}", body: { url: url }
34
+ end
35
+
36
+ def delete_function(functionName)
37
+ request :put, "#{HOOKS_PREFIX}functions/#{functionName}", body: { __op: "Delete" }
38
+ end
39
+
40
+ def triggers
41
+ request :get, "#{HOOKS_PREFIX}triggers"
42
+ end
43
+
44
+ def fetch_trigger(triggerName, className)
45
+ triggerName = _verify_trigger(triggerName)
46
+ request :get, "#{HOOKS_PREFIX}triggers/#{className}/#{triggerName}"
47
+ end
48
+
49
+ def create_trigger(triggerName, className, url)
50
+ triggerName = _verify_trigger(triggerName)
51
+ body = {className: className, triggerName: triggerName, url: url }
52
+ request :post, "#{HOOKS_PREFIX}triggers", body: body
53
+ end
54
+
55
+ def update_trigger(triggerName, className, url)
56
+ triggerName = _verify_trigger(triggerName)
57
+ request :put, "#{HOOKS_PREFIX}triggers/#{className}/#{triggerName}", body: { url: url }
58
+ end
59
+
60
+ def delete_trigger(triggerName, className)
61
+ triggerName = _verify_trigger(triggerName)
62
+ request :put, "#{HOOKS_PREFIX}triggers/#{className}/#{triggerName}", body: { __op: "Delete" }
63
+ end
64
+
65
+ end
66
+ end
67
+
68
+ end
@@ -0,0 +1,77 @@
1
+
2
+ module Parse
3
+
4
+ module API
5
+ #object fetch methods
6
+ module Objects
7
+
8
+ CLASS_PATH_PREFIX = "/1/classes/".freeze
9
+ PREFIX_MAP = { installation: "installations", _installation: "installations",
10
+ user: "users", _user: "users",
11
+ role: "roles", _role: "roles",
12
+ session: "sessions", _session: "sessions"
13
+ }.freeze
14
+
15
+ def self.included(base)
16
+ base.extend(ClassMethods)
17
+ end
18
+
19
+ module ClassMethods
20
+ def uri_path(className, id = nil)
21
+ if className.is_a?(Parse::Pointer)
22
+ id = className.id
23
+ className = className.parse_class
24
+ end
25
+ uri = "#{CLASS_PATH_PREFIX}#{className}"
26
+ class_prefix = className.downcase.to_sym
27
+ if PREFIX_MAP.has_key?(class_prefix)
28
+ uri = "/1/#{PREFIX_MAP[class_prefix]}/"
29
+ end
30
+ id.present? ? "#{uri}/#{id}" : uri
31
+ end
32
+
33
+ end
34
+
35
+ def uri_path(className, id = nil)
36
+ self.class.uri_path(className, id)
37
+ end
38
+
39
+ # /1/classes/<className> POST Creating Objects
40
+ def create_object(className, data = {})
41
+ response = request :post, uri_path(className) , body: data
42
+ response.parse_class = className if response.present?
43
+ response
44
+ end
45
+
46
+ # /1/classes/<className>/<objectId> DELETE Deleting Objects
47
+ def delete_object(className, id)
48
+ response = request :delete, uri_path(className, id)
49
+ response.parse_class = className if response.present?
50
+ response
51
+ end
52
+
53
+ # /1/classes/<className>/<objectId> GET Retrieving Objects
54
+ def fetch_object(className, id)
55
+ response = request :get, uri_path(className, id)
56
+ response.parse_class = className if response.present?
57
+ response
58
+ end
59
+
60
+ # /1/classes/<className> GET Queries
61
+ def find_objects(className, query = {})
62
+ response = request :get, uri_path(className), query: query
63
+ response.parse_class = className if response.present?
64
+ response
65
+ end
66
+
67
+ # /1/classes/<className>/<objectId> PUT Updating Objects
68
+ def update_object(className, id, data = {})
69
+ response = request :put, uri_path(className,id) , body: data
70
+ response.parse_class = className if response.present?
71
+ response
72
+ end
73
+
74
+ end #Objects
75
+ end #API
76
+
77
+ end