Module: Browserino

Defined in:
lib/browserino.rb,
lib/browserino/client.rb,
lib/browserino/config.rb,
lib/browserino/matcher.rb,
lib/browserino/methods.rb,
lib/browserino/options.rb,
lib/browserino/version.rb,
lib/browserino/integrate/rails.rb,
lib/browserino/integrate/action_controller.rb

Defined Under Namespace

Classes: ActionController, Client, Config, Matcher, Options, Railtie, Version

Class Method Summary collapse

Class Method Details

.analyze(uas, matcher = nil, headers = nil) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/browserino/methods.rb', line 4

def self.analyze(uas, matcher = nil, headers = nil)
  @defaults ||= config.global_matchers.map(&:properties).reduce(&:merge)

  props = @defaults.merge(matcher && matcher.properties || {})
  like  = props.delete :like
  props = mass_collect props, uas

  if headers.is_a? Hash
    headers = normalize_header_keys headers
    props   = fill_missing_with_headers headers, props
  end

  props = fix_inconsistent_props props

  like = Client.new props.merge(like_attrs(props, like, uas)) if like

  Client.new props, like
end

.collect(properties, uas) ⇒ Object



129
130
131
132
133
# File 'lib/browserino/methods.rb', line 129

def self.collect(properties, uas)
  properties.each_with_object({}) do |(n, v), r|
    r[n] = convert (v.is_a?(Regexp) ? v.match(uas).to_a[1] : v), format: n
  end
end

.collect_with_smart_watchers(properties, user_agent) ⇒ Object



102
103
104
# File 'lib/browserino/methods.rb', line 102

def self.collect_with_smart_watchers(properties, user_agent)
  properties.merge collect(smart_matchers(properties), user_agent)
end

.configObject



66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/browserino/methods.rb', line 66

def self.config
  @config ||= Config.new before_parse:    [],
                         global_matchers: [],
                         properties:      [],
                         types:           [:unknown],
                         names:           [],
                         smart_matchers:  {},
                         missing_props:   {},
                         matchers:        [],
                         labels:          Hash.new { |h, k| h[k] = [] },
                         filters:         Hash.new { |h, k| h[k] = [] },
                         aliasses:        Hash.new { |h, k| h[k] = [] }
end

.convert(val, **opts) ⇒ Object



135
136
137
138
139
140
141
142
# File 'lib/browserino/methods.rb', line 135

def self.convert(val, **opts)
  filters = config.filters[:global] + config.filters[opts[:format]]
  filters.each do |fmt|
    val = fmt.call val
  end

  val
end

.fill_missing_with_headers(headers, props) ⇒ Object



47
48
49
50
51
52
53
54
# File 'lib/browserino/methods.rb', line 47

def self.fill_missing_with_headers(headers, props)
  config.missing_props.each_with_object props do |(prop, blk), res|
    if props.key?(prop) && (!props[prop] ||
       (props[prop].respond_to?(:empty?) && props[prop].empty?))
      res[prop] = convert blk.call(headers, res), format: prop
    end
  end
end

.fix_inconsistent_props(hsh) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/browserino/methods.rb', line 23

def self.fix_inconsistent_props(hsh)
  # patch locale and locales props
  if hsh[:locales].any?
    # when locale is not present in locales, append it
    hsh[:locales] << hsh[:locale] unless hsh[:locales].include? hsh[:locale]

    # ensure locales.first == locale when locale present in locales
    hsh[:locale] = hsh[:locales].first
  elsif hsh[:locale]
    # no locales from header but locale is found in UA, append locale
    # to locales to ensure expected output
    hsh[:locales] << hsh[:locale]
  end

  hsh
end

.get_flags(*flags) ⇒ Object



144
145
146
147
148
149
150
151
152
# File 'lib/browserino/methods.rb', line 144

def self.get_flags(*flags)
  flags.reduce(0) do |val, flag|
    case flag.to_sym
    when :m then val | Regexp::MULTILINE
    when :i then val | Regexp::IGNORECASE
    when :x then val | Regexp::EXTENDED
    end
  end
end

.label_for(target_name, version = nil) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
# File 'lib/browserino/methods.rb', line 80

def self.label_for(target_name, version = nil)
  return unless config.labels.key?(target_name) && version

  version = Version.new version unless version.is_a? Version
  return unless version > 0

  config.labels[target_name].each do |candidate|
    return candidate[:name] if version.between? candidate[:range]
  end
  nil
end

.like_attrs(props, like, user_agent) ⇒ Object



56
57
58
59
60
61
62
63
64
# File 'lib/browserino/methods.rb', line 56

def self.like_attrs(props, like, user_agent)
  version = config.matchers.select { |m| m == like }
                  .first.properties[:version]
  fattrs = { name: like }
  fattrs[:version] = version if version.is_a? Regexp
  fattrs[:version] ||= smart_matchers(fattrs)[:version]

  props.dup.merge collect(fattrs, user_agent)
end

.mass_collect(props, uas) ⇒ Object



123
124
125
126
127
# File 'lib/browserino/methods.rb', line 123

def self.mass_collect(props, uas)
  props = collect props, uas
  props = collect_with_smart_watchers props, uas
  with_labels props
end

.normalize_header_keys(headers) ⇒ Object



40
41
42
43
44
45
# File 'lib/browserino/methods.rb', line 40

def self.normalize_header_keys(headers)
  headers.each_with_object({}) do |(header, value), normalized|
    header = header.gsub(/^HTTP_/i, '').tr('-', '_').downcase.to_sym
    normalized[header] = value
  end
end

.parse(uas, headers = nil) ⇒ Object



19
20
21
22
23
24
25
26
27
# File 'lib/browserino.rb', line 19

def self.parse(uas, headers = nil)
  uas = '' unless uas #stringify user agent before usage when nil or false

  uas = config.before_parse.reduce(uas) { |u, b| b.call u }
  config.matchers.each do |matcher|
    return analyze uas, matcher, headers if matcher.matches? uas
  end
  analyze uas, nil, headers
end

.parse_detector(detect, properties) ⇒ Object



114
115
116
117
118
119
120
121
# File 'lib/browserino/methods.rb', line 114

def self.parse_detector(detect, properties)
  pat = properties.each_with_object(detect[:with].dup) do |(key, val), str|
    replacement = val.to_s.strip.gsub '_', '[_\s-]'
    str.gsub! ":#{key}", replacement unless replacement.empty?
  end

  Regexp.new pat, get_flags(*detect[:flags].to_a)
end

.smart_matchers(properties) ⇒ Object



106
107
108
109
110
111
112
# File 'lib/browserino/methods.rb', line 106

def self.smart_matchers(properties)
  config.smart_matchers.each_with_object({}) do |(prop, detector), props|
    next if properties.key? prop

    props[prop] = parse_detector detector, properties
  end
end

.version(current = '4.5.0') ⇒ Object



4
5
6
# File 'lib/browserino/version.rb', line 4

def self.version(current = '4.5.0')
  @version ||= Version.new current
end

.with_labels(properties) ⇒ Object



92
93
94
95
96
97
98
99
100
# File 'lib/browserino/methods.rb', line 92

def self.with_labels(properties)
  i[name engine platform].each do |prop|
    lbl_prop = (prop == :name) && :label || "#{prop}_label".to_sym
    ver_prop = (prop == :name) && :version || "#{prop}_version".to_sym
    properties[lbl_prop] ||= label_for properties[prop], properties[ver_prop]
  end

  properties
end