From eefaf706d3d4e3b7cdd54e4332c20f9128302012 Mon Sep 17 00:00:00 2001 From: Marco Roth Date: Tue, 12 Dec 2023 20:44:15 +0100 Subject: [PATCH] Introduce API version namespaces and implement `Gems::V2.info` (#66) * Introduce API version namespaces and implement `Gems::V2.info` * Remove inspect call * Add tests to bring coverage back up --- README.md | 3 + lib/gems.rb | 31 +- lib/gems/abstract_client.rb | 27 ++ lib/gems/client.rb | 308 +---------------- lib/gems/v1.rb | 14 + lib/gems/v1/client.rb | 312 ++++++++++++++++++ lib/gems/v2.rb | 14 + lib/gems/v2/client.rb | 35 ++ spec/fixtures/v2/rails-7.0.6.json | 1 + spec/gems/abstract_client_spec.rb | 11 + .../{client_spec.rb => client_v1_spec.rb} | 9 + spec/gems/client_v2_spec.rb | 46 +++ 12 files changed, 484 insertions(+), 327 deletions(-) create mode 100644 lib/gems/abstract_client.rb create mode 100644 lib/gems/v1.rb create mode 100644 lib/gems/v1/client.rb create mode 100644 lib/gems/v2.rb create mode 100644 lib/gems/v2/client.rb create mode 100644 spec/fixtures/v2/rails-7.0.6.json create mode 100644 spec/gems/abstract_client_spec.rb rename spec/gems/{client_spec.rb => client_v1_spec.rb} (98%) create mode 100644 spec/gems/client_v2_spec.rb diff --git a/README.md b/README.md index f960be3..73a8f4d 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,9 @@ require 'gems' # Return some basic information about rails. Gems.info 'rails' +# Return some basic information about rails version 7.0.6. +Gems::V2.info 'rails', '7.0.6' + # Return an array of active gems that match the query. Gems.search 'cucumber' diff --git a/lib/gems.rb b/lib/gems.rb index 4afb8d6..e84fcef 100644 --- a/lib/gems.rb +++ b/lib/gems.rb @@ -1,28 +1,19 @@ +require 'gems/abstract_client' + +require 'gems/v1' +require 'gems/v2' + require 'gems/client' require 'gems/configuration' module Gems extend Configuration - class << self - # Alias for Gems::Client.new - # - # @return [Gems::Client] - def new(options = {}) - Gems::Client.new(options) - end - - # Delegate to Gems::Client - def method_missing(method, *args, &block) - return super unless new.respond_to?(method) - new.send(method, *args, &block) - end - - def respond_to?(method_name, include_private = false) - new.respond_to?(method_name, include_private) || super(method_name, include_private) - end + include AbstractClient - def respond_to_missing?(method_name, include_private = false) - new.respond_to?(method_name, include_private) || super(method_name, include_private) - end + # Alias for Gems::Client.new + # + # @return [Gems::Client] + def self.new(options = {}) + Gems::Client.new(options) end end diff --git a/lib/gems/abstract_client.rb b/lib/gems/abstract_client.rb new file mode 100644 index 0000000..b86d753 --- /dev/null +++ b/lib/gems/abstract_client.rb @@ -0,0 +1,27 @@ +module Gems + module AbstractClient + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + def new(options = {}) + raise NotImplementedError.new + end + + # Delegate to Gems::Client + def method_missing(method, *args, &block) + return super unless new.respond_to?(method) + new.send(method, *args, &block) + end + + def respond_to?(method_name, include_private = false) + new.respond_to?(method_name, include_private) || super(method_name, include_private) + end + + def respond_to_missing?(method_name, include_private = false) + new.respond_to?(method_name, include_private) || super(method_name, include_private) + end + end + end +end diff --git a/lib/gems/client.rb b/lib/gems/client.rb index a1bda0f..0897c5b 100644 --- a/lib/gems/client.rb +++ b/lib/gems/client.rb @@ -1,310 +1,4 @@ -require 'date' -require 'gems/configuration' -require 'gems/request' -require 'json' - module Gems - class Client - include Gems::Request - attr_accessor(*Configuration::VALID_OPTIONS_KEYS) - - def initialize(options = {}) - options = Gems.options.merge(options) - Configuration::VALID_OPTIONS_KEYS.each do |key| - send("#{key}=", options[key]) - end - end - - # Returns some basic information about the given gem - # - # @authenticated false - # @param gem_name [String] The name of a gem. - # @return [Hash] - # @example - # Gems.info 'rails' - def info(gem_name) - response = get("/api/v1/gems/#{gem_name}.json") - JSON.parse(response) - rescue JSON::ParserError - {} - end - - # Returns an array of active gems that match the query - # - # @authenticated false - # @param query [String] A term to search for. - # @param options [Hash] A customizable set of options. - # @option options [Integer] :page - # @return [Array] - # @example - # Gems.search 'cucumber' - def search(query, options = {}) - response = get('/api/v1/search.json', options.merge(:query => query)) - JSON.parse(response) - end - - # List all gems that you own - # - # @authenticated true - # @param user_handle [String] The handle of a user. - # @return [Array] - # @example - # Gems.gems - def gems(user_handle = nil) - response = user_handle ? get("/api/v1/owners/#{user_handle}/gems.json") : get('/api/v1/gems.json') - JSON.parse(response) - end - - # Submit a gem to RubyGems.org or another host - # - # @authenticated true - # @param gem [File] A built gem. - # @param host [String] A RubyGems compatible host to use. - # @return [String] - # @example - # Gems.push File.new 'pkg/gemcutter-0.2.1.gem' - def push(gem, host = Configuration::DEFAULT_HOST) - post('/api/v1/gems', gem.read, 'application/octet-stream', host) - end - - # Remove a gem from RubyGems.org's index - # - # @authenticated true - # @param gem_name [String] The name of a gem. - # @param gem_version [String] The version of a gem. - # @param options [Hash] A customizable set of options. - # @option options [String] :platform - # @return [String] - # @example - # Gems.yank "gemcutter", "0.2.1", {:platform => "x86-darwin-10"} - def yank(gem_name, gem_version = nil, options = {}) - gem_version ||= info(gem_name)['version'] - delete('/api/v1/gems/yank', options.merge(:gem_name => gem_name, :version => gem_version)) - end - - # Update a previously yanked gem back into RubyGems.org's index - # - # @authenticated true - # @param gem_name [String] The name of a gem. - # @param gem_version [String] The version of a gem. - # @param options [Hash] A customizable set of options. - # @option options [String] :platform - # @return [String] - # @example - # Gems.unyank "gemcutter", "0.2.1", {:platform => "x86-darwin-10"} - def unyank(gem_name, gem_version = nil, options = {}) - gem_version ||= info(gem_name)['version'] - put('/api/v1/gems/unyank', options.merge(:gem_name => gem_name, :version => gem_version)) - end - - # Returns an array of gem version details - # - # @authenticated false - # @param gem_name [String] The name of a gem. - # @return [Hash] - # @example - # Gems.versions 'coulda' - def versions(gem_name) - response = get("/api/v1/versions/#{gem_name}.json") - JSON.parse(response) - end - - # Returns an hash of gem latest version - # - # @authenticated false - # @param gem_name [String] The name of a gem. - # @return [Hash] - # @example - # Gems.latest_version 'coulda' - def latest_version(gem_name) - response = get("/api/v1/versions/#{gem_name}/latest.json") - JSON.parse(response) - end - - # Returns the total number of downloads for a particular gem - # - # @authenticated false - # @param gem_name [String] The name of a gem. - # @param gem_version [String] The version of a gem. - # @return [Hash] - # @example - # Gems.total_downloads 'rails_admin', '0.0.1' - def total_downloads(gem_name = nil, gem_version = nil) - response = gem_name ? get("/api/v1/downloads/#{gem_name}-#{gem_version || info(gem_name)['version']}.json") : get('/api/v1/downloads.json') - JSON.parse(response, :symbolize_names => true) - end - - # Returns an array containing the top 50 downloaded gem versions of all time - # - # @authenticated false - # @return [Array] - # @example - # Gems.most_downloaded - def most_downloaded - response = get('/api/v1/downloads/all.json') - JSON.parse(response)['gems'] - end - - # Returns the number of downloads by day for a particular gem version - # - # @authenticated false - # @param gem_name [String] The name of a gem. - # @param gem_version [String] The version of a gem. - # @param from [Date] Search start date. - # @param to [Date] Search end date. - # @return [Hash] - # @example - # Gems.downloads 'coulda', '0.6.3', Date.today - 30, Date.today - def downloads(gem_name, gem_version = nil, from = nil, to = Date.today) - gem_version ||= info(gem_name)['version'] - response = from ? get("/api/v1/versions/#{gem_name}-#{gem_version}/downloads/search.json", :from => from.to_s, :to => to.to_s) : get("/api/v1/versions/#{gem_name}-#{gem_version}/downloads.json") - JSON.parse(response) - end - - # View all owners of a gem that you own - # - # @authenticated true - # @param gem_name [String] The name of a gem. - # @return [Array] - # @example - # Gems.owners 'gemcutter' - def owners(gem_name) - response = get("/api/v1/gems/#{gem_name}/owners.json") - JSON.parse(response) - end - - # Add an owner to a RubyGem you own, giving that user permission to manage it - # - # @authenticated true - # @param gem_name [String] The name of a gem. - # @param owner [String] The email address of the user you want to add. - # @return [String] - # @example - # Gems.add_owner 'gemcutter', 'josh@technicalpickles.com' - def add_owner(gem_name, owner) - post("/api/v1/gems/#{gem_name}/owners", :email => owner) - end - - # Remove a user's permission to manage a RubyGem you own - # - # @authenticated true - # @param gem_name [String] The name of a gem. - # @param owner [String] The email address of the user you want to remove. - # @return [String] - # @example - # Gems.remove_owner 'gemcutter', 'josh@technicalpickles.com' - def remove_owner(gem_name, owner) - delete("/api/v1/gems/#{gem_name}/owners", :email => owner) - end - - # List the webhooks registered under your account - # - # @authenticated true - # @return [Hash] - # @example - # Gems.web_hooks - def web_hooks - response = get('/api/v1/web_hooks.json') - JSON.parse(response) - end - - # Create a webhook - # - # @authenticated true - # @param gem_name [String] The name of a gem. Specify "*" to add the hook to all gems. - # @param url [String] The URL of the web hook. - # @return [String] - # @example - # Gems.add_web_hook 'rails', 'http://example.com' - def add_web_hook(gem_name, url) - post('/api/v1/web_hooks', :gem_name => gem_name, :url => url) - end - - # Remove a webhook - # - # @authenticated true - # @param gem_name [String] The name of a gem. Specify "*" to remove the hook from all gems. - # @param url [String] The URL of the web hook. - # @return [String] - # @example - # Gems.remove_web_hook 'rails', 'http://example.com' - def remove_web_hook(gem_name, url) - delete('/api/v1/web_hooks/remove', :gem_name => gem_name, :url => url) - end - - # Test fire a webhook - # - # @authenticated true - # @param gem_name [String] The name of a gem. Specify "*" to fire the hook for all gems. - # @param url [String] The URL of the web hook. - # @return [String] - # @example - # Gems.fire_web_hook 'rails', 'http://example.com' - def fire_web_hook(gem_name, url) - post('/api/v1/web_hooks/fire', :gem_name => gem_name, :url => url) - end - - # Returns the 50 gems most recently added to RubyGems.org (for the first time) - # - # @authenticated false - # @param options [Hash] A customizable set of options. - # @return [Array] - # @example - # Gem.latest - def latest(options = {}) - response = get('/api/v1/activity/latest.json', options) - JSON.parse(response) - end - - # Returns the 50 most recently updated gems - # - # @authenticated false - # @param options [Hash] A customizable set of options. - # @return [Array] - # @example - # Gem.just_updated - def just_updated(options = {}) - response = get('/api/v1/activity/just_updated.json', options) - JSON.parse(response) - end - - # Retrieve your API key using HTTP basic auth - # - # @authenticated true - # @return [String] - # @example - # Gems.configure do |config| - # config.username = 'nick@gemcutter.org' - # config.password = 'schwwwwing' - # end - # Gems.api_key - def api_key - get('/api/v1/api_key') - end - - # Returns an array of hashes for all versions of given gems - # - # @authenticated false - # @param gems [Array] A list of gem names - # @return [Array] - # @example - # Gems.dependencies 'rails', 'thor' - def dependencies(*gems) - response = get('/api/v1/dependencies', :gems => gems.join(',')) - Marshal.load(response) - end - - # Returns an array of all the reverse dependencies to the given gem. - # - # @authenticated false - # @param gem_name [String] The name of a gem - # @param options [Hash] A customizable set of options. - # @return [Array] - # @example - # Gems.reverse_dependencies 'money' - def reverse_dependencies(gem_name, options = {}) - response = get("/api/v1/gems/#{gem_name}/reverse_dependencies.json", options) - JSON.parse(response) - end + class Client < V1::Client end end diff --git a/lib/gems/v1.rb b/lib/gems/v1.rb new file mode 100644 index 0000000..93fb510 --- /dev/null +++ b/lib/gems/v1.rb @@ -0,0 +1,14 @@ +require 'gems/v1/client' + +module Gems + module V1 + include AbstractClient + + # Alias for Gems::V1::Client.new + # + # @return [Gems::V1::Client] + def self.new(options = {}) + Gems::V1::Client.new(options) + end + end +end diff --git a/lib/gems/v1/client.rb b/lib/gems/v1/client.rb new file mode 100644 index 0000000..761c96f --- /dev/null +++ b/lib/gems/v1/client.rb @@ -0,0 +1,312 @@ +require 'date' +require 'gems/configuration' +require 'gems/request' +require 'json' + +module Gems + module V1 + class Client + include Gems::Request + attr_accessor(*Configuration::VALID_OPTIONS_KEYS) + + def initialize(options = {}) + options = Gems.options.merge(options) + Configuration::VALID_OPTIONS_KEYS.each do |key| + send("#{key}=", options[key]) + end + end + + # Returns some basic information about the given gem + # + # @authenticated false + # @param gem_name [String] The name of a gem. + # @return [Hash] + # @example + # Gems.info 'rails' + def info(gem_name) + response = get("/api/v1/gems/#{gem_name}.json") + JSON.parse(response) + rescue JSON::ParserError + {} + end + + # Returns an array of active gems that match the query + # + # @authenticated false + # @param query [String] A term to search for. + # @param options [Hash] A customizable set of options. + # @option options [Integer] :page + # @return [Array] + # @example + # Gems.search 'cucumber' + def search(query, options = {}) + response = get('/api/v1/search.json', options.merge(:query => query)) + JSON.parse(response) + end + + # List all gems that you own + # + # @authenticated true + # @param user_handle [String] The handle of a user. + # @return [Array] + # @example + # Gems.gems + def gems(user_handle = nil) + response = user_handle ? get("/api/v1/owners/#{user_handle}/gems.json") : get('/api/v1/gems.json') + JSON.parse(response) + end + + # Submit a gem to RubyGems.org or another host + # + # @authenticated true + # @param gem [File] A built gem. + # @param host [String] A RubyGems compatible host to use. + # @return [String] + # @example + # Gems.push File.new 'pkg/gemcutter-0.2.1.gem' + def push(gem, host = Configuration::DEFAULT_HOST) + post('/api/v1/gems', gem.read, 'application/octet-stream', host) + end + + # Remove a gem from RubyGems.org's index + # + # @authenticated true + # @param gem_name [String] The name of a gem. + # @param gem_version [String] The version of a gem. + # @param options [Hash] A customizable set of options. + # @option options [String] :platform + # @return [String] + # @example + # Gems.yank "gemcutter", "0.2.1", {:platform => "x86-darwin-10"} + def yank(gem_name, gem_version = nil, options = {}) + gem_version ||= info(gem_name)['version'] + delete('/api/v1/gems/yank', options.merge(:gem_name => gem_name, :version => gem_version)) + end + + # Update a previously yanked gem back into RubyGems.org's index + # + # @authenticated true + # @param gem_name [String] The name of a gem. + # @param gem_version [String] The version of a gem. + # @param options [Hash] A customizable set of options. + # @option options [String] :platform + # @return [String] + # @example + # Gems.unyank "gemcutter", "0.2.1", {:platform => "x86-darwin-10"} + def unyank(gem_name, gem_version = nil, options = {}) + gem_version ||= info(gem_name)['version'] + put('/api/v1/gems/unyank', options.merge(:gem_name => gem_name, :version => gem_version)) + end + + # Returns an array of gem version details + # + # @authenticated false + # @param gem_name [String] The name of a gem. + # @return [Hash] + # @example + # Gems.versions 'coulda' + def versions(gem_name) + response = get("/api/v1/versions/#{gem_name}.json") + JSON.parse(response) + end + + # Returns an hash of gem latest version + # + # @authenticated false + # @param gem_name [String] The name of a gem. + # @return [Hash] + # @example + # Gems.latest_version 'coulda' + def latest_version(gem_name) + response = get("/api/v1/versions/#{gem_name}/latest.json") + JSON.parse(response) + end + + # Returns the total number of downloads for a particular gem + # + # @authenticated false + # @param gem_name [String] The name of a gem. + # @param gem_version [String] The version of a gem. + # @return [Hash] + # @example + # Gems.total_downloads 'rails_admin', '0.0.1' + def total_downloads(gem_name = nil, gem_version = nil) + response = gem_name ? get("/api/v1/downloads/#{gem_name}-#{gem_version || info(gem_name)['version']}.json") : get('/api/v1/downloads.json') + JSON.parse(response, :symbolize_names => true) + end + + # Returns an array containing the top 50 downloaded gem versions of all time + # + # @authenticated false + # @return [Array] + # @example + # Gems.most_downloaded + def most_downloaded + response = get('/api/v1/downloads/all.json') + JSON.parse(response)['gems'] + end + + # Returns the number of downloads by day for a particular gem version + # + # @authenticated false + # @param gem_name [String] The name of a gem. + # @param gem_version [String] The version of a gem. + # @param from [Date] Search start date. + # @param to [Date] Search end date. + # @return [Hash] + # @example + # Gems.downloads 'coulda', '0.6.3', Date.today - 30, Date.today + def downloads(gem_name, gem_version = nil, from = nil, to = Date.today) + gem_version ||= info(gem_name)['version'] + response = from ? get("/api/v1/versions/#{gem_name}-#{gem_version}/downloads/search.json", :from => from.to_s, :to => to.to_s) : get("/api/v1/versions/#{gem_name}-#{gem_version}/downloads.json") + JSON.parse(response) + end + + # View all owners of a gem that you own + # + # @authenticated true + # @param gem_name [String] The name of a gem. + # @return [Array] + # @example + # Gems.owners 'gemcutter' + def owners(gem_name) + response = get("/api/v1/gems/#{gem_name}/owners.json") + JSON.parse(response) + end + + # Add an owner to a RubyGem you own, giving that user permission to manage it + # + # @authenticated true + # @param gem_name [String] The name of a gem. + # @param owner [String] The email address of the user you want to add. + # @return [String] + # @example + # Gems.add_owner 'gemcutter', 'josh@technicalpickles.com' + def add_owner(gem_name, owner) + post("/api/v1/gems/#{gem_name}/owners", :email => owner) + end + + # Remove a user's permission to manage a RubyGem you own + # + # @authenticated true + # @param gem_name [String] The name of a gem. + # @param owner [String] The email address of the user you want to remove. + # @return [String] + # @example + # Gems.remove_owner 'gemcutter', 'josh@technicalpickles.com' + def remove_owner(gem_name, owner) + delete("/api/v1/gems/#{gem_name}/owners", :email => owner) + end + + # List the webhooks registered under your account + # + # @authenticated true + # @return [Hash] + # @example + # Gems.web_hooks + def web_hooks + response = get('/api/v1/web_hooks.json') + JSON.parse(response) + end + + # Create a webhook + # + # @authenticated true + # @param gem_name [String] The name of a gem. Specify "*" to add the hook to all gems. + # @param url [String] The URL of the web hook. + # @return [String] + # @example + # Gems.add_web_hook 'rails', 'http://example.com' + def add_web_hook(gem_name, url) + post('/api/v1/web_hooks', :gem_name => gem_name, :url => url) + end + + # Remove a webhook + # + # @authenticated true + # @param gem_name [String] The name of a gem. Specify "*" to remove the hook from all gems. + # @param url [String] The URL of the web hook. + # @return [String] + # @example + # Gems.remove_web_hook 'rails', 'http://example.com' + def remove_web_hook(gem_name, url) + delete('/api/v1/web_hooks/remove', :gem_name => gem_name, :url => url) + end + + # Test fire a webhook + # + # @authenticated true + # @param gem_name [String] The name of a gem. Specify "*" to fire the hook for all gems. + # @param url [String] The URL of the web hook. + # @return [String] + # @example + # Gems.fire_web_hook 'rails', 'http://example.com' + def fire_web_hook(gem_name, url) + post('/api/v1/web_hooks/fire', :gem_name => gem_name, :url => url) + end + + # Returns the 50 gems most recently added to RubyGems.org (for the first time) + # + # @authenticated false + # @param options [Hash] A customizable set of options. + # @return [Array] + # @example + # Gem.latest + def latest(options = {}) + response = get('/api/v1/activity/latest.json', options) + JSON.parse(response) + end + + # Returns the 50 most recently updated gems + # + # @authenticated false + # @param options [Hash] A customizable set of options. + # @return [Array] + # @example + # Gem.just_updated + def just_updated(options = {}) + response = get('/api/v1/activity/just_updated.json', options) + JSON.parse(response) + end + + # Retrieve your API key using HTTP basic auth + # + # @authenticated true + # @return [String] + # @example + # Gems.configure do |config| + # config.username = 'nick@gemcutter.org' + # config.password = 'schwwwwing' + # end + # Gems.api_key + def api_key + get('/api/v1/api_key') + end + + # Returns an array of hashes for all versions of given gems + # + # @authenticated false + # @param gems [Array] A list of gem names + # @return [Array] + # @example + # Gems.dependencies 'rails', 'thor' + def dependencies(*gems) + response = get('/api/v1/dependencies', :gems => gems.join(',')) + Marshal.load(response) + end + + # Returns an array of all the reverse dependencies to the given gem. + # + # @authenticated false + # @param gem_name [String] The name of a gem + # @param options [Hash] A customizable set of options. + # @return [Array] + # @example + # Gems.reverse_dependencies 'money' + def reverse_dependencies(gem_name, options = {}) + response = get("/api/v1/gems/#{gem_name}/reverse_dependencies.json", options) + JSON.parse(response) + end + end + end +end diff --git a/lib/gems/v2.rb b/lib/gems/v2.rb new file mode 100644 index 0000000..f29d117 --- /dev/null +++ b/lib/gems/v2.rb @@ -0,0 +1,14 @@ +require 'gems/v2/client' + +module Gems + module V2 + include AbstractClient + + # Alias for Gems::V2::Client.new + # + # @return [Gems::V2::Client] + def self.new(options = {}) + Gems::V2::Client.new(options) + end + end +end diff --git a/lib/gems/v2/client.rb b/lib/gems/v2/client.rb new file mode 100644 index 0000000..4da99ba --- /dev/null +++ b/lib/gems/v2/client.rb @@ -0,0 +1,35 @@ +require 'date' +require 'gems/configuration' +require 'gems/request' +require 'json' + +module Gems + module V2 + class Client + include Gems::Request + attr_accessor(*Configuration::VALID_OPTIONS_KEYS) + + def initialize(options = {}) + options = Gems.options.merge(options) + Configuration::VALID_OPTIONS_KEYS.each do |key| + send("#{key}=", options[key]) + end + end + + # Returns information about the given gem for a sepcific version + # + # @authenticated false + # @param gem_name [String] The name of a gem. + # @param version [String] The requested version of the gem. + # @return [Hash] + # @example + # Gems::V2.info 'rails', '7.0.6' + def info(gem_name, version) + response = get("/api/v2/rubygems/#{gem_name}/versions/#{version}.json") + JSON.parse(response) + rescue JSON::ParserError + {} + end + end + end +end diff --git a/spec/fixtures/v2/rails-7.0.6.json b/spec/fixtures/v2/rails-7.0.6.json new file mode 100644 index 0000000..a89c410 --- /dev/null +++ b/spec/fixtures/v2/rails-7.0.6.json @@ -0,0 +1 @@ +{"name":"rails","downloads":454392739,"version":"7.0.6","version_created_at":"2023-06-29T20:57:24.359Z","version_downloads":1492222,"platform":"ruby","authors":"David Heinemeier Hansson","info":"Ruby on Rails is a full-stack web framework optimized for programmer happiness and sustainable productivity. It encourages beautiful code by favoring convention over configuration.","licenses":["MIT"],"metadata":{"changelog_uri":"https://github.com/rails/rails/releases/tag/v7.0.6","bug_tracker_uri":"https://github.com/rails/rails/issues","source_code_uri":"https://github.com/rails/rails/tree/v7.0.6","mailing_list_uri":"https://discuss.rubyonrails.org/c/rubyonrails-talk","documentation_uri":"https://api.rubyonrails.org/v7.0.6/","rubygems_mfa_required":"true"},"yanked":false,"sha":"5dfbd481a23556ad425fc8541399a129a08ed550f877294b44d0170ca5b9f421","project_uri":"https://rubygems.org/gems/rails","gem_uri":"https://rubygems.org/gems/rails-7.0.6.gem","homepage_uri":"https://rubyonrails.org","wiki_uri":null,"documentation_uri":"https://api.rubyonrails.org/v7.0.6/","mailing_list_uri":"https://discuss.rubyonrails.org/c/rubyonrails-talk","source_code_uri":"https://github.com/rails/rails/tree/v7.0.6","bug_tracker_uri":"https://github.com/rails/rails/issues","changelog_uri":"https://github.com/rails/rails/releases/tag/v7.0.6","funding_uri":null,"dependencies":{"development":[],"runtime":[{"name":"actioncable","requirements":"= 7.0.6"},{"name":"actionmailbox","requirements":"= 7.0.6"},{"name":"actionmailer","requirements":"= 7.0.6"},{"name":"actionpack","requirements":"= 7.0.6"},{"name":"actiontext","requirements":"= 7.0.6"},{"name":"actionview","requirements":"= 7.0.6"},{"name":"activejob","requirements":"= 7.0.6"},{"name":"activemodel","requirements":"= 7.0.6"},{"name":"activerecord","requirements":"= 7.0.6"},{"name":"activestorage","requirements":"= 7.0.6"},{"name":"activesupport","requirements":"= 7.0.6"},{"name":"bundler","requirements":">= 1.15.0"},{"name":"railties","requirements":"= 7.0.6"}]},"built_at":"2023-06-29T00:00:00.000Z","created_at":"2023-06-29T20:57:24.359Z","description":"Ruby on Rails is a full-stack web framework optimized for programmer happiness and sustainable productivity. It encourages beautiful code by favoring convention over configuration.","downloads_count":1492222,"number":"7.0.6","summary":"Full-stack web application framework.","rubygems_version":">= 1.8.11","ruby_version":">= 2.7.0","prerelease":false,"requirements":[]} diff --git a/spec/gems/abstract_client_spec.rb b/spec/gems/abstract_client_spec.rb new file mode 100644 index 0000000..3106066 --- /dev/null +++ b/spec/gems/abstract_client_spec.rb @@ -0,0 +1,11 @@ +require 'helper' + +describe Gems::AbstractClient do + it "raises NotImplementedError if new isn't overritten" do + FooClient = Class.new do + include Gems::AbstractClient + end + + expect { FooClient.new }.to raise_error(NotImplementedError) + end +end diff --git a/spec/gems/client_spec.rb b/spec/gems/client_v1_spec.rb similarity index 98% rename from spec/gems/client_spec.rb rename to spec/gems/client_v1_spec.rb index 4d55ddc..c260845 100644 --- a/spec/gems/client_spec.rb +++ b/spec/gems/client_v1_spec.rb @@ -5,6 +5,15 @@ Gems.reset end + describe '#new' do + it 'creates a new v1 client' do + client = Gems::V1.new + + expect(client).to_not be_nil + expect(client.class).to eq(Gems::V1::Client) + end + end + describe '#info' do context 'when gem exists' do before do diff --git a/spec/gems/client_v2_spec.rb b/spec/gems/client_v2_spec.rb new file mode 100644 index 0000000..3382d5d --- /dev/null +++ b/spec/gems/client_v2_spec.rb @@ -0,0 +1,46 @@ +require 'helper' + +describe Gems::V2::Client do + after do + Gems.reset + end + + describe '#info' do + context 'when gem exists' do + before do + stub_get('/api/v2/rubygems/rails/versions/7.0.6.json').to_return(body: fixture('v2/rails-7.0.6.json')) + end + + it 'returns some basic information about the given gem' do + info = Gems::V2.info 'rails', '7.0.6' + expect(a_get('/api/v2/rubygems/rails/versions/7.0.6.json')).to have_been_made + expect(info['name']).to eq 'rails' + expect(info['version']).to eq '7.0.6' + end + end + + context 'when gem version does not exist' do + before do + stub_get('/api/v2/rubygems/rails/versions/7.0.99.json').to_return(body: 'This version could not be found.') + end + + it 'returns error message' do + info = Gems::V2.info 'rails', '7.0.99' + expect(a_get('/api/v2/rubygems/rails/versions/7.0.99.json')).to have_been_made + expect(info['name']).to be_nil + end + end + + context 'when gem does not exist' do + before do + stub_get('/api/v2/rubygems/nonexistentgem/versions/1.0.0.json').to_return(body: 'This gem could not be found') + end + + it 'returns error message' do + info = Gems::V2.info 'nonexistentgem', '1.0.0' + expect(a_get('/api/v2/rubygems/nonexistentgem/versions/1.0.0.json')).to have_been_made + expect(info['name']).to be_nil + end + end + end +end