Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 46 additions & 20 deletions lib/app_store/application.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
require "app_store/base"
require "app_store/company"
require "app_store/user_review"
require "app_store/artwork"
require "app_store/list"
require "app_store/link"
require "app_store/image"

# Represents an application in the AppStore.
# = Available attributes:
Expand Down Expand Up @@ -54,16 +54,15 @@ class AppStore::Application < AppStore::Base

attr_reader :company, :price, :size, :artworks, :icon, :icon_thumbnail, :screenshots

plist :accepted_type => 'software',
:mapping => {
'average-user-rating' => :average_user_rating,
'user-rating-count' => :user_rating_count,
'release-date' => :release_date,
'description' => :description,
'item-id' => :item_id,
'version' => :version,
'title' => :title
}
plist :mapping => {
'average-user-rating' => :average_user_rating,
'user-rating-count' => :user_rating_count,
'release-date' => :release_date,
'description' => :description,
'item-id' => :item_id,
'version' => :version,
'title' => :title
}

# Search an Application by its <tt>id</tt>. Accepts only one <tt>id</tt> and returns an Application instance.
def self.find_by_id(id, options = {})
Expand Down Expand Up @@ -101,32 +100,59 @@ def itunes_url

def icon
if @icon.nil?
parsed = @client.itunes_get(AppStore::Client::ApplicationURL, :id => item_id)
@icon = AppStore::Image.new(:plist => parsed.search('PictureView[@height="100"][@width="100"]').first)
parsed = @client.browser_get(AppStore::Client::ApplicationURL, :id => item_id)

plist = parsed.search('PictureView[@height="100"][@width="100"]').first
@icon = AppStore::Image.new(:plist => plist) if plist
end

@icon
end

protected
def custom_init_from_plist(plist)
client = AppStore::Client.new

# Set size and price
@price = plist['store-offers']['STDQ']['price']
@size = plist['store-offers']['STDQ']['size']

# Seek for company
@company = AppStore::Company.new(:plist => plist['company'])

icons = []

# Parse artwork
@artworks = plist['artwork-urls'].collect do |plist_artwork|
# OPTIMIZE : handle default_screenshot
if plist_artwork['image-type'] and plist_artwork['default']
artwork = AppStore::Artwork.new :plist => plist_artwork
@icon_thumbnail ||= artwork.default if artwork.is_icon?
@screenshots << artwork.default unless artwork.is_icon?
artwork
@artworks = plist['artwork-urls'].collect do |plist_artwork_p|
if plist_artwork_p.include?("default@2x")
plist_artwork = plist_artwork_p["default@2x"]
elsif plist_artwork_p.include?("default")
plist_artwork = plist_artwork_p["default"]
else
plist_artwork = plist_artwork_p
end

result = AppStore::Image.new :plist => plist_artwork

icons << result if plist_artwork_p["image-type"] == "software-icon"

result
end

@artworks.compact!

@icon_thumbnail ||= @artworks.first if @artworks.size > 0

if icons.size > 0
@icon = icons.sort_by { |icon| icon.width * icon.height }.last

icon_url = @icon.url.dup
icon_url[/\d+x\d+/] = "512x512"

if icon_url != @icon.url then
result = begin; client.browser_get(icon_url); rescue Mechanize::ResponseCodeError; nil; end
@icon.url.replace(icon_url) if result
end
end
end
end
19 changes: 0 additions & 19 deletions lib/app_store/artwork.rb

This file was deleted.

23 changes: 18 additions & 5 deletions lib/app_store/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require "plist"

require "app_store"
require "app_store/plist_parser"

# Client regroups all the calling and xml parsing mechanism to call the AppStore.
class AppStore::Client
Expand Down Expand Up @@ -44,24 +45,36 @@ def initialize(args = {})
# Call the Apple AppStore using given <tt>url</tt> and passing <tt>params</tt> with an HTTP Get method.
# Call is seen as a fake iPhone iTunes client.
def iphone_get(url, params = {})
Plist::parse_xml make_call_and_handle_answer(iphone_agent, url, params).body
make_call_and_handle_answer(iphone_agent, url, params)
end

alias :get :iphone_get

# Call the Apple AppStore using given <tt>url</tt> and passing <tt>params</tt> with HTTP Get method.
# Call is seen as a fake Mac OS X iTunes client.
def itunes_get(url, params = {})
Nokogiri.parse make_call_and_handle_answer(itunes_agent, url, params).body
make_call_and_handle_answer(itunes_agent, url, params)
end

def browser_get(url, params = {})
make_call_and_handle_answer(browser_agent, url, params)
end

protected
def setup_agent(agent)
agent.pluggable_parser['text/xml'] = PlistParser
end

def iphone_agent
@iphone_agent ||= WWW::Mechanize.new { |a| a.user_agent = 'iTunes-iPhone/3.0 (2)' }
@iphone_agent ||= Mechanize.new { |a| setup_agent(a); a.user_agent = 'iTunes-iPhone/3.0 (2)' }
end

def itunes_agent
@itunes_agent ||= WWW::Mechanize.new { |a| a.user_agent = 'iTunes/9.0.1 (Macintosh; Intel Mac OS X 10.6.1) AppleWebKit/531.9' }
@itunes_agent ||= Mechanize.new { |a| setup_agent(a); a.user_agent = 'iTunes/9.0.1 (Macintosh; Intel Mac OS X 10.6.1) AppleWebKit/531.9' }
end

def browser_agent
@itunes_agent ||= Mechanize.new { |a| setup_agent(a); a.user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7.4) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5' }
end

def headers
Expand All @@ -74,7 +87,7 @@ def headers
# Make call using given <tt>agent</tt>, <tt>url</tt> and <tt>params</tt>.
# Handle answer code and returns answer if answer code was correct.
def make_call_and_handle_answer(agent, url, params)
answer = agent.get(:url => url, :headers => headers, :params => params)
answer = agent.get(url, params, nil, headers)
raise AppStore::RequestError if answer.code.to_s != '200'
answer
end
Expand Down
2 changes: 1 addition & 1 deletion lib/app_store/link.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class AppStore::Link < AppStore::Base
def destination
@destination ||= case @item_type
when 'software'
AppStore::Application.new :plist => @client.get(@url)['item-metadata'],
AppStore::Application.new :plist => @client.itunes_get(@url)['item-metadata'] || @raw,
:client => @client
else
raise 'unsupported'
Expand Down
20 changes: 20 additions & 0 deletions lib/app_store/plist_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require 'mechanize'

class PlistParser < Mechanize::File
extend Forwardable

attr_accessor :mech, :plist

def initialize(uri=nil, response=nil, body=nil, code=nil, mech=nil)
raise 'no' if mech and not Mechanize === mech
@mech = mech

super uri, response, body, code

@plist = @body ? Plist::parse_xml(@body) : nil
end

def_delegator :plist, :[], :[]
def_delegator :plist, :search, :search
end

4 changes: 2 additions & 2 deletions test/client_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ class ClientTest < Test::Unit::TestCase
end

should "returns a mechanize agent on call to iphone_agent" do
assert_kind_of WWW::Mechanize, @client.iphone_agent
assert_kind_of Mechanize, @client.iphone_agent
end

should "set mechanize user agent to iTunes-iPhone/3.0 (2) for iphone_agent" do
assert_equal 'iTunes-iPhone/3.0 (2)', @client.iphone_agent.user_agent
end

should "returns a mechanize agent on call to itunes_agent" do
assert_kind_of WWW::Mechanize, @client.itunes_agent
assert_kind_of Mechanize, @client.itunes_agent
end

should "set mechanize user agent to iTunes/9.0.1 (Macintosh; Intel Mac OS X 10.6.1) AppleWebKit/531.9 for itunes_agent" do
Expand Down