brauser 3.2.6 → 3.3.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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +29 -0
  4. data/.travis-gemfile +1 -1
  5. data/.travis.yml +2 -1
  6. data/Gemfile +3 -3
  7. data/README.md +6 -6
  8. data/brauser.gemspec +1 -1
  9. data/doc/Brauser.html +7 -7
  10. data/doc/Brauser/Browseable.html +127 -0
  11. data/doc/Brauser/{BrowserMethods → Browseable}/Attributes.html +34 -30
  12. data/doc/Brauser/Browseable/DefaultDefinitions.html +387 -0
  13. data/doc/Brauser/{BrowserMethods → Browseable}/General.html +9 -9
  14. data/doc/Brauser/{BrowserMethods → Browseable}/General/ClassMethods.html +33 -33
  15. data/doc/Brauser/{BrowserMethods → Browseable}/Parsing.html +23 -23
  16. data/doc/Brauser/{BrowserMethods → Browseable}/PartialQuerying.html +59 -53
  17. data/doc/Brauser/{BrowserMethods → Browseable}/Querying.html +43 -37
  18. data/doc/Brauser/{BrowserMethods → Browseable}/Register.html +9 -9
  19. data/doc/Brauser/Browseable/Register/ClassMethods.html +516 -0
  20. data/doc/Brauser/Browser.html +787 -1362
  21. data/doc/Brauser/Definition.html +230 -40
  22. data/doc/Brauser/Hooks.html +4 -4
  23. data/doc/Brauser/Hooks/RubyOnRails.html +4 -4
  24. data/doc/Brauser/Query.html +53 -53
  25. data/doc/Brauser/{BrowserMethods.html → Queryable.html} +13 -11
  26. data/doc/Brauser/{Chainers.html → Queryable/Chainers.html} +51 -45
  27. data/doc/Brauser/{Queries.html → Queryable/Queries.html} +47 -41
  28. data/doc/Brauser/Version.html +6 -6
  29. data/doc/_index.html +41 -27
  30. data/doc/class_list.html +1 -1
  31. data/doc/css/style.css +1 -0
  32. data/doc/file.README.html +10 -10
  33. data/doc/frames.html +1 -1
  34. data/doc/index.html +10 -10
  35. data/doc/method_list.html +68 -74
  36. data/doc/top-level-namespace.html +4 -4
  37. data/lib/brauser.rb +14 -3
  38. data/lib/brauser/browseable/attributes.rb +95 -0
  39. data/lib/brauser/browseable/general.rb +104 -0
  40. data/lib/brauser/browseable/parsing.rb +127 -0
  41. data/lib/brauser/browseable/partial_querying.rb +116 -0
  42. data/lib/brauser/browseable/querying.rb +63 -0
  43. data/lib/brauser/browseable/register.rb +73 -0
  44. data/lib/brauser/browser.rb +34 -741
  45. data/lib/brauser/definition.rb +30 -5
  46. data/lib/brauser/definitions/browsers.rb +66 -0
  47. data/lib/brauser/definitions/languages.rb +130 -0
  48. data/lib/brauser/definitions/platforms.rb +30 -0
  49. data/lib/brauser/query.rb +4 -99
  50. data/lib/brauser/queryable/chainers.rb +56 -0
  51. data/lib/brauser/queryable/queries.rb +60 -0
  52. data/lib/brauser/version.rb +3 -2
  53. data/spec/brauser/browser_spec.rb +26 -29
  54. data/spec/brauser/query_spec.rb +15 -13
  55. metadata +30 -17
  56. data/.bundle/install.log +0 -106
  57. data/doc/Brauser/BrowserMethods/Register/ClassMethods.html +0 -770
@@ -0,0 +1,63 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the brauser gem. Copyright (C) 2013 and above Shogun <shogun@cowtech.it>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ module Brauser
8
+ # The interface of brauser browsers.
9
+ module Browseable
10
+ # Methods to end querying.
11
+ module Querying
12
+ # Checks if the browser is a specific name and optionally of a specific version and platform.
13
+ #
14
+ # @see #v?
15
+ # @see #on?
16
+ #
17
+ # @param names [Symbol|Array] A list of specific names to match. Also, this meta-names are supported: `:capable` and `:tablet`.
18
+ # @param versions [Hash] An hash with specific version to match against. Need to be in form `{:operator => version}`, where operator
19
+ # is one of `:lt, :lte, :eq, :gt, :gte`.
20
+ # @param platforms [Symbol|Array] A list of specific platform to match. Valid values are all those possible for the platform attribute.
21
+ # @return [Boolean] `true` if current browser matches, `false` otherwise.
22
+ def is?(names = [], versions = {}, platforms = [])
23
+ is(names, versions, platforms).result
24
+ end
25
+
26
+ # Checks if the browser is a specific version.
27
+ #
28
+ # @param versions [String|Hash] A string in the form `operator version && ...` (example: `>= 7 && < 4`) or an hash with specific version to match against,
29
+ # in form `{:operator => version}`, where operator is one of `:lt, :lte, :eq, :gt, :gte`.
30
+ # @return [Boolean] `true` if current browser matches, `false` otherwise.
31
+ def version?(versions = {})
32
+ version(versions).result
33
+ end
34
+ alias_method :v?, :version?
35
+
36
+ # Check if the browser is on a specific platform.
37
+ #
38
+ # @param platforms [Symbol|Array] A list of specific platform to match.
39
+ # @return [Boolean] `true` if current browser matches, `false` otherwise.
40
+ def on?(platforms = [])
41
+ on(platforms).result
42
+ end
43
+
44
+ # Check if the browser accepts the specified languages.
45
+ #
46
+ # @param langs [String|Array] A list of languages to match against.
47
+ # @return [Boolean] `true` if current browser matches, `false` otherwise.
48
+ def accepts?(langs = [])
49
+ accepts(langs).result
50
+ end
51
+
52
+ # Check if the browser is supported.
53
+ #
54
+ # @param supported [Hash|String] A map of engines and minimum supported major version, or a path to YAML file containing the map.
55
+ # @return [Boolean] `true` if current browser is supported, `false` otherwise.
56
+ def supported?(supported)
57
+ supported = YAML.load_file(supported).symbolize_keys if supported.is_a?(String)
58
+ minimum = supported[name]
59
+ (minimum && v?(gte: minimum)).to_boolean
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,73 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the brauser gem. Copyright (C) 2013 and above Shogun <shogun@cowtech.it>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ module Brauser
8
+ # The interface of brauser browsers.
9
+ module Browseable
10
+ # Methods for register recognized browsers, versions and platforms.
11
+ module Register
12
+ extend ActiveSupport::Concern
13
+
14
+ # Class methods.
15
+ module ClassMethods
16
+ # Adds a definitions for recognition.
17
+ #
18
+ # @param type [Symbol] The type of the definition. Can be `:browsers`, `:platforms` or `:languages`.
19
+ # @param definition [Definition|Array] The definition to add. Can be also an array of definitions.
20
+ # @return [Boolean] `true` if at least one definition has been added, `false` otherwise.
21
+ def add(type, definition)
22
+ rv = false
23
+
24
+ if [:browsers, :platforms, :languages].include?(type)
25
+ prepare_definitions_for(type)
26
+
27
+ definition.ensure_array.each do |d|
28
+ next unless d.is_a?(::Brauser::Definition)
29
+ @definitions[type][d.tag] = d
30
+ rv = true
31
+ end
32
+ end
33
+
34
+ rv
35
+ end
36
+
37
+ # Adds definitions for a default list of browsers that can be recognized.
38
+ #
39
+ # @return [Boolean] `true` if at least one browser has been added, `false` otherwise.
40
+ def add_default_browsers
41
+ add(:browsers, (
42
+ ::Brauser::Browseable::DefaultDefinitions::MOBILE_BROWSERS + ::Brauser::Browseable::DefaultDefinitions::MAJOR_DESKTOP_BROWSERS +
43
+ ::Brauser::Browseable::DefaultDefinitions::MSIE_BROWSERS + ::Brauser::Browseable::DefaultDefinitions::MINOR_DESKTOP_BROWSERS
44
+ ).map { |browser| ::Brauser::Definition.send(:new, *browser) })
45
+ end
46
+
47
+ # Adds a default list of platforms that can be recognized.
48
+ #
49
+ # @return [Boolean] `true` if at least one platform has been added, `false` otherwise.
50
+ def add_default_platforms
51
+ add(:platforms, ::Brauser::Browseable::DefaultDefinitions::PLATFORMS.map { |platform| ::Brauser::Definition.send(:new, *platform) })
52
+ end
53
+
54
+ # Adds a default list of languages that can be recognized.
55
+ #
56
+ # @return [Boolean] `true` if at least one language has been added, `false` otherwise.
57
+ def add_default_languages
58
+ add(:languages, ::Brauser::Browseable::DefaultDefinitions::LANGUAGES.map { |code, name| ::Brauser::Definition.new(code, name, code) })
59
+ end
60
+
61
+ private
62
+
63
+ # Prepares definition for a specific type.
64
+ #
65
+ # @param type [Symbol] The type to prepare.
66
+ def prepare_definitions_for(type)
67
+ @definitions ||= {}
68
+ @definitions[type] ||= {}
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -6,670 +6,6 @@
6
6
 
7
7
  # A framework agnostic browser detection and querying helper.
8
8
  module Brauser
9
- # Methods of the {Browser Browser} class.
10
- module BrowserMethods
11
- # Methods for register recognized browsers, versions and platforms.
12
- module Register
13
- extend ActiveSupport::Concern
14
-
15
- # Class methods.
16
- module ClassMethods
17
- # Adds a definitions for recognition.
18
- #
19
- # @param type [Symbol] The type of the definition. Can be `:browsers`, `:platforms` or `:languages`.
20
- # @param definition [Definition|Array] The definition to add. Can be also an array of definitions.
21
- # @return [Boolean] `true` if at least one definition has been added, `false` otherwise.
22
- def add(type, definition)
23
- rv = false
24
-
25
- if [:browsers, :platforms, :languages].include?(type) then
26
- @definitions ||= {}
27
- @definitions[type] ||= {}
28
-
29
- definition.ensure_array.each do |d|
30
- if d.is_a?(::Brauser::Definition) then
31
- @definitions[type][d.tag] = d
32
- rv = true
33
- end
34
- end
35
- end
36
-
37
- rv
38
- end
39
-
40
- # Adds definitions for a default list of browsers that can be recognized.
41
- #
42
- # @return [Boolean] `true` if at least one browser has been added, `false` otherwise.
43
- def add_default_browsers
44
- add_mobile_browsers && add_major_desktop_browsers && add_msie_browsers && add_minor_desktop_browsers
45
- end
46
-
47
- # Adds a default list of platforms that can be recognized.
48
- #
49
- # @return [Boolean] `true` if at least one platform has been added, `false` otherwise.
50
- def add_default_platforms
51
- add(:platforms, [
52
- [:symbian, "Symbian", /s60|symb/i],
53
- [:windows_phone, "Microsoft Windows Phone", /windows phone/i],
54
- [:kindle, "Nokia Symbian", /kindle|silk/i, ],
55
- [:ios, "Apple iOS", Proc.new { |_, agent| [:iphone, :ipad, :ipod, :chrome_ios].include?(name) || agent =~ /ipad|iphone|ipod|crios/i }],
56
- [:android, "Android", /android/i],
57
- [:blackberry, "RIM BlackBerry", /blackberry/i],
58
- [:psp, "Sony Playstation Portable", /psp/i],
59
- [:ps3, "Sony Playstation 3", /playstation 3/i],
60
- [:wii, "Nintendo Wii", /wii/i],
61
-
62
- [:linux, "Linux", /linux/i],
63
- [:osx, "Apple MacOS X", /mac|macintosh|mac os x/i],
64
- [:windows, "Microsoft Windows", /windows/i]
65
- ].map { |platform| ::Brauser::Definition.send(:new, *platform) })
66
- end
67
-
68
- # Adds a default list of languages that can be recognized.
69
- #
70
- # @return [Boolean] `true` if at least one language has been added, `false` otherwise.
71
- def add_default_languages
72
- add(:languages, {
73
- "af" => "Afrikaans",
74
- "sq" => "Albanian",
75
- "eu" => "Basque",
76
- "bg" => "Bulgarian",
77
- "be" => "Byelorussian",
78
- "ca" => "Catalan",
79
- "zh" => "Chinese",
80
- "zh-cn" => "Chinese/China",
81
- "zh-tw" => "Chinese/Taiwan",
82
- "zh-hk" => "Chinese/Hong Kong",
83
- "zh-sg" => "Chinese/singapore",
84
- "hr" => "Croatian",
85
- "cs" => "Czech",
86
- "da" => "Danish",
87
- "nl" => "Dutch",
88
- "nl-nl" => "Dutch/Netherlands",
89
- "nl-be" => "Dutch/Belgium",
90
- "en" => "English",
91
- "en-gb" => "English/United Kingdom",
92
- "en-us" => "English/United States",
93
- "en-au" => "English/Australian",
94
- "en-ca" => "English/Canada",
95
- "en-nz" => "English/New Zealand",
96
- "en-ie" => "English/Ireland",
97
- "en-za" => "English/South Africa",
98
- "en-jm" => "English/Jamaica",
99
- "en-bz" => "English/Belize",
100
- "en-tt" => "English/Trinidad",
101
- "et" => "Estonian",
102
- "fo" => "Faeroese",
103
- "fa" => "Farsi",
104
- "fi" => "Finnish",
105
- "fr" => "French",
106
- "fr-be" => "French/Belgium",
107
- "fr-fr" => "French/France",
108
- "fr-ch" => "French/Switzerland",
109
- "fr-ca" => "French/Canada",
110
- "fr-lu" => "French/Luxembourg",
111
- "gd" => "Gaelic",
112
- "gl" => "Galician",
113
- "de" => "German",
114
- "de-at" => "German/Austria",
115
- "de-de" => "German/Germany",
116
- "de-ch" => "German/Switzerland",
117
- "de-lu" => "German/Luxembourg",
118
- "de-li" => "German/Liechtenstein",
119
- "el" => "Greek",
120
- "he" => "Hebrew",
121
- "he-il" => "Hebrew/Israel",
122
- "hi" => "Hindi",
123
- "hu" => "Hungarian",
124
- "ie-ee" => "Internet Explorer/Easter Egg",
125
- "is" => "Icelandic",
126
- "id" => "Indonesian",
127
- "in" => "Indonesian",
128
- "ga" => "Irish",
129
- "it" => "Italian",
130
- "it-ch" => "Italian/ Switzerland",
131
- "ja" => "Japanese",
132
- "km" => "Khmer",
133
- "km-kh" => "Khmer/Cambodia",
134
- "ko" => "Korean",
135
- "lv" => "Latvian",
136
- "lt" => "Lithuanian",
137
- "mk" => "Macedonian",
138
- "ms" => "Malaysian",
139
- "mt" => "Maltese",
140
- "no" => "Norwegian",
141
- "pl" => "Polish",
142
- "pt" => "Portuguese",
143
- "pt-br" => "Portuguese/Brazil",
144
- "rm" => "Rhaeto-Romanic",
145
- "ro" => "Romanian",
146
- "ro-mo" => "Romanian/Moldavia",
147
- "ru" => "Russian",
148
- "ru-mo" => "Russian /Moldavia",
149
- "sr" => "Serbian",
150
- "sk" => "Slovack",
151
- "sl" => "Slovenian",
152
- "sb" => "Sorbian",
153
- "es" => "Spanish",
154
- "es-do" => "Spanish",
155
- "es-ar" => "Spanish/Argentina",
156
- "es-co" => "Spanish/Colombia",
157
- "es-mx" => "Spanish/Mexico",
158
- "es-es" => "Spanish/Spain",
159
- "es-gt" => "Spanish/Guatemala",
160
- "es-cr" => "Spanish/Costa Rica",
161
- "es-pa" => "Spanish/Panama",
162
- "es-ve" => "Spanish/Venezuela",
163
- "es-pe" => "Spanish/Peru",
164
- "es-ec" => "Spanish/Ecuador",
165
- "es-cl" => "Spanish/Chile",
166
- "es-uy" => "Spanish/Uruguay",
167
- "es-py" => "Spanish/Paraguay",
168
- "es-bo" => "Spanish/Bolivia",
169
- "es-sv" => "Spanish/El salvador",
170
- "es-hn" => "Spanish/Honduras",
171
- "es-ni" => "Spanish/Nicaragua",
172
- "es-pr" => "Spanish/Puerto Rico",
173
- "sx" => "Sutu",
174
- "sv" => "Swedish",
175
- "sv-se" => "Swedish/Sweden",
176
- "sv-fi" => "Swedish/Finland",
177
- "ts" => "Thai",
178
- "tn" => "Tswana",
179
- "tr" => "Turkish",
180
- "uk" => "Ukrainian",
181
- "ur" => "Urdu",
182
- "vi" => "Vietnamese",
183
- "xh" => "Xshosa",
184
- "ji" => "Yiddish",
185
- "zu" => "Zulu"
186
- }.map { |code, name| ::Brauser::Definition.new(code, name, code) })
187
- end
188
-
189
- private
190
- # Registers the most common desktop browsers.
191
- #
192
- # @return [Boolean] `true` if at least one browser has been added, `false` otherwise.
193
- def add_major_desktop_browsers
194
- add(:browsers, [
195
- [:chrome, "Google Chrome", /((chrome)|(chromium))/i, /(.+Chrom[a-z]+\/)([a-z0-9.]+)/i],
196
- [:netscape, "Netscape Navigator", /(netscape|navigator)\//i, /((Netscape|Navigator)\/)([a-z0-9.]+)/i],
197
- [:firefox, "Mozilla Firefox", /firefox/i, /(.+Firefox\/)([a-z0-9.]+)/i],
198
- [:safari, "Apple Safari", Proc.new{ |_, agent| disambiguate_browser(agent, /safari/i, /((chrome)|(chromium)|(crios))/i) }, /(.+Version\/)([a-z0-9.]+)/i],
199
- ].map { |browser| ::Brauser::Definition.send(:new, *browser) })
200
- end
201
-
202
- # Registers definitions for MSIE browsers.
203
- #
204
- # @return [Boolean] `true` if at least one browser has been added, `false` otherwise.
205
- def add_msie_browsers
206
- add(:browsers, [
207
- [:msie_compatibility, "Microsoft Internet Explorer (Compatibility View)", /(msie 7\.0).+(trident)/i, Proc.new { |_, agent|
208
- version = /(.+trident\/)(?<version>[a-z0-9.]+)/i.match(agent)["version"].split(".")
209
- version[0] = version[0].to_integer + 4
210
- version.join(".")
211
- }],
212
- [:msie, "Microsoft Internet Explorer", Proc.new{ |_, agent| disambiguate_browser(agent, /msie/i, /opera/i) }, /(.+MSIE )([a-z0-9.]+)/i],
213
- ].map { |browser| ::Brauser::Definition.send(:new, *browser) })
214
- end
215
-
216
- # Registers the least common desktop browsers.
217
- #
218
- # @return [Boolean] `true` if at least one browser has been added, `false` otherwise.
219
- def add_minor_desktop_browsers
220
- add(:browsers, [
221
- [:quicktime, "Apple QuickTime", /quicktime/i, /(.+((QuickTime\/)|(qtver=)))([a-z0-9.]+)/i],
222
- [:webkit, "WebKit Browser", /webkit/i, /(.+WebKit\/)([a-z0-9.]+)/i],
223
- [:gecko, "Gecko Browser", /gecko/i, /(.+rv:|Gecko\/)([a-z0-9.]+)/i],
224
- ].map { |browser| ::Brauser::Definition.send(:new, *browser) })
225
- end
226
-
227
- # Register the most common mobile and console browsers.
228
- #
229
- # @return [Boolean] `true` if at least one browser has been added, `false` otherwise.
230
- def add_mobile_browsers
231
- add(:browsers, [
232
- [:coremedia, "Apple CoreMedia", /coremedia/i, /.+CoreMedia v([a-z0-9.]+)/i],
233
-
234
- [:opera_mobile, "Opera Mobile", /opera mobi/i, /.+Opera Mobi.+((.+Opera )|(Version\/))([a-z0-9.]+)/i],
235
- [:opera, "Opera", /opera/i, Proc.new{ |_, a|
236
- version = ((a !~ /wii/i) ? /((.+Opera )|(Version\/))(?<version>[a-z0-9.]+)/i : /(.+Nintendo Wii; U; ; )(?<version>[a-z0-9.]+)/i).match(a)
237
- version ? version["version"] : nil
238
- }],
239
-
240
- [:android, "Android", /android/i, /(.+Android )([a-z0-9.]+)/i],
241
- [:blackberry, "RIM BlackBerry", /blackberry/i, /(.+Version\/)([a-z0-9.]+)/i],
242
- [:kindle, "Amazon Kindle", /(kindle)/i, /(.+(Kindle|Silk)\/)([a-z0-9.]+)/i],
243
- [:psp, "Sony Playstation Portable", /psp/i, /(.+PlayStation Portable\); )([a-z0-9.]+)/i],
244
- [:ps3, "Sony Playstation 3", /playstation 3/i, /(.+PLAYSTATION 3; )([a-z0-9.]+)/i],
245
- [:windows_phone, "Microsoft Windows Phone", /windows phone/i, /(.+IEMobile\/)([a-z0-9.]+)/i],
246
- [:wii, "Nintendo Wii", /nintendo wii/, /(.+Nintendo Wii; U; ; )([a-z0-9.]+)/i],
247
-
248
- [:chrome_ios, "Chrome iOS", /crios/i, /(.+CriOS\/)([a-z0-9.]+)/i],
249
- [:ipod, "Apple iPod", /ipod/i, /(.+Version\/)([a-z0-9.]+)/i],
250
- [:iphone, "Apple iPhone", /iphone/i, /(.+Version\/)([a-z0-9.]+)/i],
251
- [:ipad, "Apple iPad", /ipad/i, /(.+Version\/)([a-z0-9.]+)/i],
252
-
253
- [:mobile, "Other Mobile Browser", /(mobile|symbian|midp|windows ce)/i, /.+\/([a-z0-9.]+)/i],
254
- ].map { |browser| ::Brauser::Definition.send(:new, *browser) })
255
- end
256
-
257
- # Recognizes a browser disambiguating against another.
258
- #
259
- # @param agent [String] The agent to match.
260
- # @param positive_matcher [Regexp] The expression to match.
261
- # @param negative_matcher [Regexp] The expression NOT to match.
262
- # @return [Boolean] `true` if matching succeeded, `false otherwise`.
263
- def disambiguate_browser(agent, positive_matcher, negative_matcher)
264
- agent =~ positive_matcher && agent !~ negative_matcher
265
- end
266
- end
267
- end
268
-
269
- # General methods.
270
- module General
271
- extend ActiveSupport::Concern
272
-
273
- # Class methods.
274
- module ClassMethods
275
- # Returns the list of browser that can be recognized.
276
- #
277
- # The keys are the browser name, the values are arrays of the name matcher, the version match and the label.
278
- #
279
- # @return [Hash] The list of browser that can be recognized.
280
- def browsers
281
- @definitions[:browsers]
282
- end
283
-
284
- # Returns the list of platforms that can be recognized.
285
- #
286
- # The keys are the platform name, values are arrays of the matcher and the label.
287
- #
288
- # @return [Hash] The list of platform that can be recognized.
289
- def platforms
290
- @definitions[:platforms]
291
- end
292
-
293
- # Returns the list of languages that can be recognized.
294
- #
295
- # The keys are the languages code, the values the labels.
296
- #
297
- # @return [Hash] The list of languages that can be recognized.
298
- def languages
299
- @definitions[:languages]
300
- end
301
-
302
- # Compares two versions.
303
- #
304
- # @param v1 [String] The first versions to compare.
305
- # @param operator [Symbol] The operator to use for comparison, can be one of `[:lt, :lte, :eq, :gte, :gt]`.
306
- # @param v2 [Symbol] The second version to compare.
307
- # @return [Boolean] true if comparison is valid, `false` otherwise.
308
- def compare_versions(v1 = "", operator = :eq, v2 = "")
309
- valid_results = {lt: [-1], lte: [-1, 0], eq: [0], gte: [0, 1], gt: [1]}.fetch(operator, [])
310
-
311
- if valid_results.present? && v1.ensure_string.present? then
312
- p1, p2 = find_relevant_tokens(v1, v2)
313
- p1, p2 = normalize_tokens(p1, p2)
314
- valid_results.include?(p1 <=> p2)
315
- else
316
- false
317
- end
318
- end
319
-
320
- private
321
- # Find relevant tokens (that is, the first two which are not equals) in a string for comparison.
322
- #
323
- # @param v1 [String] The first versions to compare.
324
- # @param v2 [String] The second version to compare.
325
- # @return [Array] The tokens to compare.
326
- def find_relevant_tokens(v1, v2)
327
- v1 = v1.ensure_string.strip.split(".")
328
- v2 = v2.ensure_string.strip.split(".")
329
-
330
- p1 = nil
331
- p2 = nil
332
- [v1.length, v2.length].max.times do |i|
333
- p1 = v1[i]
334
- p2 = v2[i]
335
- break if !p1 && !p2 || p1 != p2
336
- end
337
-
338
- [p1 || "0", p2 || "0"]
339
- end
340
-
341
- # Normalizes token for comparison.
342
- #
343
- # @param p1 [String] The first token to normalize.
344
- # @param p2 [String] The second token to normalize.
345
- # @return [Array] The tokens to compare.
346
- def normalize_tokens(p1, p2)
347
- if !p1.is_integer? then
348
- ll = p1.length
349
- p1 = p2 + p1
350
- p2 = p2 + ("z" * ll)
351
- else
352
- ll = [p1.length, p2.length].max
353
- p1 = p1.rjust(ll, "0")
354
- p2 = p2.rjust(ll, "0")
355
- end
356
-
357
- [p1, p2]
358
- end
359
- end
360
- end
361
-
362
- # Methods to handle attributes
363
- module Attributes
364
- # Gets a human-readable browser name.
365
- #
366
- # @return [String] A human-readable browser name.
367
- def readable_name
368
- parse_agent(@agent) if !@name
369
- ::Brauser::Browser.browsers.fetch(@name).label rescue "Unknown Browser"
370
- end
371
-
372
- # Gets a human-readable platform name.
373
- #
374
- # @return [String] A readable platform name.
375
- def platform_name
376
- parse_agent(@agent) if !@platform
377
- ::Brauser::Browser.platforms[@platform].try(:label) || "Unknown Platform"
378
- end
379
-
380
- # Returns an array of information about the browser. Information are strings which are suitable to use as CSS classes.
381
- #
382
- # For version, it will be included a class for every token of the version. For example, version `7.0.1.2` will return this:
383
- #
384
- # ```ruby
385
- # ["version-7", "version-7_0", "version-7_0_1", "version-7_0_1_2"]
386
- # ```
387
- #
388
- # If you provide a block (with accepts name, version and platform as arguments), it will be used for translating the name.
389
- #
390
- # @param join [String|NilClass] If non falsy, the separator to use to join information. If falsy, informations will be returned as array.
391
- # @param name [Boolean] If non falsy, the string to prepend to the name. If falsy, the name information will not be included.
392
- # @param version [String|NilClass] If non falsy, the string to prepend to the version. If falsy, the version information will not be included.
393
- # @param platform [String|NilClass] If non falsy, the string to prepend to the platform. If falsy, the platform information will not be included.
394
- # @param block [Proc] A block to translate browser name.
395
- # @return [String|Array] CSS ready information of the current browser.
396
- def classes(join = " ", name = "", version = "version-", platform = "platform-", &block)
397
- platform = "platform-" if platform.is_a?(TrueClass)
398
- rv = [stringify_name(name, &block), stringify_version(version), !platform ? nil : (platform + @platform.to_s)].compact.flatten
399
- join ? rv.join(join) : rv
400
- end
401
- alias :meta :classes
402
-
403
- private
404
- # Stringifies a browser name(s).
405
- #
406
- # @param name [Boolean] If non falsy, the string to prepend to the name. If falsy, the name information will not be included.
407
- # @param block [Proc] A block to translate browser name.
408
- # @return [String|Array|nil] The browser name(s) or `nil`, if it was set to be skipped.
409
- def stringify_name(name, &block)
410
- if name then
411
- name, block = prepare_name_stringification(name, block)
412
- names = block.call(@name, @version, @platform).ensure_array {|n| "#{name}#{n}" }
413
- names.length > 1 ? names : names.first
414
- else
415
- nil
416
- end
417
- end
418
-
419
- # Prepare a name stringification
420
- #
421
- # @param name [Boolean] If non falsy, the string to prepend to the name. If falsy, the name information will not be included.
422
- # @param block [Proc] A block to translate browser name.
423
- # @return [Array] A name and a translator ready to use.
424
- def prepare_name_stringification(name, block)
425
- self.parse_agent(@agent) if !@name
426
- [(name.is_a?(TrueClass) ? "" : name), block || Proc.new {|n, *| n == :msie_compatibility ? [:msie_compatibility, :msie] : n }]
427
- end
428
-
429
- # Stringifies a browser version.
430
- #
431
- # @param version [String|NilClass] If non falsy, the string to prepend to the version. If falsy, the version information will not be included.
432
- # @return [Array] The version strings or `nil`, if it was set to be skipped.
433
- def stringify_version(version)
434
- version = "version-" if version.is_a?(TrueClass)
435
- tokens = @version.split(".")
436
-
437
- !version ? nil : tokens.reduce([version + tokens.shift]) {|prev, current|
438
- prev + [prev.last + "_" + current]
439
- }.flatten
440
- end
441
- end
442
-
443
- # Methods to parse the user agent.
444
- module Parsing
445
- # Parses the User-Agent header.
446
- # @param agent [String] The User-Agent header.
447
- # @return [Boolean] `true` if the browser was detected, `false` otherwise.
448
- def parse_agent(agent = nil)
449
- agent = agent.ensure_string
450
-
451
- @name, _ = match_name_and_version(agent)
452
- @version = adjust_version(@version)
453
- @platform = match_platform(agent)
454
-
455
- (@name != :unknown) ? true : false
456
- end
457
-
458
- # Parses the Accept-Language header.
459
- #
460
- # @param accept_language [String] The Accept-Language header.
461
- # @return [Array] The list of accepted languages.
462
- def parse_accept_language(accept_language = nil)
463
- accept_language.ensure_string.gsub(/;q=[\d.]+/, "").split(",").map {|l| l.downcase.strip }.select{|l| l.present? }
464
- end
465
-
466
- private
467
- # Matches a browser name and version.
468
- #
469
- # @param agent [String] The User-Agent header.
470
- # @return [String|Symbol] The browser name or `:unknown`, if no match was found.
471
- def match_name_and_version(agent)
472
- catch(:name) do
473
- ::Brauser::Browser.browsers.each do |tag, definition|
474
- matched = definition.match(:primary, definition, agent)
475
-
476
- if matched then
477
- @version = definition.match(:secondary, definition, agent)
478
- throw(:name, tag)
479
- end
480
- end
481
-
482
- :unknown
483
- end
484
- end
485
-
486
- # Adjusts a browser version.
487
- #
488
- # @param version [String] The version to adjust.
489
- # @return [String] The adjusted version.
490
- def adjust_version(version)
491
- # Adjust version
492
- if version.blank? then
493
- "0.0"
494
- elsif version.is_a?(::MatchData) then
495
- version.to_a.last
496
- else
497
- version
498
- end
499
- end
500
-
501
- # Matches a browser platform.
502
- #
503
- # @param agent [String] The User-Agent header.
504
- # @return [String|Symbol] The browser platform or `:unknown`, if no match was found.
505
- def match_platform(agent)
506
- catch(:platform) do
507
- ::Brauser::Browser.platforms.each do |tag, definition|
508
- throw(:platform, tag) if definition.match(:primary, definition, agent)
509
- end
510
-
511
- :unknown
512
- end
513
- end
514
- end
515
-
516
- # Methods to query with chaining.
517
- module PartialQuerying
518
- # Checks if the browser is a specific name and optionally of a specific version and platform.
519
- #
520
- # @see #v?
521
- # @see #on?
522
- #
523
- # @param names [Symbol|Array] A list of specific names to match. Also, this meta-names are supported: `:capable` and `:tablet`.
524
- # @param versions [String|Hash] A string in the form `operator version && ...` (example: `>= 7 && < 4`) or an hash with specific version to match against,
525
- # in form `{:operator => version}`, where operator is one of `:lt, :lte, :eq, :gt, :gte`.
526
- # @param platforms [Symbol|Array] A list of specific platform to match. Valid values are all those possible for the platform attribute.
527
- # @return [Query] A query which can evaluated for concatenation or result.
528
- def is(names = [], versions = {}, platforms = [])
529
- parse_agent(@agent) if !@name
530
-
531
- names = adjust_names(names)
532
- versions = parse_versions_query(versions)
533
- platforms = platforms.ensure_array
534
-
535
- ::Brauser::Query.new(self,
536
- (names.blank? || (names.include?(@name) && check_capable(names))) &&
537
- (versions.blank? || v?(versions)) &&
538
- (platforms.blank? || on?(platforms))
539
- )
540
- end
541
-
542
- # Checks if the browser is a specific version.
543
- #
544
- # @param versions [String|Hash] A string in the form `operator version && ...` (example: `>= 7 && < 4`) or an hash with specific version to match against,
545
- # in form `{:operator => version}`, where operator is one of `:lt, :lte, :eq, :gt, :gte`.
546
- # @return [Query] A query which can evaluated for concatenation or result.
547
- def v(versions = {})
548
- parse_agent(@agent) if !@version
549
- versions = versions.is_a?(String) ? parse_versions_query(versions) : versions.ensure_hash
550
-
551
- ::Brauser::Query.new(self, versions.all? { |operator, value| Brauser::Browser.compare_versions(@version, operator, value) })
552
- end
553
-
554
- # Check if the browser is on a specific platform.
555
- #
556
- # @param platforms [Symbol|Array] A list of specific platform to match.
557
- # @return [Query] A query which can evaluated for concatenation or result.
558
- def on(platforms = [])
559
- parse_agent(@agent) if !@platform
560
-
561
- ::Brauser::Query.new(self, platforms.blank? || platforms.ensure_array(nil, true, true, true, :to_sym).include?(@platform))
562
- end
563
-
564
- # Check if the browser accepts the specified languages.
565
- #
566
- # @param langs [String|Array] A list of languages to match against.
567
- # @return [Query] A query which can evaluated for concatenation or result.
568
- def accepts(langs = [])
569
- parse_accept_language(@accept_language) if !@languages
570
-
571
- ::Brauser::Query.new(self, (@languages & langs.ensure_array(nil, true, true, true, :to_s)).present?)
572
- end
573
-
574
- private
575
- # Adjusts names for correct matching.
576
- #
577
- # @param names [Array] A list of names.
578
- # @return [Array] The adjusted list of names.
579
- def adjust_names(names)
580
- # Adjust names
581
- names = names.ensure_array(nil, true, true, true, :to_sym)
582
- names << [:msie, :msie_compatibility] if names.include?(:ie) || names.include?(:msie)
583
- names << [:chromium] if names.include?(:chrome)
584
- names << [:chrome, :firefox, :safari, :opera, :msie] if names.include?(:capable)
585
- names << [:ipad, :android, :kindle] if names.include?(:tablet)
586
- names.flatten.compact.uniq
587
- end
588
-
589
- # Checks if the browser is capable.
590
- #
591
- # @param names [Array] A list of names.
592
- # @return [Boolean] `true` if the browser is capable, `false` otherwise.
593
- def check_capable(names)
594
- !names.include?(:capable) || @name != :msie || Brauser::Browser.compare_versions(@version, :gte, 9)
595
- end
596
-
597
- # Parses a version query.
598
- #
599
- # @param versions [String|Hash] A string in the form `operator version && ...` (example: `>= 7 && < 4`) or an hash with specific version to match
600
- # against, in form `{:operator => version}`, where operator is one of `:lt, :lte, :eq, :gt, :gte`.
601
- # @return [Hash] The hash representation of the query.
602
- def parse_versions_query(versions)
603
- versions.is_a?(::Hash) ? versions : versions.ensure_string.split(/\s*&&\s*/).reduce({}) do |prev, token|
604
- operator, version = parse_versions_query_component(token)
605
- prev[operator] = version if operator.present? && version.present?
606
- prev
607
- end
608
- end
609
-
610
- # Parses a token of a version query.
611
- #
612
- # @param token [String] The token to parse.
613
- # @return [Array] An operator and an argument.
614
- def parse_versions_query_component(token)
615
- operator, version = token.strip.split(/\s+/, 2).map(&:strip)
616
- [{"<" => :lt, "<=" => :lte, "=" => :eq, "==" => :eq, ">" => :gt, ">=" => :gte}.fetch(operator, nil), version]
617
- end
618
- end
619
-
620
- # Methods to end querying.
621
- module Querying
622
- # Checks if the browser is a specific name and optionally of a specific version and platform.
623
- #
624
- # @see #v?
625
- # @see #on?
626
- #
627
- # @param names [Symbol|Array] A list of specific names to match. Also, this meta-names are supported: `:capable` and `:tablet`.
628
- # @param versions [Hash] An hash with specific version to match against. Need to be in form `{:operator => version}`, where operator
629
- # is one of `:lt, :lte, :eq, :gt, :gte`.
630
- # @param platforms [Symbol|Array] A list of specific platform to match. Valid values are all those possible for the platform attribute.
631
- # @return [Boolean] `true` if current browser matches, `false` otherwise.
632
- def is?(names = [], versions = {}, platforms = [])
633
- is(names, versions, platforms).result
634
- end
635
-
636
- # Checks if the browser is a specific version.
637
- #
638
- # @param versions [String|Hash] A string in the form `operator version && ...` (example: `>= 7 && < 4`) or an hash with specific version to match against,
639
- # in form `{:operator => version}`, where operator is one of `:lt, :lte, :eq, :gt, :gte`.
640
- # @return [Boolean] `true` if current browser matches, `false` otherwise.
641
- def v?(versions = {})
642
- v(versions).result
643
- end
644
-
645
- # Check if the browser is on a specific platform.
646
- #
647
- # @param platforms [Symbol|Array] A list of specific platform to match.
648
- # @return [Boolean] `true` if current browser matches, `false` otherwise.
649
- def on?(platforms = [])
650
- on(platforms).result
651
- end
652
-
653
- # Check if the browser accepts the specified languages.
654
- #
655
- # @param langs [String|Array] A list of languages to match against.
656
- # @return [Boolean] `true` if current browser matches, `false` otherwise.
657
- def accepts?(langs = [])
658
- accepts(langs).result
659
- end
660
-
661
- # Check if the browser is supported.
662
- #
663
- # @param supported [Hash|String] A map of engines and minimum supported major version, or a path to YAML file containing the map.
664
- # @return [Boolean] `true` if current browser is supported, `false` otherwise.
665
- def supported?(supported)
666
- supported = YAML.load_file(supported).symbolize_keys if supported.is_a?(String)
667
- minimum = supported[name]
668
- (minimum && v?(gte: minimum)).to_boolean
669
- end
670
- end
671
- end
672
-
673
9
  # This class represents a detection of the current user browser.
674
10
  #
675
11
  # @attribute agent
@@ -693,15 +29,15 @@ module Brauser
693
29
  attr_accessor :platform
694
30
 
695
31
  # Aliases
696
- alias :ua :agent
697
- alias :ua= :agent=
32
+ alias_attribute :ua, :agent
698
33
 
699
- include ::Brauser::BrowserMethods::General
700
- include ::Brauser::BrowserMethods::Attributes
701
- include ::Brauser::BrowserMethods::Register
702
- include ::Brauser::BrowserMethods::Parsing
703
- include ::Brauser::BrowserMethods::PartialQuerying
704
- include ::Brauser::BrowserMethods::Querying
34
+ include ::Brauser::Browseable::General
35
+ include ::Brauser::Browseable::Attributes
36
+ include ::Brauser::Browseable::DefaultDefinitions
37
+ include ::Brauser::Browseable::Register
38
+ include ::Brauser::Browseable::Parsing
39
+ include ::Brauser::Browseable::PartialQuerying
40
+ include ::Brauser::Browseable::Querying
705
41
 
706
42
  # Creates a new browser.
707
43
  #
@@ -719,6 +55,16 @@ module Brauser
719
55
  parse_agent(@agent) if @agent
720
56
  end
721
57
 
58
+ # Get the current browser version (if called without arguments) or checks if the browser is a specific version.
59
+ #
60
+ # @see #is_version
61
+ # @param versions [String|Hash] A string in the form `operator version && ...` (example: `>= 7 && < 4`) or an hash with specific version to match against,
62
+ # in form `{:operator => version}`, where operator is one of `:lt, :lte, :eq, :gt, :gte`.
63
+ # @return [String|Query] The browser version or a query which can evaluated for concatenation or result.
64
+ def version(versions = nil)
65
+ !versions ? @version : version_equals_to(versions)
66
+ end
67
+
722
68
  # This method enables the use of dynamic queries in just one method.
723
69
  #
724
70
  # For example:
@@ -737,77 +83,24 @@ module Brauser
737
83
  # @param block [Proc] A block to pass to the method. Unused from the query.
738
84
  # @return [Boolean|Query|nil] A query or a boolean value (if `method` ends with `?`). If the query is not valid, `NoMethodError` will be raised.
739
85
  def method_missing(query, *arguments, &block)
740
- begin
741
- query_s = query.ensure_string
742
- rv = execute_query(parse_query(query_s)) || Brauser::Query.new(self, false)
743
- query_s =~ /\?$/ ? rv.result : rv
744
- rescue NoMethodError
745
- super(query, *arguments, &block)
746
- end
747
- end
748
-
749
- # Returns the current browser as a string.
750
- #
751
- # @see #classes
752
- #
753
- # @return [String] A string representation of the current browser.
754
- def to_s
755
- self.classes
86
+ query_s = query.ensure_string
87
+ rv = execute_query(parse_query(query_s)) || Brauser::Query.new(self, false)
88
+ query_s =~ /\?$/ ? rv.result : rv
89
+ rescue NoMethodError
90
+ super(query, *arguments, &block)
756
91
  end
757
92
 
758
93
  private
759
- # Parse query, getting all arguments.
760
- #
761
- # @param query [String] The query to issue. Use `__` to separate query and `_` in place of `.` in the version.
762
- # @return [Array] And array of `[method, arguments]` entries.
763
- def parse_query(query)
764
- query.gsub(/\?$/, "").gsub(/(_(v|on|is))/, " \\2").split(" ").map do |part|
765
- parse_query_part(part)
766
- end
767
- end
768
-
769
- # Handles a part of a query.
770
- #
771
- # @param part [String] A part of a query.
772
- # @return [Boolean|Query|nil] A query or a boolean value (if `method` ends with `?`). If the query is not valid, `NoMethodError` will be raised.
773
- def parse_query_part(part)
774
- method, arguments = part.split("_", 2)
775
94
 
776
- if method == "v" then
777
- arguments = parse_query_version(arguments)
778
- elsif !["is", "on"].include?(method)
779
- raise NoMethodError
780
- end
781
-
782
- [method, arguments]
783
- end
784
-
785
- # Parses the version for a query.
786
- #
787
- # @param version [String] The version to parse.
788
- # @return [String] The parsed version.
789
- def parse_query_version(version)
790
- [
791
- [/_?eq_?/, " == "], # Parse ==
792
- [/_?lte_?/, " <= "], # Parse <=
793
- [/_?gte_?/, " >= "], # Parse >=
794
- [/_?lt_?/, " < "], # Parse <
795
- [/_?gt_?/, " > "], # Parse >
796
- [/_?and_?/, " && "], # Parse &&
797
- ["_", "."], # Dot notation
798
- [/\s+/, " "]
799
- ].reduce(version) { |current, parse| current.gsub(parse[0], parse[1])}.strip
800
- end
801
-
802
- # Executes a parsed query
803
- #
804
- # @param query [Array] And array of `[method, arguments]` entries.
805
- # @return [Brauser::Query] The result of the query.
806
- def execute_query(query)
807
- query.reduce(Brauser::Query.new(self, true)) { |rv, call|
808
- break if !rv.result
809
- rv.send(call[0], *call[1])
810
- }
811
- end
95
+ # Executes a parsed query
96
+ #
97
+ # @param query [Array] And array of `[method, arguments]` entries.
98
+ # @return [Brauser::Query] The result of the query.
99
+ def execute_query(query)
100
+ query.reduce(Brauser::Query.new(self, true)) { |rv, call|
101
+ break unless rv.result
102
+ rv.send(call[0], *call[1])
103
+ }
104
+ end
812
105
  end
813
- end
106
+ end