Class: Parse::Installation

Inherits:
Object show all
Defined in:
lib/parse/model/classes/installation.rb,
lib/parse/stack/generators/templates/model_installation.rb

Overview

This class represents the data and columns contained in the standard Parse _Installation collection. This class is also responsible for managing the device tokens for mobile devices in order to use push notifications. All queries done to send pushes using Parse::Push are performed against the Installation collection. An installation object represents an instance of your app being installed on a device. These objects are used to store subscription data for installations which have subscribed to one or more push notification channels.

The default schema for Installation is as follows:

class Parse::Installation < Parse::Object # See Parse::Object for inherited properties...

property :gcm_sender_id, field: :GCMSenderId
property :app_identifier
property :app_name
property :app_version
property :app_build_number
property :badge, :integer
property :channels, :array
property :device_token
property :device_token_last_modified, :integer
property :device_type, enum: [:ios, :android, :osx, :tvos, :watchos, :web, :expo, :win, :other, :unknown, :unsupported]
property :installation_id
property :locale_identifier
property :parse_version
property :push_type
property :time_zone, :timezone

has_one :session, ->{ where(installation_id: i.installation_id) }, scope_only: true

end

Class-Level Permissions on _Installation

_Installation is special-cased inside Parse Server. Some operations are hardcoded at the REST layer and CANNOT be relaxed via CLP — calling Object.set_clp for them has no effect on the server's actual behavior, regardless of what you pass. Other operations work the way CLP normally does. The matrix:

Operation Behavior
find Master key only. Hardcoded. set_clp :find, ... is effectively ignored by the server.
delete Master key only. Hardcoded. set_clp :delete, ... is effectively ignored by the server.
create Open to anonymous clients (the X-Parse-Installation-Id header is the credential). Locking via CLP breaks first-launch device registration.
update Open to clients whose installationId matches the record; else master key. Locking via CLP breaks silent device-token refresh and channel subscribe/unsubscribe before login.
get CLP applies normally. Safe to tighten — SDKs don't usually GET their own installation from the server.
count CLP applies normally. Safe to tighten to master-only (the push flow doesn't need it).
addField CLP applies normally. Safe to tighten to master-only as a hardening default.

What you can safely do with set_clp on _Installation

  • set_clp :get, requires_authentication: true (or {} for master-only)
  • set_clp :count (master-only)
  • set_clp :addField (master-only)
  • Object.protect_fields to hide device_token, gcm_sender_id, push_type, etc. from non-master reads — these are write-only from the client's perspective in normal SDK flows.

What you should NOT do with set_clp on _Installation

  • set_clp :create, requires_authentication: true — breaks device registration for users who haven't logged in yet.
  • set_clp :update, requires_authentication: true — breaks background device-token refresh and pre-login channel subscribe/unsubscribe.
  • Pointer-based set_read_user_fields / set_write_user_fields — an installation has no stable owning user (a device can outlive a user session and change users), so user-pointer ACLing is unreliable here.
  • set_clp :find, public: true (or any other :find config) — has no effect; the server enforces master-only at the REST layer.

If your app actually does require login before any installation write, put that policy in a beforeSave('_Installation') Cloud Code trigger rather than in CLP — the trigger fires under master-key context and can inspect request.user directly without breaking the SDK's anonymous registration handshake.

See Also:

Constant Summary

Constants inherited from Object

Object::BUILTIN_PARSE_CLASS_NAMES, Object::IDENTIFICATION_FIELDS, Object::VALID_ACL_POLICIES

Constants included from Core::Schema

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

Constants included from Core::Describe

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

Constants included from Core::Indexing

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

Constants included from Core::SearchIndexing

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

Constants included from Core::Fetching

Core::Fetching::NON_SERIALIZABLE_IVARS

Constants included from Core::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::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

Attributes inherited from Object

#acl, #created_at, #id, #updated_at

Attributes inherited from Pointer

#id, #parse_class

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Object

#[], #[]=, #__type, #_resolve_acl_owner_id, #_resolve_default_acl, #acl_changed?, acl_owner_field, acl_policy, acl_policy_setting, #acl_was, #acl_will_change!, #after_create, #after_destroy, #after_save, #after_update, #after_validation, #apply_defaults!, #around_create, #around_destroy, #around_save, #around_update, #around_validation, #as_json, #autofetch_disabled?, #before_create, #before_destroy, #before_save, #before_update, #before_validation, build, #changed, #changed?, class_permissions, #clear_attribute_change!, #clear_changes!, #clear_partial_fetch_state!, default_acls, describe_access, #disable_autofetch!, #enable_autofetch!, #existed?, fetch_clp, #fetched?, #fetched_keys, #fetched_keys=, #field_was_fetched?, #filter_for_user, filter_results_for_user, #fully_fetched?, #has?, #has_selective_keys?, #initialize, #keys, master_only_class!, #nested_fetched_keys, #nested_fetched_keys=, #nested_keys_for, #new?, #parse_class, #partially_fetched?, #persisted?, pointer, #pretty, private_acl!, #reload!, roles_for_user, #rollback!, #run_after_create_callbacks, #run_after_delete_callbacks, #run_after_save_callbacks, #schema, #search_highlights, #search_score, set_default_acl, set_default_clp, #twin, unlistable_class!, update_clp!, #updates, #valid?, #validate!, #vector_score, wait_for, watch, webhook, webhook_function

Methods included from Core::Querying

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

Methods included from Core::Schema

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

Methods included from Core::Describe

#describe

Methods included from Core::Indexing

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

Methods included from Core::SearchIndexing

#apply_search_indexes!, #mongo_search_index, #mongo_search_index_declarations, #search_indexes_plan

Methods included from 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

#==, #[], #[]=, #__type, #attributes, #className, #fetch, #fetch_cache!, #fetch_json, #fetch_object, #fetched?, #hash, #initialize, #json_hash, #method_missing, #pointer, #pointer?, #present?, #respond_to_missing?, #search_highlights, #search_score, #sig, #vector_score

Methods inherited from Model

#dirty?, find_class, same_parse_class?

Methods included from Client::Connectable

#client

Constructor Details

This class inherits a constructor from Parse::Object

Dynamic Method Handling

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

Instance Attribute Details

#app_build_numberString

The build number of the client application to which this installation belongs.

Returns:



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

property :app_build_number

#app_identifierString

A unique identifier for this installation’s client application. In iOS, this is the Bundle Identifier.

Returns:



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

property :app_identifier

#app_nameString

The display name of the client application to which this installation belongs.

Returns:



167
# File 'lib/parse/model/classes/installation.rb', line 167

property :app_name

#app_versionString

The version string of the client application to which this installation belongs.

Returns:



172
# File 'lib/parse/model/classes/installation.rb', line 172

property :app_version

#badgeInteger

A number field representing the last known application badge for iOS installations.

Returns:

  • (Integer)


182
# File 'lib/parse/model/classes/installation.rb', line 182

property :badge, :integer

#channelsArray

An array of the channels to which a device is currently subscribed. Note that channelUris (the Microsoft-generated push URIs for Windows devices) is not supported at this time.

Returns:



189
# File 'lib/parse/model/classes/installation.rb', line 189

property :channels, :array

#device_tokenString

The Apple or Google generated token used to deliver messages to the APNs or GCM push networks respectively.

Returns:



195
# File 'lib/parse/model/classes/installation.rb', line 195

property :device_token

#device_token_last_modifiedInteger

Returns number of seconds since token modified.

Returns:

  • (Integer)

    number of seconds since token modified



199
# File 'lib/parse/model/classes/installation.rb', line 199

property :device_token_last_modified, :integer

#device_typeString

The type of device: "ios", "android", "osx", "tvos", "watchos", "web", "expo", "win", "other", "unknown", or "unsupported". This property is implemented as a Parse::Stack enumeration.

Returns:



206
# File 'lib/parse/model/classes/installation.rb', line 206

property :device_type, enum: [:ios, :android, :osx, :tvos, :watchos, :web, :expo, :win, :other, :unknown, :unsupported]

#gcm_sender_idString

This field only has meaning for Android installations that use the GCM push type. It is reserved for directing Parse to send pushes to this installation with an alternate GCM sender ID. This field should generally not be set unless you are uploading installation data from another push provider. If you set this field, then you must set the GCM API key corresponding to this GCM sender ID in your Parse application’s push settings.

Returns:



157
# File 'lib/parse/model/classes/installation.rb', line 157

property :gcm_sender_id, field: :GCMSenderId

#installation_idString

Universally Unique Identifier (UUID) for the device used by Parse. It must be unique across all of an app’s installations. (readonly).

Returns:



212
# File 'lib/parse/model/classes/installation.rb', line 212

property :installation_id

#locale_identifierString

The locale for this device.

Returns:



217
# File 'lib/parse/model/classes/installation.rb', line 217

property :locale_identifier

#parse_versionString

The version of the Parse SDK which this installation uses.

Returns:



222
# File 'lib/parse/model/classes/installation.rb', line 222

property :parse_version

#push_typeString

This field is reserved for directing Parse to the push delivery network to be used. If the device is registered to receive pushes via GCM, this field will be marked “gcm”. If this device is not using GCM, and is using Parse’s push notification service, it will be blank (readonly).

Returns:



230
# File 'lib/parse/model/classes/installation.rb', line 230

property :push_type

#sessionParse::Session

Returns the corresponding Session associated with this installation, if any exists. This is implemented as a has_one association to the Session class using the #installation_id.

Returns:

Version:

  • 1.7.1



243
# File 'lib/parse/model/classes/installation.rb', line 243

has_one :session, -> { where(installation_id: i.installation_id) }, scope_only: true

#time_zoneParse::TimeZone

The current time zone where the target device is located. This should be an IANA time zone identifier or a TimeZone instance.

Returns:



236
# File 'lib/parse/model/classes/installation.rb', line 236

property :time_zone, :timezone

#userParse::User

The User associated with this installation. Parse Server populates this pointer when the installation is created or updated by an authenticated client (the session-token holder on the request). It is useful for targeted push delivery — finding all installations belonging to a given user.

Caveat — do not use for ACL or CLP scoping. Devices outlive sessions and can change users (account switch, sign-out, shared device), so the user pointer on _Installation is not a reliable owner identity. See the "What you should NOT do with set_clp" notes above for the broader context.

Returns:



258
# File 'lib/parse/model/classes/installation.rb', line 258

belongs_to :user

Class Method Details

.all_channelsArray<String>

List all unique channel names across all installations.

Examples:

all_channels = Parse::Installation.all_channels
# => ["news", "sports", "weather"]

Returns:



270
271
272
# File 'lib/parse/model/classes/installation.rb', line 270

def all_channels
  distinct(:channels)
end

.by_device_type(type) ⇒ Parse::Query

Query scope for a specific device type.

Examples:

mac_devices = Parse::Installation.by_device_type(:osx).all

Parameters:

  • type (String, Symbol)

    the device type (ios, android, osx, tvos, watchos, web, expo, win, other, unknown, unsupported)

Returns:



308
309
310
# File 'lib/parse/model/classes/installation.rb', line 308

def by_device_type(type)
  query(device_type: type.to_s)
end

.cleanup_stale_tokens!(days: 90) ⇒ Integer

Delete all installations with stale tokens. Use with caution - this permanently removes installation records.

Examples:

# Clean up installations not updated in 180 days
deleted = Parse::Installation.cleanup_stale_tokens!(days: 180)

Parameters:

  • days (Integer) (defaults to: 90)

    number of days since last update (default: 90)

Returns:

  • (Integer)

    the number of installations deleted



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

def cleanup_stale_tokens!(days: 90)
  installations = stale_tokens(days: days).all
  installations.each(&:destroy)
  installations.count
end

.protect_fields(pattern, fields) ⇒ Object

protect_fields on _Installation is a documented-legitimate use (e.g. hiding device_token / gcm_sender_id / push_type from non-master reads), so we deliberately do NOT fire the find/delete-are-hardcoded advisory here. The advisory exists to nudge callers away from CLP changes that the server ignores; protectedFields is one of the four operations on _Installation that CLP actually controls.



114
115
116
# File 'lib/parse/model/classes/installation.rb', line 114

def protect_fields(pattern, fields)
  super
end

.reset_all_badges(type = :ios) ⇒ Integer

Reset badge count for all installations of a specific device type.

Examples:

Parse::Installation.reset_all_badges
Parse::Installation.reset_all_badges(:android)

Parameters:

  • type (String, Symbol) (defaults to: :ios)

    the device type (default: :ios since badges are primarily iOS)

Returns:

  • (Integer)

    the number of installations updated



336
337
338
339
340
341
342
343
# File 'lib/parse/model/classes/installation.rb', line 336

def reset_all_badges(type = :ios)
  installations = by_device_type(type).where(:badge.gt => 0).all
  installations.each do |installation|
    installation.badge = 0
    installation.save
  end
  installations.count
end

.reset_badges_for_channel(channel) ⇒ Integer

Reset badge count for all installations in a channel.

Examples:

Parse::Installation.reset_badges_for_channel("news")

Parameters:

  • channel (String)

    the channel name

Returns:

  • (Integer)

    the number of installations updated



321
322
323
324
325
326
327
328
# File 'lib/parse/model/classes/installation.rb', line 321

def reset_badges_for_channel(channel)
  installations = subscribers(channel).where(:badge.gt => 0).all
  installations.each do |installation|
    installation.badge = 0
    installation.save
  end
  installations.count
end

.set_class_access(**ops_to_access) ⇒ Object

Same advisory for the bulk-config DSL.



102
103
104
105
# File 'lib/parse/model/classes/installation.rb', line 102

def set_class_access(**ops_to_access)
  _warn_about_installation_clp!(:set_class_access, ops_to_access.keys)
  super
end

.set_clp(operation, **opts) ⇒ Object

Override Object.set_clp on _Installation so that any attempt to change CLP from the SDK emits a one-time advisory. Parse Server hardcodes find and delete on _Installation to master-key-only at the REST layer, and gates create/update on the X-Parse-Installation-Id header rather than CLP — so most CLP changes here either do nothing or break the SDK's device-registration flow. Behavior is otherwise unchanged.



96
97
98
99
# File 'lib/parse/model/classes/installation.rb', line 96

def set_clp(operation, **opts)
  _warn_about_installation_clp!(:set_clp, operation)
  super
end

.set_read_user_fields(*fields) ⇒ Object

Pointer-permission helpers on _Installation are a mistake in practice (devices have no stable owning user); warn loudly.



120
121
122
123
# File 'lib/parse/model/classes/installation.rb', line 120

def set_read_user_fields(*fields)
  _warn_about_installation_clp!(:set_read_user_fields, fields)
  super
end

.set_write_user_fields(*fields) ⇒ Object



125
126
127
128
# File 'lib/parse/model/classes/installation.rb', line 125

def set_write_user_fields(*fields)
  _warn_about_installation_clp!(:set_write_user_fields, fields)
  super
end

.stale_count(days: 90) ⇒ Integer

Count installations with stale tokens.

Examples:

count = Parse::Installation.stale_count(days: 60)

Parameters:

  • days (Integer) (defaults to: 90)

    number of days since last update (default: 90)

Returns:

  • (Integer)

    count of stale installations



369
370
371
# File 'lib/parse/model/classes/installation.rb', line 369

def stale_count(days: 90)
  stale_tokens(days: days).count
end

.stale_tokens(days: 90) ⇒ Parse::Query

Query for installations with stale (old) device tokens. Useful for cleaning up installations that are likely no longer active.

Examples:

# Find installations not updated in 90 days
stale = Parse::Installation.stale_tokens.all

# Find installations not updated in 30 days
stale = Parse::Installation.stale_tokens(days: 30).all

Parameters:

  • days (Integer) (defaults to: 90)

    number of days since last token modification (default: 90)

Returns:

  • (Parse::Query)

    a query for installations with old tokens



359
360
361
362
# File 'lib/parse/model/classes/installation.rb', line 359

def stale_tokens(days: 90)
  cutoff = Time.now - (days * 24 * 60 * 60)
  query(:updated_at.lt => cutoff)
end

.subscribers(channel) ⇒ Parse::Query

Get a query for installations subscribed to a specific channel.

Examples:

# Get all iOS subscribers to the "news" channel
installations = Parse::Installation.subscribers("news")
  .where(device_type: "ios")
  .all

Parameters:

  • channel (String)

    the channel name to find subscribers for

Returns:

  • (Parse::Query)

    a query scoped to the channel's subscribers



292
293
294
# File 'lib/parse/model/classes/installation.rb', line 292

def subscribers(channel)
  query(:channels.in => [channel])
end

.subscribers_count(channel) ⇒ Integer

Count the number of installations subscribed to a specific channel.

Examples:

count = Parse::Installation.subscribers_count("news")
# => 1250

Parameters:

  • channel (String)

    the channel name to count subscribers for

Returns:

  • (Integer)

    the number of subscribers



280
281
282
# File 'lib/parse/model/classes/installation.rb', line 280

def subscribers_count(channel)
  query(:channels.in => [channel]).count
end

Instance Method Details

#days_since_updateInteger?

Get the number of days since this installation was last updated.

Examples:

puts "Last active #{installation.days_since_update} days ago"

Returns:

  • (Integer, nil)

    days since last update, or nil if no updated_at



473
474
475
476
# File 'lib/parse/model/classes/installation.rb', line 473

def days_since_update
  return nil if updated_at.nil?
  ((Time.now - updated_at.to_time) / (24 * 60 * 60)).to_i
end

#increment_badge!(amount = 1) ⇒ Boolean

Increment the badge count and save.

Examples:

installation.increment_badge!
installation.increment_badge!(5)

Parameters:

  • amount (Integer) (defaults to: 1)

    amount to increment by (default: 1)

Returns:

  • (Boolean)

    true if save was successful



447
448
449
450
# File 'lib/parse/model/classes/installation.rb', line 447

def increment_badge!(amount = 1)
  self.badge = (badge || 0) + amount
  save
end

#reset_badge!Boolean

Reset the badge count to 0 and save.

Examples:

installation.reset_badge!

Returns:

  • (Boolean)

    true if save was successful



436
437
438
439
# File 'lib/parse/model/classes/installation.rb', line 436

def reset_badge!
  self.badge = 0
  save
end

#stale?(days: 90) ⇒ Boolean

Check if this installation's token is considered stale.

Examples:

if installation.stale?
  puts "This installation may no longer be active"
end

Parameters:

  • days (Integer) (defaults to: 90)

    number of days to consider stale (default: 90)

Returns:

  • (Boolean)

    true if the installation hasn't been updated in the given days



463
464
465
466
467
# File 'lib/parse/model/classes/installation.rb', line 463

def stale?(days: 90)
  return false if updated_at.nil?
  cutoff = Time.now - (days * 24 * 60 * 60)
  updated_at < cutoff
end

#subscribe(*channel_names) ⇒ Boolean

Subscribe this installation to one or more channels. The changes are automatically saved to the server.

Examples:

installation.subscribe("news", "weather")
installation.subscribe(["sports", "updates"])

Parameters:

  • channel_names (Array<String>)

    the channel names to subscribe to

Returns:

  • (Boolean)

    true if the save was successful



398
399
400
401
402
# File 'lib/parse/model/classes/installation.rb', line 398

def subscribe(*channel_names)
  self.channels ||= []
  self.channels = (self.channels + channel_names.flatten.map(&:to_s)).uniq
  save
end

#subscribed_to?(channel) ⇒ Boolean

Check if this installation is subscribed to a specific channel.

Examples:

if installation.subscribed_to?("news")
  puts "Subscribed to news!"
end

Parameters:

  • channel (String)

    the channel name to check

Returns:

  • (Boolean)

    true if subscribed to the channel



424
425
426
# File 'lib/parse/model/classes/installation.rb', line 424

def subscribed_to?(channel)
  channels&.include?(channel.to_s) || false
end

#unsubscribe(*channel_names) ⇒ Boolean

Unsubscribe this installation from one or more channels. The changes are automatically saved to the server.

Examples:

installation.unsubscribe("news")
installation.unsubscribe("sports", "weather")

Parameters:

  • channel_names (Array<String>)

    the channel names to unsubscribe from

Returns:

  • (Boolean)

    true if the save was successful, or true if no channels were set



411
412
413
414
415
# File 'lib/parse/model/classes/installation.rb', line 411

def unsubscribe(*channel_names)
  return true unless channels.present?
  self.channels = channels - channel_names.flatten.map(&:to_s)
  save
end