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.
Files changed (198) hide show
  1. data/ChangeLog +1590 -0
  2. data/LICENSE +28 -0
  3. data/README +75 -0
  4. data/Rakefile +366 -0
  5. data/Rakefile.local +63 -0
  6. data/data/arrow/applets/TEMPLATE.rb.tpl +53 -0
  7. data/data/arrow/applets/args.rb +50 -0
  8. data/data/arrow/applets/config.rb +55 -0
  9. data/data/arrow/applets/error.rb +63 -0
  10. data/data/arrow/applets/files.rb +46 -0
  11. data/data/arrow/applets/inspect.rb +46 -0
  12. data/data/arrow/applets/nosuchapplet.rb +31 -0
  13. data/data/arrow/applets/status.rb +92 -0
  14. data/data/arrow/applets/test.rb +133 -0
  15. data/data/arrow/applets/tutorial/counter.rb +96 -0
  16. data/data/arrow/applets/tutorial/dingus.rb +67 -0
  17. data/data/arrow/applets/tutorial/hello.rb +34 -0
  18. data/data/arrow/applets/tutorial/hello2.rb +73 -0
  19. data/data/arrow/applets/tutorial/imgtext.rb +90 -0
  20. data/data/arrow/applets/tutorial/imgtext2.rb +286 -0
  21. data/data/arrow/applets/tutorial/index.rb +36 -0
  22. data/data/arrow/applets/tutorial/logo.rb +98 -0
  23. data/data/arrow/applets/tutorial/memcache.rb +61 -0
  24. data/data/arrow/applets/tutorial/missing.rb +37 -0
  25. data/data/arrow/applets/tutorial/protected.rb +100 -0
  26. data/data/arrow/applets/tutorial/redirector.rb +52 -0
  27. data/data/arrow/applets/tutorial/rndimages.rb +159 -0
  28. data/data/arrow/applets/tutorial/sharenotes.rb +83 -0
  29. data/data/arrow/applets/tutorial/subclassed-hello.rb +32 -0
  30. data/data/arrow/applets/tutorial/superhello.rb +72 -0
  31. data/data/arrow/applets/tutorial/timeclock.rb +78 -0
  32. data/data/arrow/applets/view-applet.rb +123 -0
  33. data/data/arrow/applets/view-template.rb +85 -0
  34. data/data/arrow/applets/wiki.rb +274 -0
  35. data/data/arrow/templates/TEMPLATE.tmpl.tpl +36 -0
  36. data/data/arrow/templates/applet-status.tmpl +153 -0
  37. data/data/arrow/templates/args-display.tmpl +120 -0
  38. data/data/arrow/templates/config/display-table.tmpl +36 -0
  39. data/data/arrow/templates/config/display.tmpl +36 -0
  40. data/data/arrow/templates/counter-deleted.tmpl +33 -0
  41. data/data/arrow/templates/counter.tmpl +59 -0
  42. data/data/arrow/templates/dingus.tmpl +55 -0
  43. data/data/arrow/templates/enumtable.tmpl +8 -0
  44. data/data/arrow/templates/error-display.tmpl +92 -0
  45. data/data/arrow/templates/filemap.tmpl +89 -0
  46. data/data/arrow/templates/hello-world-src.tmpl +34 -0
  47. data/data/arrow/templates/hello-world.tmpl +60 -0
  48. data/data/arrow/templates/imgtext/fontlist.tmpl +46 -0
  49. data/data/arrow/templates/imgtext/form.tmpl +70 -0
  50. data/data/arrow/templates/imgtext/reload-error.tmpl +40 -0
  51. data/data/arrow/templates/imgtext/reload.tmpl +55 -0
  52. data/data/arrow/templates/inspect/display.tmpl +80 -0
  53. data/data/arrow/templates/loginform.tmpl +64 -0
  54. data/data/arrow/templates/logout.tmpl +32 -0
  55. data/data/arrow/templates/memcache/display.tmpl +41 -0
  56. data/data/arrow/templates/navbar.incl +27 -0
  57. data/data/arrow/templates/nosuchapplet.tmpl +32 -0
  58. data/data/arrow/templates/printsource.tmpl +35 -0
  59. data/data/arrow/templates/protected.tmpl +36 -0
  60. data/data/arrow/templates/rndimages.tmpl +38 -0
  61. data/data/arrow/templates/service-response.tmpl +13 -0
  62. data/data/arrow/templates/sharenotes/display.tmpl +38 -0
  63. data/data/arrow/templates/status.tmpl +120 -0
  64. data/data/arrow/templates/templateviewer.tmpl +43 -0
  65. data/data/arrow/templates/test/harness.tmpl +57 -0
  66. data/data/arrow/templates/test/list.tmpl +48 -0
  67. data/data/arrow/templates/test/problem.tmpl +42 -0
  68. data/data/arrow/templates/tutorial/index.tmpl +37 -0
  69. data/data/arrow/templates/tutorial/missingapplet.tmpl +29 -0
  70. data/data/arrow/templates/view-applet-nosuch.tmpl +32 -0
  71. data/data/arrow/templates/view-applet.tmpl +40 -0
  72. data/data/arrow/templates/view-template.tmpl +83 -0
  73. data/data/arrow/templates/wiki/formerror.tmpl +47 -0
  74. data/data/arrow/templates/wiki/markup_help.incl +6 -0
  75. data/data/arrow/templates/wiki/new.tmpl +56 -0
  76. data/data/arrow/templates/wiki/new_system.tmpl +122 -0
  77. data/data/arrow/templates/wiki/sectionlist.tmpl +43 -0
  78. data/data/arrow/templates/wiki/show.tmpl +34 -0
  79. data/docs/manual/layouts/default.page +43 -0
  80. data/docs/manual/lib/api-filter.rb +81 -0
  81. data/docs/manual/lib/editorial-filter.rb +64 -0
  82. data/docs/manual/lib/examples-filter.rb +244 -0
  83. data/docs/manual/lib/links-filter.rb +117 -0
  84. data/lib/apache/fakerequest.rb +448 -0
  85. data/lib/apache/logger.rb +33 -0
  86. data/lib/arrow.rb +51 -0
  87. data/lib/arrow/acceptparam.rb +207 -0
  88. data/lib/arrow/applet.rb +725 -0
  89. data/lib/arrow/appletmixins.rb +218 -0
  90. data/lib/arrow/appletregistry.rb +590 -0
  91. data/lib/arrow/applettestcase.rb +503 -0
  92. data/lib/arrow/broker.rb +255 -0
  93. data/lib/arrow/cache.rb +176 -0
  94. data/lib/arrow/config-loaders/yaml.rb +75 -0
  95. data/lib/arrow/config.rb +615 -0
  96. data/lib/arrow/constants.rb +24 -0
  97. data/lib/arrow/cookie.rb +359 -0
  98. data/lib/arrow/cookieset.rb +108 -0
  99. data/lib/arrow/dispatcher.rb +368 -0
  100. data/lib/arrow/dispatcherloader.rb +50 -0
  101. data/lib/arrow/exceptions.rb +61 -0
  102. data/lib/arrow/fallbackhandler.rb +48 -0
  103. data/lib/arrow/formvalidator.rb +631 -0
  104. data/lib/arrow/htmltokenizer.rb +343 -0
  105. data/lib/arrow/logger.rb +488 -0
  106. data/lib/arrow/logger/apacheoutputter.rb +69 -0
  107. data/lib/arrow/logger/arrayoutputter.rb +63 -0
  108. data/lib/arrow/logger/coloroutputter.rb +111 -0
  109. data/lib/arrow/logger/fileoutputter.rb +96 -0
  110. data/lib/arrow/logger/htmloutputter.rb +54 -0
  111. data/lib/arrow/logger/outputter.rb +123 -0
  112. data/lib/arrow/mixins.rb +425 -0
  113. data/lib/arrow/monkeypatches.rb +94 -0
  114. data/lib/arrow/object.rb +117 -0
  115. data/lib/arrow/path.rb +196 -0
  116. data/lib/arrow/service.rb +447 -0
  117. data/lib/arrow/session.rb +289 -0
  118. data/lib/arrow/session/dbstore.rb +100 -0
  119. data/lib/arrow/session/filelock.rb +160 -0
  120. data/lib/arrow/session/filestore.rb +132 -0
  121. data/lib/arrow/session/id.rb +98 -0
  122. data/lib/arrow/session/lock.rb +253 -0
  123. data/lib/arrow/session/md5id.rb +42 -0
  124. data/lib/arrow/session/nulllock.rb +42 -0
  125. data/lib/arrow/session/posixlock.rb +166 -0
  126. data/lib/arrow/session/sha1id.rb +54 -0
  127. data/lib/arrow/session/store.rb +366 -0
  128. data/lib/arrow/session/usertrackid.rb +52 -0
  129. data/lib/arrow/spechelpers.rb +73 -0
  130. data/lib/arrow/template.rb +713 -0
  131. data/lib/arrow/template/attr.rb +31 -0
  132. data/lib/arrow/template/call.rb +31 -0
  133. data/lib/arrow/template/comment.rb +33 -0
  134. data/lib/arrow/template/container.rb +118 -0
  135. data/lib/arrow/template/else.rb +41 -0
  136. data/lib/arrow/template/elsif.rb +44 -0
  137. data/lib/arrow/template/escape.rb +53 -0
  138. data/lib/arrow/template/export.rb +87 -0
  139. data/lib/arrow/template/for.rb +145 -0
  140. data/lib/arrow/template/if.rb +78 -0
  141. data/lib/arrow/template/import.rb +119 -0
  142. data/lib/arrow/template/include.rb +206 -0
  143. data/lib/arrow/template/iterator.rb +208 -0
  144. data/lib/arrow/template/nodes.rb +734 -0
  145. data/lib/arrow/template/parser.rb +571 -0
  146. data/lib/arrow/template/prettyprint.rb +53 -0
  147. data/lib/arrow/template/render.rb +191 -0
  148. data/lib/arrow/template/selectlist.rb +94 -0
  149. data/lib/arrow/template/set.rb +87 -0
  150. data/lib/arrow/template/timedelta.rb +81 -0
  151. data/lib/arrow/template/unless.rb +78 -0
  152. data/lib/arrow/template/urlencode.rb +51 -0
  153. data/lib/arrow/template/yield.rb +139 -0
  154. data/lib/arrow/templatefactory.rb +125 -0
  155. data/lib/arrow/testcase.rb +567 -0
  156. data/lib/arrow/transaction.rb +608 -0
  157. data/rake/191_compat.rb +26 -0
  158. data/rake/dependencies.rb +76 -0
  159. data/rake/documentation.rb +114 -0
  160. data/rake/helpers.rb +502 -0
  161. data/rake/hg.rb +282 -0
  162. data/rake/manual.rb +787 -0
  163. data/rake/packaging.rb +129 -0
  164. data/rake/publishing.rb +278 -0
  165. data/rake/style.rb +62 -0
  166. data/rake/svn.rb +668 -0
  167. data/rake/testing.rb +187 -0
  168. data/rake/verifytask.rb +64 -0
  169. data/spec/arrow/acceptparam_spec.rb +157 -0
  170. data/spec/arrow/applet_spec.rb +575 -0
  171. data/spec/arrow/appletmixins_spec.rb +409 -0
  172. data/spec/arrow/appletregistry_spec.rb +294 -0
  173. data/spec/arrow/broker_spec.rb +153 -0
  174. data/spec/arrow/config_spec.rb +224 -0
  175. data/spec/arrow/cookieset_spec.rb +164 -0
  176. data/spec/arrow/dispatcher_spec.rb +137 -0
  177. data/spec/arrow/dispatcherloader_spec.rb +65 -0
  178. data/spec/arrow/formvalidator_spec.rb +781 -0
  179. data/spec/arrow/logger_spec.rb +346 -0
  180. data/spec/arrow/mixins_spec.rb +120 -0
  181. data/spec/arrow/service_spec.rb +645 -0
  182. data/spec/arrow/session_spec.rb +121 -0
  183. data/spec/arrow/template/iterator_spec.rb +222 -0
  184. data/spec/arrow/templatefactory_spec.rb +185 -0
  185. data/spec/arrow/transaction_spec.rb +319 -0
  186. data/spec/arrow_spec.rb +37 -0
  187. data/spec/lib/appletmatchers.rb +281 -0
  188. data/spec/lib/constants.rb +77 -0
  189. data/spec/lib/helpers.rb +41 -0
  190. data/spec/lib/matchers.rb +44 -0
  191. data/tests/cookie.tests.rb +310 -0
  192. data/tests/path.tests.rb +157 -0
  193. data/tests/session.tests.rb +111 -0
  194. data/tests/session_id.tests.rb +82 -0
  195. data/tests/session_lock.tests.rb +191 -0
  196. data/tests/session_store.tests.rb +53 -0
  197. data/tests/template.tests.rb +1360 -0
  198. metadata +339 -0
@@ -0,0 +1,255 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'arrow/mixins'
4
+ require 'arrow/exceptions'
5
+ require 'arrow/object'
6
+ require 'arrow/cache'
7
+ require 'arrow/templatefactory'
8
+ require 'arrow/appletregistry'
9
+
10
+
11
+ # The broker is the applet manager. It maintains a registry of applets, and delegates
12
+ # transactions based on the request's URI.
13
+ #
14
+ # == Authors
15
+ #
16
+ # * Michael Granger <mgranger@RubyCrafters.com>
17
+ #
18
+ # Please see the file LICENSE in the top-level directory for licensing details.
19
+ #
20
+ class Arrow::Broker < Arrow::Object
21
+
22
+ ### Class constants/methods
23
+
24
+ # A regular expression that matches the file separator on this system
25
+ FILE_SEPARATOR = Regexp.new( Regexp.compile(File::SEPARATOR) )
26
+
27
+
28
+ ### Create a new Arrow::Broker object from the specified +config+ (an
29
+ ### Arrow::Config object).
30
+ def initialize( config )
31
+ @config = config
32
+ @registry = Arrow::AppletRegistry.new( config )
33
+ @start_time = Time.now
34
+ end
35
+
36
+
37
+ ######
38
+ public
39
+ ######
40
+
41
+ # The Hash of RegistryEntry structs keyed by uri
42
+ attr_accessor :registry
43
+
44
+ # The Time when the Broker was started
45
+ attr_reader :start_time
46
+
47
+
48
+
49
+ ### Dispatch the specified transaction +txn+ to the appropriate handler
50
+ ### based on the request's path_info.
51
+ def delegate( txn )
52
+ rval = appletchain = nil
53
+ self.log.debug "Start of delegation (%s)" % [ txn.unparsed_uri ]
54
+
55
+ # Fetch the path and trim the leading '/'
56
+ path = txn.path
57
+ path.sub!( %r{^/}, '' )
58
+ self.log.debug "Request's path is %p" % path
59
+
60
+ # Check for updated/deleted/added applets
61
+ @registry.check_for_updates
62
+
63
+ # Get the chain of applets to execute for the request
64
+ appletchain = @registry.find_applet_chain( path )
65
+
66
+ # If the pathinfo doesn't correspond to at least one applet, run
67
+ # the no-such-applet handler.
68
+ if appletchain.empty?
69
+ rval = self.run_missing_applet_handler( txn, path )
70
+ else
71
+ rval = self.run_applet_chain( txn, appletchain )
72
+ end
73
+
74
+ # Set the request status to declined if it hasn't been set yet and
75
+ # the return value is false.
76
+ if !rval
77
+ self.log.error "Applet returned false value. " +
78
+ "Setting status to DECLINED"
79
+ txn.status = Apache::DECLINED
80
+ end
81
+
82
+ # self.log.debug "Returning %p" % [ rval ]
83
+ return rval
84
+ end
85
+
86
+
87
+ ### Run the specified +applet+ with the given +txn+ (an Arrow::Transaction)
88
+ ### and the +rest+ of the path_info split on '/'.
89
+ def run_applet( applet, txn, rest )
90
+ self.log.debug "Running '%s' with args: %p" %
91
+ [ applet.signature.name, rest ]
92
+ return applet.run( txn, *rest )
93
+ rescue ::Exception => err
94
+ self.log.error "[%s]: Error running %s (%s): %s:\n\t%s" % [
95
+ txn.serial,
96
+ applet.signature.name,
97
+ applet.class.filename,
98
+ err.message,
99
+ err.backtrace.join("\n\t"),
100
+ ]
101
+ return self.run_error_handler( applet, txn, err )
102
+ end
103
+
104
+
105
+ ### Handle requests that target an applet that doesn't exist.
106
+ def run_missing_applet_handler( txn, uri )
107
+ rval = appletchain = nil
108
+ handlerUri = @config.applets.missingApplet
109
+ args = uri.split( %r{/} )
110
+
111
+ # Build an applet chain for user-configured handlers
112
+ if handlerUri != "(builtin)"
113
+ appletchain = @registry.find_applet_chain( handlerUri )
114
+ self.log.error "Configured MissingApplet handler (%s) doesn't exist" %
115
+ handlerUri if appletchain.empty?
116
+ end
117
+
118
+ # If the user-configured handler maps to one or more handlers, run
119
+ # them. Otherwise, run the build-in handler.
120
+ unless appletchain.nil? || appletchain.empty?
121
+ rval = self.run_applet_chain( txn, appletchain )
122
+ else
123
+ rval = self.builtin_missing_handler( txn, *args )
124
+ end
125
+
126
+ return rval
127
+ end
128
+
129
+
130
+ ### Handle the given applet error +err+ for the specified +applet+, using
131
+ ### the given transaction +txn+. This will attempt to run whatever applet
132
+ ### is configured as the error-handler, or run a builtin handler applet
133
+ ### if none is configured or the configured one isn't loaded.
134
+ def run_error_handler( applet, txn, err )
135
+ rval = nil
136
+ handlerName = @config.applets.errorApplet.sub( %r{^/}, '' )
137
+
138
+ unless handlerName == "(builtin)" or !@registry.key?( handlerName )
139
+ handler = @registry[handlerName]
140
+ self.log.notice "Running error handler applet '%s' (%s)" %
141
+ [ handler.signature.name, handlerName ]
142
+
143
+ begin
144
+ rval = handler.run( txn, "report_error", applet, err )
145
+ rescue ::Exception => err2
146
+ self.log.error "Error while attempting to use custom error "\
147
+ "handler '%s': %s\n\t%s" % [
148
+ handler.signature.name,
149
+ err2.message,
150
+ err2.backtrace.join("\n\t"),
151
+ ]
152
+
153
+ rval = self.builtin_error_handler( applet, txn, err )
154
+ end
155
+ else
156
+ rval = self.builtin_error_handler( applet, txn, err )
157
+ end
158
+
159
+ return rval
160
+ end
161
+
162
+
163
+ #########
164
+ protected
165
+ #########
166
+
167
+ ### Given a chain of applets built from a URI, run the +index+th one with
168
+ ### the specified transaction (+txn+). Applets before the last get called
169
+ ### via their #delegate method, while the last one is called via #run.
170
+ def run_applet_chain( txn, chain )
171
+ self.log.debug "Running applet chain: #{chain.inspect}"
172
+ raise Arrow::AppletError, "Malformed applet chain" if
173
+ chain.empty? || !chain.first.respond_to?( :applet )
174
+
175
+ res = nil
176
+ applet, txn.applet_path, args = self.unwrap_chain_link( chain.first )
177
+
178
+ # If there's only one item left, run it
179
+ if chain.nitems == 1
180
+ self.log.debug "Running final applet in chain"
181
+ res = self.run_applet( applet, txn, args )
182
+
183
+ # Otherwise, delegate the transaction to the next applet with the
184
+ # remainder of the chain.
185
+ else
186
+ dchain = chain[ 1..-1 ]
187
+ self.log.debug "Running applet %s in chain of %d; chain = %p" %
188
+ [ applet.signature.name, chain.nitems, dchain ]
189
+
190
+ begin
191
+ res = applet.delegate( txn, dchain, *args ) do |subchain|
192
+ subchain = dchain if subchain.nil?
193
+ self.log.debug "Delegated call to appletchain %p" % [ subchain ]
194
+ self.run_applet_chain( txn, subchain )
195
+ end
196
+ rescue ::Exception => err
197
+ self.log.error "Error while executing applet chain: %p (/%s): %s:\n\t%s" % [
198
+ applet,
199
+ chain.first[1],
200
+ err.message,
201
+ err.backtrace.join("\n\t"),
202
+ ]
203
+ res = self.run_error_handler( applet, txn, err )
204
+ end
205
+ end
206
+
207
+ return res
208
+ end
209
+
210
+
211
+ ### Check the specified +link+ of an applet chain for sanity and return
212
+ ### its constituent bits for assignment. This is necessary to provide
213
+ ### sensible errors if a delegating app screws up a chain somehow.
214
+ def unwrap_chain_link( link )
215
+ applet = link.applet or raise Arrow::AppletChainError, "Null applet"
216
+ path = link.path or raise Arrow::AppletChainError, "Null path"
217
+ args = link.args or raise Arrow::AppletChainError, "Null argument list"
218
+ unless args.is_a?( Array )
219
+ emsg = "Argument list is a %s: expected an Array" %
220
+ args.class.name
221
+ raise Arrow::AppletChainError, emsg
222
+ end
223
+
224
+ return applet, path, args
225
+ end
226
+
227
+
228
+ ### The builtin missing-applet handler routine. Returns +false+, which
229
+ ### causes the dispatcher to decline the request.
230
+ def builtin_missing_handler( txn, *args )
231
+ self.log.notice "Using builtin missing-applet handler."
232
+ return false
233
+ end
234
+
235
+
236
+ ### The builtin error handler routine. Outputs a plain-text backtrace
237
+ ### for the given exception +err+ and +applet+ to the given
238
+ ### transaction +txn+.
239
+ def builtin_error_handler( applet, txn, err )
240
+ self.log.notice "Using builtin error handler."
241
+ txn.request.content_type = "text/plain"
242
+ txn.status = Apache::OK
243
+
244
+ return "Arrow Applet Error in '%s': %s\n\t%s" %
245
+ [ applet.class.signature.name, err.message, err.backtrace.join("\n\t") ]
246
+ end
247
+
248
+
249
+
250
+ end # class Arrow::Broker
251
+
252
+
253
+ __END__
254
+
255
+
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'cache'
4
+ require 'forwardable'
5
+
6
+ require 'arrow/logger'
7
+ require 'arrow/mixins'
8
+
9
+
10
+ #
11
+ # A derivative of the Cache class from the ruby-cache module. It adds a few
12
+ # convenience and introspection methods to its parent.
13
+ #
14
+ # Instances of this class are LRU caches for disk-based objects which keep
15
+ # track of the cached object's modification time, expiring the cached
16
+ # version when the disk-based version changes (e.g., for template caching).
17
+ #
18
+ # == Authors
19
+ #
20
+ # * Michael Granger <ged@FaerieMUD.org>
21
+ #
22
+ # Please see the file LICENSE in the top-level directory for licensing details.
23
+ #
24
+ class Arrow::Cache < ::Cache
25
+ extend Forwardable
26
+ include Arrow::Loggable
27
+
28
+ # Default configuration values
29
+ DefaultConfig = {
30
+ :maxNum => 10,
31
+ :maxObjSize => nil,
32
+ :maxSize => nil,
33
+ :expiration => 3600,
34
+ }
35
+
36
+
37
+ #############################################################
38
+ ### C L A S S M E T H O D S
39
+ #############################################################
40
+
41
+ @extent = []
42
+ class << self
43
+ attr_reader :extent
44
+ end
45
+
46
+
47
+ #############################################################
48
+ ### I N S T A N C E M E T H O D S
49
+ #############################################################
50
+
51
+ ### Create a new cache. This merges the DefaultConfig with the specified
52
+ ### values and transforms camelCased keys into under_barred ones.
53
+ def initialize( name, config={}, &cleanup )
54
+ @name = name
55
+
56
+ # Merge defaults and specified values
57
+ merged = DefaultConfig.merge( config )
58
+
59
+ # Transform the config hash into the form the superclass expects
60
+ merged.each_key do |key|
61
+ lckey = key.to_s.gsub( /(.)([A-Z])/ ) {|match|
62
+ match[0,1] + "_" + match[1,1].downcase
63
+ }.to_sym
64
+
65
+ next if key == lckey
66
+ merged[ lckey ] = merged.delete( key )
67
+ end
68
+
69
+ # Register this instance with the class for introspection (costs
70
+ # much less than ObjectSpace.each_object).
71
+ obj = super( merged, &cleanup )
72
+ self.class.extent << obj
73
+
74
+ return obj
75
+ end
76
+
77
+
78
+ ######
79
+ public
80
+ ######
81
+
82
+ # Delegate some methods to the underlying hash
83
+ def_delegators :@objs, :length, :keys, :values
84
+
85
+
86
+ # The name of the cache; used in introspection
87
+ attr_reader :name
88
+
89
+ # Total count of cache hits
90
+ attr_reader :hits
91
+
92
+ # Total count of cache misses
93
+ attr_reader :misses
94
+
95
+ # Cache size in bytes
96
+ attr_reader :size
97
+
98
+ # The list of cached objects
99
+ attr_reader :list
100
+
101
+
102
+ ### Overridden to provide logging of invalidated keys.
103
+ def invalidate( key )
104
+ self.log.debug "invalidating cache key '%p' for %s" % [key, self.name]
105
+ super
106
+ end
107
+
108
+
109
+ ### Overridden for logging.
110
+ def invalidate_all
111
+ self.log.debug "invalidating all cached objects for %s" % [self.name]
112
+ super
113
+ end
114
+
115
+
116
+ ### Overridden to provide logging of expire phase.
117
+ def expire
118
+ self.log.debug "looking for expired entries in %s" % [self.name]
119
+ super
120
+ end
121
+
122
+
123
+ ### Overridden from the superclass to prevent .to_s from being called on
124
+ ### objects to determine their size if the object supports a #memsize
125
+ ### method. This is mostly to stop templates from being rendered every
126
+ ### time they're cached.
127
+ def []=( key, obj )
128
+ self.expire
129
+
130
+ self.invalidate( key ) if self.cached?( key )
131
+
132
+ if obj.respond_to?( :memsize )
133
+ size = obj.memsize
134
+ else
135
+ size = obj.to_s.size
136
+ end
137
+
138
+ # Test against size threshold
139
+ if @max_obj_size && size > @max_obj_size
140
+ Arrow::Logger[self.class].debug \
141
+ "%p not cached: size exceeds maxObjSize: %d" %
142
+ [ obj, @max_obj_size ]
143
+ return obj
144
+ end
145
+ if @max_obj_size.nil? && @max_size && size > @max_size
146
+ Arrow::Logger[self.class].debug \
147
+ "%p not cached: size exceeds maxSize: %d" %
148
+ [ obj, @max_size ]
149
+ return obj
150
+ end
151
+
152
+ if @max_num && @list.size >= @max_num
153
+ Arrow::Logger[self.class].debug \
154
+ "Dropping %p from the cache: count exceeds maxNum: %d" %
155
+ [ @list.first, @max_num ]
156
+ self.invalidate( @list.first )
157
+ end
158
+
159
+ @size += size
160
+ if @max_size
161
+ while @size > @max_size
162
+ Arrow::Logger[self.class].debug \
163
+ "Dropping %p from the cache: size exceeds maxSize: %d" %
164
+ [ @list.first, @max_size ]
165
+ self.invalidate( @list.first )
166
+ end
167
+ end
168
+
169
+ @objs[ key ] = Cache::CACHE_OBJECT.new( obj, size, Time.now.to_i )
170
+ @list.push( key )
171
+
172
+ return obj
173
+ end
174
+
175
+
176
+ end # class Arrow::Cache
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'yaml'
4
+
5
+ require 'arrow/config'
6
+ require 'arrow/path'
7
+ require 'arrow/constants'
8
+
9
+
10
+ # The Arrow::Config::YamlLoader class, a derivative of Arrow::Config::Loader. It
11
+ # is used to load configuration files written in YAML for the Arrow web
12
+ # application framework.
13
+ #
14
+ # == Authors
15
+ #
16
+ # * Michael Granger <ged@FaerieMUD.org>
17
+ #
18
+ # Please see the file LICENSE in the top-level directory for licensing details.
19
+ #
20
+ class Arrow::Config::YamlLoader < Arrow::Config::Loader
21
+ include Arrow::Constants
22
+
23
+
24
+ # Add YAML domain types for Arrow classes
25
+
26
+ YAML.add_domain_type( YAML_DOMAIN, "arrowPath" ) do |type, val|
27
+ obj = nil
28
+ case val
29
+ when Array
30
+ Arrow::Logger.debug "Adding %p to loaded Arrow::Path" % [ val ]
31
+ obj = Arrow::Path.new( val )
32
+ else
33
+ raise "Invalid #{type}: %p" % val
34
+ end
35
+
36
+ obj
37
+ end
38
+
39
+
40
+
41
+ ######
42
+ public
43
+ ######
44
+
45
+ ### Load and return configuration values from the YAML +file+
46
+ ### specified.
47
+ def load( filename )
48
+ self.log.info "Loading YAML-format configuration from '%s'" % filename
49
+ return YAML.load_file( filename )
50
+ end
51
+
52
+
53
+ ### Save configuration values to the YAML +file+ specified.
54
+ def save( confighash, filename )
55
+ self.log.info "Saving YAML-format configuration to '%s'" % filename
56
+ File.open( filename, File::WRONLY|File::CREAT|File::TRUNC ) {|ofh|
57
+ ofh.print( confighash.to_yaml )
58
+ }
59
+ end
60
+
61
+
62
+ ### Return +true+ if the specified +file+ is newer than the given
63
+ ### +time+.
64
+ def is_newer?( file, time )
65
+ return false unless File.exists?( file )
66
+ st = File.stat( file )
67
+ self.log.debug "File mtime is: %s, comparison time is: %s" %
68
+ [ st.mtime, time ]
69
+ return st.mtime > time
70
+ end
71
+
72
+
73
+ end # class Arrow::Config::YamlLoader
74
+
75
+