1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
|
# frozen_string_literal: true
class DeviceDetector
class Parser
ROOT = File.expand_path('../..', __dir__)
REGEX_CACHE = ::DeviceDetector::MemoryCache.new({})
private_constant :REGEX_CACHE
def initialize(user_agent)
@user_agent = user_agent
end
attr_reader :user_agent
def name
from_cache(['name', self.class.name, user_agent]) do
NameExtractor.new(user_agent, regex_meta).call
end
end
def full_version
from_cache(['full_version', self.class.name, user_agent]) do
VersionExtractor.new(user_agent, regex_meta).call
end
end
private
def regex_meta
@regex_meta ||= matching_regex || {}
end
def matching_regex
from_cache([self.class.name, user_agent]) do
regexes.find { |r| user_agent =~ r[:regex] }
end
end
def regexes
@regexes ||= regexes_for(filepaths)
end
def filenames
raise NotImplementedError
end
def filepaths
filenames.map do |filename|
[filename.to_sym, File.join(ROOT, 'regexes', filename)]
end
end
def regexes_for(file_paths)
REGEX_CACHE.get_or_set(file_paths) do
load_regexes(file_paths).flat_map { |path, regex| parse_regexes(path, regex) }
end
end
def load_regexes(file_paths)
file_paths.map do |path, full_path|
object = YAML.load_file(full_path)
object = rewrite_device_object!(object) if device_yml_file?(full_path)
object = rewrite_vendor_object!(object) if vendor_yml_file?(full_path)
[path, symbolize_keys!(object)]
end
end
def device_yml_file?(file_path)
file_path.include?('/regexes/device/')
end
def vendor_yml_file?(file_path)
file_path.include?('/regexes/vendorfragments')
end
def rewrite_vendor_object!(object)
object.map { |key, values| values.map { |v| { 'regex_name' => key, 'regex' => v } } }.flatten
end
def rewrite_device_object!(object)
object.map { |key, value| [key, { 'regex_name' => key }.merge!(value)] }.to_h
end
def symbolize_keys!(object)
case object
when Array
object.map! { |v| symbolize_keys!(v) }
when Hash
keys = object.keys
keys.each do |k|
object[k.to_sym] = symbolize_keys!(object.delete(k)) if k.is_a?(String)
end
end
object
end
def parse_regexes(path, raw_regexes)
raw_regexes.map do |meta|
raise "invalid device spec: #{meta.inspect}" unless meta[:regex].is_a? String
meta[:regex] = build_regex(meta[:regex])
meta[:versions].each { |v| v[:regex] = build_regex(v[:regex]) } if meta.key?(:versions)
meta[:path] = path
meta
end
end
def build_regex(src)
Regexp.new("(?:^|[^A-Z0-9_-]|[^A-Z0-9-]_|sprd-|MZ-)(?:#{src})", Regexp::IGNORECASE)
end
def from_cache(key, &block)
DeviceDetector.cache.get_or_set(key, &block)
end
end
end
|