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::Schema

auto_upgrade!, create_schema, fetch_schema, update_schema

Methods included from Core::Querying

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

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


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

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:


217
218
219
# File 'lib/parse/model/object.rb', line 217

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.


207
208
209
# File 'lib/parse/model/object.rb', line 207

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.


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

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.


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

def created_at
  @created_at
end

#idString

Returns the value of Parse “objectId” field.

Returns:

  • (String)

    the value of Parse “objectId” field.


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

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.


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

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:


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

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.


333
334
335
336
# File 'lib/parse/model/object.rb', line 333

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)

    Whether to allow read permissions (default: false).

  • write (Boolean)

    Whether to allow write permissions (default: false).

  • role (Boolean)

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

See Also:

Version:

  • 1.7.0


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

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.


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

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.


524
525
526
527
# File 'lib/parse/model/object.rb', line 524

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


324
325
326
327
328
# File 'lib/parse/model/object.rb', line 324

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.


273
274
275
276
277
# File 'lib/parse/model/object.rb', line 273

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.


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

def clear_attribute_change!(atts)
  clear_attribute_changes(atts)
end

#clear_changes!Object

clears all dirty tracking information


356
357
358
# File 'lib/parse/model/object.rb', line 356

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.


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

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.


363
364
365
# File 'lib/parse/model/object.rb', line 363

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:


261
262
263
# File 'lib/parse/model/object.rb', line 261

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.


341
342
343
# File 'lib/parse/model/object.rb', line 341

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

438
439
440
# File 'lib/parse/model/object.rb', line 438

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!

349
350
351
352
353
# File 'lib/parse/model/object.rb', line 349

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:


409
410
411
# File 'lib/parse/model/object.rb', line 409

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:


268
269
270
# File 'lib/parse/model/object.rb', line 268

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


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

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:


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

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!

419
420
421
422
# File 'lib/parse/model/object.rb', line 419

def validate!
  super
  self
end