parse-stack 1.4.3 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changes.md +52 -39
- data/Gemfile.lock +2 -2
- data/README.md +609 -124
- data/bin/console +0 -9
- data/lib/parse/api/all.rb +3 -0
- data/lib/parse/api/analytics.rb +2 -2
- data/lib/parse/api/apps.rb +15 -17
- data/lib/parse/api/batch.rb +4 -1
- data/lib/parse/api/cloud_functions.rb +2 -0
- data/lib/parse/api/config.rb +14 -2
- data/lib/parse/api/files.rb +6 -3
- data/lib/parse/api/hooks.rb +4 -4
- data/lib/parse/api/objects.rb +14 -11
- data/lib/parse/api/push.rb +4 -2
- data/lib/parse/api/schemas.rb +6 -5
- data/lib/parse/api/sessions.rb +11 -1
- data/lib/parse/api/users.rb +65 -15
- data/lib/parse/client/authentication.rb +4 -2
- data/lib/parse/client/body_builder.rb +11 -3
- data/lib/parse/client/caching.rb +17 -6
- data/lib/parse/client/protocol.rb +14 -8
- data/lib/parse/client/request.rb +4 -1
- data/lib/parse/client/response.rb +59 -6
- data/lib/parse/client.rb +72 -42
- data/lib/parse/model/acl.rb +22 -4
- data/lib/parse/model/associations/belongs_to.rb +22 -10
- data/lib/parse/model/associations/collection_proxy.rb +14 -1
- data/lib/parse/model/associations/has_many.rb +76 -15
- data/lib/parse/model/associations/has_one.rb +69 -0
- data/lib/parse/model/associations/pointer_collection_proxy.rb +13 -6
- data/lib/parse/model/associations/relation_collection_proxy.rb +5 -2
- data/lib/parse/model/bytes.rb +6 -2
- data/lib/parse/model/classes/installation.rb +27 -0
- data/lib/parse/model/classes/role.rb +20 -0
- data/lib/parse/model/classes/session.rb +26 -0
- data/lib/parse/model/classes/user.rb +185 -0
- data/lib/parse/model/core/actions.rb +40 -26
- data/lib/parse/model/core/properties.rb +126 -20
- data/lib/parse/model/core/querying.rb +63 -3
- data/lib/parse/model/core/schema.rb +9 -6
- data/lib/parse/model/date.rb +5 -1
- data/lib/parse/model/file.rb +12 -9
- data/lib/parse/model/geopoint.rb +6 -4
- data/lib/parse/model/model.rb +29 -21
- data/lib/parse/model/object.rb +29 -76
- data/lib/parse/model/pointer.rb +8 -6
- data/lib/parse/model/push.rb +4 -1
- data/lib/parse/query/constraint.rb +3 -0
- data/lib/parse/query/constraints.rb +6 -3
- data/lib/parse/query/operation.rb +3 -0
- data/lib/parse/query/ordering.rb +3 -0
- data/lib/parse/query.rb +85 -38
- data/lib/parse/stack/generators/rails.rb +3 -0
- data/lib/parse/stack/railtie.rb +2 -0
- data/lib/parse/stack/tasks.rb +4 -1
- data/lib/parse/stack/version.rb +4 -1
- data/lib/parse/stack.rb +3 -0
- data/lib/parse/webhooks/payload.rb +14 -8
- data/lib/parse/webhooks/registration.rb +11 -8
- data/lib/parse/webhooks.rb +11 -8
- data/lib/parse-stack.rb +3 -0
- data/parse-stack.gemspec +10 -8
- metadata +16 -4
@@ -1,3 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
1
4
|
require 'active_model'
|
2
5
|
require 'active_support'
|
3
6
|
require 'active_support/inflector'
|
@@ -7,6 +10,7 @@ require 'active_support/inflector'
|
|
7
10
|
require 'active_model_serializers'
|
8
11
|
require 'active_support/inflector'
|
9
12
|
require 'active_model_serializers'
|
13
|
+
require 'active_support/hash_with_indifferent_access'
|
10
14
|
require 'time'
|
11
15
|
|
12
16
|
=begin
|
@@ -25,8 +29,8 @@ module Parse
|
|
25
29
|
|
26
30
|
module Properties
|
27
31
|
# This is an exception that is thrown if there is an issue when creating a specific property for a class.
|
28
|
-
class DefinitionError <
|
29
|
-
class ValueError <
|
32
|
+
class DefinitionError < StandardError; end;
|
33
|
+
class ValueError < StandardError; end;
|
30
34
|
|
31
35
|
# These are the base types supported by Parse.
|
32
36
|
TYPES = [:id, :string, :relation, :integer, :float, :boolean, :date, :array, :file, :geopoint, :bytes, :object, :acl].freeze
|
@@ -61,6 +65,10 @@ module Parse
|
|
61
65
|
@field_map ||= BASE_FIELD_MAP.dup
|
62
66
|
end
|
63
67
|
|
68
|
+
def enums
|
69
|
+
@enums ||= {}
|
70
|
+
end
|
71
|
+
|
64
72
|
# Keeps track of all the attributes supported by this class.
|
65
73
|
def attributes=(hash)
|
66
74
|
@attributes = BASE.merge(hash)
|
@@ -93,9 +101,12 @@ module Parse
|
|
93
101
|
# for enum type fields that are string columns in Parse. Ex. a booking_status for a field
|
94
102
|
# could be either "submitted" or "completed" in Parse, however with symbolize, these would
|
95
103
|
# be available as :submitted or :completed.
|
96
|
-
def property(key, data_type = :string, opts
|
104
|
+
def property(key, data_type = :string, **opts)
|
97
105
|
|
98
106
|
key = key.to_sym
|
107
|
+
ivar = :"@#{key}"
|
108
|
+
will_change_method = :"#{key}_will_change!"
|
109
|
+
set_attribute_method = :"#{key}_set_attribute!"
|
99
110
|
|
100
111
|
if data_type.is_a?(Hash)
|
101
112
|
opts.merge!(data_type)
|
@@ -104,10 +115,16 @@ module Parse
|
|
104
115
|
|
105
116
|
# allow :bool for :boolean
|
106
117
|
data_type = :boolean if data_type == :bool
|
118
|
+
data_type = :integer if data_type == :int
|
119
|
+
|
107
120
|
# set defaults
|
108
121
|
opts = { required: false,
|
109
122
|
alias: true,
|
110
123
|
symbolize: false,
|
124
|
+
enum: nil,
|
125
|
+
scopes: true,
|
126
|
+
_prefix: nil,
|
127
|
+
_suffix: false,
|
111
128
|
field: key.to_s.camelize(:lower)
|
112
129
|
}.merge( opts )
|
113
130
|
#By default, the remote field name is a lower-first-camelcase version of the key
|
@@ -142,11 +159,79 @@ module Parse
|
|
142
159
|
validates_presence_of key
|
143
160
|
end
|
144
161
|
|
162
|
+
|
163
|
+
is_enum_type = opts[:enum].nil? == false
|
164
|
+
|
165
|
+
if is_enum_type
|
166
|
+
|
167
|
+
unless data_type == :string
|
168
|
+
raise DefinitionError, "Property #{self}##{parse_field} :enum option is only supported on :string data types."
|
169
|
+
end
|
170
|
+
|
171
|
+
enum_values = opts[:enum]
|
172
|
+
unless enum_values.is_a?(Array) && enum_values.empty? == false
|
173
|
+
raise DefinitionError, "Property #{self}##{parse_field} :enum option must be an Array type of symbols."
|
174
|
+
end
|
175
|
+
opts[:symbolize] = true
|
176
|
+
|
177
|
+
enum_values = enum_values.dup.map(&:to_sym).freeze
|
178
|
+
|
179
|
+
self.enums.merge!( key => enum_values )
|
180
|
+
allow_nil = opts[:required] == false
|
181
|
+
validates key, inclusion: { in: enum_values }, allow_nil: allow_nil
|
182
|
+
|
183
|
+
unless opts[:scopes] == false
|
184
|
+
# You can use the :_prefix or :_suffix options when you need to define multiple enums with same values.
|
185
|
+
# If the passed value is true, the methods are prefixed/suffixed with the name of the enum. It is also possible to supply a custom value:
|
186
|
+
prefix = opts[:_prefix]
|
187
|
+
unless opts[:_prefix].nil? || prefix.is_a?(Symbol) || prefix.is_a?(String)
|
188
|
+
raise DefinitionError, "Enumeration option :_prefix must either be a symbol or string for #{self}##{key}."
|
189
|
+
end
|
190
|
+
|
191
|
+
unless opts[:_suffix].is_a?(TrueClass) || opts[:_suffix].is_a?(FalseClass)
|
192
|
+
raise DefinitionError, "Enumeration option :_suffix must either be true or false for #{self}##{key}."
|
193
|
+
end
|
194
|
+
|
195
|
+
add_suffix = opts[:_suffix] == true
|
196
|
+
prefix_or_key = (prefix.blank? ? key : prefix).to_sym
|
197
|
+
|
198
|
+
class_method_name = prefix_or_key.to_s.pluralize.to_sym
|
199
|
+
if singleton_class.method_defined?(class_method_name)
|
200
|
+
raise DefinitionError, "You tried to define an enum named `#{key}` for #{self} " + \
|
201
|
+
"but this will generate a method `#{self}.#{class_method_name}` " + \
|
202
|
+
" which is already defined. Try using :_suffix or :_prefix options."
|
203
|
+
end
|
204
|
+
|
205
|
+
define_singleton_method(class_method_name) { enum_values }
|
206
|
+
|
207
|
+
method_name = add_suffix ? :"valid_#{prefix_or_key}?" : :"#{prefix_or_key}_valid?"
|
208
|
+
define_method(method_name) do
|
209
|
+
value = instance_variable_get(ivar)
|
210
|
+
return true if allow_nil && value.nil?
|
211
|
+
enum_values.include?(value.to_s.to_sym)
|
212
|
+
end
|
213
|
+
|
214
|
+
enum_values.each do |enum|
|
215
|
+
method_name = enum # default
|
216
|
+
scope_name = enum
|
217
|
+
if add_suffix
|
218
|
+
method_name = :"#{enum}_#{prefix_or_key}"
|
219
|
+
elsif prefix.present?
|
220
|
+
method_name = :"#{prefix}_#{enum}"
|
221
|
+
end
|
222
|
+
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 }
|
225
|
+
end
|
226
|
+
end # unless scopes
|
227
|
+
|
228
|
+
end
|
229
|
+
|
145
230
|
symbolize_value = opts[:symbolize]
|
146
231
|
|
147
232
|
#only support symbolization of string data types
|
148
233
|
if symbolize_value && (data_type == :string || data_type == :array) == false
|
149
|
-
raise
|
234
|
+
raise DefinitionError, "Tried to symbolize #{self}##{key}, but it is only supported on :string or :array data types."
|
150
235
|
end
|
151
236
|
|
152
237
|
# Here is the where the 'magic' begins. For each property defined, we will
|
@@ -166,11 +251,11 @@ module Parse
|
|
166
251
|
end
|
167
252
|
|
168
253
|
# We define a getter with the key
|
254
|
+
|
169
255
|
define_method(key) do
|
170
256
|
|
171
257
|
# we will get the value using the internal value of the instance variable
|
172
258
|
# using the instance_variable_get
|
173
|
-
ivar = :"@#{key}"
|
174
259
|
value = instance_variable_get ivar
|
175
260
|
|
176
261
|
# If the value is nil and this current Parse::Object instance is a pointer?
|
@@ -191,7 +276,7 @@ module Parse
|
|
191
276
|
value = format_value(key, value, data_type)
|
192
277
|
# lets set the variable with the updated value
|
193
278
|
instance_variable_set ivar, value
|
194
|
-
send
|
279
|
+
send will_change_method
|
195
280
|
end
|
196
281
|
|
197
282
|
# if the value is a String (like an iso8601 date) and the data type of
|
@@ -199,7 +284,7 @@ module Parse
|
|
199
284
|
if value.is_a?(String) && data_type == :date
|
200
285
|
value = format_value(key, value, data_type)
|
201
286
|
instance_variable_set ivar, value
|
202
|
-
send
|
287
|
+
send will_change_method
|
203
288
|
end
|
204
289
|
# finally return the value
|
205
290
|
if symbolize_value
|
@@ -214,6 +299,19 @@ module Parse
|
|
214
299
|
value
|
215
300
|
end
|
216
301
|
|
302
|
+
# support question mark methods for boolean
|
303
|
+
if data_type == :boolean
|
304
|
+
if self.method_defined?("#{key}?")
|
305
|
+
puts "Creating boolean helper :#{key}?. Will overwrite existing method #{self}##{key}?."
|
306
|
+
end
|
307
|
+
|
308
|
+
# returns true if set to true, false otherwise
|
309
|
+
define_method("#{key}?") { (send(key) == true) }
|
310
|
+
unless opts[:scopes] == false
|
311
|
+
scope key, ->(opts = {}){ query( opts.merge(key => true) ) }
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
217
315
|
# The second method to be defined is a setter method. This is done by
|
218
316
|
# defining :key with a '=' sign. However, to support setting the attribute
|
219
317
|
# with and without dirty tracking, we really will just proxy it to another method
|
@@ -221,12 +319,12 @@ module Parse
|
|
221
319
|
define_method("#{key}=") do |val|
|
222
320
|
#we proxy the method passing the value and true. Passing true to the
|
223
321
|
# method tells it to make sure dirty tracking is enabled.
|
224
|
-
self.send
|
322
|
+
self.send set_attribute_method, val, true
|
225
323
|
end
|
226
324
|
|
227
325
|
# This is the real setter method. Takes two arguments, the value to set
|
228
326
|
# and whether to mark it as dirty tracked.
|
229
|
-
define_method(
|
327
|
+
define_method(set_attribute_method) do |val, track = true|
|
230
328
|
# Each value has a data type, based on that we can treat the incoming
|
231
329
|
# value as input, and format it to the correct storage format. This method is
|
232
330
|
# defined in this file (instance method)
|
@@ -235,7 +333,7 @@ module Parse
|
|
235
333
|
# this will grab the current value and keep a copy of it - but we only do this if
|
236
334
|
# the new value being set is different from the current value stored.
|
237
335
|
if track == true
|
238
|
-
send
|
336
|
+
send will_change_method unless val == instance_variable_get( ivar )
|
239
337
|
end
|
240
338
|
|
241
339
|
if symbolize_value
|
@@ -247,8 +345,12 @@ module Parse
|
|
247
345
|
val.set_collection! items
|
248
346
|
end
|
249
347
|
end
|
348
|
+
|
349
|
+
# if is_enum_type
|
350
|
+
#
|
351
|
+
# end
|
250
352
|
# now set the instance value
|
251
|
-
instance_variable_set
|
353
|
+
instance_variable_set ivar, val
|
252
354
|
end
|
253
355
|
|
254
356
|
# The core methods above support all attributes with the base local :key parameter
|
@@ -267,7 +369,7 @@ module Parse
|
|
267
369
|
if self.method_defined?(parse_field) == false && opts[:alias]
|
268
370
|
alias_method parse_field, key
|
269
371
|
alias_method "#{parse_field}=", "#{key}="
|
270
|
-
alias_method "#{parse_field}_set_attribute!",
|
372
|
+
alias_method "#{parse_field}_set_attribute!", set_attribute_method
|
271
373
|
elsif parse_field.to_sym != :objectId
|
272
374
|
warn "Alias property method #{self}##{parse_field} already defined."
|
273
375
|
end
|
@@ -286,6 +388,7 @@ module Parse
|
|
286
388
|
self.class.fields(type)
|
287
389
|
end
|
288
390
|
|
391
|
+
# TODO: We can optimize
|
289
392
|
def attributes
|
290
393
|
{__type: :string, :className => :string}.merge!(self.class.attributes)
|
291
394
|
end
|
@@ -295,9 +398,9 @@ module Parse
|
|
295
398
|
def apply_attributes!(hash, dirty_track: false)
|
296
399
|
return unless hash.is_a?(Hash)
|
297
400
|
|
298
|
-
@id ||= hash[
|
401
|
+
@id ||= hash[Parse::Model::ID] || hash[Parse::Model::OBJECT_ID] || hash[:objectId]
|
299
402
|
hash.each do |key, value|
|
300
|
-
method = "#{key}_set_attribute!"
|
403
|
+
method = "#{key}_set_attribute!".freeze
|
301
404
|
send(method, value, dirty_track) if respond_to?( method )
|
302
405
|
end
|
303
406
|
end
|
@@ -326,6 +429,8 @@ module Parse
|
|
326
429
|
h[remote_field] = send key
|
327
430
|
h[remote_field] = {__op: :Delete} if h[remote_field].nil?
|
328
431
|
# in the case that the field is a Parse object, generate a pointer
|
432
|
+
# if it is a Parse::PointerCollectionProxy, then make sure we get a list of pointers.
|
433
|
+
h[remote_field] = h[remote_field].parse_pointers if h[remote_field].is_a?(Parse::PointerCollectionProxy)
|
329
434
|
h[remote_field] = h[remote_field].pointer if h[remote_field].respond_to?(:pointer)
|
330
435
|
end
|
331
436
|
h
|
@@ -341,21 +446,22 @@ module Parse
|
|
341
446
|
def format_operation(key, val, data_type)
|
342
447
|
return val unless val.is_a?(Hash) && val["__op"].present?
|
343
448
|
op = val["__op"]
|
449
|
+
ivar = :"@#{key}"
|
344
450
|
#handles delete case otherwise 'null' shows up in column
|
345
451
|
if "Delete" == op
|
346
452
|
val = nil
|
347
453
|
elsif "Add" == op && data_type == :array
|
348
|
-
val = (instance_variable_get(
|
454
|
+
val = (instance_variable_get(ivar) || []).to_a + (val["objects"] || [])
|
349
455
|
elsif "Remove" == op && data_type == :array
|
350
|
-
val = (instance_variable_get(
|
456
|
+
val = (instance_variable_get(ivar) || []).to_a - (val["objects"] || [])
|
351
457
|
elsif "AddUnique" == op && data_type == :array
|
352
458
|
objects = (val["objects"] || []).uniq
|
353
|
-
original_items = (instance_variable_get(
|
459
|
+
original_items = (instance_variable_get(ivar) || []).to_a
|
354
460
|
objects.reject! { |r| original_items.include?(r) }
|
355
461
|
val = original_items + objects
|
356
462
|
elsif "Increment" == op && data_type == :integer || data_type == :integer
|
357
463
|
# for operations that increment by a certain amount, they come as a hash
|
358
|
-
val = (instance_variable_get(
|
464
|
+
val = (instance_variable_get(ivar) || 0) + (val["amount"] || 0).to_i
|
359
465
|
end
|
360
466
|
val
|
361
467
|
end
|
@@ -370,7 +476,7 @@ module Parse
|
|
370
476
|
|
371
477
|
case data_type
|
372
478
|
when :object
|
373
|
-
val = val
|
479
|
+
val = val.with_indifferent_access if val.is_a?(Hash)
|
374
480
|
when :array
|
375
481
|
# All "array" types use a collection proxy
|
376
482
|
val = val.to_a if val.is_a?(Parse::CollectionProxy) #all objects must be in array form
|
@@ -408,7 +514,7 @@ module Parse
|
|
408
514
|
val = val.parse_date
|
409
515
|
# if the value is a hash, then it may be the Parse hash format for an iso date.
|
410
516
|
elsif val.is_a?(Hash) # val.respond_to?(:iso8601)
|
411
|
-
val = Parse::Date.parse(val["iso"
|
517
|
+
val = Parse::Date.parse(val["iso"] || val[:iso])
|
412
518
|
elsif val.is_a?(String)
|
413
519
|
# if it's a string, try parsing the date
|
414
520
|
val = Parse::Date.parse val
|
@@ -1,3 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
1
4
|
require_relative '../../query'
|
2
5
|
|
3
6
|
# This module provides most of the querying methods for Parse Objects.
|
@@ -12,14 +15,57 @@ module Parse
|
|
12
15
|
|
13
16
|
module ClassMethods
|
14
17
|
|
18
|
+
def scope(name, body)
|
19
|
+
unless body.respond_to?(:call)
|
20
|
+
raise ArgumentError, 'The scope body needs to be callable.'
|
21
|
+
end
|
22
|
+
|
23
|
+
name = name.to_sym
|
24
|
+
if respond_to?(name, true)
|
25
|
+
puts "Creating scope :#{name}. Will overwrite existing method #{self}.#{name}."
|
26
|
+
end
|
27
|
+
|
28
|
+
define_singleton_method(name) do |*args, &block|
|
29
|
+
|
30
|
+
if body.arity.zero?
|
31
|
+
res = body.call
|
32
|
+
res.conditions(*args) if args.present?
|
33
|
+
else
|
34
|
+
res = body.call(*args)
|
35
|
+
end
|
36
|
+
|
37
|
+
_q = res || query
|
38
|
+
|
39
|
+
if _q.is_a?(Parse::Query)
|
40
|
+
klass = self
|
41
|
+
_q.define_singleton_method(:method_missing) do |m, *args, &block|
|
42
|
+
if klass.respond_to?(m, true)
|
43
|
+
klass_scope = klass.send(m, *args, &block)
|
44
|
+
if klass_scope.is_a?(Parse::Query)
|
45
|
+
return self.add_constraints( klass_scope.constraints )
|
46
|
+
end
|
47
|
+
klass = nil # help clean up ruby gc
|
48
|
+
return klass_scope
|
49
|
+
end
|
50
|
+
klass = nil # help clean up ruby gc
|
51
|
+
return self.results.send(m, *args, &block)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
return _q if block.nil?
|
55
|
+
_q.results(&block)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
|
15
61
|
# This query method helper returns a Query object tied to a parse class.
|
16
62
|
# The parse class should be the name of the one that will be sent in the query
|
17
63
|
# request pointing to the remote table.
|
18
64
|
def query(constraints = {})
|
19
65
|
Parse::Query.new self.parse_class, constraints
|
20
|
-
end
|
66
|
+
end; alias_method :where, :query
|
21
67
|
|
22
|
-
def
|
68
|
+
def literal_where(clauses = {})
|
23
69
|
query.where(clauses)
|
24
70
|
end
|
25
71
|
|
@@ -49,10 +95,24 @@ module Parse
|
|
49
95
|
end
|
50
96
|
|
51
97
|
# creates a count request (which is more performant when counting objects)
|
52
|
-
def count(constraints
|
98
|
+
def count(**constraints)
|
53
99
|
query(constraints).count
|
54
100
|
end
|
55
101
|
|
102
|
+
def newest(**constraints)
|
103
|
+
constraints.merge!(order: :created_at.desc)
|
104
|
+
_q = query(constraints)
|
105
|
+
_q.define_singleton_method(:method_missing) { |m, *args, &block| self.results.send(m, *args, &block) }
|
106
|
+
_q
|
107
|
+
end
|
108
|
+
|
109
|
+
def oldest(**constraints)
|
110
|
+
constraints.merge!(order: :created_at.asc)
|
111
|
+
_q = query(constraints)
|
112
|
+
_q.define_singleton_method(:method_missing) { |m, *args, &block| self.results.send(m, *args, &block) }
|
113
|
+
_q
|
114
|
+
end
|
115
|
+
|
56
116
|
# Find objects based on objectIds. The result is a list (or single item) of the
|
57
117
|
# objects that were successfully found.
|
58
118
|
# Example:
|
@@ -1,10 +1,13 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
1
4
|
require_relative "properties"
|
2
5
|
# This class adds methods to Parse::Objects in order to create a JSON Parse schema
|
3
6
|
# in order to support table creation and table alterations.
|
4
7
|
module Parse
|
5
8
|
|
6
9
|
def self.auto_upgrade!
|
7
|
-
klassModels = Parse::Object.descendants
|
10
|
+
klassModels = Parse::Object.descendants
|
8
11
|
klassModels.sort_by { |c| c.parse_class }.each do |klass|
|
9
12
|
yield(klass) if block_given?
|
10
13
|
klass.auto_upgrade!
|
@@ -31,13 +34,13 @@ module Parse
|
|
31
34
|
# if it is a basic column property, find the right datatype
|
32
35
|
case v
|
33
36
|
when :integer, :float
|
34
|
-
result[:type] = "Number"
|
37
|
+
result[:type] = "Number"
|
35
38
|
when :geopoint, :geo_point
|
36
|
-
result[:type] = "GeoPoint"
|
39
|
+
result[:type] = "GeoPoint"
|
37
40
|
when :pointer
|
38
|
-
result = { type: "Pointer"
|
41
|
+
result = { type: "Pointer", targetClass: references[k] }
|
39
42
|
when :acl
|
40
|
-
result[:type] = "ACL"
|
43
|
+
result[:type] = "ACL"
|
41
44
|
else
|
42
45
|
result[:type] = v.to_s.camelize
|
43
46
|
end
|
@@ -47,7 +50,7 @@ module Parse
|
|
47
50
|
end
|
48
51
|
#then add all the relational column attributes
|
49
52
|
relations.each do |k,v|
|
50
|
-
sch[:fields][k] = { type: "Relation"
|
53
|
+
sch[:fields][k] = { type: "Relation", targetClass: relations[k] }
|
51
54
|
end
|
52
55
|
sch
|
53
56
|
end
|
data/lib/parse/model/date.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
1
4
|
require 'time'
|
2
5
|
require 'date'
|
3
6
|
require 'active_model'
|
@@ -19,6 +22,7 @@ require_relative 'model'
|
|
19
22
|
# providing our own encoding for sending to Parse.
|
20
23
|
module Parse
|
21
24
|
class Date < ::DateTime
|
25
|
+
ATTRIBUTES = { __type: :string, iso: :string }.freeze
|
22
26
|
include ::ActiveModel::Model
|
23
27
|
include ::ActiveModel::Serializers::JSON
|
24
28
|
def self.parse_class; Parse::Model::TYPE_DATE; end;
|
@@ -27,7 +31,7 @@ module Parse
|
|
27
31
|
|
28
32
|
# called when encoding to JSON.
|
29
33
|
def attributes
|
30
|
-
|
34
|
+
ATTRIBUTES
|
31
35
|
end
|
32
36
|
|
33
37
|
# this method is defined because it is used by JSON encoding
|
data/lib/parse/model/file.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
1
4
|
require 'active_support'
|
2
5
|
require 'active_support/core_ext/object'
|
3
6
|
require_relative "model"
|
@@ -12,14 +15,14 @@ require 'open-uri'
|
|
12
15
|
module Parse
|
13
16
|
|
14
17
|
class File < Model
|
15
|
-
|
18
|
+
ATTRIBUTES = { __type: :string, name: :string, url: :string }.freeze
|
16
19
|
attr_accessor :name, :url
|
17
20
|
attr_accessor :contents, :mime_type
|
18
21
|
def self.parse_class; TYPE_FILE; end;
|
19
22
|
def parse_class; self.class.parse_class; end;
|
20
23
|
alias_method :__type, :parse_class
|
21
|
-
FIELD_NAME = "name"
|
22
|
-
FIELD_URL = "url"
|
24
|
+
FIELD_NAME = "name"
|
25
|
+
FIELD_URL = "url"
|
23
26
|
class << self
|
24
27
|
attr_accessor :default_mime_type
|
25
28
|
|
@@ -36,7 +39,7 @@ module Parse
|
|
36
39
|
def initialize(name, contents = nil, mime_type = nil)
|
37
40
|
mime_type ||= Parse::File.default_mime_type
|
38
41
|
|
39
|
-
if name.is_a?(String) && name.start_with?(
|
42
|
+
if name.is_a?(String) && name.start_with?('http') #could be url string
|
40
43
|
file = open( name )
|
41
44
|
@contents = file.read
|
42
45
|
@name = File.basename file.base_uri.to_s
|
@@ -54,7 +57,7 @@ module Parse
|
|
54
57
|
@contents = contents
|
55
58
|
end
|
56
59
|
if @name.blank?
|
57
|
-
raise "Invalid Parse::File initialization with name '#{@name}'"
|
60
|
+
raise ArgumentError, "Invalid Parse::File initialization with name '#{@name}'"
|
58
61
|
end
|
59
62
|
|
60
63
|
@mime_type ||= mime_type
|
@@ -72,11 +75,11 @@ module Parse
|
|
72
75
|
# A File object is considered saved if the basename of the URL and the name parameters are equal and
|
73
76
|
# the name of the file begins with 'tfss'
|
74
77
|
def saved?
|
75
|
-
@url.present? && @name.present? && @name == File.basename(@url) && @name.start_with?("tfss"
|
78
|
+
@url.present? && @name.present? && @name == File.basename(@url) && @name.start_with?("tfss")
|
76
79
|
end
|
77
80
|
|
78
81
|
def attributes
|
79
|
-
|
82
|
+
ATTRIBUTES
|
80
83
|
end
|
81
84
|
|
82
85
|
def ==(u)
|
@@ -135,8 +138,8 @@ class Hash
|
|
135
138
|
def parse_file?
|
136
139
|
url = self[Parse::File::FIELD_URL]
|
137
140
|
name = self[Parse::File::FIELD_NAME]
|
138
|
-
(count == 2 || self["__type"
|
141
|
+
(count == 2 || self["__type"] == Parse::File.parse_class) &&
|
139
142
|
url.present? && name.present? &&
|
140
|
-
name == ::File.basename(url) && name.start_with?("tfss"
|
143
|
+
name == ::File.basename(url) && name.start_with?("tfss")
|
141
144
|
end
|
142
145
|
end
|
data/lib/parse/model/geopoint.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
1
3
|
|
2
4
|
require_relative "model"
|
3
5
|
|
@@ -6,10 +8,10 @@ module Parse
|
|
6
8
|
# A basic geo location object in Parse. It represents a location on a map through a
|
7
9
|
# latitude and longitue.
|
8
10
|
class GeoPoint < Model
|
9
|
-
|
11
|
+
ATTRIBUTES = { __type: :string, latitude: :float, longitude: :float }.freeze
|
10
12
|
attr_accessor :latitude, :longitude
|
11
|
-
FIELD_LAT = "latitude"
|
12
|
-
FIELD_LNG = "longitude"
|
13
|
+
FIELD_LAT = "latitude"
|
14
|
+
FIELD_LNG = "longitude"
|
13
15
|
# Latitude should not be -90.0 or 90.0.
|
14
16
|
# Longitude should not be -180.0 or 180.0.
|
15
17
|
LAT_MIN = -90.0
|
@@ -58,7 +60,7 @@ module Parse
|
|
58
60
|
end
|
59
61
|
|
60
62
|
def attributes
|
61
|
-
|
63
|
+
ATTRIBUTES
|
62
64
|
end
|
63
65
|
|
64
66
|
def max_miles(m)
|
data/lib/parse/model/model.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
1
4
|
require 'active_model'
|
2
5
|
require 'active_support'
|
3
6
|
require 'active_support/inflector'
|
@@ -20,20 +23,24 @@ module Parse
|
|
20
23
|
extend ::ActiveModel::Naming # provides the methods for getting class names from Model classes
|
21
24
|
|
22
25
|
# General Parse constants
|
26
|
+
ID = "id".freeze
|
27
|
+
OBJECT_ID = 'objectId'.freeze
|
23
28
|
KEY_CLASS_NAME = 'className'.freeze
|
24
29
|
KEY_OBJECT_ID = 'objectId'.freeze
|
25
|
-
KEY_CREATED_AT = 'createdAt'
|
26
|
-
KEY_UPDATED_AT = 'updatedAt'
|
27
|
-
CLASS_USER = '_User'
|
28
|
-
CLASS_INSTALLATION = '_Installation'
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
30
|
+
KEY_CREATED_AT = 'createdAt'
|
31
|
+
KEY_UPDATED_AT = 'updatedAt'
|
32
|
+
CLASS_USER = '_User'
|
33
|
+
CLASS_INSTALLATION = '_Installation'
|
34
|
+
CLASS_SESSION = '_Session'
|
35
|
+
CLASS_ROLE = '_Role'
|
36
|
+
TYPE_FILE = 'File'
|
37
|
+
TYPE_GEOPOINT = 'GeoPoint'
|
38
|
+
TYPE_OBJECT = 'Object'
|
39
|
+
TYPE_DATE = 'Date'
|
40
|
+
TYPE_BYTES = 'Bytes'
|
41
|
+
TYPE_POINTER = 'Pointer'
|
42
|
+
TYPE_RELATION = 'Relation'
|
43
|
+
TYPE_FIELD = '__type'
|
37
44
|
|
38
45
|
# To support being able to have different ruby class names from the 'table'
|
39
46
|
# names used in Parse, we will need to have a dynamic lookup system where
|
@@ -63,12 +70,12 @@ module Parse
|
|
63
70
|
# class method to find the responsible ruby Parse::Object subclass that handles
|
64
71
|
# the provided parse class (str).
|
65
72
|
def self.find_class(str)
|
66
|
-
return Parse::File if str == TYPE_FILE
|
67
|
-
return Parse::GeoPoint if str == TYPE_GEOPOINT
|
68
|
-
return Parse::Date if str == TYPE_DATE
|
69
|
-
return Parse::Bytes if str == TYPE_BYTES
|
70
|
-
# return Parse::User if str == "User"
|
71
|
-
# return Parse::Installation if str == "Installation"
|
73
|
+
return Parse::File if str == TYPE_FILE
|
74
|
+
return Parse::GeoPoint if str == TYPE_GEOPOINT
|
75
|
+
return Parse::Date if str == TYPE_DATE
|
76
|
+
return Parse::Bytes if str == TYPE_BYTES
|
77
|
+
# return Parse::User if str == "User"
|
78
|
+
# return Parse::Installation if str == "Installation"
|
72
79
|
|
73
80
|
str = str.to_s
|
74
81
|
# Basically go through all Parse::Object subclasses and see who is has a parse_class
|
@@ -87,9 +94,10 @@ end
|
|
87
94
|
class String
|
88
95
|
# short helper method to provide lower-first-camelcase
|
89
96
|
def columnize
|
90
|
-
return
|
91
|
-
|
92
|
-
|
97
|
+
return Parse::Model::OBJECT_ID if self == Parse::Model::ID
|
98
|
+
u = '_'.freeze
|
99
|
+
(first == u ? sub(u,'') : self).camelize(:lower)
|
100
|
+
end
|
93
101
|
|
94
102
|
#users for properties: ex. :users -> "_User" or :songs -> Song
|
95
103
|
def to_parse_class(singularize: false)
|