configurability 1.0.10 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data.tar.gz.sig CHANGED
Binary file
data/History.rdoc CHANGED
@@ -1,3 +1,11 @@
1
+ == v1.1.0 [2012-04-25] Michael Granger <ged@FaerieMUD.org>
2
+
3
+ Add a 'defaults' API that allows defaults to be gathered from any
4
+ object with configurability and merged into a hash keyed by the
5
+ object's config_key. This hash can be used to generate an initial
6
+ unified config file for all configurable parts of a given system.
7
+
8
+
1
9
  == v1.0.10 [2012-03-13] Michael Granger <ged@FaerieMUD.org>
2
10
 
3
11
  - Fix log level message.
data/Manifest.txt CHANGED
@@ -10,7 +10,7 @@ lib/configurability.rb
10
10
  lib/configurability/behavior.rb
11
11
  lib/configurability/config.rb
12
12
  lib/configurability/deferredconfig.rb
13
- lib/configurability/logformatter.rb
13
+ lib/configurability/logging.rb
14
14
  spec/configurability/config_spec.rb
15
15
  spec/configurability/deferredconfig_spec.rb
16
16
  spec/configurability_spec.rb
data/README.rdoc CHANGED
@@ -1,14 +1,19 @@
1
1
  = Configurability
2
2
 
3
- * http://bitbucket.org/ged/configurability
3
+ home :: https://bitbucket.org/ged/configurability
4
+ code :: https://bitbucket.org/ged/configurability
5
+ docs :: http://deveiate.org/code/configurability
6
+ github :: https://github.com/ged/configurability
4
7
 
5
8
 
6
9
  == Description
7
10
 
8
- Configurability is a mixin that allows you to add configurability to one or
9
- more objects or classes. You can assign them each a subsection of the
10
- configuration, and then later, when the configuration is loaded, the
11
- configuration is split up and sent to the objects that will use it.
11
+ Configurability is a unified, unintrusive, assume-nothing configuration system
12
+ for Ruby. It lets you keep the configuration for multiple objects in a single
13
+ config file, load the file when it's convenient for you, and distribute the
14
+ configuration when you're ready, sending it everywhere it needs to go with a
15
+ single action.
16
+
12
17
 
13
18
 
14
19
  == Installation
@@ -93,6 +98,9 @@ as a Symbol:
93
98
 
94
99
  === Changing How an Object Is Configured
95
100
 
101
+ [:FIXME:] Explain the 'at least once' configuration call, and how to handle
102
+ being called with 'nil'.
103
+
96
104
  You can also change what happens when an object is configured by implementing
97
105
  a `#configure` method that takes the config section as an argument:
98
106
 
@@ -216,19 +224,27 @@ then dump it to a YAML string:
216
224
 
217
225
  config.dump
218
226
  # => "--- \ndatabase: \n development: \n adapter: sqlite3\n
219
- database: db/dev.db\n pool: 5\n timeout: 5000\n testing: \n
220
- adapter: mysql\n database: t_fixedassets\n pool: 2\n timeout:
221
- 5000\n production: \n adapter: postgres\n database:
222
- fixedassets\n pool: 25\n timeout: 50\nldap: \n uri:
223
- ldap://ldap.acme.com/dc=acme,dc=com\n bind_dn:
224
- cn=web,dc=acme,dc=com\n bind_pass: Mut@ge.Mix@ge\nbranding: \n
225
- header: \"#333\"\n title: \"#dedede\"\n anchor: \"#9fc8d4\"\n"
227
+ database: db/dev.db\n pool: 5\n timeout: 5000\n testing: \n
228
+ adapter: mysql\n database: t_fixedassets\n pool: 2\n timeout:
229
+ 5000\n production: \n adapter: postgres\n database:
230
+ fixedassets\n pool: 25\n timeout: 50\nldap: \n uri:
231
+ ldap://ldap.acme.com/dc=acme,dc=com\n bind_dn:
232
+ cn=web,dc=acme,dc=com\n bind_pass: Mut@ge.Mix@ge\nbranding: \n
233
+ header: \"#333\"\n title: \"#dedede\"\n anchor: \"#9fc8d4\"\n"
226
234
 
227
235
  or write it back to the file it was loaded from:
228
236
 
229
237
  config.write
230
238
 
231
239
 
240
+ == Configuration Defaults
241
+
242
+ Configurability also supports an API for generating a new config file with
243
+ defaults for all objects with Configurability.
244
+
245
+ [:FIXME:] Finish up the documentation
246
+
247
+
232
248
  == Development
233
249
 
234
250
  You can submit bug reports, suggestions, clone it with Mercurial, and
data/Rakefile CHANGED
@@ -10,6 +10,7 @@ end
10
10
  Hoe.plugin :mercurial
11
11
  Hoe.plugin :yard
12
12
  Hoe.plugin :signing
13
+ Hoe.plugin :deveiate
13
14
 
14
15
  Hoe.plugins.delete :rubyforge
15
16
 
@@ -25,6 +26,7 @@ hoespec = Hoe.spec 'configurability' do
25
26
  self.dependency 'simplecov', '~> 0.3', :developer
26
27
 
27
28
  self.spec_extras[:licenses] = ["BSD"]
29
+ self.spec_extras[:rdoc_options] = ['-f', 'fivefish', '-t', 'Configurability Toolkit']
28
30
  self.require_ruby_version( '>= 1.8.7' )
29
31
 
30
32
  self.hg_sign_tags = true if self.respond_to?( :hg_sign_tags= )
@@ -11,14 +11,19 @@ require 'yaml'
11
11
  module Configurability
12
12
 
13
13
  # Library version constant
14
- VERSION = '1.0.10'
14
+ VERSION = '1.1.0'
15
15
 
16
16
  # Version-control revision constant
17
- REVISION = %q$Revision: 0dcd8dcebc87 $
17
+ REVISION = %q$Revision: e56100a012e3 $
18
+
19
+
20
+ require 'configurability/logging'
21
+ extend Configurability::Logging
18
22
 
19
- require 'configurability/logformatter'
20
23
  require 'configurability/deferredconfig'
21
24
 
25
+ autoload :Config, 'configurability/config'
26
+
22
27
 
23
28
  ### The objects that have had Configurability added to them
24
29
  @configurable_objects = []
@@ -31,16 +36,6 @@ module Configurability
31
36
  ### are the config section it was called with
32
37
  @configured = Hash.new( false )
33
38
 
34
- ### Logging
35
- @default_logger = Logger.new( $stderr )
36
- @default_logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN
37
-
38
- @default_log_formatter = Configurability::LogFormatter.new( @default_logger )
39
- @default_logger.formatter = @default_log_formatter
40
-
41
- @logger = @default_logger
42
-
43
-
44
39
  class << self
45
40
 
46
41
  # the Array of objects that have had Configurability added to them
@@ -52,17 +47,15 @@ module Configurability
52
47
  # the hash of configure methods => config sections which have already been installed
53
48
  attr_reader :configured
54
49
 
55
- # the log formatter that will be used when the logging subsystem is
56
- # reset
57
- attr_accessor :default_log_formatter
50
+ end
58
51
 
59
- # the logger that will be used when the logging subsystem is reset
60
- attr_accessor :default_logger
61
52
 
62
- # the logger that's currently in effect
63
- attr_accessor :logger
64
- alias_method :log, :logger
65
- alias_method :log=, :logger=
53
+ ### Get the library version. If +include_buildnum+ is true, the version string will
54
+ ### include the VCS rev ID.
55
+ def self::version_string( include_buildnum=false )
56
+ vstring = "%s %s" % [ self.name, VERSION ]
57
+ vstring << " (build %s)" % [ REVISION[/: ([[:xdigit:]]+)/, 1] || '0' ] if include_buildnum
58
+ return vstring
66
59
  end
67
60
 
68
61
 
@@ -128,14 +121,6 @@ module Configurability
128
121
  end
129
122
 
130
123
 
131
- ### Reset the global logger object to the default
132
- def self::reset_logger
133
- self.logger = self.default_logger
134
- self.logger.level = Logger::WARN
135
- self.logger.formatter = self.default_log_formatter
136
- end
137
-
138
-
139
124
  ### Install the appropriate section of the +config+ into the given +object+.
140
125
  def self::install_config( config, object )
141
126
  section = object.config_key.to_sym
@@ -155,8 +140,7 @@ module Configurability
155
140
  section = nil
156
141
  end
157
142
  else
158
- self.log.info " don't know how to get the %p section of the config from %p" %
159
- [ section, config ]
143
+ self.log.info " no %p section in %p; configuring with nil" % [ section, config ]
160
144
  section = nil
161
145
  end
162
146
 
@@ -174,10 +158,42 @@ module Configurability
174
158
  end
175
159
 
176
160
 
161
+ ### Gather defaults from objects with Configurability in the given +collection+
162
+ ### object. Objects that wish to add a section to the defaults should implement
163
+ ### a #defaults method in the same scope as #configure that returns the Hash of
164
+ ### default, or set one of the constants in the default implementation of
165
+ ### #defaults. The hash for each object will be merged into the +collection+
166
+ ### via #merge!.
167
+ def self::gather_defaults( collection={} )
168
+ self.configurable_objects.each do |obj|
169
+ next unless obj.respond_to?( :defaults )
170
+ unless subhash = obj.defaults
171
+ Configurability.log.warn "No defaults for %p; skipping" % [ obj ]
172
+ next
173
+ end
174
+ section = obj.config_key.to_sym
175
+
176
+ collection.merge!( section => subhash )
177
+ end
178
+
179
+ return collection
180
+ end
181
+
182
+
183
+ ### Gather the default configuration in a Configurability::Config object and return it.
184
+ def self::default_config
185
+ return self.gather_defaults( Configurability::Config.new )
186
+ end
187
+
188
+
177
189
  #############################################################
178
190
  ### A P P E N D E D M E T H O D S
179
191
  #############################################################
180
192
 
193
+ #
194
+ # :section: Configuration API
195
+ #
196
+
181
197
  ### Get (and optionally set) the +config_key+ (a Symbol).
182
198
  def config_key( sym=nil )
183
199
  @config_key = sym unless sym.nil?
@@ -198,5 +214,31 @@ module Configurability
198
214
  end
199
215
 
200
216
 
217
+ #
218
+ # :section: Configuration Defaults API
219
+ #
220
+
221
+ ### The default implementation of the method called by ::gather_defaults when
222
+ ### gathering configuration defaults. This method expects either a
223
+ ### +DEFAULT_CONFIG+ or a +CONFIG_DEFAULTS+ constant to contain the configuration
224
+ ### defaults, and will just return +nil+ if neither exists.
225
+ def defaults
226
+
227
+ return nil unless respond_to?( :const_defined? )
228
+
229
+ Configurability.log.debug "Looking for defaults in %p's constants." % [ self ]
230
+ if self.const_defined?( :DEFAULT_CONFIG )
231
+ Configurability.log.debug " found DEFAULT_CONFIG"
232
+ return self.const_get( :DEFAULT_CONFIG ).dup
233
+ elsif self.const_defined?( :CONFIG_DEFAULTS )
234
+ Configurability.log.debug " found CONFIG_DEFAULTS"
235
+ return self.const_get( :CONFIG_DEFAULTS ).dup
236
+ else
237
+ Configurability.log.debug " no default constants."
238
+ return nil
239
+ end
240
+ end
241
+
242
+
201
243
  end # module Configurability
202
244
 
@@ -140,9 +140,17 @@ class Configurability::Config
140
140
  ### Write the configuration object using the specified name and any
141
141
  ### additional +args+.
142
142
  def write( path=@path, *args )
143
+ unless path.is_a?( String ) || path.is_a?( Pathname )
144
+ args.unshift( path )
145
+ path = @path
146
+ end
147
+
143
148
  raise ArgumentError,
144
149
  "No name associated with this config." unless path
145
- path.open( File::WRONLY|File::CREAT|File::TRUNC ) do |ofh|
150
+
151
+ self.log.info "Writing config to %s with args: %p" % [ path, args ]
152
+ path = Pathname( path )
153
+ path.open( File::WRONLY|File::CREAT|File::TRUNC, *args ) do |ofh|
146
154
  ofh.print( self.dump )
147
155
  end
148
156
  end
@@ -254,6 +262,12 @@ class Configurability::Config
254
262
  end
255
263
 
256
264
 
265
+ ### Delegate logging to the module's Logger.
266
+ def log
267
+ Configurability.logger
268
+ end
269
+
270
+
257
271
  #######
258
272
  private
259
273
  #######
@@ -0,0 +1,300 @@
1
+ # -*- ruby -*-
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ # encoding: utf-8
4
+
5
+ require 'logger'
6
+ require 'date'
7
+
8
+ require 'configurability' unless defined?( Configurability )
9
+
10
+
11
+ # A mixin that provides a top-level logging subsystem based on Logger.
12
+ module Configurability::Logging
13
+
14
+ ### Logging
15
+ # Log levels
16
+ LOG_LEVELS = {
17
+ 'debug' => Logger::DEBUG,
18
+ 'info' => Logger::INFO,
19
+ 'warn' => Logger::WARN,
20
+ 'error' => Logger::ERROR,
21
+ 'fatal' => Logger::FATAL,
22
+ }.freeze
23
+ LOG_LEVEL_NAMES = LOG_LEVELS.invert.freeze
24
+
25
+
26
+ ### Inclusion hook
27
+ def self::extended( mod )
28
+ super
29
+
30
+ class << mod
31
+ # the log formatter that will be used when the logging subsystem is reset
32
+ attr_accessor :default_log_formatter
33
+
34
+ # the logger that will be used when the logging subsystem is reset
35
+ attr_accessor :default_logger
36
+
37
+ # the logger that's currently in effect
38
+ attr_accessor :logger
39
+ alias_method :log, :logger
40
+ alias_method :log=, :logger=
41
+ end
42
+
43
+ mod.default_logger = mod.logger = Logger.new( $stderr )
44
+ mod.default_logger.level = case
45
+ when $DEBUG then Logger::DEBUG
46
+ when $VERBOSE then Logger::INFO
47
+ else Logger::WARN end
48
+ mod.default_log_formatter = Configurability::Logging::Formatter.new( mod.default_logger )
49
+ end
50
+
51
+
52
+ ### Reset the global logger object to the default
53
+ def reset_logger
54
+ self.logger = self.default_logger
55
+ self.logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN
56
+ self.logger.formatter = self.default_log_formatter
57
+ end
58
+
59
+
60
+ ### Returns +true+ if the global logger has not been set to something other than
61
+ ### the default one.
62
+ def using_default_logger?
63
+ return self.logger == self.default_logger
64
+ end
65
+
66
+
67
+ # A alternate formatter for Logger instances.
68
+ class Formatter < Logger::Formatter
69
+
70
+ # The format to output unless debugging is turned on
71
+ DEFAULT_FORMAT = "[%1$s.%2$06d %3$d/%4$s] %5$5s -- %7$s\n"
72
+
73
+ # The format to output if debugging is turned on
74
+ DEFAULT_DEBUG_FORMAT = "[%1$s.%2$06d %3$d/%4$s] %5$5s {%6$s} -- %7$s\n"
75
+
76
+
77
+ ### Initialize the formatter with a reference to the logger so it can check for log level.
78
+ def initialize( logger, format=DEFAULT_FORMAT, debug=DEFAULT_DEBUG_FORMAT ) # :notnew:
79
+ @logger = logger
80
+ @format = format
81
+ @debug_format = debug
82
+
83
+ super()
84
+ end
85
+
86
+ ######
87
+ public
88
+ ######
89
+
90
+ # The Logger object associated with the formatter
91
+ attr_accessor :logger
92
+
93
+ # The logging format string
94
+ attr_accessor :format
95
+
96
+ # The logging format string that's used when outputting in debug mode
97
+ attr_accessor :debug_format
98
+
99
+
100
+ ### Log using either the DEBUG_FORMAT if the associated logger is at ::DEBUG level or
101
+ ### using FORMAT if it's anything less verbose.
102
+ def call( severity, time, progname, msg )
103
+ args = [
104
+ time.strftime( '%Y-%m-%d %H:%M:%S' ), # %1$s
105
+ time.usec, # %2$d
106
+ Process.pid, # %3$d
107
+ Thread.current == Thread.main ? 'main' : Thread.object_id, # %4$s
108
+ severity, # %5$s
109
+ progname, # %6$s
110
+ msg # %7$s
111
+ ]
112
+
113
+ if @logger.level == Logger::DEBUG
114
+ return self.debug_format % args
115
+ else
116
+ return self.format % args
117
+ end
118
+ end
119
+ end # class Formatter
120
+
121
+
122
+ # A ANSI-colorized formatter for Logger instances.
123
+ class ColorFormatter < Logger::Formatter
124
+
125
+ # Set some ANSI escape code constants (Shamelessly stolen from Perl's
126
+ # Term::ANSIColor by Russ Allbery <rra@stanford.edu> and Zenin <zenin@best.com>
127
+ ANSI_ATTRIBUTES = {
128
+ 'clear' => 0,
129
+ 'reset' => 0,
130
+ 'bold' => 1,
131
+ 'dark' => 2,
132
+ 'underline' => 4,
133
+ 'underscore' => 4,
134
+ 'blink' => 5,
135
+ 'reverse' => 7,
136
+ 'concealed' => 8,
137
+
138
+ 'black' => 30, 'on_black' => 40,
139
+ 'red' => 31, 'on_red' => 41,
140
+ 'green' => 32, 'on_green' => 42,
141
+ 'yellow' => 33, 'on_yellow' => 43,
142
+ 'blue' => 34, 'on_blue' => 44,
143
+ 'magenta' => 35, 'on_magenta' => 45,
144
+ 'cyan' => 36, 'on_cyan' => 46,
145
+ 'white' => 37, 'on_white' => 47
146
+ }
147
+
148
+
149
+ ### Create a string that contains the ANSI codes specified and return it
150
+ def self::ansi_code( *attributes )
151
+ attributes.flatten!
152
+ attributes.collect! {|at| at.to_s }
153
+ return '' unless /(?:vt10[03]|xterm(?:-color)?|linux|screen)/i =~ ENV['TERM']
154
+ attributes = ANSI_ATTRIBUTES.values_at( *attributes ).compact.join(';')
155
+
156
+ if attributes.empty?
157
+ return ''
158
+ else
159
+ return "\e[%sm" % attributes
160
+ end
161
+ end
162
+
163
+
164
+ ### Colorize the given +string+ with the specified +attributes+ and
165
+ ### return it, handling line-endings, color reset, etc.
166
+ def self::colorize( *args )
167
+ string = ''
168
+
169
+ if block_given?
170
+ string = yield
171
+ else
172
+ string = args.shift
173
+ end
174
+
175
+ ending = string[/(\s)$/] || ''
176
+ string = string.rstrip
177
+
178
+ return self.ansi_code( args.flatten ) + string + self.ansi_code( 'reset' ) + ending
179
+ end
180
+
181
+
182
+ # Color settings
183
+ LEVEL_FORMATS = {
184
+ :debug => colorize( :bold, :black ) {"[%1$s.%2$06d %3$d/%4$s] %5$5s {%6$s} -- %7$s\n"},
185
+ :info => colorize( :normal ) {"[%1$s.%2$06d %3$d/%4$s] %5$5s -- %7$s\n"},
186
+ :warn => colorize( :bold, :yellow ) {"[%1$s.%2$06d %3$d/%4$s] %5$5s -- %7$s\n"},
187
+ :error => colorize( :red ) {"[%1$s.%2$06d %3$d/%4$s] %5$5s -- %7$s\n"},
188
+ :fatal => colorize( :bold, :red, :on_white ) {"[%1$s.%2$06d %3$d/%4$s] %5$5s -- %7$s\n"},
189
+ }
190
+
191
+
192
+ ### Initialize the formatter with a reference to the logger so it can check for log level.
193
+ def initialize( logger, settings={} ) # :notnew:
194
+ settings = LEVEL_FORMATS.merge( settings )
195
+
196
+ @logger = logger
197
+ @settings = settings
198
+
199
+ super()
200
+ end
201
+
202
+ ######
203
+ public
204
+ ######
205
+
206
+ # The Logger object associated with the formatter
207
+ attr_accessor :logger
208
+
209
+ # The formats, by level
210
+ attr_accessor :settings
211
+
212
+
213
+ ### Log using the format associated with the severity
214
+ def call( severity, time, progname, msg )
215
+ args = [
216
+ time.strftime( '%Y-%m-%d %H:%M:%S' ), # %1$s
217
+ time.usec, # %2$d
218
+ Process.pid, # %3$d
219
+ Thread.current == Thread.main ? 'main' : Thread.object_id, # %4$s
220
+ severity, # %5$s
221
+ progname, # %6$s
222
+ msg # %7$s
223
+ ]
224
+
225
+ return self.settings[ severity.downcase.to_sym ] % args
226
+ end
227
+
228
+ end # class Formatter
229
+
230
+
231
+ # An alternate formatter for Logger instances that outputs +div+ HTML
232
+ # fragments.
233
+ class HtmlFormatter < Logger::Formatter
234
+
235
+ # The default HTML fragment that'll be used as the template for each log message.
236
+ HTML_LOG_FORMAT = %q{
237
+ <div class="log-message %5$s">
238
+ <span class="log-time">%1$s.%2$06d</span>
239
+ [
240
+ <span class="log-pid">%3$d</span>
241
+ /
242
+ <span class="log-tid">%4$s</span>
243
+ ]
244
+ <span class="log-level">%5$s</span>
245
+ :
246
+ <span class="log-name">%6$s</span>
247
+ <span class="log-message-text">%7$s</span>
248
+ </div>
249
+ }
250
+
251
+ ### Override the logging formats with ones that generate HTML fragments
252
+ def initialize( logger, format=HTML_LOG_FORMAT ) # :notnew:
253
+ @logger = logger
254
+ @format = format
255
+ super()
256
+ end
257
+
258
+
259
+ ######
260
+ public
261
+ ######
262
+
263
+ # The HTML fragment that will be used as a format() string for the log
264
+ attr_accessor :format
265
+
266
+
267
+ ### Return a log message composed out of the arguments formatted using the
268
+ ### formatter's format string
269
+ def call( severity, time, progname, msg )
270
+ args = [
271
+ time.strftime( '%Y-%m-%d %H:%M:%S' ), # %1$s
272
+ time.usec, # %2$d
273
+ Process.pid, # %3$d
274
+ Thread.current == Thread.main ? 'main' : Thread.object_id, # %4$s
275
+ severity.downcase, # %5$s
276
+ progname, # %6$s
277
+ escape_html( msg ).gsub(/\n/, '<br />') # %7$s
278
+ ]
279
+
280
+ return self.format % args
281
+ end
282
+
283
+
284
+ #######
285
+ private
286
+ #######
287
+
288
+ ### Escape any HTML special characters in +string+.
289
+ def escape_html( string )
290
+ return string.
291
+ gsub( '&', '&amp;' ).
292
+ gsub( '<', '&lt;' ).
293
+ gsub( '>', '&gt;' )
294
+ end
295
+
296
+ end # class HtmlFormatter
297
+
298
+
299
+ end # module Configurability
300
+
@@ -10,7 +10,6 @@ BEGIN {
10
10
  $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
11
11
  }
12
12
 
13
- require 'tempfile'
14
13
  require 'logger'
15
14
  require 'fileutils'
16
15
  require 'rspec'
@@ -48,6 +47,11 @@ describe Configurability::Config do
48
47
  setup_logging( :fatal )
49
48
  end
50
49
 
50
+ after( :each ) do
51
+ Configurability.configurable_objects.clear
52
+ Configurability.reset
53
+ end
54
+
51
55
  after( :all ) do
52
56
  reset_logging()
53
57
  end
@@ -135,7 +139,7 @@ describe Configurability::Config do
135
139
 
136
140
  context "created with in-memory YAML source" do
137
141
 
138
- before(:each) do
142
+ before( :each ) do
139
143
  @config = Configurability::Config.new( TEST_CONFIG )
140
144
  end
141
145
 
@@ -206,7 +210,7 @@ describe Configurability::Config do
206
210
 
207
211
  # saving if changed since loaded
208
212
  context " whose internal values have been changed since loaded" do
209
- before(:each) do
213
+ before( :each ) do
210
214
  @config = Configurability::Config.new( TEST_CONFIG )
211
215
  @config.section.subsection.anothersection = 11451
212
216
  end
@@ -226,42 +230,50 @@ describe Configurability::Config do
226
230
 
227
231
  # loading from a file
228
232
  context " loaded from a file" do
229
- before(:all) do
230
- @tmpfile = Tempfile.new( 'test.conf', '.' )
231
- @tmpfile.print( TEST_CONFIG )
232
- @tmpfile.close
233
+ before( :all ) do
234
+ filename = Dir::Tmpname.make_tmpname( './test', '.conf' )
235
+ @tmpfile = Pathname( filename )
236
+ @tmpfile.open( 'w', 0644 ) {|io| io.print(TEST_CONFIG) }
233
237
  end
234
238
 
235
- after(:all) do
236
- @tmpfile.delete
239
+ after( :all ) do
240
+ @tmpfile.unlink
237
241
  end
238
242
 
239
-
240
- before(:each) do
241
- @config = Configurability::Config.load( @tmpfile.path )
243
+ before( :each ) do
244
+ @config = Configurability::Config.load( @tmpfile.to_s )
242
245
  end
243
246
 
244
247
 
245
248
  ### Specifications
246
249
  it "remembers which file it was loaded from" do
247
- @config.path.should == Pathname( @tmpfile.path ).expand_path
250
+ @config.path.should == @tmpfile.expand_path
248
251
  end
249
252
 
250
253
  it "writes itself back to the same file by default" do
251
254
  @config.port = 114411
252
255
  @config.write
253
- otherconfig = Configurability::Config.load( @tmpfile.path )
256
+ otherconfig = Configurability::Config.load( @tmpfile.to_s )
254
257
 
255
258
  otherconfig.port.should == 114411
256
259
  end
257
260
 
261
+ it "can be written to a different file" do
262
+ path = Dir::Tmpname.make_tmpname( './another-', '.config' )
263
+
264
+ @config.write( path )
265
+ File.read( path ).should =~ /section:\n subsection/
266
+
267
+ File.unlink( path )
268
+ end
269
+
258
270
  it "includes the name of the file in its inspect output" do
259
- @config.inspect.should include( File.basename(@tmpfile.path) )
271
+ @config.inspect.should include( File.basename(@tmpfile.to_s) )
260
272
  end
261
273
 
262
274
  it "yields itself if a block is given at load-time" do
263
275
  yielded_self = nil
264
- config = Configurability::Config.load( @tmpfile.path ) do
276
+ config = Configurability::Config.load( @tmpfile.to_s ) do
265
277
  yielded_self = self
266
278
  end
267
279
  yielded_self.should equal( config )
@@ -270,7 +282,7 @@ describe Configurability::Config do
270
282
  it "passes itself as the block argument if a block of arity 1 is given at load-time" do
271
283
  arg_self = nil
272
284
  yielded_self = nil
273
- config = Configurability::Config.load( @tmpfile.path ) do |arg|
285
+ config = Configurability::Config.load( @tmpfile.to_s ) do |arg|
274
286
  yielded_self = self
275
287
  arg_self = arg
276
288
  end
@@ -288,23 +300,23 @@ describe Configurability::Config do
288
300
 
289
301
  # reload if file changes
290
302
  context " whose file changes after loading" do
291
- before(:all) do
292
- @tmpfile = Tempfile.new( 'test.conf', '.' )
293
- @tmpfile.print( TEST_CONFIG )
294
- @tmpfile.close
303
+ before( :all ) do
304
+ filename = Dir::Tmpname.make_tmpname( './test', '.conf' )
305
+ @tmpfile = Pathname( filename )
306
+ @tmpfile.open( 'w', 0644 ) {|io| io.print(TEST_CONFIG) }
295
307
  end
296
308
 
297
- after(:all) do
298
- @tmpfile.delete
309
+ after( :all ) do
310
+ @tmpfile.unlink
299
311
  end
300
312
 
301
313
 
302
- before(:each) do
314
+ before( :each ) do
303
315
  old_date = Time.now - 3600
304
- File.utime( old_date, old_date, @tmpfile.path )
305
- @config = Configurability::Config.load( @tmpfile.path )
316
+ File.utime( old_date, old_date, @tmpfile.to_s )
317
+ @config = Configurability::Config.load( @tmpfile.to_s )
306
318
  now = Time.now + 10
307
- File.utime( now, now, @tmpfile.path )
319
+ File.utime( now, now, @tmpfile.to_s )
308
320
  end
309
321
 
310
322
 
@@ -324,7 +336,7 @@ describe Configurability::Config do
324
336
  end
325
337
 
326
338
  it "reapplies its defaults when reloading" do
327
- config = Configurability::Config.load( @tmpfile.path, :defaultskey => 8 )
339
+ config = Configurability::Config.load( @tmpfile.to_s, :defaultskey => 8 )
328
340
  config.reload
329
341
  config.defaultskey.should == 8
330
342
  end
@@ -333,7 +345,7 @@ describe Configurability::Config do
333
345
 
334
346
  # merging
335
347
  context " created by merging two other configs" do
336
- before(:each) do
348
+ before( :each ) do
337
349
  @config1 = Configurability::Config.new
338
350
  @config2 = Configurability::Config.new( TEST_CONFIG )
339
351
  @merged = @config1.merge( @config2 )
@@ -36,7 +36,7 @@ describe Configurability::DeferredConfig do
36
36
  end
37
37
 
38
38
  it "calls Configurability.install_config with itself when a 'configure' method is defined" do
39
- config = double( "config object", :testing => :testing_config )
39
+ config = { :testing => :testing_config }
40
40
  Configurability.configure_objects( config )
41
41
 
42
42
  a_class = Class.new do
@@ -287,6 +287,79 @@ describe Configurability do
287
287
 
288
288
  end
289
289
 
290
+
291
+ describe "defaults hash" do
292
+
293
+ it "can generate a Hash of defaults for all objects with Configurability" do
294
+ Configurability.gather_defaults.should be_a( Hash )
295
+ end
296
+
297
+ it "fetches defaults from a CONFIG_DEFAULTS constant if the object defines one" do
298
+ klass = Class.new do
299
+ extend Configurability
300
+ config_key :testconfig
301
+ self::CONFIG_DEFAULTS = { :one => 1, :types => {:one => true} }
302
+ Configurability.log.debug "Defaults: %p" % [ self.defaults ]
303
+ end
304
+
305
+ Configurability.gather_defaults.should include( :testconfig )
306
+ Configurability.gather_defaults[:testconfig].should == klass.const_get( :CONFIG_DEFAULTS )
307
+ Configurability.gather_defaults[:testconfig].should_not be( klass.const_get(:CONFIG_DEFAULTS) )
308
+ end
309
+
310
+ it "fetches defaults from a DEFAULT_CONFIG constant if the object defines one" do
311
+ klass = Class.new do
312
+ extend Configurability
313
+ config_key :testconfig
314
+ self::DEFAULT_CONFIG = { :two => 2, :types => {:two => true} }
315
+ end
316
+
317
+ Configurability.gather_defaults.should include( :testconfig )
318
+ Configurability.gather_defaults[:testconfig].should == klass.const_get( :DEFAULT_CONFIG )
319
+ Configurability.gather_defaults[:testconfig].should_not be( klass.const_get(:DEFAULT_CONFIG) )
320
+ end
321
+
322
+ it "fetches defaults from a #defaults method if the object implements one" do
323
+ klass = Class.new do
324
+ extend Configurability
325
+ config_key :otherconfig
326
+ def self::defaults; { :other => true }; end
327
+ end
328
+
329
+ Configurability.gather_defaults.should include( :otherconfig )
330
+ Configurability.gather_defaults[:otherconfig].should == klass.defaults
331
+ Configurability.gather_defaults[:otherconfig].should_not be( klass.defaults )
332
+ end
333
+
334
+ it "can return a Configurability::Config object with defaults, too" do
335
+ klass1 = Class.new do
336
+ extend Configurability
337
+ config_key :testconfig
338
+ self::CONFIG_DEFAULTS = { :one => 1, :types => {:one => true} }
339
+ end
340
+ klass2 = Class.new do
341
+ extend Configurability
342
+ config_key :testconfig
343
+ self::DEFAULT_CONFIG = { :two => 2, :types => {:two => true} }
344
+ end
345
+ klass3 = Class.new do
346
+ extend Configurability
347
+ config_key :otherconfig
348
+ def self::defaults; { :other => true }; end
349
+ end
350
+
351
+ config = Configurability.default_config
352
+
353
+ config.should be_a( Configurability::Config )
354
+ config.testconfig.one.should == 1
355
+ config.testconfig.two.should == 2
356
+ config.testconfig.types.one.should be_true()
357
+ config.testconfig.types.two.should be_true()
358
+ config.otherconfig.other.should be_true()
359
+ end
360
+
361
+ end
362
+
290
363
  end
291
364
 
292
365
  # vim: set nosta noet ts=4 sw=4:
data/spec/lib/helpers.rb CHANGED
@@ -142,6 +142,10 @@ end
142
142
  RSpec.configure do |config|
143
143
  config.mock_with( :rspec )
144
144
  config.include( Configurability::SpecHelpers )
145
+ config.treat_symbols_as_metadata_keys_with_true_values = true
146
+
147
+ config.filter_run_excluding :only_ruby_19 if RUBY_VERSION < '1.9.2'
148
+
145
149
  end
146
150
 
147
151
  # vim: set nosta noet ts=4 sw=4:
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: configurability
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.10
4
+ version: 1.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -36,22 +36,27 @@ cert_chain:
36
36
  YUhDS0xaZFNLai9SSHVUT3QrZ2JsUmV4OEZBaDhOZUEKY21saFhlNDZwWk5K
37
37
  Z1dLYnhaYWg4NWpJang5NWhSOHZPSStOQU01aUg5a09xSzEzRHJ4YWNUS1Bo
38
38
  cWo1UGp3RgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
39
- date: 2012-03-13 00:00:00.000000000 Z
39
+ date: 2012-04-26 00:00:00.000000000 Z
40
40
  dependencies:
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: hoe-mercurial
43
- requirement: &70327439150940 !ruby/object:Gem::Requirement
43
+ requirement: !ruby/object:Gem::Requirement
44
44
  none: false
45
45
  requirements:
46
46
  - - ~>
47
47
  - !ruby/object:Gem::Version
48
- version: 1.3.1
48
+ version: 1.4.0
49
49
  type: :development
50
50
  prerelease: false
51
- version_requirements: *70327439150940
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: 1.4.0
52
57
  - !ruby/object:Gem::Dependency
53
58
  name: hoe-highline
54
- requirement: &70327439150420 !ruby/object:Gem::Requirement
59
+ requirement: !ruby/object:Gem::Requirement
55
60
  none: false
56
61
  requirements:
57
62
  - - ~>
@@ -59,10 +64,31 @@ dependencies:
59
64
  version: 0.0.1
60
65
  type: :development
61
66
  prerelease: false
62
- version_requirements: *70327439150420
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ~>
71
+ - !ruby/object:Gem::Version
72
+ version: 0.0.1
73
+ - !ruby/object:Gem::Dependency
74
+ name: rdoc
75
+ requirement: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ~>
79
+ - !ruby/object:Gem::Version
80
+ version: '3.10'
81
+ type: :development
82
+ prerelease: false
83
+ version_requirements: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ~>
87
+ - !ruby/object:Gem::Version
88
+ version: '3.10'
63
89
  - !ruby/object:Gem::Dependency
64
90
  name: rspec
65
- requirement: &70327439149940 !ruby/object:Gem::Requirement
91
+ requirement: !ruby/object:Gem::Requirement
66
92
  none: false
67
93
  requirements:
68
94
  - - ~>
@@ -70,10 +96,15 @@ dependencies:
70
96
  version: '2.4'
71
97
  type: :development
72
98
  prerelease: false
73
- version_requirements: *70327439149940
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ~>
103
+ - !ruby/object:Gem::Version
104
+ version: '2.4'
74
105
  - !ruby/object:Gem::Dependency
75
106
  name: simplecov
76
- requirement: &70327439149500 !ruby/object:Gem::Requirement
107
+ requirement: !ruby/object:Gem::Requirement
77
108
  none: false
78
109
  requirements:
79
110
  - - ~>
@@ -81,44 +112,45 @@ dependencies:
81
112
  version: '0.3'
82
113
  type: :development
83
114
  prerelease: false
84
- version_requirements: *70327439149500
85
- - !ruby/object:Gem::Dependency
86
- name: rdoc
87
- requirement: &70327439149040 !ruby/object:Gem::Requirement
115
+ version_requirements: !ruby/object:Gem::Requirement
88
116
  none: false
89
117
  requirements:
90
118
  - - ~>
91
119
  - !ruby/object:Gem::Version
92
- version: '3.10'
93
- type: :development
94
- prerelease: false
95
- version_requirements: *70327439149040
120
+ version: '0.3'
96
121
  - !ruby/object:Gem::Dependency
97
122
  name: hoe
98
- requirement: &70327439148620 !ruby/object:Gem::Requirement
123
+ requirement: !ruby/object:Gem::Requirement
99
124
  none: false
100
125
  requirements:
101
126
  - - ~>
102
127
  - !ruby/object:Gem::Version
103
- version: '2.13'
128
+ version: '3.0'
104
129
  type: :development
105
130
  prerelease: false
106
- version_requirements: *70327439148620
107
- description: ! 'Configurability is a mixin that allows you to add configurability
108
- to one or
131
+ version_requirements: !ruby/object:Gem::Requirement
132
+ none: false
133
+ requirements:
134
+ - - ~>
135
+ - !ruby/object:Gem::Version
136
+ version: '3.0'
137
+ description: ! 'Configurability is a unified, unintrusive, assume-nothing configuration
138
+ system
139
+
140
+ for Ruby. It lets you keep the configuration for multiple objects in a single
109
141
 
110
- more objects or classes. You can assign them each a subsection of the
142
+ config file, load the file when it''s convenient for you, and distribute the
111
143
 
112
- configuration, and then later, when the configuration is loaded, the
144
+ configuration when you''re ready, sending it everywhere it needs to go with a
113
145
 
114
- configuration is split up and sent to the objects that will use it.'
146
+ single action.'
115
147
  email:
116
148
  - ged@FaerieMUD.org
117
149
  executables: []
118
150
  extensions: []
119
151
  extra_rdoc_files:
120
- - Manifest.txt
121
152
  - History.rdoc
153
+ - Manifest.txt
122
154
  - README.rdoc
123
155
  files:
124
156
  - ChangeLog
@@ -133,19 +165,21 @@ files:
133
165
  - lib/configurability/behavior.rb
134
166
  - lib/configurability/config.rb
135
167
  - lib/configurability/deferredconfig.rb
136
- - lib/configurability/logformatter.rb
168
+ - lib/configurability/logging.rb
137
169
  - spec/configurability/config_spec.rb
138
170
  - spec/configurability/deferredconfig_spec.rb
139
171
  - spec/configurability_spec.rb
140
172
  - spec/lib/helpers.rb
141
173
  - .gemtest
142
- homepage: http://bitbucket.org/ged/configurability
174
+ homepage: https://bitbucket.org/ged/configurability
143
175
  licenses:
144
176
  - BSD
145
177
  post_install_message:
146
178
  rdoc_options:
147
- - --main
148
- - README.rdoc
179
+ - -f
180
+ - fivefish
181
+ - -t
182
+ - Configurability Toolkit
149
183
  require_paths:
150
184
  - lib
151
185
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -162,9 +196,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
162
196
  version: '0'
163
197
  requirements: []
164
198
  rubyforge_project: configurability
165
- rubygems_version: 1.8.16
199
+ rubygems_version: 1.8.21
166
200
  signing_key:
167
201
  specification_version: 3
168
- summary: Configurability is a mixin that allows you to add configurability to one
169
- or more objects or classes
202
+ summary: Configurability is a unified, unintrusive, assume-nothing configuration system
203
+ for Ruby
170
204
  test_files: []
metadata.gz.sig CHANGED
Binary file
@@ -1,60 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'logger'
4
-
5
- require 'configurability'
6
-
7
- # A custom log-formatter class for
8
- class Configurability::LogFormatter < Logger::Formatter
9
-
10
- # The format to output unless debugging is turned on
11
- DEFAULT_FORMAT = "[%1$s.%2$06d %3$d/%4$s] %5$5s -- %7$s\n"
12
-
13
- # The format to output if debugging is turned on
14
- DEFAULT_DEBUG_FORMAT = "[%1$s.%2$06d %3$d/%4$s] %5$5s {%6$s} -- %7$s\n"
15
-
16
-
17
- ### Initialize the formatter with a reference to the logger so it can check for log level.
18
- def initialize( logger, format=DEFAULT_FORMAT, debug=DEFAULT_DEBUG_FORMAT ) # :notnew:
19
- @logger = logger
20
- @format = format
21
- @debug_format = debug
22
-
23
- super()
24
- end
25
-
26
- ######
27
- public
28
- ######
29
-
30
- # The Logger object associated with the formatter
31
- attr_accessor :logger
32
-
33
- # The logging format string
34
- attr_accessor :format
35
-
36
- # The logging format string that's used when outputting in debug mode
37
- attr_accessor :debug_format
38
-
39
-
40
- ### Log using either the DEBUG_FORMAT if the associated logger is at ::DEBUG level or
41
- ### using FORMAT if it's anything less verbose.
42
- def call( severity, time, progname, msg )
43
- args = [
44
- time.strftime( '%Y-%m-%d %H:%M:%S' ), # %1$s
45
- time.usec, # %2$d
46
- Process.pid, # %3$d
47
- Thread.current == Thread.main ? 'main' : Thread.object_id, # %4$s
48
- severity, # %5$s
49
- progname, # %6$s
50
- msg # %7$s
51
- ]
52
-
53
- if @logger.level == Logger::DEBUG
54
- return self.debug_format % args
55
- else
56
- return self.format % args
57
- end
58
- end
59
-
60
- end # class Configurability::LogFormatter