Class: Parse::Object

Inherits:
Pointer show all
Extended by:
Core::Querying, Core::Schema
Includes:
Associations::BelongsTo, Associations::HasMany, Associations::HasOne, Core::Actions, Core::Fetching, Properties
Defined in:
lib/parse/model/object.rb,
lib/parse/webhooks.rb

Overview

This is the core class for all app specific Parse table subclasses. This class in herits from Parse::Pointer since an Object is a Parse::Pointer with additional fields, at a minimum, created_at, updated_at and ACLs. This class also handles all the relational types of associations in a Parse application and handles the main CRUD operations.

As the parent class to all custom subclasses, this class provides the default property schema:

class Parse::Object
   # All subclasses will inherit these properties by default.

   # the objectId column of a record.
   property :id, :string, field: :objectId

   # The the last updated date for a record (Parse::Date)
   property :updated_at, :date

   # The original creation date of a record (Parse::Date)
   property :created_at, :date

   # The Parse::ACL field
   property :acl, :acl, field: :ACL

end

Most Pointers and Object subclasses are treated the same. Therefore, defining a class Artist < Parse::Object that only has id set, will be treated as a pointer. Therefore a Parse::Object can be in a “pointer” state based on the data that it contains. Becasue of this, it is possible to take a Artist instance (in this example), that is in a pointer state, and fetch the rest of the data for that particular record without having to create a new object. Doing so would now mark it as not being a pointer anymore. This is important to the understanding on how relations and properties are handled.

The implementation of this class is large and has been broken up into several modules.

Properties:

All columns in a Parse object are considered a type of property (ex. string, numbers, arrays, etc) except in two cases - Pointers and Relations. For a detailed discussion of properties, see The Defining Properties section.

Associations:

Parse supports a three main types of relational associations. One type of relation is the One-to-One association. This is implemented through a specific column in Parse with a Pointer data type. This pointer column, contains a local value that refers to a different record in a separate Parse table. This association is implemented using the :belongs_to feature. The second association is of One-to-Many. This is implemented is in Parse as a Array type column that contains a list of of Parse pointer objects. It is recommended by Parse that this array does not exceed 100 items for performance reasons. This feature is implemented using the :has_many operation with the plural name of the local Parse class. The last association type is a Parse Relation. These can be used to implement a large Many-to-Many association without requiring an explicit intermediary Parse table or class. This feature is also implemented using the :has_many method but passing the option of :relation.

Direct Known Subclasses

Installation, Product, Role, Session, User

Constant Summary

Constants included from Properties

Properties::BASE, Properties::BASE_FIELD_MAP, Properties::BASE_KEYS, Properties::CORE_FIELDS, Properties::DELETE_OP, Properties::TYPES

Constants inherited from Pointer

Pointer::ATTRIBUTES

Constants inherited from Model

Model::CLASS_INSTALLATION, Model::CLASS_PRODUCT, Model::CLASS_ROLE, Model::CLASS_SESSION, Model::CLASS_USER, Model::ID, Model::KEY_CLASS_NAME, Model::KEY_CREATED_AT, Model::KEY_OBJECT_ID, Model::KEY_UPDATED_AT, Model::OBJECT_ID, Model::TYPE_ACL, Model::TYPE_BYTES, Model::TYPE_DATE, Model::TYPE_FIELD, Model::TYPE_FILE, Model::TYPE_GEOPOINT, Model::TYPE_NUMBER, Model::TYPE_OBJECT, Model::TYPE_POINTER, Model::TYPE_RELATION

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Core::Querying

all, count, each, find, first, literal_where, newest, oldest, query, scope

Methods included from Core::Schema

auto_upgrade!, create_schema, fetch_schema, update_schema

Methods included from Core::Actions

#change_requests, #changes_applied!, #changes_payload, #create, #destroy, #destroy_request, #op_add!, #op_add_relation!, #op_add_unique!, #op_destroy!, #op_increment!, #op_remove!, #op_remove_relation!, #operate_field!, #prepare_save!, #relation_change_operations, #save, #save!, #set_attributes!, #update, #update!, #update_relations, #uri_path

Methods included from Core::Fetching

#autofetch!, #fetch, #fetch!

Methods included from Associations::HasMany

has_many, #relation_changes?, #relation_updates, #relations

Methods included from Associations::BelongsTo

belongs_to, #key?

Methods included from Associations::HasOne

has_one

Methods included from Properties

#apply_attributes!, #attribute_changes?, #attribute_updates, #attributes, #attributes=, #field_map, #fields, #format_operation, #format_value

Methods inherited from Pointer

#==, #attributes, #fetch, #fetched?, #hash, #json_hash, #pointer, #pointer?, #present?, #sig

Methods inherited from Model

find_class

Methods included from Client::Connectable

#client

Constructor Details

#new(id) ⇒ Parse::Object #new(hash = {}) ⇒ Parse::Object

Note:

Should only be called with Parse::Object subclasses.

The main constructor for subclasses. It can take different parameter types including a String and a JSON hash. Assume a Post class that inherits from Parse::Object:

Overloads:

  • #new(id) ⇒ Parse::Object

    Create a new object with an objectId. This method is useful for creating an unfetched object (pointer-state).

    Examples:

    Post.new "1234"
    

    Parameters:

    • id (String)

      The object id.

  • #new(hash = {}) ⇒ Parse::Object

    Create a new object with Parse JSON hash.

    Examples:

    # JSON hash from Parse
    Post.new({"className" => "Post", "objectId" => "1234", "title" => "My Title"})
    
    post = Post.new title: "My Title"
    post.title # => "My Title"
    

    Parameters:

    • hash (Hash) (defaults to: {})

      the hash representing the object



304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/parse/model/object.rb', line 304

def initialize(opts = {})
  if opts.is_a?(String) #then it's the objectId
    @id = opts.to_s
  elsif opts.is_a?(Hash)
    #if the objectId is provided we will consider the object pristine
    #and not track dirty items
    dirty_track = opts[Parse::Model::OBJECT_ID] || opts[:objectId] || opts[:id]
    apply_attributes!(opts, dirty_track: !dirty_track)
  end

  # if no ACLs, then apply the class default acls
  # ACL.typecast will auto convert of Parse::ACL
  self.acl = self.class.default_acls.as_json if self.acl.nil?

  clear_changes! if @id.present? #then it was an import

  # do not apply defaults on a pointer because it will stop it from being
  # a pointer and will cause its field to be autofetched (for sync)
  apply_defaults! unless pointer?
  # do not call super since it is Pointer subclass
end

Class Attribute Details

.default_aclsParse::ACL (readonly)

The set of default ACLs to be applied on newly created instances of this class. By default, public read and write are enabled.

Returns:

  • (Parse::ACL)

    the current default ACLs for this class.

See Also:



221
222
223
# File 'lib/parse/model/object.rb', line 221

def default_acls
  @default_acls
end

.disable_serialized_string_dateBoolean

Disables returning a serialized string date properties when encoding to JSON. This affects created_at and updated_at fields in order to be backwards compatible with old SDKs.

Returns:

  • (Boolean)

    the disabled status of whether string dates should be serialized.



189
190
191
# File 'lib/parse/model/object.rb', line 189

def disable_serialized_string_date
  @disable_serialized_string_date
end

.parse_class(remoteName = nil) ⇒ String

The class method to override the implicitly assumed Parse collection name in your Parse database. The default Parse collection name is the singular form of the ruby Parse::Object subclass name. The Parse class value should match to the corresponding remote table in your database in order to properly store records and perform queries.

Examples:

class Song < Parse::Object; end;
class Artist < Parse::Object
  parse_class "Musician" # remote collection name
end

Parse::User.parse_class # => '_User'
Song.parse_class # => 'Song'
Artist.parse_class # => 'Musician'

Parameters:

  • remoteName (String) (defaults to: nil)

    the name of the remote collection

Returns:

  • (String)

    the name of the Parse collection for this model.



211
212
213
# File 'lib/parse/model/object.rb', line 211

def parse_class
  @parse_class
end

Instance Attribute Details

#aclACL

Returns the access control list (permissions) object for this record.

Returns:

  • (ACL)

    the access control list (permissions) object for this record.



510
511
512
# File 'lib/parse/model/object.rb', line 510

def acl
  @acl
end

#created_atDate

Returns the created_at date of the record in UTC Zulu iso 8601 with 3 millisecond format.

Returns:

  • (Date)

    the created_at date of the record in UTC Zulu iso 8601 with 3 millisecond format.



502
503
504
# File 'lib/parse/model/object.rb', line 502

def created_at
  @created_at
end

#idString

Returns the value of Parse “objectId” field.

Returns:

  • (String)

    the value of Parse “objectId” field.



498
# File 'lib/parse/model/object.rb', line 498

property :id, field: :objectId

#updated_atDate

Returns the updated_at date of the record in UTC Zulu iso 8601 with 3 millisecond format.

Returns:

  • (Date)

    the updated_at date of the record in UTC Zulu iso 8601 with 3 millisecond format.



506
507
508
# File 'lib/parse/model/object.rb', line 506

def updated_at
  @updated_at
end

Class Method Details

.build(json, table = nil) ⇒ Parse::Object

Note:

If a Parse class object hash is encoutered for which we don’t have a corresponding Parse::Object subclass for, a Parse::Pointer will be returned instead.

Method used for decoding JSON objects into their corresponding Object subclasses. The first parameter is a hash containing the object data and the second parameter is the name of the table / class if it is known. If it is not known, we we try and determine it by checking the “className” or :className entries in the hash.

Examples:

# assume you have defined Post subclass
post = Parse::Object.build({"className" => "Post", "objectId" => '1234'})
post # => #<Post:....>

# if you know the table name
post = Parse::Object.build({"title" => "My Title"}, "Post")
# or
post = Post.build({"title" => "My Title"})

Parameters:

  • json (Hash)

    a JSON hash that contains a Parse object.

  • table (String) (defaults to: nil)

    the Parse class for this hash. If not passed it will be detected.

Returns:



470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
# File 'lib/parse/model/object.rb', line 470

def self.build(json, table = nil)
  className = table
  className ||= (json[Parse::Model::KEY_CLASS_NAME] || json[:className]) if json.is_a?(Hash)
  if json.is_a?(Hash) && json["error"].present? && json["code"].present?
    warn "[Parse::Object] Detected object hash with 'error' and 'code' set. : #{json}"
  end
  className = parse_class unless parse_class == BASE_OBJECT_CLASS
  return if className.nil?
  # we should do a reverse lookup on who is registered for a different class type
  # than their name with parse_class
  klass = Parse::Model.find_class className
  o = nil
  if klass.present?
    # when creating objects from Parse JSON data, don't use dirty tracking since
    # we are considering these objects as "pristine"
    o = klass.new(json)
  else
    o = Parse::Pointer.new className, (json[Parse::Model::OBJECT_ID] || json[:objectId])
  end
  return o
# rescue NameError => e
#   puts "Parse::Object.build constant class error: #{e}"
# rescue Exception => e
#   puts "Parse::Object.build error: #{e}"
end

.pointer(id) ⇒ Parse::Pointer

Helper method to create a Parse::Pointer object for a given id.

Parameters:

  • id (String)

    The objectId

Returns:

  • (Parse::Pointer)

    a pointer object corresponding to this class and id.



337
338
339
340
# File 'lib/parse/model/object.rb', line 337

def self.pointer(id)
  return nil if id.nil?
  Parse::Pointer.new self.parse_class, id
end

.set_default_acl(id, read: false, write: false, role: false) ⇒ Object

A method to set default ACLs to be applied for newly created instances of this class. All subclasses have public read and write enabled by default.

Examples:

class AdminData < Parse::Object

  # Disable public read and write
  set_default_acl :public, read: false, write: false

  # but allow members of the Admin role to read and write
  set_default_acl 'Admin', role: true, read: true, write: true

end

data = AdminData.new
data.acl # => ACL({"role:Admin"=>{"read"=>true, "write"=>true}})

Parameters:

  • id (String|:public)

    The name for ACL entry. This can be an objectId, a role name or :public.

  • read (Boolean) (defaults to: false)

    Whether to allow read permissions (default: false).

  • write (Boolean) (defaults to: false)

    Whether to allow write permissions (default: false).

  • role (Boolean) (defaults to: false)

    Whether the id argument should be applied as a role name.

See Also:

Version:

  • 1.7.0



249
250
251
252
253
254
# File 'lib/parse/model/object.rb', line 249

def set_default_acl(id, read: false, write: false, role: false)
  unless id.present?
    raise ArgumentError, "Invalid argument applying #{self}.default_acls : must be either objectId, role or :public"
  end
  role ? default_acls.apply_role(id, read, write) : default_acls.apply(id, read, write)
end

.webhook(type, block = nil) { ... } ⇒ OpenStruct

Register a webhook trigger or function for this subclass.

Examples:

class Post < Parse::Object

 webhook :before_save do
    # ... do something ...
   parse_object
 end

end

Parameters:

  • block (Symbol) (defaults to: nil)

    the name of the method to call, if no block is passed.

  • type (Symbol)

    The type of cloud code webhook to register. This can be any of the supported routes. These are :before_save, :after_save,

Yields:

  • the body of the function to be evaluated in the scope of a Webhooks::Payload instance.

Returns:

  • (OpenStruct)


57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/parse/webhooks.rb', line 57

def self.webhook(type, block = nil)

  if type == :function
    unless block.is_a?(String) || block.is_a?(Symbol)
      raise ArgumentError, "Invalid Cloud Code function name: #{block}"
    end
    Parse::Webhooks.route(:function, block, &Proc.new)
    # then block must be a symbol or a string
  else
    if block_given?
      Parse::Webhooks.route(type, self, &Proc.new)
    else
      Parse::Webhooks.route(type, self, block)
    end
  end
  #if block

end

.webhook_function(functionName, block = nil) { ... } ⇒ OpenStruct

Register a webhook function for this subclass.

Examples:

class Post < Parse::Object

 webhook_function :helloWorld do
    # ... do something when this function is called ...
 end
end

Parameters:

  • functionName (String)

    the literal name of the function to be registered with the server.

  • block (Symbol) (defaults to: nil)

    the name of the method to call, if no block is passed.

Yields:

  • the body of the function to be evaluated in the scope of a Webhooks::Payload instance.

Returns:

  • (OpenStruct)


33
34
35
36
37
38
39
40
41
# File 'lib/parse/webhooks.rb', line 33

def self.webhook_function(functionName, block = nil)
  if block_given?
    Parse::Webhooks.route(:function, functionName, &Proc.new)
  else
    block = functionName.to_s.underscore.to_sym if block.blank?
    block = method(block.to_sym) if block.is_a?(Symbol)
    Parse::Webhooks.route(:function, functionName, block)
  end
end

Instance Method Details

#[](key) ⇒ Object

Access the value for a defined property through hash accessor. This method returns nil if the key is not one of the defined properties for this Parse::Object subclass.

Parameters:

Returns:

  • (Object)

    the value for this key.



531
532
533
534
# File 'lib/parse/model/object.rb', line 531

def [](key)
  return nil unless self.class.fields[key.to_sym].present?
  send(key)
end

#[]=(key, value) ⇒ Object

Set the value for a specific property through a hash accessor. This method does nothing if key is not one of the defined properties for this Parse::Object subclass.

Parameters:

Returns:

  • (Object)

    the value passed in.



542
543
544
545
# File 'lib/parse/model/object.rb', line 542

def []=(key,value)
  return unless self.class.fields[key.to_sym].present?
  send("#{key}=",value)
end

#__typeModel::TYPE_OBJECT

Returns:



142
# File 'lib/parse/model/object.rb', line 142

def __type; Parse::Model::TYPE_OBJECT; end

#after_create { ... } ⇒ Object

A callback called after the object has been created.

Yields:

  • A block to execute for the callback.

See Also:

  • ActiveModel::Callbacks


176
# File 'lib/parse/model/object.rb', line 176

define_model_callbacks :create, :save, :destroy, only: [:after, :before]

#after_destroy { ... } ⇒ Object

Note:

This is not related to a Parse afterDelete webhook trigger.

A callback called after the object has been successfully deleted.

Yields:

  • A block to execute for the callback.

See Also:

  • ActiveModel::Callbacks


176
# File 'lib/parse/model/object.rb', line 176

define_model_callbacks :create, :save, :destroy, only: [:after, :before]

#after_save { ... } ⇒ Object

Note:

This is not related to a Parse afterSave webhook trigger.

A callback called after the object has been successfully saved.

Yields:

  • A block to execute for the callback.

See Also:

  • ActiveModel::Callbacks


176
# File 'lib/parse/model/object.rb', line 176

define_model_callbacks :create, :save, :destroy, only: [:after, :before]

#apply_defaults!Array

force apply default values for any properties defined with default values.

Returns:

  • (Array)

    list of default fields



328
329
330
331
332
# File 'lib/parse/model/object.rb', line 328

def apply_defaults!
  self.class.defaults_list.each do |key|
    send(key) # should call set default proc/values if nil
  end
end

#as_json(opts = nil) ⇒ Hash

Returns a json-hash representing this object.

Returns:

  • (Hash)

    a json-hash representing this object.



277
278
279
280
281
# File 'lib/parse/model/object.rb', line 277

def as_json(opts = nil)
  return pointer if pointer?
  changed_fields = changed_attributes
  super(opts).delete_if { |k, v| v.nil? && !changed_fields.has_key?(k) }
end

#before_create { ... } ⇒ Object

A callback called before the object has been created.

Yields:

  • A block to execute for the callback.

See Also:

  • ActiveModel::Callbacks


176
# File 'lib/parse/model/object.rb', line 176

define_model_callbacks :create, :save, :destroy, only: [:after, :before]

#before_destroy { ... } ⇒ Object

Note:

This is not related to a Parse beforeDelete webhook trigger.

A callback called before the object is about to be deleted.

Yields:

  • A block to execute for the callback.

See Also:

  • ActiveModel::Callbacks


176
# File 'lib/parse/model/object.rb', line 176

define_model_callbacks :create, :save, :destroy, only: [:after, :before]

#before_save { ... } ⇒ Object

Note:

This is not related to a Parse beforeSave webhook trigger.

A callback called before the object is saved.

Yields:

  • A block to execute for the callback.

See Also:

  • ActiveModel::Callbacks


176
# File 'lib/parse/model/object.rb', line 176

define_model_callbacks :create, :save, :destroy, only: [:after, :before]

#clear_attribute_change!(atts) ⇒ Object

clear all change and dirty tracking information.



447
448
449
# File 'lib/parse/model/object.rb', line 447

def clear_attribute_change!(atts)
  clear_attribute_changes(atts)
end

#clear_changes!Object

clears all dirty tracking information



360
361
362
# File 'lib/parse/model/object.rb', line 360

def clear_changes!
  clear_changes_information
end

#createdAtDate

Alias to #created_at

Returns:

  • (Date)

    the created_at date of the record in UTC Zulu iso 8601 with 3 millisecond format.



514
515
516
517
# File 'lib/parse/model/object.rb', line 514

def createdAt
  return @created_at if Parse::Object.disable_serialized_string_date.present?
  @created_at.to_time.utc.iso8601(3) if @created_at.present?
end

#existed?Boolean

Note:

You should not use this method inside a beforeSave webhook.

Existed returns true if the object had existed before *its last save operation*. This method returns false if the #created_at and #updated_at dates of an object are equal, implyiny this object has been newly created and saved (especially in an afterSave hook).

This is a helper method in a webhook afterSave to know if this object was recently saved in the beforeSave webhook. Checking for #existed? == false in an afterSave hook, is equivalent to using #new? in a beforeSave hook.

Returns:

  • (Boolean)

    true iff the last beforeSave successfully saved this object for the first time.



382
383
384
385
386
387
# File 'lib/parse/model/object.rb', line 382

def existed?
  if @id.blank? || @created_at.blank? || @updated_at.blank?
    return false
  end
  created_at != updated_at
end

#new?Boolean

An object is considered new if it has no id. This is the method to use in a webhook beforeSave when checking if this object is new.

Returns:

  • (Boolean)

    true if the object has no id.



367
368
369
# File 'lib/parse/model/object.rb', line 367

def new?
  @id.blank?
end

#parse_classString Also known as: className

Returns the Parse class for this object.

Returns:

  • (String)

    the Parse class for this object.

See Also:



265
266
267
# File 'lib/parse/model/object.rb', line 265

def parse_class
  self.class.parse_class
end

#persisted?Boolean

Determines if this object has been saved to the Parse database. If an object has pending changes, then it is considered to not yet be persisted.

Returns:

  • (Boolean)

    true if this object has not been saved.



345
346
347
# File 'lib/parse/model/object.rb', line 345

def persisted?
  changed? == false && !(@id.nil? || @created_at.nil? || @updated_at.nil? || @acl.nil?)
end

#prettyString

Returns a pretty-formatted JSON string.

Returns:

  • (String)

    a pretty-formatted JSON string

See Also:

  • JSON.pretty_generate


442
443
444
# File 'lib/parse/model/object.rb', line 442

def pretty
  JSON.pretty_generate( as_json )
end

#reload!(opts = {}) ⇒ Object

force reload from the database and replace any local fields with data from the persistent store

Parameters:

  • opts (Hash) (defaults to: {})

    a set of options to send to fetch!

See Also:

  • Fetching#fetch!


353
354
355
356
357
# File 'lib/parse/model/object.rb', line 353

def reload!(opts = {})
# get the values from the persistence layer
  fetch!(opts)
  clear_changes!
end

#rollback!Object

Note:

This does not reload the object from the persistent store, for this use “reload!” instead.

Locally restores the previous state of the object and clears all dirty tracking information.

See Also:



413
414
415
# File 'lib/parse/model/object.rb', line 413

def rollback!
  restore_attributes
end

#schemaHash

Returns the schema structure for this Parse collection from the server.

Returns:

  • (Hash)

    the schema structure for this Parse collection from the server.

See Also:



272
273
274
# File 'lib/parse/model/object.rb', line 272

def schema
  self.class.schema
end

#twinParse::Object

This method creates a new object of the same instance type with a copy of all the properties of the current instance. This is useful when you want to create a duplicate record.

Returns:

  • (Parse::Object)

    a twin copy of the object without the objectId



432
433
434
435
436
437
438
# File 'lib/parse/model/object.rb', line 432

def twin
  h = self.as_json
  h.delete(Parse::Model::OBJECT_ID)
  h.delete(:objectId)
  h.delete(:id)
  self.class.new h
end

#updatedAtDate

Alias to #updated_at

Returns:

  • (Date)

    the updated_at date of the record in UTC Zulu iso 8601 with 3 millisecond format.



521
522
523
524
# File 'lib/parse/model/object.rb', line 521

def updatedAt
  return @updated_at if Parse::Object.disable_serialized_string_date.present?
  @updated_at.to_time.utc.iso8601(3) if @updated_at.present?
end

#updates(include_all = false) ⇒ Hash

Returns a hash of all the changes that have been made to the object. By default changes to the Parse::Properties::BASE_KEYS are ignored unless you pass true as an argument.

Parameters:

  • include_all (Boolean) (defaults to: false)

    whether to include all keys in result.

Returns:

  • (Hash)

    a hash containing only the change information.

See Also:



395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/parse/model/object.rb', line 395

def updates(include_all = false)
  h = {}
  changed.each do |key|
    next if include_all == false && Parse::Properties::BASE_KEYS.include?(key.to_sym)
    # lookup the remote Parse field name incase it is different from the local attribute name
    remote_field = self.field_map[key.to_sym] || key
    h[remote_field] = send key
    # make an exception to Parse::Objects, we should return a pointer to them instead
    h[remote_field] = h[remote_field].parse_pointers if h[remote_field].is_a?(Parse::PointerCollectionProxy)
    h[remote_field] = h[remote_field].pointer if h[remote_field].respond_to?(:pointer)
  end
  h
end

#validate!self

Overrides ActiveModel::Validations#validate! instance method. It runs all validations for this object. If validation fails, it raises ActiveModel::ValidationError otherwise it returns the object.

Returns:

  • (self)

    self the object if validation passes.

Raises:

  • ActiveModel::ValidationError

See Also:

  • ActiveModel::Validations#validate!


423
424
425
426
# File 'lib/parse/model/object.rb', line 423

def validate!
  super
  self
end