class BBC::Redux::Client

Redux API Client

@example Initialize client with either username and password or token

client = BBC::Redux::Client.new({
  :username => 'username',
  :password => 'password',
})

client = BBC::Redux::Client.new :token => 'some-token'

@example Using the client to retrieve data

client.asset('5966413090093319525') #=> BBC::Redux::Asset
client.channel_categories           #=> Array<BBC::Redux::ChannelCategory>
client.channels                     #=> Array<BBC::Redux::Channel>
client.schedule(Date.today)         #=> Array<BBC::Redux::Asset>
client.search(:name => 'Pingu')     #=> BBC::Redux::SearchResults
client.user                         #=> BBC::Redux::User

@example Call logout once finished to destroy your session

client.logout

@author Matt Haynes <matt.haynes@bbc.co.uk>

Attributes

host[R]

@!attribute [r] host @return [String] API HTTP host

http[R]

@!attribute [r] http @return [Object] http client, by default this is Typhoeus

token[R]

@!attribute [r] token @return [String] token for current session

Public Class Methods

new(options = {}) click to toggle source

Client must be initialized with either a username and password combination or a token

@param [Hash] options the options to create client with @option options [String] :username username of a redux account @option options [String] :password password of a redux account @option options [String] :token token for an existing redux session @option options [String] :host (i.bbcredux.com) api host @option options [Object] :http (Typhoeus) The http client, can be

overidden but expects method .post to return an object looking like
Typhoeus::Response (code, headers, body, etc)

@raise [ArgumentError] if you provide neither :username / :password nor

:token keys

@raise [AccountCompromisedException] if the account has been flagged as

compromised

@raise [ForbiddenException] if your username or password are incorrect @raise [HttpException] if backend fails

# File lib/bbc/redux/client.rb, line 84
def initialize(options = {})
  @host  = options[:host] || 'https://i.bbcredux.com'
  @http  = options[:http] || Typhoeus
  @token = options[:token] || begin
    username = options[:username]
    password = options[:password]

    if username && password
      data = data_for(:login, {
        :username => username, :password => password
      })

      if data['compromised']
        raise AccountCompromisedException
      end

      @token = data.fetch('token')
    else
      err = 'Supply either :token or :username and :password options'
      raise ArgumentError.new(err)
    end
  end
end

Public Instance Methods

==(other_client) click to toggle source

@return [Boolean] true if other_clinet is a redux client with the same

token, host and http
# File lib/bbc/redux/client.rb, line 256
def ==(other_client)
  self.class == other_client.class && \
  self.token == other_client.token && \
  self.host  == other_client.host  && \
  self.http  == other_client.http
end
Also aliased as: eql?
asset(identifier) click to toggle source

Fetch asset object @param [String] identifier either disk reference or uuid @see BBC::Redux::Asset @return [BBC::Redux::Asset, nil] the asset

# File lib/bbc/redux/client.rb, line 112
def asset(identifier)
  rex = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\Z/

  if identifier =~ rex
    params = { :uuid => identifier }
  else
    params = { :reference => identifier }
  end

  build :asset, :using => data_for(:asset, params)
end
channel_categories() click to toggle source

Fetch available channel categories for this session @see BBC::Redux::ChannelCategory @return [Array<BBC::Redux::ChannelCategory>] array of channel categories

# File lib/bbc/redux/client.rb, line 134
def channel_categories
  build :channel_categories, :using => data_for(:channel_categories)
end
channels() click to toggle source

Fetch available channels for this session @see BBC::Redux::Channel @return [Array<BBC::Redux::Channel>] array of channels

# File lib/bbc/redux/client.rb, line 127
def channels
  build :channels, :using => data_for(:channels)
end
eql?(other_client)
Alias for: ==
logout() click to toggle source

Logout of redux, invalidates your token. After calling this you cannot make any further requests with this client @return [nil]

# File lib/bbc/redux/client.rb, line 141
def logout
  data_for(:logout)
  return nil
end
schedule(date, channels = nil) click to toggle source

Return all programmes for the schedule date, everything from 0600 for 24 hours afterwards. May make multiple requests to backend to retreive all the data.

@param [Date,DateTime,Time] date query this schedule date @param [String,Array<String>,Channel,Array<Channel>,nil] channel

optionally limit schedule query to one or an array of channels

@return [Array<BBC::Redux::Asset>] the list of assets broadcast on date

# File lib/bbc/redux/client.rb, line 154
def schedule(date, channels = nil)

  date    = DateTime.parse date.strftime('%Y-%m-%dT06:00:00Z00:00')

  query   = {
    :after      => date.strftime('%Y-%m-%dT%H:%M:%S'),
    :before     => ( date + 1 ).strftime('%Y-%m-%dT05:59:59'),
    :channel    => channels,
    :offset     => 0,
    :limit      => 100,
    :sort_by    =>'time',
    :sort_order => 'ascending',
    :repeats    => true,
  }

  results = search(query)

  assets  = [ ]

  while true do

    assets.concat(results.assets)

    if results.has_more?
      next_query = results.query.merge({
        :offset => results.query[:offset] + 100
      })

      results = self.search(next_query)
    else
      break
    end
  end

  assets
end
user() click to toggle source

Fetch user object for current session @see BBC::Redux::User @return [BBC::Redux::User, nil] the user

# File lib/bbc/redux/client.rb, line 250
def user
  build :user, :using => data_for(:user)
end

Private Instance Methods

build(type, options) click to toggle source

@private

# File lib/bbc/redux/client.rb, line 268
def build(type, options)
  data = options.fetch(:using)

  case type
  when :asset
    Serializers::Asset.new(Asset.new).from_hash(data)
  when :channels
    Serializers::Channels.new([]).from_hash(data)
  when :channel_categories
    Serializers::ChannelCategories.new([]).from_hash(data)
  when :search_results
    Serializers::SearchResults.new(SearchResults.new).from_hash(data)
  when :user
    Serializers::User.new(User.new).from_hash(data)
  end
end
data_for(action, params = {}) click to toggle source

@private

# File lib/bbc/redux/client.rb, line 306
def data_for(action, params = {})
  url  = url_for action

  # Patch typhoeus / ethon's handling of array params, essentially
  # turn this /?key[0]=1&key[1]=2&key[2]=3 into this
  # /?key=1&key=2&key=3

  arrays = params.select { |_,v| v.class == Array }

  unless arrays.empty?
    url += '?' unless url =~ /\?$/

    arrays.each do |key, values|
      url += values.map { |v| "#{key}=#{v}" }.join('&')
    end
  end

  params = params.select { |_,v| v.class != Array }

  resp = http.post(url, {
    :body           => params.merge(:token => token).delete_if {|k,v| v.nil?},
    :followlocation => true,
  })

  case resp.code
  when 200
    JSON.parse(resp.body)
  when 403
    raise ForbiddenException.new("403 response for #{url}")
  when 404
    raise NotFoundException.new("404 response for #{url}")
  when 400..599
    raise HttpException.new("#{resp.code} response for #{url}")
  else
    raise "Umm, not sure how to handle #{resp.code} for #{url}"
  end

rescue JSON::ParserError => e
  raise JsonParseException.new("Error parsing #{url}, #{e.message}")
end
url_for(action) click to toggle source

@private

# File lib/bbc/redux/client.rb, line 286
def url_for(action)
  case action
  when :asset
    host + '/asset/details'
  when :channels
    host + '/asset/channel/available'
  when :channel_categories
    host + '/asset/channel/categories'
  when :login
    host + '/user/login'
  when :logout
    host + '/user/logout'
  when :search_results
    host + '/asset/search'
  when :user
    host + '/user/details'
  end
end