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, :public_read, :private, :owner_else_public, :owner_else_private, :owner_but_public_read].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 {"<user-id>": 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::INDEX_REGISTRY_MUTEX, 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::EmbedManaged

Core::EmbedManaged::WRITER_KEY

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::SYSTEM_CLASS_MAP, 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, all_as, count, count_distinct, cursor, distinct, each, find, find_cached, first, first_as, group_by, group_by_date, 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, unique_index_on

Methods included from Core::SearchIndexing

apply_search_indexes!, mongo_search_index, mongo_search_index_declarations, search_indexes_plan

Methods included from Core::VectorSearchable

find_similar

Methods included from Agent::MetadataDSL

#agent_description, #agent_methods, #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 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, same_parse_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.



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
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
# File 'lib/parse/model/object.rb', line 1317

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



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

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.



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

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["PARSE_SUPPRESS_PERMISSIVE_ACL_WARNING"] is set to a truthy value (1, true, yes).

Returns:

  • (Boolean)

Version:

  • 4.1.0



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

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.



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

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.



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

property :created_at, :date

#idString

Returns the value of Parse "objectId" field.

Returns:

  • (String)

    the value of Parse "objectId" field.



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

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.



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

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



572
573
574
575
576
577
578
579
# File 'lib/parse/model/object.rb', line 572

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, :public_read, :private, :owner_else_public, :owner_else_private, :owner_but_public_read. :public_read stamps {"*": {"read": true}} — anyone can read, no one can write through ACL (only the master key can mutate). Useful for catalog/lookup tables. :owner_but_public_read stamps the resolved owner with R/W AND grants public read in the same ACL — useful for publicly-viewable content with a single authoring user; falls back to :public_read semantics when no owner resolves.

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

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

Raises:

See Also:

Version:

  • 4.1.0



499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
# File 'lib/parse/model/object.rb', line 499

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, :owner_else_private, and :owner_but_public_read use it."
  end
  if owner.nil? && policy.to_s.start_with?("owner_")
    fallback = case policy
               when :owner_else_public then "public R/W"
               when :owner_but_public_read then "public read only"
               else "master-key-only"
               end
    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



555
556
557
558
559
560
561
562
563
564
565
# File 'lib/parse/model/object.rb', line 555

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:



1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
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
# File 'lib/parse/model/object.rb', line 1750

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 && !Parse::Model.same_parse_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:



645
646
647
# File 'lib/parse/model/object.rb', line 645

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:



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

def default_acls
  @default_acls ||= case acl_policy_setting
                    when :public, :owner_else_public then Parse::ACL.everyone
                    when :public_read, :owner_but_public_read then Parse::ACL.everyone(true, false)
                    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:



1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
# File 'lib/parse/model/object.rb', line 1006

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:



1055
1056
1057
1058
1059
1060
1061
1062
# File 'lib/parse/model/object.rb', line 1055

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:



1158
1159
1160
1161
1162
1163
1164
1165
1166
# File 'lib/parse/model/object.rb', line 1158

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!

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


801
802
803
804
# File 'lib/parse/model/object.rb', line 801

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.



1410
1411
1412
1413
# File 'lib/parse/model/object.rb', line 1410

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



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

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:



944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
# File 'lib/parse/model/object.rb', line 944

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)



1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
# File 'lib/parse/model/object.rb', line 1177

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)

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:



862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
# File 'lib/parse/model/object.rb', line 862

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:



764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
# File 'lib/parse/model/object.rb', line 764

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



424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
# File 'lib/parse/model/object.rb', line 424

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



680
681
682
683
684
685
686
687
688
689
690
691
692
693
# File 'lib/parse/model/object.rb', line 680

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:



708
709
710
711
712
713
714
# File 'lib/parse/model/object.rb', line 708

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:



727
728
729
730
731
732
733
# File 'lib/parse/model/object.rb', line 727

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!

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


819
820
821
822
823
# File 'lib/parse/model/object.rb', line 819

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:



1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
# File 'lib/parse/model/object.rb', line 1078

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

.wait_for(**kwargs, &block) ⇒ Object

Block until the first row matching Core::Querying#where (and an optional predicate block) arrives via LiveQuery. See Console.wait_for.



198
199
200
# File 'lib/parse/console.rb', line 198

def wait_for(**kwargs, &block)
  Parse::Console.wait_for(self, **kwargs, &block)
end

.watch(**kwargs, &block) ⇒ Object

Tail this class as LiveQuery events arrive — blocking, Ctrl-C to stop. See Console.watch.



191
192
193
# File 'lib/parse/console.rb', line 191

def watch(**kwargs, &block)
  Parse::Console.watch(self, **kwargs, &block)
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.



2026
2027
2028
2029
# File 'lib/parse/model/object.rb', line 2026

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.



2037
2038
2039
2040
# File 'lib/parse/model/object.rb', line 2037

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

#__typeModel::TYPE_OBJECT

Returns:



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

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.


1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
# File 'lib/parse/model/object.rb', line 1933

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.



1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
# File 'lib/parse/model/object.rb', line 1843

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 :public_read
                 Parse::ACL.everyone(true, false)
               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
               when :owner_but_public_read
                 acl = Parse::ACL.everyone(true, false)
                 acl.apply(owner_id, true, true) if owner_id
                 acl
               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.



1990
1991
1992
1993
1994
1995
1996
1997
# File 'lib/parse/model/object.rb', line 1990

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.



1978
1979
1980
1981
1982
1983
1984
1985
# File 'lib/parse/model/object.rb', line 1978

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.



1958
1959
1960
1961
1962
1963
1964
1965
1966
# File 'lib/parse/model/object.rb', line 1958

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 188

#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 188

#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 188

#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 188

#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 188

#apply_defaults!Array

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

Returns:

  • (Array)

    list of default fields



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

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 188

#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 188

#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 188

#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 188

#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 188

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



1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
# File 'lib/parse/model/object.rb', line 1209

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

  # `:vector` fields are excluded from serialization by default —
  # embeddings are large (often 1024–4096 floats), they leak ML
  # signal to clients, and they round-trip through the dedicated
  # embed/find_similar pipelines rather than the standard REST
  # save/find. Pass `include_vectors: true` to opt back in (e.g.,
  # for tests or internal mongo-direct bulk writes).
  unless opts[:include_vectors] == true
    vector_fields = self.class.respond_to?(:fields) ? self.class.fields(:vector).keys.map(&:to_s) : []
    if vector_fields.any?
      except = Array(opts[:except]).map(&:to_s) | vector_fields
      opts = opts.merge(except: except)
    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



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

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 188

#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 188

#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 188

#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 188

#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 188

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



2003
2004
2005
2006
2007
2008
2009
2010
2011
# File 'lib/parse/model/object.rb', line 2003

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.



2017
2018
2019
# File 'lib/parse/model/object.rb', line 2017

def changed?
  changed.any?
end

#clear_attribute_change!(atts) ⇒ Object

clear all change and dirty tracking information.



1725
1726
1727
# File 'lib/parse/model/object.rb', line 1725

def clear_attribute_change!(atts)
  clear_attribute_changes(atts)
end

#clear_changes!Object

clears all dirty tracking information



1449
1450
1451
1452
1453
# File 'lib/parse/model/object.rb', line 1449

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!

This method returns an undefined value.

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



1622
1623
1624
1625
# File 'lib/parse/model/object.rb', line 1622

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

#disable_autofetch!

This method returns an undefined value.

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



1541
1542
1543
# File 'lib/parse/model/object.rb', line 1541

def disable_autofetch!
  @_autofetch_disabled = true
end

#enable_autofetch!

This method returns an undefined value.

Enables autofetch for this object instance (default behavior).



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

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.



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

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



1526
1527
1528
# File 'lib/parse/model/object.rb', line 1526

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:



1534
1535
1536
# File 'lib/parse/model/object.rb', line 1534

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



1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
# File 'lib/parse/model/object.rb', line 1561

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.



1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
# File 'lib/parse/model/object.rb', line 1578

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:



1136
1137
1138
1139
1140
1141
# File 'lib/parse/model/object.rb', line 1136

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.



1519
1520
1521
# File 'lib/parse/model/object.rb', line 1519

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.



2053
2054
2055
2056
2057
# File 'lib/parse/model/object.rb', line 2053

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.



1504
1505
1506
# File 'lib/parse/model/object.rb', line 1504

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.



2046
2047
2048
# File 'lib/parse/model/object.rb', line 2046

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



1599
1600
1601
# File 'lib/parse/model/object.rb', line 1599

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



1606
1607
1608
# File 'lib/parse/model/object.rb', line 1606

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



1613
1614
1615
1616
1617
# File 'lib/parse/model/object.rb', line 1613

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.



1465
1466
1467
# File 'lib/parse/model/object.rb', line 1465

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:



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

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.



1512
1513
1514
# File 'lib/parse/model/object.rb', line 1512

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.



1418
1419
1420
# File 'lib/parse/model/object.rb', line 1418

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


1720
1721
1722
# File 'lib/parse/model/object.rb', line 1720

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!


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

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:



1691
1692
1693
# File 'lib/parse/model/object.rb', line 1691

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



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

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



1644
1645
1646
# File 'lib/parse/model/object.rb', line 1644

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



1637
1638
1639
# File 'lib/parse/model/object.rb', line 1637

def run_after_save_callbacks
  run_callbacks_from_list(self.class._save_callbacks, :after)
end

#run_before_create_callbacksBoolean

Run before_create callbacks for this object (BEFORE phase only). Parse Server exposes no separate beforeCreate trigger, so the beforeSave webhook runs these for new objects right after before_save -- matching ActiveModel order, where before_save wraps before_create. Honors :if/:unless and the terminator.

Returns:

  • (Boolean)

    false if a before_create callback halted the chain, else true.



1663
1664
1665
# File 'lib/parse/model/object.rb', line 1663

def run_before_create_callbacks
  run_before_phase_callbacks(:create)
end

#run_before_save_callbacksBoolean

Run before_save callbacks for this object (BEFORE phase only). Used by the beforeSave webhook. Honors :if/:unless conditions and the callback terminator: returns false if a callback halts the chain. The after_* callbacks are NOT run here -- they belong to the afterSave webhook.

Returns:

  • (Boolean)

    false if a before_save callback halted the chain, else true.



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

def run_before_save_callbacks
  run_before_phase_callbacks(:save)
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:



1109
1110
1111
# File 'lib/parse/model/object.rb', line 1109

def schema
  self.class.schema
end

#search_highlightsHash?

Returns Atlas Search highlights blob.

Returns:

  • (Hash, nil)

    Atlas Search highlights blob.



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

def search_highlights; @_search_highlights; end

#search_scoreFloat?

Returns Atlas Search relevance score.

Returns:

  • (Float, nil)

    Atlas Search relevance score.



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

def search_score; @_search_score; 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



1710
1711
1712
1713
1714
1715
1716
# File 'lib/parse/model/object.rb', line 1710

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:



1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
# File 'lib/parse/model/object.rb', line 1673

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



1473
1474
1475
1476
1477
1478
1479
# File 'lib/parse/model/object.rb', line 1473

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!


1701
1702
1703
1704
# File 'lib/parse/model/object.rb', line 1701

def validate!
  super
  self
end

#vector_scoreFloat?

Search/vector-search result accessors. Populated by Parse::AtlasSearch.process_search_results and Parse::Core::VectorSearchable.build_vector_hits via instance_variable_set. Defined here once instead of per-result via define_singleton_method so high-k result sets don't inflate a singleton class per row, and so a user-defined override on a subclass can't silently desync from the ivar.

Each returns nil unless the object was returned from the corresponding search path.

Returns:

  • (Float, nil)

    vectorSearch relevance score.



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

def vector_score; @_vector_score; end