device_atlas 1.3.0

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.
Files changed (2) hide show
  1. data/lib/device_atlas.rb +251 -0
  2. metadata +64 -0
@@ -0,0 +1,251 @@
1
+ require 'rubygems'
2
+ require 'JSON'
3
+ #
4
+ # JSON is a required rependency
5
+ # sudo gem install JSON
6
+ #
7
+ # Used to load the recognition tree and perform lookups of all properties, or
8
+ # individual properties. Typical usage is as follows:
9
+ #
10
+ # device_atlas = DeviceAtlas.new
11
+ # tree = device_atlas.getTreeFromFile("sample/DeviceAtlas.json")
12
+ # properties = device_atlas.getProperties(tree, "Nokia6680...")
13
+ # property = device_atlas.getProperty(tree, "Nokia6680...", "displayWidth")
14
+ #
15
+ # Note that you should normally use the user-agent that was received in
16
+ # the device's HTTP request. In a Rails environment, you would do this as follows:
17
+ #
18
+ #
19
+ # user_agent = request.env['HTTP_USER_AGENT']
20
+ # display_width = device_atlas.getPropertyAsInteger(tree, user_agent, "displayWidth")
21
+ #
22
+ # Author:: MTLD (dotMobi)
23
+ #
24
+ class DeviceAtlas
25
+
26
+ class IncorrectPropertyTypeException < StandardError; end
27
+ class InvalidPropertyException < StandardError; end
28
+ class JsonException < StandardError; end
29
+ class UnknownPropertyException < StandardError; end
30
+
31
+ attr_accessor :found_properties, :patricia, :matched_ua, :unmatched_ua
32
+
33
+ # Returns a tree from a JSON string
34
+ def getTreeFromString(string)
35
+ tree = JSON::Parser.new(string, :max_nesting => false).parse
36
+ raise(JsonException, "Unable to load Json data.") if (tree.nil? || !tree.kind_of?(Hash))
37
+ raise(JsonException, "Bad data loaded into the tree") unless tree.has_key?("$")
38
+ raise(JsonException, "DeviceAtlas json file must be v0.7 or greater. Please download a more recent version.") if(tree["$"]["Ver"].to_f < 0.7)
39
+
40
+ pr = {}
41
+ pn = {}
42
+ tree['p'].each_with_index do |key,value|
43
+ pr[key] = value
44
+ pn[key[1..key.size]] = value
45
+ end
46
+ tree['pr'] = pr
47
+ tree['pn'] = pn
48
+ self.patricia = tree
49
+ tree
50
+ end
51
+
52
+ # Returns a tree from a JSON file.
53
+ # Use an absolute path name to be sure of success if the current working directory is not clear.
54
+ def getTreeFromFile(filename)
55
+ json = File.open(filename,"r").readlines.to_s
56
+ getTreeFromString(json)
57
+ end
58
+
59
+ # Returns the revision number of the tree
60
+ def getTreeRevision(tree)
61
+ _getRevisionFromKeyword(tree['$']["Rev"])
62
+ end
63
+
64
+ # Returns the revision number of this API
65
+ def getApiRevision()
66
+ _getRevisionFromKeyword('$Rev: 2830 $')
67
+ end
68
+
69
+ # Returns an array of known property names.
70
+ # Returns all properties available for all user agents in this tree, with their data type names
71
+ def listProperties(tree)
72
+ types = {:s => "string", :b =>"boolean", :i =>"integer", :d =>"date", :u =>"unknown"}
73
+ properties = {}
74
+ tree['p'].each do |property|
75
+ properties[property[1..property.length]] = types[property[0..0].to_sym]
76
+ end
77
+ properties
78
+ end
79
+
80
+ # Returns an array of known properties (as strings) for the user agent
81
+ def getProperties(tree, userAgent)
82
+ _getProperties(tree, userAgent, false)
83
+ end
84
+
85
+ # Returns an array of known properties (as typed) for the user agent
86
+ def getPropertiesAsTyped(tree, userAgent)
87
+ _getProperties(tree, userAgent, true)
88
+ end
89
+
90
+ # Returns a value for the named property for this user agent
91
+ def getProperty(tree, userAgent, property)
92
+ _getProperty(tree, userAgent, property, false)
93
+ end
94
+
95
+ # Strongly typed property accessor.
96
+ # Returns a boolean property.
97
+ # (Throws an exception if the property is actually of another type.)
98
+ def getPropertyAsBoolean(tree, userAgent, property)
99
+ _propertyTypeCheck(tree, property, "b", "boolean")
100
+ _getProperty(tree, userAgent, property, true)
101
+ end
102
+
103
+ # Strongly typed property accessor.
104
+ # Returns a date property.
105
+ # (Throws an exception if the property is actually of another type.)
106
+ def getPropertyAsDate(tree, userAgent, property)
107
+ _propertyTypeCheck(tree, property, "d", "date")
108
+ _getProperty(tree, userAgent, property, true)
109
+ end
110
+
111
+ # Strongly typed property accessor.
112
+ # Returns an integer property.
113
+ # (Throws an exception if the property is actually of another type.)
114
+ def getPropertyAsInteger(tree, userAgent, property)
115
+ _propertyTypeCheck(tree, property, "i", "integer")
116
+ _getProperty(tree, userAgent, property, true)
117
+ end
118
+
119
+ # Strongly typed property accessor.
120
+ # Returns a string property.
121
+ # (Throws an exception if the property is actually of another type.)
122
+ def getPropertyAsString(tree, userAgent, property)
123
+ _propertyTypeCheck(tree, property, "s", "string")
124
+ _getProperty(tree, userAgent, property, true)
125
+ end
126
+
127
+ protected
128
+ # Formats the SVN revision string to return a number
129
+ def _getRevisionFromKeyword(keyword)
130
+ keyword.gsub("$","")[5..keyword.size].strip.to_i
131
+ end
132
+
133
+ # Returns an array of known properties for the user agent.
134
+ # Allows the values of properties to be forced to be strings.
135
+ def _getProperties(tree, userAgent, typedValues)
136
+ idProperties = []
137
+ matched = ""
138
+ sought = nil
139
+ self.found_properties = {}
140
+
141
+ _seekProperties(tree['t'], userAgent.strip, idProperties, sought, matched)
142
+ properties = {}
143
+ self.found_properties.each_pair do |id,value|
144
+ if typedValues
145
+ properties[_propertyFromId(tree, id)] = _valueAsTypedFromId(tree, value, id)
146
+ else
147
+ properties[_propertyFromId(tree, id)] = _valueFromId(tree, value)
148
+ end
149
+ end
150
+ properties["_matched"] = self.matched_ua
151
+ properties["_unmatched"] = self.unmatched_ua
152
+ properties
153
+ end
154
+
155
+ # Returns a value for the named property for this user agent.
156
+ # Allows the value to be typed or forced as a string.
157
+ def _getProperty(tree, userAgent, property, typedValue)
158
+ propertyId = _idFromProperty(tree, property)
159
+ idProperties = []
160
+ sought = {}
161
+ sought[propertyId.to_s] = 1
162
+ matched = ""
163
+ unmatched = ""
164
+ self.found_properties = {}
165
+ _seekProperties(tree['t'], userAgent.strip, idProperties, sought, matched)
166
+
167
+ raise(InvalidPropertyException, "The property #{property} is invalid for the User Agent:#{userAgent}") if self.found_properties.size == 0
168
+
169
+ if typedValue
170
+ _valueAsTypedFromId(tree, self.found_properties[propertyId.to_s], propertyId)
171
+ else
172
+ _valueFromId(tree, self.found_properties[propertyId.to_s])
173
+ end
174
+ end
175
+
176
+ # Return the coded ID for a property's name
177
+ def _idFromProperty(tree, property)
178
+ raise(UnknownPropertyException, "The property #{property} is not known in this tree.") unless tree['pn'].has_key?(property)
179
+ tree['pn'][property]
180
+ end
181
+
182
+ # Return the name for a property's coded ID
183
+ def _propertyFromId(tree, id)
184
+ string = tree['p'][id.to_i]
185
+ return if string.nil?
186
+ string[1..string.size]
187
+ end
188
+
189
+ # Checks that the property is of the supplied type or throws an error.
190
+ def _propertyTypeCheck(tree, property, prefix, typeName)
191
+ key = prefix + property
192
+ raise(IncorrectPropertyTypeException, "#{property} is not of type #{typeName}") unless tree['pr'].has_key?(key)
193
+ end
194
+
195
+ # Seek properties for a user agent within a node.
196
+ # This is designed to be recursed, and only externally called with the node representing the top of the tree
197
+ def _seekProperties(node, string, properties, sought, matched)
198
+ unmatched = string
199
+ self.unmatched_ua = unmatched
200
+ if node.has_key?('d')
201
+ if (sought != nil && sought.size == 0)
202
+ return
203
+ end
204
+ node['d'].each do |property,value|
205
+ if (sought == nil || sought[property])
206
+ self.found_properties[property.to_s] = value
207
+ properties[property.to_i] = value
208
+ end
209
+ if (sought != nil) && ( !node.has_key?('m') || ( node.has_key?('m') && !node['m'].has_key?(property) ) )
210
+ sought.delete(property)
211
+ end
212
+ end
213
+ end
214
+ if node.has_key?('c')
215
+ (0..string.length+1).each do |c|
216
+ seek = string[0..c]
217
+ # TODO for some reason the last node is an array? handle it better?
218
+ if node['c'].kind_of?(Hash) && node['c'][seek]
219
+ matched += seek
220
+ self.matched_ua = matched
221
+ return _seekProperties(node['c'][seek], string[c+1..string.length], properties, sought, matched)
222
+ elsif node['c'][seek.to_i]
223
+ matched += seek
224
+ self.matched_ua = matched
225
+ return _seekProperties(node['c'][seek.to_i], string[c+1..string.length], properties, sought, matched)
226
+ end
227
+ end
228
+ end
229
+ end
230
+
231
+ # Returns the property value as typed
232
+ def _valueAsTypedFromId(tree, id, propertyId)
233
+ obj = tree['v'][id]
234
+ case tree['p'][propertyId.to_i][0..0]
235
+ when 's'
236
+ return obj.to_s
237
+ when 'b'
238
+ return (obj.to_i == 1)
239
+ when 'i'
240
+ return obj.to_i
241
+ when 'd'
242
+ return Date.parse(obj)
243
+ end
244
+ end
245
+
246
+ # Return the value for a value's coded ID
247
+ def _valueFromId(tree, id)
248
+ tree['v'][id]
249
+ end
250
+
251
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: device_atlas
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.0
5
+ platform: ruby
6
+ authors:
7
+ - dotMobi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-04-28 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: json
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.0.0
24
+ version:
25
+ description:
26
+ email: contact@mtld.mobi
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - lib/device_atlas.rb
35
+ has_rdoc: true
36
+ homepage: http://mtld.mobi
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ version:
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ requirements: []
57
+
58
+ rubyforge_project:
59
+ rubygems_version: 1.3.5
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: ""
63
+ test_files: []
64
+