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?