Class: Parse::Pointer
Overview
The Pointer class represents the pointer type in Parse and is the superclass of Parse::Object types. A pointer can be considered a type of Parse::Object in which only the class name and id is known. In most cases, you may not deal with Parse::Pointer objects directly if you have defined all your Parse::Object subclasses.
A Parse::Pointer only contains data about the specific Parse class and
the id for the object. Therefore, creating an instance of any
Parse::Object subclass with only the :id field set will be
considered in "pointer" state even though its specific class is not
Parse::Pointer type. The only case that you may have a Parse::Pointer
is in the case where an object was received for one of your classes and
the framework has no registered class handler for it.
Assume you have the tables Post, Comment and Author defined in your
remote Parse database, but have only defined Post and Commentary
locally.
The effect is that for any unknown classes that the framework encounters,
it will generate Parse::Pointer instances until you define those classes
with valid properties and associations. While this might be ok for some
classes you do not use, we still recommend defining all your Parse classes
locally in the framework.
Once you have a subclass, you may also create a Parse::Pointer object using the pointer method.
Direct Known Subclasses
Constant Summary collapse
- ATTRIBUTES =
The default attributes in a Parse Pointer hash.
{ __type: :string, className: :string, objectId: :string }.freeze
- OBJECT_ID_FORMAT =
Permitted character set + length for a Parse objectId. Parse Server itself generates 10-char
[A-Za-z0-9]ids; withallowCustomObjectId: trueapps can pass arbitrary identifiers, so we accept the wider URL-safe set[A-Za-z0-9_.-]and cap length at 64. Anything OUTSIDE this set (/,\, CR/LF,?,&,#,%, quotes, angle brackets, semicolons, whitespace) is rejected — those are the bytes that turn aPointer.id=write into a path-traversal, header-injection, or batch-op-path-poisoning vector when interpolated into REST URLs or batch oppathfields. /\A[A-Za-z0-9_.\-]{1,64}\z/.freeze
Constants inherited from Model
Model::CLASS_AUDIENCE, Model::CLASS_INSTALLATION, Model::CLASS_JOB_SCHEDULE, Model::CLASS_JOB_STATUS, Model::CLASS_PRODUCT, Model::CLASS_PUSH_STATUS, Model::CLASS_ROLE, Model::CLASS_SCHEMA, Model::CLASS_SESSION, Model::CLASS_USER, Model::ID, Model::KEY_CLASS_NAME, Model::KEY_CREATED_AT, Model::KEY_OBJECT_ID, Model::KEY_UPDATED_AT, Model::OBJECT_ID, Model::TYPE_ACL, Model::TYPE_BYTES, Model::TYPE_DATE, Model::TYPE_FIELD, Model::TYPE_FILE, Model::TYPE_GEOPOINT, Model::TYPE_NUMBER, Model::TYPE_OBJECT, Model::TYPE_POINTER, Model::TYPE_POLYGON, Model::TYPE_RELATION
Instance Attribute Summary collapse
-
#id ⇒ String
(also: #objectId)
The objectId field.
-
#parse_class ⇒ String
The name of the collection for this Pointer.
Instance Method Summary collapse
-
#==(o) ⇒ Boolean
(also: #eql?)
Two Parse::Pointers (or Parse::Objects) are equal if both of them have the same Parse class and the same id.
-
#[](key) ⇒ Object
Access the pointer properties through hash accessor.
-
#[]=(key, value) ⇒ Object
Set the pointer properties through hash accessor.
- #__type ⇒ Model::TYPE_POINTER
- #attributes ⇒ Hash
-
#className ⇒ String
The name of the Parse class for this pointer.
-
#fetch(return_object = nil, keys: nil, includes: nil, cache: nil) ⇒ Object
This method is a general implementation that gets overriden by Parse::Object subclass.
-
#fetch_cache!(keys: nil, includes: nil) ⇒ Parse::Object
Fetches the pointer with explicit caching enabled and returns a Parse::Object.
-
#fetch_json(keys: nil, includes: nil) ⇒ Hash?
Returns raw JSON data from the server without creating an object.
-
#fetch_object ⇒ Parse::Object
Fetches the Parse object from the data store and returns a Parse::Object instance.
-
#fetched? ⇒ Boolean
Returns true if the data for this instance has been fetched.
-
#hash ⇒ Integer
Compute a hash-code for this object based on identity (class and id).
-
#initialize(table, oid) ⇒ Pointer
constructor
A Parse pointer only requires the name of the remote Parse collection name, and the
objectIdof the record. -
#json_hash ⇒ Hash
Serialized JSON structure.
-
#method_missing(method_name, *args, &block) ⇒ Object
Handles method calls for properties that exist on the target model class.
-
#pointer ⇒ Pointer
Create a new pointer with the current class name and id.
-
#pointer? ⇒ Boolean
Whether this instance is in pointer state.
-
#present? ⇒ Boolean
True if instance has a Parse class and an id.
-
#respond_to_missing?(method_name, include_private = false) ⇒ Boolean
Indicates whether this object responds to methods that would trigger autofetch.
- #search_highlights ⇒ Hash?
- #search_score ⇒ Float?
-
#sig ⇒ String
A string representing the class and id of this instance.
-
#vector_score ⇒ Float?
Search/vector-search result accessors.
Methods inherited from Model
Methods included from Client::Connectable
Constructor Details
#initialize(table, oid) ⇒ Pointer
A Parse pointer only requires the name of the remote Parse collection name,
and the objectId of the record.
135 136 137 138 |
# File 'lib/parse/model/pointer.rb', line 135 def initialize(table, oid) @parse_class = table.to_s self.id = oid end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_name, *args, &block) ⇒ Object
Handles method calls for properties that exist on the target model class. When a property is accessed on a Pointer, this will auto-fetch the object and delegate the method call to the fetched object.
If Parse.autofetch_raise_on_missing_keys is enabled, this will raise Parse::AutofetchTriggeredError instead of fetching.
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 |
# File 'lib/parse/model/pointer.rb', line 370 def method_missing(method_name, *args, &block) # Try to find the model class for this pointer klass = Parse::Model.find_class(parse_class) # If no class is registered or the class doesn't have this field, use default behavior unless klass && klass.respond_to?(:fields) && klass.fields[method_name.to_s.chomp("=").to_sym] return super end # We have a registered class with this field - handle autofetch field_name = method_name.to_s.chomp("=").to_sym # If autofetch_raise_on_missing_keys is enabled, raise an error if Parse.autofetch_raise_on_missing_keys raise Parse::AutofetchTriggeredError.new(klass, id, field_name, is_pointer: true) end # Log info about autofetch being triggered if Parse.warn_on_query_issues puts "[Parse::Autofetch] Fetching #{parse_class}##{id} - pointer accessed field :#{field_name} (silence with Parse.warn_on_query_issues = false)" end # Fetch the object and delegate the method call @_fetched_object ||= fetch return nil unless @_fetched_object @_fetched_object.send(method_name, *args, &block) end |
Instance Attribute Details
#id ⇒ String Also known as: objectId
Returns the objectId field.
85 86 87 |
# File 'lib/parse/model/pointer.rb', line 85 def id @id end |
#parse_class ⇒ String
Returns the name of the collection for this Pointer.
83 84 85 |
# File 'lib/parse/model/pointer.rb', line 83 def parse_class @parse_class end |
Instance Method Details
#==(o) ⇒ Boolean Also known as: eql?
Two Parse::Pointers (or Parse::Objects) are equal if both of them have the same Parse class and the same id.
317 318 319 320 321 |
# File 'lib/parse/model/pointer.rb', line 317 def ==(o) return false unless o.is_a?(Pointer) #only equal if the Parse class and object ID are the same. self.parse_class == o.parse_class && id == o.id end |
#[](key) ⇒ Object
Access the pointer properties through hash accessor. This is done for compatibility with the hash access of a Parse::Object. This method returns nil if the key is not one of: :id, :objectId, or :className.
349 350 351 352 |
# File 'lib/parse/model/pointer.rb', line 349 def [](key) return nil unless [:id, :objectId, :className].include?(key.to_sym) send(key) end |
#[]=(key, value) ⇒ Object
Set the pointer properties through hash accessor. This is done for compatibility with the hash access of a Parse::Object. This method does nothing if the key is not one of: :id, :objectId, or :className.
419 420 421 422 |
# File 'lib/parse/model/pointer.rb', line 419 def []=(key, value) return unless [:id, :objectId, :className].include?(key.to_sym) send("#{key}=", value) end |
#__type ⇒ Model::TYPE_POINTER
110 |
# File 'lib/parse/model/pointer.rb', line 110 def __type; Parse::Model::TYPE_POINTER; end |
#attributes ⇒ Hash
151 152 153 |
# File 'lib/parse/model/pointer.rb', line 151 def attributes ATTRIBUTES end |
#className ⇒ String
Returns the name of the Parse class for this pointer.
126 127 128 |
# File 'lib/parse/model/pointer.rb', line 126 def parse_class @parse_class end |
#fetch ⇒ Parse::Object #fetch(return_object) ⇒ Parse::Object, Hash #fetch(keys:, includes:, cache:) ⇒ Parse::Object
This method is a general implementation that gets overriden by Parse::Object subclass. Given the class name and the id, we will go to Parse and fetch the actual record, returning the Parse::Object by default.
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 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/parse/model/pointer.rb', line 208 def fetch(return_object = nil, keys: nil, includes: nil, cache: nil) # Handle legacy signature: fetch(false) returns JSON if return_object == false return fetch_json(keys: keys, includes: includes) end # Build query parameters for partial fetch query = {} if keys.present? keys_array = Array(keys).map { |k| Parse::Query.format_field(k) } query[:keys] = keys_array.join(",") end if includes.present? includes_array = Array(includes).map(&:to_s) query[:include] = includes_array.join(",") end # Build opts for caching opts = {} opts[:cache] = cache unless cache.nil? response = client.fetch_object(parse_class, id, query: query.presence, **opts) return nil if response.error? # Check if the result is empty - this indicates object not found result = response.result if result.nil? || (result.is_a?(Array) && result.empty?) return nil end # Convert the JSON result to a proper Parse::Object return nil unless result.is_a?(Hash) # Try to find the appropriate Parse class, fallback to Parse::Object klass = Parse::Model.find_class(parse_class) || Parse::Object # For partial fetch, build with fetched_keys tracking if keys.present? # Parse keys to get top-level field names and nested keys top_level_keys = Array(keys).map { |k| Parse::Query.format_field(k).split(".").first.to_sym } top_level_keys << :id unless top_level_keys.include?(:id) top_level_keys << :objectId unless top_level_keys.include?(:objectId) top_level_keys.uniq! # Parse dot notation into nested fetched keys nested_keys = Parse::Query.parse_keys_to_nested_keys(Array(keys)) obj = klass.build(result, parse_class, fetched_keys: top_level_keys, nested_fetched_keys: nested_keys.presence) else # Full fetch - create without partial fetch tracking. Trusted # hydration: +result+ is the server response body, which # legitimately carries +createdAt+/+updatedAt+/+sessionToken+ # and other PROTECTED_MASS_ASSIGNMENT_KEYS. The +@_trusted_init+ # ivar tells {Parse::Object#initialize} to skip the protected-key # filter — see that method for why we don't use a kwarg. obj = klass.allocate obj.instance_variable_set(:@_trusted_init, true) obj.send(:initialize, result) end obj.clear_changes! if obj.respond_to?(:clear_changes!) obj end |
#fetch_cache!(keys: nil, includes: nil) ⇒ Parse::Object
Fetches the pointer with explicit caching enabled and returns a Parse::Object. This is a convenience method that calls fetch with cache: true. Use this when you want to leverage cached responses for better performance.
310 311 312 |
# File 'lib/parse/model/pointer.rb', line 310 def fetch_cache!(keys: nil, includes: nil) fetch(keys: keys, includes: includes, cache: true) end |
#fetch_json(keys: nil, includes: nil) ⇒ Hash?
Returns raw JSON data from the server without creating an object.
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
# File 'lib/parse/model/pointer.rb', line 276 def fetch_json(keys: nil, includes: nil) query = {} if keys.present? keys_array = Array(keys).map { |k| Parse::Query.format_field(k) } query[:keys] = keys_array.join(",") end if includes.present? includes_array = Array(includes).map(&:to_s) query[:include] = includes_array.join(",") end response = client.fetch_object(parse_class, id, query: query.presence) return nil if response.error? response.result end |
#fetch_object ⇒ Parse::Object
Fetches the Parse object from the data store and returns a Parse::Object instance. This is a convenience method that calls fetch.
295 296 297 |
# File 'lib/parse/model/pointer.rb', line 295 def fetch_object fetch end |
#fetched? ⇒ Boolean
Returns true if the data for this instance has been fetched. Because of some autofetching mechanisms, this is useful to know whether the object already has data without actually causing a fetch of the data.
184 185 186 |
# File 'lib/parse/model/pointer.rb', line 184 def fetched? present? && pointer? == false end |
#hash ⇒ Integer
Compute a hash-code for this object based on identity (class and id). This is consistent with the == method which compares by parse_class and id.
Two objects with the same class and id will have the same hash code regardless of their dirty state or other attributes. This is important for:
- Array operations (uniq, &, |) to work correctly based on identity
- Hash key lookups to find objects by identity
- Set operations
335 336 337 |
# File 'lib/parse/model/pointer.rb', line 335 def hash [parse_class, id].hash end |
#json_hash ⇒ Hash
Returns serialized JSON structure.
156 157 158 |
# File 'lib/parse/model/pointer.rb', line 156 def json_hash JSON.parse to_json end |
#pointer ⇒ Pointer
Create a new pointer with the current class name and id. While this may not make sense for a pointer instance, Parse::Object subclasses use this inherited method to turn themselves into pointer objects.
169 170 171 |
# File 'lib/parse/model/pointer.rb', line 169 def pointer Pointer.new parse_class, @id end |
#pointer? ⇒ Boolean
Whether this instance is in pointer state. A pointer is determined if we have a parse class and an id, but no created_at or updated_at fields.
176 177 178 |
# File 'lib/parse/model/pointer.rb', line 176 def pointer? present? && @created_at.blank? && @updated_at.blank? end |
#present? ⇒ Boolean
Returns true if instance has a Parse class and an id.
340 341 342 |
# File 'lib/parse/model/pointer.rb', line 340 def present? parse_class.present? && @id.present? end |
#respond_to_missing?(method_name, include_private = false) ⇒ Boolean
Indicates whether this object responds to methods that would trigger autofetch. Returns true for properties defined on the target model class.
405 406 407 408 409 410 411 412 |
# File 'lib/parse/model/pointer.rb', line 405 def respond_to_missing?(method_name, include_private = false) klass = Parse::Model.find_class(parse_class) if klass && klass.respond_to?(:fields) field_name = method_name.to_s.chomp("=").to_sym return true if klass.fields[field_name] end super end |
#search_highlights ⇒ Hash?
124 |
# File 'lib/parse/model/pointer.rb', line 124 def search_highlights; @_search_highlights; end |
#search_score ⇒ Float?
122 |
# File 'lib/parse/model/pointer.rb', line 122 def search_score; @_search_score; end |
#sig ⇒ String
Returns a string representing the class and id of this instance.
146 147 148 |
# File 'lib/parse/model/pointer.rb', line 146 def sig "#{@parse_class}##{id || "new"}" end |
#vector_score ⇒ Float?
Search/vector-search result accessors. Defined here (instead of
only on Parse::Object) so that hydration paths which fall back to
a Pointer — e.g. Atlas Search results whose className has no
corresponding Ruby subclass loaded — still surface the score and
highlights attached by Parse::AtlasSearch.process_search_results
/ Parse::Core::VectorSearchable.build_vector_hits. Each returns
nil unless the instance came from the corresponding search path.
120 |
# File 'lib/parse/model/pointer.rb', line 120 def vector_score; @_vector_score; end |