Class: Parse::Object

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`.

Defined Under Namespace

Modules: ValidationCallbackOnSupport

Constant Summary collapse

VALID_ACL_POLICIES =

Valid ACL policies that can be passed to acl_policy.

[:public, :private, :owner_else_public, :owner_else_private].freeze
BUILTIN_PARSE_CLASS_NAMES =

SDK-provided Parse model class names that the policy resolver and init-time default-ACL stamp both skip. Parse Server applies its own per-class defaults for these classes when the save body omits the ‘ACL` field — most importantly, `_User` gets `R/W, “*”: R` so the newly created user can edit their own profile. Stamping any ACL from the SDK side (even `{}`) overrides those server-side defaults and is almost always wrong.

%w[
  Parse::User Parse::Installation Parse::Session Parse::Role
  Parse::Product Parse::PushStatus Parse::Audience
  Parse::JobStatus Parse::JobSchedule
].freeze
IDENTIFICATION_FIELDS =

Core identification fields that are always included in serialization unless strict: true is specified

%w[id objectId __type className].freeze

Constants included from Core::Schema

Core::Schema::DEFAULT_PUBLIC_CLP, Core::Schema::SCHEMA_READONLY_CLASSES

Constants included from Core::Describe

Core::Describe::ALL_SECTIONS, Core::Describe::CORE_FIELD_KEYS, Core::Describe::LOCAL_SECTIONS, Core::Describe::NETWORK_SECTIONS

Constants included from Core::Indexing

Core::Indexing::MAX_INDEXES_PER_COLLECTION, Core::Indexing::PARSE_MANAGED_ARRAY_FIELDS, Core::Indexing::SENSITIVE_FIELDS

Constants included from Core::SearchIndexing

Core::SearchIndexing::ALLOWED_INDEX_TYPES, Core::SearchIndexing::INDEX_NAME_PATTERN

Constants included from Core::Fetching

Core::Fetching::NON_SERIALIZABLE_IVARS

Constants included from Core::ParseReference

Core::ParseReference::OBJECT_ID_LENGTH, Core::ParseReference::SEPARATOR

Constants included from Core::FieldGuards

Core::FieldGuards::GUARD_MODES

Constants included from Properties

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

Constants inherited from Pointer

Pointer::ATTRIBUTES, Pointer::OBJECT_ID_FORMAT

Constants inherited from Model

Model::CLASS_AUDIENCE, Model::CLASS_INSTALLATION, Model::CLASS_JOB_SCHEDULE, Model::CLASS_JOB_STATUS, Model::CLASS_PRODUCT, Model::CLASS_PUSH_STATUS, Model::CLASS_ROLE, Model::CLASS_SCHEMA, 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_POLYGON, Model::TYPE_RELATION

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class-Level Permissions (CLP) collapse

Field Filtering (CLP) collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Core::Querying

all, count, count_distinct, cursor, distinct, each, find, find_cached, first, last_updated, latest, literal_where, newest, oldest, query, scope, subscribe

Methods included from Core::Schema

_default_class_level_permissions_for_upgrade, auto_upgrade!, create_schema, fetch_schema, reset_clp!, update_schema

Methods included from Core::Describe

describe

Methods included from Core::Indexing

apply_indexes!, indexes_plan, mongo_geo_index, mongo_index, mongo_index_declarations, mongo_relation_index

Methods included from Core::SearchIndexing

apply_search_indexes!, mongo_search_index, mongo_search_index_declarations, search_indexes_plan

Methods included from Agent::MetadataDSL

#agent_description, #agent_methods, included, #property_descriptions, #property_enum_descriptions

Methods included from Core::Actions

#_deleted?, #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!, #fetch_cache!, #fetch_json, #fetch_object, #prepare_for_dirty_tracking!

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 Core::ParseReference

format, generate_object_id, parse

Methods included from Core::FieldGuards

#apply_field_guards!

Methods included from Core::EnhancedChangeTracking

included

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, #fetch_cache!, #fetch_json, #fetch_object, #hash, #json_hash, #method_missing, #pointer, #pointer?, #present?, #respond_to_missing?, #sig

Methods inherited from Model

#dirty?, 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. Untrusted by default: keys in Properties::PROTECTED_INITIALIZE_KEYS (sessionToken, _rperm, _wperm, _hashed_password, authData, roles) are filtered out even when an objectId is present. This closes the mass-assignment hole where klass.new(attacker_params) on a hash that happens to include objectId would overwrite session tokens, ACLs, and auth data. Use build for trusted hydration from server JSON; it bypasses the filter.



1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
# File 'lib/parse/model/object.rb', line 1265

def initialize(opts = {})
  # Trusted hydration is signalled by the +@_trusted_init+ instance
  # variable rather than by a +trusted:+ keyword argument. Using a
  # keyword would break subclasses that override +initialize(*args)+
  # and call +super+ — Ruby 3 keyword-arg semantics would convert the
  # kwarg into a positional Hash through the variadic +*args+ splat
  # and the subsequent +super+ would arrive at this method with two
  # positional args. The internal hydration paths
  # ({Parse::Object.build}, {Parse::Pointer} autofetch,
  # {Parse::User#session}) +allocate+ the object, set the ivar, then
  # invoke +initialize+ so subclass overrides still fire and pick up
  # the trust signal here.
  trusted = @_trusted_init == true
  @_trusted_init = nil
  acl_owner_override = nil
  if opts.is_a?(String) #then it's the objectId
    @id = opts.to_s
  elsif opts.is_a?(Hash)
    # Pop the `:as` option (also accepts string key) before applying
    # attributes so it is not mistaken for a model property. This holds
    # the caller-supplied owner user for save-time ACL resolution.
    acl_owner_override = opts.delete(:as) || opts.delete("as")
    #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]
    # Always filter the narrow PROTECTED_INITIALIZE_KEYS set unless
    # the caller is a trusted hydration path. Decoupled from
    # dirty_track so an objectId-bearing hash from a controller,
    # JSON params, or cache rehydrator cannot mass-assign
    # sessionToken / _rperm / _wperm / _hashed_password / authData /
    # roles. The narrow list deliberately allows createdAt /
    # updatedAt / className / __type through so the legitimate
    # +Klass.new("objectId" => id, "createdAt" => ts, …)+
    # cache-rehydrate pattern keeps working.
    apply_attributes!(opts,
                      dirty_track: !dirty_track,
                      filter_protected: !trusted,
                      protected_set: Parse::Properties::PROTECTED_INITIALIZE_KEYS)
  end

  # If the caller did not set an ACL via opts, stamp the class default ACL
  # (the policy's fallback half) so `obj.acl` reads sensibly pre-save.
  # We mark the object as "ACL-pristine": the save-time resolver
  # (#_resolve_default_acl) may upgrade this to an owner-only ACL if an
  # `as:` user or owner field is resolvable. Any explicit caller change
  # via `acl=` flips pristine off via #acl_will_change!.
  #
  # Built-in Parse classes (User, Installation, Session, Role, …) are
  # exempt: the SDK leaves their `acl` untouched (nil) so the save body
  # omits the `ACL` field and Parse Server applies its own per-class
  # defaults. Most importantly this lets `_User` get the standard
  # self-write-plus-public-read ACL on signup; stamping any value from
  # the SDK side (even `{}`) overrides that and locks the new user out
  # of editing their own profile without the master key.
  acl_was_user_supplied = !self.acl.nil?
  unless self.class.builtin_acl_default_active?
    self.acl = self.class.default_acls.as_json if self.acl.nil?
  end
  @_acl_pristine = !acl_was_user_supplied
  @_acl_owner_override = acl_owner_override

  # One-time per-class permissive-default warning. Fires only when the
  # effective policy is :public or :owner_else_public.
  self.class._warn_permissive_acl_default_once

  # 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).
  # Note: apply_defaults! already skips unfetched fields on selectively fetched objects.
  if !pointer?
    apply_defaults!
  end

  # clear changes AFTER applying defaults, so fields set by defaults
  # are not marked dirty when fetching with specific keys
  clear_changes! if @id.present? #then it was an import
  # do not call super since it is Pointer subclass
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Parse::Pointer

Class Attribute Details

.default_acl_privateBoolean

When set to true, new instances of this class will have a private ACL (no public access, master key only) instead of the default public read/write.

Examples:

class PrivateDocument < Parse::Object
  self.default_acl_private = true
end

doc = PrivateDocument.new
doc.acl.as_json # => {} (no permissions, master key only)

Returns:

  • (Boolean)

    whether new objects default to private ACLs.

Version:

  • 3.1.3



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

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



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

def parse_class(remoteName = nil)
  @parse_class ||= model_name.name
  @parse_class = remoteName.to_s unless remoteName.nil?
  @parse_class
end

.suppress_permissive_acl_warningBoolean

When set on ‘Parse::Object` itself, suppresses the one-time per-class warning emitted when a class’s effective acl_policy_setting is ‘:public` or `:owner_else_public`. Useful in test suites or apps that have deliberately reviewed and accepted permissive defaults. Defaults to `true` when `ENV` is set to a truthy value (`1`, `true`, `yes`).

Returns:

  • (Boolean)

Version:

  • 4.1.0



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

attr_writer :suppress_permissive_acl_warning

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.



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

property :acl, :acl, field: :ACL

#created_atDate (readonly)

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.



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

property :created_at, :date

#idString

Returns the value of Parse “objectId” field.

Returns:

  • (String)

    the value of Parse “objectId” field.



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

property :id, field: :objectId

#updated_atDate (readonly)

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.



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

property :updated_at, :date

Class Method Details

.acl_owner_fieldSymbol?

The name of the property/belongs_to designating the owner user for ‘:owner_else_*` ACL policies. Inherited from the superclass when not explicitly declared via acl_policy.

Returns:

Version:

  • 4.1.0



534
535
536
537
538
539
540
541
# File 'lib/parse/model/object.rb', line 534

def acl_owner_field
  return @acl_owner_field if defined?(@acl_owner_field) && @acl_owner_field
  if self != Parse::Object && superclass.respond_to?(:acl_owner_field)
    superclass.acl_owner_field
  else
    nil
  end
end

.acl_policy(policy, owner: nil) ⇒ Object

Declarative ACL policy applied to newly-created instances of this class. The policy is resolved at save time so that explicit ACL changes by the caller (‘obj.acl = …`, `as:` kwarg, owner-field assignment after `.new`) always take precedence over the default.

Resolution order at save (only when caller has not overridden):

  1. Explicit ‘as: user` passed at construction → owner R/W only

  2. Owner pointer resolved from the declared ‘owner:` field → owner R/W only

  3. The else-half of the policy: ‘:public` → public R/W, `:private` → master-key only

Examples:

class Post < Parse::Object
  acl_policy :owner_else_private, owner: :author
end

# server-side: no owner resolvable → master-key-only fallback
Post.create!(title: "draft")

# owner pointer set → ACL granting R/W to that user only
Post.create!(title: "live", author: current_user)

# explicit caller override (works regardless of `author` field)
Post.create!({ title: "x" }, as: current_user)

Parameters:

  • policy (Symbol)

    one of ‘:public`, `:private`, `:owner_else_public`, `:owner_else_private`.

  • owner (Symbol, nil) (defaults to: nil)

    the name of the property/belongs_to whose pointer designates the owner user. Only meaningful for ‘:owner_else_*` policies.

Raises:

See Also:

Version:

  • 4.1.0



465
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
491
492
493
494
495
496
497
498
499
500
501
502
503
504
# File 'lib/parse/model/object.rb', line 465

def acl_policy(policy, owner: nil)
  unless VALID_ACL_POLICIES.include?(policy)
    raise ArgumentError, "Invalid acl_policy #{policy.inspect}; must be one of #{VALID_ACL_POLICIES.inspect}"
  end
  # Symmetric to the guard in set_default_acl: pick one API per class.
  if defined?(@acl_default_customized_by_set_default_acl) && @acl_default_customized_by_set_default_acl
    raise ArgumentError,
      "#{self}: cannot combine `acl_policy` with `set_default_acl`. " \
      "This class already calls `set_default_acl`. Use the declarative " \
      "DSL for the entire ACL configuration, or remove `acl_policy` and " \
      "use only `set_default_acl`."
  end
  # `owner: :self` is a special marker meaning "the record itself is
  # its own owner" — only meaningful for Parse::User and subclasses,
  # where the record IS a user. The save-time resolver pre-generates
  # `@id` via Parse::Core::ParseReference.generate_object_id when
  # blank so the ACL can grant R/W to the record's own objectId in
  # a single roundtrip. Non-User classes have no sensible
  # interpretation (a Post's objectId is not a user id).
  if owner == :self && !(self <= Parse::User)
    raise ArgumentError,
      "#{self}: `owner: :self` is only supported on Parse::User and " \
      "its subclasses (the record IS the owner). For other classes, " \
      "declare a belongs_to pointer to the owning user."
  end
  if owner && !policy.to_s.start_with?("owner_")
    warn "[#{self}] `owner:` is ignored when acl_policy is #{policy.inspect}; only :owner_else_public and :owner_else_private use it."
  end
  if owner.nil? && policy.to_s.start_with?("owner_")
    fallback = (policy == :owner_else_public) ? "public R/W" : "master-key-only"
    warn "[#{self}] acl_policy #{policy.inspect} declared without `owner:` field; ACL resolution will always use the fallback (#{fallback}). Pass `as:` at construction to override."
  end
  @acl_policy_setting = policy
  @acl_owner_field = owner
  # Reset materialized default_acls so it picks up the new policy's fallback half.
  @default_acls = nil
  # Re-arm the permissive-default warning so a subsequent change is re-evaluated.
  @_permissive_default_warned = nil
  policy
end

.acl_policy_settingSymbol

The effective ACL policy for this class. Inherits from the superclass when not explicitly declared. The gem-wide default is ‘:owner_else_private` — records grant read/write to the resolved owner (from `as:` or the class’s ‘owner:` field) when one is supplied, and fall back to master-key-only when no owner is resolvable. `default_acl_private = true` is honored as `:private`. Classes that need public access for new records should declare `acl_policy :public` or `:owner_else_public` explicitly, or use the legacy `set_default_acl` additive API.

Returns:

Version:

  • 4.1.0



517
518
519
520
521
522
523
524
525
526
527
# File 'lib/parse/model/object.rb', line 517

def acl_policy_setting
  return @acl_policy_setting if defined?(@acl_policy_setting) && @acl_policy_setting
  return :private if default_acl_private
  if self == Parse::Object
    :owner_else_private
  elsif superclass.respond_to?(:acl_policy_setting)
    superclass.acl_policy_setting
  else
    :owner_else_private
  end
end

.build(json, table = nil, fetched_keys: nil, nested_fetched_keys: 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.

  • fetched_keys (Array) (defaults to: nil)

    optional array of keys that were fetched (for partial fetch tracking).

  • nested_fetched_keys (Hash) (defaults to: nil)

    optional map of field names to their fetched keys for nested objects.

Returns:



1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
# File 'lib/parse/model/object.rb', line 1679

def self.build(json, table = nil, fetched_keys: nil, nested_fetched_keys: nil)
  # Precedence (most → least authoritative):
  # 1. Caller-supplied +table+ — caller knows the expected class
  #    (e.g. webhook payload routed to a typed handler, has_many that
  #    knows its declared target class).
  # 2. The subclass +parse_class+ when invoked on a Parse::Object
  #    subclass directly (Song.build(json)).
  # 3. The className inside the JSON — only trusted when neither of
  #    the above is available (e.g. base-class +Parse::Object.build+
  #    on untyped JSON).
  # Warn on mismatch between an explicit caller class and the
  # payload-supplied className so type-confusion attacks surface in
  # logs.
  incoming_class = nil
  if json.is_a?(Hash)
    incoming_class = json[Parse::Model::KEY_CLASS_NAME] || json[:className]
  end
  className = table
  if className.nil? && parse_class != BASE_OBJECT_CLASS
    className = parse_class
  end
  className ||= incoming_class
  if className && incoming_class && incoming_class != className
    warn "[Parse::Object.build] expected className=#{className.inspect}, ignoring incoming className=#{incoming_class.inspect}"
  end
  if json.is_a?(Hash) && json["error"].present? && json["code"].present?
    warn "[Parse::Object] Detected object hash with 'error' and 'code' set. : #{json}"
  end
  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.allocate

    # Set BOTH nested_fetched_keys AND fetched_keys BEFORE initialize
    # to ensure partially_fetched? returns correct value during attribute application
    o.instance_variable_set(:@_nested_fetched_keys, nested_fetched_keys) if nested_fetched_keys.present?
    if fetched_keys.present?
      # Process fetched_keys like the setter does - convert to symbols and include :id
      processed_keys = fetched_keys.map { |k| Parse::Query.format_field(k).to_sym }
      processed_keys << :id unless processed_keys.include?(:id)
      processed_keys << :objectId unless processed_keys.include?(:objectId)
      processed_keys.uniq!
      o.instance_variable_set(:@_fetched_keys, processed_keys)
    end

    # Trusted hydration: this path runs on server-side JSON (response
    # bodies, webhook payloads that have already been scrubbed,
    # autofetch results). Server responses legitimately include
    # protected keys like +sessionToken+, +_rperm+ that must populate
    # the in-memory object. Untrusted +klass.new(hash)+ callers
    # default to filter those keys. The +@_trusted_init+ ivar is the
    # signal — see {#initialize} for why we don't use a kwarg.
    o.instance_variable_set(:@_trusted_init, true)
    o.send(:initialize, 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

.class_permissionsParse::CLP Also known as: clp

The Class-Level Permissions for this model. CLPs control access to the class at the schema level.

Returns:

  • (Parse::CLP)

    the CLP instance for this class

See Also:



607
608
609
# File 'lib/parse/model/object.rb', line 607

def class_permissions
  @class_permissions ||= Parse::CLP.new
end

.default_aclsParse::ACL

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

Returns:

  • (Parse::ACL)

    the current default ACLs for this class.

See Also:



365
366
367
368
369
370
371
# File 'lib/parse/model/object.rb', line 365

def default_acls
  @default_acls ||= case acl_policy_setting
                    when :public, :owner_else_public then Parse::ACL.everyone
                    when :private, :owner_else_private then Parse::ACL.private
                    else Parse::ACL.everyone
                    end
end

.describe_accessHash

Introspect the locally-configured access surface for this class. Combines the CLP operations, protectedFields read-side hiding, and the write-side protections installed via the field_guards DSL into a single hash, so it’s easy to audit who can do what to which fields without reading three separate parts of the class body.

The hash is built from the Parse-Stack model declarations only. It does NOT round-trip the Parse Server schema; if you’ve configured CLPs on the server side that haven’t been mirrored locally, those won’t appear here. Conversely, calling ‘update_clp!` pushes what this method reflects.

Examples:

class Post < Parse::Object
  property :title, :string
  property :owner, :string
  guard :owner, :master_only
  parse_reference
  set_class_access(find: :public, create: :authenticated, update: "Admin")
end

Post.describe_access
# =>
# {
#   operations: {
#     find:   { "*" => true },
#     create: { "requiresAuthentication" => true },
#     update: { "role:Admin" => true },
#     ...
#   },
#   read_user_fields:  [],
#   write_user_fields: [],
#   fields: {
#     title:           { write: :open,        read: :open },
#     owner:           { write: :master_only, read: :open },
#     parse_reference: { write: :set_once,    read: { hidden_from: ["*"] } },
#   },
# }

Returns:



968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
# File 'lib/parse/model/object.rb', line 968

def describe_access
  perms = class_permissions
  protected_by_pattern = perms.respond_to?(:protected_fields) ? perms.protected_fields : {}
  guards_map = respond_to?(:field_guards) && field_guards ? field_guards : {}

  # Per-field access summary. Iterate `field_map` (local -> remote)
  # rather than `fields`, because `fields` redundantly stores BOTH
  # the local key (e.g. :full_name) and the remote key (:fullName)
  # for every property. That redundancy would cause multi-word
  # properties to appear twice in the output.
  per_field = {}
  field_map.each do |local_sym, remote_sym|
    local_sym = local_sym.to_sym
    next if Parse::Properties::CORE_FIELDS.key?(local_sym)
    data_type = fields[local_sym]
    remote = remote_sym.to_s

    # Read protection -- collect every protectedFields pattern that
    # lists this field (under either its local or remote name).
    hidden_from = protected_by_pattern.each_with_object([]) do |(pattern, hidden_fields), acc|
      acc << pattern if hidden_fields.include?(remote) || hidden_fields.include?(local_sym.to_s)
    end

    per_field[local_sym] = {
      write: guards_map[local_sym] || :open,
      read:  hidden_from.empty? ? :open : { hidden_from: hidden_from },
      type:  data_type,
    }
  end

  # Deep-copy the operations hash so callers mutating the result
  # don't accidentally mutate the live class_permissions state.
  operations = if perms.respond_to?(:permissions)
      perms.permissions.transform_values { |v| v.is_a?(Hash) ? v.dup : v }
    else
      {}
    end

  {
    operations:        operations,
    read_user_fields:  perms.respond_to?(:read_user_fields)  ? perms.read_user_fields  : [],
    write_user_fields: perms.respond_to?(:write_user_fields) ? perms.write_user_fields : [],
    fields:            per_field,
  }
end

.fetch_clp(client: nil) ⇒ Parse::CLP Also known as: fetch_class_permissions

Fetch the current CLP from the Parse Server for this class.

Parameters:

  • client (Parse::Client) (defaults to: nil)

    optional client to use

Returns:



1017
1018
1019
1020
1021
1022
1023
1024
# File 'lib/parse/model/object.rb', line 1017

def fetch_clp(client: nil)
  client ||= self.client
  response = client.schema(parse_class)
  return Parse::CLP.new unless response.success?

  clp_data = response.result["classLevelPermissions"] || {}
  Parse::CLP.new(clp_data)
end

.filter_results_for_user(objects, user, roles: [], authenticated: nil, clp: nil) ⇒ Array<Hash>

Filter an array of Parse objects or hashes for a user. Class method that applies CLP filtering to multiple results.

Examples:

Filter query results for a user

songs = Song.query(artist: "Beatles").results
filtered = Song.filter_results_for_user(songs, current_user, roles: user_roles)

Parameters:

  • objects (Array<Parse::Object, Hash>)

    array of objects or hashes to filter

  • user (Parse::User, String, nil)

    the user or user ID

  • roles (Array<String>) (defaults to: [])

    role names the user belongs to

  • authenticated (Boolean) (defaults to: nil)

    whether the user is authenticated

  • clp (Parse::CLP, nil) (defaults to: nil)

    optional CLP to use (defaults to class CLP)

Returns:

  • (Array<Hash>)

    filtered data hashes with protected fields removed

See Also:



1120
1121
1122
1123
1124
1125
1126
1127
1128
# File 'lib/parse/model/object.rb', line 1120

def self.filter_results_for_user(objects, user, roles: [], authenticated: nil, clp: nil)
  clp ||= class_permissions
  return objects.map { |o| o.is_a?(Parse::Object) ? o.as_json : o } unless clp.present?

  objects.map do |obj|
    data = obj.is_a?(Parse::Object) ? obj.as_json : obj
    clp.filter_fields(data, user: user, roles: roles, authenticated: authenticated)
  end
end

.master_only_class!void

This method returns an undefined value.

Lock every CLP operation to master-key access only. Use as a starting point when a class should be entirely hidden from clients; you can then selectively open specific operations with set_clp or set_class_access afterward.

Examples:

Hide a class entirely from clients

class AuditLog < Parse::Object
  master_only_class!
end

Hide everything, then open create+get for clients

class Invitation < Parse::Object
  master_only_class!
  set_clp :create, public: true
  set_clp :get, public: true
end


763
764
765
766
# File 'lib/parse/model/object.rb', line 763

def master_only_class!
  Parse::CLP::OPERATIONS.each { |op| set_clp(op) }
  nil
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.



1358
1359
1360
1361
# File 'lib/parse/model/object.rb', line 1358

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

.private_acl!Object

Convenience method to set default ACL to private (no public access). Equivalent to ‘self.default_acl_private = true`.

Examples:

class PrivateDocument < Parse::Object
  private_acl!
end

Version:

  • 3.1.3



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

def private_acl!
  self.default_acl_private = true
end

.protect_fields(pattern, fields) ⇒ Object Also known as: set_protected_fields

Define protected fields that should be hidden from certain users/roles. This is used to implement field-level security.

Field names are automatically converted from snake_case (Ruby convention) to camelCase (Parse Server convention). You can use either format.

Examples:

Hide fields from public but allow admins to see everything

class User < Parse::Object
  property :email, :string
  property :phone, :string
  property :internal_notes, :string

  # Hide sensitive fields from public (use snake_case Ruby names)
  protect_fields "*", [:email, :phone, :internal_notes]

  # Admins can see everything (empty array = no restrictions)
  protect_fields "role:Admin", []

  # Users can see their own data
  protect_fields "userField:objectId", []
end

Hide metadata from non-owners

class Image < Parse::Object
  property :url, :string
  property :metadata, :object  # GPS, camera info, etc.
  belongs_to :owner, as: :user

  # Hide metadata from everyone (auto-converts to "metadata" in Parse)
  protect_fields "*", [:metadata]

  # But owners can see their own image metadata
  protect_fields "userField:owner", []
end

Master key only fields

class SensitiveDoc < Parse::Object
  property :admin_notes, :string
  property :internal_score, :integer

  # Only master key can see these fields
  # (converts to ["adminNotes", "internalScore"] for Parse Server)
  protect_fields "*", [:admin_notes, :internal_score]
end

Parameters:

  • pattern (String, Symbol)

    the pattern to apply protection for:

    • “*” or :public - applies to all users (public)

    • “role:RoleName” - applies to users in a specific role

    • “userField:fieldName” - applies to users referenced in a pointer field

    • user objectId - applies to a specific user

  • fields (Array<String, Symbol>)

    field names to hide from this pattern. Use Ruby property names (snake_case) - they will be auto-converted. An empty array means the user can see all fields.

See Also:



906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
# File 'lib/parse/model/object.rb', line 906

def protect_fields(pattern, fields)
  pattern = "*" if pattern.to_sym == :public rescue pattern

  # Convert userField:field_name pattern to use camelCase field name
  if pattern.to_s.start_with?("userField:")
    field_name = pattern.to_s.sub("userField:", "")
    field_sym = field_name.to_sym
    converted_field = field_map[field_sym] || field_name.camelize(:lower)
    pattern = "userField:#{converted_field}"
  end

  # Convert snake_case Ruby property names to camelCase Parse field names
  converted_fields = Array(fields).map do |field|
    field_sym = field.to_sym
    # Use field_map if available, otherwise convert to camelCase
    field_map[field_sym] || field.to_s.camelize(:lower)
  end
  class_permissions.set_protected_fields(pattern, converted_fields)
end

.roles_for_user(user) ⇒ Array<String>

Fetch a user’s roles for use with field filtering. Convenience method to get role names that can be passed to filter methods.

Examples:

Get roles and filter

roles = Song.roles_for_user(current_user)
filtered = song.filter_for_user(current_user, roles: roles)

Parameters:

Returns:

  • (Array<String>)

    role names (without “role:” prefix)



1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
# File 'lib/parse/model/object.rb', line 1139

def self.roles_for_user(user)
  return [] unless user.is_a?(Parse::User) || user.is_a?(Parse::Pointer)
  return [] unless defined?(Parse::Role)

  user_id = user.respond_to?(:id) ? user.id : user.to_s
  return [] if user_id.blank?

  Parse::Role.all(users: user).map(&:name)
rescue => e
  warn "[Parse] Error fetching roles for user: #{e.message}"
  []
end

.set_class_access(**ops_to_access) ⇒ void

This method returns an undefined value.

Set CLP for multiple operations in one call, choosing a coarse access mode per operation. Each value can be:

  • ‘:master` / `:master_only` / `nil` / `false` – master key only (Parse Server’s empty ‘{}` permission for that op)

  • ‘:public` / `true` – wildcard `*` access

  • ‘:authenticated` – requiresAuthentication

  • a String or Symbol – a single role name (the ‘role:` prefix is added automatically)

  • an Array of Strings/Symbols – multiple role names

Operations not listed in the hash are left at their current setting. For finer control (mixed roles, users, pointer-fields, requires_authentication) use set_clp directly.

Examples:

The _Installation pattern – get-by-id and create, but no listing

class Invitation < Parse::Object
  set_class_access(
    find:     :master,        # nobody can list
    count:    :master,        # nobody can count
    get:      :public,        # anyone with the id can fetch
    create:   :authenticated, # logged-in users may create
    update:   :master,        # only server may update
    delete:   :master,        # only server may delete
  )
end

Admin-only writes, public reads

class Article < Parse::Object
  set_class_access(
    find: :public, get: :public,
    create: "Admin", update: "Admin", delete: "Admin",
  )
end

Parameters:



824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
# File 'lib/parse/model/object.rb', line 824

def set_class_access(**ops_to_access)
  ops_to_access.each do |op, access|
    op = op.to_sym
    unless Parse::CLP::OPERATIONS.include?(op)
      raise ArgumentError,
            "Unknown CLP operation #{op.inspect}. Allowed: #{Parse::CLP::OPERATIONS.inspect}"
    end
    case access
    when :master, :master_only, nil, false
      set_clp(op)
    when :public, true
      set_clp(op, public: true)
    when :authenticated
      set_clp(op, requires_authentication: true)
    when Array
      set_clp(op, roles: access.map(&:to_s))
    when String, Symbol
      set_clp(op, roles: [access.to_s])
    else
      raise ArgumentError,
            "Unknown class_access value for :#{op}: #{access.inspect}. " \
            "Use :master, :public, :authenticated, a role name, or an array of roles."
    end
  end
  nil
end

.set_clp(operation, public: nil, roles: [], users: [], pointer_fields: [], requires_authentication: false) ⇒ Object Also known as: set_class_permission

Set a class-level permission for a specific operation. This is the main DSL method for configuring CLPs in your model.

Examples:

Basic usage

class Song < Parse::Object
  # Allow public read
  set_clp :find, public: true
  set_clp :get, public: true

  # Restrict write operations to specific roles
  set_clp :create, public: false, roles: ["Admin", "Editor"]
  set_clp :update, public: false, roles: ["Admin", "Editor"]
  set_clp :delete, public: false, roles: ["Admin"]
end

Requiring authentication

class PrivateData < Parse::Object
  set_clp :find, requires_authentication: true
  set_clp :get, requires_authentication: true
end

Parameters:

  • operation (Symbol)

    the operation (:find, :get, :count, :create, :update, :delete, :addField)

  • public (Boolean, nil) (defaults to: nil)

    whether public access is allowed

  • roles (Array<String>, String) (defaults to: [])

    role names that have access

  • users (Array<String>, String) (defaults to: [])

    user objectIds that have access

  • pointer_fields (Array<String>, String) (defaults to: [])

    pointer field names for userField access

  • requires_authentication (Boolean) (defaults to: false)

    whether authentication is required

See Also:



726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
# File 'lib/parse/model/object.rb', line 726

def set_clp(operation, public: nil, roles: [], users: [], pointer_fields: [], requires_authentication: false)
  # Convert snake_case pointer field names to camelCase
  converted_pointer_fields = Array(pointer_fields).map do |field|
    field_sym = field.to_sym
    field_map[field_sym] || field.to_s.camelize(:lower)
  end

  class_permissions.set_permission(
    operation,
    public_access: public,
    roles: Array(roles),
    users: Array(users),
    pointer_fields: converted_pointer_fields,
    requires_authentication: requires_authentication
  )
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



397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
# File 'lib/parse/model/object.rb', line 397

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
  # Mixing the declarative `acl_policy` DSL with the legacy additive
  # `set_default_acl` API on the same class produces ambiguous behavior
  # (which one wins at save time? which fields get which permissions?).
  # Pick one and stick with it.
  if defined?(@acl_policy_setting) && @acl_policy_setting
    raise ArgumentError,
      "#{self}: cannot combine `set_default_acl` with `acl_policy`. " \
      "This class already declares `acl_policy #{@acl_policy_setting.inspect}`. " \
      "Use the declarative DSL for the entire ACL configuration, or remove " \
      "`acl_policy` and use only `set_default_acl` (the legacy additive API)."
  end
  # Mark the class as using the legacy additive ACL API. The save-time
  # policy resolver respects this and leaves the init-stamped default
  # ACL alone, preserving pre-4.1 behavior for classes that customize
  # via set_default_acl.
  @acl_default_customized_by_set_default_acl = true
  role ? default_acls.apply_role(id, read, write) : default_acls.apply(id, read, write)
end

.set_default_clp(public: nil, roles: [], requires_authentication: false) ⇒ Object

Set default permissions for all CLP operations at once. This is useful for establishing a baseline before customizing specific operations.

Examples:

Public read, authenticated write

class Document < Parse::Object
  # Start with public read access for all operations
  set_default_clp public: true

  # Then restrict write operations
  set_clp :create, requires_authentication: true
  set_clp :update, requires_authentication: true
  set_clp :delete, public: false, roles: ["Admin"]
end

Role-based access for everything

class AdminReport < Parse::Object
  # Only admins can do anything
  set_default_clp public: false, roles: ["Admin"]
end

Authenticated users only

class PrivateData < Parse::Object
  # Require authentication for all operations
  set_default_clp requires_authentication: true
end

Parameters:

  • public (Boolean) (defaults to: nil)

    whether public access is allowed for all operations

  • roles (Array<String>) (defaults to: [])

    role names that have access to all operations

  • requires_authentication (Boolean) (defaults to: false)

    whether authentication is required for all operations



642
643
644
645
646
647
648
649
650
651
652
653
654
655
# File 'lib/parse/model/object.rb', line 642

def set_default_clp(public: nil, roles: [], requires_authentication: false)
  # Set the default permission on the CLP instance
  # This will be used by as_json to fill in missing operations
  class_permissions.set_default_permission(
    public_access: public,
    roles: Array(roles),
    requires_authentication: requires_authentication
  )

  # Also explicitly set all operations to ensure they're included
  Parse::CLP::OPERATIONS.each do |operation|
    set_clp(operation, public: public, roles: roles, requires_authentication: requires_authentication)
  end
end

.set_read_user_fields(*fields) ⇒ Object

Set pointer-permission fields for read access. Users pointed to by these fields can read objects of this class. This is an alternative to ACLs for owner-based access control.

Examples:

class Document < Parse::Object
  belongs_to :owner, as: :user
  belongs_to :editor, as: :user

  # Only owner and editor can read
  set_read_user_fields :owner, :editor
end

Parameters:



670
671
672
673
674
675
676
# File 'lib/parse/model/object.rb', line 670

def set_read_user_fields(*fields)
  converted = fields.flatten.map do |f|
    field_sym = f.to_sym
    field_map[field_sym] || f.to_s.camelize(:lower)
  end
  class_permissions.set_read_user_fields(*converted)
end

.set_write_user_fields(*fields) ⇒ Object

Set pointer-permission fields for write access. Users pointed to by these fields can write to objects of this class.

Examples:

class Document < Parse::Object
  belongs_to :owner, as: :user

  # Only owner can write
  set_write_user_fields :owner
end

Parameters:



689
690
691
692
693
694
695
# File 'lib/parse/model/object.rb', line 689

def set_write_user_fields(*fields)
  converted = fields.flatten.map do |f|
    field_sym = f.to_sym
    field_map[field_sym] || f.to_s.camelize(:lower)
  end
  class_permissions.set_write_user_fields(*converted)
end

.unlistable_class!void

This method returns an undefined value.

Restrict ‘find` and `count` to master-key only, leaving the other operations (`get`, `create`, `update`, `delete`, `addField`) at their current settings. This is the canonical “Installation-style” pattern: clients can interact with individual records but cannot enumerate or count them.

Examples:

Mirror _Installation semantics

class Invitation < Parse::Object
  unlistable_class!
  # clients can still get/create/update/delete by objectId
end


781
782
783
784
785
# File 'lib/parse/model/object.rb', line 781

def unlistable_class!
  set_clp(:find)
  set_clp(:count)
  nil
end

.update_clp!(client: nil, replace: false) ⇒ Parse::Response Also known as: update_class_permissions!

Update the CLP on the Parse Server for this class. Merges local CLP with any existing server CLP.

Examples:

Push local CLP to server

Song.update_clp!

Replace server CLP entirely

Song.update_clp!(replace: true)

Parameters:

  • client (Parse::Client) (defaults to: nil)

    optional client to use

  • replace (Boolean) (defaults to: false)

    if true, replaces server CLP entirely; otherwise merges

Returns:



1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
# File 'lib/parse/model/object.rb', line 1040

def update_clp!(client: nil, replace: false)
  client ||= self.client

  unless client.master_key.present?
    warn "[Parse] CLP changes for #{parse_class} require the master key!"
    return nil
  end

  clp_data = class_permissions.as_json
  return nil if clp_data.empty?

  schema_update = { "classLevelPermissions" => clp_data }
  client.update_schema(parse_class, schema_update)
end

.webhook(type, &block) { ... } ⇒ 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)

    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)


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

def self.webhook(type, &block)
  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, &block)
    # then block must be a symbol or a string
  else
    if block_given?
      Parse::Webhooks.route(type, self, &block)
    else
      Parse::Webhooks.route(type, self, block)
    end
  end
  #if block

end

.webhook_function(functionName, &block) { ... } ⇒ 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)

    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)


36
37
38
39
40
41
42
43
44
# File 'lib/parse/webhooks.rb', line 36

def self.webhook_function(functionName, &block)
  if block_given?
    Parse::Webhooks.route(:function, functionName, &block)
  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.



1949
1950
1951
1952
# File 'lib/parse/model/object.rb', line 1949

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.



1960
1961
1962
1963
# File 'lib/parse/model/object.rb', line 1960

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

#__typeModel::TYPE_OBJECT

Returns:



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

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

#_resolve_acl_owner_id(owner) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Resolves an ‘as:` value or owner-field pointer to an objectId string. Strictly type-gated to Parse::User-shaped inputs to prevent accidental ACL grants to non-user records (Roles use `role:` ACL keys, not raw objectIds; pointers to non-User classes would silently grant access to whatever record happens to share that objectId in the User collection). Accepted forms:

- Parse::User instance
- Parse::Pointer with parse_class == "_User"
- Raw objectId String (caller's responsibility to ensure it is a user id)

Anything else returns nil and the policy falls through to its else-half.



1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
# File 'lib/parse/model/object.rb', line 1856

def _resolve_acl_owner_id(owner)
  return nil if owner.nil?
  return nil if owner.respond_to?(:empty?) && owner.empty?
  if owner.is_a?(Parse::Pointer)
    return nil unless owner.parse_class == Parse::Model::CLASS_USER
    return owner.id if owner.id.present?
    return nil
  end
  return owner if owner.is_a?(String) && owner.present?
  nil
end

#_resolve_default_aclObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Save-time resolver for the declarative acl_policy default ACL. Runs as a ‘before_save` callback. If the caller has not overridden the ACL (no `acl=` since the init-time default stamp), resolves an owner from `@_acl_owner_override` (the `as:` kwarg) or from the class’s declared owner field, and applies an owner-only ACL. Falls back to the policy’s else-half (‘:public` or `:private`) when no owner is resolvable.



1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
# File 'lib/parse/model/object.rb', line 1772

def _resolve_default_acl
  return true unless defined?(@_acl_pristine) && @_acl_pristine
  # Legacy classes that customize defaults via set_default_acl opt out
  # of the policy resolver: the init-time stamp already reflects the
  # caller's intent and we must not overwrite it.
  return true if self.class.acl_default_customized_by_set_default_acl?
  # Built-in Parse classes (User, Installation, Session, …) are exempt
  # by default; see the matching guard in #initialize. Parse Server
  # applies its own ACL defaults when the save body omits the `ACL`
  # field, and those defaults (e.g. `_User` → self-write + public read)
  # are the right answer in nearly every case. Applications that need
  # to customize a built-in's ACL policy do so by calling `acl_policy`
  # or `set_default_acl` on the class — that flips
  # `builtin_acl_default_active?` to false and re-enables both the
  # init-time stamp and this resolver for that class.
  return true if self.class.builtin_acl_default_active?
  policy = self.class.acl_policy_setting

  owner = @_acl_owner_override if defined?(@_acl_owner_override)
  if owner.nil? && (field = self.class.acl_owner_field)
    owner = if field == :self
      # Self-referential ownership (Parse::User only — enforced at
      # declaration time). Pre-generate a Parse-compatible objectId
      # client-side so the ACL grant can reference the record's own
      # id in the same POST body that creates it. Skipped when the id
      # is already set (e.g. when re-saving an existing user, or when
      # parse_reference precompute already ran).
      @id = Parse::Core::ParseReference.generate_object_id if @id.blank?
      @id
    elsif respond_to?(field)
      send(field)
    end
  end
  owner_id = _resolve_acl_owner_id(owner)

  target_acl = case policy
               when :public
                 Parse::ACL.everyone(true, true)
               when :private
                 Parse::ACL.private
               when :owner_else_public
                 if owner_id
                   acl = Parse::ACL.new
                   acl.apply(owner_id, true, true)
                   acl
                 else
                   Parse::ACL.everyone(true, true)
                 end
               when :owner_else_private
                 if owner_id
                   acl = Parse::ACL.new
                   acl.apply(owner_id, true, true)
                   acl
                 else
                   Parse::ACL.private
                 end
               end

  # Only re-stamp if the resolved ACL differs from the init-time stamp;
  # this avoids an unnecessary dirty mark on the acl field for `:public`
  # / `:private` policies where the init stamp already matches.
  if @acl.nil? || @acl.as_json != target_acl.as_json
    self.acl = target_acl.as_json
  end
  # @_acl_pristine is now false via #acl_will_change! (when re-stamped)
  # or it remains true (when nothing needed to change); either way the
  # resolver has done its job and need not run again. Return a non-false
  # value so the save callback chain is not halted by the model's
  # terminator (`result_lambda.call == false`).
  @_acl_pristine = false
  true
end

#acl_changed?Boolean

Override acl_changed? to compare actual ACL content, not just object references. This ensures that setting an ACL to identical values doesn’t mark it as changed.

Returns:

  • (Boolean)

    true only if the ACL content has actually changed.



1913
1914
1915
1916
1917
1918
1919
1920
# File 'lib/parse/model/object.rb', line 1913

def acl_changed?
  # First check if ActiveModel thinks it changed
  return false unless super
  # Then verify the content actually changed by comparing JSON representations
  acl_was_json = acl_was.respond_to?(:as_json) ? acl_was.as_json : acl_was
  acl_current_json = @acl&.respond_to?(:as_json) ? @acl.as_json : @acl
  acl_was_json != acl_current_json
end

#acl_wasParse::ACL

Override acl_was to return the captured snapshot instead of the reference stored by ActiveModel’s dirty tracking.

Returns:

  • (Parse::ACL)

    the ACL value before any changes were made.



1901
1902
1903
1904
1905
1906
1907
1908
# File 'lib/parse/model/object.rb', line 1901

def acl_was
  # If we have a snapshot, return it; otherwise fall back to ActiveModel's behavior
  if defined?(@_acl_snapshot_before_change) && @_acl_snapshot_before_change
    @_acl_snapshot_before_change
  else
    super
  end
end

#acl_will_change!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Override acl_will_change! to capture a snapshot of the ACL before modification. This is necessary because ACL is a mutable object that can be modified in place (via apply, apply_role, etc.). Without this, acl_was would return a reference to the same object as acl, making them appear identical after in-place changes.

Also clears the ACL-pristine flag so the save-time default-ACL resolver leaves caller-set ACLs alone. The initial default stamp performed in #initialize is excluded by re-asserting ‘@_acl_pristine = true` after the stamp, so this hook can safely treat any subsequent change as a caller intent to override.



1881
1882
1883
1884
1885
1886
1887
1888
1889
# File 'lib/parse/model/object.rb', line 1881

def acl_will_change!
  # Only capture snapshot on the first change (before any modifications)
  unless defined?(@_acl_snapshot_before_change) && @_acl_snapshot_before_change
    # Deep copy the ACL by creating a new one from its JSON representation
    @_acl_snapshot_before_change = @acl ? Parse::ACL.new(@acl.as_json) : Parse::ACL.new
  end
  @_acl_pristine = false if defined?(@_acl_pristine)
  super
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


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

#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


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

#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


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

#after_update { ... } ⇒ Object

A callback called after the object has been updated.

Yields:

  • A block to execute for the callback.

See Also:

  • ActiveModel::Callbacks


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

#after_validation { ... } ⇒ Object

A callback called after validations are run.

Yields:

  • A block to execute for the callback.

See Also:

  • ActiveModel::Callbacks


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

#apply_defaults!Array

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

Returns:

  • (Array)

    list of default fields



1345
1346
1347
1348
1349
1350
1351
1352
1353
# File 'lib/parse/model/object.rb', line 1345

def apply_defaults!
  self.class.defaults_list.each do |key|
    # Skip applying defaults to unfetched fields on selectively fetched objects.
    # This preserves the ability to autofetch when the field is accessed.
    next if has_selective_keys? && !field_was_fetched?(key)

    send(key) # should call set default proc/values if nil
  end
end

#around_create { ... } ⇒ Object

A callback called around object creation.

Yields:

  • A block to execute for the callback.

See Also:

  • ActiveModel::Callbacks


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

#around_destroy { ... } ⇒ Object

A callback called around object destruction.

Yields:

  • A block to execute for the callback.

See Also:

  • ActiveModel::Callbacks


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

#around_save { ... } ⇒ Object

A callback called around object save.

Yields:

  • A block to execute for the callback.

See Also:

  • ActiveModel::Callbacks


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

#around_update { ... } ⇒ Object

A callback called around object update.

Yields:

  • A block to execute for the callback.

See Also:

  • ActiveModel::Callbacks


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

#around_validation { ... } ⇒ Object

A callback called around validations.

Yields:

  • A block to execute for the callback.

See Also:

  • ActiveModel::Callbacks


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

#as_json(opts = nil) ⇒ Hash

Returns a json-hash representing this object.

Parameters:

  • opts (Hash) (defaults to: nil)

    options for serialization

Options Hash (opts):

  • :only_fetched (Boolean)

    when true (or when Parse.serialize_only_fetched_fields is true and this option is not explicitly set to false), only serialize fields that were fetched for partially fetched objects. This prevents autofetch during serialization.

  • :only (Array<Symbol,String>)

    limit serialization to these fields. By default, identification fields (objectId, className, __type, id) are always included for proper object identification. Use strict: true to disable this behavior.

  • :except (Array<Symbol,String>)

    exclude these fields from serialization

  • :exclude_keys (Array<Symbol,String>)

    alias for :except

  • :exclude (Array<Symbol,String>)

    alias for :except

  • :strict (Boolean)

    when true with :only, performs strict filtering without automatically including identification fields. Default is false.

Returns:

  • (Hash)

    a json-hash representing this object.



1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
# File 'lib/parse/model/object.rb', line 1171

def as_json(opts = nil)
  opts ||= {}

  # Normalize :exclude_keys and :exclude to :except (alias support)
  if !opts[:except]
    if opts[:exclude_keys]
      opts = opts.merge(except: opts[:exclude_keys])
    elsif opts[:exclude]
      opts = opts.merge(except: opts[:exclude])
    end
  end

  # When :only is specified without :strict, automatically include identification fields
  # so the serialized object can be properly identified
  if opts[:only] && !opts[:strict]
    only_keys = Array(opts[:only]).map(&:to_s)
    only_keys |= IDENTIFICATION_FIELDS
    opts = opts.merge(only: only_keys)
  end

  # For selectively fetched objects (partial fetch), serialize only the fetched fields.
  # This takes priority over pointer detection because a partial fetch has actual data
  # even if it lacks timestamps (which would otherwise make it look like a pointer).
  # This behavior is controlled by:
  # 1. Per-call: opts[:only_fetched] (explicit true/false)
  # 2. Global: Parse.serialize_only_fetched_fields (default true)
  if has_selective_keys?
    # Determine if we should serialize only fetched fields
    only_fetched = opts.fetch(:only_fetched) { Parse.serialize_only_fetched_fields }

    if only_fetched && !opts.key?(:only)
      # Build the :only list from fetched keys
      # Use the local field names which match the attribute methods
      only_keys = fetched_keys.map(&:to_s)
      # Always include Parse metadata fields for proper object identification
      only_keys |= IDENTIFICATION_FIELDS
      only_keys |= %w[created_at updated_at]
      opts = opts.merge(only: only_keys)
    end

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

  # When in pointer state (no data fetched, just an objectId), return the serialized
  # pointer hash (with __type, className, objectId) for proper JSON serialization
  return pointer.as_json(opts) if pointer?

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

#autofetch_disabled?Boolean

Returns whether autofetch is disabled for this instance.

Returns:

  • (Boolean)

    true if autofetch is disabled



1501
1502
1503
# File 'lib/parse/model/object.rb', line 1501

def autofetch_disabled?
  @_autofetch_disabled == true
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


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

#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


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

#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


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

#before_update { ... } ⇒ Object

A callback called before the object is updated (not on create).

Yields:

  • A block to execute for the callback.

See Also:

  • ActiveModel::Callbacks


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

#before_validation { ... } ⇒ Object

A callback called before validations are run.

Yields:

  • A block to execute for the callback.

See Also:

  • ActiveModel::Callbacks


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

#changedArray<String>

Override changed to filter out ACL when its content hasn’t actually changed. This ensures dirty? returns false when ACL is rebuilt to identical values. For new objects, ACL is always included since it needs to be sent to the server.

Returns:



1926
1927
1928
1929
1930
1931
1932
1933
1934
# File 'lib/parse/model/object.rb', line 1926

def changed
  result = super.dup
  # If ACL is in the changed list but content is identical, remove it
  # BUT keep it if the object is new (needs to be sent to server)
  if result.include?("acl") && !new? && !acl_changed?
    result.delete("acl")
  end
  result
end

#changed?Boolean

Override changed? to use our filtered changed list. ActiveModel’s changed? uses internal tracking that doesn’t account for ACL content comparison.

Returns:

  • (Boolean)

    true if any attributes have changed.



1940
1941
1942
# File 'lib/parse/model/object.rb', line 1940

def changed?
  changed.any?
end

#clear_attribute_change!(atts) ⇒ Object

clear all change and dirty tracking information.



1654
1655
1656
# File 'lib/parse/model/object.rb', line 1654

def clear_attribute_change!(atts)
  clear_attribute_changes(atts)
end

#clear_changes!Object

clears all dirty tracking information



1397
1398
1399
1400
1401
# File 'lib/parse/model/object.rb', line 1397

def clear_changes!
  clear_changes_information
  # Clear the ACL snapshot used for proper acl_was tracking
  @_acl_snapshot_before_change = nil
end

#clear_partial_fetch_state!void

This method returns an undefined value.

Clears all partial fetch tracking state. Called after successful save since server returns updated object.



1570
1571
1572
1573
# File 'lib/parse/model/object.rb', line 1570

def clear_partial_fetch_state!
  @_fetched_keys = nil
  @_nested_fetched_keys = nil
end

#disable_autofetch!void

This method returns an undefined value.

Disables autofetch for this object instance. Useful for preventing automatic network requests.



1489
1490
1491
# File 'lib/parse/model/object.rb', line 1489

def disable_autofetch!
  @_autofetch_disabled = true
end

#enable_autofetch!void

This method returns an undefined value.

Enables autofetch for this object instance (default behavior).



1495
1496
1497
# File 'lib/parse/model/object.rb', line 1495

def enable_autofetch!
  @_autofetch_disabled = false
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.



1440
1441
1442
1443
1444
1445
# File 'lib/parse/model/object.rb', line 1440

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

#fetched?Boolean

Returns whether this object has been fetched from the server (fully or partially). Overrides Pointer#fetched? to return true for any object with data.

Returns:

  • (Boolean)

    true if the object has data (not just a pointer).



1474
1475
1476
# File 'lib/parse/model/object.rb', line 1474

def fetched?
  !pointer?
end

#fetched_keysArray<Symbol>

Returns the array of keys that were fetched for this object. Empty array means the object was fully fetched. Returns a frozen duplicate to prevent external mutation.

Returns:



1482
1483
1484
# File 'lib/parse/model/object.rb', line 1482

def fetched_keys
  (@_fetched_keys || []).dup.freeze
end

#fetched_keys=(keys) ⇒ Array

Sets the fetched keys for this object. Used internally when building objects from partial fetch queries.

Parameters:

  • keys (Array)

    the keys that were fetched

Returns:

  • (Array)

    the stored keys



1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
# File 'lib/parse/model/object.rb', line 1509

def fetched_keys=(keys)
  if keys.nil? || keys.empty?
    @_fetched_keys = nil
  else
    # Always include :id and convert to symbols
    @_fetched_keys = keys.map { |k| Parse::Query.format_field(k).to_sym }
    @_fetched_keys << :id unless @_fetched_keys.include?(:id)
    @_fetched_keys << :objectId unless @_fetched_keys.include?(:objectId)
    @_fetched_keys.uniq!
  end
  @_fetched_keys
end

#field_was_fetched?(key) ⇒ Boolean

Returns whether a specific field was fetched for this object. Base keys (id, created_at, updated_at) are always considered fetched.

Parameters:

Returns:

  • (Boolean)

    true if the field was fetched or if object is fully fetched.



1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
# File 'lib/parse/model/object.rb', line 1526

def field_was_fetched?(key)
  # If not partially fetched (i.e., still a pointer), all fields are NOT fetched
  return false if pointer?

  # If no selective keys were specified, this is a fully fetched object
  # All fields are considered fetched
  return true unless has_selective_keys?

  key = key.to_sym
  # Base keys are always considered fetched
  return true if Parse::Properties::BASE_KEYS.include?(key)
  return true if key == :acl || key == :ACL

  # Check both local key and remote field name
  # Convert remote_key to symbol for consistent comparison
  remote_key = self.field_map[key]&.to_sym
  @_fetched_keys.include?(key) || (remote_key && @_fetched_keys.include?(remote_key))
end

#filter_for_user(user, roles: [], authenticated: nil, clp: nil) ⇒ Hash

Filter this object’s fields based on Class-Level Permissions for a user. Uses the CLP configured on the model class to determine which fields should be visible to the given user/roles context.

This is useful for filtering webhook responses or API data before sending to clients.

Examples:

Filter object for a specific user

song = Song.first
filtered = song.filter_for_user(current_user, roles: ["Member"])

Filter for unauthenticated access

filtered = song.filter_for_user(nil)

Parameters:

  • user (Parse::User, String, nil)

    the user or user ID

  • roles (Array<String>) (defaults to: [])

    role names the user belongs to

  • authenticated (Boolean) (defaults to: nil)

    whether the user is authenticated

  • clp (Parse::CLP, nil) (defaults to: nil)

    optional CLP to use (defaults to class CLP)

Returns:

  • (Hash)

    filtered data hash with protected fields removed

See Also:



1098
1099
1100
1101
1102
1103
# File 'lib/parse/model/object.rb', line 1098

def filter_for_user(user, roles: [], authenticated: nil, clp: nil)
  clp ||= self.class.class_permissions
  return as_json unless clp.present?

  clp.filter_fields(as_json, user: user, roles: roles, authenticated: authenticated)
end

#fully_fetched?Boolean

Returns whether this object is fully fetched with all fields available. Returns false if the object is a pointer or was fetched with specific keys.

Returns:

  • (Boolean)

    true if the object is fully fetched.



1467
1468
1469
# File 'lib/parse/model/object.rb', line 1467

def fully_fetched?
  !pointer? && !has_selective_keys?
end

#has?(key) ⇒ Boolean

Check if a field has a value (is present and not nil).

Parameters:

Returns:

  • (Boolean)

    true if the field has a non-nil value, false otherwise.



1976
1977
1978
1979
1980
# File 'lib/parse/model/object.rb', line 1976

def has?(key)
  return false unless self.class.fields[key.to_sym].present?
  value = send(key)
  !value.nil?
end

#has_selective_keys?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns whether this object was fetched with specific keys (selective fetch). When selectively fetched, accessing unfetched fields will trigger an autofetch. This is an internal method used for autofetch logic.

Returns:

  • (Boolean)

    true if the object was fetched with specific keys.



1452
1453
1454
# File 'lib/parse/model/object.rb', line 1452

def has_selective_keys?
  @_fetched_keys&.any? || false
end

#keysArray<String>

Returns an array of property names (keys) for this Parse::Object. Similar to Hash#keys, this method returns all the defined field names for this object’s class.

Returns:

  • (Array<String>)

    an array of property names as strings.



1969
1970
1971
# File 'lib/parse/model/object.rb', line 1969

def keys
  self.class.fields.keys.map(&:to_s)
end

#nested_fetched_keysHash

Returns the nested fetched keys map for building nested objects.

Returns:

  • (Hash)

    map of field names to their fetched keys



1547
1548
1549
# File 'lib/parse/model/object.rb', line 1547

def nested_fetched_keys
  @_nested_fetched_keys || {}
end

#nested_fetched_keys=(keys_map) ⇒ Hash

Sets the nested fetched keys map for building nested objects.

Parameters:

  • keys_map (Hash)

    map of field names to their fetched keys

Returns:

  • (Hash)

    the stored map



1554
1555
1556
# File 'lib/parse/model/object.rb', line 1554

def nested_fetched_keys=(keys_map)
  @_nested_fetched_keys = keys_map.is_a?(Hash) ? keys_map : nil
end

#nested_keys_for(field_name) ⇒ Array?

Gets the fetched keys for a specific nested field.

Parameters:

Returns:

  • (Array, nil)

    the fetched keys for the nested object, or nil if not specified



1561
1562
1563
1564
1565
# File 'lib/parse/model/object.rb', line 1561

def nested_keys_for(field_name)
  return nil unless @_nested_fetched_keys.present?
  field_name = field_name.to_sym
  @_nested_fetched_keys[field_name]
end

#new?Boolean

An object is considered new until it has been successfully persisted to the server. “Persisted” means the server has returned a ‘createdAt` timestamp, which only happens after a successful create. Checking path assigns @id client-side in a `before_create` callback, so an @id-only check would flip mid-callback-chain and confuse user code (validation `on: :create / :update`, beforeSave handlers, etc.). Treating an object as “new” until createdAt arrives keeps semantics stable from the first `before_save` through the end of `after_create`.

Returns:

  • (Boolean)

    true if the object has not yet been persisted.



1413
1414
1415
# File 'lib/parse/model/object.rb', line 1413

def new?
  @id.blank? || @created_at.nil?
end

#parse_classString Also known as: className

Returns the Parse class for this object.

Returns:

  • (String)

    the Parse class for this object.

See Also:



1063
1064
1065
# File 'lib/parse/model/object.rb', line 1063

def parse_class
  self.class.parse_class
end

#partially_fetched?Boolean

Returns whether this object was fetched with specific keys (partial/selective fetch). When partially fetched, only the specified keys are available and accessing other fields will trigger an autofetch. Returns false for pointers and fully fetched objects.

Returns:

  • (Boolean)

    true if the object was fetched with specific keys.



1460
1461
1462
# File 'lib/parse/model/object.rb', line 1460

def partially_fetched?
  !pointer? && has_selective_keys?
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.



1366
1367
1368
# File 'lib/parse/model/object.rb', line 1366

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


1649
1650
1651
# File 'lib/parse/model/object.rb', line 1649

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. By default, bypasses cache reads but updates the cache with fresh data (write-only mode) so future cached reads get the latest data.

Examples:

Reload with fresh data (default - updates cache)

song.reload!

Reload with full caching (may return cached data)

song.reload!(cache: true)

Reload completely bypassing cache

song.reload!(cache: false)

Parameters:

  • opts (Hash)

    a set of options to send to fetch!

Options Hash (**opts):

  • :cache (Boolean, Symbol) — default: :write_only

    caching mode:

    • :write_only (default) - skip cache read, but update cache with fresh data

    • true - read from and write to cache

    • false - completely bypass cache (no read or write)

See Also:

  • Fetching#fetch!


1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
# File 'lib/parse/model/object.rb', line 1385

def reload!(**opts)
  # Default to write-only cache mode - reload always gets fresh data
  # but updates cache for future cached reads. Controlled by feature flag.
  unless opts.key?(:cache)
    opts[:cache] = Parse.cache_write_on_fetch ? :write_only : false
  end
  # 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:



1620
1621
1622
# File 'lib/parse/model/object.rb', line 1620

def rollback!
  restore_attributes
end

#run_after_create_callbacksBoolean

Run after_create callbacks for this object. This method is called by webhook handlers when an object is created.

Returns:

  • (Boolean)

    true if callbacks executed successfully



1578
1579
1580
# File 'lib/parse/model/object.rb', line 1578

def run_after_create_callbacks
  run_callbacks_from_list(self.class._create_callbacks, :after)
end

#run_after_delete_callbacksBoolean

Run after_destroy callbacks for this object. This method is called by webhook handlers when an object is deleted.

Returns:

  • (Boolean)

    true if callbacks executed successfully



1592
1593
1594
# File 'lib/parse/model/object.rb', line 1592

def run_after_delete_callbacks
  run_callbacks_from_list(self.class._destroy_callbacks, :after)
end

#run_after_save_callbacksBoolean

Run after_save callbacks for this object. This method is called by webhook handlers when an object is saved.

Returns:

  • (Boolean)

    true if callbacks executed successfully



1585
1586
1587
# File 'lib/parse/model/object.rb', line 1585

def run_after_save_callbacks
  run_callbacks_from_list(self.class._save_callbacks, :after)
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:



1071
1072
1073
# File 'lib/parse/model/object.rb', line 1071

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



1639
1640
1641
1642
1643
1644
1645
# File 'lib/parse/model/object.rb', line 1639

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:



1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
# File 'lib/parse/model/object.rb', line 1602

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

#valid?(context = nil) ⇒ Boolean

Override valid? to run validation callbacks. This wraps the standard ActiveModel validation with our custom :validation callbacks.

Parameters:

  • context (Symbol, nil) (defaults to: nil)

    validation context (same as ActiveModel)

Returns:

  • (Boolean)

    true if the object passes all validations



1421
1422
1423
1424
1425
1426
1427
# File 'lib/parse/model/object.rb', line 1421

def valid?(context = nil)
  result = true
  run_callbacks :validation do
    result = super(context)
  end
  result
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!


1630
1631
1632
1633
# File 'lib/parse/model/object.rb', line 1630

def validate!
  super
  self
end