browserino 3.0.1 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +10 -0
- data/.travis.yml +6 -7
- data/bin/browserino +20 -52
- data/bin/console +5 -8
- data/browserino.gemspec +2 -6
- data/lib/browserino/client.rb +216 -0
- data/lib/browserino/config.rb +90 -0
- data/lib/browserino/definitions/aliasses.rb +12 -0
- data/lib/browserino/definitions/filters.rb +67 -0
- data/lib/browserino/definitions/labels.rb +45 -0
- data/lib/browserino/definitions/matchers.rb +290 -0
- data/lib/browserino/identity.rb +55 -0
- data/lib/browserino/integrate/action_controller.rb +4 -2
- data/lib/browserino/integrate/rails.rb +2 -0
- data/lib/browserino/methods.rb +110 -0
- data/lib/browserino/options.rb +44 -0
- data/lib/browserino/version.rb +72 -1
- data/lib/browserino.rb +16 -58
- metadata +15 -61
- data/lib/browserino/agent.rb +0 -116
- data/lib/browserino/browser.rb +0 -28
- data/lib/browserino/console.rb +0 -10
- data/lib/browserino/core/alias.rb +0 -23
- data/lib/browserino/core/helpers.rb +0 -141
- data/lib/browserino/core/lies.rb +0 -14
- data/lib/browserino/core/mapping.rb +0 -78
- data/lib/browserino/core/patterns.rb +0 -142
- data/lib/browserino/core/questions.rb +0 -88
- data/lib/browserino/core/supported.rb +0 -31
- data/lib/browserino/engine.rb +0 -16
- data/lib/browserino/operating_system.rb +0 -33
- data/lib/browserino/unknown.rb +0 -3
@@ -0,0 +1,290 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
Browserino.config.define do
|
3
|
+
# a set of global matchers that will use formatted properties found earlier
|
4
|
+
# they will also be applied to every matcher unless that matcher has it's own
|
5
|
+
# property set for the defined smart matcher
|
6
|
+
smart_match :version, with: ':name/([\d\._]+)', flags: [:i]
|
7
|
+
smart_match :engine_version, with: ':engine/([\d\._]+)', flags: [:i]
|
8
|
+
|
9
|
+
# a simple set of global matchers that will be merged
|
10
|
+
# with an identity the final client object is created
|
11
|
+
match do
|
12
|
+
locale %r{(?<!nintendo)[;\s](\w{2}(?:\-\w{2})?)[;)]}i
|
13
|
+
architecture %r{((?:(?:x|x86_|amd|wow|win)64)|i[36]86)}i
|
14
|
+
mobile %r{(bolt|nokia|samsung(?!b)|mobi(?:le)?|i?p(?:[ao]d|hone)
|
15
|
+
|android|bb\d+|blackberry|iemobile|fennec|bada|meego
|
16
|
+
|tizen|vodafone|t\-mobile|opera\sm(?:ob|in)i)}xi
|
17
|
+
platform %r{.*(xbox|wii|nintendo\sds|playstation|windows(?:\sphone)?
|
18
|
+
|macintosh|mac\sos\sx|android|tizen|ip(?:[ao]d|hone)
|
19
|
+
|blackberry|linux|ubuntu|x11|bsd|s(?:unos|olaris)
|
20
|
+
|w(?:eb)?os)}xi
|
21
|
+
platform_version %r{(?:windows(?:\sphone(?:\sos)?)?|nt|mac\sos\sx|android
|
22
|
+
|(?:cpu\s|ip(?:[ao]d|hone)\s)os|blackberry|bb
|
23
|
+
|s(?:unos|olaris)/?|w(?:eb)?os/|tizen)\s?([\d\._]+)}xi
|
24
|
+
end
|
25
|
+
|
26
|
+
# automatically set type to :browser for each defined matcher
|
27
|
+
browsers do
|
28
|
+
# a single matcher that will create an Identity for a specific match
|
29
|
+
# identities are added in an array in order of definition - higher is better
|
30
|
+
# aliasses are prepended to the list instead of appended (e.g. like blocks)
|
31
|
+
# this ensures that aliasses will be matched before any regular matcher
|
32
|
+
match %r{maxthon}i do
|
33
|
+
# define properties by calling a method with the desired name and:
|
34
|
+
# -- value # => will create a static method that returns that value
|
35
|
+
# -- /(pat)/ # => will create a method that scans the UA and
|
36
|
+
# saves the first capture group
|
37
|
+
# -- { blk } # => will create a dynamic method that returns the result
|
38
|
+
# of the block, it will be executed within an
|
39
|
+
# instantiated Client object
|
40
|
+
name :maxthon
|
41
|
+
|
42
|
+
version %r{maxthon[/\s]([\d\.]+)}i
|
43
|
+
engine %r{(webkit|presto|gecko|trident)}i
|
44
|
+
engine_version %r{(?:webkit|presto|gecko|trident)/([\d\.]+)}i
|
45
|
+
end
|
46
|
+
|
47
|
+
match %r{retawq}i do
|
48
|
+
name :retawq
|
49
|
+
locale %r{\[(\w{2}(?:\-\w{2})?)\]}i
|
50
|
+
end
|
51
|
+
|
52
|
+
match %r{lynx}i do
|
53
|
+
name :lynx
|
54
|
+
end
|
55
|
+
|
56
|
+
match %r{ucbrowser}i do
|
57
|
+
name :ucbrowser
|
58
|
+
|
59
|
+
version %r{ucbrowser/?([\d\.]+)}i
|
60
|
+
engine %r{(trident|gecko|webkit|presto)}i
|
61
|
+
engine_version %r{(?:trident|gecko|webkit|presto)/([\d\.]+)}i
|
62
|
+
end
|
63
|
+
|
64
|
+
match %r{edge}i do
|
65
|
+
name :edge
|
66
|
+
engine :edge
|
67
|
+
modern? true
|
68
|
+
end
|
69
|
+
|
70
|
+
match %r{ope?ra?\smini}i do
|
71
|
+
name :opera_mini
|
72
|
+
|
73
|
+
version %r{ope?ra?\smini/([\d\.]+)}i
|
74
|
+
engine %r{(presto|webkit)}i
|
75
|
+
engine_version %r{(?:presto|webkit)/([\d\.]+)}i
|
76
|
+
end
|
77
|
+
|
78
|
+
match %r{opera[^\w]}i do
|
79
|
+
name :opera
|
80
|
+
|
81
|
+
version %r{(?:opera[\s/]|version/)([\d\.]+)}i
|
82
|
+
engine %r{(presto|webkit)}i
|
83
|
+
engine_version %r{(?:presto|webkit)/([\d\.]+)}i
|
84
|
+
end
|
85
|
+
|
86
|
+
match %r{msie|trident}i do
|
87
|
+
name :ie
|
88
|
+
engine :trident
|
89
|
+
|
90
|
+
version %r{(?:(?:ms)?ie\s|rv:)([\d\.]+)}i
|
91
|
+
modern? { version >= 10 }
|
92
|
+
end
|
93
|
+
|
94
|
+
match %r{chrome(?:ium)?}i do
|
95
|
+
name :chrome
|
96
|
+
|
97
|
+
version %r{chrome(?:ium)?/([\d\.]+)}i
|
98
|
+
engine %r{(webkit|blink)}i
|
99
|
+
engine_version %r{(?:webkit|blink)/([\d\.]+)}i
|
100
|
+
modern? { version >= 50 }
|
101
|
+
end
|
102
|
+
|
103
|
+
match %r{firefox}i do
|
104
|
+
name :firefox
|
105
|
+
|
106
|
+
engine %r{(gecko|servo)}i
|
107
|
+
engine_version %r{(?:rv:\s?|servo/)([\d\.]+)}i
|
108
|
+
modern? { version >= 50 }
|
109
|
+
end
|
110
|
+
|
111
|
+
match %r{konqueror}i do
|
112
|
+
name :konqueror
|
113
|
+
end
|
114
|
+
|
115
|
+
match %r{midori}i do
|
116
|
+
name :midori
|
117
|
+
engine :webkit
|
118
|
+
end
|
119
|
+
|
120
|
+
match %r{safari}i do
|
121
|
+
name :safari
|
122
|
+
engine :webkit
|
123
|
+
|
124
|
+
version %r{(?:safari|version)/([\d\.]+)}i
|
125
|
+
modern? { version >= 9 }
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# automatically set type to :bot for each defined matcher
|
130
|
+
bots do
|
131
|
+
match %r{googlebot}i, name: :googlebot
|
132
|
+
match %r{yahoo\!\sslurp}i, name: :yahoo_slurp
|
133
|
+
match %r{msnbot}i, name: :msnbot
|
134
|
+
match %r{bingbot}i, name: :bingbot
|
135
|
+
match %r{baiduspider}i, name: :baiduspider
|
136
|
+
match %r{yandexbot}i, name: :yandexbot
|
137
|
+
match %r{sosospider}i, name: :sosospider
|
138
|
+
match %r{exabot}i, name: :exabot
|
139
|
+
match %r{sogou\s?spider}i, name: :sogou_spider
|
140
|
+
match %r{nutch}i, name: :nutch
|
141
|
+
match %r{scrapy}i, name: :scrapy
|
142
|
+
match %r{dataparksearch}i, name: :dataparksearch
|
143
|
+
match %r{holmes}i, name: :holmes
|
144
|
+
match %r{(?:ask)\sjeeves}i, name: :ask
|
145
|
+
match %r{duckduckgo}i, name: :duckduckgo
|
146
|
+
match %r{beslistbot}i, name: :beslistbot
|
147
|
+
match %r{twitterbot}i, name: :twitterbot
|
148
|
+
match %r{linkedinbot}i, name: :linkedin
|
149
|
+
match %r{instagram}i, name: :instagram
|
150
|
+
match %r{pinterest}i, name: :pinterest
|
151
|
+
match %r{tumblr}i, name: :tumblr
|
152
|
+
match %r{ia_archiver}i, name: :alexa
|
153
|
+
match %r{4anything}i, name: :four_anything
|
154
|
+
match %r{autoemailspider}i, name: :auto_email_spider
|
155
|
+
match %r{boston[_ ]project}i, name: :boston_project
|
156
|
+
match %r{atomz}i, name: :atomz
|
157
|
+
match %r{atomic_email_hunter}i, name: :atomic_email_hunter
|
158
|
+
match %r{altavista}i, name: :altavista
|
159
|
+
match %r{alkaline}i, name: :alkalinebot
|
160
|
+
match %r{acoi}i, name: :acoi
|
161
|
+
match %r{acoon}i, name: :acoon
|
162
|
+
match %r{accoona}i, name: :accoona
|
163
|
+
match %r{face(?:bookexternalhit|bot)}i, name: :facebook
|
164
|
+
match %r{linkdexbot}i, name: :linkdexbot
|
165
|
+
match %r{aitcsrobot}i, name: :aitcsrobot
|
166
|
+
match %r{alexibot}i, name: :alexibot
|
167
|
+
match %r{aqua_products}i, name: :aqua_products
|
168
|
+
match %r{arachnophilia}i, name: :arachnophilia
|
169
|
+
match %r{aspider}i, name: :aspider
|
170
|
+
match %r{asterias}i, name: :asterias
|
171
|
+
match %r{auresys}i, name: :auresys
|
172
|
+
match %r{b2w}i, name: :b2w
|
173
|
+
match %r{backdoorbot}i, name: :backdoorbot
|
174
|
+
match %r{backrub}i, name: :backrub
|
175
|
+
match %r{big\sbrother}i, name: :big_brother
|
176
|
+
match %r{bizbot}i, name: :bizbot
|
177
|
+
match %r{black\shole}i, name: :black_hole
|
178
|
+
match %r{blackwidow}i, name: :blackwidow
|
179
|
+
match %r{blexbot}i, name: :blexbot
|
180
|
+
match %r{bspider}i, name: :bspider
|
181
|
+
match %r{bullseye}i, name: :bullseye
|
182
|
+
match %r{emailcollector}i, name: :emailcollector
|
183
|
+
match %r{emailsiphon}i, name: :emailsiphon
|
184
|
+
match %r{emailwolf}i, name: :emailwolf
|
185
|
+
match %r{ecatch}i, name: :ecatch
|
186
|
+
match %r{flaming\sattackbot}i, name: :flaming_attackbot
|
187
|
+
match %r{getright}i, name: :getright
|
188
|
+
match %r{harvest}i, name: :harvest
|
189
|
+
match %r{huaweisymantecspider}i, name: :huaweisymantecspider
|
190
|
+
|
191
|
+
match %r{mass\sdownloader}i, name: :mass_downloader,
|
192
|
+
version: %r{mass\sdownloader/([\d\.]+)}i
|
193
|
+
match %r{safetynet\srobot}i, name: :safetynet_robot,
|
194
|
+
version: %r{safetynet\srobot\s([\d\.]+)}i
|
195
|
+
match %r{internet\sninja}i, name: :internet_ninja,
|
196
|
+
version: %r{internet\sninja\s([\d\.]+)}i
|
197
|
+
match %r{go\!zilla}i, name: :gozilla,
|
198
|
+
version: %r{go\!zilla\s([\d\.]+)}i
|
199
|
+
match %r{larbin}i, name: :larbin,
|
200
|
+
version: %r{larbin_([\d\.]+)}i
|
201
|
+
match %r{download\sdemon}i, name: :download_demon,
|
202
|
+
version: %r{download\sdemon/([\d\.]+)}i
|
203
|
+
end
|
204
|
+
|
205
|
+
# automatically set type to :library for each defined matcher
|
206
|
+
validators do
|
207
|
+
match %r{cse\shtml\svalidator}i, name: :cse_html_validator
|
208
|
+
match %r{csscheck}i, name: :csscheck
|
209
|
+
match %r{htmlparser}i, name: :htmlparser
|
210
|
+
match %r{p3p\svalidator}i, name: :p3p_validator
|
211
|
+
match %r{wdg_validator}i, name: :wdg_validator
|
212
|
+
match %r{w3c_validator}i, name: :w3c_validator
|
213
|
+
|
214
|
+
match %r{cynthia}i, name: :cynthia,
|
215
|
+
version: %r{cynthia\s([\d\.]+)}i
|
216
|
+
|
217
|
+
match %r{w3c_css_validator}i,
|
218
|
+
name: :w3c_css_validator,
|
219
|
+
version: %r{w3c_css_validator_jfouffa/([\d\.]+)}i
|
220
|
+
end
|
221
|
+
|
222
|
+
# automatically set type to :library for each defined matcher
|
223
|
+
libraries do
|
224
|
+
match %r{php}i, name: :php
|
225
|
+
match %r{python}i, name: :python, version: %r{-urllib/([\d\.]+)}i
|
226
|
+
match %r{perl}i, name: :perl
|
227
|
+
match %r{java}i, name: :java
|
228
|
+
match %r{pycurl}i, name: :pycurl
|
229
|
+
match %r{curl}i, name: :curl
|
230
|
+
end
|
231
|
+
|
232
|
+
# inherit properties a standard set of properties by the name of a
|
233
|
+
# previously defined matcher, overwritten by properties added within matchers
|
234
|
+
# inherit properties from Identity where name == :chrome
|
235
|
+
like :chrome do
|
236
|
+
match %r{brave}i, name: :brave, version: %r{brave/([\d\.]+)}i
|
237
|
+
match %r{vivaldi}i, name: :vivaldi, version: %r{vivaldi/([\d\.]+)}i
|
238
|
+
match %r{colibri}i, name: :colibri, version: %r{colibri/([\d\.]+)}i
|
239
|
+
match %r{rockmelt}i, name: :rockmelt, version: %r{rockmelt/([\d\.]+)}i
|
240
|
+
match %r{flock}i, name: :flock, version: %r{flock/([\d\.]+)}i
|
241
|
+
|
242
|
+
match %r{comodo_dragon}i, name: :comodo_dragon,
|
243
|
+
version: %r{comodo_dragon/([\d\.]+)}i
|
244
|
+
end
|
245
|
+
|
246
|
+
# inherit properties from Identity where name == :safari
|
247
|
+
like :safari do
|
248
|
+
match %r{bolt}i, name: :bolt, version: %r{bolt/([\d\.]+)}i
|
249
|
+
|
250
|
+
match %r{stainless}i, name: :stainless,
|
251
|
+
version: %r{stainless/([\d\.]+)}i
|
252
|
+
match %r{samsungbrowser}i, name: :samsungbrowser,
|
253
|
+
version: %r{samsungbrowser/([\d\.]+)}i
|
254
|
+
match %r{omniweb}i, name: :omniweb,
|
255
|
+
version: %r{omniweb/v([\d\.]+)}i
|
256
|
+
|
257
|
+
match %r{webos|wosbrowser}i,
|
258
|
+
name: :webosbrowser,
|
259
|
+
version: %r{(?:version|w(?:eb)?osbrowser)/([\d\.]+)}i
|
260
|
+
end
|
261
|
+
|
262
|
+
# inherit properties from Identity where name == :firefox
|
263
|
+
like :firefox do
|
264
|
+
match %r{prism}i, name: :prism, version: %r{prism/([\d\.]+)}i
|
265
|
+
match %r{waterfox}i, name: :waterfox, version: %r{waterfox/([\d\.]+)}i
|
266
|
+
match %r{firebird}i, name: :firebird, version: %r{firebird/([\d\.]+)}i
|
267
|
+
match %r{netscape}i, name: :netscape, version: %r{netscape/([\d\.]+)}i
|
268
|
+
match %r{icecat}i, name: :icecat, version: %r{icecat/([\d\.]+)}i
|
269
|
+
match %r{iceweasel}i, name: :iceweasel, version: %r{iceweasel/([\d\.]+)}i
|
270
|
+
match %r{seamonkey}i, name: :seamonkey, version: %r{seamonkey/([\d\.]+)}i
|
271
|
+
match %r{superswan}i, name: :superswan, version: %r{superswan/([\d\.]+)}i
|
272
|
+
match %r{lunascape}i, name: :lunascape, version: %r{lunascape/([\d\.]+)}i
|
273
|
+
match %r{camino}i, name: :camino, version: %r{camino/([\d\.]+)}i,
|
274
|
+
locale: %r{\s(\w{2}(?:\-\w{2})?),}i
|
275
|
+
end
|
276
|
+
|
277
|
+
# never thought a browser would want to be like IE...
|
278
|
+
like :ie do
|
279
|
+
# version does not have to be supplied because we simply want to use
|
280
|
+
# the version supplied by the MSIE token instead (there is no version on
|
281
|
+
# the avant browser or slimbrowser UA itself)
|
282
|
+
match %r{avant\sbrowser}i, name: :avant_browser
|
283
|
+
match %r{slimbrowser}i, name: :slimbrowser
|
284
|
+
|
285
|
+
match %r{sleipnir}i, name: :sleipnir,
|
286
|
+
version: %r{sleipnir/([\d\.]+)}i
|
287
|
+
match %r{deepnet\sexplorer}i, name: :deepnet_explorer,
|
288
|
+
version: %r{deepnet\sexplorer ([\d\.]+)}i
|
289
|
+
end
|
290
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Browserino
|
3
|
+
class Identity
|
4
|
+
attr_reader :pattern, :properties
|
5
|
+
|
6
|
+
SETTINGS = { name: nil, type: :unknown }.freeze
|
7
|
+
|
8
|
+
def initialize(pattern = //, opts = {}, **additional, &block)
|
9
|
+
opts = pattern if pattern.is_a?(Hash) && opts.empty?
|
10
|
+
@properties = SETTINGS.merge(opts.merge(additional))
|
11
|
+
@pattern = pattern
|
12
|
+
|
13
|
+
instance_eval(&block) if block
|
14
|
+
end
|
15
|
+
|
16
|
+
def matches?(user_agent)
|
17
|
+
pattern =~ user_agent if pattern.is_a? Regexp
|
18
|
+
end
|
19
|
+
|
20
|
+
def ===(other)
|
21
|
+
return false if properties[:name].nil?
|
22
|
+
|
23
|
+
case other
|
24
|
+
when Regexp then other =~ properties[:name]
|
25
|
+
when String then other.to_sym == properties[:name]
|
26
|
+
when Symbol then other == properties[:name]
|
27
|
+
when Identity then other.properties[:name] == properties[:name]
|
28
|
+
else false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_str
|
33
|
+
properties[:name].to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
def =~(other)
|
37
|
+
self === other
|
38
|
+
end
|
39
|
+
|
40
|
+
def ==(other)
|
41
|
+
self === other
|
42
|
+
end
|
43
|
+
|
44
|
+
def method_missing(sym, *args, &block)
|
45
|
+
return @properties[sym] = block if block
|
46
|
+
return @properties[sym] = args.shift if args.any?
|
47
|
+
|
48
|
+
@properties[sym]
|
49
|
+
end
|
50
|
+
|
51
|
+
def respond_to_missing?(_, *__, &___)
|
52
|
+
true
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'action_controller/railtie'
|
2
4
|
|
3
5
|
module Browserino
|
4
6
|
class ActionController
|
5
7
|
module Base
|
6
|
-
def
|
7
|
-
@
|
8
|
+
def client
|
9
|
+
@client ||= Browserino.parse request.headers['User-Agent']
|
8
10
|
end
|
9
11
|
end
|
10
12
|
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Browserino
|
3
|
+
def self.analyze(user_agent, identity = nil)
|
4
|
+
props = [*config.global_identities, identity].compact.map(&:properties)
|
5
|
+
.reduce(&:merge)
|
6
|
+
like = props.delete :like if props.key? :like
|
7
|
+
props = collect(props, user_agent)
|
8
|
+
props = normalize props
|
9
|
+
props = with_smart_matchers props
|
10
|
+
left = props.select { |_, val| val.is_a? Regexp }
|
11
|
+
props = props.merge normalize(collect(left, user_agent)) if left.any?
|
12
|
+
props = with_labels props
|
13
|
+
|
14
|
+
if like
|
15
|
+
repl = user_agent =~ %r{#{like}}i && '' || like.to_s
|
16
|
+
like = parse user_agent.gsub identity.pattern, repl
|
17
|
+
end
|
18
|
+
|
19
|
+
Client.new props, like
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.config
|
23
|
+
@config ||= Config.new({ before_parse: [],
|
24
|
+
global_identities: [],
|
25
|
+
properties: [],
|
26
|
+
types: [:unknown],
|
27
|
+
names: [],
|
28
|
+
smart_matchers: {},
|
29
|
+
identities: [],
|
30
|
+
labels: Hash.new { |h, k| h[k] = [] },
|
31
|
+
filters: Hash.new { |h, k| h[k] = [] },
|
32
|
+
aliasses: Hash.new { |h, k| h[k] = [] } })
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.label_for(target_name, version = nil)
|
36
|
+
return unless config.labels.key?(target_name) && version
|
37
|
+
version = Version.new version unless version.is_a? Version
|
38
|
+
return unless version > 0
|
39
|
+
config.labels[target_name].each do |candidate|
|
40
|
+
min = Version.new candidate[:range].min
|
41
|
+
max = Version.new candidate[:range].max
|
42
|
+
|
43
|
+
return candidate[:name] if version >= min && version <= max
|
44
|
+
end
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.with_labels(properties)
|
49
|
+
[:name, :engine, :platform].each do |prop|
|
50
|
+
lbl_prop = (prop == :name) && :label || "#{prop}_label".to_sym
|
51
|
+
ver_prop = (prop == :name) && :version || "#{prop}_version".to_sym
|
52
|
+
properties[lbl_prop] ||= label_for properties[prop], properties[ver_prop]
|
53
|
+
end
|
54
|
+
|
55
|
+
properties
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.with_smart_matchers(properties)
|
59
|
+
config.smart_matchers.each_with_object properties do |(prop, detector), props|
|
60
|
+
props[prop] ||= parse_detector detector, properties
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.parse_detector(detect, properties)
|
65
|
+
pat = properties.each_with_object(detect[:with].dup) do |(key, val), str|
|
66
|
+
replacement = val.to_s.strip
|
67
|
+
str.gsub! ":#{key}", replacement unless replacement.empty?
|
68
|
+
end
|
69
|
+
|
70
|
+
Regexp.new pat, get_flags(*detect[:flags].to_a)
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.collect(properties, ua)
|
74
|
+
properties.each_with_object({}) do |(prop, value), res|
|
75
|
+
res[prop] = case value
|
76
|
+
when Regexp then value.match(ua).to_a[1]
|
77
|
+
else value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.normalize(properties)
|
83
|
+
properties.each_with_object({}) do |(prop, value), store|
|
84
|
+
store[prop] = convert value, format: prop
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.convert(val, **opts)
|
89
|
+
filters = config.filters[:global] + config.filters[opts[:format]]
|
90
|
+
filters.compact.each do |fmt|
|
91
|
+
val = fmt.call val
|
92
|
+
end
|
93
|
+
|
94
|
+
val
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.get_flags(*flags)
|
98
|
+
val = 0
|
99
|
+
|
100
|
+
flags.each do |flag|
|
101
|
+
case flag.to_sym
|
102
|
+
when :m then val |= Regexp::MULTILINE
|
103
|
+
when :i then val |= Regexp::IGNORECASE
|
104
|
+
when :x then val |= Regexp::EXTENDED
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
val
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Browserino
|
3
|
+
class Options
|
4
|
+
def initialize(options = {})
|
5
|
+
@options = options
|
6
|
+
end
|
7
|
+
|
8
|
+
def method_missing(sym, *args, &block)
|
9
|
+
return @options[opt(sym)] == args.first if args.any?
|
10
|
+
@options[opt(sym)]
|
11
|
+
end
|
12
|
+
|
13
|
+
def respond_to_missing?(sym, *args, &block)
|
14
|
+
option? sym
|
15
|
+
end
|
16
|
+
|
17
|
+
def merge(other)
|
18
|
+
@options.merge! other
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_hash
|
23
|
+
@options
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_a
|
27
|
+
@options.to_a
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
@options.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def opt(sym)
|
37
|
+
sym.to_s.tr('?', '').to_sym
|
38
|
+
end
|
39
|
+
|
40
|
+
def option?(sym)
|
41
|
+
@options.keys.include? opt(sym)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/browserino/version.rb
CHANGED
@@ -1,3 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Browserino
|
2
|
-
VERSION = '
|
3
|
+
VERSION = '4.0.0'.freeze
|
4
|
+
|
5
|
+
def self.version
|
6
|
+
@verion ||= Version.new VERSION.dup
|
7
|
+
end
|
8
|
+
|
9
|
+
# This class makes versions easily comparable using logical operators
|
10
|
+
# it makes it convenient to check versions in a natural way
|
11
|
+
class Version < Array
|
12
|
+
attr_reader :major, :minor, :patch
|
13
|
+
|
14
|
+
def initialize(val, *rest)
|
15
|
+
super parse_params(val, *rest)
|
16
|
+
@major = fetch 0, 0
|
17
|
+
@minor = fetch 1, 0
|
18
|
+
@patch = fetch 2, 0
|
19
|
+
end
|
20
|
+
|
21
|
+
def full
|
22
|
+
to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
@str ||= join '.'
|
27
|
+
end
|
28
|
+
|
29
|
+
def <(other)
|
30
|
+
compare :<, other
|
31
|
+
end
|
32
|
+
|
33
|
+
def <=(other)
|
34
|
+
compare :<=, other
|
35
|
+
end
|
36
|
+
|
37
|
+
def >(other)
|
38
|
+
compare :>, other
|
39
|
+
end
|
40
|
+
|
41
|
+
def >=(other)
|
42
|
+
compare :>=, other
|
43
|
+
end
|
44
|
+
|
45
|
+
def ==(other)
|
46
|
+
compare :==, other
|
47
|
+
end
|
48
|
+
|
49
|
+
def !=(other)
|
50
|
+
compare :!=, other
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def parse_params(val, *rest)
|
56
|
+
case val
|
57
|
+
when Float then val.to_s.split '.'
|
58
|
+
when Integer then [val, *rest]
|
59
|
+
when String then val.tr('_', '.').split '.'
|
60
|
+
when Array then val
|
61
|
+
when Hash then [val[:major], val[:minor], val[:patch]]
|
62
|
+
when Symbol then []
|
63
|
+
else val.to_a
|
64
|
+
end.map(&:to_i)
|
65
|
+
end
|
66
|
+
|
67
|
+
def compare(op, other)
|
68
|
+
other = Version.new other unless other.is_a? Version
|
69
|
+
subsize = [size, other.size].min
|
70
|
+
|
71
|
+
(self[0...subsize] <=> other[0...subsize]).send op, 0
|
72
|
+
end
|
73
|
+
end
|
3
74
|
end
|
data/lib/browserino.rb
CHANGED
@@ -1,67 +1,25 @@
|
|
1
|
-
|
2
|
-
require_relative 'browserino/core/patterns'
|
3
|
-
require_relative 'browserino/core/supported'
|
4
|
-
require_relative 'browserino/core/questions'
|
5
|
-
require_relative 'browserino/core/helpers'
|
6
|
-
require_relative 'browserino/core/lies'
|
7
|
-
require_relative 'browserino/core/alias'
|
8
|
-
|
1
|
+
# frozen_string_literal: true
|
9
2
|
require_relative 'browserino/integrate/rails' if defined?(::Rails)
|
10
3
|
|
11
|
-
require_relative 'browserino/
|
12
|
-
require_relative 'browserino/
|
13
|
-
|
4
|
+
require_relative 'browserino/options'
|
5
|
+
require_relative 'browserino/config'
|
6
|
+
require_relative 'browserino/methods'
|
7
|
+
require_relative 'browserino/client'
|
14
8
|
require_relative 'browserino/version'
|
15
|
-
require_relative 'browserino/
|
16
|
-
require_relative 'browserino/engine'
|
17
|
-
require_relative 'browserino/operating_system'
|
18
|
-
require_relative 'browserino/console'
|
19
|
-
|
20
|
-
module Browserino
|
21
|
-
module_function
|
22
|
-
|
23
|
-
def parse(ua)
|
24
|
-
name = Browser.name(ua)
|
25
|
-
info = fetch_info(strip_lies(ua), name)
|
26
|
-
tmp = info[:name].to_s.tr('_', ' ')
|
27
|
-
info[:name] = tmp.strip == '' ? nil : tmp
|
28
|
-
|
29
|
-
Agent.new check_for_aliases(info), ua
|
30
|
-
end
|
9
|
+
require_relative 'browserino/identity'
|
31
10
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
engine_version: Engine.version(ua),
|
37
|
-
system_name: OperatingSystem.name(ua),
|
38
|
-
system_version: OperatingSystem.version(ua),
|
39
|
-
system_architecture: OperatingSystem.architecture(ua),
|
40
|
-
console_name: Console.name(ua),
|
41
|
-
locale: OperatingSystem.locale(ua) }
|
42
|
-
end
|
43
|
-
|
44
|
-
def strip_lies(ua)
|
45
|
-
Core::LIES.inject(ua) do |s, arr|
|
46
|
-
s = s.gsub(arr[0], '') if arr[1] == true || s =~ arr[1]
|
47
|
-
s
|
48
|
-
end
|
49
|
-
end
|
11
|
+
require_relative 'browserino/definitions/matchers'
|
12
|
+
require_relative 'browserino/definitions/aliasses'
|
13
|
+
require_relative 'browserino/definitions/filters'
|
14
|
+
require_relative 'browserino/definitions/labels'
|
50
15
|
|
51
|
-
|
52
|
-
|
53
|
-
|
16
|
+
module Browserino
|
17
|
+
def self.parse(ua)
|
18
|
+
config.before_parse.each { |b| ua = b.call ua } if config.before_parse.any?
|
19
|
+
config.identities.each do |identity|
|
20
|
+
return analyze ua, identity if identity.matches? ua
|
54
21
|
end
|
55
|
-
h
|
56
|
-
end
|
57
22
|
|
58
|
-
|
59
|
-
if match && match.names.include?(sym.to_s)
|
60
|
-
m = match[sym].to_s.downcase.strip
|
61
|
-
m = yield(m) if block_given?
|
62
|
-
m
|
63
|
-
else
|
64
|
-
UNKNOWN
|
65
|
-
end
|
23
|
+
analyze ua
|
66
24
|
end
|
67
25
|
end
|