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 ADDED
@@ -0,0 +1,2 @@
1
+ *.swp
2
+ pkg/*
data/LICENSE ADDED
@@ -0,0 +1,35 @@
1
+ Copyright (c) 2009, mobalean (http://www.mobalean.com/)
2
+
3
+ Copyright (c) 2003, Ubiquitous Business Technology (http://ubit.com)
4
+ All rights reserved.
5
+
6
+
7
+ Redistribution and use in source and binary forms, with or without
8
+ modification, are permitted provided that the following conditions are
9
+ met:
10
+
11
+
12
+ * Redistributions of source code must retain the above copyright
13
+ notice, this list of conditions and the following disclaimer.
14
+
15
+ * Redistributions in binary form must reproduce the above
16
+ copyright notice, this list of conditions and the following
17
+ disclaimer in the documentation and/or other materials provided
18
+ with the distribution.
19
+
20
+ * Neither the name of the WURFL nor the names of its
21
+ contributors may be used to endorse or promote products derived
22
+ from this software without specific prior written permission.
23
+
24
+
25
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.rdoc ADDED
@@ -0,0 +1,144 @@
1
+ = WURFL
2
+
3
+ WURFL is a collection of libraries and command line tools for using and manipulating the WURFL. To start using it, simply install the gem:
4
+
5
+ sudo gem install wurfl
6
+
7
+ == Tools
8
+
9
+ === wurflloader.rb
10
+
11
+ Is used to parse and load a WURFL XML file into memory or save a
12
+ PStore database that is used by most of the other tools. This
13
+ application creates WURFL PStore databases that are essential for use
14
+ with the other Ruby applications.
15
+
16
+ === wurflinspector.rb
17
+
18
+ Is a tool that will let you do various searches and queries on the
19
+ WURFL. This is a very simple, yet powerful program for finding
20
+ information about the handsets in the WURFL. See the wurflinspector
21
+ examples section for usage.
22
+
23
+ ==== Usage
24
+
25
+ The command below will search through all handsets and return the ids
26
+ of handsets that match the passed Ruby boolean evaluation
27
+ *) This command will return all handsets that have more than 2 colors.
28
+ "wurflinspector.rb -d pstorehandsets.db -s '{ |hand| hand["colors"].to_i > 2 }'"
29
+ The Ruby query must go in between the single quote marks and needs to
30
+ declare the WurflHandset instance variable name.
31
+
32
+ *) This command shows you how you can cheat with the current design of
33
+ the wurflinspector and print more user friendly results. This example
34
+ assumes you have the command line programs sort and uniq, but that is
35
+ only to make the output look better. This example does the same as the
36
+ above, except that it prints out the brand name and model name of the
37
+ matching handsets instead of the WURFL id.
38
+
39
+ Note: this should all go into one command line call
40
+ "wurflinspector.rb -d pstorehandsets.db -s '{|hand| puts
41
+ "#{hand["brand_name"]} #{hand["model_name"]}" if hand["colors"].to_i >
42
+ 2}' | sort | uniq"
43
+
44
+
45
+ The following individual handset query commands will tell the value of
46
+ capabilities and from where it obtained the setting.
47
+
48
+ *) A command to query the handset with the id sonyericsson_t300_ver1
49
+ for all of its' capabilities:
50
+ "wurflinspector.rb -d pstorehandsets.db -i sonyericsson_t300_ver1"
51
+
52
+ *) A command to query the handset with the id sonyericsson_t300_ver1 for
53
+ backlight capability:
54
+ "wurflinspector.rb -d pstorehandsets.db -i sonyericsson_t300_ver1 -q
55
+ backlight"
56
+
57
+ === wurflsanitycheck.rb
58
+
59
+ Is a partial WURFL validating program. It does a few simple
60
+ checks to make sure the XML structure is parse-able by the wurflloader.
61
+ If you receive loading errors by the wurflloader, then you can run the
62
+ wurflsanitycheck program to find the lines in the XML file that might
63
+ be causing the problem.
64
+
65
+ === wurflcomparator.rb
66
+
67
+ Is a another simple program that will find the differences from two
68
+ WURFL Ruby PStore databases. This is another way of finding changes
69
+ from the different versions of the WURFL without running a diff on the
70
+ XML files.
71
+
72
+ === uaproftowurfl.rb
73
+
74
+ Is a program that takes UAProfiles and creates an equivalent WURFL
75
+ entry. It holds all of the mappings used to convert a UAProfile to a
76
+ WURFL entry. This program alone makes using the Ruby tools worth it.
77
+
78
+ The mappings are not fully complete and can certainly use your input
79
+ in improving them.
80
+
81
+ About the source code:
82
+ The main piece of code to read are the methods under the
83
+ "UAProfile Mappings" comment.
84
+
85
+ For now you can ignore the details above this comment.
86
+ Basically each method is the name of a UAProfifle component.
87
+ When you parse a UAProfile file it will call each UAProfile
88
+ component's method name.
89
+
90
+ So you can simply look at UAProfile component's method to see
91
+ how it maps to the WURFL.
92
+ If a component is not found as a method it will be logged to
93
+ Standard Error. For this log one can then add the method to
94
+ the UAProfToWurfl class later.
95
+
96
+ ==== Usage
97
+
98
+ A simple usage is:
99
+
100
+ ruby uaproftowurfl.rb sampleprofile.xml
101
+
102
+ Example use from a bash shell with many profiles:
103
+
104
+ for i in `ls profiles`; do
105
+ ruby uaproftowurfl.rb profiles/$i >output/$i.wurfl
106
+ 2> output/$i.parser.err;
107
+ done
108
+
109
+ This assumes that you have a profiles directory with all of the UAProf
110
+ file you wish to parse and a output directory to place the results and
111
+ errors.
112
+
113
+ === uaprofwurflcomparator.rb
114
+
115
+ Is a program that compares UAProfiles against the equivalent WURFL
116
+ entries. It takes a file that contains an UAProfile URL and an
117
+ User-Agent per line, in addition to a Ruby Wurfl PStore database. It
118
+ then compares the UAProfile against the Wurfl that matches the same
119
+ User Agent. The output is a list of the differences and a WURFL
120
+ formatted entry showing the differences.
121
+
122
+ ==== Usage
123
+
124
+ You pass the program a directory to save the UAProfile files taken
125
+ from the given URL, a file that holds the UAProfil URL and User-Agent
126
+ mappings, and the Ruby PStore database that holds the WURFL.
127
+
128
+ The following is a simple example of execution
129
+ uaprofwurflcomparator.rb -d ./profiles -f all-profile.2003-08.log -c -w wurfl.db
130
+
131
+ == Authors
132
+
133
+ Zev Blut (Original Author)
134
+ Paul McMahon (gem installer, refactorings)
135
+
136
+ == Copyright
137
+
138
+ Copyright (c) 2009, mobalean (http://www.mobalean.com/)
139
+
140
+ Copyright (c) 2003, Ubiquitous Business Technology (http://ubit.com)
141
+ All rights reserved.
142
+
143
+ Send enquiries to: mailto:info@mobalean.com
144
+
data/Rakefile ADDED
@@ -0,0 +1,60 @@
1
+ require 'rake/rdoctask'
2
+ require 'rake/testtask'
3
+ require 'rubygems'
4
+
5
+ task :default => ['test']
6
+
7
+ Rake::TestTask.new(:test) do |t|
8
+ t.test_files = FileList['test/*_test.rb']
9
+ t.ruby_opts = ['-rubygems'] if defined? Gem
10
+ end
11
+
12
+ begin
13
+ require 'jeweler'
14
+ Jeweler::Tasks.new do |s|
15
+ s.name = "wurfl"
16
+ s.summary = "Library and tools for manipulating the WURFL"
17
+ s.description = "Library and tools for manipulating the WURFL"
18
+ s.email = "info@mobalean.com"
19
+ s.homepage = "http://github.com/pwim/wurfl"
20
+ s.description = "TODO"
21
+ s.authors = ["Paul McMahon", "Zev Blut"]
22
+ s.rubyforge_project = 'wurfl'
23
+ end
24
+ rescue LoadError
25
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
26
+ end
27
+
28
+ Rake::RDocTask.new(:rdoc) do |rdoc|
29
+ rdoc.rdoc_dir = 'rdoc'
30
+ rdoc.title = 'WURFL'
31
+ rdoc.main = "README.rdoc"
32
+ rdoc.rdoc_files.include("README.rdoc", "lib/**/*.rb")
33
+ end
34
+
35
+ begin
36
+ require 'rake/contrib/sshpublisher'
37
+ namespace :rubyforge do
38
+
39
+ desc "Release gem and RDoc documentation to RubyForge"
40
+ task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
41
+
42
+ namespace :release do
43
+ desc "Publish RDoc to RubyForge."
44
+ task :docs => [:rdoc] do
45
+ config = YAML.load(
46
+ File.read(File.expand_path('~/.rubyforge/user-config.yml'))
47
+ )
48
+
49
+ host = "#{config['username']}@rubyforge.org"
50
+ remote_dir = "/var/www/gforge-projects/wurfl/"
51
+ local_dir = 'rdoc'
52
+
53
+ Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
54
+ end
55
+ end
56
+ end
57
+ rescue LoadError
58
+ puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
59
+ end
60
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.2
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require "wurfl/uaproftowurfl"
5
+
6
+ # The code below is called if this file is executed from the command line.
7
+
8
+ def usage
9
+ puts "Usage: usaprofparser.rb uaprof_files"
10
+ puts "No files passed to parse."
11
+ exit 1
12
+ end
13
+
14
+ if ARGV.size == 0
15
+ usage
16
+ end
17
+
18
+ uaprof = Wurfl::UAProfToWURLF.new
19
+
20
+ # Parse all the files and merge them into one UAProf.
21
+ # Following profs take precedence of previous ones
22
+ ARGV.each do |file|
23
+ uaprof.parse_UAProf(file)
24
+ end
25
+
26
+ # Now output the mapped WURFL to standard out
27
+ uaprof.output_WURFL
28
+
@@ -0,0 +1,223 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
4
+
5
+ require "getoptlong"
6
+ require "net/http"
7
+
8
+ require "wurfl/uaproftowurfl"
9
+ require "wurfl/handset"
10
+ require "wurfl/utils"
11
+ include Wurfl::Utils
12
+
13
+ # An addition to the UAProf to Wurfl to generate a WurflHandset from the UAProf.
14
+ class Wurfl::UAProfToWURLF
15
+ def make_wurfl_handset
16
+ hand = Wurfl::Handset.new("UAProf",@wurfl["user_agent"])
17
+ @wurflgroups.each do |group|
18
+ @wurfl[group].sort.each do |key,value|
19
+ hand[key] = value
20
+ end
21
+ end
22
+ return hand
23
+ end
24
+ end
25
+
26
+
27
+ def parse_mapping_file(file)
28
+ if !File.exist?(file)
29
+ $stderr.puts "Mapping File does not exist. File passed was #{file}."
30
+ return Array.new
31
+ end
32
+ mappings = Array.new
33
+ f = File.new(file)
34
+ f.each do |line|
35
+ if m = /^"(.*)" "(.*)"$/.match(line.strip)
36
+ uaprof = m[1]
37
+ useragent = m[2]
38
+ mappings<< [uaprof,useragent]
39
+ else
40
+ $stderr.puts "Irregular format for line: #{line}" if line.strip != ""
41
+ end
42
+ end
43
+ f.close
44
+
45
+ return mappings
46
+ end
47
+
48
+ def get_uaprofile(uaprof,profiledir,check=false)
49
+ file = strip_uaprof(uaprof)
50
+ if File.exists?("#{profiledir}/#{file}") && check
51
+ return file
52
+ end
53
+
54
+ get_and_save_uaprof_file(uaprof,profiledir)
55
+ return file
56
+ end
57
+
58
+ def strip_uaprof(uaprof)
59
+ uaprof_file = nil
60
+ if m = /([^\/]*)$/.match(uaprof)
61
+ uaprof_file = m[1]
62
+ else
63
+ $stderr.puts "Cannot find the base UAProf file in URI: #{uaprof}"
64
+ end
65
+ return uaprof_file
66
+ end
67
+
68
+ def load_pstore(pstorefile)
69
+ hands = Hash.new
70
+ begin
71
+ handsid, = load_wurfl_pstore(pstorefile)
72
+ handsid.each { |id,val| hands[val.user_agent] = val }
73
+ rescue => err
74
+ $stderr.puts "Error: Cannot load PStore file. #{pstorefile}"
75
+ $stderr.puts err.message
76
+ exit 1
77
+ end
78
+ return hands
79
+ end
80
+
81
+ def get_and_save_uaprof_file(uaprof_url,savedirectory,limit=0)
82
+ base,path,port = parse_url(uaprof_url)
83
+
84
+ raise "Too many redirects from original url" if limit > 3
85
+ raise "Unparseable URL: #{url}" if base.nil?
86
+
87
+ port = 80 if port.nil?
88
+ http = Net::HTTP.new(base,port)
89
+ begin
90
+ resp, data = http.get(path)
91
+ if resp.code == "301"
92
+ # get location and call self again
93
+ http.finish
94
+ limit += 1
95
+ get_and_save_uaprof_file(resp['location'],savedirectory,limit)
96
+ return
97
+ elsif resp.code != "200"
98
+ raise "Unexpected HTTP Response code:#{resp.code} for #{uaprof_url}"
99
+ end
100
+ rescue => err
101
+ raise
102
+ end
103
+
104
+ f = File.new("#{savedirectory}/#{strip_uaprof(path)}","w")
105
+ f.write(data)
106
+ f.close
107
+
108
+ end
109
+
110
+ def parse_url(url)
111
+ m = /(http:\/\/)?(.*?)(:(\d*))?\/(.*)/i.match(url.strip)
112
+
113
+ return nil if m.nil?
114
+ return m[2],"/#{m[5]}",m[4]
115
+ end
116
+
117
+ def usage
118
+ puts "Usage: uaprofwurflcomparator.rb -d profiledirectory -f mappingfile [-w wurfldb] [-c] [-h | --help]"
119
+ puts "Examples:"
120
+ puts "uaprofwurflcomparator.rb -d ./profiles -f all-profile.2003-08.log -c -w wurfl.db"
121
+ exit 1
122
+ end
123
+
124
+ def help
125
+ puts "-d --directory : The directory to store the UA Profiles found in the log file."
126
+ puts "-f --file : The log file that has a UAProfile to User-Agent mapping per line."
127
+ puts "-c --check : A flag that will make sure to check if the profile is already in the directory or not. If it is not then it will download it."
128
+ puts "-w --wurfldb : A Ruby PStore Database of the WURFL, that is used to compare against the UAProfiles."
129
+ puts "-h --help : This message."
130
+ exit 1
131
+ end
132
+
133
+ profiledirectory = mappingfile = pstorefile = nil
134
+ existancecheck = false
135
+ begin
136
+ opt = GetoptLong.new(
137
+ ["-d","--directory", GetoptLong::REQUIRED_ARGUMENT],
138
+ ["-f","--file", GetoptLong::REQUIRED_ARGUMENT],
139
+ ["-c","--check", GetoptLong::NO_ARGUMENT],
140
+ ["-h","--help", GetoptLong::NO_ARGUMENT],
141
+ ["-w","--wurfldb", GetoptLong::REQUIRED_ARGUMENT]
142
+ )
143
+
144
+ opt.each { |arg,val|
145
+ case arg
146
+ when "-d"
147
+ profiledirectory = val.strip
148
+ when "-f"
149
+ mappingfile = val.strip
150
+ when "-c"
151
+ existancecheck = true
152
+ when "-h"
153
+ help
154
+ when "-w"
155
+ pstorefile = val.strip
156
+ else
157
+ usage
158
+ end
159
+ }
160
+ usage if mappingfile.nil? || profiledirectory.nil?
161
+ rescue => err
162
+ usage
163
+ end
164
+
165
+ profiles = Hash.new
166
+ duplicates = Hash.new
167
+ mappings = parse_mapping_file(mappingfile)
168
+ mappings.each do |uaprof,useragent|
169
+ begin
170
+ prof_file = get_uaprofile(uaprof,profiledirectory,existancecheck)
171
+ uaprof_mapper = UAProfToWURLF.new
172
+ if profiles.key?(useragent)
173
+ duplicates[useragent] = Array.new if !duplicates.key?(useragent)
174
+ duplicates[useragent]<<uaprof
175
+ next
176
+ end
177
+ uaprof_mapper.parse_UAProf("#{profiledirectory}/#{prof_file}")
178
+ profiles[useragent] = uaprof_mapper
179
+ rescue => err
180
+ $stderr.puts "Error: File #{prof_file}; User-Agent:#{useragent}"
181
+ $stderr.puts "Error:#{err.message}"
182
+ end
183
+ end
184
+
185
+ duplicates.each do |key,profs|
186
+ $stderr.puts "Duplicates exist for #{key}"
187
+ profs.each {|prof| $stderr.puts "-- #{prof}" }
188
+ end
189
+
190
+ exit 0 if !pstorefile
191
+
192
+ wurflhandsets = load_pstore(pstorefile)
193
+
194
+ puts "Comparing WURFL Handsets"
195
+ profiles.each do |key,val|
196
+ puts "",""
197
+
198
+ if !wurflhandsets.key?(key)
199
+ puts "UAProf has a new Handset: #{key}"
200
+ puts "--------------------------------"
201
+ val.output_WURFL
202
+ puts "--------------------------------"
203
+ else
204
+ uahand = val.make_wurfl_handset
205
+ res = uahand.compare(wurflhandsets[key])
206
+ if res.size > 0
207
+ puts "#{key} : For UAProf and WURFL differ"
208
+ res.each do |dkey,dval,did|
209
+ next if did == "generic"
210
+ #Key UAPROF Value WURFL Value WURFL source id
211
+ puts " Key:#{dkey}; UVAL:#{uahand[dkey]}; WVAL:#{dval}; WSRCID:#{did}"
212
+ end
213
+ #val["user_agent"] = key
214
+ puts ""
215
+ puts "WURFL Changes are:"
216
+ puts ""
217
+ val.output_WURFL(res.map {|entry| entry[0]})
218
+ else
219
+ puts "#{key} : For UAProf and WURFL match"
220
+ end
221
+ end
222
+ end
223
+