construct 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+