Fullscreen/yt-core

View on GitHub
lib/yt/response.rb

Summary

Maintainability
A
2 hrs
Test Coverage
module Yt
  # @private
  class Response
    def initialize(options, &block)
      @options = options
      @block = block
    end

    def run
      instance_exec @options, &@block
    end

  private

    def get(path, params = {})
      request :get, path: path, params: params
    end

    def post(path, params = {}, body = {})
      request :post, path: path, params: params, body: body
    end

    def delete(path, params = {})
      request :delete, path: path, params: params
    end

    def request(method, options = {})
      HTTPRequest.new(request_options options.merge method: method).run
    rescue HTTPError => error
      if unauthorized?(error) && refresh_access_token
        retry
      else
        raise
      end
    end

    def request_options(options)
      options[:error_message] = ->(body) {JSON(body)['error']['message']}
      if access_token = Yt.configuration.access_token || refresh_access_token
        options[:headers] = {'Authorization' => "Bearer #{access_token}"}
      else
        options[:params] = options[:params].merge key: Yt.configuration.api_key
      end
      options
    end

    def unauthorized?(error)
      error.response.is_a? Net::HTTPUnauthorized
    end

    def refresh_access_token
      if Yt.configuration.refresh_token
        auth = Auth.find_by refresh_token: Yt.configuration.refresh_token
        auth.access_token_was_refreshed
        Yt.configuration.access_token = auth.access_token
      end
    end

    def resources_path
      @options[:item_class].name.split('::').last.gsub(/^(\w{1})(.*)/) do
        "/youtube/v3/#{$1.downcase}#{$2}s"
      end
    end

    def where_params(options)
      ids = options[:conditions].fetch(:id, []).join ','
      default_params(options).merge id: ids
    end

    def channel_playlists_params(options)
      default_params(options).merge channel_id: options[:channel_id]
    end

    def channel_videos_params(options)
      params = {channel_id: options[:channel_id], order: :date, type: :video}
      default_params(options.merge parts: %i(id)).merge params
    end

    def playlist_items_params(options)
      default_params(options).merge playlist_id: options[:playlist_id]
    end

    def video_threads_params(options)
      default_params(options).merge video_id: options[:video_id]
    end

    def thread_comments_params(options)
      default_params(options).merge parent_id: options[:parent_id]
    end

    def resource_params(options)
      default_params(options).merge id: options[:ids].join(',')
    end

    def default_params(options)
      {}.tap do |params|
        params[:max_results] = 50
        params[:part] = options[:parts].join ','
        params[:page_token] = options[:offset]
      end
    end

    def slicing_conditions_every(size, &block)
      slices = @options[:conditions].fetch(:id, []).each_slice size
      slices.inject(nil) do |response, ids|
        slice_options = @options.merge conditions: {id: ids}
        response = combine_slices response, yield(slice_options)
      end
    end

    def combine_slices(total, partial)
      if total
        total.body['items'] += partial.body['items']
        total_results = partial.body['pageInfo']['totalResults']
        total.body['pageInfo']['totalResults'] += total_results
        total
      else
        partial
      end
    end

    # Expands the resultset into a collection of videos by fetching missing
    # parts, eventually with an additional HTTP request.
    def videos_for(items, key, options)
      items.body['items'].map{|item| item['id'] = item[key]['videoId']}

      if options[:parts] == %i(id)
        items
      else
        options[:ids] = items.body['items'].map{|item| item['id']}
        options[:offset] = nil
        get('/youtube/v3/videos', resource_params(options)).tap do |response|
          response.body['nextPageToken'] = items.body['nextPageToken']
        end
      end
    end
  end
end