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; with `allowCustomObjectId: true` apps 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 a `Pointer.id=` write into a path-traversal, header-injection, or batch-op-path-poisoning vector when interpolated into REST URLs or batch op `path` fields.
/\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 ‘objectId` of 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.
-
#sig ⇒ String
A string representing the class and id of this instance.
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.
121 122 123 124 |
# File 'lib/parse/model/pointer.rb', line 121 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.
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
# File 'lib/parse/model/pointer.rb', line 356 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.
303 304 305 306 307 |
# File 'lib/parse/model/pointer.rb', line 303 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.
335 336 337 338 |
# File 'lib/parse/model/pointer.rb', line 335 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.
405 406 407 408 |
# File 'lib/parse/model/pointer.rb', line 405 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
137 138 139 |
# File 'lib/parse/model/pointer.rb', line 137 def attributes ATTRIBUTES end |
#className ⇒ String
Returns the name of the Parse class for this pointer.
112 113 114 |
# File 'lib/parse/model/pointer.rb', line 112 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.
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 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/parse/model/pointer.rb', line 194 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.
296 297 298 |
# File 'lib/parse/model/pointer.rb', line 296 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.
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
# File 'lib/parse/model/pointer.rb', line 262 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.
281 282 283 |
# File 'lib/parse/model/pointer.rb', line 281 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.
170 171 172 |
# File 'lib/parse/model/pointer.rb', line 170 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
321 322 323 |
# File 'lib/parse/model/pointer.rb', line 321 def hash [parse_class, id].hash end |
#json_hash ⇒ Hash
Returns serialized JSON structure.
142 143 144 |
# File 'lib/parse/model/pointer.rb', line 142 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.
155 156 157 |
# File 'lib/parse/model/pointer.rb', line 155 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.
162 163 164 |
# File 'lib/parse/model/pointer.rb', line 162 def pointer? present? && @created_at.blank? && @updated_at.blank? end |
#present? ⇒ Boolean
Returns true if instance has a Parse class and an id.
326 327 328 |
# File 'lib/parse/model/pointer.rb', line 326 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.
391 392 393 394 395 396 397 398 |
# File 'lib/parse/model/pointer.rb', line 391 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 |
#sig ⇒ String
Returns a string representing the class and id of this instance.
132 133 134 |
# File 'lib/parse/model/pointer.rb', line 132 def sig "#{@parse_class}##{id || "new"}" end |