browscapper 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +12 -0
- data/Gemfile +3 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +43 -0
- data/Rakefile +55 -0
- data/browscapper.gemspec +28 -0
- data/lib/browscapper/reader/csv_reader.rb +63 -0
- data/lib/browscapper/reader/ini_reader.rb +47 -0
- data/lib/browscapper/reader/marshal_reader.rb +13 -0
- data/lib/browscapper/reader/yaml_reader.rb +13 -0
- data/lib/browscapper/reader.rb +20 -0
- data/lib/browscapper/user_agent.rb +98 -0
- data/lib/browscapper/version.rb +6 -0
- data/lib/browscapper.rb +71 -0
- data/test/browscapper_tests.rb +57 -0
- data/test/reader_tests.rb +37 -0
- metadata +128 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 J Smith <dark.panda@gmail.com>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
= Browscapper
|
3
|
+
|
4
|
+
I suppose this could also be called Yet Another Browscap.ini Reader.
|
5
|
+
|
6
|
+
Browscapper.load('path/to/browscap.ini')
|
7
|
+
Browscapper.match('Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8')
|
8
|
+
#=> Browscapper::UserAgent
|
9
|
+
|
10
|
+
Browscapper.matches
|
11
|
+
#=> Array of Browscapper::UserAgents, in order of best-to-worst matches.
|
12
|
+
|
13
|
+
== File Formats
|
14
|
+
|
15
|
+
The most widely used browscap.ini file is probably Gary Keith's, found at
|
16
|
+
https://browsers.garykeith.com/downloads . For the sake of convenience and
|
17
|
+
compatibiilty, Browscapper.load will look in the current directory for
|
18
|
+
a browscap.ini file. However, Browscapper can also read in browscap.csv
|
19
|
+
files as well, which are also available from Gary's site, as well as YAML
|
20
|
+
files and files written in Ruby's own Marshal'd file format. In fact, for the
|
21
|
+
sake of performance and speed, you may want to consider dumping browscap.ini
|
22
|
+
files into the Marshal'd format, which loads much faster than any of the
|
23
|
+
other file formats. For the sake of comparison, here are some benchmarks from
|
24
|
+
on my current development laptop (MacBook Pro 10,1 with a solid state drive):
|
25
|
+
|
26
|
+
bench_csv 1.111839s
|
27
|
+
bench_ini 3.801561s
|
28
|
+
bench_marshal 0.081942s
|
29
|
+
|
30
|
+
These are just the averages from a few runs, but the result is pretty clear
|
31
|
+
all the same: the Marshal'd format is by far quicker. (The YAML parser
|
32
|
+
currently has some problems with some messed up encodings and isn't
|
33
|
+
really recommended for prime time.)
|
34
|
+
|
35
|
+
To dump Browscapper into a Marshal'd format, you can either call
|
36
|
+
Browscapper.dump and write the results to a file or you can use the provided
|
37
|
+
rake task thusly:
|
38
|
+
|
39
|
+
rake browscapper:dump IN=browscap.ini OUT=browscap.dump
|
40
|
+
|
41
|
+
== License
|
42
|
+
|
43
|
+
See the MIT-LICENSE file for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
gem 'rdoc', '~> 3.12'
|
6
|
+
|
7
|
+
require 'rubygems/package_task'
|
8
|
+
require 'rake/testtask'
|
9
|
+
require 'rdoc/task'
|
10
|
+
require 'bundler/gem_tasks'
|
11
|
+
|
12
|
+
if RUBY_VERSION >= '1.9'
|
13
|
+
begin
|
14
|
+
gem 'psych'
|
15
|
+
rescue Exception => e
|
16
|
+
# it's okay, fall back on the bundled psych
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
$:.push 'lib'
|
21
|
+
|
22
|
+
version = Browscapper::VERSION
|
23
|
+
|
24
|
+
desc 'Test browscapper library'
|
25
|
+
Rake::TestTask.new(:test) do |t|
|
26
|
+
t.test_files = FileList['test/**/*_tests.rb']
|
27
|
+
t.verbose = !!ENV['VERBOSE_TESTS']
|
28
|
+
t.warning = !!ENV['WARNINGS']
|
29
|
+
end
|
30
|
+
|
31
|
+
desc 'Build docs'
|
32
|
+
Rake::RDocTask.new do |t|
|
33
|
+
t.title = "browscapper #{version}"
|
34
|
+
t.main = 'README.rdoc'
|
35
|
+
t.rdoc_dir = 'doc'
|
36
|
+
t.rdoc_files.include('README.rdoc', 'MIT-LICENSE', 'lib/**/*.rb')
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "Converts a browscap file to another format"
|
40
|
+
task "browscapper:dump" do
|
41
|
+
require File.join(File.dirname(__FILE__), %w{ lib browscapper })
|
42
|
+
|
43
|
+
if !ENV['IN'] || !ENV['OUT']
|
44
|
+
puts "Usage: IN=browscap OUT=browscap.dump"
|
45
|
+
puts
|
46
|
+
puts "Converts a browscap file to a ruby Marshal'd format. These files"
|
47
|
+
puts "are much quicker to load than CSV or INI files."
|
48
|
+
exit
|
49
|
+
end
|
50
|
+
|
51
|
+
Browscapper.load(ENV['IN'])
|
52
|
+
File.open(ENV['OUT'], 'wb') do |fp|
|
53
|
+
fp.write(Marshal.dump(Browscapper.entries))
|
54
|
+
end
|
55
|
+
end
|
data/browscapper.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require File.expand_path('../lib/browscapper/version', __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = %q{browscapper}
|
7
|
+
s.version = Browscapper::VERSION
|
8
|
+
|
9
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
10
|
+
s.authors = ["J Smith"]
|
11
|
+
s.description = "A browscap file parser and matcher."
|
12
|
+
s.summary = s.description
|
13
|
+
s.email = "dark.panda@gmail.com"
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"README.rdoc"
|
16
|
+
]
|
17
|
+
s.files = `git ls-files`.split($\)
|
18
|
+
s.executables = s.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
19
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
20
|
+
s.homepage = "http://github.com/dark-panda/browscapper"
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
|
23
|
+
s.add_dependency("rdoc")
|
24
|
+
s.add_dependency("rake", ["~> 0.9"])
|
25
|
+
s.add_dependency("minitest")
|
26
|
+
s.add_dependency("inifile")
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# encoding: BINARY
|
2
|
+
|
3
|
+
if RUBY_VERSION.to_f < 1.9
|
4
|
+
require 'fastercsv'
|
5
|
+
else
|
6
|
+
require 'csv'
|
7
|
+
end
|
8
|
+
|
9
|
+
module Browscapper
|
10
|
+
module CSVReader
|
11
|
+
CSV_ENGINE = if defined?(FasterCSV)
|
12
|
+
FasterCSV
|
13
|
+
else
|
14
|
+
CSV
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
include Reader
|
19
|
+
|
20
|
+
def load(file)
|
21
|
+
csv = CSV_ENGINE.open(file, 'rb')
|
22
|
+
|
23
|
+
# skip header
|
24
|
+
2.times { csv.shift }
|
25
|
+
headers = csv.shift
|
26
|
+
|
27
|
+
entries = Hash.new
|
28
|
+
|
29
|
+
csv.each do |l|
|
30
|
+
entry = UserAgent.new
|
31
|
+
headers.each_with_index do |v, i|
|
32
|
+
entry[v] = case l[i]
|
33
|
+
when 'false'
|
34
|
+
false
|
35
|
+
when 'true'
|
36
|
+
true
|
37
|
+
when /^\d+$/
|
38
|
+
l[i].to_i
|
39
|
+
else
|
40
|
+
l[i]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
entry.user_agent = if entry.user_agent
|
45
|
+
entry.user_agent.sub!(/^\[(.+)\]$/, '\1')
|
46
|
+
elsif entry.browser
|
47
|
+
entry.browser
|
48
|
+
end
|
49
|
+
|
50
|
+
entry.pattern = pattern_to_regexp(entry.user_agent.to_s)
|
51
|
+
|
52
|
+
if entries[entry.parent]
|
53
|
+
entry.merge!(entries[entry.parent])
|
54
|
+
end
|
55
|
+
|
56
|
+
entries[entry.user_agent] = entry
|
57
|
+
end
|
58
|
+
|
59
|
+
entries
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: BINARY
|
2
|
+
|
3
|
+
require 'inifile'
|
4
|
+
|
5
|
+
module Browscapper
|
6
|
+
module INIReader
|
7
|
+
class << self
|
8
|
+
include Reader
|
9
|
+
|
10
|
+
def load(file)
|
11
|
+
ini = IniFile.load(file, :encoding => 'BINARY')
|
12
|
+
ini.delete_section('GJK_Browscap_Version')
|
13
|
+
|
14
|
+
injector = proc { |memo, section|
|
15
|
+
unless memo[section]
|
16
|
+
entry = UserAgent.new
|
17
|
+
ini[section].each do |k, v|
|
18
|
+
entry[k] = case v
|
19
|
+
when 'false'
|
20
|
+
false
|
21
|
+
when 'true'
|
22
|
+
true
|
23
|
+
when /^\d+$/
|
24
|
+
v.to_i
|
25
|
+
else
|
26
|
+
v
|
27
|
+
end
|
28
|
+
end
|
29
|
+
entry.user_agent = section
|
30
|
+
entry.pattern = pattern_to_regexp(entry.user_agent)
|
31
|
+
|
32
|
+
memo[section] = entry
|
33
|
+
|
34
|
+
if entry.parent
|
35
|
+
memo = injector.call(memo, entry.parent)
|
36
|
+
entry.merge!(memo[entry.parent])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
memo
|
41
|
+
}
|
42
|
+
|
43
|
+
ini.sections.inject({}, &injector)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: BINARY
|
2
|
+
|
3
|
+
module Browscapper
|
4
|
+
module Reader
|
5
|
+
def pattern_to_regexp(pattern)
|
6
|
+
pattern = pattern.dup
|
7
|
+
|
8
|
+
if pattern.respond_to?(:force_encoding)
|
9
|
+
pattern.force_encoding('BINARY')
|
10
|
+
end
|
11
|
+
|
12
|
+
pattern.downcase!
|
13
|
+
pattern.gsub!(/([\^\$\(\)\[\]\.\-])/, "\\\\\\1")
|
14
|
+
pattern.gsub!('?', '.')
|
15
|
+
pattern.gsub!('*', '.*?')
|
16
|
+
|
17
|
+
Regexp.new("^#{pattern}$")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# encoding: BINARY
|
2
|
+
|
3
|
+
module Browscapper
|
4
|
+
class UserAgent
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
ACCESSORS = [
|
8
|
+
:parent, :user_agent, :comment, :browser, :version, :major_version,
|
9
|
+
:minor_version, :platform, :platform_version, :alpha, :beta, :win16, :win32, :win64,
|
10
|
+
:frames, :iframes, :tables, :cookies, :background_sounds, :vbscript,
|
11
|
+
:javascript, :java_applets, :activex_controls, :banned, :mobile_device,
|
12
|
+
:syndication_reader, :crawler, :css_version, :aol_version, :user_agent_id,
|
13
|
+
:css, :cdf, :aol, :device_name, :device_maker, :rendering_engine_name,
|
14
|
+
:rendering_engine_version, :rendering_engine_description
|
15
|
+
]
|
16
|
+
|
17
|
+
ACCESSORS.each do |accessor|
|
18
|
+
self.class_eval do
|
19
|
+
attr_accessor accessor
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_accessor :pattern
|
24
|
+
|
25
|
+
MAP = {
|
26
|
+
'useragent' => :user_agent,
|
27
|
+
'version' => :version,
|
28
|
+
'majorver' => :major_version,
|
29
|
+
'minorver' => :minor_version,
|
30
|
+
'majorversion' => :major_version,
|
31
|
+
'minorversion' => :minor_version,
|
32
|
+
'backgroundsounds' => :background_sounds,
|
33
|
+
'javaapplets' => :java_applets,
|
34
|
+
'activexcontrols' => :activex_controls,
|
35
|
+
'isbanned' => :banned,
|
36
|
+
'ismobiledevice' => :mobile_device,
|
37
|
+
'mobiledevice' => :mobile_device,
|
38
|
+
'issyndicationreader' => :syndication_reader,
|
39
|
+
'syndicationreader' => :syndication_reader,
|
40
|
+
'cssversion' => :css_version,
|
41
|
+
'aolversion' => :aol_version,
|
42
|
+
'useragentid' => :user_agent_id,
|
43
|
+
'supportscss' => :css,
|
44
|
+
'renderingengine_name' => :rendering_engine_name,
|
45
|
+
'renderingengine_version' => :rendering_engine_version,
|
46
|
+
'renderingengine_description' => :rendering_engine_description
|
47
|
+
}
|
48
|
+
|
49
|
+
def initialize(hash = {})
|
50
|
+
self.merge!(hash)
|
51
|
+
end
|
52
|
+
|
53
|
+
def each
|
54
|
+
self.to_hash.each do |k, v|
|
55
|
+
yield k, v
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_hash
|
60
|
+
@to_hash ||= ACCESSORS.inject({}) do |memo, accessor|
|
61
|
+
memo[accessor] = self.send(accessor)
|
62
|
+
memo
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def []=(key, value)
|
67
|
+
key = key.to_s.downcase
|
68
|
+
key = "#{MAP[key] || key}="
|
69
|
+
self.send(key, value) if self.respond_to?(key)
|
70
|
+
end
|
71
|
+
|
72
|
+
def [](key)
|
73
|
+
key = key.to_s.downcase
|
74
|
+
key = MAP[key] || key
|
75
|
+
self.send(key) if self.respond_to?(key)
|
76
|
+
end
|
77
|
+
|
78
|
+
def merge!(parent)
|
79
|
+
parent = parent.to_hash if parent.is_a?(UserAgent)
|
80
|
+
ACCESSORS.each do |k, v|
|
81
|
+
if parent.has_key?(k)
|
82
|
+
val = self.send(k)
|
83
|
+
if val == 'default' || val.nil?
|
84
|
+
self.send("#{k}=", parent[k])
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
def ==(other)
|
92
|
+
ACCESSORS.each do |accessor|
|
93
|
+
return false if self.send(accessor) != other.send(accessor)
|
94
|
+
end
|
95
|
+
true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
data/lib/browscapper.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# encoding: BINARY
|
2
|
+
|
3
|
+
module Browscapper
|
4
|
+
autoload :UserAgent, 'browscapper/user_agent'
|
5
|
+
autoload :Reader, 'browscapper/reader'
|
6
|
+
autoload :CSVReader, 'browscapper/reader/csv_reader'
|
7
|
+
autoload :YAMLReader, 'browscapper/reader/yaml_reader'
|
8
|
+
autoload :INIReader, 'browscapper/reader/ini_reader'
|
9
|
+
autoload :MarshalReader, 'browscapper/reader/marshal_reader'
|
10
|
+
|
11
|
+
class << self
|
12
|
+
attr_reader :entries, :file
|
13
|
+
MATCH_CACHE = Hash.new
|
14
|
+
|
15
|
+
def load(file = File.join(%w{ . browscap.ini }))
|
16
|
+
if !File.exists?(file)
|
17
|
+
raise ArgumentError.new("File #{file} not found.")
|
18
|
+
end
|
19
|
+
|
20
|
+
reader = case file.downcase
|
21
|
+
when /\.csv$/
|
22
|
+
CSVReader
|
23
|
+
when /\.ya?ml$/
|
24
|
+
YAMLReader
|
25
|
+
when /\.ini$/
|
26
|
+
INIReader
|
27
|
+
else
|
28
|
+
MarshalReader
|
29
|
+
end
|
30
|
+
|
31
|
+
@file = file
|
32
|
+
@entries = reader.load(file)
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def dump(format = :marshal)
|
37
|
+
@entries or Browscapper.load
|
38
|
+
|
39
|
+
Marshal.dump(@entries)
|
40
|
+
end
|
41
|
+
|
42
|
+
def clear_cache
|
43
|
+
MATCH_CACHE.clear
|
44
|
+
end
|
45
|
+
|
46
|
+
def matches(ua)
|
47
|
+
@entries or self.load
|
48
|
+
|
49
|
+
ua_str = ua.downcase
|
50
|
+
ua_len = ua.length
|
51
|
+
|
52
|
+
MATCH_CACHE[ua] ||= @entries.select { |k, v|
|
53
|
+
v[:pattern] =~ ua_str if v
|
54
|
+
}.sort_by { |(k, v)|
|
55
|
+
ua_len - v[:user_agent].gsub(/[?*]/, '').length
|
56
|
+
}.collect { |(k, v)|
|
57
|
+
v
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def match(ua)
|
62
|
+
if MATCH_CACHE[ua] && !MATCH_CACHE[ua].empty?
|
63
|
+
MATCH_CACHE[ua].first
|
64
|
+
else
|
65
|
+
m = self.matches(ua)
|
66
|
+
m.first if !m.empty?
|
67
|
+
end
|
68
|
+
end
|
69
|
+
alias :query :match
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# encoding: BINARY
|
2
|
+
|
3
|
+
$: << './lib'
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'minitest/autorun'
|
7
|
+
require './lib/browscapper'
|
8
|
+
|
9
|
+
class BrowscapperTest < MiniTest::Unit::TestCase
|
10
|
+
FIREFOX_ON_OSX = Browscapper::UserAgent.new({
|
11
|
+
:activex_controls => false,
|
12
|
+
:alpha => false,
|
13
|
+
:aol_version => 0,
|
14
|
+
:background_sounds => false,
|
15
|
+
:beta => false,
|
16
|
+
:browser => "Firefox",
|
17
|
+
:comment => "Firefox 3.6",
|
18
|
+
:cookies => true,
|
19
|
+
:crawler => false,
|
20
|
+
:css_version => 3,
|
21
|
+
:device_name => "unknown",
|
22
|
+
:device_maker => "unknown",
|
23
|
+
:frames => true,
|
24
|
+
:iframes => true,
|
25
|
+
:java_applets => true,
|
26
|
+
:javascript => true,
|
27
|
+
:major_version => 3,
|
28
|
+
:minor_version => 6,
|
29
|
+
:mobile_device => false,
|
30
|
+
:parent => "Firefox 3.6",
|
31
|
+
:platform => "MacOSX",
|
32
|
+
:platform_version => "unknown",
|
33
|
+
:rendering_engine_name => "Gecko",
|
34
|
+
:rendering_engine_version => "unknown",
|
35
|
+
:rendering_engine_description => "For Firefox, Camino, K-Meleon, SeaMonkey, Netscape, and other Gecko-based browsers.",
|
36
|
+
:syndication_reader => false,
|
37
|
+
:tables => true,
|
38
|
+
:user_agent => "Mozilla/5.0 (Macintosh; *; *Mac OS X*; *; rv:1.9.2*) Gecko/* Firefox/3.6*",
|
39
|
+
:user_agent_id => nil,
|
40
|
+
:vbscript => false,
|
41
|
+
:version => "3.6",
|
42
|
+
:win16 => false,
|
43
|
+
:win32 => false,
|
44
|
+
:win64 => false,
|
45
|
+
})
|
46
|
+
|
47
|
+
FIREFOX_ON_OSX.pattern = /^mozilla\/5\.0 \(macintosh; .*; .*mac os x.*; .*; rv:1\.9\.2.*\) gecko\/.* firefox\/3\.6.*$/
|
48
|
+
|
49
|
+
def test_browscap_ini
|
50
|
+
Browscapper.load(File.join('vendor', 'browscap.dump'))
|
51
|
+
match = Browscapper.match('Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8')
|
52
|
+
|
53
|
+
FIREFOX_ON_OSX.each do |k, v|
|
54
|
+
assert(v == match[k], " Expected #{v.inspect} for #{k}, got #{match[k].inspect}")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
# encoding: BINARY
|
3
|
+
|
4
|
+
$: << './lib'
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'minitest/autorun'
|
8
|
+
require 'minitest/benchmark' if ENV['BENCH']
|
9
|
+
require './lib/browscapper'
|
10
|
+
require 'benchmark'
|
11
|
+
|
12
|
+
class ReaderTests < MiniTest::Unit::TestCase
|
13
|
+
def bench_csv
|
14
|
+
assert_performance_constant do |n|
|
15
|
+
Browscapper.load('vendor/browscap.csv')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def bench_yaml
|
20
|
+
assert_performance_constant do |n|
|
21
|
+
Browscapper.load('vendor/browscap.yml')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def bench_ini
|
26
|
+
assert_performance_constant do |n|
|
27
|
+
Browscapper.load('vendor/browscap.ini')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def bench_marshal
|
32
|
+
assert_performance_constant do |n|
|
33
|
+
Browscapper.load('vendor/browscap.dump')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
metadata
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: browscapper
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: !binary |-
|
5
|
+
MC4wLjE=
|
6
|
+
prerelease:
|
7
|
+
platform: ruby
|
8
|
+
authors:
|
9
|
+
- J Smith
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-09-29 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rdoc
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ! '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '0'
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: rake
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ~>
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0.9'
|
39
|
+
type: :runtime
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0.9'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: minitest
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: inifile
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
type: :runtime
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
description: A browscap file parser and matcher.
|
80
|
+
email: dark.panda@gmail.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files:
|
84
|
+
- README.rdoc
|
85
|
+
files:
|
86
|
+
- .gitignore
|
87
|
+
- Gemfile
|
88
|
+
- MIT-LICENSE
|
89
|
+
- README.rdoc
|
90
|
+
- Rakefile
|
91
|
+
- browscapper.gemspec
|
92
|
+
- lib/browscapper.rb
|
93
|
+
- lib/browscapper/reader.rb
|
94
|
+
- lib/browscapper/reader/csv_reader.rb
|
95
|
+
- lib/browscapper/reader/ini_reader.rb
|
96
|
+
- lib/browscapper/reader/marshal_reader.rb
|
97
|
+
- lib/browscapper/reader/yaml_reader.rb
|
98
|
+
- lib/browscapper/user_agent.rb
|
99
|
+
- lib/browscapper/version.rb
|
100
|
+
- test/browscapper_tests.rb
|
101
|
+
- test/reader_tests.rb
|
102
|
+
homepage: http://github.com/dark-panda/browscapper
|
103
|
+
licenses: []
|
104
|
+
post_install_message:
|
105
|
+
rdoc_options: []
|
106
|
+
require_paths:
|
107
|
+
- lib
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ! '>='
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ! '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
requirements: []
|
121
|
+
rubyforge_project:
|
122
|
+
rubygems_version: 1.8.24
|
123
|
+
signing_key:
|
124
|
+
specification_version: 3
|
125
|
+
summary: A browscap file parser and matcher.
|
126
|
+
test_files:
|
127
|
+
- test/browscapper_tests.rb
|
128
|
+
- test/reader_tests.rb
|