Class: Construct

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

Constant Summary collapse

APP_NAME =

Construct is extensible, persistent, structured configuration for Ruby and humans with text editors.

'Construct'
APP_VERSION =
'0.1.3'
APP_AUTHOR =
'Kyle Kingsbury'
APP_EMAIL =
'[email protected]'
APP_URL =
'http://aphyr.com'
'Copyright (c) 2009 Kyle Kingsbury <[email protected]>. All rights reserved.'

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data = {}, schema = {}) ⇒ Construct

Returns a new instance of Construct.



38
39
40
41
42
43
44
# File 'lib/construct.rb', line 38

def initialize(data = {}, schema = {})
  @data = Hash.new
  data.each do |key, value|
    self[key] = value
  end
  @schema = self.class.schema.merge(schema)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/construct.rb', line 118

def method_missing(meth, *args)
  meth_s = meth.to_s
  if meth_s[-1..-1] == '='
    # Assignment
    if args.size != 1
      raise ArgumentError.new("#{meth} takes exactly one argument")
    end

    self[meth_s[0..-2]] = args[0]
  elsif include? meth
    self[meth]
  else
    raise NoMethodError.new("no such key #{meth} in construct")
  end
end

Class Attribute Details

.schemaObject

Returns the class schema



32
33
34
# File 'lib/construct.rb', line 32

def self.schema
  @schema ||= {}
end

Instance Attribute Details

#dataObject

Returns the value of attribute data.



36
37
38
# File 'lib/construct.rb', line 36

def data
  @data
end

#schemaObject

Returns the value of attribute schema.



36
37
38
# File 'lib/construct.rb', line 36

def schema
  @schema
end

Class Method Details

.define(key, schema) ⇒ Object

Define a schema for a key on the class. The class schema is used as the defaults on initialization of a new instance.



20
21
22
23
# File 'lib/construct.rb', line 20

def self.define(key, schema)
  key = key.to_sym if String === key
  @schema[key] = schema
end

.load(yaml) ⇒ Object

Load a construct from a YAML string



26
27
28
29
# File 'lib/construct.rb', line 26

def self.load(yaml)
  hash = YAML::load(yaml)
  new(hash)
end

Instance Method Details

#==(other) ⇒ Object



46
47
48
49
# File 'lib/construct.rb', line 46

def ==(other)
  other.respond_to? :schema and other.respond_to? :data and
    @schema == other.schema and @data == other.data
end

#[](key) ⇒ Object



51
52
53
54
55
56
57
58
59
# File 'lib/construct.rb', line 51

def [](key)
  key = key.to_sym if String === key

  if @data.include? key
    @data[key]
  elsif @schema.include? key and @schema[key].include? :default
    @schema[key][:default]
  end 
end

#[]=(key, value) ⇒ Object

Assign a value to a key. Constructs accept only symbols as values, and will convert strings to symbols when necessary. They will also implicitly convert Hashes as values into Constructs when possible. Hence you can do:

construct.people = => ‘Awesome’, :joe => ‘suspicious’ construct.people.mary # => ‘Awesome’

Raises:

  • (ArgumentError)


68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/construct.rb', line 68

def []=(key, value)
  key = key.to_sym if String === key
  raise ArgumentError.new('construct only accepts symbols (and strings) as keys.') unless key.is_a? Symbol

  # Convert suitable hashes into Constructs
  if value.is_a? Hash
    if value.keys.all? { |k| 
          k.is_a? String or k.is_a? Symbol
        }
      value = Construct.new(value)
    end
  end

  @data[key] = value
end

#clearObject

Clears the data in the construct.



85
86
87
# File 'lib/construct.rb', line 85

def clear
  @data.clear
end

#define(key, options = {}) ⇒ Object

Defines a new field in the schema. Fields are :default and :desc.



90
91
92
93
# File 'lib/construct.rb', line 90

def define(key, options = {})
  key = key.to_sym if String === key
  @schema[key] = options
end

#delete(key) ⇒ Object

delete simply removes the value from the data hash, but leaves the schema unchanged. Hence the construct may still respond to include? if the schema defines that field. Use #schema.delete(:key) to remove the key entirely.



99
100
101
102
# File 'lib/construct.rb', line 99

def delete(key)
  key = key.to_sym if String === key
  @data.delete key
end

#include?(*args) ⇒ Boolean

Returns true if the construct has a value set for, or the schema defines, the key.

Returns:

  • (Boolean)


106
107
108
# File 'lib/construct.rb', line 106

def include?(*args)
  @data.include?(*args) or (@schema.include?(*args) and @schema[*args].include? :default)
end

#keysObject

Returns the keys, both set in the construct and specified in the schema.



111
112
113
# File 'lib/construct.rb', line 111

def keys
  @data.keys | @schema.keys
end

#load(str) ⇒ Object



115
116
# File 'lib/construct.rb', line 115

def load(str)
end

#to_yaml(opts = {}) ⇒ Object

Dumps the data (not the schema!) of this construct to YAML. Keys are expressed as strings.

This gets a little complicated.

If you define a schema where the default is a Construct

conf.define :sub, :default => Construct

and then try to write to it:

conf.sub.opt = 2

That opt gets stored on the schema sub. Everything works fine… except that when it comes time to serialize there’s now data buried in the schema tree. Therefore, we write out schema objects as well when they are non-empty.



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

def to_yaml(opts = {})
  hash = {}
  @schema.each do |key, value|
    if value[:default].kind_of? Construct
      hashed = YAML::load(value[:default].to_yaml)
      next if hashed.empty?
      hash[key.to_s] = hashed
    end
  end

  @data.each do |key, value|
    hash[key.to_s] = value
  end
  hash.to_yaml(opts)
end