require 'app_store'
# Represents a list based on data from Apple AppStore.
# If a list contains too much elements (> 25), the Apple AppStore
# sends only 24 elements followed by a link for the next 24 elements.
# This class represents an abstraction of Apple AppStore lists, have
# a real count attribute and is enumerable over the entire list.
# = Example
# list = Category.featured.first.items
# list.count # => 23453
# list.elements # => [AppStore::Category, AppStore::Category, ...]
# list.collect {|item| item.item_id} # => [9843509, 9028423, 8975435, 987345, ...]
#
class AppStore::List
include Enumerable
# All the elements already gathered.
attr_reader :elements
# A required attribute :element_initializer can be used
# to specify a block used for the initialization of each element
# of the list
def initialize(attrs = {})
@element_initializer = attrs[:element_initializer]
@element_type = attrs[:element_type]
@client = attrs[:client] || AppStore::Client.new
@elements ||= []
process_new_elements attrs[:list]
end
# Returns the real elements count, not a count based on the elements
# already fetched.
def count
@count ||= @elements.count
end
# Iterates the given block using each object in the list.
def each(&block)
collect(&block)
@elements
end
# Iterates the given block using each object in the list,
# returns an array containing the execution block result for each element.
def collect
# First, iterate through already fetched elements
result = @elements.collect {|element| yield element}
# Then, iterate until we have no more links to follow
while (last_elements = fetch_next_elements) do
result += last_elements.collect {|element| yield element}
end
# Returns full array
result
end
private
# Fetch next elements using link, append them to @elements
# Return last fetched elements if any, nil otherwise
def fetch_next_elements
return nil if @link_to_next_elements.nil?
process_new_elements(@client.get(@link_to_next_elements)['items'])
end
def process_new_elements(new_elements)
result = []
@link_to_next_elements = nil
new_elements.each do |element|
case element['type']
when @element_type
result << initialize_element_and_append(element)
when 'software'
result << append_element(AppStore::Application.new(:client => @client, :plist => element))
when 'review'
result << append_element(AppStore::UserReview.new(:client => @client, :plist => element))
when 'link'
result << append_element(AppStore::Link.new(:client => @client, :plist => element))
when 'more'
@count = element['total-items']
@link_to_next_elements = element['url']
when 'review-header'
;
else
raise "unsupported type" unless @element_initializer
end
end
result
end
# Initialize given element using @element_initializer block if given,
# append block execution result to @elements and return it.
def initialize_element_and_append(element)
append_element @element_initializer[element]
end
def append_element(element)
@elements << element
element
end
end