device-identifier 0.0.1
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/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:
|