device-identifier 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +206 -0
- data/lib/dev-id/base.rb +59 -0
- data/lib/dev-id/binary_address.rb +136 -0
- data/lib/dev-id/device_identifier.rb +14 -0
- data/lib/dev-id/mac_address.rb +80 -0
- data/lib/dev-id/version.rb +13 -0
- data/lib/dev-id.rb +9 -0
- metadata +69 -0
data/README.md
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
# Device identifier models
|
2
|
+
|
3
|
+
Models for device identifiers, e.g. MAC addresses and other identifiers for VoIP phones.
|
4
|
+
|
5
|
+
Rationale:
|
6
|
+
|
7
|
+
- Hard-phones have an Ethernet MAC address.
|
8
|
+
- Soft-phones don't. They have/need some sort of identification string
|
9
|
+
(any sequence of bytes).
|
10
|
+
- Both are device identifiers, with the MAC address being a sub-class.
|
11
|
+
|
12
|
+
Author: Philipp Kempgen, [http://kempgen.net](http://kempgen.net)
|
13
|
+
|
14
|
+
|
15
|
+
## Inheritance
|
16
|
+
|
17
|
+
The class inheritance is as follows:
|
18
|
+
|
19
|
+
- **`Base`**
|
20
|
+
|
21
|
+
Base model class, providing validations etc.
|
22
|
+
|
23
|
+
- **`BinaryAddress`**
|
24
|
+
|
25
|
+
Model for a raw binary address.
|
26
|
+
|
27
|
+
- **`DeviceIdentifier`**
|
28
|
+
|
29
|
+
Model for a device identifier.
|
30
|
+
|
31
|
+
- **`MacAddress`**
|
32
|
+
|
33
|
+
Model for a MAC address.
|
34
|
+
|
35
|
+
- **`MacAddressPartial`**
|
36
|
+
|
37
|
+
Model for a partial MAC address.
|
38
|
+
|
39
|
+
- **`MacAddressOui`**
|
40
|
+
|
41
|
+
Model for the OUI (vendor part) of a MAC address.
|
42
|
+
|
43
|
+
|
44
|
+
## Device identifiers
|
45
|
+
|
46
|
+
There are 2 kinds of device identifiers: the generic `DeviceIdentifier` and the more specific `MacAddress`.
|
47
|
+
|
48
|
+
Both support the methods in following (amongst others):
|
49
|
+
|
50
|
+
- `bytes`
|
51
|
+
|
52
|
+
The raw bytes.
|
53
|
+
|
54
|
+
- `ascii( opts = nil )`
|
55
|
+
|
56
|
+
An ASCII representation (hex format).
|
57
|
+
|
58
|
+
- `to_s( opts = nil )`
|
59
|
+
|
60
|
+
An ASCII representation (hex format).
|
61
|
+
|
62
|
+
|
63
|
+
## Classes
|
64
|
+
|
65
|
+
|
66
|
+
### `Base`
|
67
|
+
|
68
|
+
Includes some `ActiveModel` mix-ins (`ActiveModel::Validations` etc.).
|
69
|
+
|
70
|
+
Provides methods such as `valid?`.
|
71
|
+
|
72
|
+
|
73
|
+
### `BinaryAddress`
|
74
|
+
|
75
|
+
**Inheritance**:
|
76
|
+
`Base` >
|
77
|
+
`BinaryAddress`
|
78
|
+
|
79
|
+
Model for a raw binary address.
|
80
|
+
|
81
|
+
Includes the `::Comparable` mix-in.
|
82
|
+
|
83
|
+
Class methods:
|
84
|
+
|
85
|
+
- `self.from_raw( str )`
|
86
|
+
|
87
|
+
Initializer. Creates a new object from a raw representation.
|
88
|
+
|
89
|
+
- `self.from_hex( str )`
|
90
|
+
|
91
|
+
Initializer. Creates a new object from a hex representation,
|
92
|
+
with or without "`:`" or "`-`" separators.
|
93
|
+
|
94
|
+
Instance methods:
|
95
|
+
|
96
|
+
- `bytes`
|
97
|
+
|
98
|
+
The raw bytes.
|
99
|
+
|
100
|
+
- `bytesize`
|
101
|
+
|
102
|
+
The number of bytes.
|
103
|
+
|
104
|
+
- `length`
|
105
|
+
|
106
|
+
Alias for `bytesize`.
|
107
|
+
|
108
|
+
- `ascii( opts = nil )`
|
109
|
+
|
110
|
+
An ASCII representation (hex format).
|
111
|
+
|
112
|
+
Default options are:
|
113
|
+
|
114
|
+
{
|
115
|
+
:sep => '', # separator, typically ":" or "-" or ""
|
116
|
+
:upcase => false, # use uppercase?
|
117
|
+
}
|
118
|
+
|
119
|
+
Returns `nil` if invalid.
|
120
|
+
|
121
|
+
- `to_int`
|
122
|
+
|
123
|
+
An integer representation.
|
124
|
+
|
125
|
+
- `starts_with?( other )`
|
126
|
+
|
127
|
+
Whether it starts with the other `BinaryAddress`.
|
128
|
+
|
129
|
+
- `start_with?( other )`
|
130
|
+
|
131
|
+
Alias for `starts_with?( other )`.
|
132
|
+
|
133
|
+
|
134
|
+
### `DeviceIdentifier`
|
135
|
+
|
136
|
+
**Inheritance**:
|
137
|
+
`Base` >
|
138
|
+
`BinaryAddress` >
|
139
|
+
`DeviceIdentifier`
|
140
|
+
|
141
|
+
Model for a device identifier.
|
142
|
+
|
143
|
+
Validates that the raw address has a minimum length of 1 byte.
|
144
|
+
|
145
|
+
|
146
|
+
### `MacAddress`
|
147
|
+
|
148
|
+
**Inheritance**:
|
149
|
+
`Base` >
|
150
|
+
`BinaryAddress` >
|
151
|
+
`DeviceIdentifier` >
|
152
|
+
`MacAddress`
|
153
|
+
|
154
|
+
Model for a MAC address.
|
155
|
+
|
156
|
+
Instance methods:
|
157
|
+
|
158
|
+
- `multicast?`
|
159
|
+
|
160
|
+
If it's a multicast MAC address (integer value of first byte odd).
|
161
|
+
|
162
|
+
- `null?`
|
163
|
+
|
164
|
+
If it's a null address (all 6 bytes \x00: 00:00:00:00:00:00).
|
165
|
+
|
166
|
+
- `pretty( opts = nil )`
|
167
|
+
|
168
|
+
A pretty ASCII representation (hex format).
|
169
|
+
|
170
|
+
Default options are:
|
171
|
+
|
172
|
+
{
|
173
|
+
:sep => ':', # separator, typically ":" or "-" or ""
|
174
|
+
:upcase => true, # use uppercase?
|
175
|
+
}
|
176
|
+
|
177
|
+
The default options are the same as for the inherited `ascii` method, except that the separator is "`:`" instead of "".
|
178
|
+
|
179
|
+
- `oui_raw`
|
180
|
+
|
181
|
+
The OUI (vendor) part (first 3 bytes) in raw format.
|
182
|
+
|
183
|
+
|
184
|
+
### `MacAddressPartial`
|
185
|
+
|
186
|
+
**Inheritance**:
|
187
|
+
`Base` >
|
188
|
+
`BinaryAddress` >
|
189
|
+
`MacAddressPartial`
|
190
|
+
|
191
|
+
Model for a partial MAC address.
|
192
|
+
|
193
|
+
|
194
|
+
### `MacAddressOui`
|
195
|
+
|
196
|
+
**Inheritance**:
|
197
|
+
`Base` >
|
198
|
+
`BinaryAddress` >
|
199
|
+
`MacAddressPartial` >
|
200
|
+
`MacAddressOui`
|
201
|
+
|
202
|
+
Model for the OUI (vendor) part of a MAC address.
|
203
|
+
|
204
|
+
Validates that the raw address has a length of 3 bytes.
|
205
|
+
|
206
|
+
|
data/lib/dev-id/base.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'dev-id'
|
2
|
+
require 'active_model'
|
3
|
+
|
4
|
+
module ::DevId
|
5
|
+
|
6
|
+
# @private
|
7
|
+
class BasicActiveModel
|
8
|
+
extend ::ActiveModel::Naming
|
9
|
+
include ::ActiveModel::Validations
|
10
|
+
include ::ActiveModel::MassAssignmentSecurity
|
11
|
+
include ::ActiveModel::Conversion
|
12
|
+
# extend ::ActiveModel::Callbacks
|
13
|
+
|
14
|
+
validate :before, :do_before_validation
|
15
|
+
|
16
|
+
def initialize( attrs={} )
|
17
|
+
assign_attributes( attrs )
|
18
|
+
end
|
19
|
+
|
20
|
+
# see `activerecord/lib/active_record/attribute_assignment.rb`
|
21
|
+
def assign_attributes( new_attributes, options={} )
|
22
|
+
return unless new_attributes.present?
|
23
|
+
|
24
|
+
attributes = new_attributes.stringify_keys
|
25
|
+
|
26
|
+
unless options[:without_protection]
|
27
|
+
attributes = sanitize_for_mass_assignment( attributes, options[:as] || :default )
|
28
|
+
end
|
29
|
+
|
30
|
+
attributes.each { |k, v|
|
31
|
+
#setter_name = :"#{k}="
|
32
|
+
#send( setter_name, v ) if respond_to?( setter_name )
|
33
|
+
send( :"#{k}=", v )
|
34
|
+
}
|
35
|
+
|
36
|
+
#self
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def persisted?
|
41
|
+
false
|
42
|
+
end
|
43
|
+
|
44
|
+
def do_before_validation
|
45
|
+
@errors.clear
|
46
|
+
before_validation()
|
47
|
+
end
|
48
|
+
|
49
|
+
def before_validation
|
50
|
+
# override in sub-classes
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# @private
|
55
|
+
class Base < BasicActiveModel
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'dev-id/base'
|
2
|
+
require 'scanf'
|
3
|
+
|
4
|
+
module ::DevId
|
5
|
+
|
6
|
+
# Model for a raw binary address.
|
7
|
+
#
|
8
|
+
class BinaryAddress < Base
|
9
|
+
|
10
|
+
include ::Comparable
|
11
|
+
|
12
|
+
# The raw address.
|
13
|
+
attr_accessor :raw
|
14
|
+
|
15
|
+
def before_validation
|
16
|
+
if raw
|
17
|
+
raw = raw.to_s.force_encoding( ::Encoding::ASCII_8BIT )
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
validates_presence_of :raw
|
22
|
+
|
23
|
+
# Default options for conversion to ASCII in the `ascii`
|
24
|
+
# method.
|
25
|
+
DEFAULT_TO_ASCII_OPTS = {
|
26
|
+
:sep => '', # typically ":" or "-" or ""
|
27
|
+
:upcase => false,
|
28
|
+
}.freeze
|
29
|
+
|
30
|
+
def bytes
|
31
|
+
(raw || '').bytes
|
32
|
+
end
|
33
|
+
|
34
|
+
def bytesize
|
35
|
+
(raw || '').bytesize
|
36
|
+
end
|
37
|
+
alias :length :bytesize
|
38
|
+
|
39
|
+
# Construct a new instance from a raw string.
|
40
|
+
#
|
41
|
+
def self.from_raw( str )
|
42
|
+
addr = self.new( :raw => str )
|
43
|
+
end
|
44
|
+
|
45
|
+
# Construct a new instance from a hex string.
|
46
|
+
#
|
47
|
+
def self.from_hex( str )
|
48
|
+
addr = self.new()
|
49
|
+
str = str.to_s.dup
|
50
|
+
str.force_encoding( ::Encoding::ASCII_8BIT )
|
51
|
+
str.gsub!( /[:\-]/, '' )
|
52
|
+
raw = str.scanf( '%2X' ) { |x,| x.to_i.chr( ::Encoding::ASCII_8BIT ) }.
|
53
|
+
join( ''.force_encoding( ::Encoding::ASCII_8BIT ) )
|
54
|
+
addr.raw = raw
|
55
|
+
return addr
|
56
|
+
end
|
57
|
+
|
58
|
+
# ASCII representation.
|
59
|
+
#
|
60
|
+
def ascii( opts = nil )
|
61
|
+
return nil if ! valid?
|
62
|
+
opts = DEFAULT_TO_ASCII_OPTS.merge( opts || {} )
|
63
|
+
return self.bytes.map{ |b| (opts[:upcase] ? '%02X' : '%02x') % [b] }.join( opts[:sep] )
|
64
|
+
end
|
65
|
+
|
66
|
+
# `to_s` is an alias for `ascii`.
|
67
|
+
#
|
68
|
+
def to_s( opts = nil )
|
69
|
+
ascii( opts )
|
70
|
+
end
|
71
|
+
|
72
|
+
# If `other` is of the same class, compares the raw values.
|
73
|
+
# Returns `nil` unless `self` and `other` have a raw address.
|
74
|
+
# Returns `nil` if `other` is an instance of a different class.
|
75
|
+
#
|
76
|
+
def <=>( other )
|
77
|
+
return nil unless other.kind_of?( BinaryAddress )
|
78
|
+
return nil unless (self.raw && other.raw)
|
79
|
+
return self.raw <=> other.raw #OPTIMIZE ?
|
80
|
+
end
|
81
|
+
|
82
|
+
# `to_int` helps with `include?`.
|
83
|
+
#
|
84
|
+
# http://rhnh.net/2009/08/03/range-include-in-ruby-1-9
|
85
|
+
#
|
86
|
+
# Note: ActiveSupport (as of Version 3.2.1) messes with
|
87
|
+
# Range#include?() (in
|
88
|
+
# `lib/active_support/core_ext/range/include_range.rb`)
|
89
|
+
# even though they say "The native Range#include? behavior
|
90
|
+
# is untouched.", so you will have to use Range#cover?()
|
91
|
+
# instead.
|
92
|
+
#
|
93
|
+
def to_int
|
94
|
+
v = 0
|
95
|
+
self.bytes.to_a.reverse.each_with_index { |b,i|
|
96
|
+
vb = b
|
97
|
+
i.times {
|
98
|
+
vb = vb << 8
|
99
|
+
}
|
100
|
+
v += vb
|
101
|
+
}
|
102
|
+
return v
|
103
|
+
end
|
104
|
+
|
105
|
+
def ==( other )
|
106
|
+
return super unless other.kind_of?( BinaryAddress )
|
107
|
+
return false unless (self.raw && other.raw)
|
108
|
+
return self.raw == other.raw
|
109
|
+
end
|
110
|
+
alias :'eql?' :'=='
|
111
|
+
|
112
|
+
def starts_with?( other )
|
113
|
+
return super unless other.kind_of?( BinaryAddress )
|
114
|
+
return false unless (self.raw && other.raw)
|
115
|
+
return self.raw.start_with?( other.raw )
|
116
|
+
end
|
117
|
+
alias :'start_with?' :'starts_with?'
|
118
|
+
|
119
|
+
def =~( other )
|
120
|
+
return super unless other.kind_of?( BinaryAddress )
|
121
|
+
return false unless (self.raw && other.raw)
|
122
|
+
return (self.bytesize > other.bytesize) ?
|
123
|
+
self .raw.start_with?( other.raw ) :
|
124
|
+
other.raw.start_with?( self .raw )
|
125
|
+
end
|
126
|
+
alias :'eql?' :'=='
|
127
|
+
|
128
|
+
def !~( other )
|
129
|
+
return ! (self =~ other)
|
130
|
+
end
|
131
|
+
|
132
|
+
#TODO getbyte, [], byteslice
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'dev-id/binary_address'
|
2
|
+
require 'scanf'
|
3
|
+
|
4
|
+
module ::DevId
|
5
|
+
|
6
|
+
# Model for a device identifier.
|
7
|
+
#
|
8
|
+
class DeviceIdentifier < BinaryAddress
|
9
|
+
|
10
|
+
validates_length_of :raw, :minimum => 1, :allow_blank => false, :allow_nil => true
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'dev-id/device_identifier'
|
2
|
+
require 'dev-id/binary_address'
|
3
|
+
|
4
|
+
module ::DevId
|
5
|
+
|
6
|
+
# Model for a MAC address.
|
7
|
+
#
|
8
|
+
# 6 bytes.
|
9
|
+
#
|
10
|
+
class MacAddress < DeviceIdentifier
|
11
|
+
|
12
|
+
validates_length_of :raw, :is => 6
|
13
|
+
|
14
|
+
# Default options for conversion to ASCII in the `pretty`
|
15
|
+
# method.
|
16
|
+
DEFAULT_TO_PRETTY_OPTS = {
|
17
|
+
:sep => ':', # typically ":" or "-" or ""
|
18
|
+
:upcase => true,
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
# Whether the MAC address is a multicast address.
|
22
|
+
#
|
23
|
+
def multicast?
|
24
|
+
return nil if ! valid?
|
25
|
+
return (raw.getbyte(0).to_i % 2) != 0
|
26
|
+
end
|
27
|
+
|
28
|
+
# Whether the MAC address is the "null" address (6 NULL
|
29
|
+
# bytes).
|
30
|
+
#
|
31
|
+
def null?
|
32
|
+
return nil if ! valid?
|
33
|
+
return raw == (?\0.force_encoding( ::Encoding::ASCII_8BIT ) * 6)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Pretty ASCII representation.
|
37
|
+
#
|
38
|
+
def pretty( opts = nil )
|
39
|
+
return nil if ! valid?
|
40
|
+
opts = DEFAULT_TO_PRETTY_OPTS.merge( opts || {} )
|
41
|
+
return ascii( opts )
|
42
|
+
end
|
43
|
+
|
44
|
+
# The OUI (vendor part) as a raw string.
|
45
|
+
#
|
46
|
+
def oui_raw
|
47
|
+
return nil if ! valid?
|
48
|
+
return raw.byteslice( 0, 3 )
|
49
|
+
end
|
50
|
+
|
51
|
+
# `to_s` is an alias for `pretty`.
|
52
|
+
#
|
53
|
+
def to_s( opts = nil )
|
54
|
+
pretty( opts )
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
module ::DevId
|
61
|
+
|
62
|
+
# Model for a partial MAC address.
|
63
|
+
#
|
64
|
+
class MacAddressPartial < BinaryAddress
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
module ::DevId
|
69
|
+
|
70
|
+
# Model for the OUI (vendor part) of a MAC address.
|
71
|
+
#
|
72
|
+
# 3 bytes.
|
73
|
+
#
|
74
|
+
class MacAddressOui < MacAddressPartial
|
75
|
+
|
76
|
+
validates_length_of :raw, :is => 3
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# This file should not require anything, as it's loaded in the
|
2
|
+
# `*.gemspec` file.
|
3
|
+
|
4
|
+
module ::DevId
|
5
|
+
|
6
|
+
# The version of this Gem.
|
7
|
+
VERSION = [ 0, 0, 1 ].freeze
|
8
|
+
|
9
|
+
# The version of this Gem as a string.
|
10
|
+
VERSION_STR = VERSION.map(& :to_s).join('.').freeze
|
11
|
+
|
12
|
+
end
|
13
|
+
|
data/lib/dev-id.rb
ADDED
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: device-identifier
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Philipp Kempgen
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-04-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activemodel
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.2'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.2'
|
30
|
+
description: Models for device identifiers, e.g. MAC addresses for hardware clients
|
31
|
+
and arbitrary identifier strings for software clients.
|
32
|
+
email:
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- lib/dev-id/base.rb
|
38
|
+
- lib/dev-id/binary_address.rb
|
39
|
+
- lib/dev-id/device_identifier.rb
|
40
|
+
- lib/dev-id/mac_address.rb
|
41
|
+
- lib/dev-id/version.rb
|
42
|
+
- lib/dev-id.rb
|
43
|
+
- README.md
|
44
|
+
homepage: https://github.com/philipp-kempgen/device-identifier
|
45
|
+
licenses: []
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
requirements: []
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 1.8.25
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: Models for device identifiers.
|
68
|
+
test_files: []
|
69
|
+
has_rdoc:
|