wurfl_device 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
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 =~ /([^\.]*)(\..*)?/