class ZendeskAPI::Collection

Represents a collection of resources. Lazily loaded, resources aren’t actually fetched until explicitly needed (e.g. each, {#fetch}).

Constants

SPECIALLY_JOINED_PARAMS

Options passed in that are automatically converted from an array to a comma-separated list.

Attributes

association[R]

@return [ZendeskAPI::Association] The class association

error[R]

@return [ZendeskAPI::ClientError] The last response error

options[R]

@return [Hash] query options

response[R]

@return [Faraday::Response] The last response

Public Class Methods

new(client, resource, options = {}) click to toggle source

Creates a new Collection instance. Does not fetch resources. Additional options are: verb (default: GET), path (default: resource param), page, per_page. @param [Client] client The {Client} to use. @param [String] resource The resource being collected. @param [Hash] options Any additional options to be passed in.

# File lib/zendesk_api/collection.rb, line 33
def initialize(client, resource, options = {})
  @client, @resource_class, @resource = client, resource, resource.resource_path
  @options = SilentMash.new(options)

  set_association_from_options
  join_special_params

  @verb = @options.delete(:verb)
  @includes = Array(@options.delete(:include))

  # Used for Attachments, TicketComment
  if @resource_class.is_a?(Class) && @resource_class.superclass == ZendeskAPI::Data
    @resources = []
    @fetchable = false
  else
    @fetchable = true
  end
end

Public Instance Methods

<<(item) click to toggle source

Adds an item to this collection @option item [ZendeskAPI::Data] the resource to add @raise [ArgumentError] if the resource doesn’t belong in this collection

# File lib/zendesk_api/collection.rb, line 140
def <<(item)
  fetch

  if item.is_a?(Resource)
    if item.is_a?(@resource_class)
      @resources << item
    else
      raise "this collection is for #{@resource_class}"
    end
  else
    @resources << wrap_resource(item, true)
  end
end
all(start_page = @options["page"], &block) click to toggle source

Calls each on every page with the passed in block @param [Block] block Passed to each

# File lib/zendesk_api/collection.rb, line 197
def all(start_page = @options["page"], &block)
  _all(start_page, &block)
end
all!(start_page = @options["page"], &block) click to toggle source

Calls each on every page with the passed in block @param [Block] block Passed to each

# File lib/zendesk_api/collection.rb, line 191
def all!(start_page = @options["page"], &block)
  _all(start_page, :bang, &block)
end
build(opts = {}) click to toggle source

Convenience method to build a new resource and add it to the collection. Fetches the collection as well. @param [Hash] opts Options or attributes to pass

# File lib/zendesk_api/collection.rb, line 89
def build(opts = {})
  wrap_resource(opts, true).tap do |res|
    self << res
  end
end
build!(opts = {}) click to toggle source

Convenience method to build a new resource and add it to the collection. Fetches the collection as well. @param [Hash] opts Options or attributes to pass

# File lib/zendesk_api/collection.rb, line 98
def build!(opts = {})
  wrap_resource(opts, true).tap do |res|
    fetch!

    # << does a fetch too
    self << res
  end
end
clear_cache() click to toggle source

Clears all cached resources and associated values.

# File lib/zendesk_api/collection.rb, line 256
def clear_cache
  @resources = nil
  @count = nil
  @next_page = nil
  @prev_page = nil
  @query = nil
end
count() click to toggle source

@return [Number] The total number of resources server-side (disregarding pagination).

# File lib/zendesk_api/collection.rb, line 108
def count
  fetch
  @count || -1
end
count!() click to toggle source

@return [Number] The total number of resources server-side (disregarding pagination).

# File lib/zendesk_api/collection.rb, line 114
def count!
  fetch!
  @count || -1
end
each_page(*args, &block) click to toggle source
# File lib/zendesk_api/collection.rb, line 206
def each_page(*args, &block)
  warn "ZendeskAPI::Collection#each_page is deprecated, please use ZendeskAPI::Collection#all"
  all(*args, &block)
end
each_page!(*args, &block) click to toggle source
# File lib/zendesk_api/collection.rb, line 201
def each_page!(*args, &block)
  warn "ZendeskAPI::Collection#each_page! is deprecated, please use ZendeskAPI::Collection#all!"
  all!(*args, &block)
end
fetch(*args) click to toggle source
# File lib/zendesk_api/collection.rb, line 171
def fetch(*args)
  fetch!(*args)
rescue Faraday::ClientError => e
  @error = e

  []
end
fetch!(reload = false) click to toggle source

Executes actual GET from API and loads resources into proper class. @param [Boolean] reload Whether to disregard cache

# File lib/zendesk_api/collection.rb, line 161
def fetch!(reload = false)
  if @resources && (!@fetchable || !reload)
    return @resources
  elsif association && association.options.parent && association.options.parent.new_record?
    return (@resources = [])
  end

  get_resources(@query || path)
end
get_next_page_data(original_response_body) click to toggle source
# File lib/zendesk_api/collection.rb, line 302
def get_next_page_data(original_response_body)
  link = original_response_body["links"]["next"]
  result_key = @resource_class.model_key || "results"
  while link
    response = @client.connection.send("get", link).body

    original_response_body[result_key] = original_response_body[result_key] + response[result_key]

    link = response["meta"]["has_more"] ? response["links"]["next"] : nil
  end

  original_response_body
end
include(*sideloads) click to toggle source

Adds an item (or items) to the list of side-loaded resources to request @option sideloads [Symbol or String] The item(s) to sideload

# File lib/zendesk_api/collection.rb, line 133
def include(*sideloads)
  tap { @includes.concat(sideloads.map(&:to_s)) }
end
method_missing(name, *args, &block) click to toggle source

Sends methods to underlying array of resources.

# File lib/zendesk_api/collection.rb, line 274
def method_missing(name, *args, &block)
  if resource_methods.include?(name)
    collection_method(name, *args, &block)
  elsif [].respond_to?(name, false)
    array_method(name, *args, &block)
  else
    next_collection(name, *args, &block)
  end
end
next() click to toggle source

Find the next page. Does one of three things:

  • If there is already a page number in the options hash, it increases it and invalidates the cache, returning the new page number.

  • If there is a next_page url cached, it executes a fetch on that url and returns the results.

  • Otherwise, returns an empty array.

# File lib/zendesk_api/collection.rb, line 223
def next
  if @options["page"] && !cbp_request?
    clear_cache
    @options["page"] = @options["page"].to_i + 1
  elsif (@query = @next_page)
    # Send _only_ url param "?page[after]=token" to get the next page
    @options.page&.delete("before")
    fetch(true)
  else
    clear_cache
    @resources = []
  end
end
path() click to toggle source

The API path to this collection

# File lib/zendesk_api/collection.rb, line 155
def path
  @association.generate_path(:with_parent => true)
end
prev() click to toggle source

Find the previous page. Does one of three things:

  • If there is already a page number in the options hash, it increases it and invalidates the cache, returning the new page number.

  • If there is a prev_page url cached, it executes a fetch on that url and returns the results.

  • Otherwise, returns an empty array.

# File lib/zendesk_api/collection.rb, line 241
def prev
  if !cbp_request? && @options["page"].to_i > 1
    clear_cache
    @options["page"] -= 1
  elsif (@query = @prev_page)
    # Send _only_ url param "?page[before]=token" to get the prev page
    @options.page&.delete("after")
    fetch(true)
  else
    clear_cache
    @resources = []
  end
end
replace(collection) click to toggle source

Replaces the current (loaded or not) resources with the passed in collection @option collection [Array] The collection to replace this one with @raise [ArgumentError] if any resources passed in don’t belong in this collection

# File lib/zendesk_api/collection.rb, line 214
def replace(collection)
  raise "this collection is for #{@resource_class}" if collection.any? { |r| !r.is_a?(@resource_class) }
  @resources = collection
end
respond_to_missing?(name, include_all) click to toggle source
# File lib/zendesk_api/collection.rb, line 269
def respond_to_missing?(name, include_all)
  [].respond_to?(name, include_all)
end
save() click to toggle source

Saves all newly created resources stored in this collection. @return [Collection] self

# File lib/zendesk_api/collection.rb, line 121
def save
  _save
end
save!() click to toggle source

Saves all newly created resources stored in this collection. @return [Collection] self

# File lib/zendesk_api/collection.rb, line 127
def save!
  _save(:save!)
end
to_a() click to toggle source

Alias for fetch(false)

# File lib/zendesk_api/collection.rb, line 180
def to_a
  fetch
end
to_a!() click to toggle source

Alias for fetch!(false)

# File lib/zendesk_api/collection.rb, line 185
def to_a!
  fetch!
end
to_ary() click to toggle source

@private

# File lib/zendesk_api/collection.rb, line 265
def to_ary
  nil
end
to_param() click to toggle source
# File lib/zendesk_api/collection.rb, line 298
def to_param
  map(&:to_param)
end
to_s() click to toggle source

@private

# File lib/zendesk_api/collection.rb, line 285
def to_s
  if @resources
    @resources.inspect
  else
    inspect = []
    inspect << "options=#{@options.inspect}" if @options.any?
    inspect << "path=#{path}"
    "#{Inflection.singular(@resource)} collection [#{inspect.join(',')}]"
  end
end
Also aliased as: to_str
to_str()
Alias for: to_s

Private Instance Methods

_all(start_page = @options["page"], bang = false, &block) click to toggle source
# File lib/zendesk_api/collection.rb, line 335
def _all(start_page = @options["page"], bang = false, &block)
  raise(ArgumentError, "must pass a block") unless block

  page(start_page)
  clear_cache

  while (bang ? fetch! : fetch)
    each do |resource|
      block.call(resource, @options["page"] || 1)
    end

    last_page? ? break : self.next
  end

  page(nil)
  clear_cache
end
_save(method = :save) click to toggle source
# File lib/zendesk_api/collection.rb, line 353
def _save(method = :save)
  return self unless @resources

  result = true

  @resources.map! do |item|
    if item.respond_to?(method) && !item.destroyed? && item.changed?
      result &&= item.send(method)
    end

    item
  end

  result
end
array_method(name, *args, &block) click to toggle source

Method missing

# File lib/zendesk_api/collection.rb, line 460
def array_method(name, *args, &block)
  to_a.public_send(name, *args, &block)
end
assert_results(results, body) click to toggle source
# File lib/zendesk_api/collection.rb, line 488
def assert_results(results, body)
  return if results
  raise ZendeskAPI::Error::ClientError, "Expected #{@resource_class.model_key} or 'results' in response keys: #{body.keys.inspect}"
end
assert_valid_response_body(response_body) click to toggle source
# File lib/zendesk_api/collection.rb, line 482
def assert_valid_response_body(response_body)
  unless response_body.is_a?(Hash)
    raise ZendeskAPI::Error::NetworkError, @response.env
  end
end
collection_method(name, *args, &block) click to toggle source
# File lib/zendesk_api/collection.rb, line 474
def collection_method(name, *args, &block)
  @resource_class.send(name, @client, *args, &block)
end
get_resources(path_query_link) click to toggle source
# File lib/zendesk_api/collection.rb, line 318
def get_resources(path_query_link)
  if intentional_obp_request?
    warn "Offset Based Pagination will be deprecated soon"
  elsif supports_cbp? && first_cbp_request?
    # only set cbp options if it's the first request, otherwise the options would be already in place
    set_cbp_options
  end
  @response = get_response(path_query_link)

  # Keep pre-existing behaviour for search/export
  if path_query_link == "search/export"
    handle_search_export_response(@response.body)
  else
    handle_response(@response.body)
  end
end
get_response(path) click to toggle source
# File lib/zendesk_api/collection.rb, line 387
def get_response(path)
  @error = nil
  @client.connection.send(@verb || "get", path) do |req|
    opts = @options.delete_if { |_, v| v.nil? }

    req.params.merge!(:include => @includes.join(",")) if @includes.any?

    if %w{put post}.include?(@verb.to_s)
      req.body = opts
    else
      req.params.merge!(opts)
    end
  end
end
handle_response(response_body) click to toggle source

For both CBP and OBP

# File lib/zendesk_api/collection.rb, line 419
def handle_response(response_body)
  assert_valid_response_body(response_body)

  body = response_body.dup
  results = body.delete(@resource_class.model_key) || body.delete("results")

  assert_results(results, body)

  @resources = results.map do |res|
    wrap_resource(res)
  end

  set_page_and_count(body)
  set_includes(@resources, @includes, body)

  @resources
end
handle_search_export_response(response_body) click to toggle source
# File lib/zendesk_api/collection.rb, line 402
def handle_search_export_response(response_body)
  assert_valid_response_body(response_body)

  # Note this doesn't happen in #handle_response
  response_body = get_next_page_data(response_body) if more_results?(response_body)

  body = response_body.dup
  results = body.delete(@resource_class.model_key) || body.delete("results")

  assert_results(results, body)

  @resources = results.map do |res|
    wrap_resource(res)
  end
end
join_special_params() click to toggle source
# File lib/zendesk_api/collection.rb, line 369
def join_special_params
  # some params use comma-joined strings instead of query-based arrays for multiple values
  @options.each do |k, v|
    if SPECIALLY_JOINED_PARAMS.include?(k.to_sym) && v.is_a?(Array)
      @options[k] = v.join(',')
    end
  end
end
next_collection(name, *args, &block) click to toggle source

If you call client.tickets.foo - and foo is not an attribute nor an association, it ends up here, as a new collection

# File lib/zendesk_api/collection.rb, line 465
def next_collection(name, *args, &block)
  opts = args.last.is_a?(Hash) ? args.last : {}
  opts.merge!(collection_path: [*@collection_path, name], page: nil)
  # Why `page: nil`?
  # when you do client.tickets.fetch followed by client.tickets.foos => the request to /tickets/foos will
  # have the options page set to whatever the last options were for the tickets collection
  self.class.new(@client, @resource_class, @options.merge(opts))
end
resource_methods() click to toggle source
# File lib/zendesk_api/collection.rb, line 478
def resource_methods
  @resource_methods ||= @resource_class.singleton_methods(false).map(&:to_sym)
end
set_association_from_options() click to toggle source
# File lib/zendesk_api/collection.rb, line 378
def set_association_from_options
  @collection_path = @options.delete(:collection_path)

  association_options = { :path => @options.delete(:path) }
  association_options[:path] ||= @collection_path.join("/") if @collection_path
  @association = @options.delete(:association) || Association.new(association_options.merge(:class => @resource_class))
  @collection_path ||= [@resource]
end
with_association?() click to toggle source

Two special cases, and all namespaced classes

# File lib/zendesk_api/collection.rb, line 453
def with_association?
  [Tag, Setting].include?(@resource_class) ||
    @resource_class.to_s.split("::").size > 2
end
wrap_resource(res, with_association = with_association?) click to toggle source

Simplified Associations#wrap_resource

# File lib/zendesk_api/collection.rb, line 438
def wrap_resource(res, with_association = with_association?)
  case res
  when Array
    wrap_resource(Hash[*res], with_association)
  when Hash
    res = res.merge(:association => @association) if with_association
    @resource_class.new(@client, res)
  else
    res = { :id => res }
    res.merge!(:association => @association) if with_association
    @resource_class.new(@client, res)
  end
end