module ActiveModel::Datastore::ClassMethods

Methods defined here will be class methods when 'include ActiveModel::Datastore'.

Public Instance Methods

all(options = {}) click to toggle source

Queries entities from Cloud Datastore by named kind and using the provided options. When a limit option is provided queries up to the limit and returns results with a cursor.

This method may make several API calls until all query results are retrieved. The `run` method returns a QueryResults object, which is a special case Array with additional values. QueryResults are returned in batches, and the batch size is determined by the Datastore API. Batch size is not guaranteed. It will be affected by the size of the data being returned, and by other forces such as how distributed and/or consistent the data in Datastore is. Calling `all` on the QueryResults retrieves all results by repeatedly loading next until next? returns false. The `all` method returns an enumerator which from_entities iterates on.

Be sure to use as narrow a search criteria as possible. Please use with caution.

@param [Hash] options The options to construct the query with.

@option options [Google::Cloud::Datastore::Key] :ancestor Filter for inherited results. @option options [String] :cursor Sets the cursor to start the results at. @option options [Integer] :limit Sets a limit to the number of results to be returned. @option options [String] :order Sort the results by property name. @option options [String] :desc_order Sort the results by descending property name. @option options [Array] :select Retrieve only select properties from the matched entities. @option options [Array] :distinct_on Group results by a list of properties. @option options [Array] :where Adds a property filter of arrays in the format

[name, operator, value].

@return [Array<Model>, String] An array of ActiveModel results

or if options was provided:

@return [Array<Model>, String] An array of ActiveModel results and a cursor that

can be used to query for additional results.
# File lib/active_model/datastore.rb, line 290
def all(options = {})
  next_cursor = nil
  query = build_query(options)
  query_results = retry_on_exception { CloudDatastore.dataset.run query }
  if options[:limit]
    next_cursor = query_results.cursor if query_results.size == options[:limit]
    return from_entities(query_results.all), next_cursor
  end
  from_entities(query_results.all)
end
build_model(entity) click to toggle source
# File lib/active_model/datastore.rb, line 497
def build_model(entity)
  model_entity = new
  model_entity.id = entity.key.id unless entity.key.id.nil?
  model_entity.id = entity.key.name unless entity.key.name.nil?
  model_entity.parent_key_id = entity.key.parent.id if entity.key.parent.present?
  model_entity
end
build_query(options = {}) click to toggle source

Constructs a Google::Cloud::Datastore::Query.

@param [Hash] options The options to construct the query with.

@option options [Google::Cloud::Datastore::Key] :ancestor Filter for inherited results. @option options [String] :cursor Sets the cursor to start the results at. @option options [Integer] :limit Sets a limit to the number of results to be returned. @option options [String] :order Sort the results by property name. @option options [String] :desc_order Sort the results by descending property name. @option options [Array] :select Retrieve only select properties from the matched entities. @option options [Array] :distinct_on Group results by a list of properties. @option options [Array] :where Adds a property filter of arrays in the format

[name, operator, value].

@return [Query] A datastore query.

# File lib/active_model/datastore.rb, line 398
def build_query(options = {})
  query = CloudDatastore.dataset.query name
  query_options(query, options)
end
find(*ids, parent: nil) click to toggle source

Find entity by id - this can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]). The parent key is optional.

@param [Integer] ids One or more ids to retrieve. @param [Google::Cloud::Datastore::Key] parent The parent key of the entity.

@return [Model, nil] An ActiveModel object or nil for a single id. @return [Array<Model>] An array of ActiveModel objects for more than one id.

# File lib/active_model/datastore.rb, line 311
def find(*ids, parent: nil)
  expects_array = ids.first.is_a?(Array)
  ids = ids.flatten.compact.uniq.map(&:to_i)

  case ids.size
  when 0
    raise EntityError, "Couldn't find #{name} without an ID"
  when 1
    entity = find_entity(ids.first, parent)
    model_entity = from_entity(entity)
    expects_array ? [model_entity].compact : model_entity
  else
    lookup_results = find_all_entities(ids, parent)
    from_entities(lookup_results.all)
  end
end
find_all_entities(ids_or_names, parent) click to toggle source

Finds entities by keys using the provided array items. Results provided by the dataset `find_all` is a Dataset::LookupResults object.

@param [Array<Integer>, Array<String>] ids_or_names An array of ids or names.

# File lib/active_model/datastore.rb, line 491
def find_all_entities(ids_or_names, parent)
  keys = ids_or_names.map { |id| CloudDatastore.dataset.key name, id }
  keys.map { |key| key.parent = parent } if parent.present?
  retry_on_exception { CloudDatastore.dataset.find_all keys }
end
find_by(args) click to toggle source

Finds the first entity matching the specified condition.

@param [Hash] args In which the key is the property and the value is the value to look for. @option args [Google::Cloud::Datastore::Key] :ancestor filter for inherited results

@return [Model, nil] An ActiveModel object or nil.

@example

User.find_by(name: 'Joe')
User.find_by(name: 'Bryce', ancestor: parent_key)
# File lib/active_model/datastore.rb, line 340
def find_by(args)
  query = CloudDatastore.dataset.query name
  query.ancestor(args[:ancestor]) if args[:ancestor]
  query.limit(1)
  query.where(args.keys[0].to_s, '=', args.values[0])
  query_results = retry_on_exception { CloudDatastore.dataset.run query }
  from_entity(query_results.first)
end
find_entities(*ids_or_names, parent: nil) click to toggle source

Retrieves the entities for the provided ids by key and by an optional parent. The find_all method returns LookupResults, which is a special case Array with additional values. LookupResults are returned in batches, and the batch size is determined by the Datastore API. Batch size is not guaranteed. It will be affected by the size of the data being returned, and by other forces such as how distributed and/or consistent the data in Datastore is. Calling `all` on the LookupResults retrieves all results by repeatedly loading next until next? returns false. The `all` method returns an enumerator unless passed a block. We iterate on the enumerator to return the model entity objects.

@param [Integer, String] ids_or_names One or more ids to retrieve. @param [Google::Cloud::Datastore::Key] parent The parent Key of the entity.

@return [Array<Entity>] an array of Google::Cloud::Datastore::Entity objects.

# File lib/active_model/datastore.rb, line 251
def find_entities(*ids_or_names, parent: nil)
  ids_or_names = ids_or_names.flatten.compact.uniq
  lookup_results = find_all_entities(ids_or_names, parent)
  lookup_results.all.collect { |x| x }
end
find_entity(id_or_name, parent = nil) click to toggle source

Retrieves an entity by id or name and by an optional parent.

@param [Integer or String] id_or_name The id or name value of the entity Key. @param [Google::Cloud::Datastore::Key] parent The parent Key of the entity.

@return [Entity, nil] a Google::Cloud::Datastore::Entity object or nil.

# File lib/active_model/datastore.rb, line 229
def find_entity(id_or_name, parent = nil)
  key = CloudDatastore.dataset.key name, id_or_name
  key.parent = parent if parent.present?
  retry_on_exception { CloudDatastore.dataset.find key }
end
from_entities(entities) click to toggle source

Translates an Enumerator of Datastore::Entity objects to ActiveModel::Model objects.

Results provided by the dataset `find_all` or `run query` will be a Dataset::LookupResults or Dataset::QueryResults object. Invoking `all` on those objects returns an enumerator.

@param [Enumerator] entities An enumerator representing the datastore entities.

# File lib/active_model/datastore.rb, line 357
def from_entities(entities)
  raise ArgumentError, 'Entities param must be an Enumerator' unless entities.is_a? Enumerator

  entities.map { |entity| from_entity(entity) }
end
from_entity(entity) click to toggle source

Translates between Datastore::Entity objects and ActiveModel::Model objects.

@param [Entity] entity Entity from Cloud Datastore. @return [Model] The translated ActiveModel object.

# File lib/active_model/datastore.rb, line 369
def from_entity(entity)
  return if entity.nil?

  model_entity = build_model(entity)
  model_entity.entity_property_values = entity.properties.to_h
  entity.properties.to_h.each do |name, value|
    model_entity.send "#{name}=", value if model_entity.respond_to? "#{name}="
  end
  model_entity.reload!
  model_entity
end
log_google_cloud_error() { || ... } click to toggle source
# File lib/active_model/datastore.rb, line 437
def log_google_cloud_error
  yield
rescue Google::Cloud::Error => e
  puts "\e[33m[#{e.message.inspect}]\e[0m"
  raise e
end
parent_key(parent_id) click to toggle source

A default parent key for specifying an ancestor path and creating an entity group.

# File lib/active_model/datastore.rb, line 217
def parent_key(parent_id)
  CloudDatastore.dataset.key('Parent' + name, parent_id.to_i)
end
query_options(query, options) click to toggle source

**************** private ****************

# File lib/active_model/datastore.rb, line 446
def query_options(query, options)
  query.ancestor(options[:ancestor]) if options[:ancestor]
  query.cursor(options[:cursor]) if options[:cursor]
  query.limit(options[:limit]) if options[:limit]
  query_sort(query, options)
  query.select(*options[:select]) if options[:select]
  query.distinct_on(*options[:distinct_on]) if options[:distinct_on]
  query_property_filter(query, options)
end
query_property_filter(query, options) click to toggle source

Adds property filters to the query if included in the options. Accepts individual or nested Arrays:

[['superseded', '=', false], ['email', '=', 'something']]
# File lib/active_model/datastore.rb, line 470
def query_property_filter(query, options)
  if options[:where]
    opts = options[:where]
    if opts[0].is_a?(Array)
      opts.each do |opt|
        query.where(opt[0], opt[1], opt[2]) unless opt.nil?
      end
    else
      query.where(opts[0], opts[1], opts[2])
    end
  end
  query
end
query_sort(query, options) click to toggle source

Adds sorting to the results by a property name if included in the options.

# File lib/active_model/datastore.rb, line 459
def query_sort(query, options)
  query.order(options[:order]) if options[:order]
  query.order(options[:desc_order], :desc) if options[:desc_order]
  query
end
retry_on_exception(max_retry_count = 5) { || ... } click to toggle source
# File lib/active_model/datastore.rb, line 420
def retry_on_exception(max_retry_count = 5)
  retries = 0
  sleep_time = 0.25
  begin
    yield
  rescue Google::Cloud::Error => e
    raise e if retries >= max_retry_count

    puts "\e[33mRescued exception #{e.message.inspect}, retrying in #{sleep_time}\e[0m"
    # 0.25, 0.5, 1, 2, and 4 second between retries.
    sleep sleep_time
    retries += 1
    sleep_time *= 2
    retry
  end
end
retry_on_exception?(max_retry_count = 5) { || ... } click to toggle source
# File lib/active_model/datastore.rb, line 403
def retry_on_exception?(max_retry_count = 5)
  retries = 0
  sleep_time = 0.25
  begin
    yield
  rescue Google::Cloud::Error => e
    return false if retries >= max_retry_count

    puts "\e[33mRescued exception #{e.message.inspect}, retrying in #{sleep_time}\e[0m"
    # 0.25, 0.5, 1, 2, and 4 second between retries.
    sleep sleep_time
    retries += 1
    sleep_time *= 2
    retry
  end
end