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 +10 -0
- data/Rakefile +2 -0
- data/lib/app-store-emigrant.rb +1 -0
- data/lib/app-store-emigrant/app.rb +29 -12
- data/lib/app-store-emigrant/cache.rb +35 -0
- data/lib/app-store-emigrant/cli.rb +13 -5
- data/lib/app-store-emigrant/library.rb +27 -0
- data/lib/app-store-emigrant/version.rb +1 -1
- data/specs/app.rb +20 -10
- data/specs/fixtures/dummy-library/{iTunesMetadata.plist → GTA-iTunesMetadata.plist} +2 -0
- data/specs/fixtures/dummy-library/GTA.ipa +0 -0
- data/specs/fixtures/dummy-library/Soosiz-iTunesMetadata.plist +12 -0
- data/specs/fixtures/dummy-library/Soosiz.ipa +0 -0
- data/specs/helpers.rb +3 -0
- data/specs/library.rb +21 -2
- metadata +67 -104
- data/specs/fixtures/dummy-library/Outdated.ipa +0 -0
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
data/lib/app-store-emigrant.rb
CHANGED
@@ -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
|
48
|
+
@filename = @path.basename.to_s
|
48
49
|
end
|
49
50
|
|
50
51
|
@filename
|
51
52
|
end
|
52
53
|
|
53
|
-
#
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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=' +
|
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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
print "
|
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|
|
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
|
-
@
|
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
|
-
@
|
31
|
+
@gta.filename.must_equal 'GTA.ipa'
|
32
|
+
@soosiz.filename.must_equal 'Soosiz.ipa'
|
31
33
|
end
|
32
34
|
|
33
|
-
it '
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
@
|
47
|
+
@gta.version.must_equal '0.9'
|
48
|
+
@soosiz.version.must_equal '1.1'
|
41
49
|
end
|
42
50
|
|
43
|
-
it 'can
|
44
|
-
@
|
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
|
-
@
|
57
|
+
@gta.outdated?.must_equal true
|
58
|
+
@soosiz.outdated?.must_equal true
|
49
59
|
end
|
50
60
|
|
51
61
|
end
|
Binary file
|
@@ -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>
|
Binary file
|
data/specs/helpers.rb
CHANGED
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
|
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
|
-
|
5
|
-
|
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
|
-
|
18
|
-
|
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
|
-
|
23
|
-
|
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
|
-
|
38
|
-
|
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
|
-
|
52
|
-
|
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
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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/
|
126
|
-
- specs/fixtures/dummy-library/
|
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
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
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
|
-
|
145
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
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
|
-
|
115
|
+
hash: -4548139282794052788
|
152
116
|
requirements: []
|
153
|
-
|
154
117
|
rubyforge_project: app-store-emigrant
|
155
|
-
rubygems_version: 1.
|
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
|
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
|
-
|
Binary file
|