pod_ident 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +52 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +20 -0
- data/.ruby-version +1 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +44 -0
- data/README.md +80 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/parse-rules +8 -0
- data/bin/setup +8 -0
- data/lib/detection_rules.yml +978 -0
- data/lib/pod_ident/detection_result.rb +24 -0
- data/lib/pod_ident/detection_rules.rb +3 -0
- data/lib/pod_ident/rule_parser.rb +60 -0
- data/lib/pod_ident/version.rb +5 -0
- data/lib/pod_ident.rb +108 -0
- data/pod_ident.gemspec +33 -0
- metadata +134 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PodIdent
|
4
|
+
class DetectionResult
|
5
|
+
attr_reader :app, :user_agent, :rule
|
6
|
+
attr_accessor :platform
|
7
|
+
|
8
|
+
def initialize(rule, user_agent)
|
9
|
+
@user_agent = user_agent
|
10
|
+
return unless rule
|
11
|
+
|
12
|
+
@rule = rule
|
13
|
+
@app = rule[:app]
|
14
|
+
end
|
15
|
+
|
16
|
+
def positive?
|
17
|
+
!@app.nil?
|
18
|
+
end
|
19
|
+
|
20
|
+
def platform_rule
|
21
|
+
@rule[:platform]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,3 @@
|
|
1
|
+
# DO NOT EDIT THIS FILE - it gets automatically generated by running "bin/parse-rules"
|
2
|
+
|
3
|
+
RULES = [{:app=>"Apple Podcasts", :match=>{"startsWith"=>"AppleCoreMedia"}, :platform=>{"regex"=>"\\((\\w+\\s*\\w*)", "replacements"=>[{"name"=>"Macintosh", "replaceWith"=>"Mac"}]}}, {:app=>"Apple Podcasts", :match=>{"startsWith"=>"itunesstored"}, :platform=>{"regex"=>"(iPad|iPod|iPhone)"}}, {:app=>"iTunes", :match=>{"startsWith"=>"iTunes", "excluding"=>{"regex"=>"Downcast|iCatcher|SqueezeCenter|SqueezeNetwork|MusicServer"}}, :platform=>{"regex"=>"\\((\\w+\\s*\\w*)", "replacements"=>[{"name"=>"Macintosh", "replaceWith"=>"Mac"}]}}, {:app=>"Apple Podcasts", :match=>{"includes"=>"watchOS"}, :platform=>{"text"=>"watchOS"}}, {:app=>"CastBox", :match=>{"startsWith"=>["CastBox", "Castbox"]}, :platform=>{"regexes"=>["(Android|iOS|OS\\sVersion)", "(CastBox)"], "replacements"=>[{"name"=>"OS Version", "replaceWith"=>"iOS"}, {"name"=>"CastBox", "replaceWith"=>"Android"}]}}, {:app=>"Amazon Alexa", :match=>{"startsWith"=>"Alexa"}, :platform=>{"text"=>"Alexa-capable device"}}, {:app=>"Amazon Alexa", :match=>{"startsWith"=>"Echo"}, :platform=>{"text"=>"Amazon Echo", "userAgents"=>[{"userAgent"=>"Echo/1.0(APNG)", "platform"=>"Amazon Echo"}]}}, {:app=>"Deezer", :match=>{"startsWith"=>"Deezer"}, :platform=>{"regexes"=>["(Android|Darwin)", "\\((\\w+\\s*\\w*)"], "replacements"=>[{"name"=>"osx", "replaceWith"=>"Mac"}, {"name"=>"Darwin", "replaceWith"=>"Apple Device"}]}}, {:app=>"Overcast", :match=>{"startsWith"=>"Overcast"}, :platform=>{"text"=>"iOS"}}, {:app=>"Radio.net", :match=>{"startsWith"=>["radio.net", "radio.de", "radio.at", "radio.fr", "radio.dk", "radio.es", "radio.it", "radio.pt", "radio.pl"]}, :platform=>{"regex"=>"(Android|Darwin)", "replacements"=>[{"name"=>"Darwin", "replaceWith"=>"iOS"}]}}, {:app=>"PocketCasts", :match=>{"startsWith"=>["PocketCasts", "Pocket Casts", "Shifty Jelly Pocket Casts"]}, :platform=>{"regex"=>"(Android)"}}, {:app=>"Himalaya", :match=>{"startsWith"=>"Himalaya"}, :platform=>{"regex"=>"(Darwin|Android)", "replacements"=>[{"name"=>"Darwin", "replaceWith"=>"iOS"}]}}, {:app=>"ExoPlayer", :match=>{"startsWith"=>["ExoPlayer", "yourApplicationName", "null", "md5d42223d6ee7473da82e8136ffb794439.App"]}, :platform=>{"text"=>"Android"}}, {:app=>"Download Manager", :match=>{"startsWith"=>"AndroidDownloadManager"}, :platform=>{"text"=>"Android"}}, {:app=>"Castamatic", :match=>{"startsWith"=>"Castamatic"}, :platform=>{"text"=>"iOS"}}, {:app=>"The Podcast App (podcast.app)", :match=>{"includes"=>"The Podcast App"}, :platform=>{"text"=>"iOS"}}, {:app=>"CastMix", :match=>{"startsWith"=>"CastMix"}, :platform=>{"text"=>"Android"}}, {:app=>"Unknown App", :match=>{"startsWith"=>"okhttp"}, :platform=>{"text"=>"Android"}}, {:app=>"Stagefright Media Playback Engine", :match=>{"includes"=>"stagefright", "excluding"=>{"text"=>"stagefright alternative"}}, :platform=>{"regex"=>"(Fire OS|Android)"}}, {:app=>"LG Player", :match=>{"startsWith"=>"Player/LG Player", "includes"=>["LG Player", "LG-Player"]}, :platform=>{"text"=>"Android"}}, {:app=>"Android Browser", :match=>{"startsWith"=>"Dalvik"}, :platform=>{"text"=>"Android"}}, {:app=>"Acast", :match=>{"startsWith"=>"Acast"}, :platform=>{"regex"=>"(Darwin|Android|Windows)", "replacements"=>[{"name"=>"Darwin", "replaceWith"=>"iOS"}]}}, {:app=>"Castro", :match=>{"startsWith"=>"Castro"}, :platform=>{"text"=>"iOS"}}, {:app=>"Breaker", :match=>{"startsWith"=>"Breaker"}, :platform=>{"regex"=>"(Darwin|Android)", "replacements"=>[{"name"=>"Darwin", "replaceWith"=>"iOS"}]}}, {:app=>"Podcast Addict", :match=>{"startsWith"=>["PodcastAddict", "Podcast Addict"]}, :platform=>{"text"=>"Android"}}, {:app=>"Podbean", :match=>{"startsWith"=>"Podbean"}, :platform=>{"regex"=>"(iOS|Android)"}}, {:app=>"Google Podcasts", :match=>{"includes"=>["GSA"], "excluding"=>{"regex"=>"iPhone|iPad"}}, :platform=>{"text"=>"Android"}}, {:app=>"Google Search App", :match=>{"includes"=>["GSA"], "excluding"=>{"regex"=>"Android"}}, :platform=>{"regexes"=>["(iPhone|iPad)"]}}, {:app=>"Google Podcasts", :match=>{"includes"=>"GoogleChirp"}, :platform=>{"text"=>"Google Smart Speaker"}}, {:app=>"Stitcher", :match=>{"startsWith"=>"Stitcher"}, :platform=>{"regex"=>"(iOS|Android)"}}, {:app=>"TuneIn", :match=>{"startsWith"=>"TuneIn"}, :platform=>{"regex"=>"(Darwin|Android)", "replacements"=>[{"name"=>"Darwin", "replaceWith"=>"iOS"}]}}, {:app=>"PodCruncher", :match=>{"startsWith"=>"PodCruncher"}, :platform=>{"text"=>"iOS"}}, {:app=>"iCatcher!", :match=>{"startsWith"=>"iCatcher!", "includes"=>"iCatcher!"}, :platform=>{"regex"=>"\\((iPhone|iPad|iPod touch)", "fallback"=>"iOS"}}, {:app=>"Castaway", :match=>{"startsWith"=>"Castaway"}, :platform=>{"text"=>"iOS"}}, {:app=>"Instacast", :match=>{"startsWith"=>"Instacast"}, :platform=>{"text"=>"Apple Device"}}, {:app=>"VLC", :match=>{"startsWith"=>["VLC", "LibVLC"], "includes"=>"VLC"}, :platform=>{"regex"=>"(Android|iPhone)"}}, {:app=>"Podcast Republic", :match=>{"startsWith"=>"PodcastRepublic"}, :platform=>{"text"=>"Android"}}, {:app=>"DoggCatcher", :match=>{"includes"=>"DoggCatcher"}, :platform=>{"text"=>"Android"}}, {:app=>"Player FM", :match=>{"startsWith"=>["Player FM", "Player%20FM"]}, :platform=>{"regex"=>"(Darwin)", "fallback"=>"Android", "replacements"=>[{"name"=>"Darwin", "replaceWith"=>"iOS"}]}}, {:app=>"Podkicker", :match=>{"startsWith"=>"Podkicker"}, :platform=>{"text"=>"Android"}}, {:app=>"AntennaPod", :match=>{"startsWith"=>"AntennaPod"}, :platform=>{"text"=>"Android"}}, {:app=>"Downcast", :match=>{"startsWith"=>"Downcast", "includes"=>"Downcast"}, :platform=>{"regex"=>"\\((iPhone|iPad|iPod touch|Mac)"}}, {:app=>"gPodder", :match=>{"startsWith"=>"gPodder"}, :platform=>{"regex"=>"(Linux|Windows)"}}, {:app=>"Podcatcher Deluxe", :match=>{"includes"=>"Podcatcher Deluxe"}, :platform=>{"text"=>"Android"}}, {:app=>"Procast", :match=>{"startsWith"=>["Procast", "ProCast"]}, :platform=>{"text"=>"iOS"}}, {:app=>"RSSRadio", :match=>{"startsWith"=>"RSSRadio"}, :platform=>{"regex"=>"(iPhone|iPad|iPod touch|Darwin)", "replacements"=>[{"name"=>"Darwin", "replaceWith"=>"iOS"}], "fallback"=>"iOS"}}, {:app=>"Podcat", :match=>{"startsWith"=>"Podcat", "excluding"=>{"regex"=>"Podcatcher"}}, :platform=>{"text"=>"iOS"}}, {:app=>"Audio Now", :match=>{"startsWith"=>"AudioNow", "includes"=>"audionow"}, :platform=>{"regex"=>"(iOS|Android)"}}, {:app=>"DIE ZEIT App", :match=>{"includes"=>"ZONApp"}, :platform=>{"regex"=>"(iPhone|iPad|iPod touch|Android)"}}, {:app=>"F.A.Z Der Tag App", :match=>{"includes"=>"FAZDERTAG"}, :platform=>{"regexes"=>["(Android)", "\\((iPhone|iPad|iPod touch)"]}}, {:app=>"ANTENNE BAYERN App", :match=>{"includes"=>"AntenneBayern"}, :platform=>{"regexes"=>["(Android)", "\\((iPhone|iPad|iPod touch)"]}}, {:app=>"BuzzFeed App", :match=>{"includes"=>"buzzfeed"}, :platform=>{"regexes"=>["(Android)", "\\((iPhone|iPad|iPod touch)"]}}, {:app=>"Facebook in-app browser", :match=>{"includes"=>["FBAN", "FBAV"]}, :platform=>{"regexes"=>["\\((iPhone|iPad|iPod touch)", "(Android)"]}}, {:app=>"Instagram in-app browser", :match=>{"includes"=>"Instagram"}, :platform=>{"regexes"=>["(iPad)", "(iPhone|Android)"]}}, {:app=>"Twitter in-app browser", :match=>{"includes"=>"Twitter"}, :platform=>{"regex"=>"(iPhone|iPad|Darwin|Android)", "replacements"=>[{"name"=>"Darwin", "replaceWith"=>"Apple device"}]}}, {:app=>"Pinterest in-app browser", :match=>{"includes"=>"Pinterest"}, :platform=>{"regexes"=>["(Android)", "\\((iPhone|iPad|iPod touch)"]}}, {:app=>"Windows Media Player", :match=>{"startsWith"=>["NSPlayer", "WMPlayer"]}, :platform=>{"text"=>"Windows"}}, {:app=>"Sonos", :match=>{"includes"=>"Sonos"}, :platform=>{"text"=>"Sonos"}}, {:app=>"Internet Explorer", :match=>{"includes"=>"Trident"}, :platform=>{"regex"=>"(Windows Phone)", "fallback"=>"Windows"}}, {:app=>"Kodi Media Center", :match=>{"startsWith"=>"Kodi", "includes"=>"Kodi"}, :platform=>{"regex"=>"(X11|Android|Windows)", "replacements"=>[{"name"=>"X11", "replaceWith"=>"Linux"}]}}, {:app=>"HermesPod", :match=>{"startsWith"=>"+hermespod.com"}, :platform=>{"text"=>"Windows"}}, {:app=>"ViennaRSS", :match=>{"includes"=>"Vienna"}, :platform=>{"text"=>"Mac"}}, {:app=>"Unknown client", :match=>{"startsWith"=>"(null)"}, :platform=>{"regex"=>"(iPhone|iPad|iPod touch)"}}, {:app=>"Clementine Music Player", :match=>{"startsWith"=>"Clementine"}, :platform=>{"text"=>"Unknown"}}, {:app=>"Flipboard", :match=>{"includes"=>"Flipboard"}, :platform=>{"regexes"=>["(Android)", "\\((iPhone|iPad|iPod touch|Macintosh)"], "replacements"=>[{"name"=>"Macintosh", "replaceWith"=>"Mac"}]}}, {:app=>"iVoox", :match=>{"startsWith"=>["ivoox", "iVoox"]}, :platform=>{"regexes"=>["(Android)", "(Darwin)\\/", "\\((iPhone|iPad|iPod touch|Macintosh)"], "replacements"=>[{"name"=>"Darwin", "replaceWith"=>"Apple device"}, {"name"=>"Macintosh", "replaceWith"=>"Mac"}]}}, {:app=>"FYEO", :match=>{"startsWith"=>"FYEO"}, :platform=>{"regex"=>"(iOS|Android)"}}].freeze
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'pry'
|
5
|
+
|
6
|
+
module PodIdent
|
7
|
+
class RuleParser
|
8
|
+
RULES_YAML = File.expand_path('../detection_rules.yml', __dir__)
|
9
|
+
RULES_RUBY = File.expand_path('detection_rules.rb', __dir__)
|
10
|
+
RULES_SPEC_RUBY = File.expand_path('../../spec/detection_rules.rb', __dir__)
|
11
|
+
DO_NOT_EDIT_TEXT = <<~HEREDOC
|
12
|
+
# DO NOT EDIT THIS FILE - it gets automatically generated by running \"bin/parse-rules\"\n
|
13
|
+
HEREDOC
|
14
|
+
|
15
|
+
attr_accessor :rules
|
16
|
+
|
17
|
+
def call
|
18
|
+
parse_yaml
|
19
|
+
write_rules_rb
|
20
|
+
write_rules_spec_rb
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def write_rules_rb
|
26
|
+
cleaned_rules = rules.dup.map do |rule|
|
27
|
+
{
|
28
|
+
app: rule['app'],
|
29
|
+
match: rule['match'],
|
30
|
+
platform: rule['platform']
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
File.open(RULES_RUBY, 'w') do |file|
|
35
|
+
file.write(DO_NOT_EDIT_TEXT)
|
36
|
+
file.write("RULES = #{cleaned_rules}.freeze")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def write_rules_spec_rb
|
41
|
+
all_rules = rules.dup.map do |rule|
|
42
|
+
# symbolize keys
|
43
|
+
Hash[rule.map { |(k, v)| [k.to_sym, v] }]
|
44
|
+
end
|
45
|
+
|
46
|
+
File.open(RULES_SPEC_RUBY, 'w') do |file|
|
47
|
+
file.write(DO_NOT_EDIT_TEXT)
|
48
|
+
file.write("RULES = #{all_rules}.freeze")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse_yaml
|
53
|
+
@rules = YAML.safe_load(rules_yaml_file_content)
|
54
|
+
end
|
55
|
+
|
56
|
+
def rules_yaml_file_content
|
57
|
+
File.read(RULES_YAML).gsub('\\\\', '\\')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/pod_ident.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pod_ident/version'
|
4
|
+
require 'pod_ident/detection_rules'
|
5
|
+
require 'pod_ident/detection_result'
|
6
|
+
|
7
|
+
module PodIdent
|
8
|
+
class Detector
|
9
|
+
attr_reader :user_agent_string
|
10
|
+
attr_accessor :result
|
11
|
+
|
12
|
+
def self.detect(user_agent_string)
|
13
|
+
new(user_agent_string).detect
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(user_agent_string)
|
17
|
+
@user_agent_string = user_agent_string
|
18
|
+
end
|
19
|
+
|
20
|
+
def detect
|
21
|
+
# !~ /[^[:space:]]/ is what Active Support does to detect blank strings
|
22
|
+
return nil if user_agent_string !~ /[^[:space:]]/
|
23
|
+
|
24
|
+
self.result = DetectionResult.new(find_rule, user_agent_string)
|
25
|
+
identify_platform if result.positive?
|
26
|
+
|
27
|
+
result
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def find_rule
|
33
|
+
RULES.detect do |rule|
|
34
|
+
found = false
|
35
|
+
|
36
|
+
match = rule.fetch(:match)
|
37
|
+
|
38
|
+
found = apply_starts_with(match['startsWith'], found)
|
39
|
+
found ||= apply_includes(match['includes'], found)
|
40
|
+
|
41
|
+
excluding = match['excluding']
|
42
|
+
if found && excluding
|
43
|
+
found = false if excluding['text'] && user_agent_string.include?(excluding['text'])
|
44
|
+
|
45
|
+
if excluding['regex'] && Regexp.new(excluding['regex']).match(user_agent_string)
|
46
|
+
found = false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
found
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def apply_starts_with(starts_with, found)
|
55
|
+
return found unless starts_with
|
56
|
+
|
57
|
+
Array(starts_with).detect do |string|
|
58
|
+
found = user_agent_string.start_with?(string)
|
59
|
+
end
|
60
|
+
|
61
|
+
found
|
62
|
+
end
|
63
|
+
|
64
|
+
def apply_includes(includes, found)
|
65
|
+
return found unless includes
|
66
|
+
|
67
|
+
Array(includes).detect do |string|
|
68
|
+
found = user_agent_string.include?(string)
|
69
|
+
end
|
70
|
+
found
|
71
|
+
end
|
72
|
+
|
73
|
+
def identify_platform
|
74
|
+
result.platform = nil
|
75
|
+
|
76
|
+
regexes = if result.platform_rule['regex']
|
77
|
+
Array(result.platform_rule['regex'])
|
78
|
+
else
|
79
|
+
result.platform_rule['regexes']
|
80
|
+
end
|
81
|
+
|
82
|
+
result.platform = result.platform_rule['text'] if result.platform_rule['text']
|
83
|
+
|
84
|
+
regexes&.detect do |regex|
|
85
|
+
match = Regexp.new(regex).match(user_agent_string)
|
86
|
+
next if match.nil?
|
87
|
+
|
88
|
+
result.platform = match[1]
|
89
|
+
if result.platform_rule['replacements']
|
90
|
+
result.platform = replace_name(result.platform, result.platform_rule['replacements'])
|
91
|
+
end
|
92
|
+
true
|
93
|
+
end
|
94
|
+
|
95
|
+
result.platform = result.platform_rule['fallback'] if !result.platform && result.platform_rule['fallback']
|
96
|
+
end
|
97
|
+
|
98
|
+
def replace_name(target, replacements)
|
99
|
+
replacement = replacements.detect { |repl| repl['name'] == target }
|
100
|
+
|
101
|
+
if replacement
|
102
|
+
replacement['replaceWith']
|
103
|
+
else
|
104
|
+
target
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/pod_ident.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'pod_ident/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'pod_ident'
|
9
|
+
spec.version = PodIdent::VERSION
|
10
|
+
spec.authors = ['Podigee GmbH']
|
11
|
+
spec.email = ['hello@podigee.com']
|
12
|
+
|
13
|
+
spec.summary = 'Identifies podcast client user agents'
|
14
|
+
spec.description = <<~HEREDOC
|
15
|
+
Library to identify podcast client user agents and translate them into human readable information.'
|
16
|
+
HEREDOC
|
17
|
+
spec.homepage = 'https://www.podigee.com'
|
18
|
+
|
19
|
+
# Specify which files should be added to the gem when it is released.
|
20
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
21
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
22
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
23
|
+
end
|
24
|
+
spec.bindir = 'exe'
|
25
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
26
|
+
spec.require_paths = ['lib']
|
27
|
+
|
28
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
29
|
+
spec.add_development_dependency 'pry'
|
30
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
31
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
32
|
+
spec.add_development_dependency 'rspec_junit_formatter'
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pod_ident
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Podigee GmbH
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-03-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.16'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.16'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pry
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '13.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '13.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec_junit_formatter
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: 'Library to identify podcast client user agents and translate them into
|
84
|
+
human readable information.''
|
85
|
+
|
86
|
+
'
|
87
|
+
email:
|
88
|
+
- hello@podigee.com
|
89
|
+
executables: []
|
90
|
+
extensions: []
|
91
|
+
extra_rdoc_files: []
|
92
|
+
files:
|
93
|
+
- ".circleci/config.yml"
|
94
|
+
- ".gitignore"
|
95
|
+
- ".rspec"
|
96
|
+
- ".rubocop.yml"
|
97
|
+
- ".ruby-version"
|
98
|
+
- Gemfile
|
99
|
+
- Gemfile.lock
|
100
|
+
- README.md
|
101
|
+
- Rakefile
|
102
|
+
- bin/console
|
103
|
+
- bin/parse-rules
|
104
|
+
- bin/setup
|
105
|
+
- lib/detection_rules.yml
|
106
|
+
- lib/pod_ident.rb
|
107
|
+
- lib/pod_ident/detection_result.rb
|
108
|
+
- lib/pod_ident/detection_rules.rb
|
109
|
+
- lib/pod_ident/rule_parser.rb
|
110
|
+
- lib/pod_ident/version.rb
|
111
|
+
- pod_ident.gemspec
|
112
|
+
homepage: https://www.podigee.com
|
113
|
+
licenses: []
|
114
|
+
metadata: {}
|
115
|
+
post_install_message:
|
116
|
+
rdoc_options: []
|
117
|
+
require_paths:
|
118
|
+
- lib
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
requirements: []
|
130
|
+
rubygems_version: 3.0.3
|
131
|
+
signing_key:
|
132
|
+
specification_version: 4
|
133
|
+
summary: Identifies podcast client user agents
|
134
|
+
test_files: []
|