browserino 3.0.1 → 4.0.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/.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
|