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. 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] @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 def each(&block) collect(&block) @elements end 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(AppStore::Caller.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(:plist => element)) when 'review' result << append_element(AppStore::UserReview.new(:plist => element)) when 'link' result << append_element(AppStore::Link.new(: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