parse-stack 1.4.3 → 1.5.1
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.
- 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)
|