wurfl_device 0.1.5 → 0.1.6

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/.rvmrc CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
- ruby_string="1.9.2"
3
+ ruby_string="1.9.3"
4
4
  gemset_name="wurfl_device"
5
5
 
6
6
  if rvm list strings | grep -q "${ruby_string}" ; then
@@ -9,7 +9,7 @@ if rvm list strings | grep -q "${ruby_string}" ; then
9
9
  if [[ -d "${rvm_path:-$HOME/.rvm}/environments" && -s "${rvm_path:-$HOME/.rvm}/environments/${ruby_string}@${gemset_name}" ]] ; then
10
10
  \. "${rvm_path:-$HOME/.rvm}/environments/${ruby_string}@${gemset_name}"
11
11
  else
12
- rvm --create "${ruby_string}@${gemset_name}"
12
+ rvm --create "${ruby_string}@${gemset_name}"
13
13
  fi
14
14
 
15
15
  if ! command -v bundle ; then
data/README.md CHANGED
@@ -1,22 +1,46 @@
1
- # WurflDevice
1
+ WurflDevice
2
+ ===========
2
3
  Ruby client library for mobile handset detection
3
4
 
4
5
 
5
- requirements
6
+ Requirements
6
7
  ------------
7
- * [redis server](http://redis.io/)
8
+ * [redis server](http://redis.io/)
8
9
 
9
10
 
10
- usage
11
+ Installation
12
+ ------------
13
+ install using rubygems
14
+
15
+ gem install wurfl_device
16
+
17
+ or add to your Gemfile:
18
+
19
+ gem wurfl_device
20
+
21
+ and install it via Bundler:
22
+
23
+ $ bundle
24
+
25
+
26
+ Usage
11
27
  -----
12
28
 
13
29
  require 'wurfl_device'
14
30
 
31
+ get capabilities hash from user agent
32
+
15
33
  user_agent = 'Mozilla/5.0 (SymbianOS/9.2; U; Series60/3.1 NokiaN95_8GB/20.0.016; Profile/MIDP-2.0 Configuration/CLDC-1.1 ) AppleWebKit/413 (KHTML, like Gecko) Safari/413'
34
+ capabilities = WurflDevice.capabilities_from_user_agent(user_agent)
35
+
36
+ get capabilities hash from device id
16
37
 
17
- device = WurflDevice.get_device_from_ua(user_agent)
38
+ device_id = 'generic'
39
+ capabilities = WurflDevice.capabilities_from_id(device_id)
18
40
 
19
- # see device capabilities list for names
20
- puts device.id
41
+ get capability from user agent
21
42
 
43
+ user_agent = 'generic'
44
+ capability_name = 'id'
45
+ capability = WurflDevice.capabilities(capability_name, user_agent)
22
46
 
@@ -5,17 +5,23 @@ require 'raindrops'
5
5
 
6
6
  $stats ||= Raindrops::Middleware::Stats.new
7
7
 
8
- FileUtils.mkdir_p(WurflDevice::Constants::WEBSERVICE_ROOT) unless File.directory?(WurflDevice::Constants::WEBSERVICE_ROOT)
8
+ FileUtils.mkdir_p(WurflDevice::Settings::BASE_DIR) unless File.directory?(WurflDevice::Settings::BASE_DIR)
9
9
 
10
10
  app_env = ENV['RACK_ENV'] || 'production'
11
11
  app_root = ::File.expand_path('../..', __FILE__)
12
12
 
13
- timeout 60
13
+ app_timeout = 60
14
+ app_workers = WurflDevice::Settings::WEBSERVICE_WORKER
15
+ app_listen_socket = File.join(WurflDevice::Settings::BASE_DIR, WurflDevice::Settings::WEBSERVICE_SOCKET)
16
+ app_pid_file = File.join(WurflDevice::Settings::BASE_DIR, WurflDevice::Settings::WEBSERVICE_PID)
17
+ app_log_file = File.join(WurflDevice::Settings::BASE_DIR, WurflDevice::Settings::WEBSERVICE_LOG)
18
+
19
+ timeout app_timeout
14
20
  working_directory app_root
15
- worker_processes (app_env != 'development' ? WurflDevice::Constants::WEBSERVICE_WORKER : 1)
16
- listen File.join(WurflDevice::Constants::WEBSERVICE_ROOT, WurflDevice::Constants::WEBSERVICE_SOCKET), :backlog => 64
17
- pid File.join(WurflDevice::Constants::WEBSERVICE_ROOT, WurflDevice::Constants::WEBSERVICE_PID)
18
- stderr_path File.join(WurflDevice::Constants::WEBSERVICE_ROOT, WurflDevice::Constants::WEBSERVICE_LOG)
19
- stdout_path File.join(WurflDevice::Constants::WEBSERVICE_ROOT, WurflDevice::Constants::WEBSERVICE_LOG)
21
+ worker_processes (app_env != 'development' ? app_workers : 1)
22
+ listen app_listen_socket, :backlog => 64
23
+ pid app_pid_file
24
+ stderr_path app_log_file
25
+ stdout_path app_log_file
20
26
 
21
27
  preload_app false
@@ -0,0 +1,215 @@
1
+ require 'redis/connection/hiredis' unless defined?(FakeRedis)
2
+ require 'redis'
3
+
4
+ module WurflDevice
5
+ module Cache
6
+ class Entries
7
+ class << self
8
+ def clear
9
+ entries.each { |key| Cache.storage.del(build_cache_id(key)) }
10
+ end
11
+
12
+ def set(id, key, value)
13
+ Cache.storage.hset(build_cache_id(id), key, value)
14
+ end
15
+
16
+ def get(id, key)
17
+ Cache.storage.hget(build_cache_id(id), key)
18
+ end
19
+
20
+ def keys(id)
21
+ Cache.storage.hkeys(build_cache_id(id))
22
+ end
23
+
24
+ def keys_values(id)
25
+ Cache.storage.hgetall(build_cache_id(id))
26
+ end
27
+
28
+ def entries
29
+ entry_ids = Array.new
30
+ Cache.storage.keys(build_cache_id('*')).each do |key|
31
+ entry_ids << key.gsub(build_cache_id(''), '')
32
+ end
33
+ entry_ids
34
+ end
35
+
36
+ def get_value(id)
37
+ Cache.storage.get(build_cache_id(id))
38
+ end
39
+
40
+ def set_value(id, value)
41
+ Cache.storage.set(build_cache_id(id), value)
42
+ end
43
+
44
+ def build_cache_id(id)
45
+ "#{self.name}:#{id}"
46
+ end
47
+ end
48
+ end
49
+
50
+ class Devices < Entries; end
51
+ class UserAgents < Entries; end
52
+ class UserAgentsManufacturers < Entries; end
53
+
54
+ class Status < Entries
55
+ def self.version
56
+ keys 'version' || ''
57
+ end
58
+
59
+ def self.last_updated
60
+ get_value('last_updated') || ''
61
+ end
62
+ end
63
+
64
+ class << self
65
+ attr_writer :storage
66
+
67
+ def storage
68
+ @storage ||= Redis.new(:db => 7)
69
+ end
70
+
71
+ def clear
72
+ Status.clear
73
+ Devices.clear
74
+ UserAgents.clear
75
+ UserAgentsManufacturers.clear
76
+ end
77
+
78
+ def initialized?
79
+ Status.get_value('initialized').to_actual_value
80
+ end
81
+
82
+ def initialize_cache(xml_file)
83
+ version_list = Array.new
84
+
85
+ XmlLoader.load_xml_file(xml_file) do |capabilities|
86
+ version = capabilities.delete(:version)
87
+ version_list << version unless version.nil?
88
+
89
+ device_id = capabilities.delete('id')
90
+ next if device_id.nil? || device_id.empty?
91
+
92
+ user_agent = capabilities.delete('user_agent')
93
+ user_agent = Settings::GENERIC if user_agent.nil? || user_agent.empty?
94
+ user_agent.strip!
95
+
96
+ fall_back = capabilities.delete('fall_back')
97
+ fall_back = '' if fall_back.nil?
98
+ fall_back.strip!
99
+
100
+ Devices.set device_id, 'id', device_id
101
+ Devices.set device_id, 'user_agent', user_agent
102
+ Devices.set device_id, 'fall_back', fall_back
103
+
104
+ user_agent = UserAgent.new user_agent
105
+ UserAgents.set user_agent, 'id', device_id
106
+ unless user_agent =~ /^DO_NOT_MATCH/i
107
+ UserAgentsManufacturers.set user_agent.manufacturer, user_agent, device_id
108
+ end
109
+
110
+ capabilities.each_pair do |key, value|
111
+ if value.is_a?(Hash)
112
+ value.each_pair do |k, v|
113
+ Devices.set device_id, "#{key.to_s}:#{k.to_s}", v
114
+ end
115
+ else
116
+ Devices.set device_id, key.to_s, value
117
+ end
118
+ end
119
+ end
120
+
121
+ version_list.each do |ver|
122
+ Status.set 'version', ver, Time.now
123
+ end
124
+ Status.set_value 'last_updated', Time.now
125
+ Status.set_value 'initialized', true
126
+ end
127
+
128
+ def rebuild_user_agents
129
+ UserAgents.clear
130
+ UserAgentsManufacturers.clear
131
+ Devices.entries.each do |device_id|
132
+ device = Devices.keys_values device_id
133
+ user_agent = UserAgent.new device['user_agent']
134
+ UserAgents.set user_agent, 'id', device_id
135
+ unless user_agent =~ /^DO_NOT_MATCH/i
136
+ UserAgentsManufacturers.set user_agent.manufacturer, user_agent, device_id
137
+ end
138
+ end
139
+ end
140
+
141
+ def update_actual_capabilities(user_agent, capabilities)
142
+ capabilities.each_pair do |key, value|
143
+ if value.kind_of?(Hash)
144
+ value.each_pair do |k, v|
145
+ UserAgents.set user_agent, "#{key}:#{k}", v
146
+ end
147
+ elsif value.is_a?(Array)
148
+ value.each_with_index do |v, i|
149
+ UserAgents.set user_agent, "#{key}:#{i}", v
150
+ end
151
+ else
152
+ UserAgents.set user_agent, key, value
153
+ end
154
+ end
155
+ end
156
+
157
+ def parse_actual_capabilities(actual_capabilities)
158
+ capabilities = Capability.new
159
+
160
+ actual_capabilities.each_pair do |key, value|
161
+ if key =~ /^(.+)\:(\d+)$/i
162
+ capabilities[$1] ||= Array.new
163
+ capabilities[$1][$2.to_i] = value.to_actual_value
164
+ elsif key =~ /^(.+)\:(.+)$/i
165
+ capabilities[$1] ||= Capability.new
166
+ capabilities[$1][$2] = value.to_actual_value
167
+ else
168
+ capabilities[key] = value.to_actual_value
169
+ end
170
+ end
171
+
172
+ capabilities
173
+ end
174
+
175
+ def build_capabilities(device_id)
176
+ capabilities = Capability.new
177
+ actual_capabilities = Devices.keys_values(device_id)
178
+ return capabilities if actual_capabilities.nil?
179
+
180
+ capabilities['fall_back_tree'] ||= Array.new
181
+
182
+ if !actual_capabilities['fall_back'].nil? && !actual_capabilities['fall_back'].empty? && actual_capabilities['fall_back'] != 'root'
183
+ fall_back = build_capabilities(actual_capabilities['fall_back'])
184
+ if !fall_back.nil? && !fall_back['id'].nil? && !fall_back['id'].empty?
185
+ capabilities['fall_back_tree'].unshift(fall_back['id'])
186
+ fall_back.each_pair do |key, value|
187
+ if value.kind_of?(Hash)
188
+ capabilities[key] ||= Capability.new
189
+ value.each_pair do |k, v|
190
+ capabilities[key][k] = v.to_actual_value
191
+ end
192
+ elsif value.is_a?(Array)
193
+ capabilities[key] ||= Array.new
194
+ capabilities[key] |= value.to_actual_value
195
+ else
196
+ capabilities[key] = value.to_actual_value
197
+ end
198
+ end
199
+ end
200
+ end
201
+
202
+ actual_capabilities.each_pair do |key, value|
203
+ if key =~ /^(.+)\:(.+)$/i
204
+ capabilities[$1] ||= Capability.new
205
+ capabilities[$1][$2] = value.to_actual_value
206
+ else
207
+ capabilities[key] = value.to_actual_value
208
+ end
209
+ end
210
+
211
+ capabilities
212
+ end
213
+ end
214
+ end
215
+ end
@@ -3,7 +3,11 @@ module WurflDevice
3
3
  def initialize(hash={})
4
4
  super()
5
5
  hash.each do |key, value|
6
- self[convert_key(key)] = value
6
+ if value.kind_of?(Hash)
7
+ self[convert_key(key)] = Capability.new(value)
8
+ else
9
+ self[convert_key(key)] = value
10
+ end
7
11
  end
8
12
  end
9
13
 
@@ -1,6 +1,8 @@
1
+ # encoding: utf-8
1
2
  require 'thor'
2
- require 'yaml'
3
+ require 'benchmark'
3
4
  require 'wurfl_device'
5
+ require 'yaml'
4
6
 
5
7
  module WurflDevice
6
8
  class CLI < Thor
@@ -25,18 +27,19 @@ module WurflDevice
25
27
  super
26
28
  end
27
29
 
28
- desc "server [start|stop|restart|status]", "start a wurfl_device server"
29
- method_option "base-dir", :type => :string, :banner => "set base directory for data files", :aliases => "-d", :default => WurflDevice::Constants::WEBSERVICE_ROOT
30
- method_option :host, :type => :string, :banner => "set webservice host", :aliases => "-h", :default => WurflDevice::Constants::WEBSERVICE_HOST
31
- method_option :port, :type => :numeric, :banner => "set webservice port", :aliases => "-p", :default => WurflDevice::Constants::WEBSERVICE_PORT
32
- method_option :socket, :type => :string, :banner => "use unix domain socket", :aliases => "-s", :default => File.join(WurflDevice::Constants::WEBSERVICE_ROOT, WurflDevice::Constants::WEBSERVICE_SOCKET)
30
+ desc "webservice [start|stop|restart|status]", "start a wurfl_device server"
31
+ method_option "base-dir", :type => :string, :banner => "set base directory for data files", :aliases => "-d", :default => WurflDevice::Settings::BASE_DIR
32
+ method_option :host, :type => :string, :banner => "set webservice host", :aliases => "-h", :default => WurflDevice::Settings::WEBSERVICE_HOST
33
+ method_option :port, :type => :numeric, :banner => "set webservice port", :aliases => "-p", :default => WurflDevice::Settings::WEBSERVICE_PORT
34
+ method_option :worker, :type => :numeric, :banner => "set worker count", :aliases => "-w", :default => WurflDevice::Settings::WEBSERVICE_WORKER
35
+ method_option :socket, :type => :string, :banner => "use unix domain socket", :aliases => "-s", :default => File.join(WurflDevice::Settings::BASE_DIR, WurflDevice::Settings::WEBSERVICE_SOCKET)
33
36
  method_option :socket_only, :type => :boolean, :banner => "start as unix domain socket listener only", :aliases => "-t", :default => false
34
- def server(action=nil)
37
+ def webservice(action=nil)
35
38
  opts = options.dup
36
39
 
37
40
  action ||= 'status'
38
41
 
39
- pid_file = File.join(WurflDevice::Constants::WEBSERVICE_ROOT, WurflDevice::Constants::WEBSERVICE_PID)
42
+ pid_file = File.join(WurflDevice::Settings::BASE_DIR, WurflDevice::Settings::WEBSERVICE_PID)
40
43
  base_dir = opts['base-dir']
41
44
 
42
45
  FileUtils.mkdir_p(base_dir) unless File.directory?(base_dir)
@@ -77,102 +80,90 @@ module WurflDevice
77
80
 
78
81
  system(args.join(' '))
79
82
  end
83
+ FileUtils.rm_f(pid_file)
80
84
  elsif action == 'restart'
81
85
  server('stop')
86
+ sleep(0.3)
82
87
  server('start')
83
88
  else
84
89
  #status
85
90
  end
86
91
  end
92
+ map %w(server) => :webservice
87
93
 
88
94
  desc "dump DEVICE_ID|USER_AGENT", "display capabilities DEVICE_ID|USER_AGENT"
89
95
  method_option :json, :type => :boolean, :banner => "show the dump in json format", :aliases => "-j"
90
96
  method_option :yaml, :type => :boolean, :banner => "show the dump in yaml format", :aliases => "-y"
91
97
  def dump(device_id)
92
- device = WurflDevice.get_device_from_id(device_id)
93
- device = WurflDevice.get_device_from_ua(device_id) if device.nil?
98
+ capabilities = WurflDevice.capabilities_from_id(device_id)
99
+ capabilities = WurflDevice.capabilities_from_user_agent(device_id) if capabilities['id'].nil?
94
100
 
95
- if options.json?
96
- WurflDevice.ui.info device.capabilities.to_json
101
+ if capabilities.nil?
102
+ WurflDevice.ui.info "Nothing to dump"
103
+ elsif options.json?
104
+ WurflDevice.ui.info capabilities.to_json
97
105
  else
98
- WurflDevice.ui.info device.capabilities.to_yaml
106
+ WurflDevice.ui.info capabilities.to_yaml
99
107
  end
100
108
  end
101
109
 
102
110
  desc "list", "list user agent cache list"
111
+ method_option "matched-only", :type => :boolean, :banner => "show user agents that were matched", :aliases => "-m"
103
112
  def list
104
- WurflDevice.get_user_agents_in_cache.each do |user_agent|
105
- device = WurflDevice.get_device_from_ua_cache(user_agent)
106
- device_id = device.id || ''
107
- user_agent ||= ''
108
- WurflDevice.ui.info user_agent + ":" + device_id
113
+ matched_only = options['matched-only']
114
+ WurflDevice::Cache::UserAgents.entries.each do |user_agent|
115
+ kv = WurflDevice::Cache::UserAgents.keys_values user_agent
116
+ next if kv.count <= 1 && matched_only
117
+ WurflDevice.ui.info "#{user_agent}:#{kv['id']}"
109
118
  end
110
119
  end
111
120
 
112
- desc "update", "update the wurfl cache"
113
- method_option "clear-all", :type => :boolean, :banner => "remove all wurfl cache related"
114
- method_option "clear-dev", :type => :boolean, :banner => "clear the device cache first before updating"
115
- method_option "clear-ua", :type => :boolean, :banner => "remove all wurfl user agents cache"
116
- def update
117
- opts = options.dup
118
- if opts['clear-all']
119
- WurflDevice.ui.info "clearing all cache entries."
120
- WurflDevice.clear_devices
121
- WurflDevice.clear_cache
121
+ desc "init [WURFL_XML_FILE]", "initialize the wurfl device cache"
122
+ method_option :update, :type => :boolean, :banner => "don't clear previous cache", :aliases => "-u", :default => false
123
+ def init(xml_file=nil)
124
+ xml_file ||= Settings.default_wurfl_xml_file
125
+ unless options.update?
126
+ WurflDevice.ui.info "clearing existing device cache."
127
+ WurflDevice::Cache.clear
122
128
  end
123
- if opts['clear-ua']
124
- WurflDevice.ui.info "clearing user agent cache."
125
- WurflDevice.clear_user_agent_cache
126
- end
127
- if opts['clear-dev']
128
- WurflDevice.ui.info "clearing device cache."
129
- WurflDevice.clear_devices
130
- end
131
- WurflDevice.ui.info "updating wurfl devices cache."
132
- WurflDevice.initialize_cache
133
- WurflDevice.ui.info "rebuilding cache."
134
- WurflDevice.rebuild_user_agent_cache
135
- WurflDevice.ui.info "done."
136
- WurflDevice.ui.info ""
129
+ WurflDevice.ui.info "initializing wurfl device cache."
130
+ WurflDevice::Cache.initialize_cache(xml_file)
137
131
  status true
138
132
  end
139
133
 
140
- desc "rebuild", "rebuild the existing user_agents cache"
141
- def rebuild
142
- WurflDevice.ui.info "rebuilding the user_agents cache."
143
- WurflDevice.rebuild_user_agent_cache
144
- WurflDevice.ui.info "done."
145
- end
146
-
147
134
  desc "status", "show wurfl cache information"
148
135
  def status(skip_version=false)
149
136
  version unless skip_version
150
- unless WurflDevice.is_initialized?
137
+ unless WurflDevice::Cache.initialized?
151
138
  WurflDevice.ui.info "cache is not initialized"
152
139
  return
153
140
  end
154
- info = WurflDevice.get_info
155
- version = info['version'] || 'none'
156
- last_update = info['last_updated'] || 'unknown'
157
141
  WurflDevice.ui.info "cache info:"
158
- WurflDevice.ui.info " wurfl-xml version: " + version
159
- WurflDevice.ui.info " cache last updated: " + last_update
160
- devices = WurflDevice.get_devices
161
- user_agents = WurflDevice.get_user_agents
142
+ WurflDevice.ui.info " wurfl-xml version: " + WurflDevice::Cache::Status.version.join(' ')
143
+ WurflDevice.ui.info " cache last updated: " + WurflDevice::Cache::Status.last_updated
144
+ devices = WurflDevice::Cache::Devices.entries
145
+ user_agents = WurflDevice::Cache::UserAgents.entries
162
146
  user_agents_message = ''
163
- user_agents_message = " (warning count should be equal to devices count)" if devices.length != user_agents.length
147
+ user_agents_message = " (warning count should be greater than or equal to devices count)" if user_agents.length < devices.length
148
+
149
+ matched_count = 0
150
+ WurflDevice::Cache::UserAgents.entries.each do |user_agent|
151
+ kv = WurflDevice::Cache::UserAgents.keys_values user_agent
152
+ next if kv.count == 1
153
+ matched_count = matched_count + 1
154
+ end
164
155
  WurflDevice.ui.info " " + commify(devices.length) + " device id's"
165
156
  WurflDevice.ui.info " " + commify(user_agents.length) + " exact user agents" + user_agents_message
166
- WurflDevice.ui.info " " + commify(WurflDevice.get_user_agents_in_cache.length) + " user agents found in cache"
167
- indexes = Array.new
168
- WurflDevice.get_indexes.each do |index|
169
- index.gsub!(WurflDevice::Constants::WURFL_DEVICES_INDEX, '')
170
- indexes << "#{index}(" + commify(WurflDevice.get_user_agents_in_index(index).length) + ")"
157
+ WurflDevice.ui.info " " + commify(matched_count) + " user agents matched found in cache"
158
+
159
+ user_agent_manufacturers = Array.new
160
+ WurflDevice::Cache::UserAgentsManufacturers.entries.each do |index|
161
+ user_agent_manufacturers << "#{index}(" + commify(WurflDevice::Cache::UserAgentsManufacturers.keys(index).length) + ")"
171
162
  end
172
- indexes.sort!
173
- WurflDevice.ui.info "wurfl user agent indexes:"
174
- while !indexes.empty?
175
- sub = indexes.slice!(0, 7)
163
+ user_agent_manufacturers.sort!
164
+ WurflDevice.ui.info "wurfl user agent manufacturers:"
165
+ while !user_agent_manufacturers.empty?
166
+ sub = user_agent_manufacturers.slice!(0, 7)
176
167
  WurflDevice.ui.info " " + sub.join(', ')
177
168
  end
178
169
  WurflDevice.ui.info ""
@@ -184,6 +175,7 @@ module WurflDevice
184
175
  WurflDevice.ui.info "wurfl_device version #{WurflDevice::VERSION.freeze}"
185
176
  end
186
177
  map %w(-v --version) => :version
178
+
187
179
  private
188
180
  def commify(n)
189
181
  n.to_s =~ /([^\.]*)(\..*)?/