wurfl 1.0.2
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.
- data/.gitignore +2 -0
- data/LICENSE +35 -0
- data/README.rdoc +144 -0
- data/Rakefile +60 -0
- data/VERSION +1 -0
- data/bin/uaproftowurfl.rb +28 -0
- data/bin/uaprofwurflcomparator.rb +223 -0
- data/bin/wurflcomparator.rb +86 -0
- data/bin/wurflinspector.rb +141 -0
- data/bin/wurflloader.rb +127 -0
- data/bin/wurflsanitycheck.rb +39 -0
- data/lib/wurfl/handset.rb +117 -0
- data/lib/wurfl/loader.rb +130 -0
- data/lib/wurfl/uaproftowurfl.rb +1013 -0
- data/lib/wurfl/utils.rb +31 -0
- data/test/data/wurfl.simple.xml +38 -0
- data/test/handset_test.rb +70 -0
- data/test/loader_test.rb +29 -0
- data/wurfl.gemspec +59 -0
- metadata +79 -0
@@ -0,0 +1,86 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
4
|
+
|
5
|
+
require "getoptlong"
|
6
|
+
require "wurfl/handset"
|
7
|
+
require "wurfl/utils"
|
8
|
+
|
9
|
+
include Wurfl::Utils
|
10
|
+
|
11
|
+
def usage
|
12
|
+
puts "Usage: wurflcomparator.rb wurfl_pstore1_db wurfl_pstore2_db "
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
|
16
|
+
if ARGV.size != 2
|
17
|
+
usage
|
18
|
+
end
|
19
|
+
|
20
|
+
# load the wurfl databases
|
21
|
+
wurfl1 = wurfl2 = nil
|
22
|
+
begin
|
23
|
+
wurfl1, = load_wurfl_pstore(ARGV[0])
|
24
|
+
wurfl2, = load_wurfl_pstore(ARGV[1])
|
25
|
+
rescue => err
|
26
|
+
efile = ""
|
27
|
+
if wurfl1.nil?
|
28
|
+
efile = ARGV[0]
|
29
|
+
else
|
30
|
+
efile = ARGV[1]
|
31
|
+
end
|
32
|
+
STDERR.puts "Error with file #{efile}"
|
33
|
+
STDERR.puts err.message
|
34
|
+
exit 1
|
35
|
+
end
|
36
|
+
|
37
|
+
puts "Comparing files: #{ARGV[0]} and #{ARGV[1]}"
|
38
|
+
puts "-------------------------------------"
|
39
|
+
|
40
|
+
if wurfl1.size > wurfl2.size
|
41
|
+
mwurfl = wurfl1
|
42
|
+
lwurfl = wurfl2
|
43
|
+
else
|
44
|
+
mwurfl = wurfl2
|
45
|
+
lwurfl = wurfl1
|
46
|
+
end
|
47
|
+
|
48
|
+
notfound = Array.new
|
49
|
+
different = Array.new
|
50
|
+
mwurfl.each do |key,handset|
|
51
|
+
if lwurfl.key?(key)
|
52
|
+
if handset != lwurfl[key]
|
53
|
+
different<< [handset,lwurfl[key]]
|
54
|
+
end
|
55
|
+
else
|
56
|
+
notfound<< handset
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
puts "Comparision complete."
|
62
|
+
|
63
|
+
puts "Not Found Handsets: #{notfound.size}"
|
64
|
+
puts "||||||||||||||||||||||||||||||||||||"
|
65
|
+
notfound = notfound.sort { |x,y| y.wurfl_id <=> x.wurfl_id }
|
66
|
+
notfound.each { |hand| puts hand.wurfl_id }
|
67
|
+
puts "||||||||||||||||||||||||||||||||||||"
|
68
|
+
|
69
|
+
puts "Different handsets: #{different.size}"
|
70
|
+
puts "||||||||||||||||||||||||||||||||||||"
|
71
|
+
different = different.sort { |x,y| y.first.wurfl_id <=> x.first.wurfl_id }
|
72
|
+
different.each do |hand1,hand2|
|
73
|
+
puts "-------------------------------------"
|
74
|
+
puts "Handset: #{hand1.user_agent} :ID: #{hand1.wurfl_id}"
|
75
|
+
diffkeys = hand1.compare(hand2)
|
76
|
+
diffkeys.each do |key,oval,oid|
|
77
|
+
next if hand1[key].nil? || hand2[key].nil?
|
78
|
+
puts "Key:#{key}"
|
79
|
+
puts "h1>:#{hand1[key]}"
|
80
|
+
puts "h2<:#{hand2[key]}"
|
81
|
+
end
|
82
|
+
puts "-------------------------------------"
|
83
|
+
end
|
84
|
+
|
85
|
+
puts "||||||||||||||||||||||||||||||||||||"
|
86
|
+
|
@@ -0,0 +1,141 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
4
|
+
|
5
|
+
require "getoptlong"
|
6
|
+
require "wurfl/handset"
|
7
|
+
require "wurfl/utils"
|
8
|
+
|
9
|
+
=begin
|
10
|
+
A class that lists wurfl handsets that match user specified search
|
11
|
+
criteria.
|
12
|
+
=end
|
13
|
+
class WurflInspector
|
14
|
+
|
15
|
+
# Constructor
|
16
|
+
# Parameters:
|
17
|
+
# handsets: A hashtable of wurfl handsets indexed by wurfl_id.
|
18
|
+
def initialize(handsets)
|
19
|
+
@handsets = handsets
|
20
|
+
end
|
21
|
+
|
22
|
+
# A method to get the WurflHandset that matches the passed wurfl_id.
|
23
|
+
# Parameters:
|
24
|
+
# id: is the wurfl_id of the WurflHandset to get.
|
25
|
+
# Returns:
|
26
|
+
# The WurflHandset that has the requested wurfl_id. If the wurfl_id
|
27
|
+
# is not found, then nil is returned.
|
28
|
+
def get_handset(id)
|
29
|
+
return @handsets[id]
|
30
|
+
end
|
31
|
+
|
32
|
+
# A method to retrieve a list of the inspector's handsets that match
|
33
|
+
# the passed search criteria.
|
34
|
+
# Parameters:
|
35
|
+
# proc: is a Proc object that defines a function that returns
|
36
|
+
# true or false from an evaluattion with a WurflHandset.
|
37
|
+
# Returns:
|
38
|
+
# An Array of all WurflHandsets that match the proc evaluation.
|
39
|
+
def search_handsets(proc)
|
40
|
+
rez = @handsets.values.select do |hand|
|
41
|
+
x = proc.call(hand)
|
42
|
+
end
|
43
|
+
return rez if rez != nil
|
44
|
+
return Array::new
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
include Wurfl::Utils
|
51
|
+
|
52
|
+
def usage
|
53
|
+
puts "Usage: wurflinspector.rb [-s rubyblock] [-i handsetid [-q attributename]] -d pstorefile"
|
54
|
+
puts "Examples:"
|
55
|
+
puts "wurflinspector.rb -d pstorehandsets.db -s '{ |hand| hand[\"colors\"].to_i > 2 }'"
|
56
|
+
puts "wurflinspector.rb -d pstorehandsets.db -i sonyericsson_t300_ver1"
|
57
|
+
puts "wurflinspector.rb -d pstorehandsets.db -i sonyericsson_t300_ver1 -q backlight"
|
58
|
+
exit 1
|
59
|
+
end
|
60
|
+
|
61
|
+
pstorefile = nil
|
62
|
+
procstr = nil
|
63
|
+
handset = nil
|
64
|
+
query = nil
|
65
|
+
begin
|
66
|
+
opt = GetoptLong.new(
|
67
|
+
["-d","--database", GetoptLong::REQUIRED_ARGUMENT],
|
68
|
+
["-s","--search", GetoptLong::REQUIRED_ARGUMENT],
|
69
|
+
["-i","--id", GetoptLong::REQUIRED_ARGUMENT],
|
70
|
+
["-q","--query", GetoptLong::REQUIRED_ARGUMENT]
|
71
|
+
)
|
72
|
+
|
73
|
+
opt.each do |arg,val|
|
74
|
+
case arg
|
75
|
+
when "-d"
|
76
|
+
pstorefile = val
|
77
|
+
when "-s"
|
78
|
+
procstr = val.strip
|
79
|
+
when "-i"
|
80
|
+
handset = val
|
81
|
+
when "-q"
|
82
|
+
query = val
|
83
|
+
else
|
84
|
+
usage
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
rescue => err
|
89
|
+
usage
|
90
|
+
end
|
91
|
+
|
92
|
+
if !pstorefile
|
93
|
+
puts "You must specify a Wurfl PStore db"
|
94
|
+
usage
|
95
|
+
end
|
96
|
+
|
97
|
+
begin
|
98
|
+
handsets, = load_wurfl_pstore(pstorefile)
|
99
|
+
insp = WurflInspector.new(handsets)
|
100
|
+
rescue => err
|
101
|
+
STDERR.puts "Error with file #{pstorefile}"
|
102
|
+
STDERR.puts err.message
|
103
|
+
exit 1
|
104
|
+
end
|
105
|
+
|
106
|
+
if procstr
|
107
|
+
pr = nil
|
108
|
+
eval("pr = proc#{procstr}")
|
109
|
+
|
110
|
+
if pr.class != Proc
|
111
|
+
puts "You must pass a valid ruby block!"
|
112
|
+
exit 1
|
113
|
+
end
|
114
|
+
|
115
|
+
puts "--------- Searching handsets -----------"
|
116
|
+
res = insp.search_handsets(pr)
|
117
|
+
puts "Number of results: #{res.size}"
|
118
|
+
|
119
|
+
res.each { |handset| puts handset.wurfl_id }
|
120
|
+
exit 0
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
if handset
|
125
|
+
handset = insp.get_handset(handset)
|
126
|
+
puts "Handset user agent: #{handset.user_agent}"
|
127
|
+
if query
|
128
|
+
puts "Result of handset query: #{query}"
|
129
|
+
rez = handset.get_value_and_owner(query)
|
130
|
+
puts "#{rez[0]} from #{rez[1]}"
|
131
|
+
else
|
132
|
+
puts "Attributes of handset"
|
133
|
+
keys = handset.keys
|
134
|
+
keys.each do |key|
|
135
|
+
rez = handset.get_value_and_owner(key)
|
136
|
+
puts "Attr:#{key} Val:#{rez[0]} from #{rez[1]}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
exit 0
|
140
|
+
end
|
141
|
+
|
data/bin/wurflloader.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
4
|
+
|
5
|
+
require "getoptlong"
|
6
|
+
require "wurfl/utils"
|
7
|
+
require "wurfl/loader"
|
8
|
+
|
9
|
+
include Wurfl::Utils
|
10
|
+
|
11
|
+
print = false
|
12
|
+
insert = false
|
13
|
+
verbose = false
|
14
|
+
wurflfile = nil
|
15
|
+
patchfile = nil
|
16
|
+
pstorefile = nil
|
17
|
+
pstoreload = false
|
18
|
+
|
19
|
+
def usage
|
20
|
+
puts "Usage: wurflloader.rb [-p -v -h -e patchfile] -f wurflfile"
|
21
|
+
puts " --file, -f (wurflfile): The master WURFL file to load."
|
22
|
+
puts " --extension, -e (patchfile): A patch file to extend the traits of the master WURLF file."
|
23
|
+
puts " --print, -p : Prints out handsets."
|
24
|
+
puts " --verbose, -v : Verbose output."
|
25
|
+
puts " --help, -h : Prints this message."
|
26
|
+
puts " --database, -d (databasename): Makes a PStore database for quick loading of data with other tools."
|
27
|
+
puts " --load, -l (databasename): Loads handsets from a PStore database instead of XML file."
|
28
|
+
exit 1
|
29
|
+
end
|
30
|
+
|
31
|
+
begin
|
32
|
+
options = GetoptLong.new(
|
33
|
+
["-p","--print", GetoptLong::NO_ARGUMENT],
|
34
|
+
["-h","--help", GetoptLong::NO_ARGUMENT],
|
35
|
+
["-v","--verbose", GetoptLong::NO_ARGUMENT],
|
36
|
+
["-f","--file", GetoptLong::REQUIRED_ARGUMENT],
|
37
|
+
["-e","--extension", GetoptLong::REQUIRED_ARGUMENT],
|
38
|
+
["-d","--database", GetoptLong::REQUIRED_ARGUMENT],
|
39
|
+
["-l","--load", GetoptLong::REQUIRED_ARGUMENT]
|
40
|
+
)
|
41
|
+
|
42
|
+
options.each do |opt,arg|
|
43
|
+
case opt
|
44
|
+
when "-p"
|
45
|
+
print = true
|
46
|
+
when "-v"
|
47
|
+
verbose = true
|
48
|
+
when "-h"
|
49
|
+
usage
|
50
|
+
exit 1
|
51
|
+
when "-f"
|
52
|
+
wurflfile = arg
|
53
|
+
when "-e"
|
54
|
+
patchfile = arg
|
55
|
+
when "-d"
|
56
|
+
pstorefile = arg
|
57
|
+
when "-l"
|
58
|
+
pstorefile = arg
|
59
|
+
pstoreload = true
|
60
|
+
else
|
61
|
+
STDERR.puts "Unknown argument #{opt}"
|
62
|
+
usage
|
63
|
+
exit 1
|
64
|
+
end
|
65
|
+
end
|
66
|
+
rescue => err
|
67
|
+
STDERR.puts "Error: #{err}"
|
68
|
+
usage
|
69
|
+
exit 1
|
70
|
+
end
|
71
|
+
|
72
|
+
wurfll = Wurfl::Loader.new
|
73
|
+
hands = nil
|
74
|
+
fallbacks = nil
|
75
|
+
|
76
|
+
if pstorefile && pstoreload
|
77
|
+
begin
|
78
|
+
puts "Loading data from #{pstorefile}"
|
79
|
+
hands, fallbacks = load_wurfl_pstore(pstorefile)
|
80
|
+
puts "Loaded"
|
81
|
+
rescue => err
|
82
|
+
STDERR.puts "Error: Cannot load PStore file."
|
83
|
+
STDERR.puts err.message
|
84
|
+
exit 1
|
85
|
+
end
|
86
|
+
else
|
87
|
+
if !wurflfile
|
88
|
+
STDERR.puts "You must pass a wurflfile if you want to do more."
|
89
|
+
usage
|
90
|
+
exit 1
|
91
|
+
end
|
92
|
+
|
93
|
+
starttime = Time.now
|
94
|
+
puts "Loading wurfl file #{wurflfile}"
|
95
|
+
|
96
|
+
wurfll.verbose = verbose
|
97
|
+
|
98
|
+
hands, fallbacks = wurfll.load_wurfl(wurflfile)
|
99
|
+
restime = Time.now - starttime
|
100
|
+
|
101
|
+
puts "Done loading wurfl. Load took #{restime} seconds."
|
102
|
+
|
103
|
+
if patchfile
|
104
|
+
starttime = Time.now
|
105
|
+
puts "Loading Patch file #{patchfile}"
|
106
|
+
hands, fallbacks = wurfll.load_wurfl(patchfile)
|
107
|
+
restime = Time.now - starttime
|
108
|
+
puts "Done loading patchfile. Load took #{restime} seconds."
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
if pstorefile && !pstoreload
|
114
|
+
begin
|
115
|
+
puts "Saving data into #{pstorefile}"
|
116
|
+
save_wurfl_pstore(pstorefile, hands, fallbacks)
|
117
|
+
puts "Saved"
|
118
|
+
rescue => err
|
119
|
+
STDERR.puts "Error: Cannot creat PStore file."
|
120
|
+
STDERR.puts err.message
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
if print
|
125
|
+
wurfll.print_wurfl hands
|
126
|
+
end
|
127
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
4
|
+
|
5
|
+
# A simple command line tool to make sure that a wurfl file properly parses.
|
6
|
+
# Used to make sure changes to Wurfl/Patch files are OK.
|
7
|
+
if ARGV.size != 1
|
8
|
+
puts "Must have the path of the wurfl file to check"
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
lines = File.open(ARGV[0],"r").readlines
|
12
|
+
|
13
|
+
curdev = nil
|
14
|
+
c = 0
|
15
|
+
|
16
|
+
lines.each do |line|
|
17
|
+
line = line.strip
|
18
|
+
if line =~ /^(<d)evice.*[^\/]>$/
|
19
|
+
curdev = line
|
20
|
+
elsif line =~ /^(<d)evice.*\/>$/
|
21
|
+
if curdev
|
22
|
+
puts "#{c}:A device was not closed and we got a new device! #{curdev}"
|
23
|
+
end
|
24
|
+
curdev = nil
|
25
|
+
elsif line =~ /^(<\/d)evice>$/
|
26
|
+
if curdev.nil?
|
27
|
+
puts "#{c}:A closing device with no opening! #{curdev}"
|
28
|
+
end
|
29
|
+
curdev=nil
|
30
|
+
end
|
31
|
+
c += 1
|
32
|
+
end
|
33
|
+
|
34
|
+
if curdev
|
35
|
+
puts "The curent device was not closed #{curdev}"
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
puts "Done"
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require "singleton"
|
2
|
+
|
3
|
+
module Wurfl; end
|
4
|
+
|
5
|
+
=begin
|
6
|
+
A class that represents a handset based on information taken from the WURFL.
|
7
|
+
=end
|
8
|
+
class Wurfl::Handset
|
9
|
+
|
10
|
+
extend Enumerable
|
11
|
+
|
12
|
+
attr_accessor :wurfl_id, :user_agent
|
13
|
+
|
14
|
+
# Constructor
|
15
|
+
# Parameters:
|
16
|
+
# wurfl_id: is the WURFL ID of the handset
|
17
|
+
# useragent: is the user agent of the handset
|
18
|
+
# fallback: is the fallback handset that this handset
|
19
|
+
# uses for missing details.
|
20
|
+
def initialize (wurfl_id, useragent, fallback = nil)
|
21
|
+
# A hash to hold keys and values specific to this handset
|
22
|
+
@capabilityhash = Hash::new
|
23
|
+
@wurfl_id = wurfl_id
|
24
|
+
@user_agent = useragent
|
25
|
+
@fallback = fallback || NullHandset.instance
|
26
|
+
end
|
27
|
+
|
28
|
+
def fallback=(v)
|
29
|
+
@fallback = v || NullHandset.instance
|
30
|
+
end
|
31
|
+
|
32
|
+
# Hash accessor
|
33
|
+
# Parameters:
|
34
|
+
# key: the WURFL key whose value is desired
|
35
|
+
# Returns:
|
36
|
+
# The value of the key, nil if the handset does not have the key.
|
37
|
+
def [] (key)
|
38
|
+
@capabilityhash.key?(key) ? @capabilityhash[key] : @fallback[key]
|
39
|
+
end
|
40
|
+
|
41
|
+
# like the above accessor, but also to know who the value
|
42
|
+
# comes from
|
43
|
+
# Returns:
|
44
|
+
# the value and the id of the handset from which the value was obtained
|
45
|
+
def get_value_and_owner(key)
|
46
|
+
if @capabilityhash.key?(key)
|
47
|
+
[ @capabilityhash[key], @wurfl_id ]
|
48
|
+
else
|
49
|
+
@fallback.get_value_and_owner(key)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Setter, A method to set a key and value of the handset.
|
54
|
+
def []= (key,val)
|
55
|
+
@capabilityhash[key] = val
|
56
|
+
end
|
57
|
+
|
58
|
+
# A Method to iterate over all of the keys and values that the handset has.
|
59
|
+
# Note: this will abstract the hash iterator to handle all the lower level
|
60
|
+
# calls for the fallback values.
|
61
|
+
def each
|
62
|
+
self.keys.each do |key|
|
63
|
+
# here is the magic that gives us the key and value of the handset
|
64
|
+
# all the way up to the fallbacks end.
|
65
|
+
# Call the pass block with the key and value passed
|
66
|
+
yield key, self[key]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# A method to get all of the keys that the handset has.
|
71
|
+
def keys
|
72
|
+
@capabilityhash.keys | @fallback.keys
|
73
|
+
end
|
74
|
+
|
75
|
+
# A method to do a simple equality check against two handsets.
|
76
|
+
# Parameter:
|
77
|
+
# other: Is the another WurflHandset to check against.
|
78
|
+
# Returns:
|
79
|
+
# true if the two handsets are equal in all values.
|
80
|
+
# false if they are not exactly equal in values, id and user agent.
|
81
|
+
# Note: for a more detailed comparison, use the compare method.
|
82
|
+
def ==(other)
|
83
|
+
return false unless other.instance_of?(Wurfl::Handset)
|
84
|
+
return false unless self.wurfl_id == other.wurfl_id && self.user_agent == other.user_agent
|
85
|
+
other.each do |key,value|
|
86
|
+
return false if value != self[key]
|
87
|
+
end
|
88
|
+
true
|
89
|
+
end
|
90
|
+
|
91
|
+
# A method to compare a handset's values against another handset.
|
92
|
+
# Parameters:
|
93
|
+
# other: is the another WurflHandset to compare against
|
94
|
+
# Returns:
|
95
|
+
# An array of the different values.
|
96
|
+
# Each entry in the Array is an Array of three values.
|
97
|
+
# The first value is the key in which both handsets have different values.
|
98
|
+
# The second is the other handset's value for the key.
|
99
|
+
# The third is the handset id from where the other handset got it's value.
|
100
|
+
def compare(other)
|
101
|
+
differences = Array.new
|
102
|
+
self.keys.each do |key|
|
103
|
+
oval,oid = other.get_value_and_owner(key)
|
104
|
+
if @capabilityhash[key].to_s != oval.to_s
|
105
|
+
differences << [key,oval,oid]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
differences
|
109
|
+
end
|
110
|
+
|
111
|
+
class NullHandset
|
112
|
+
include Singleton
|
113
|
+
def [](key) nil end
|
114
|
+
def get_value_and_owner(key) [ nil, nil ] end
|
115
|
+
def keys; [] end
|
116
|
+
end
|
117
|
+
end
|