Class: Parse::Model

Inherits:
Object
  • Object
show all
Extended by:
ActiveModel::Callbacks, ActiveModel::Naming
Includes:
ActiveModel::Conversion, ActiveModel::Dirty, ActiveModel::Model, ActiveModel::Serializers::JSON, Client::Connectable
Defined in:
lib/parse/model/model.rb,
lib/parse/model/core/builder.rb

Overview

The core model of all Parse-Stack classes. This class works by providing a baseline for all subclass objects to support ActiveModel features such as serialization, dirty tracking, callbacks, etc.

See Also:

  • ActiveModel

Direct Known Subclasses

Bytes, File, GeoJSON::Geometry, GeoPoint, Pointer, Polygon

Defined Under Namespace

Classes: Builder

Constant Summary collapse

TYPE_FIELD =

The name of the field in a hash that contains information about the type of data the hash represents.

"__type".freeze
OBJECT_ID =

The objectId field in Parse Objects.

"objectId".freeze
ID =

See Also:

"id".freeze
KEY_CLASS_NAME =

The key field for getting class information.

"className".freeze
KEY_OBJECT_ID =
Deprecated.

Use OBJECT_ID instead.

"objectId".freeze
KEY_CREATED_AT =

The key field for getting the created at date of an object hash.

"createdAt"
KEY_UPDATED_AT =

The key field for getting the updated at date of an object hash.

"updatedAt"
CLASS_USER =

The collection for Users in Parse. Used by Parse::User.

"_User"
CLASS_INSTALLATION =

The collection for Installations in Parse. Used by Parse::Installation.

"_Installation"
CLASS_SESSION =

The collection for revocable Sessions in Parse. Used by Parse::Session.

"_Session"
CLASS_ROLE =

The collection for Roles in Parse. Used by Parse::Role.

"_Role"
CLASS_PRODUCT =

The collection for to store Products (in-App purchases) in Parse. Used by Parse::Product.

"_Product"
CLASS_AUDIENCE =

The collection for Audiences in Parse. Used by Parse::Audience.

"_Audience"
CLASS_PUSH_STATUS =

The collection for Push Status in Parse. Used by Parse::PushStatus.

"_PushStatus"
CLASS_JOB_STATUS =

The collection for background job status in Parse. Used by Parse::JobStatus.

"_JobStatus"
CLASS_JOB_SCHEDULE =

The collection for scheduled background jobs in Parse. Used by Parse::JobSchedule.

"_JobSchedule"
CLASS_SCHEMA =

The internal schema collection in Parse. Managed by Parse Server.

"_SCHEMA"
SYSTEM_CLASS_MAP =

Maps the camelized bare name of a Parse Server built-in class to its leading-underscore storage form. Consulted by String#to_parse_class ONLY as a fallback when find_class cannot resolve the name — which happens at class-declaration time for a built-in whose Ruby class is not yet registered (e.g. +Parse::Installation+ declares +belongs_to :user+ before +Parse::User+ is loaded). Without this fallback the conversion froze the wrong literal (+"User"+) into the association +references+ map and pushed it to the server schema as the pointer +targetClass+, which Parse Server then rejected (+Pointer+ vs +Pointer<_User>+). A genuinely-registered class still wins via find_class, so a custom +parse_class+ table mapping is never overridden by this map. Keys are the camelized bare names (the form String#to_parse_class computes before lookup); only +User+ is actually targeted by a built-in association before its class registers, the rest are hygiene so an app that declares a pointer/relation to any built-in resolves correctly regardless of load order.

{
  "User" => CLASS_USER,
  "Role" => CLASS_ROLE,
  "Session" => CLASS_SESSION,
  "Installation" => CLASS_INSTALLATION,
  "Product" => CLASS_PRODUCT,
  "Audience" => CLASS_AUDIENCE,
  "PushStatus" => CLASS_PUSH_STATUS,
  "JobStatus" => CLASS_JOB_STATUS,
  "JobSchedule" => CLASS_JOB_SCHEDULE,
}.freeze
TYPE_FILE =

The type label for hashes containing file data. Used by Parse::File.

"File"
TYPE_GEOPOINT =

The type label for hashes containing geopoints. Used by Parse::GeoPoint.

"GeoPoint"
TYPE_POLYGON =

The type label for hashes containing polygons. Used by Parse::Polygon.

"Polygon"
TYPE_OBJECT =

The type label for hashes containing a Parse object. Used by Parse::Object and subclasses.

"Object"
TYPE_DATE =

The type label for hashes containing a Parse date object. Used by Parse::Date.

"Date"
TYPE_BYTES =

The type label for hashes containing 'byte' data. Used by Parse::Bytes.

"Bytes"
TYPE_ACL =

The type label for hashes containing ACL data. Used by Parse::ACL

"ACL"
TYPE_NUMBER =

The type label for hashes storing numeric data.

"Number"
TYPE_POINTER =

The type label for hashes containing a Parse pointer.

"Pointer"
TYPE_RELATION =

The type label for hashes representing relational data.

"Relation"

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Client::Connectable

#client

Class Attribute Details

.raise_on_save_failureBoolean

By default, we return true or false for save and destroy operations. If you prefer to have Parse::Object raise an exception instead, you can tell to do so either globally or on a per-model basis. When a save fails, it will raise a Parse::RecordNotSaved.

When enabled, if an error is returned by Parse due to saving or destroying a record, due to your before_save or before_delete validation cloud code triggers, Parse::Object will return the a Parse::RecordNotSaved exception type. This exception has an instance method of #object which contains the object that failed to save.

Examples:

Parse::Model.raise_on_save_failure = true # globally across all models
Song.raise_on_save_failure = true          # per-model

# or per-instance raise on failure
song.save!

Returns:

  • (Boolean)


164
165
166
# File 'lib/parse/model/model.rb', line 164

def raise_on_save_failure
  @global_raise_on_save_failure ||= false
end

Class Method Details

.find_class(str) ⇒ Parse::Object

Find a Parse::Model subclass matching this type or Pares collection name. This helper method is useful to find the corresponding class ruby Parse::Object subclass that handles the provided parse class.

Examples:

Parse::Model.find_class('_User') # => Parse::User
Parse::Model.find_class('_Date') # => Parse::Date
Parse::Model.find_class('Installation') # => Parse::Installation

class Artist < Parse::Object
  parse_class "Musician"
end

Parse::Model.find_class("Musician") # => Artist

Parameters:

  • str (String)

    the class name

Returns:

  • (Parse::Object)

    a Parse::Object subclass or a specific Parse type.



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/parse/model/model.rb', line 190

def self.find_class(str)
  return Parse::File if str == TYPE_FILE
  return Parse::GeoPoint if str == TYPE_GEOPOINT
  return Parse::Polygon if str == TYPE_POLYGON
  return Parse::Date if str == TYPE_DATE
  return Parse::Bytes if str == TYPE_BYTES
  # return Parse::User if str == "User"
  # return Parse::Installation if str == "Installation"

  str = str.to_s
  # Basically go through all Parse::Object subclasses and see who is has a parse_class
  # set to this string. We will cache the results for future use.
  #
  # Anonymous descendants (`Class.new(Parse::Object)` without an
  # assigned constant) have `model_name.name == nil`, and the default
  # `parse_class` implementation calls `model_name.name` and raises
  # `ActiveModel::Name: Class name cannot be blank`. An unrescued
  # raise here poisons every subsequent `find_class` call (cf.
  # canonical-filter lookups, hidden-class registry, ACLScope role
  # expansion). Some tests, however, override `parse_class` on an
  # anonymous subclass to return a literal String — those are still
  # legitimate findables. Rescue per-descendant so the iteration
  # continues past the unnamed-and-default-parse_class case while
  # still considering anonymous-but-overridden ones.
  # Reference Parse::Model directly: this class method is inherited by
  # subclasses (e.g. Parse::Object.find_class), so a bare `@model_cache`
  # would resolve on the subclass singleton — which has no cache. The
  # cache lives on Parse::Model itself.
  Parse::Model.model_cache_mutex.synchronize do
    cached = Parse::Model.model_cache[str]
    return cached if cached

    result = Parse::Object.descendants.find do |f|
      begin
        cls = f.parse_class
      rescue StandardError
        next false
      end
      cls == str || cls == "_#{str}"
    end
    Parse::Model.model_cache[str] = result if result
    result
  end
end

.same_parse_class?(a, b) ⇒ Boolean

Whether two Parse class-name strings denote the same class. This is the canonical equality used to suppress spurious className-mismatch warnings. Two names are the same class when they are string-equal, when one is the leading-underscore system form of the other (+User+ <-> +_User+, +Role+ <-> +_Role+, +Installation+ <-> +_Installation+, +Session+ <-> +_Session+), or when both resolve to the same registered +Parse::Object+ subclass (covers custom +parse_class+ table mappings).

The underscore rule is what makes the comparison correct independent of autoload order — at declaration time find_class may not yet have +Parse::User+ registered (so a +belongs_to :user+ captures +"User"+ rather than +"User"+), but the server always emits the +_User+ storage form, and both denote the same class. The rule matches exactly one system prefix underscore, so a malformed +"_User"+ is not conflated with +"_User"+.

These warnings are an ADVISORY type-confusion signal, not the enforcement mechanism: every call site that consults this method builds the resulting object from the declared/trusted class regardless of the incoming className (see +Parse::Object.build+, which always uses the +table+ argument the caller passes). A false-positive here can therefore only suppress a log line — it can never route a pointer of the wrong class into a typed slot. Distinct classes still compare unequal (+User+ vs +_Session+, +User+ vs +_Role+, and +nil+), so a genuinely mismatched pointer still surfaces in logs.

Parameters:

Returns:

  • (Boolean)

    true when both names denote the same Parse class.



264
265
266
267
268
269
270
271
272
273
274
# File 'lib/parse/model/model.rb', line 264

def self.same_parse_class?(a, b)
  return true if a == b
  return false if a.nil? || b.nil?
  a = a.to_s
  b = b.to_s
  return true if a == b
  return true if (a == "_#{b}" && !b.start_with?("_")) ||
                 (b == "_#{a}" && !a.start_with?("_"))
  ka = find_class(a)
  !ka.nil? && ka == find_class(b)
end

Instance Method Details

#dirty?(field = nil) ⇒ Boolean

Add dirty? methods as aliases for changed? methods These provide compatibility with expected API

Returns:

  • (Boolean)


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

def dirty?(field = nil)
  if field.nil?
    changed?
  else
    field_changed = "#{field}_changed?"
    respond_to?(field_changed) ? send(field_changed) : false
  end
end