From 8bc8551281f37f311d30a677c8146d08a1326260 Mon Sep 17 00:00:00 2001 From: unurgunite Date: Fri, 12 Jun 2026 22:57:28 +0300 Subject: [PATCH] [0.3.0] Fixed Docscribe types --- .gitignore | 1 + docscribe.yml | 9 ++ lib/extensions/deep_find.rb | 31 ++---- lib/extensions/token_ext.rb | 2 +- lib/extensions/unescape.rb | 2 +- lib/genius/api/account.rb | 5 +- lib/genius/api/annotations.rb | 182 +++++--------------------------- lib/genius/api/artists.rb | 68 +++++------- lib/genius/api/authorization.rb | 28 ++--- lib/genius/api/errors.rb | 113 ++++++++------------ lib/genius/api/referents.rb | 28 ++--- lib/genius/api/search.rb | 34 +----- lib/genius/api/songs.rb | 48 ++++----- lib/genius/api/web_pages.rb | 24 +---- 14 files changed, 165 insertions(+), 410 deletions(-) create mode 100644 docscribe.yml diff --git a/.gitignore b/.gitignore index 49d936f..472090d 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ rakelib .env Rakefile .gem_rbs_collection/ +/docs/ diff --git a/docscribe.yml b/docscribe.yml new file mode 100644 index 0000000..1ef2d24 --- /dev/null +++ b/docscribe.yml @@ -0,0 +1,9 @@ +--- +rbs: + enabled: true + collection: true + collapse_generics: true + +filter: + exclude: + - Genius::Errors::DynamicRescue.rescue_from diff --git a/lib/extensions/deep_find.rb b/lib/extensions/deep_find.rb index ffded2c..b48c44e 100644 --- a/lib/extensions/deep_find.rb +++ b/lib/extensions/deep_find.rb @@ -1,29 +1,11 @@ # frozen_string_literal: true class Hash # :nodoc: - # +Hash#deep_find+ -> value + # Searches for a key in nested hashes and arrays. Returns matching values or +nil+. # - # @param [Object] key A key, which value should be found - # @param [FalseClass] uniq A flag to make values unique in an array - # @return [Object] output depends on key value - # This method is an extension for Hash core class to search for a value of a key in N-nested - # hash. It provides search for multiple values if key appears more than once. For e.g.: - # - # @example - # musicians = { "Travis Scott" => { "28" => ["Highest in the Room", "Franchise"] }, - # "Adele" => { "19" => ["Day Dreamer", "Best for Last"] }, - # "Ed Sheeran" => { "28" => ["Shape of You", "Castle on the Hill"] } } - # musicians.deep_find("19") #=> ["Day Dreamer", "Best for Last"] - # musicians.deep_find("Adele") #=> {"19"=>["Day Dreamer", "Best for Last"]} - # musicians.deep_find("28") #=> [["Highest in the Room", "Franchise"], ["Shape of You", "Castle on the Hill"]] - # - # If values are identical, they will be returned in a single copy. You can disable this - # feature with special param +uniq+, which is +true+ by default. For e.g.: - # - # @example - # h = {"a" => "b", "c" => {"a" => "b"}} - # h.deep_find("a") #=> "b", instead ["b", "b"] - # @todo change uniq true to uniq false + # @param [Object] key Key to search for. + # @param [Boolean] uniq If +true+, deduplicates results. + # @return [nil, Object] def deep_find(key, uniq: true) result = collect_values(key) result.compact! @@ -36,6 +18,11 @@ def deep_find(key, uniq: true) private + # Recursively collects values for a key from nested hashes. + # + # @private + # @param [Object] key Key to search for. + # @return [Array] def collect_values(key) result = [self[key]] each_value do |value| diff --git a/lib/extensions/token_ext.rb b/lib/extensions/token_ext.rb index 2bae846..90afafc 100644 --- a/lib/extensions/token_ext.rb +++ b/lib/extensions/token_ext.rb @@ -4,7 +4,7 @@ class Object # :nodoc: # +Object#token_ext+ -> String # # Helper method to check if token is correct - # @param [String] token Token to access https://api.genius.com. + # @param [String?] token Token to access https://api.genius.com. # @return [String] def token_ext(token) token || Genius::Auth.instance_variable_get(:@token) diff --git a/lib/extensions/unescape.rb b/lib/extensions/unescape.rb index a7fcb9a..63df72b 100644 --- a/lib/extensions/unescape.rb +++ b/lib/extensions/unescape.rb @@ -5,7 +5,7 @@ class String # :nodoc: # # String#unescape method unescapes input JSON strings. # - # @return [String (frozen)] + # @return [String] def unescape string = gsub(/(? true or false - # - # @param [Object] id Identification of annotations resource. - # @param [Object] action Action to do during PUT request. Possible actions: nil, upvote, downvote, unvote. - # @param [String] token Token to access https://api.genius.com. - # @param [String (frozen)] http_verb HTTP verb for request. Possible verbs: get, post, put, delete. - # @param [Hash] options Options for PUT response. - # @option options [String] :markdown The text for the note, in - # {markdown}[https://help.github.com/articles/github-flavored-markdown/] - # @option options [String] :raw_annotatable_url The original URL of the page. - # @option options [String] :fragment The highlighted fragment. - # @option options [String] :before_html The HTML before the highlighted fragment (prefer up to 200 characters). - # @option options [String] :after_html The HTML after the highlighted fragment (prefer up to 200 characters). - # @option options [String] :canonical_url The href property of the tag - # on the page. Including it will help make sure newly created annotation appear on the correct page. - # @option options [String] :og_url The content property of the tag on - # the page. Including it will help make sure newly created annotation appear on the correct page. - # @option options [String] :title The title of the page. - # @raise [ArgumentError] if +action+ got incorrect value. - # @raise [TokenError] if +token+ or +Genius::Auth.token+ are invalid. - # @return [NilClass] if TokenError exceptions raised. - # - # +GET /annotations/:id+
- # Data for a specific annotation. - # - # @example Example usage - # Genius::Annotations.annotations(id: 10225840) - # - # +POST /annotations+
- # Requires scope: _create_annotation_ - # - # Creates a new annotation on a public web page. The returned value will be the new annotation object, in the - # same form as would be returned by GET /annotation/:id with the new annotation's ID. Requires JSON payload. - # @example Example Payload - # { - # "annotation": { - # "body": { - # "markdown": "hello **world!**" - # } - # }, - # "referent": { - # "raw_annotatable_url": "http://seejohncode.com/2014/01/27/vim-commands-piping/", - # "fragment": "execute commands", - # "context_for_display": { - # "before_html": "You may know that you can ", - # "after_html": " from inside of vim, with a vim command:" - # } - # }, - # "web_page": { - # "canonical_url": null, - # "og_url": null, - # "title": "Secret of Mana" - # } - # } - # - # Example usage - # Genius::Annotations.annotation(id:, http_verb: "post", markdown: "Foo **Bar**", - # raw_annotatable_url: "https://example.com") - # will reproduce JSON object via +Genius::Annotations.post_payload+ method. According to last example it will - # return - # { - # annotation: { - # body: { - # markdown: "Foo **Bar**" - # } - # }, - # referent: { - # raw_annotatable_url: "https://example.com", - # fragment: null, - # context_for_display: { - # before_html: null, - # after_html: null - # } - # }, - # web_page: { - # canonical_url: null, - # og_url: null, - # title: null - # } - # } - # - # There is a full list of possible params: - # * annotation - # * body - # * markdown - The text for the note, in - # [markdown](https://help.github.com/articles/github-flavored-markdown/) _(Required)_ - # * referent - # * raw_annotatable_url - The original URL of the page _(Required)_ - # - # * fragment - The highlighted fragment _(Required)_ - # * context_for_display - # - # * before_html - The HTML before the highlighted fragment (prefer up to 200 characters) - # - # * after_html - The HTML after the highlighted fragment (prefer up to 200 characters) - # * web_page At least one required - # - # * canonical_url - The href property of the ++ tag on the page. Including it will - # help make sure newly created annotation appear on the correct page - # - # * og_url - The content property of the tag on the page. Including it will help make sure newly created - # annotation appear on the correct page - # - # * title - The title of the page - # - # +PUT /annotations/:id+
- # Requires scope: _manage_annotation_
- # Updates an annotation created by the authenticated user. Accepts the same parameters as POST /annotation above. - # - # @example Example usage - # Genius::Annotations.annotations(id: 10225840, http_verb: "put") - # - # +DELETE /annotations/:id+
- # Requires scope: _manage_annotation_
- # Deletes an annotation created by the authenticated user. - # - # @example Example usage - # Genius::Annotations.annotations(id: 10225840, http_verb: "delete") - # - # +PUT /annotations/:id/upvote+
- # Requires scope: _vote_
- # Votes positively for the annotation on behalf of the authenticated user. - # - # @example Example usage - # Genius::Annotations.annotations(id: 10225840, http_verb: "put", action: "upvote") - # - # +PUT /annotations/:id/downvote+
- # Requires scope: _vote_
- # Votes negatively for the annotation on behalf of the authenticated user. - # - # @example Example usage - # Genius::Annotations.annotations(id: 10225840, http_verb: "put", action: "vote") - # - # +PUT /annotations/:id/unvote+
- # Requires scope: _vote_
- # Removes the authenticated user's vote (up or down) for the annotation. - # - # @example Example usage - # Genius::Annotations.annotations(id: 10225840, http_verb: "put", action: "vote") + # Data for a specific annotation. Supports GET, POST, PUT, DELETE verbs with optional voting actions. + # + # @param [Integer] id ID of the annotation. + # @param [String?] action Action for PUT request: +nil+, +upvote+, +downvote+, or +unvote+. + # @param [String?] token Token to access https://api.genius.com. + # @param [String] http_verb HTTP verb: +get+, +post+, +put+, +delete+. + # @param [Hash] options Options for POST/PUT payload. + # @raise [ArgumentError] if +action+ is set for non-PUT request. + # @return [Hash, nil] def annotations(id:, action:, token:, http_verb: 'get', options: {}) return if token.nil? && !Auth.authorized?.nil? @@ -155,10 +25,16 @@ def annotations(id:, action:, token:, http_verb: 'get', options: {}) private - # +Genius::Annotations.request+ -> HTTParty::Response + # Sends an HTTP request based on the verb and returns the raw response. # # @private - # @see .annotations + # @param [Integer] id ID of the annotation. + # @param [String?] action Action for PUT request. + # @param [String?] token Token to access https://api.genius.com. + # @param [String] http_verb HTTP verb. + # @param [Hash] options Options for POST/PUT payload. + # @raise [ArgumentError] if HTTP verb is invalid. + # @return [HTTParty::Response] def request(id:, action:, token:, http_verb:, options:) case http_verb when 'get' then HTTParty.get("#{Api::RESOURCE}/annotations/#{id}?access_token=#{token_ext(token)}") @@ -170,10 +46,15 @@ def request(id:, action:, token:, http_verb:, options:) end end - # +Genius::Annotations.put_request+ -> HTTParty::Response + # Sends a PUT request with optional voting action. # # @private - # @see .annotations + # @param [Integer] id ID of the annotation. + # @param [String?] action Action: +nil+, +upvote+, +downvote+, or +unvote+. + # @param [String?] token Token to access https://api.genius.com. + # @param [Hash] options Options for PUT payload. + # @raise [ArgumentError] if +action+ is invalid. + # @return [HTTParty::Response] def put_request(id:, action:, token:, options:) case action when nil then HTTParty.put("#{Api::RESOURCE}/annotations/#{id}/#{action}?access_token=#{token_ext(token)}", @@ -186,21 +67,10 @@ def put_request(id:, action:, token:, options:) end end - # +Genius::Annotations.post_payload+ -> String + # Builds a JSON payload for POST and PUT requests from options. # # @private - # @param [Hash] options Options for PUT response. - # @option options [String] :markdown The text for the note, in - # {markdown}[https://help.github.com/articles/github-flavored-markdown/]. - # @option options [String] :raw_annotatable_url The original URL of the page. - # @option options [String] :fragment The highlighted fragment. - # @option options [String] :before_html The HTML before the highlighted fragment (prefer up to 200 characters). - # @option options [String] :after_html The HTML after the highlighted fragment (prefer up to 200 characters). - # @option options [String] :canonical_url The href property of the tag - # on the page. Including it will help make sure newly created annotation appear on the correct page. - # @option options [String] :og_url The content property of the tag on - # the page. Including it will help make sure newly created annotation appear on the correct page. - # @option options [String] :title The title of the page. + # @param [Hash] options Options containing +:markdown+, +:raw_annotatable_url+, +:fragment+, etc. # @return [String] def post_payload(options: {}) { diff --git a/lib/genius/api/artists.rb b/lib/genius/api/artists.rb index 77bd551..d1e00f9 100644 --- a/lib/genius/api/artists.rb +++ b/lib/genius/api/artists.rb @@ -5,16 +5,12 @@ module Genius # usually a musician or group of musicians. module Artists class << self - # +Genius::Artists.artists+ -> Hash - # # Data for a specific artist. # - # @param [String] token Token to access https://api.genius.com. - # @param [String] id ID of the song. - # @raise [ArgumentError] if +id+ is +nil+. - # @raise [TokenError] if +token+ or +Genius::Auth.token+ are invalid. - # @return [Hash] - # @return [NilClass] if TokenError exception raised. + # @param [String?] token Token to access https://api.genius.com. + # @param [Integer?] id ID of the artist. + # @raise [ArgumentError] if +id+ is nil. + # @return [Hash, nil] def artists(token: nil, id: nil) Auth.authorized?(method_name: "#{Module.nesting[1].name}.#{__method__}") if token.nil? Errors.validate_token(token) unless token.nil? @@ -24,21 +20,12 @@ def artists(token: nil, id: nil) JSON.parse(response) end - # +Genius::Artists.artists_songs+ -> Hash | NilClass - # - # Documents (songs) for the artist specified. By default, 20 items are returned for each request. + # Songs for the artist specified. By default 20 items per request. # - # @param [String] token Token to access https://api.genius.com. - # @param [String] id ID of the song. - # @param [Hash] options - # @option options [Integer] :per_page Number of results to return per request. - # @option options [Integer] :page Paginated offset, (e.g., +per_page=5&page=3+ returns songs 11-15). - # @option options [String] :sort +title+ (default) or +popularity+. - # @raise [ArgumentError] if +sort+ got incorrect value. - # @raise [ArgumentError] if +per_page+ or +page+ are negative. - # @raise [TokenError] if +token+ or +Genius::Auth.token+ are invalid. - # @return [Hash] - # @return [NilClass] if TokenError exception raised. + # @param [String?] token Token to access https://api.genius.com. + # @param [Integer?] id ID of the artist. + # @param [Hash] options Optional query params: +:sort+, +:per_page+, +:page+. + # @return [Hash, nil] def artists_songs(token: nil, id: nil, options: {}) return if token.nil? && !Auth.authorized?.nil? @@ -54,42 +41,37 @@ def artists_songs(token: nil, id: nil, options: {}) private - # +Genius::Artists.validate+ -> value - # - # A helper method which validates some options for artists endpoint. + # Validates sort, per_page, and page options for artists endpoint. # - # @param [Array] sort_values - # @param [Hash] options - # @return [NilClass] + # @private + # @param [Array] sort_values Allowed sort values. + # @param [Object] options Options with +:sort+, +:per_page+, +:page+. + # @return [void] def validate(sort_values, **options) validate_sort(options[:sort], sort_values) validate_page_per_page(options[:per_page]) validate_page_per_page(options[:page]) end - # +Genius::Artists.validate_sort+ -> value + # Validates the sort option against allowed values. # - # A helper method which validates sort options for artists endpoint. - # - # @see Artists.artists_songs - # @param [String] sort - # @param [Array] sort_values Possible values for sort. - # @raise [ArgumentError] if sort is invalid value. - # @return [Object] + # @private + # @param [String?] sort Sort value to validate. + # @param [Array] sort_values Allowed sort values. + # @raise [ArgumentError] if +sort+ is not in +sort_values+. + # @return [void] def validate_sort(sort, sort_values) return unless sort && !sort_values.include?(sort) raise ArgumentError, "`sort` can't be #{sort}. Possible values: #{sort_values.join(', ')}." end - # +Genius::Artists.validate_page_per_page+ -> value - # - # A helper method which validates per_page or page option for artists endpoint. + # Validates that per_page or page is not negative. # - # @see Artists.artists_songs - # @param [Integer] page_per_page - # @raise [ArgumentError] if per_page or page does not exist or negative. - # @return [NilClass] + # @private + # @param [Integer?] page_per_page Value to validate. + # @raise [ArgumentError] if value is negative. + # @return [void] def validate_page_per_page(page_per_page) raise ArgumentError, "`per_page` or `page` can't be negative." if page_per_page&.negative? end diff --git a/lib/genius/api/authorization.rb b/lib/genius/api/authorization.rb index 81cd066..6da7186 100644 --- a/lib/genius/api/authorization.rb +++ b/lib/genius/api/authorization.rb @@ -8,31 +8,22 @@ module Genius # Genius::Auth.login="yuiaYqbncErCVwItjQxFspNWUZLhGpXrPbkvgbgHSEKJRAlToamzMfdOeDB" module Auth class << self - # +Genius::Auth.token=+ -> true or false - # - # +Genius::Auth.token=+ is a setter which handles all possible exceptions - # under the hood during authentication. It means that you should never use - # +token=+ method unless you actually know that your credentials are - # valid (not recommended). + # Sets the authentication token after validation. # # @param [String] token Token to access https://api.genius.com. - # @raise [TokenError] if +token+ is invalid. + # @raise [Genius::Errors::TokenError] if +token+ is invalid. # @return [String] - # @see .authorized? def token=(token) Genius::Errors.validate_token(token) @token = token end - # +Genius::Auth.authorized?+ -> true or false - # - # +authorized?+ method checks if user in current session is authorized. + # Checks if the current token is authorized. Returns +false+ on validation failure. # - # @param [NilClass|String] method_name Optional param to pass method name - # where exception was raised. - # @raise [TokenError] if +token+ is invalid. + # @param [String] token Token to validate. + # @param [String] method_name Method name for error messages. + # @raise [Genius::Errors::TokenError] # @return [Boolean] - # @todo somehow detect exceptions as boolean type def authorized?(token = @token, method_name: "#{Module.nesting[1].name}.#{__method__}") Errors.validate_token(token, method_name: method_name) rescue Genius::Errors::TokenError @@ -41,12 +32,9 @@ def authorized?(token = @token, method_name: "#{Module.nesting[1].name}.#{__meth true end - # +Genius::Auth.logout!+ -> NilClass - # - # +logout!+ method modifies a +token+ object and revoke session by - # setting +nil+ to the +token+. + # Revokes the current session by setting the token to +nil+. # - # @return [NilClass] + # @return [nil] def logout! @token = nil unless @token.nil? end diff --git a/lib/genius/api/errors.rb b/lib/genius/api/errors.rb index 84aec05..8b22644 100644 --- a/lib/genius/api/errors.rb +++ b/lib/genius/api/errors.rb @@ -59,9 +59,12 @@ class GeniusExceptionSuperClass < StandardError class TokenError < GeniusExceptionSuperClass attr_reader :msg, :exception_type, :method_name - # @param [String (frozen)] msg Exception message. - # @param [String (frozen)] exception_type Exception type. - # @return [String (frozen)] + # Initializes a token validation error with optional method name hint. + # + # @param [String] msg Error message. + # @param [String] exception_type Error type identifier. + # @param [String?] method_name Optional method name for user hint. + # @return [void] def initialize(msg: 'Invalid token. The access token provided is expired, revoked, malformed or invalid for ' \ 'other reasons.', exception_type: 'token_error', method_name: nil) @msg = if method_name.nil? @@ -79,9 +82,11 @@ def initialize(msg: 'Invalid token. The access token provided is expired, revoke class LyricsNotFoundError < GeniusExceptionSuperClass attr_reader :msg, :exception_type - # @param [String (frozen)] msg Exception message. - # @param [String (frozen)] exception_type Exception type. - # @return [String (frozen)] + # Initializes a lyrics-not-found error. + # + # @param [String] msg Error message. + # @param [String] exception_type Error type identifier. + # @return [void] def initialize(msg: 'Lyrics not found in current session. Retrying...', exception_type: 'invalid_lyrics') @msg = msg @exception_type = exception_type @@ -94,23 +99,21 @@ def initialize(msg: 'Lyrics not found in current session. Retrying...', exceptio class PageNotFound < GeniusExceptionSuperClass attr_reader :msg, :exception_type - # @param [String (frozen)] msg Exception message. - # @param [String (frozen)] exception_type Exception type. - # @return [String (frozen)] + # Initializes a page-not-found error. + # + # @param [String] msg Error message. + # @param [String] exception_type Error type identifier. + # @return [void] def initialize(msg: 'Page not found. Try again with another response', exception_type: 'page_not_found') @msg = msg @exception_type = exception_type super(msg) end - # +Genius::Errors::PageNotFound.page_not_found?+ -> true or false - # - # +PageNotFound.page_not_found?+ method is used to be a predicate for - # handling 404 error. + # Checks if the HTML response indicates a page-not-found error. # - # @param [Object] html - # @return [TrueClass] if genius page is not found - # @return [FalseClass] if genius page is found + # @param [Object] html Parsed HTML document. + # @return [Boolean] def self.page_not_found?(html) html.text.include?('Page not found') end @@ -121,16 +124,10 @@ def self.page_not_found?(html) # +Genius::Errors+ scope. module DynamicRescue class << self - # +Genius::Errors::DynamicRescue.rescue+ -> value - # - # +Genius::Errors::DynamicRescue.rescue_from+ is a helper method, - # which, according to reflection, redefine singleton method for - # specified module, adding to it exception handler for DRY pattern. - # - # @todo: add docs + # Wraps singleton methods of +klass+ with exception handling via {DynamicRescue.rescue_from}. # - # @param [Object] klass Class name of structure - module/class/etc. - # @return [Object] + # @param [Module] klass Module whose singleton methods to wrap. + # @return [Array] def rescue(klass) DynamicRescue.rescue_from klass.singleton_methods, klass, GeniusExceptionSuperClass do |e| puts "Error description: #{e.msg}\nException type: #{e.exception_type}" @@ -138,11 +135,13 @@ def rescue(klass) end end - # @param [Object] meths List of methods to redefine. - # @param [Object] klass Class name of structure - module/class/etc. - # @param [Object] exception Exception class. - # @param [Proc] handler Body of rescue block. - # @return [Object] + # Redefines each method in +meths+ on +klass+ to rescue +exception+ and yield to the block. + # + # @param [Array] meths Method names to wrap. + # @param [Module] klass Module to redefine methods on. + # @param [Module] exception Exception class to rescue. + # @raise [StandardError] + # @return [Array] def rescue_from(meths, klass, exception, &) meths.each do |meth| old = klass.singleton_method(meth) @@ -157,6 +156,12 @@ def rescue_from(meths, klass, exception, &) end class << self + # Validates the access token by checking length and making a test request to the API. + # + # @param [String?] token Token to validate. + # @param [String?] method_name Optional method name for error hints. + # @raise [StandardError] if +token+ is nil, wrong length, or invalid. + # @return [void] def validate_token(token, method_name: nil) raise TokenError.new(method_name: method_name) if token.nil? || token.size != 64 @@ -165,36 +170,13 @@ def validate_token(token, method_name: nil) raise TokenError.new(method_name: method_name) unless status == 200 end - # +Genius::Errors.error_handle(token)+ -> true or false + # Validates token and raises on failure. Returns +true+ if valid. # - # @deprecated Since 0.2.1 - # @param [String] token Token to access https://api.genius.com. - # @param [NilClass or String] method_name Optional param to pass method - # name where exception was raised. + # @deprecated Use {.validate_token} instead. + # @param [String?] token Token to validate. + # @param [String?] method_name Optional method name for error hints. + # @raise [StandardError] if token is invalid. # @return [Boolean] - # - # @example - # begin - # Genius::Errors.validate_token(token) - # rescue Genius::Errors::TokenError => e - # puts e.message - # puts e.exception_type - # end - # This method is necessary to handle all errors during validation. - # +token+ param is not optional and it is needed to validate token - # itself. +method_name+ param optional and it to passes - # method name in error exception for dynamical error message, and - # because of unimportance this method is - # +nil+ by default. If you are ready to pass method, it will look like - # this: - # - # @example - # begin - # Genius::Errors.error_handle(token, method_name: __method__) - # rescue Genius::Errors::TokenError => e - # puts e.message - # puts e.exception_type - # end def error_handle?(token, method_name: nil) if token.nil? raise TokenError.new(msg: 'Token is required for this method. Please, add token via ' \ @@ -208,20 +190,13 @@ def error_handle?(token, method_name: nil) private - # +Genius::Errors.check_status(token)+ -> true or false - # - # @deprecated Since 0.2.1 - # This method was made to check token state. Token must be 64-sized - # string and could be validated only if response status equals 200. - # More description in {docs}[https://docs.genius.com/] and - # {api-clients page}[https://genius.com/api-clients] or in - # {TokenError documentation}[Genius::Auth.TokenError]. + # Checks if the token returns a 200 status from the API. # + # @deprecated Use {.validate_token} instead. # @private - # @param [String] token Token to access https://api.genius.com. + # @param [String] token Token to check. + # @raise [TokenError] if the response status is not 200. # @return [Boolean] - # - # @see .error_handle def check_status?(token) return false if token.size != 64 || token.nil? diff --git a/lib/genius/api/referents.rb b/lib/genius/api/referents.rb index 1130983..e67c65f 100644 --- a/lib/genius/api/referents.rb +++ b/lib/genius/api/referents.rb @@ -11,29 +11,13 @@ module Referents class << self # Endpoint of the resource ENDPOINT = "#{Api::RESOURCE}/referents".freeze - # +Genius::Referents.referents+ -> Hash + # Referents by content item or user. Pass only one of +:song_id+ and +:web_page+. # - # Referents by content item or user responsible for an included - # annotation. You may pass only one of song_id and web_page_id, not both. - # - # @param [Hash] options - # @option options [Integer] :created_by_id ID of a user to get - # referents for. - # @option options [String] :text_format Format for text bodies related - # to the document. One or more of +dom+, +plain+, and +html+, - # separated by commas (defaults to +dom+). See details of each option - # {here}[https://docs.genius.com/#response-format-h1]. - # @option options [Integer] :web_page_id ID of a web page to get - # referents for. - # @option options [Integer] :song_id ID of a song to get referents for. - # @option options [Integer] :per_page Number of results to return per - # request. - # @option options [Integer] :page Paginated offset, - # (e.g., per_page=5&page=3 returns songs 11-15). - # @raise [ArgumentError] if +song_id+ and +web_page+ are presented in - # the same scope. - # @raise [TokenError] if +token+ or +Genius::Auth.token+ are invalid. - # @return [Hash] + # @param [String?] token Token to access https://api.genius.com. + # @param [Hash] options Options: +:created_by_id+, +:text_format+, +:web_page_id+, + # +:song_id+, +:per_page+, +:page+. + # @raise [ArgumentError] if both +:song_id+ and +:web_page+ are present. + # @return [Hash, nil] def referents(token: nil, options: {}) return if token.nil? && !Auth.authorized?.nil? diff --git a/lib/genius/api/search.rb b/lib/genius/api/search.rb index 621b180..d0b9276 100644 --- a/lib/genius/api/search.rb +++ b/lib/genius/api/search.rb @@ -4,36 +4,12 @@ module Genius # +Genius::Search+ module provides methods to work with Genius search database module Search class << self - # +Genius::Search.search+ -> NilClass + # Searches Genius for songs, artists, and other content. Optionally filters results by key using +deep_find+. # - # @param [String] token Token to access https://api.genius.com. - # @param [String] query Search query. - # @param [Object] search_by Optional parameter to search by key in - # output +JSON+. - # @raise [ArgumentError] if +query+ got incorrect value. - # @raise [TokenError] if +token+ or +Genius::Auth.token+ are invalid. - # @return [String] if +search_by+ is +TrueClass+. - # @return [Hash] if +search_by+ is +FalseClass+. - # @return [NilClass] if TokenError exception raised. - # @todo: refactor example group in method documentation - # This method is a standard Genius API - # {method}[https://docs.genius.com/#search-h2] and it is needed to send - # a request to the server and get information about artists, tracks and - # everything else that may be inside the response body. According to - # https://docs.genius.com/#search-h2, token is required to be in - # response, but you can use this method without use of it! =) - # - # @example - # Genius::Search.search(query: "Ariana Grande") #=> {..., "full_title"=>" thank u, next by Ariana Grande", ...} - # - # Also, you can use this method with +search_by+ param, which is - # needed to search interested data through returned +JSON+. It uses - # +deep_find+ extension under the hood. - # - # @example - # Genius::Search.search(query: "Bones", search_by: "title") #=> ["Dirt", "HDMI", "RestInPeace", "Sodium"] - # - # @see #deep_find + # @param [String?] token Token to access https://api.genius.com. + # @param [String?] query Search query. + # @param [String?] search_by Key to filter results with {Hash#deep_find}. + # @return [Hash, String, nil] def search(token: nil, query: nil, search_by: nil) return if token.nil? && !Auth.authorized?.nil? diff --git a/lib/genius/api/songs.rb b/lib/genius/api/songs.rb index 00ba5fa..017049e 100644 --- a/lib/genius/api/songs.rb +++ b/lib/genius/api/songs.rb @@ -6,24 +6,12 @@ module Songs class << self include Genius::Errors - # +Genius::Songs.songs+ -> NilClass + # Returns song data by ID. Optionally merges lyrics from the Genius page when +combine+ is +true+. # - # This method provides info about song by its id. It is not the same - # with +Genius::Search.search+ method, because it modify a +JSON+ only - # for concrete song id, not for whole search database, which is returned - # in +Genius::Search.search+. - # - # @param [String] token Token to access https://api.genius.com. - # @param [Integer] song_id Song id. - # @raise [PageNotFound] if page is not found. - # @raise [LyricsNotFound] if output JSON is nil. - # @raise [TokenError] if +token+ or +Genius::Auth.token+ are invalid. - # @return [String] the error message if +lyrics+ param is +true+. - # @return [Hash] if +lyrics+ param is +false+. - # @return [NilClass] if TokenError exception raised. - # - # @example - # Genius::Songs.songs(song_id: 294649) #=> {"some_kind_of_hash"} + # @param [String?] token Token to access https://api.genius.com. + # @param [Integer?] song_id ID of the song. + # @param [Boolean] combine If +true+, fetches and merges lyrics into the response. + # @return [Hash, String, nil] def songs(token: nil, song_id: nil, combine: false) return if token.nil? && !Auth.authorized?.nil? @@ -36,6 +24,14 @@ def songs(token: nil, song_id: nil, combine: false) private + # Fetches the Genius HTML page for a song and merges lyrics into the API response. + # + # @private + # @param [Integer] song_id ID of the song. + # @param [Hash] response Original API response hash. + # @raise [Errors::PageNotFound] if the song page is not found. + # @raise [Errors::LyricsNotFoundError] if lyrics cannot be parsed. + # @return [Hash, String] def merge_lyrics(song_id, response) output_html = Nokogiri::HTML(HTTParty.get("https://genius.com/songs/#{song_id}")) raise Errors::PageNotFound if Errors::PageNotFound.page_not_found?(output_html) @@ -48,6 +44,12 @@ def merge_lyrics(song_id, response) "Error description: #{e.msg}\nException type: #{e.exception_type}" end + # Extracts the preloaded state JSON from the Genius page HTML. + # + # @private + # @param [Object] output_html Parsed Nokogiri HTML document. + # @raise [Errors::LyricsNotFoundError] if the preloaded state script is not found. + # @return [Hash] def parse_preloaded_state(output_html) unformed_json = output_html.css('script')[17] .text.match(/window\.__PRELOADED_STATE__\s=\sJSON.parse\('(?(?:.+?))'\);/) @@ -58,14 +60,12 @@ def parse_preloaded_state(output_html) public - # +Genius::Songs.get_lyrics+ -> Hash - # - # +Genius::Songs.get_lyrics+ method is used for extracting lyrics in - # plain text format. + # Extracts lyrics as plain text from the Genius song page. # - # @param [Integer] song_id Song id. - # @raise [ArgumentError] if +song_id+ is blank. - # @return [Hash] + # @param [Integer] song_id ID of the song. + # @raise [ArgumentError] if +song_id+ is nil. + # @raise [NoMethodError] + # @return [String] def get_lyrics(song_id) raise ArgumentError, '`song_id` should be not blank!' if song_id.nil? diff --git a/lib/genius/api/web_pages.rb b/lib/genius/api/web_pages.rb index 33df173..085e3e5 100644 --- a/lib/genius/api/web_pages.rb +++ b/lib/genius/api/web_pages.rb @@ -5,27 +5,11 @@ module Genius # be attached. Web pages map 1-to-1 with unique, canonical URLs. module WebPages class << self - # +Genius::WebPages.lookup+ -> value + # Looks up a web page by URL variants and returns Genius metadata. # - # Information about a web page retrieved by the page's full URL - # (including protocol). The returned data includes Genius's ID for the - # page, which may be used to look up associated referents with the - # {/referents}[https://docs.genius.com/#/referents-index] endpoint. - # - # Data is only available for pages that already have at least one - # annotation. - # - # Provide as many of the following variants of the URL as possible: - # @param [Hash] options - # @option options [String] :raw_annotatable_url The URL as it would - # appear in a browser. - # @option options [String] :canonical_url The URL as specified by an - # appropriate tag in a page's . - # @option options [String] :og_url The URL as specified by an - # og:url tag in a page's . - # @raise [ArgumentError] if +song_id+ is blank. - # @raise [TokenError] if +token+ or +Genius::Auth.token+ are invalid. - # @return [Hash] + # @param [String?] token Token to access https://api.genius.com. + # @param [Hash] options URL variants: +:raw_annotatable_url+, +:canonical_url+, +:og_url+. + # @return [Hash, nil] def lookup(token: nil, options: {}) return if token.nil? && !Auth.authorized?.nil?