Class: BjnInventory::Device

Inherits:
Object
  • Object
show all
Defined in:
lib/bjn_inventory/device.rb

Constant Summary collapse

@@model =
DEFAULT_MODEL
@@context =
{ }

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hash_data = {}) ⇒ Device

Returns a new instance of Device.



133
134
135
136
137
138
139
140
141
# File 'lib/bjn_inventory/device.rb', line 133

def initialize(hash_data={})
    @@model.each do |field, _dummy|
        generate_getter(field)
        generate_setter(field)
    end
    @rule = { }
    @data = @@model.stringify_keys
    self.set(hash_data)
end

Class Method Details

.maybe_block(args, block = nil, default = nil) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/bjn_inventory/device.rb', line 62

def self.maybe_block(args, block=nil, default=nil)
    if args.length == 1
        if args[0].respond_to? :call
            return args[0]
        else
            # A constant
            return Proc.new { |_dummy| args[0] }
        end
    elsif block
        return block
    end
    return default
end

.modelObject



54
55
56
# File 'lib/bjn_inventory/device.rb', line 54

def self.model()
    @@model
end

.model=(new_model) ⇒ Object



58
59
60
# File 'lib/bjn_inventory/device.rb', line 58

def self.model=(new_model)
    @@model = new_model
end

.use_context(data) ⇒ Object



34
35
36
37
38
# File 'lib/bjn_inventory/device.rb', line 34

def self.use_context(data)
    if data
        @@context = @@context.merge(data.stringify_keys)
    end
end

.use_default_modelObject



49
50
51
52
# File 'lib/bjn_inventory/device.rb', line 49

def self.use_default_model()
    @@model = DEFAULT_MODEL
    self
end

.use_model(model) ⇒ Object



40
41
42
43
44
45
46
47
# File 'lib/bjn_inventory/device.rb', line 40

def self.use_model(model)
    if model
        @@model = model
    else
        @@model = DEFAULT_MODEL
    end
    self
end

.using(rules = nil) ⇒ Object

Creates a prototype using the specified origin rules file (or rules text), which can be used to create new objects based on data, kind of like a class (but it’s not a class, it’s a prototype).



120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/bjn_inventory/device.rb', line 120

def self.using(rules=nil)
    prototype = self.new()
    if rules
        prototype.load_rules(rules)
    end
    # Define an instance method new() that copies
    # the prototype
    prototype.define_singleton_method(:new) do |hash_data={}|
        self.clone().set(hash_data)
    end
    prototype
end

Instance Method Details

#always(value) ⇒ Object

DSL Allows: name always ‘virtual’



207
208
209
210
211
# File 'lib/bjn_inventory/device.rb', line 207

def always(value)
    Proc.new do |_data, _device|
        value
    end
end

#contextObject



148
149
150
# File 'lib/bjn_inventory/device.rb', line 148

def context()
    @@context
end

#entryObject



213
214
215
# File 'lib/bjn_inventory/device.rb', line 213

def entry
    @data
end

#generate_getter(field) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/bjn_inventory/device.rb', line 76

def generate_getter(field)
    self.define_singleton_method(field.intern) do |*args, &block|
        value = nil
        new_block = Device.maybe_block(args, block)
        if new_block
            @rule[field.to_s] = new_block
        else
            if @rule[field.to_s]
                begin
                    value = @rule[field.to_s].call(@data, self)
                rescue Exception => err
                    identifier_phrase = " for #{self.guessed_identity}"
                    raise RuntimeError, "Error evaluating #{field.to_s} rule (#{err.class}: #{err.to_s})#{identifier_phrase}"
                end
            else
                value = @data[field.to_s]
            end
        end
        value
    end
end

#generate_setter(field) ⇒ Object



110
111
112
113
114
# File 'lib/bjn_inventory/device.rb', line 110

def generate_setter(field)
    self.define_singleton_method((field.to_s + '=').intern) do |new_value|
        @data[field.to_s] = new_value
    end
end

#guessed_identityObject



98
99
100
101
102
103
104
105
106
107
108
# File 'lib/bjn_inventory/device.rb', line 98

def guessed_identity
    # try to find a way to summarize the name or id
    # this needs to work regardless of rules (within rules) so it can't exercise actual name/id methods
    identifier_key = %w(name id fqdn device_name device_id hostname instance_id).find { |key| @data[key] }
    if identifier_key
        identifier_phrase = "#{identifier_key}=#{@data[identifier_key]}"
    else
        identifier_phrase = "(no-id)"
    end
    identifier_phrase
end

#jsonpath(expr) ⇒ Object

DSL Allows: name jsonpath ‘$.aliases



190
191
192
193
194
195
# File 'lib/bjn_inventory/device.rb', line 190

def jsonpath(expr)
    Proc.new do |data|
        value = JsonPath.on(data.to_json, expr).first
        value
    end
end

#load_rules(origin) ⇒ Object



152
153
154
155
156
157
158
159
# File 'lib/bjn_inventory/device.rb', line 152

def load_rules(origin)
    if File.exist? origin
        rules_text = File.read(origin)
    else
        rules_text = origin
    end
    self.instance_eval(rules_text)
end

#map(fieldmap) ⇒ Object

DSL Allows: map field: ruby { |data| data } Allows: map field: jsonpath ‘expr’ but it is actually unnecessary



174
175
176
177
178
# File 'lib/bjn_inventory/device.rb', line 174

def map(fieldmap)
    fieldmap.each do |field, value|
        self.send(field.intern, value)
    end
end

#merge(other) ⇒ Object



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/bjn_inventory/device.rb', line 223

def merge(other)
    # Use myself as prototype or create generic Device?
    origins = self.origin.nil? ? [] : [@origin]

    unless other.origin.nil?
        origins.push other.origin
    end

    merged_data = Hash[@@model.map do |field, default|
                           mine = self.send field.intern
                           theirs = other.send field.intern

                           value = if mine.nil?
                                       theirs
                                   elsif theirs.nil?
                                       mine
                                   elsif default.respond_to? :merge
                                       # The default value is a hash
                                       begin
                                           mine.merge theirs
                                       rescue Exception => err
                                           raise RuntimeError, "Error hash-merging field value #{theirs.inspect} " +
                                               "into #{mine.inspect} for #{self.guessed_identity}: #{err.to_s}"
                                       end
                                   elsif default.respond_to? :push
                                       # The default value is an array
                                       begin
                                           mine.concat theirs.reject { |el| mine.include? el }
                                       rescue Exception => err
                                           raise RuntimeError, "Error concatenating field value #{theirs.inspect} " +
                                               "onto #{mine.inspect} for #{self.guessed_identity}: #{err.to_s}"
                                       end
                                   else
                                       theirs
                                   end
                           [field, value]
                       end]

    device = Device.new(merged_data)
    device.origin origins
    device
end

#origin(origin_name = nil) ⇒ Object

DSL - Set origin name



162
163
164
165
166
167
168
# File 'lib/bjn_inventory/device.rb', line 162

def origin(origin_name=nil)
    if origin_name
        @origin = origin_name
    else
        @origin
    end
end

#ruby(&block) ⇒ Object

DSL Syntactic sugar for Proc.new basically. Allows: Allows: name ruby { |data| data[0] || device } Allows: name ruby { |_dummy| name + ‘.domain.org’ } ???



184
185
186
# File 'lib/bjn_inventory/device.rb', line 184

def ruby(&block)
    block
end

#set(hash_data = {}) ⇒ Object



143
144
145
146
# File 'lib/bjn_inventory/device.rb', line 143

def set(hash_data={})
    @data = @data.merge(hash_data.stringify_keys)
    self
end

#synonym(symbol) ⇒ Object

DSL Allows: name synonym :model_field



199
200
201
202
203
# File 'lib/bjn_inventory/device.rb', line 199

def synonym(symbol)
    Proc.new do |data, device|
        device.send(symbol)
    end
end

#to_hashObject



267
268
269
# File 'lib/bjn_inventory/device.rb', line 267

def to_hash()
    Hash[@@model.map { |field, _default| [field.to_s, self.send(field.intern)] }]
end

#validateObject



217
218
219
220
221
# File 'lib/bjn_inventory/device.rb', line 217

def validate()
    # Raises exceptions if any of the getters barf, basically
    @@model.each { |field, _default| self.send field.intern }
    self
end