require 'puppet_forge/connection' require 'puppet_forge/v3/base/paginated_collection' require 'puppet_forge/error' require 'puppet_forge/lazy_accessors' require 'puppet_forge/lazy_relations' require 'puppet_forge/lru_cache' module PuppetForge module V3 # Acts as the base class for all PuppetForge::V3::* models. # # @api private class Base include PuppetForge::LazyAccessors include PuppetForge::LazyRelations def initialize(json_response) @attributes = json_response orm_resp_item json_response end def orm_resp_item(json_response) json_response.each do |key, value| unless respond_to? key define_singleton_method("#{key}") { @attributes[key] } define_singleton_method("#{key}=") { |val| @attributes[key] = val } end end end # @return true if attribute exists, false otherwise # def has_attribute?(attr) @attributes.has_key?(:"#{attr}") end def attribute(name) @attributes[:"#{name}"] end def attributes @attributes end class << self include PuppetForge::Connection API_VERSION = "v3" def api_version API_VERSION end # @private def lru_cache @lru_cache ||= PuppetForge::LruCache.new end # @private def lru_cache_key(*args) PuppetForge::LruCache.new_key(*args) end # @private def request(resource, item = nil, params = {}, reset_connection = false, conn_opts = {}) cache_key = lru_cache_key(resource, item, params) cached = lru_cache.get(cache_key) return cached unless cached.nil? conn(reset_connection, conn_opts) if reset_connection unless conn.url_prefix.to_s =~ /^#{PuppetForge.host}/ conn.url_prefix = "#{PuppetForge.host}" end if item.nil? uri_path = "v3/#{resource}" else uri_path = "v3/#{resource}/#{item}" end # The API expects a space separated string. This allows the user to invoke it with a more natural feeling array. params['endorsements'] = params['endorsements'].join(' ') if params['endorsements'].is_a? Array result = PuppetForge::V3::Base.conn.get uri_path, params lru_cache.put(cache_key, result) result end # @private def find_request(slug, reset_connection = false, conn_opts = {}) return nil if slug.nil? resp = request("#{self.name.split("::").last.downcase}s", slug, {}, reset_connection, conn_opts) self.new(resp.body) end def find(slug) find_request(slug) end def find_stateless(slug, conn_opts = {}) find_request(slug, true, conn_opts) end # @private def where_request(params, reset_connection = false, conn_opts = {}) resp = request("#{self.name.split("::").last.downcase}s", nil, params, reset_connection, conn_opts) new_collection(resp) end def where(params) where_request(params) end def where_stateless(params, conn_opts = {}) where_request(params, true, conn_opts) end # Return a paginated collection of all modules def all(params = {}) where(params) end # Return a paginated collection of all modules def all_stateless(params = {}, conn_opts = {}) where_stateless(params, conn_opts) end # @private def get_collection_request(uri_path, reset_connection = false, conn_opts = {}) resource, params = split_uri_path uri_path resp = request(resource, nil, params, reset_connection, conn_opts) new_collection(resp) end def get_collection(uri_path) get_collection_request(uri_path) end def get_collection_stateless(uri_path, conn_opts) get_collection_request(uri_path, true, conn_opts) end # Faraday's Util#escape method will replace a '+' with '%2B' to prevent it being # interpreted as a space. For compatibility with the Forge API, we would like a '+' # to be interpreted as a space so they are changed to spaces here. def convert_plus_to_space(str) str.gsub(/[+]/, ' ') end # @private def split_uri_path(uri_path) all, resource, params = /(?:\/v3\/)([^\/]+)(?:\?)(.*)/.match(uri_path).to_a params = convert_plus_to_space(params).split('&') param_hash = Hash.new params.each do |param| key, val = param.split('=') param_hash[key] = val end [resource, param_hash] end # @private def new_collection(faraday_resp) if faraday_resp[:errors].nil? PaginatedCollection.new(self, faraday_resp.body[:results], faraday_resp.body[:pagination], nil) else PaginatedCollection.new(self) end end end end end end