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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +52 -39
  3. data/Gemfile.lock +2 -2
  4. data/README.md +609 -124
  5. data/bin/console +0 -9
  6. data/lib/parse/api/all.rb +3 -0
  7. data/lib/parse/api/analytics.rb +2 -2
  8. data/lib/parse/api/apps.rb +15 -17
  9. data/lib/parse/api/batch.rb +4 -1
  10. data/lib/parse/api/cloud_functions.rb +2 -0
  11. data/lib/parse/api/config.rb +14 -2
  12. data/lib/parse/api/files.rb +6 -3
  13. data/lib/parse/api/hooks.rb +4 -4
  14. data/lib/parse/api/objects.rb +14 -11
  15. data/lib/parse/api/push.rb +4 -2
  16. data/lib/parse/api/schemas.rb +6 -5
  17. data/lib/parse/api/sessions.rb +11 -1
  18. data/lib/parse/api/users.rb +65 -15
  19. data/lib/parse/client/authentication.rb +4 -2
  20. data/lib/parse/client/body_builder.rb +11 -3
  21. data/lib/parse/client/caching.rb +17 -6
  22. data/lib/parse/client/protocol.rb +14 -8
  23. data/lib/parse/client/request.rb +4 -1
  24. data/lib/parse/client/response.rb +59 -6
  25. data/lib/parse/client.rb +72 -42
  26. data/lib/parse/model/acl.rb +22 -4
  27. data/lib/parse/model/associations/belongs_to.rb +22 -10
  28. data/lib/parse/model/associations/collection_proxy.rb +14 -1
  29. data/lib/parse/model/associations/has_many.rb +76 -15
  30. data/lib/parse/model/associations/has_one.rb +69 -0
  31. data/lib/parse/model/associations/pointer_collection_proxy.rb +13 -6
  32. data/lib/parse/model/associations/relation_collection_proxy.rb +5 -2
  33. data/lib/parse/model/bytes.rb +6 -2
  34. data/lib/parse/model/classes/installation.rb +27 -0
  35. data/lib/parse/model/classes/role.rb +20 -0
  36. data/lib/parse/model/classes/session.rb +26 -0
  37. data/lib/parse/model/classes/user.rb +185 -0
  38. data/lib/parse/model/core/actions.rb +40 -26
  39. data/lib/parse/model/core/properties.rb +126 -20
  40. data/lib/parse/model/core/querying.rb +63 -3
  41. data/lib/parse/model/core/schema.rb +9 -6
  42. data/lib/parse/model/date.rb +5 -1
  43. data/lib/parse/model/file.rb +12 -9
  44. data/lib/parse/model/geopoint.rb +6 -4
  45. data/lib/parse/model/model.rb +29 -21
  46. data/lib/parse/model/object.rb +29 -76
  47. data/lib/parse/model/pointer.rb +8 -6
  48. data/lib/parse/model/push.rb +4 -1
  49. data/lib/parse/query/constraint.rb +3 -0
  50. data/lib/parse/query/constraints.rb +6 -3
  51. data/lib/parse/query/operation.rb +3 -0
  52. data/lib/parse/query/ordering.rb +3 -0
  53. data/lib/parse/query.rb +85 -38
  54. data/lib/parse/stack/generators/rails.rb +3 -0
  55. data/lib/parse/stack/railtie.rb +2 -0
  56. data/lib/parse/stack/tasks.rb +4 -1
  57. data/lib/parse/stack/version.rb +4 -1
  58. data/lib/parse/stack.rb +3 -0
  59. data/lib/parse/webhooks/payload.rb +14 -8
  60. data/lib/parse/webhooks/registration.rb +11 -8
  61. data/lib/parse/webhooks.rb +11 -8
  62. data/lib/parse-stack.rb +3 -0
  63. data/parse-stack.gemspec +10 -8
  64. 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 < Exception; end;
29
- class ValueError < Exception; end;
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 'Symbolization is only supported on :string or :array data types.'
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 "#{key}_will_change!"
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 "#{key}_will_change!"
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 "#{key}_set_attribute!", val, true
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("#{key}_set_attribute!") do |val, track = true|
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 :"#{key}_will_change!" unless val == instance_variable_get( :"@#{key}" )
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 :"@#{key}", val
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!", "#{key}_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["id"] || hash["objectId"] || hash[:objectId]
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(:"@#{key}") || []).to_a + (val["objects"] || [])
454
+ val = (instance_variable_get(ivar) || []).to_a + (val["objects"] || [])
349
455
  elsif "Remove" == op && data_type == :array
350
- val = (instance_variable_get(:"@#{key}") || []).to_a - (val["objects"] || [])
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(:"@#{key}") || []).to_a
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(:"@#{key}") || 0) + (val["amount"] || 0).to_i
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 #should be regular hash, maybe in the future we return hashie?
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".freeze] || 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 where(clauses = {})
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 - [Parse::User, Parse::Installation, Parse::Role, Parse::Session]
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".freeze
37
+ result[:type] = "Number"
35
38
  when :geopoint, :geo_point
36
- result[:type] = "GeoPoint".freeze
39
+ result[:type] = "GeoPoint"
37
40
  when :pointer
38
- result = { type: "Pointer".freeze, targetClass: references[k] }
41
+ result = { type: "Pointer", targetClass: references[k] }
39
42
  when :acl
40
- result[:type] = "ACL".freeze
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".freeze, targetClass: relations[k] }
53
+ sch[:fields][k] = { type: "Relation", targetClass: relations[k] }
51
54
  end
52
55
  sch
53
56
  end
@@ -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
- { __type: :string, iso: :string }.freeze
34
+ ATTRIBUTES
31
35
  end
32
36
 
33
37
  # this method is defined because it is used by JSON encoding
@@ -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".freeze
22
- FIELD_URL = "url".freeze
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?("http".freeze) #could be url string
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".freeze)
78
+ @url.present? && @name.present? && @name == File.basename(@url) && @name.start_with?("tfss")
76
79
  end
77
80
 
78
81
  def attributes
79
- { __type: :string, name: :string, url: :string }.freeze
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".freeze] == Parse::File.parse_class) &&
141
+ (count == 2 || self["__type"] == Parse::File.parse_class) &&
139
142
  url.present? && name.present? &&
140
- name == ::File.basename(url) && name.start_with?("tfss".freeze)
143
+ name == ::File.basename(url) && name.start_with?("tfss")
141
144
  end
142
145
  end
@@ -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".freeze
12
- FIELD_LNG = "longitude".freeze
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
- { __type: :string, latitude: :float, longitude: :float }.freeze
63
+ ATTRIBUTES
62
64
  end
63
65
 
64
66
  def max_miles(m)
@@ -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'.freeze
26
- KEY_UPDATED_AT = 'updatedAt'.freeze
27
- CLASS_USER = '_User'.freeze
28
- CLASS_INSTALLATION = '_Installation'.freeze
29
- TYPE_FILE = "File".freeze
30
- TYPE_GEOPOINT = "GeoPoint".freeze
31
- TYPE_OBJECT = "Object".freeze
32
- TYPE_DATE = "Date".freeze
33
- TYPE_BYTES = "Bytes".freeze
34
- TYPE_POINTER = "Pointer".freeze
35
- TYPE_RELATION = "Relation".freeze
36
- TYPE_FIELD = "__type".freeze
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.freeze
67
- return Parse::GeoPoint if str == TYPE_GEOPOINT.freeze
68
- return Parse::Date if str == TYPE_DATE.freeze
69
- return Parse::Bytes if str == TYPE_BYTES.freeze
70
- # return Parse::User if str == "User".freeze
71
- # return Parse::Installation if str == "Installation".freeze
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 "objectId".freeze if self == "id".freeze
91
- camelize(:lower)
92
- end;
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)