app-store-emigrant 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG CHANGED
@@ -1,5 +1,15 @@
1
1
  CHANGELOG
2
2
 
3
+ v0.0.5
4
+ - Restructured fixtures to improve test coverage
5
+ - Finding applications by id
6
+ - Metadata caching
7
+ - Implemented loading cloud data in bulk
8
+ - Gracefully handle invalid applications in CLI
9
+
10
+ v0.0.4
11
+ - Removed autoloading mechanism as it breaks on stock Ruby for OSX
12
+
3
13
  v0.0.3
4
14
  - Support library organization structures prior to iTunes 9
5
15
  - Improved exception messages
data/Rakefile CHANGED
@@ -6,3 +6,5 @@ Rake::TestTask.new do |t|
6
6
  t.verbose = true
7
7
  t.libs << 'specs'
8
8
  end
9
+
10
+ task :default => :test
@@ -1,5 +1,6 @@
1
1
 
2
2
  require 'app-store-emigrant/app'
3
+ require 'app-store-emigrant/cache'
3
4
  require 'app-store-emigrant/cli'
4
5
  require 'app-store-emigrant/library'
5
6
  require 'app-store-emigrant/version'
@@ -21,6 +21,7 @@ module AppStore::Emigrant
21
21
  FILENAME_VERSION_REGEX = Regexp.new('([0-9.]+)(?:' + VALID_EXTENSIONS.join('|').gsub!('.', '\.') + ')')
22
22
 
23
23
  attr_reader :path
24
+ attr_writer :clouddata
24
25
 
25
26
  # Initializes application from given path
26
27
  def initialize path
@@ -44,15 +45,25 @@ module AppStore::Emigrant
44
45
  # Filename of this application (including extension)
45
46
  def filename
46
47
  unless @filename
47
- @filename = @path.basename().to_s
48
+ @filename = @path.basename.to_s
48
49
  end
49
50
 
50
51
  @filename
51
52
  end
52
53
 
53
- # Name of this application (from its metadata)
54
+ # Unique application id
55
+ def id
56
+ metadata['itemId'] rescue nil
57
+ end
58
+
59
+ # Name of this application
54
60
  def name
55
- metadata['itemName']
61
+ metadata['itemName'] rescue nil
62
+ end
63
+
64
+ # Whether this application is valid (validates metadata, id and name)
65
+ def valid?
66
+ metadata && id && name rescue false
56
67
  end
57
68
 
58
69
  # Version of this application
@@ -60,7 +71,7 @@ module AppStore::Emigrant
60
71
  unless @version
61
72
 
62
73
  # Extract version information (if available)
63
- @version = metadata['bundleShortVersionString'] || metadata['bundleVersion']
74
+ @version = metadata['bundleShortVersionString'] || metadata['bundleVersion'] rescue nil
64
75
 
65
76
  # Otherwise, use the filename
66
77
  unless @version
@@ -75,15 +86,21 @@ module AppStore::Emigrant
75
86
  # Lazily loads local metadata for this application from its iTunesMetadata.plist
76
87
  def metadata
77
88
  unless @metadata
78
- begin
79
- Zip::ZipFile.open(@path) do |zip|
80
- data = zip.file.read('iTunesMetadata.plist')
81
- plist = CFPropertyList::List.new(:data => data)
82
- @metadata = CFPropertyList.native_types(plist.value)
89
+
90
+ # Determine plist name
91
+ plist = Pathname.new(filename).basename('.ipa').to_s + '.plist'
92
+
93
+ unless Cache.has?(plist)
94
+ begin
95
+ Zip::ZipFile.open(@path) do |zip|
96
+ zip.extract('iTunesMetadata.plist', Cache.path_to(plist))
97
+ end
98
+ rescue Zip::ZipError => e
99
+ raise Invalid, e.message
83
100
  end
84
- rescue Zip::ZipError => e
85
- raise Invalid, e.message
86
101
  end
102
+
103
+ @metadata = CFPropertyList.native_types(CFPropertyList::List.new(:file => Cache.path_to(plist)).value)
87
104
  end
88
105
 
89
106
  @metadata
@@ -93,7 +110,7 @@ module AppStore::Emigrant
93
110
  # Note: Clouddata may be nil if the application was removed from the store
94
111
  def clouddata
95
112
  unless @clouddata
96
- response = Net::HTTP.get('itunes.apple.com', '/lookup?id=' + metadata['itemId'].to_s)
113
+ response = Net::HTTP.get('itunes.apple.com', '/lookup?id=' + id.to_s)
97
114
  @clouddata = JSON.parse(response)['results'].first
98
115
  end
99
116
 
@@ -0,0 +1,35 @@
1
+ require 'pathname'
2
+
3
+ module AppStore; end
4
+
5
+ module AppStore::Emigrant
6
+
7
+ # Cache mechanism
8
+ class Cache
9
+
10
+ PATH = begin
11
+ path = Pathname.new('~/.ase-cache').expand_path
12
+ unless path.directory?
13
+ path.mkpath
14
+ end
15
+ path
16
+ end
17
+
18
+ # Whether cache has this item
19
+ def self.has? item
20
+ PATH.join(item).file?
21
+ end
22
+
23
+ # Path to given item (whether existent or not)
24
+ def self.path_to item
25
+ PATH.join(item).to_s
26
+ end
27
+
28
+ # Forcefully clears the cache
29
+ def self.clear!
30
+ # TODO: Implement clearing cache
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -16,17 +16,25 @@ module AppStore::Emigrant
16
16
  # Use the default library on this system when no path is specified
17
17
  library = path ? Library.new(path) : Library.default
18
18
 
19
+ # Load cloud data in bulk
20
+ library.clouddata!
21
+
19
22
  # Loop through all applications
20
23
  library.apps.each do |app|
21
24
 
22
- # And print their name, version and whether it's outdated
23
- print app.name + ' @ ' + "v#{app.version}".foreground(app.outdated? ? :red : :green)
24
- if app.outdated?
25
- print " (v#{app.cloudversion} available in the cloud)"
25
+ if app.valid?
26
+
27
+ # Print their name, version and whether it's outdated
28
+ print app.name + ' @ ' + "v#{app.version}".foreground(app.outdated? ? :red : :green)
29
+ if app.outdated?
30
+ print " (v#{app.cloudversion} available in the cloud)"
31
+ end
32
+ else
33
+ print app.filename + ' is invalid (metadata, id or name missing)'
26
34
  end
35
+
27
36
  puts
28
37
 
29
- sleep(0.75)
30
38
  end
31
39
 
32
40
  end
@@ -1,3 +1,5 @@
1
+ require 'json'
2
+ require 'net/http'
1
3
  require 'pathname'
2
4
 
3
5
  module AppStore; end
@@ -44,6 +46,31 @@ module AppStore::Emigrant
44
46
  self
45
47
  end
46
48
 
49
+ # Populates applications' cloud data in bulk
50
+ def clouddata!
51
+
52
+ # Collect all application ids, skipping any invalid ones
53
+ ids = apps.collect do |app|
54
+ app.id
55
+ end.compact
56
+
57
+ # Queries Apple's iTunes Store API for latest cloud data using undocumented bulk method
58
+ response = Net::HTTP.get('itunes.apple.com', '/lookup?id=' + ids.join(','))
59
+ results = JSON.parse(response)['results']
60
+ results.each do |result|
61
+ if app = get(result['trackId'] || -1)
62
+ app.clouddata = result
63
+ end
64
+ end
65
+ end
66
+
67
+ # Searches for application with given id
68
+ def get id
69
+ apps.select do |app|
70
+ app.id == id
71
+ end.first
72
+ end
73
+
47
74
  # Searches for applications containing given snippet in filename
48
75
  def find snippet
49
76
  apps.select do |app|
@@ -3,6 +3,6 @@ module AppStore; end
3
3
 
4
4
  module AppStore::Emigrant
5
5
 
6
- VERSION = '0.0.4'
6
+ VERSION = '0.0.5'
7
7
 
8
8
  end
data/specs/app.rb CHANGED
@@ -11,7 +11,8 @@ describe App do
11
11
 
12
12
  before do
13
13
  @dummy = App.new(LIBRARY + '/Dummy.ipa')
14
- @outdated = App.new(LIBRARY + '/Outdated.ipa')
14
+ @gta = App.new(LIBRARY + '/GTA.ipa')
15
+ @soosiz = App.new(LIBRARY + '/Soosiz.ipa')
15
16
  end
16
17
 
17
18
  it 'must be a valid file on disk' do
@@ -27,25 +28,34 @@ describe App do
27
28
  end
28
29
 
29
30
  it 'can determine its own filename' do
30
- @dummy.filename.must_equal 'Dummy.ipa'
31
+ @gta.filename.must_equal 'GTA.ipa'
32
+ @soosiz.filename.must_equal 'Soosiz.ipa'
31
33
  end
32
34
 
33
- it 'can handle invalid structures' do
34
- lambda do
35
- @dummy.version
36
- end.must_raise App::Invalid
35
+ it 'will report invalid structures' do
36
+ @dummy.valid?.must_equal false
37
+ @dummy.id.must_equal nil
38
+ @dummy.version.must_equal nil
39
+ end
40
+
41
+ it 'can extract its name' do
42
+ @gta.name.must_equal 'Grand Theft Auto: Chinatown Wars'
43
+ @soosiz.name.must_equal 'Soosiz'
37
44
  end
38
45
 
39
46
  it 'can query local metadata' do
40
- @outdated.version.must_equal '0.9'
47
+ @gta.version.must_equal '0.9'
48
+ @soosiz.version.must_equal '1.1'
41
49
  end
42
50
 
43
- it 'can query clouddata' do
44
- @outdated.cloudversion.must_equal '1.1.0'
51
+ it 'can load cloud data' do
52
+ @gta.cloudversion.must_equal '1.1.0'
53
+ @soosiz.cloudversion.must_equal '1.3'
45
54
  end
46
55
 
47
56
  it 'can determine whether it is outdated' do
48
- @outdated.outdated?.must_equal true
57
+ @gta.outdated?.must_equal true
58
+ @soosiz.outdated?.must_equal true
49
59
  end
50
60
 
51
61
  end
@@ -6,5 +6,7 @@
6
6
  <string>0.9</string>
7
7
  <key>itemId</key>
8
8
  <integer>344186162</integer>
9
+ <key>itemName</key>
10
+ <string>Grand Theft Auto: Chinatown Wars</string>
9
11
  </dict>
10
12
  </plist>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>bundleVersion</key>
6
+ <string>1.1</string>
7
+ <key>itemId</key>
8
+ <integer>331891505</integer>
9
+ <key>itemName</key>
10
+ <string>Soosiz</string>
11
+ </dict>
12
+ </plist>
data/specs/helpers.rb CHANGED
@@ -1,3 +1,6 @@
1
1
 
2
2
  # Helper constant holding path to tests-folder
3
3
  ROOT = File.dirname __FILE__
4
+
5
+ # Clear the cache before running tests
6
+ AppStore::Emigrant::Cache.clear!
data/specs/library.rb CHANGED
@@ -22,11 +22,30 @@ describe Library do
22
22
  it 'will gracefully and lazily load applications' do
23
23
  @library.instance_variable_get('@apps').must_be_nil
24
24
  @library.apps.must_be_instance_of Array
25
- @library.apps.length.must_equal 2
25
+ @library.apps.length.must_equal 3
26
26
  end
27
27
 
28
- it 'can find applications' do
28
+ it 'can find applications by (partial) filename' do
29
29
  @library.find('Dummy').length.must_equal 1
30
+ @library.find('GTA').length.must_equal 1
31
+ @library.find('Soosiz').length.must_equal 1
32
+ end
33
+
34
+ it 'can get applications by id' do
35
+ @library.get(344186162).filename.must_equal 'GTA.ipa'
36
+ @library.get(331891505).filename.must_equal 'Soosiz.ipa'
37
+ end
38
+
39
+ it 'can load cloud data in bulk' do
40
+ gta = @library.find('GTA').first
41
+ soosiz = @library.find('Soosiz').first
42
+ gta.instance_variable_get('@clouddata').must_be_nil
43
+ soosiz.instance_variable_get('@clouddata').must_be_nil
44
+
45
+ @library.clouddata!
46
+
47
+ gta.instance_variable_get('@clouddata').must_be_instance_of Hash
48
+ soosiz.instance_variable_get('@clouddata').must_be_instance_of Hash
30
49
  end
31
50
 
32
51
  end
metadata CHANGED
@@ -1,110 +1,70 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: app-store-emigrant
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 0
8
- - 4
9
- version: 0.0.4
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ prerelease:
10
6
  platform: ruby
11
- authors:
7
+ authors:
12
8
  - Tim Kurvers
13
9
  autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
-
17
- date: 2012-04-19 00:00:00 +02:00
18
- default_executable:
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
12
+ date: 2012-05-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
21
15
  name: rubyzip
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
24
- requirements:
16
+ requirement: &70231670236280 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
25
19
  - - ~>
26
- - !ruby/object:Gem::Version
27
- segments:
28
- - 0
29
- - 9
30
- - 5
20
+ - !ruby/object:Gem::Version
31
21
  version: 0.9.5
32
22
  type: :runtime
33
- version_requirements: *id001
34
- - !ruby/object:Gem::Dependency
35
- name: rainbow
36
23
  prerelease: false
37
- requirement: &id002 !ruby/object:Gem::Requirement
38
- requirements:
24
+ version_requirements: *70231670236280
25
+ - !ruby/object:Gem::Dependency
26
+ name: rainbow
27
+ requirement: &70231670235480 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
39
30
  - - ~>
40
- - !ruby/object:Gem::Version
41
- segments:
42
- - 1
43
- - 1
44
- - 3
31
+ - !ruby/object:Gem::Version
45
32
  version: 1.1.3
46
33
  type: :runtime
47
- version_requirements: *id002
48
- - !ruby/object:Gem::Dependency
49
- name: CFPropertyList
50
34
  prerelease: false
51
- requirement: &id003 !ruby/object:Gem::Requirement
52
- requirements:
35
+ version_requirements: *70231670235480
36
+ - !ruby/object:Gem::Dependency
37
+ name: CFPropertyList
38
+ requirement: &70231670234800 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
53
41
  - - ~>
54
- - !ruby/object:Gem::Version
55
- segments:
56
- - 2
57
- - 0
58
- - 17
42
+ - !ruby/object:Gem::Version
59
43
  version: 2.0.17
60
44
  type: :runtime
61
- version_requirements: *id003
62
- - !ruby/object:Gem::Dependency
63
- name: rake
64
45
  prerelease: false
65
- requirement: &id004 !ruby/object:Gem::Requirement
66
- requirements:
67
- - - ">="
68
- - !ruby/object:Gem::Version
69
- segments:
70
- - 0
71
- version: "0"
46
+ version_requirements: *70231670234800
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: &70231670234020 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
72
55
  type: :development
73
- version_requirements: *id004
74
- - !ruby/object:Gem::Dependency
75
- name: json
76
56
  prerelease: false
77
- requirement: &id005 !ruby/object:Gem::Requirement
78
- requirements:
79
- - - ">="
80
- - !ruby/object:Gem::Version
81
- segments:
82
- - 0
83
- version: "0"
84
- type: :runtime
85
- version_requirements: *id005
86
- - !ruby/object:Gem::Dependency
87
- name: minitest
88
- prerelease: false
89
- requirement: &id006 !ruby/object:Gem::Requirement
90
- requirements:
91
- - - ">="
92
- - !ruby/object:Gem::Version
93
- segments:
94
- - 0
95
- version: "0"
96
- type: :development
97
- version_requirements: *id006
98
- description: App Store Emigrant will manually attempt to verify whether any of your local mobile applications are out of date, which iTunes - unfortunately - will refuse once you have moved countries
99
- email:
57
+ version_requirements: *70231670234020
58
+ description: App Store Emigrant will manually attempt to verify whether any of your
59
+ local mobile applications are out of date, which iTunes - unfortunately - will refuse
60
+ once you have moved countries
61
+ email:
100
62
  - tim@moonsphere.net
101
- executables:
63
+ executables:
102
64
  - ase
103
65
  extensions: []
104
-
105
66
  extra_rdoc_files: []
106
-
107
- files:
67
+ files:
108
68
  - .gitignore
109
69
  - .travis.yml
110
70
  - CHANGELOG
@@ -116,45 +76,48 @@ files:
116
76
  - bin/ase
117
77
  - lib/app-store-emigrant.rb
118
78
  - lib/app-store-emigrant/app.rb
79
+ - lib/app-store-emigrant/cache.rb
119
80
  - lib/app-store-emigrant/cli.rb
120
81
  - lib/app-store-emigrant/library.rb
121
82
  - lib/app-store-emigrant/version.rb
122
83
  - specs/app.rb
123
84
  - specs/fixtures/dummy-library/Dummy.ipa
85
+ - specs/fixtures/dummy-library/GTA-iTunesMetadata.plist
86
+ - specs/fixtures/dummy-library/GTA.ipa
124
87
  - specs/fixtures/dummy-library/Invalid.extension
125
- - specs/fixtures/dummy-library/Outdated.ipa
126
- - specs/fixtures/dummy-library/iTunesMetadata.plist
88
+ - specs/fixtures/dummy-library/Soosiz-iTunesMetadata.plist
89
+ - specs/fixtures/dummy-library/Soosiz.ipa
127
90
  - specs/helpers.rb
128
91
  - specs/library.rb
129
- has_rdoc: true
130
92
  homepage: https://github.com/timkurvers/app-store-emigrant
131
93
  licenses: []
132
-
133
94
  post_install_message:
134
95
  rdoc_options: []
135
-
136
- require_paths:
96
+ require_paths:
137
97
  - lib
138
- required_ruby_version: !ruby/object:Gem::Requirement
139
- requirements:
140
- - - ">="
141
- - !ruby/object:Gem::Version
142
- segments:
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ segments:
143
105
  - 0
144
- version: "0"
145
- required_rubygems_version: !ruby/object:Gem::Requirement
146
- requirements:
147
- - - ">="
148
- - !ruby/object:Gem::Version
149
- segments:
106
+ hash: -4548139282794052788
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ! '>='
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ segments:
150
114
  - 0
151
- version: "0"
115
+ hash: -4548139282794052788
152
116
  requirements: []
153
-
154
117
  rubyforge_project: app-store-emigrant
155
- rubygems_version: 1.3.6
118
+ rubygems_version: 1.8.17
156
119
  signing_key:
157
120
  specification_version: 3
158
- summary: App Store Emigrant will manually attempt to verify whether any of your local mobile applications are out of date
121
+ summary: App Store Emigrant will manually attempt to verify whether any of your local
122
+ mobile applications are out of date
159
123
  test_files: []
160
-