diff --git a/README.md b/README.md index 5c390b8..0648019 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,11 @@ or when instantiating a `UserAgentParser::Parser`: UserAgentParser::Parser.new(patterns_path: '/some/path/to/regexes.yaml').parse(ua_string) ``` +Extending the standard database is possible by providing multiple files in `patterns_paths` (plural) array argument: +```ruby +UserAgentParser::Parser.new(patterns_paths: [UserAgentParser::DefaultPatternsPath, '/some/path/to/regexes.yaml']) +``` + ## Command line tool The gem incldes a `user_agent_parser` bin command which will read from diff --git a/lib/user_agent_parser.rb b/lib/user_agent_parser.rb index 5fb2fda..d0ec6aa 100644 --- a/lib/user_agent_parser.rb +++ b/lib/user_agent_parser.rb @@ -10,7 +10,7 @@ module UserAgentParser DefaultPatternsPath = File.join(File.dirname(__FILE__), '../vendor/uap-core/regexes.yaml') # Parse the given +user_agent_string+, returning a +UserAgent+ - def self.parse(user_agent_string, options = {}) - Parser.new(options).parse(user_agent_string) + def self.parse(user_agent_string, **args) + Parser.new(**args).parse(user_agent_string) end end diff --git a/lib/user_agent_parser/parser.rb b/lib/user_agent_parser/parser.rb index dccb08d..4d4de41 100644 --- a/lib/user_agent_parser/parser.rb +++ b/lib/user_agent_parser/parser.rb @@ -4,6 +4,8 @@ module UserAgentParser class Parser + extend Gem::Deprecate + FAMILY_REPLACEMENT_KEYS = %w[ family_replacement v1_replacement @@ -22,11 +24,13 @@ class Parser private_constant :FAMILY_REPLACEMENT_KEYS, :OS_REPLACEMENT_KEYS - attr_reader :patterns_path + attr_reader :patterns_paths + + def initialize(patterns_path: nil, patterns_paths: []) + @patterns_paths = [patterns_path, *patterns_paths].compact + @patterns_paths = [UserAgentParser::DefaultPatternsPath] if @patterns_paths.empty? - def initialize(options = {}) - @patterns_path = options[:patterns_path] || UserAgentParser::DefaultPatternsPath - @ua_patterns, @os_patterns, @device_patterns = load_patterns(patterns_path) + @ua_patterns, @os_patterns, @device_patterns = load_patterns(@patterns_paths) end def parse(user_agent) @@ -35,9 +39,23 @@ def parse(user_agent) parse_ua(user_agent, os, device) end + def patterns_path + patterns_paths.first + end + deprecate :patterns_path, :patterns_paths, 2022, 12 + private - def load_patterns(path) + def load_patterns(patterns_paths) + patterns_paths.each_with_object([[], [], []]) do |path, patterns| + ua_patterns, os_patterns, device_patterns = load_patterns_file(path) + patterns[0] += ua_patterns + patterns[1] += os_patterns + patterns[2] += device_patterns + end + end + + def load_patterns_file(path) yml = begin YAML.load_file(path, freeze: true) rescue ArgumentError @@ -88,24 +106,11 @@ def parse_device(user_agent) end end - if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.4.0') - def first_pattern_match(patterns, value) - patterns.each do |pattern| - if pattern[:regex].match?(value) - return [pattern, pattern[:regex].match(value)] - end - end - nil - end - else - def first_pattern_match(patterns, value) - patterns.each do |pattern| - if (match = pattern[:regex].match(value)) - return [pattern, match] - end - end - nil + def first_pattern_match(patterns, value) + patterns.each do |pattern| + return [pattern, pattern[:regex].match(value)] if pattern[:regex].match?(value) end + nil end def user_agent_from_pattern_match(pattern, match, os = nil, device = nil) diff --git a/spec/custom_regexes.yaml b/spec/custom_regexes.yaml index ef6148e..13266d2 100644 --- a/spec/custom_regexes.yaml +++ b/spec/custom_regexes.yaml @@ -1,5 +1,5 @@ user_agent_parsers: - - regex: '.*' + - regex: 'Any.*' family_replacement: 'Custom browser' v1_replacement: '1' v2_replacement: '2' @@ -7,11 +7,11 @@ user_agent_parsers: v4_replacement: '4' os_parsers: - - regex: '.*' + - regex: 'Any.*' os_replacement: 'Custom OS' os_v1_replacement: '1' os_v2_replacement: '2' device_parsers: - - regex: '.*' + - regex: 'Any.*' device_replacement: 'Custom device' diff --git a/spec/other_regexes.yaml b/spec/other_regexes.yaml new file mode 100644 index 0000000..44e9138 --- /dev/null +++ b/spec/other_regexes.yaml @@ -0,0 +1,17 @@ +user_agent_parsers: + - regex: 'Other.*' + family_replacement: 'Other browser' + v1_replacement: '1' + v2_replacement: '2' + v3_replacement: '3' + v4_replacement: '4' + +os_parsers: + - regex: 'Other.*' + os_replacement: 'Other OS' + os_v1_replacement: '1' + os_v2_replacement: '2' + +device_parsers: + - regex: 'Other.*' + device_replacement: 'Other device' diff --git a/spec/parser_spec.rb b/spec/parser_spec.rb index 1ef5713..721ab79 100644 --- a/spec/parser_spec.rb +++ b/spec/parser_spec.rb @@ -57,6 +57,10 @@ def custom_patterns_path File.join(File.dirname(__FILE__), 'custom_regexes.yaml') end + def other_patterns_path + File.join(File.dirname(__FILE__), 'other_regexes.yaml') + end + describe '::parse' do it 'parses a UA' do ua = UserAgentParser.parse('Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/418.8 (KHTML, like Gecko) Safari/419.3') @@ -69,10 +73,13 @@ def custom_patterns_path end describe '#initialize with a custom patterns path' do - it 'uses the custom patterns' do + it 'accepts a single patterns_path string' do parser = UserAgentParser::Parser.new(patterns_path: custom_patterns_path) ua = parser.parse('Any user agent string') + _(parser.patterns_paths).must_equal([custom_patterns_path]) + _(parser.patterns_path).must_equal(custom_patterns_path) + _(ua.family).must_equal('Custom browser') _(ua.version.major).must_equal('1') _(ua.version.minor).must_equal('2') @@ -85,6 +92,20 @@ def custom_patterns_path _(ua.device.family).must_equal('Custom device') end + + it 'accepts patterns_paths array' do + patterns_paths = [custom_patterns_path, other_patterns_path] + parser = UserAgentParser::Parser.new(patterns_paths: patterns_paths) + + _(parser.patterns_paths).must_equal(patterns_paths) + _(parser.patterns_path).must_equal(custom_patterns_path) + + ua = parser.parse('Any user agent string') + oua = parser.parse('Other user agent string') + + _(ua.family).must_equal('Custom browser') + _(oua.family).must_equal('Other browser') + end end describe '#parse' do