Class: Configurability::Config

Inherits:
Object
  • Object
show all
Extended by:
Forwardable, Loggability
Includes:
DataUtilities
Defined in:
lib/configurability/config.rb

Overview

A configuration object class for systems with Configurability

Author/s

This class also delegates some of its methods to the underlying struct:

Configurability::Config::Struct#to_hash

#to_hash (delegated to its internal Struct)

Configurability::Config::Struct#member?

#member? (delegated to its internal Struct)

Configurability::Config::Struct#members

#members (delegated to its internal Struct)

Configurability::Config::Struct#merge

#merge (delegated to its internal Struct)

Configurability::Config::Struct#merge!

#merge! (delegated to its internal Struct)

Configurability::Config::Struct#each

#each (delegated to its internal Struct)

Configurability::Config::Struct#[]

#[] (delegated to its internal Struct)

Configurability::Config::Struct#[]=

#[]= (delegated to its internal Struct)

Defined Under Namespace

Modules: DataUtilities Classes: Struct

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from DataUtilities

#stringify_keys, #symbolify_keys

Constructor Details

#initialize(source = nil, path = nil, defaults = nil, &block) ⇒ Config

Create a new Configurability::Config object. If the optional source argument is specified, parse the config from it. If one is given, the block will be evaluated in the context of the config object after the config is loaded, unless it accepts an argument, in which case the config object is passed as the argument.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/configurability/config.rb', line 81

def initialize( source=nil, path=nil, defaults=nil, &block )

  # Shift the hash parameter if it shows up as the path
  if path.is_a?( Hash )
    defaults = path
    path = nil
  end

  # Make a deep copy of the defaults before loading so we don't modify
  # the argument
  @defaults     = defaults ? Marshal.load( Marshal.dump(defaults) ) : nil
  @time_created = Time.now
  @path         = path

  if source
    @struct = self.make_configstruct_from_source( source, @defaults )
  else
    @struct = Configurability::Config::Struct.new( @defaults )
  end

  if block
    Configurability.log.debug "Block arity is: %p" % [ block.arity ]

    # A block with an argument is called with the config as the argument
    # instead of instance_evaled
    case block.arity
    when 0, -1  # 1.9 and 1.8, respectively
      Configurability.log.debug "Instance evaling in the context of %p" % [ self ]
      self.instance_eval( &block )
    else
      block.call( self )
    end
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(sym, *args) ⇒ Object (protected)

Handle calls to struct-members



272
273
274
275
276
277
278
279
280
281
282
# File 'lib/configurability/config.rb', line 272

def method_missing( sym, *args )
  key = sym.to_s.sub( /(=|\?)$/, '' ).to_sym

  self.class.class_eval %{
    def #{key}; @struct.#{key}; end
    def #{key}=(arg); @struct.#{key} = arg; end
    def #{key}?; @struct.#{key}?; end
  }

  return self.method( sym ).call( *args )
end

Instance Attribute Details

#pathObject

the path to the config file, if loaded from a file



132
133
134
# File 'lib/configurability/config.rb', line 132

def path
  @path
end

#structObject (readonly)

The underlying config data structure



126
127
128
# File 'lib/configurability/config.rb', line 126

def struct
  @struct
end

#time_createdObject

The time the configuration was loaded



129
130
131
# File 'lib/configurability/config.rb', line 129

def time_created
  @time_created
end

Class Method Details

.load(path, defaults = nil, &block) ⇒ Object

Read and return a Configurability::Config object from the file at the given path.



55
56
57
58
59
60
# File 'lib/configurability/config.rb', line 55

def self::load( path, defaults=nil, &block )
  path = Pathname( path ).expand_path
  source = path.read
  Configurability.log.debug "Read %d bytes from %s" % [ source.length, path ]
  return new( source, path, defaults, &block )
end

.merge_complex_hashes(key, oldval, newval) ⇒ Object

Recursive hash-merge function. Used as the block argument to a Hash#merge.



64
65
66
67
68
# File 'lib/configurability/config.rb', line 64

def self::merge_complex_hashes( key, oldval, newval )
  return oldval.merge( newval, &method(:merge_complex_hashes) ) if
    oldval.respond_to?( :merge ) && newval.respond_to?( :merge )
  return newval
end

Instance Method Details

#changed?Boolean

Returns true if the configuration has changed since it was last loaded, either by setting one of its members or changing the file from which it was loaded.

Returns:

  • (Boolean)


178
179
180
# File 'lib/configurability/config.rb', line 178

def changed?
  return self.changed_reason ? true : false
end

#changed_reasonObject

If the configuration has changed, return the reason. If it hasn’t, returns nil.



185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/configurability/config.rb', line 185

def changed_reason
  if @struct.dirty?
    Configurability.log.debug "Changed_reason: struct was modified"
    return "Struct was modified"
  end

  if self.path && self.is_older_than?( self.path )
    Configurability.log.debug "Source file (%s) has changed." % [ self.path ]
    return "Config source (%s) has been updated since %s" %
      [ self.path, self.time_created ]
  end

  return nil
end

#dumpObject

Return the config object as a YAML hash



143
144
145
146
# File 'lib/configurability/config.rb', line 143

def dump
  strhash = stringify_keys( self.to_h )
  return YAML.dump( strhash )
end

#inspectObject

Return a human-readable, compact representation of the configuration suitable for debugging.



232
233
234
235
236
237
238
239
240
# File 'lib/configurability/config.rb', line 232

def inspect
  return "#<%s:0x%0x16 loaded from %s; %d sections: %s>" % [
    self.class.name,
    self.object_id * 2,
    self.path ? self.path : "memory",
    self.struct.members.length,
    self.struct.members.join( ', ' )
  ]
end

#installObject

Install this config object in any objects that have added Configurability.



137
138
139
# File 'lib/configurability/config.rb', line 137

def install
  Configurability.configure_objects( self )
end

#is_older_than?(path) ⇒ Boolean

Return true if the specified file is newer than the time the receiver was created.

Returns:

  • (Boolean)


203
204
205
206
207
208
209
# File 'lib/configurability/config.rb', line 203

def is_older_than?( path )
  return false unless path.exist?
  st = path.stat
  Configurability.log.debug "File mtime is: %s, comparison time is: %s" %
    [ st.mtime, @time_created ]
  return st.mtime > @time_created
end

#reloadObject

Reload the configuration from the original source if it has changed. Returns true if it was reloaded and false otherwise.



214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/configurability/config.rb', line 214

def reload
  raise "can't reload from an in-memory source" unless self.path

  if self.changed?
    self.time_created = Time.now
    source = self.path.read
    @struct = self.make_configstruct_from_source( source, @defaults )

    self.install
    return true
  else
    return false
  end
end

#respond_to?(sym) ⇒ Boolean

Returns true for methods which can be autoloaded

Returns:

  • (Boolean)


169
170
171
172
# File 'lib/configurability/config.rb', line 169

def respond_to?( sym )
  return true if @struct.member?( sym.to_s.sub(/(=|\?)$/, '').to_sym )
  super
end

#write(path = @path, *args) ⇒ Object

Write the configuration object using the specified name and any additional args.

Raises:

  • (ArgumentError)


151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/configurability/config.rb', line 151

def write( path=@path, *args )
  unless path.is_a?( String ) || path.is_a?( Pathname )
    args.unshift( path )
    path = @path
  end

  raise ArgumentError,
    "No name associated with this config." unless path

  self.log.info "Writing config to %s with args: %p" % [ path, args ]
  path = Pathname( path )
  path.open( File::WRONLY|File::CREAT|File::TRUNC, *args ) do |ofh|
    ofh.print( self.dump )
  end
end