android_app_index 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/Gemfile.lock +10 -0
- data/android_app_index.gemspec +4 -1
- data/lib/android_app_index.rb +92 -20
- data/lib/android_app_index/version.rb +1 -1
- metadata +30 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e020f3f623e4bb69b81c5e18f7fee22f6f73c7a
|
4
|
+
data.tar.gz: b7ac8d2205abe7834ec827758726df2e4c46015d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 30c137c6f3c67411eb15e259df03fbb755ae6bc6b62f45c3c0fb0c3fa8f369ab9cbfa3979cbd8a37c337cf090f4f04ac532a16eaf76ea539fec9aa5bfdd15889
|
7
|
+
data.tar.gz: 180c2ea98451dabe8aa99a1cee83386a68680255c4f7c3d93e695effbdc7f61d6295744444f25ce1c9f2cee65825b7c95db30f5b46831497dba0fa2b338d4e53
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -2,11 +2,21 @@ PATH
|
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
4
|
android_app_index (0.1.0)
|
5
|
+
algoliasearch (= 1.19.1)
|
6
|
+
nokogiri (= 1.8.1)
|
5
7
|
|
6
8
|
GEM
|
7
9
|
remote: https://rubygems.org/
|
8
10
|
specs:
|
11
|
+
algoliasearch (1.19.1)
|
12
|
+
httpclient (~> 2.8.3)
|
13
|
+
json (>= 1.5.1)
|
9
14
|
diff-lcs (1.3)
|
15
|
+
httpclient (2.8.3)
|
16
|
+
json (2.0.4)
|
17
|
+
mini_portile2 (2.3.0)
|
18
|
+
nokogiri (1.8.1)
|
19
|
+
mini_portile2 (~> 2.3.0)
|
10
20
|
rake (10.5.0)
|
11
21
|
rspec (3.7.0)
|
12
22
|
rspec-core (~> 3.7.0)
|
data/android_app_index.gemspec
CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
|
|
11
11
|
|
12
12
|
spec.summary = 'Index and search android app details'
|
13
13
|
spec.description = 'Search and store <package, name, icon_url> in Algolia'
|
14
|
-
spec.homepage =
|
14
|
+
spec.homepage = 'https://reactiverobot.com'
|
15
15
|
spec.license = 'MIT'
|
16
16
|
|
17
17
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
@@ -21,6 +21,9 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
22
|
spec.require_paths = ['lib']
|
23
23
|
|
24
|
+
spec.add_runtime_dependency 'algoliasearch', '= 1.19.1'
|
25
|
+
spec.add_runtime_dependency 'nokogiri', '= 1.8.1'
|
26
|
+
|
24
27
|
spec.add_development_dependency 'bundler', '~> 1.16'
|
25
28
|
spec.add_development_dependency 'rake', '~> 10.0'
|
26
29
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
data/lib/android_app_index.rb
CHANGED
@@ -1,30 +1,102 @@
|
|
1
1
|
require 'android_app_index/version'
|
2
2
|
require 'algoliasearch'
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'open-uri'
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
# Note that we use the singleton instance of the Algolia client.
|
7
|
+
# To use the instance version, we use Algolia::Client.new(application_id, api_key)
|
8
|
+
# and pass that Client to Algolia::Index. This is an interesting decision for
|
9
|
+
# the algolia api to make. It makes a simple example very easy but actually
|
10
|
+
# removes any opinion about how the api should be used. It seems easy enough for
|
11
|
+
# users to create a singleton if they want so this seems like a poor API choice on
|
12
|
+
# their part.
|
13
|
+
Algolia.init application_id: ENV['ANDROID_APP_INDEX_ALGOLIA_APP_ID'],
|
14
|
+
api_key: ENV['ANDROID_APP_INDEX_ALGOLIA_API_KEY']
|
15
|
+
|
16
|
+
class PlayStoreParser
|
17
|
+
private def get_page(package)
|
18
|
+
Nokogiri::HTML(open("https://play.google.com/store/apps/details?id=#{package}&hl=en"))
|
19
|
+
end
|
20
|
+
|
21
|
+
private def get_icon_url(page)
|
22
|
+
images = page.search('img.cover-image').map do |node|
|
23
|
+
{ alt: node.attribute('alt').value, url: "https:#{node.attribute('src').value}" }
|
24
|
+
end
|
25
|
+
|
26
|
+
images.select { |image| image[:alt] == 'Cover art' }[0][:url]
|
27
|
+
end
|
28
|
+
|
29
|
+
# TODO: Figure out how to properly handle charset.
|
30
|
+
private def replace_dash(name)
|
31
|
+
name.gsub('–', '-')
|
32
|
+
end
|
33
|
+
|
34
|
+
private def get_name(page)
|
35
|
+
replace_dash page.search('div.id-app-title').first.inner_html
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse_play_store(package)
|
39
|
+
page = get_page package
|
40
|
+
|
41
|
+
{
|
42
|
+
icon_url: get_icon_url(page),
|
43
|
+
name: get_name(page),
|
44
|
+
package: package,
|
45
|
+
}
|
8
46
|
end
|
9
47
|
end
|
10
48
|
|
49
|
+
|
11
50
|
module AndroidAppIndex
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
51
|
+
@play_store_parser = PlayStoreParser.new
|
52
|
+
@index = Algolia::Index.new('android_apps')
|
53
|
+
|
54
|
+
@hit_to_result = lambda { |hit|
|
55
|
+
{
|
56
|
+
name: hit['name'],
|
57
|
+
package: hit['package'],
|
58
|
+
icon_url: hit['icon_url']
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
@index_and_parse_play_store = lambda { |package, name, icon_url|
|
63
|
+
app_data = {
|
64
|
+
package: package,
|
65
|
+
icon_url: icon_url,
|
66
|
+
name: name
|
67
|
+
}
|
68
|
+
|
69
|
+
needs_details = name.nil? || icon_url.nil?
|
70
|
+
app_data = @play_store_parser.parse_play_store(package) if needs_details
|
71
|
+
|
72
|
+
app_data[:objectID] = package
|
73
|
+
@index.add_object(app_data)
|
74
|
+
app_data
|
75
|
+
}
|
76
|
+
|
77
|
+
def self.get(package)
|
78
|
+
@index.get_object(package)
|
79
|
+
rescue Algolia::AlgoliaProtocolError
|
80
|
+
# Handle the error that occurs if the app is not yet indexed.
|
81
|
+
index package
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.index(package, name = nil, icon_url = nil)
|
85
|
+
@index_and_parse_play_store.call(package, name, icon_url)
|
86
|
+
rescue OpenURI::HTTPError
|
87
|
+
{
|
88
|
+
error: 'no_app_found',
|
89
|
+
error_message: "Unable to find an app with package=#{package}"
|
90
|
+
}
|
27
91
|
end
|
28
|
-
end
|
29
92
|
|
93
|
+
def self.search(query)
|
94
|
+
search_results = @index.search(
|
95
|
+
query,
|
96
|
+
attributesToRetrieve: 'package,icon_url,name',
|
97
|
+
hitsPerPage: 100
|
98
|
+
)
|
30
99
|
|
100
|
+
search_results['hits'].map(&@hit_to_result)
|
101
|
+
end
|
102
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: android_app_index
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Dailey
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-01-
|
11
|
+
date: 2018-01-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: algoliasearch
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.19.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.19.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nokogiri
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.8.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.8.1
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: bundler
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -60,10 +88,6 @@ extensions: []
|
|
60
88
|
extra_rdoc_files: []
|
61
89
|
files:
|
62
90
|
- ".gitignore"
|
63
|
-
- ".idea/android_app_index.iml"
|
64
|
-
- ".idea/misc.xml"
|
65
|
-
- ".idea/modules.xml"
|
66
|
-
- ".idea/workspace.xml"
|
67
91
|
- ".rspec"
|
68
92
|
- ".travis.yml"
|
69
93
|
- CODE_OF_CONDUCT.md
|