arrow 1.0.7
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.
- data/ChangeLog +1590 -0
- data/LICENSE +28 -0
- data/README +75 -0
- data/Rakefile +366 -0
- data/Rakefile.local +63 -0
- data/data/arrow/applets/TEMPLATE.rb.tpl +53 -0
- data/data/arrow/applets/args.rb +50 -0
- data/data/arrow/applets/config.rb +55 -0
- data/data/arrow/applets/error.rb +63 -0
- data/data/arrow/applets/files.rb +46 -0
- data/data/arrow/applets/inspect.rb +46 -0
- data/data/arrow/applets/nosuchapplet.rb +31 -0
- data/data/arrow/applets/status.rb +92 -0
- data/data/arrow/applets/test.rb +133 -0
- data/data/arrow/applets/tutorial/counter.rb +96 -0
- data/data/arrow/applets/tutorial/dingus.rb +67 -0
- data/data/arrow/applets/tutorial/hello.rb +34 -0
- data/data/arrow/applets/tutorial/hello2.rb +73 -0
- data/data/arrow/applets/tutorial/imgtext.rb +90 -0
- data/data/arrow/applets/tutorial/imgtext2.rb +286 -0
- data/data/arrow/applets/tutorial/index.rb +36 -0
- data/data/arrow/applets/tutorial/logo.rb +98 -0
- data/data/arrow/applets/tutorial/memcache.rb +61 -0
- data/data/arrow/applets/tutorial/missing.rb +37 -0
- data/data/arrow/applets/tutorial/protected.rb +100 -0
- data/data/arrow/applets/tutorial/redirector.rb +52 -0
- data/data/arrow/applets/tutorial/rndimages.rb +159 -0
- data/data/arrow/applets/tutorial/sharenotes.rb +83 -0
- data/data/arrow/applets/tutorial/subclassed-hello.rb +32 -0
- data/data/arrow/applets/tutorial/superhello.rb +72 -0
- data/data/arrow/applets/tutorial/timeclock.rb +78 -0
- data/data/arrow/applets/view-applet.rb +123 -0
- data/data/arrow/applets/view-template.rb +85 -0
- data/data/arrow/applets/wiki.rb +274 -0
- data/data/arrow/templates/TEMPLATE.tmpl.tpl +36 -0
- data/data/arrow/templates/applet-status.tmpl +153 -0
- data/data/arrow/templates/args-display.tmpl +120 -0
- data/data/arrow/templates/config/display-table.tmpl +36 -0
- data/data/arrow/templates/config/display.tmpl +36 -0
- data/data/arrow/templates/counter-deleted.tmpl +33 -0
- data/data/arrow/templates/counter.tmpl +59 -0
- data/data/arrow/templates/dingus.tmpl +55 -0
- data/data/arrow/templates/enumtable.tmpl +8 -0
- data/data/arrow/templates/error-display.tmpl +92 -0
- data/data/arrow/templates/filemap.tmpl +89 -0
- data/data/arrow/templates/hello-world-src.tmpl +34 -0
- data/data/arrow/templates/hello-world.tmpl +60 -0
- data/data/arrow/templates/imgtext/fontlist.tmpl +46 -0
- data/data/arrow/templates/imgtext/form.tmpl +70 -0
- data/data/arrow/templates/imgtext/reload-error.tmpl +40 -0
- data/data/arrow/templates/imgtext/reload.tmpl +55 -0
- data/data/arrow/templates/inspect/display.tmpl +80 -0
- data/data/arrow/templates/loginform.tmpl +64 -0
- data/data/arrow/templates/logout.tmpl +32 -0
- data/data/arrow/templates/memcache/display.tmpl +41 -0
- data/data/arrow/templates/navbar.incl +27 -0
- data/data/arrow/templates/nosuchapplet.tmpl +32 -0
- data/data/arrow/templates/printsource.tmpl +35 -0
- data/data/arrow/templates/protected.tmpl +36 -0
- data/data/arrow/templates/rndimages.tmpl +38 -0
- data/data/arrow/templates/service-response.tmpl +13 -0
- data/data/arrow/templates/sharenotes/display.tmpl +38 -0
- data/data/arrow/templates/status.tmpl +120 -0
- data/data/arrow/templates/templateviewer.tmpl +43 -0
- data/data/arrow/templates/test/harness.tmpl +57 -0
- data/data/arrow/templates/test/list.tmpl +48 -0
- data/data/arrow/templates/test/problem.tmpl +42 -0
- data/data/arrow/templates/tutorial/index.tmpl +37 -0
- data/data/arrow/templates/tutorial/missingapplet.tmpl +29 -0
- data/data/arrow/templates/view-applet-nosuch.tmpl +32 -0
- data/data/arrow/templates/view-applet.tmpl +40 -0
- data/data/arrow/templates/view-template.tmpl +83 -0
- data/data/arrow/templates/wiki/formerror.tmpl +47 -0
- data/data/arrow/templates/wiki/markup_help.incl +6 -0
- data/data/arrow/templates/wiki/new.tmpl +56 -0
- data/data/arrow/templates/wiki/new_system.tmpl +122 -0
- data/data/arrow/templates/wiki/sectionlist.tmpl +43 -0
- data/data/arrow/templates/wiki/show.tmpl +34 -0
- data/docs/manual/layouts/default.page +43 -0
- data/docs/manual/lib/api-filter.rb +81 -0
- data/docs/manual/lib/editorial-filter.rb +64 -0
- data/docs/manual/lib/examples-filter.rb +244 -0
- data/docs/manual/lib/links-filter.rb +117 -0
- data/lib/apache/fakerequest.rb +448 -0
- data/lib/apache/logger.rb +33 -0
- data/lib/arrow.rb +51 -0
- data/lib/arrow/acceptparam.rb +207 -0
- data/lib/arrow/applet.rb +725 -0
- data/lib/arrow/appletmixins.rb +218 -0
- data/lib/arrow/appletregistry.rb +590 -0
- data/lib/arrow/applettestcase.rb +503 -0
- data/lib/arrow/broker.rb +255 -0
- data/lib/arrow/cache.rb +176 -0
- data/lib/arrow/config-loaders/yaml.rb +75 -0
- data/lib/arrow/config.rb +615 -0
- data/lib/arrow/constants.rb +24 -0
- data/lib/arrow/cookie.rb +359 -0
- data/lib/arrow/cookieset.rb +108 -0
- data/lib/arrow/dispatcher.rb +368 -0
- data/lib/arrow/dispatcherloader.rb +50 -0
- data/lib/arrow/exceptions.rb +61 -0
- data/lib/arrow/fallbackhandler.rb +48 -0
- data/lib/arrow/formvalidator.rb +631 -0
- data/lib/arrow/htmltokenizer.rb +343 -0
- data/lib/arrow/logger.rb +488 -0
- data/lib/arrow/logger/apacheoutputter.rb +69 -0
- data/lib/arrow/logger/arrayoutputter.rb +63 -0
- data/lib/arrow/logger/coloroutputter.rb +111 -0
- data/lib/arrow/logger/fileoutputter.rb +96 -0
- data/lib/arrow/logger/htmloutputter.rb +54 -0
- data/lib/arrow/logger/outputter.rb +123 -0
- data/lib/arrow/mixins.rb +425 -0
- data/lib/arrow/monkeypatches.rb +94 -0
- data/lib/arrow/object.rb +117 -0
- data/lib/arrow/path.rb +196 -0
- data/lib/arrow/service.rb +447 -0
- data/lib/arrow/session.rb +289 -0
- data/lib/arrow/session/dbstore.rb +100 -0
- data/lib/arrow/session/filelock.rb +160 -0
- data/lib/arrow/session/filestore.rb +132 -0
- data/lib/arrow/session/id.rb +98 -0
- data/lib/arrow/session/lock.rb +253 -0
- data/lib/arrow/session/md5id.rb +42 -0
- data/lib/arrow/session/nulllock.rb +42 -0
- data/lib/arrow/session/posixlock.rb +166 -0
- data/lib/arrow/session/sha1id.rb +54 -0
- data/lib/arrow/session/store.rb +366 -0
- data/lib/arrow/session/usertrackid.rb +52 -0
- data/lib/arrow/spechelpers.rb +73 -0
- data/lib/arrow/template.rb +713 -0
- data/lib/arrow/template/attr.rb +31 -0
- data/lib/arrow/template/call.rb +31 -0
- data/lib/arrow/template/comment.rb +33 -0
- data/lib/arrow/template/container.rb +118 -0
- data/lib/arrow/template/else.rb +41 -0
- data/lib/arrow/template/elsif.rb +44 -0
- data/lib/arrow/template/escape.rb +53 -0
- data/lib/arrow/template/export.rb +87 -0
- data/lib/arrow/template/for.rb +145 -0
- data/lib/arrow/template/if.rb +78 -0
- data/lib/arrow/template/import.rb +119 -0
- data/lib/arrow/template/include.rb +206 -0
- data/lib/arrow/template/iterator.rb +208 -0
- data/lib/arrow/template/nodes.rb +734 -0
- data/lib/arrow/template/parser.rb +571 -0
- data/lib/arrow/template/prettyprint.rb +53 -0
- data/lib/arrow/template/render.rb +191 -0
- data/lib/arrow/template/selectlist.rb +94 -0
- data/lib/arrow/template/set.rb +87 -0
- data/lib/arrow/template/timedelta.rb +81 -0
- data/lib/arrow/template/unless.rb +78 -0
- data/lib/arrow/template/urlencode.rb +51 -0
- data/lib/arrow/template/yield.rb +139 -0
- data/lib/arrow/templatefactory.rb +125 -0
- data/lib/arrow/testcase.rb +567 -0
- data/lib/arrow/transaction.rb +608 -0
- data/rake/191_compat.rb +26 -0
- data/rake/dependencies.rb +76 -0
- data/rake/documentation.rb +114 -0
- data/rake/helpers.rb +502 -0
- data/rake/hg.rb +282 -0
- data/rake/manual.rb +787 -0
- data/rake/packaging.rb +129 -0
- data/rake/publishing.rb +278 -0
- data/rake/style.rb +62 -0
- data/rake/svn.rb +668 -0
- data/rake/testing.rb +187 -0
- data/rake/verifytask.rb +64 -0
- data/spec/arrow/acceptparam_spec.rb +157 -0
- data/spec/arrow/applet_spec.rb +575 -0
- data/spec/arrow/appletmixins_spec.rb +409 -0
- data/spec/arrow/appletregistry_spec.rb +294 -0
- data/spec/arrow/broker_spec.rb +153 -0
- data/spec/arrow/config_spec.rb +224 -0
- data/spec/arrow/cookieset_spec.rb +164 -0
- data/spec/arrow/dispatcher_spec.rb +137 -0
- data/spec/arrow/dispatcherloader_spec.rb +65 -0
- data/spec/arrow/formvalidator_spec.rb +781 -0
- data/spec/arrow/logger_spec.rb +346 -0
- data/spec/arrow/mixins_spec.rb +120 -0
- data/spec/arrow/service_spec.rb +645 -0
- data/spec/arrow/session_spec.rb +121 -0
- data/spec/arrow/template/iterator_spec.rb +222 -0
- data/spec/arrow/templatefactory_spec.rb +185 -0
- data/spec/arrow/transaction_spec.rb +319 -0
- data/spec/arrow_spec.rb +37 -0
- data/spec/lib/appletmatchers.rb +281 -0
- data/spec/lib/constants.rb +77 -0
- data/spec/lib/helpers.rb +41 -0
- data/spec/lib/matchers.rb +44 -0
- data/tests/cookie.tests.rb +310 -0
- data/tests/path.tests.rb +157 -0
- data/tests/session.tests.rb +111 -0
- data/tests/session_id.tests.rb +82 -0
- data/tests/session_lock.tests.rb +191 -0
- data/tests/session_store.tests.rb +53 -0
- data/tests/template.tests.rb +1360 -0
- metadata +339 -0
data/lib/arrow/config.rb
ADDED
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'pluginfactory'
|
|
6
|
+
require 'forwardable'
|
|
7
|
+
require 'uri'
|
|
8
|
+
|
|
9
|
+
require 'arrow'
|
|
10
|
+
require 'arrow/constants'
|
|
11
|
+
require 'arrow/mixins'
|
|
12
|
+
require 'arrow/exceptions'
|
|
13
|
+
require 'arrow/object'
|
|
14
|
+
|
|
15
|
+
#
|
|
16
|
+
# The Arrow::Config class, instances of which use used to
|
|
17
|
+
# load and save configuration values for an Arrow application.
|
|
18
|
+
#
|
|
19
|
+
# == Description
|
|
20
|
+
# The configuration values are as follows:
|
|
21
|
+
#
|
|
22
|
+
# [<b>logging</b>]
|
|
23
|
+
# Arrow::Logger configuration. See arrow/logger.rb for specifics about this
|
|
24
|
+
# section.
|
|
25
|
+
# [<b>applets</b>]
|
|
26
|
+
# Applet configuration values:
|
|
27
|
+
# [<b>path</b>]
|
|
28
|
+
# An Arrow::Path object or colon-delimited list of directories to search for
|
|
29
|
+
# applet files. Defaults to: "./applets:/www/applets".
|
|
30
|
+
# [<b>pattern</b>]
|
|
31
|
+
# A glob pattern that will be used to search for applets to
|
|
32
|
+
# load. Default to '*.rb'.
|
|
33
|
+
# [<b>pollInterval</b>]
|
|
34
|
+
# The number of seconds between checks of the applet path for
|
|
35
|
+
# new/updated/deleted applet files. Defaults to 5.
|
|
36
|
+
# [<b>noSuchAppletHandler</b>]
|
|
37
|
+
# The URI of the applet which should handle requests for applets that don't
|
|
38
|
+
# exist. A value of '(builtin)' (the default) will cause a builtin handler to
|
|
39
|
+
# be invoked.
|
|
40
|
+
# [<b>errorHandler</b>]
|
|
41
|
+
# The URI of the applet which should handle untrapped exceptions raised
|
|
42
|
+
# from other applets. A value of '(builtin)' (the default) will cause a
|
|
43
|
+
# builtin handler to be invoked.
|
|
44
|
+
# [<b>templates</b>]
|
|
45
|
+
# Template configuration values:
|
|
46
|
+
# [<b>loader</b>]
|
|
47
|
+
# The name of a class or module to use to load templates for use in the
|
|
48
|
+
# appserver. Defaults to 'Arrow::Template'.
|
|
49
|
+
# [<b>path</b>]
|
|
50
|
+
# An Arrow::Path object or colon-delimited list of directories to search for
|
|
51
|
+
# templates. Defaults to "templates:/www/templates".
|
|
52
|
+
# [<b>cache</b>]
|
|
53
|
+
# Flag that determines whether or not templates are cached in an LRU cache
|
|
54
|
+
# in the TemplateFactory or loaded anew everytime. Default to +true+
|
|
55
|
+
# (templates are cached).
|
|
56
|
+
# [<b>cacheConfig</b>]
|
|
57
|
+
# Configuration for the template cache. If template caching is turned off,
|
|
58
|
+
# this section is ignored.
|
|
59
|
+
# <b>maxNum</b>:: The maximum number of templates to cache. Default to 20.
|
|
60
|
+
# <b>maxSize</b>:: The maximum estimated size of all cached objects. When
|
|
61
|
+
# the cache exceeds this size in bytes, the
|
|
62
|
+
# least-recently used one/s will be dropped until the
|
|
63
|
+
# cache's total size is less than this value.
|
|
64
|
+
# <b>maxObjSize</b>:: The maximum size of the cache, in bytes. If an
|
|
65
|
+
# object exceeeds this number of bytes in estimated
|
|
66
|
+
# size, it will not be cached.
|
|
67
|
+
# <b>expiration</b>:: The maximum lifetime of an object in the cache, in
|
|
68
|
+
# seconds. Objects which were cached more than this
|
|
69
|
+
# number of seconds before retrieval will be dropped.
|
|
70
|
+
# [<b>session</b>]
|
|
71
|
+
# Session configuration values:
|
|
72
|
+
# [<b>idType</b>]
|
|
73
|
+
# A URI which represents the id class to use and its configuration. See the
|
|
74
|
+
# documentation for Arrow::Session::Id and its derivatives for the form of
|
|
75
|
+
# the URI. 'md5:.' is the default.
|
|
76
|
+
# [<b>lockType</b>]
|
|
77
|
+
# A URI which specifies what locking class to use and its configuration. If
|
|
78
|
+
# this is the string +'recommended'+, the lock object will be created by
|
|
79
|
+
# calling the #create_recommended_lock method of the store. Defaults to
|
|
80
|
+
# 'recommended'. See Arrow::Session::Lock and its derivatives for the format
|
|
81
|
+
# of the URI.
|
|
82
|
+
# [<b>storeType</b>]
|
|
83
|
+
# A URI which specifies what backing store class to use for storing the
|
|
84
|
+
# session data between requests and its configuration. Default to
|
|
85
|
+
# 'file:/tmp'; see the documentation for Arrow::Session::Store and its
|
|
86
|
+
# derivatives for the form of the URI.
|
|
87
|
+
# [<b>idName</b>]
|
|
88
|
+
# The name of the session cookie and/or the session id parameter that will
|
|
89
|
+
# be inserted in rewritten URLs. Defaults to 'arrow-session'.
|
|
90
|
+
# [<b>rewriteUrls</b>]
|
|
91
|
+
# If set to +true+, any self-referential URLs in the appserver's output will
|
|
92
|
+
# be rewritten to include the session id as a parameter. Defaults to +true+.
|
|
93
|
+
# [<b>expires</b>]
|
|
94
|
+
# Set the expiration time of the session cookie. Defaults to "+48h"; see
|
|
95
|
+
# documentation for Apache::Cookie#expires for the format of the string.
|
|
96
|
+
#
|
|
97
|
+
#
|
|
98
|
+
# == Authors
|
|
99
|
+
#
|
|
100
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
|
101
|
+
#
|
|
102
|
+
# Please see the file LICENSE in the top-level directory for licensing details.
|
|
103
|
+
#
|
|
104
|
+
class Arrow::Config < Arrow::Object
|
|
105
|
+
include Arrow::HashUtilities
|
|
106
|
+
extend Forwardable
|
|
107
|
+
|
|
108
|
+
require 'arrow/path'
|
|
109
|
+
|
|
110
|
+
# Define the layout and defaults for the underlying structs
|
|
111
|
+
DEFAULTS = {
|
|
112
|
+
:logging => { :global => 'notice' },
|
|
113
|
+
|
|
114
|
+
:gems => {
|
|
115
|
+
:require_signed => false,
|
|
116
|
+
:autoinstall => false,
|
|
117
|
+
:path => Arrow::Path.new([ "gems", *Gem.path ]),
|
|
118
|
+
:applets => {},
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
:applets => {
|
|
122
|
+
:path => Arrow::Path.new( "applets:/www/applets" ),
|
|
123
|
+
:pattern => '**/*.rb',
|
|
124
|
+
:pollInterval => 5,
|
|
125
|
+
:layout => {},
|
|
126
|
+
:config => {},
|
|
127
|
+
:missingApplet => '/missing',
|
|
128
|
+
:errorApplet => '/error',
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
:templates => {
|
|
132
|
+
:loader => 'Arrow::Template',
|
|
133
|
+
:path => Arrow::Path.new( "templates:/www/templates" ),
|
|
134
|
+
:cache => true,
|
|
135
|
+
:cacheConfig => {
|
|
136
|
+
:maxNum => 20,
|
|
137
|
+
:maxSize => (1<<17) * 20,
|
|
138
|
+
:maxObjSize => (1<<17),
|
|
139
|
+
:expiration => 36
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
:session => {
|
|
144
|
+
:idType => 'md5:.',
|
|
145
|
+
:lockType => 'recommended',
|
|
146
|
+
:storeType => 'file:/tmp',
|
|
147
|
+
:idName => 'arrow-session',
|
|
148
|
+
:rewriteUrls => true,
|
|
149
|
+
:expires => "+48h",
|
|
150
|
+
},
|
|
151
|
+
}
|
|
152
|
+
DEFAULTS.freeze
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
#############################################################
|
|
157
|
+
### C L A S S M E T H O D S
|
|
158
|
+
#############################################################
|
|
159
|
+
|
|
160
|
+
### The default config file loader to use
|
|
161
|
+
@default_loader = 'yaml'
|
|
162
|
+
@loaders = {}
|
|
163
|
+
class << self
|
|
164
|
+
attr_accessor :default_loader, :loaders
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
### Get the loader by the given name, creating a new one if one is not
|
|
169
|
+
### already instantiated.
|
|
170
|
+
def self::get_loader( name=nil )
|
|
171
|
+
name ||= self.default_loader
|
|
172
|
+
self.loaders[name] ||= Arrow::Config::Loader.create( name )
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
### Read and return an Arrow::Config object from the given file or
|
|
177
|
+
### configuration source using the specified +loader+.
|
|
178
|
+
def self::load( source, loader_obj=nil )
|
|
179
|
+
loader_obj = self.get_loader( loader_obj ) unless
|
|
180
|
+
loader_obj.is_a?( Arrow::Config::Loader )
|
|
181
|
+
my_source = source.dup
|
|
182
|
+
my_source.untaint
|
|
183
|
+
confighash = loader_obj.load( my_source )
|
|
184
|
+
|
|
185
|
+
obj = new( confighash )
|
|
186
|
+
obj.loader = loader_obj
|
|
187
|
+
obj.name = my_source
|
|
188
|
+
|
|
189
|
+
return obj
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
#############################################################
|
|
194
|
+
### I N S T A N C E M E T H O D S
|
|
195
|
+
#############################################################
|
|
196
|
+
|
|
197
|
+
### Create a new Arrow::Config object. Values passed in via the
|
|
198
|
+
### +confighash+ will be used instead of the defaults.
|
|
199
|
+
def initialize( confighash={} )
|
|
200
|
+
ihash = internify_keys( untaint_values(confighash) )
|
|
201
|
+
mergedhash = DEFAULTS.merge( ihash, &HashMergeFunction )
|
|
202
|
+
|
|
203
|
+
@struct = ConfigStruct.new( mergedhash )
|
|
204
|
+
@create_time = Time.now
|
|
205
|
+
@name = nil
|
|
206
|
+
@loader = nil
|
|
207
|
+
|
|
208
|
+
super()
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
######
|
|
213
|
+
public
|
|
214
|
+
######
|
|
215
|
+
|
|
216
|
+
# Define delegators to the inner data structure
|
|
217
|
+
def_delegators :@struct, :to_hash, :to_h, :member?, :members, :merge,
|
|
218
|
+
:merge!, :each, :[], :[]=
|
|
219
|
+
|
|
220
|
+
# The underlying config data structure
|
|
221
|
+
attr_reader :struct
|
|
222
|
+
|
|
223
|
+
# The time the configuration was loaded
|
|
224
|
+
attr_accessor :create_time
|
|
225
|
+
|
|
226
|
+
# The name of the associated record stored on permanent storage for this
|
|
227
|
+
# configuration.
|
|
228
|
+
attr_accessor :name
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
### Change the configuration object's loader. The +new_loader+ argument
|
|
232
|
+
### can be either an Arrow::Config::Loader object or the name of one
|
|
233
|
+
### suitable for passing to Arrow::Config::Loader.create.
|
|
234
|
+
def loader=( new_loader )
|
|
235
|
+
if new_loader.is_a?( Arrow::Config::Loader )
|
|
236
|
+
@loader = new_loader
|
|
237
|
+
else
|
|
238
|
+
@loader = self.class.get_loader( new_loader )
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
### Fetch the loader from this config object, or create an instance
|
|
244
|
+
### of the default one if none is yet associated with it.
|
|
245
|
+
def loader
|
|
246
|
+
@loader ||= self.class.get_loader
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
### Write the configuration object using the specified name and any
|
|
251
|
+
### additional +args+.
|
|
252
|
+
def write( name=@name, *args )
|
|
253
|
+
raise ArgumentError,
|
|
254
|
+
"No name associated with this config." unless name
|
|
255
|
+
lobj = self.loader
|
|
256
|
+
strHash = stringify_keys( @struct.to_h )
|
|
257
|
+
self.loader.save( strHash, name, *args )
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
### Returns +true+ for methods which can be autoloaded
|
|
262
|
+
def respond_to?( sym )
|
|
263
|
+
return true if @struct.member?( sym.to_s.sub(/(=|\?)$/, '') )
|
|
264
|
+
super
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
### Returns +true+ if the configuration has changed since it was last
|
|
269
|
+
### loaded, either by setting one of its members or changing the file
|
|
270
|
+
### from which it was loaded.
|
|
271
|
+
def changed?
|
|
272
|
+
return self.changed_reason ? true : false
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
### If the configuration has changed, return the reason. If it hasn't,
|
|
277
|
+
### returns nil.
|
|
278
|
+
def changed_reason
|
|
279
|
+
return "Struct was modified" if @struct.modified?
|
|
280
|
+
|
|
281
|
+
if self.name && self.loader.is_newer?( self.name, self.create_time )
|
|
282
|
+
return "Config source (%s) has been updated since %s" %
|
|
283
|
+
[ self.name, self.create_time ]
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
return nil
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
### Reload the configuration from the original source if it has
|
|
291
|
+
### changed. Returns +true+ if it was reloaded and +false+ otherwise.
|
|
292
|
+
def reload
|
|
293
|
+
return false unless @loader && @name
|
|
294
|
+
|
|
295
|
+
# Even if reloading fails, reset the creation time so we don't keep
|
|
296
|
+
# trying to reload a broken config
|
|
297
|
+
self.create_time = Time.now
|
|
298
|
+
|
|
299
|
+
confighash = @loader.load( @name )
|
|
300
|
+
ihash = internify_keys( untaint_values(confighash) )
|
|
301
|
+
mergedhash = DEFAULTS.merge( ihash, &HashMergeFunction )
|
|
302
|
+
|
|
303
|
+
@struct = ConfigStruct.new( mergedhash )
|
|
304
|
+
|
|
305
|
+
rescue => err
|
|
306
|
+
self.log.error "Error while trying to reload the config: %s" % err.message
|
|
307
|
+
err.backtrace.each {|frame| self.log.debug " " + frame }
|
|
308
|
+
|
|
309
|
+
return false
|
|
310
|
+
else
|
|
311
|
+
return true
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
#########
|
|
316
|
+
protected
|
|
317
|
+
#########
|
|
318
|
+
|
|
319
|
+
### Hook up delegators to struct-members as they are called
|
|
320
|
+
def method_missing( sym, *args )
|
|
321
|
+
key = sym.to_s.sub( /(=|\?)$/, '' ).to_sym
|
|
322
|
+
return nil unless @struct.member?( key )
|
|
323
|
+
|
|
324
|
+
self.log.debug( "Autoloading #{key} accessors." )
|
|
325
|
+
|
|
326
|
+
self.class.class_eval %{
|
|
327
|
+
def #{key}; @struct.#{key}; end
|
|
328
|
+
def #{key}=(*args); @struct.#{key} = *args; end
|
|
329
|
+
def #{key}?; @struct.#{key}?; end
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
@struct.__send__( sym, *args )
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
#######
|
|
337
|
+
private
|
|
338
|
+
#######
|
|
339
|
+
|
|
340
|
+
### Return a copy of the specified +hash+ with all of its values
|
|
341
|
+
### untainted.
|
|
342
|
+
def untaint_values( hash )
|
|
343
|
+
newhash = {}
|
|
344
|
+
|
|
345
|
+
hash.each do |key,val|
|
|
346
|
+
case val
|
|
347
|
+
when Hash
|
|
348
|
+
newhash[ key ] = untaint_values( hash[key] )
|
|
349
|
+
|
|
350
|
+
when NilClass, TrueClass, FalseClass, Numeric, Symbol
|
|
351
|
+
newhash[ key ] = val
|
|
352
|
+
|
|
353
|
+
when Arrow::Path
|
|
354
|
+
# Arrow::Logger[ self ].debug "Untainting %p" % val
|
|
355
|
+
val.untaint
|
|
356
|
+
newhash[ key ] = val
|
|
357
|
+
|
|
358
|
+
when Array
|
|
359
|
+
# Arrow::Logger[ self ].debug "Untainting array %p" % val
|
|
360
|
+
newval = val.collect do |v|
|
|
361
|
+
case v
|
|
362
|
+
when NilClass, TrueClass, FalseClass, Numeric, Symbol
|
|
363
|
+
v
|
|
364
|
+
else
|
|
365
|
+
v.dup.untaint
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
newhash[ key ] = newval
|
|
369
|
+
|
|
370
|
+
else
|
|
371
|
+
# Arrow::Logger[ self ].debug "Untainting %p" % val
|
|
372
|
+
newval = val.dup
|
|
373
|
+
newval.untaint
|
|
374
|
+
newhash[ key ] = newval
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
return newhash
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
#############################################################
|
|
383
|
+
### I N T E R I O R C L A S S E S
|
|
384
|
+
#############################################################
|
|
385
|
+
|
|
386
|
+
### Hash-wrapper that allows struct-like accessor calls on nested
|
|
387
|
+
### hashes.
|
|
388
|
+
class ConfigStruct < Arrow::Object
|
|
389
|
+
include Enumerable, Arrow::HashUtilities
|
|
390
|
+
extend Forwardable
|
|
391
|
+
|
|
392
|
+
# Mask most of Kernel's methods away so they don't collide with
|
|
393
|
+
# config values.
|
|
394
|
+
Kernel.methods(false).each {|meth|
|
|
395
|
+
next unless method_defined?( meth )
|
|
396
|
+
next if /^(?:__|dup|object_id|inspect|class|raise|method_missing)/.match( meth )
|
|
397
|
+
undef_method( meth )
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
# Forward some methods to the internal hash
|
|
401
|
+
def_delegators :@hash, :keys, :values, :value?, :[], :[]=, :length,
|
|
402
|
+
:empty?, :clear
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
### Create a new ConfigStruct from the given +hash+.
|
|
406
|
+
def initialize( hash )
|
|
407
|
+
@hash = hash.dup
|
|
408
|
+
@dirty = false
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
######
|
|
413
|
+
public
|
|
414
|
+
######
|
|
415
|
+
|
|
416
|
+
# Modification flag. Set to +true+ to indicate the contents of the
|
|
417
|
+
# Struct have changed since it was created.
|
|
418
|
+
attr_writer :modified
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
### Returns +true+ if the ConfigStruct or any of its sub-structs
|
|
422
|
+
### have changed since it was created.
|
|
423
|
+
def modified?
|
|
424
|
+
@dirty || @hash.values.find do |obj|
|
|
425
|
+
obj.is_a?( ConfigStruct ) && obj.modified?
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
### Return the receiver's values as a (possibly multi-dimensional)
|
|
431
|
+
### Hash with String keys.
|
|
432
|
+
def to_hash
|
|
433
|
+
rhash = {}
|
|
434
|
+
@hash.each {|k,v|
|
|
435
|
+
case v
|
|
436
|
+
when ConfigStruct
|
|
437
|
+
rhash[k] = v.to_h
|
|
438
|
+
when NilClass, FalseClass, TrueClass, Numeric
|
|
439
|
+
# No-op (can't dup)
|
|
440
|
+
rhash[k] = v
|
|
441
|
+
when Symbol
|
|
442
|
+
rhash[k] = v.to_s
|
|
443
|
+
else
|
|
444
|
+
rhash[k] = v.dup
|
|
445
|
+
end
|
|
446
|
+
}
|
|
447
|
+
return rhash
|
|
448
|
+
end
|
|
449
|
+
alias_method :to_h, :to_hash
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
### Return +true+ if the receiver responds to the given
|
|
453
|
+
### method. Overridden to grok autoloaded methods.
|
|
454
|
+
def respond_to?( sym, priv=false )
|
|
455
|
+
key = sym.to_s.sub( /(=|\?)$/, '' ).to_sym
|
|
456
|
+
return true if self.member?( key )
|
|
457
|
+
super
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
### Returns an Array of Symbols, on for each of the struct's members.
|
|
462
|
+
def members
|
|
463
|
+
@hash.keys
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
### Returns +true+ if the given +name+ is the name of a member of
|
|
468
|
+
### the receiver.
|
|
469
|
+
def member?( name )
|
|
470
|
+
return @hash.key?( name.to_sym )
|
|
471
|
+
end
|
|
472
|
+
alias_method :key?, :member?
|
|
473
|
+
alias_method :has_key?, :key?
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
### Call into the given block for each member of the receiver.
|
|
477
|
+
def each( &block ) # :yield: member, value
|
|
478
|
+
@hash.each( &block )
|
|
479
|
+
end
|
|
480
|
+
alias_method :each_section, :each
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
### Merge the specified +other+ object with this config struct. The
|
|
484
|
+
### +other+ object can be either a Hash, another ConfigStruct, or an
|
|
485
|
+
### Arrow::Config.
|
|
486
|
+
def merge!( other )
|
|
487
|
+
case other
|
|
488
|
+
when Hash
|
|
489
|
+
@hash = self.to_h.merge( other,
|
|
490
|
+
&HashMergeFunction )
|
|
491
|
+
|
|
492
|
+
when ConfigStruct
|
|
493
|
+
@hash = self.to_h.merge( other.to_h,
|
|
494
|
+
&HashMergeFunction )
|
|
495
|
+
|
|
496
|
+
when Arrow::Config
|
|
497
|
+
@hash = self.to_h.merge( other.struct.to_h,
|
|
498
|
+
&HashMergeFunction )
|
|
499
|
+
|
|
500
|
+
else
|
|
501
|
+
raise TypeError,
|
|
502
|
+
"Don't know how to merge with a %p" % other.class
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
# :TODO: Actually check to see if anything has changed?
|
|
506
|
+
@dirty = true
|
|
507
|
+
|
|
508
|
+
return self
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
### Return a new ConfigStruct which is the result of merging the
|
|
513
|
+
### receiver with the given +other+ object (a Hash or another
|
|
514
|
+
### ConfigStruct).
|
|
515
|
+
def merge( other )
|
|
516
|
+
self.dup.merge!( other )
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
#########
|
|
521
|
+
protected
|
|
522
|
+
#########
|
|
523
|
+
|
|
524
|
+
### Handle calls to key-methods
|
|
525
|
+
def method_missing( sym, *args )
|
|
526
|
+
key = sym.to_s.sub( /(=|\?)$/, '' ).to_sym
|
|
527
|
+
return nil unless @hash.key?( key )
|
|
528
|
+
|
|
529
|
+
self.class.class_eval {
|
|
530
|
+
define_method( key ) {
|
|
531
|
+
if @hash[ key ].is_a?( Hash )
|
|
532
|
+
@hash[ key ] = ConfigStruct.new( @hash[key] )
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
@hash[ key ]
|
|
536
|
+
}
|
|
537
|
+
define_method( "#{key}?" ) {@hash[key] ? true : false}
|
|
538
|
+
define_method( "#{key}=" ) {|val|
|
|
539
|
+
@dirty = @hash[key] != val
|
|
540
|
+
@hash[key] = val
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
self.__send__( sym, *args )
|
|
545
|
+
end
|
|
546
|
+
end # class ConfigStruct
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
### Abstract base class (and Factory) for configuration loader
|
|
550
|
+
### delegates. Create specific instances with the
|
|
551
|
+
### Arrow::Config::Loader.create method.
|
|
552
|
+
class Loader < Arrow::Object
|
|
553
|
+
include PluginFactory
|
|
554
|
+
|
|
555
|
+
#########################################################
|
|
556
|
+
### C L A S S M E T H O D S
|
|
557
|
+
#########################################################
|
|
558
|
+
|
|
559
|
+
### Returns a list of directories to search for deriviatives.
|
|
560
|
+
def self::derivativeDirs
|
|
561
|
+
["arrow/config-loaders"]
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
#########################################################
|
|
566
|
+
### I N S T A N C E M E T H O D S
|
|
567
|
+
#########################################################
|
|
568
|
+
|
|
569
|
+
######
|
|
570
|
+
public
|
|
571
|
+
######
|
|
572
|
+
|
|
573
|
+
### Load configuration values from the storage medium associated
|
|
574
|
+
### with the given +name+ (e.g., filename, rowid, etc.) and return
|
|
575
|
+
### them in the form of a (possibly multi-dimensional) Hash.
|
|
576
|
+
def load( name )
|
|
577
|
+
raise NotImplementedError,
|
|
578
|
+
"required method 'load' not implemented in '#{self.class.name}'"
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
### Save configuration values from the given +confighash+ to the
|
|
583
|
+
### storage medium associated with the given +name+ (e.g., filename,
|
|
584
|
+
### rowid, etc.) and return them.
|
|
585
|
+
def save( confighash, name )
|
|
586
|
+
raise NotImplementedError,
|
|
587
|
+
"required method 'save' not implemented in '#{self.class.name}'"
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
### Returns +true+ if the configuration values in the storage medium
|
|
592
|
+
### associated with the given +name+ has changed since the given
|
|
593
|
+
### +time+.
|
|
594
|
+
def is_newer?( name, time )
|
|
595
|
+
raise NotImplementedError,
|
|
596
|
+
"required method 'is_newer?' not implemented in '#{self.class.name}'"
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
end # class Loader
|
|
600
|
+
|
|
601
|
+
end # class Arrow::Config
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
### If run directly, write a default config file to the current directory
|
|
605
|
+
if __FILE__ == $0
|
|
606
|
+
filename = ARGV.shift || "default.cfg"
|
|
607
|
+
loader = ARGV.shift || Arrow::Config.default_loader
|
|
608
|
+
|
|
609
|
+
$stderr.puts "Dumping default configuration to '%s' using the '%s' loader" %
|
|
610
|
+
[ filename, loader ]
|
|
611
|
+
|
|
612
|
+
conf = Arrow::Config.new
|
|
613
|
+
conf.loader = loader
|
|
614
|
+
conf.write( filename )
|
|
615
|
+
end
|