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
@return [ZendeskAPI::Association] The class association
@return [ZendeskAPI::ClientError] The last response error
@return [Hash] query options
@return [Faraday::Response] The last response
Public Class Methods
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
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
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
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
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
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
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
@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
@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
# 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
# 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
# File lib/zendesk_api/collection.rb, line 171 def fetch(*args) fetch!(*args) rescue Faraday::ClientError => e @error = e [] end
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
# 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
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
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
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
The API path to this collection
# File lib/zendesk_api/collection.rb, line 155 def path @association.generate_path(:with_parent => true) end
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
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
# File lib/zendesk_api/collection.rb, line 269 def respond_to_missing?(name, include_all) [].respond_to?(name, include_all) end
Saves all newly created resources stored in this collection. @return [Collection] self
# File lib/zendesk_api/collection.rb, line 121 def save _save end
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
Alias for fetch(false)
# File lib/zendesk_api/collection.rb, line 180 def to_a fetch end
Alias for fetch!(false)
# File lib/zendesk_api/collection.rb, line 185 def to_a! fetch! end
@private
# File lib/zendesk_api/collection.rb, line 265 def to_ary nil end
# File lib/zendesk_api/collection.rb, line 298 def to_param map(&:to_param) end
@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
Private Instance Methods
# 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
# 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
Method missing
# File lib/zendesk_api/collection.rb, line 460 def array_method(name, *args, &block) to_a.public_send(name, *args, &block) end
# 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
# 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
# File lib/zendesk_api/collection.rb, line 474 def collection_method(name, *args, &block) @resource_class.send(name, @client, *args, &block) end
# 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
# 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
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
# 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
# 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
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
# File lib/zendesk_api/collection.rb, line 478 def resource_methods @resource_methods ||= @resource_class.singleton_methods(false).map(&:to_sym) end
# 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
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
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