construct 0.1.0

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.
Files changed (4) hide show
  1. data/LICENSE +25 -0
  2. data/README +78 -0
  3. data/lib/construct.rb +135 -0
  4. metadata +55 -0
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2009 Kyle Kingsbury <aphyr@aphyr.com>
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ * Neither the name of this project nor the names of its contributors may be
13
+ used to endorse or promote products derived from this software without
14
+ specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README ADDED
@@ -0,0 +1,78 @@
1
+ Construct is extensible, persistent, structured configuration for Ruby and
2
+ humans with text editors. What do I mean?
3
+
4
+ You have a ruby program. Maybe it's a Ramaze app. It needs to store some
5
+ configuration data. It should be easily serialized to YAML so anyone can edit
6
+ the file easily. It should support nested structures. It should minimize
7
+ typing, and make access unambiguous. It should take no effort to create and
8
+ extend multi-layer configuration with many data types and accessors. Moreover,
9
+ it should allow you to define a schema for the options that are available,
10
+ making documentation easy. That's where Construct comes in.
11
+
12
+ gem install construct
13
+
14
+ config = Construct.new
15
+
16
+ Hey, we have a configuration object. Let's describe a web site. Keys are always symbols, but can be accessed via methods just like an OpenStruct.
17
+
18
+ config.name = "Aphyr"
19
+ config.base_url = "http://aphyr.com"
20
+
21
+ And getting at them is easy, too.
22
+
23
+ config.name
24
+ => "Aphyr"
25
+
26
+ If the key you want is already a method, just use config#[] and config#[]=. Construct converts strings to symbols for you..
27
+
28
+ How about a database connection? Those parameters all belong together.
29
+
30
+ config.db = {
31
+ :user => 'cr',
32
+ :password => 'some password',
33
+ }
34
+
35
+ Secretly, Construct converted that hash into another Construct. You can chain
36
+ methods to easily access nested options.
37
+
38
+ config.db.user
39
+ => 'cr'
40
+
41
+ Databases need a host. Let's define a host field.
42
+
43
+ config.db.schema[:host] = {
44
+ :default => '127.0.0.1',
45
+ :desc => 'The host the database adapter connects to.'
46
+ }
47
+ config.db.host
48
+ => '127.0.0.1'
49
+
50
+ You can override this, naturally.
51
+
52
+ config.db.host = 'db.aphyr.com'
53
+ config.db.host
54
+ => 'db.aphyr.com'
55
+
56
+ And it serializes neatly to YAML. Keys are expressed as strings, to save typing and make things look clean.
57
+
58
+ config.to_yaml =>
59
+ ---
60
+ db:
61
+ user: cr
62
+ password: some password
63
+ name: Aphyr
64
+ base_url: http://aphyr.com
65
+
66
+ Hey, this is easy! Now let's load that configuration from a string.
67
+
68
+ config = Construct.load yaml
69
+
70
+ Maybe you need to implement extra logic in your config--perhaps you can specify either a db hash as shown above, or a connection string. Just subclass Construct::Construct or define methods directly on the construct.
71
+
72
+ class DBConfig < Construct::Construct
73
+ def db_str
74
+ self[:db_str] || make_db_string_from_hash(db)
75
+ end
76
+ end
77
+
78
+ Problem solved. Now get back to having fun instead of worrying about configuration!
data/lib/construct.rb ADDED
@@ -0,0 +1,135 @@
1
+ class Construct
2
+ # Construct is extensible, persistent, structured configuration for
3
+ # Ruby and humans with text editors.
4
+
5
+ APP_NAME = 'Construct'
6
+ APP_VERSION = '0.1.0'
7
+ APP_AUTHOR = 'Kyle Kingsbury'
8
+ APP_EMAIL = 'aphyr@aphyr.com'
9
+ APP_URL = 'http://aphyr.com'
10
+ APP_COPYRIGHT = 'Copyright (c) 2009 Kyle Kingsbury <aphyr@aphyr.com>. All rights reserved.'
11
+
12
+ require 'yaml'
13
+
14
+ YAML::add_domain_type("aphyr.com,2009", "construct") do |type, val|
15
+ # Not implemented ;)
16
+ Construct.load(val)
17
+ end
18
+ yaml_as "tag:aphyr.com,2009:construct"
19
+ yaml_as "tag:yaml.org,2002:map"
20
+
21
+ # Load a construct from a YAML string
22
+ def self.load(yaml)
23
+ hash = YAML::load(yaml)
24
+ new(hash)
25
+ end
26
+
27
+ attr_accessor :schema, :data
28
+
29
+ def initialize(data = {}, schema = {})
30
+ @data = Hash.new
31
+ data.each do |key, value|
32
+ self[key] = value
33
+ end
34
+ @schema = schema
35
+ end
36
+
37
+ def ==(other)
38
+ @schema == other.schema and @data == other.data
39
+ end
40
+
41
+ def [](key)
42
+ key = key.to_sym if String === key
43
+
44
+ if @data.include? key
45
+ @data[key]
46
+ elsif @schema.include? key
47
+ @schema[key][:default]
48
+ end
49
+ end
50
+
51
+ # Assign a value to a key. Constructs accept only symbols as values,
52
+ # and will convert strings to symbols when necessary. They will also
53
+ # implicitly convert Hashes as values into Constructs when possible. Hence
54
+ # you can do:
55
+ #
56
+ # construct.people = {:mary => 'Awesome', :joe => 'suspicious'}
57
+ # construct.people.mary # => 'Awesome'
58
+ def []=(key, value)
59
+ key = key.to_sym if String === key
60
+ raise ArgumentError.new('construct only accepts symbols (and strings) as keys.') unless key.is_a? Symbol
61
+
62
+ # Convert suitable hashes into Constructs
63
+ if value.is_a? Hash
64
+ if value.keys.all? { |k|
65
+ k.is_a? String or k.is_a? Symbol
66
+ }
67
+ value = Construct.new(value)
68
+ end
69
+ end
70
+
71
+ @data[key] = value
72
+ end
73
+
74
+ # Clears the data in the construct.
75
+ def clear
76
+ @data.clear
77
+ end
78
+
79
+ # Defines a new field in the schema. Fields are :default and :desc.
80
+ def define(key, options = {})
81
+ key = key.to_sym if String === key
82
+ @schema[key] = options
83
+ end
84
+
85
+ # delete simply removes the value from the data hash, but leaves the schema
86
+ # unchanged. Hence the construct may still respond to include? if the
87
+ # schema defines that field. Use #schema.delete(:key) to remove the key
88
+ # entirely.
89
+ def delete(key)
90
+ key = key.to_sym if String === key
91
+ @data.delete key
92
+ end
93
+
94
+ # Returns true if the construct has a value set for, or the schema defines,
95
+ # the key.
96
+ def include?(*args)
97
+ @data.include?(*args) or @schema.include?(*args)
98
+ end
99
+
100
+ # Returns the keys, both set in the construct and specified in the schema.
101
+ def keys
102
+ @data.keys | @schema.keys
103
+ end
104
+
105
+ def load(str)
106
+ end
107
+
108
+ def method_missing(meth, *args)
109
+ meth_s = meth.to_s
110
+ if meth_s[-1..-1] == '='
111
+ # Assignment
112
+ if args.size != 1
113
+ raise ArgumentError.new("#{meth} takes exactly one argument")
114
+ end
115
+
116
+ self[meth_s[0..-2]] = args[0]
117
+ elsif include? meth
118
+ self[meth]
119
+ else
120
+ raise NoMethodError.new('no such key in construct')
121
+ end
122
+ end
123
+
124
+ # Dumps the data (not the schema!) of this construct to YAML. Keys are
125
+ # expressed as strings.
126
+ def to_yaml(opts = {})
127
+ YAML::quick_emit(self, opts) do |out|
128
+ out.map(taguri, to_yaml_style) do |map|
129
+ @data.each do |key, value|
130
+ map.add(key.to_s, value)
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: construct
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kyle Kingsbury
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-12 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: aphyr@aphyr.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - lib/construct.rb
26
+ - LICENSE
27
+ - README
28
+ has_rdoc: true
29
+ homepage: http://aphyr.com
30
+ post_install_message:
31
+ rdoc_options: []
32
+
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 1.8.5
40
+ version:
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ version:
47
+ requirements: []
48
+
49
+ rubyforge_project: construct
50
+ rubygems_version: 1.3.1
51
+ signing_key:
52
+ specification_version: 2
53
+ summary: Extensible, persistent, structured configuration for Ruby.
54
+ test_files: []
55
+