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, distinct, 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



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/parse/model/object.rb', line 298

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:



215
216
217
# File 'lib/parse/model/object.rb', line 215

def default_acls
  @default_acls
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.



205
206
207
# File 'lib/parse/model/object.rb', line 205

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.



504
505
506
# File 'lib/parse/model/object.rb', line 504

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.



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

def created_at
  @created_at
end

#idString

Returns the value of Parse “objectId” field.

Returns:

  • (String)

    the value of Parse “objectId” field.



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

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.



500
501
502
# File 'lib/parse/model/object.rb', line 500

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:



464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
# File 'lib/parse/model/object.rb', line 464

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.



331
332
333
334
# File 'lib/parse/model/object.rb', line 331

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



243
244
245
246
247
248
# File 'lib/parse/model/object.rb', line 243

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)


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

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)


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

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.



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

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.



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

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

#__typeModel::TYPE_OBJECT

Returns:



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

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


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

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


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

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


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

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



322
323
324
325
326
# File 'lib/parse/model/object.rb', line 322

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.



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

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


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

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


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

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


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

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

#clear_attribute_change!(atts) ⇒ Object

clear all change and dirty tracking information.



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

def clear_attribute_change!(atts)
  clear_attribute_changes(atts)
end

#clear_changes!Object

clears all dirty tracking information



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

def clear_changes!
  clear_changes_information
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.



376
377
378
379
380
381
# File 'lib/parse/model/object.rb', line 376

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.



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

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:



258
259
260
# File 'lib/parse/model/object.rb', line 258

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.



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

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


436
437
438
# File 'lib/parse/model/object.rb', line 436

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!


347
348
349
350
351
# File 'lib/parse/model/object.rb', line 347

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:



407
408
409
# File 'lib/parse/model/object.rb', line 407

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:



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

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



426
427
428
429
430
431
432
# File 'lib/parse/model/object.rb', line 426

def twin
  h = self.as_json
  h.delete(Parse::Model::OBJECT_ID)
  h.delete(:objectId)
  h.delete(:id)
  self.class.new h
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:



389
390
391
392
393
394
395
396
397
398
399
400
401
# File 'lib/parse/model/object.rb', line 389

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!


417
418
419
420
# File 'lib/parse/model/object.rb', line 417

def validate!
  super
  self
end