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
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'uri'
|
|
4
|
+
require 'pluginfactory'
|
|
5
|
+
require 'configurability'
|
|
6
|
+
|
|
7
|
+
require 'arrow/object'
|
|
8
|
+
require 'arrow/exceptions'
|
|
9
|
+
require 'arrow/mixins'
|
|
10
|
+
require 'arrow/logger'
|
|
11
|
+
require 'arrow/config'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# The Arrow::Session class, a container for maintaining state across multiple
|
|
15
|
+
# transactions.
|
|
16
|
+
#
|
|
17
|
+
# == Authors
|
|
18
|
+
#
|
|
19
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
|
20
|
+
#
|
|
21
|
+
# Please see the file LICENSE in the top-level directory for licensing details.
|
|
22
|
+
#
|
|
23
|
+
class Arrow::Session < Arrow::Object
|
|
24
|
+
include PluginFactory,
|
|
25
|
+
Enumerable
|
|
26
|
+
extend Configurability
|
|
27
|
+
|
|
28
|
+
config_key :session
|
|
29
|
+
|
|
30
|
+
require 'arrow/session/store'
|
|
31
|
+
require 'arrow/session/lock'
|
|
32
|
+
require 'arrow/session/id'
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
#############################################################
|
|
37
|
+
### C L A S S M E T H O D S
|
|
38
|
+
#############################################################
|
|
39
|
+
|
|
40
|
+
require 'arrow/config'
|
|
41
|
+
@config = Arrow::Config.new.session
|
|
42
|
+
class << self
|
|
43
|
+
attr_reader :config
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
### Parse the given string into a URI object, appending the path part if
|
|
47
|
+
### it doesn't exist.
|
|
48
|
+
def self::parse_uri( str )
|
|
49
|
+
return str if str.is_a?( URI::Generic )
|
|
50
|
+
str += ":." if /^\w+$/ =~ str
|
|
51
|
+
URI.parse( str )
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
### Configure the session class's factory with the given Arrow::Config
|
|
56
|
+
### object.
|
|
57
|
+
def self::configure( config )
|
|
58
|
+
@config = config.dup
|
|
59
|
+
Arrow::Logger[self].debug "Done. Session config is: %p" % @config
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
### Create a new session for the specified +request+.
|
|
64
|
+
def self::create( txn, configHash={} )
|
|
65
|
+
# Merge the incoming config with the factory's
|
|
66
|
+
sconfig = @config.merge( configHash )
|
|
67
|
+
Arrow::Logger[self].debug "Merged config is: %p" % sconfig
|
|
68
|
+
|
|
69
|
+
# Create a new id and backing store object
|
|
70
|
+
idobj = self.create_id( sconfig, txn )
|
|
71
|
+
store = self.create_store( sconfig, idobj )
|
|
72
|
+
lock = self.create_lock( sconfig, store, idobj )
|
|
73
|
+
|
|
74
|
+
# Create the session cookie
|
|
75
|
+
scookie = self.create_session_cookie( txn, sconfig, idobj, store, lock )
|
|
76
|
+
|
|
77
|
+
return new( idobj, lock, store, txn, scookie )
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
### Set the session cookie if we're really running under Apache.
|
|
82
|
+
def self::create_session_cookie( txn, config, id, store, lock )
|
|
83
|
+
scookie = Arrow::Cookie.new(
|
|
84
|
+
config.idName,
|
|
85
|
+
id.to_s,
|
|
86
|
+
:expires => config.expires,
|
|
87
|
+
:path => '/'
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
Arrow::Logger[self].debug "Created cookie: %p" % scookie.to_s
|
|
91
|
+
return scookie
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
### Create an Arrow::Session::Id object for the given +txn+, with the
|
|
96
|
+
### particulars dictated by the specified +config+.
|
|
97
|
+
def self::create_id( config, txn )
|
|
98
|
+
cookie_name = config.idName
|
|
99
|
+
|
|
100
|
+
# Fetch the id from the request, either from the session cookie or
|
|
101
|
+
# as a parameter if the cookie doesn't exist.
|
|
102
|
+
if txn.request_cookies.include?( cookie_name )
|
|
103
|
+
Arrow::Logger[self].debug "Found an existing session cookie (%s)" %
|
|
104
|
+
[ cookie_name ]
|
|
105
|
+
idstring = txn.request_cookies[ cookie_name ].value
|
|
106
|
+
else
|
|
107
|
+
Arrow::Logger[self].debug \
|
|
108
|
+
"No existing session cookie (%s); looking for one in a request parameter" %
|
|
109
|
+
[ cookie_name]
|
|
110
|
+
idstring = txn.param( cookie_name )
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
Arrow::Logger[self].debug "Creating a session id object: %p" % config.idType
|
|
114
|
+
return Arrow::Session::Id.create( config.idType, txn.request, idstring )
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
### Create an Arrow::Session::Store object with the given +id+. The type
|
|
119
|
+
### and configuration of the store will be dictated by the specified
|
|
120
|
+
### +config+ object.
|
|
121
|
+
def self::create_store( config, id )
|
|
122
|
+
Arrow::Logger[self].debug "Creating a session store: %p" % config.storeType
|
|
123
|
+
return Arrow::Session::Store.create( config.storeType, id )
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
### Create an Arrow::Session::Lock object for the specified +store+ and +id+.
|
|
128
|
+
def self::create_lock( config, store, id )
|
|
129
|
+
|
|
130
|
+
lockuri = Arrow::Session.parse_uri( config.lockType )
|
|
131
|
+
lock = nil
|
|
132
|
+
|
|
133
|
+
# If the configuration says to use the recommended locker, ask the
|
|
134
|
+
# backing store for a lock object.
|
|
135
|
+
if lockuri.scheme == 'recommended'
|
|
136
|
+
Arrow::Logger[self].debug "Creating recommended lock"
|
|
137
|
+
lock = store.create_recommended_lock( id ) or
|
|
138
|
+
raise Arrow::SessionError, "No recommended locker for %s" %
|
|
139
|
+
store.class.name
|
|
140
|
+
else
|
|
141
|
+
Arrow::Logger[self].debug "Creating a session lock: %p" % lockuri
|
|
142
|
+
lock = Arrow::Session::Lock.create( lockuri, id )
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
return lock
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
### Return the configured name of the session cookie.
|
|
150
|
+
def self::session_cookie_name
|
|
151
|
+
return @config.idName
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
#########
|
|
157
|
+
protected
|
|
158
|
+
#########
|
|
159
|
+
|
|
160
|
+
### Create delegators that readlock the session store before accessing
|
|
161
|
+
### it.
|
|
162
|
+
def self::def_delegated_readers( *syms )
|
|
163
|
+
syms.each do |sym|
|
|
164
|
+
define_method( sym ) do |*args|
|
|
165
|
+
@lock.read_lock
|
|
166
|
+
@store.send( sym, *args )
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
### Create delegators that writelock the session store before accessing
|
|
173
|
+
### it.
|
|
174
|
+
def self::def_delegated_writers( *syms )
|
|
175
|
+
syms.each do |sym|
|
|
176
|
+
define_method( sym ) do |*args|
|
|
177
|
+
@lock.write_lock
|
|
178
|
+
@store.send( sym, *args )
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
#############################################################
|
|
186
|
+
### I N S T A N C E M E T H O D S
|
|
187
|
+
#############################################################
|
|
188
|
+
|
|
189
|
+
### Create a new Arrow::Session object for the given +idobj+ (an
|
|
190
|
+
### Arrow::Session::Id object), using the given +lock+ (an
|
|
191
|
+
### Arrow::Session::Locker object) for serializing access.
|
|
192
|
+
def initialize( idobj, lock, store, txn, cookie=nil )
|
|
193
|
+
raise ArgumentError, "No id object" unless idobj
|
|
194
|
+
raise ArgumentError, "No lock object" unless lock
|
|
195
|
+
raise ArgumentError, "No store object" unless store
|
|
196
|
+
|
|
197
|
+
self.log.debug "Initializing session with id: %p, lock: %p, store: %p" %
|
|
198
|
+
[ idobj, lock, store ]
|
|
199
|
+
|
|
200
|
+
@id = idobj
|
|
201
|
+
@lock = lock
|
|
202
|
+
@store = store
|
|
203
|
+
@txn = txn
|
|
204
|
+
@cookie = cookie
|
|
205
|
+
|
|
206
|
+
@store[ :_session_id ] = id.to_s
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
######
|
|
211
|
+
public
|
|
212
|
+
######
|
|
213
|
+
|
|
214
|
+
# The session's unique identifier, an Apache::Session::Id object.
|
|
215
|
+
attr_reader :id
|
|
216
|
+
|
|
217
|
+
# The session's backing store; an Apache::Session::Store object.
|
|
218
|
+
attr_reader :store
|
|
219
|
+
|
|
220
|
+
# The session's lock object; an Apache::Session::Lock.
|
|
221
|
+
attr_reader :lock
|
|
222
|
+
|
|
223
|
+
# The Apache::Cookie object used to manipulate the session cookie.
|
|
224
|
+
attr_reader :cookie
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
### Delete the session
|
|
228
|
+
def remove
|
|
229
|
+
@lock.with_write_lock do
|
|
230
|
+
@store.remove
|
|
231
|
+
end
|
|
232
|
+
@lock.release_all_locks
|
|
233
|
+
@cookie.expires = Time.at(0)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
### Clear all data from the session object.
|
|
238
|
+
def clear
|
|
239
|
+
@lock.with_write_lock do
|
|
240
|
+
@store.clear
|
|
241
|
+
@store[ :_session_id ] = @id.to_s
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
### Enumerable iterface: iterate over the session's key/value pairs,
|
|
247
|
+
### calling the given block once for each pair.
|
|
248
|
+
def each( &block )
|
|
249
|
+
raise LocalJumpError, "no block given" unless block
|
|
250
|
+
@lock.read_lock
|
|
251
|
+
@store.each( &block )
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
### Save the session to fixed storage and set the session cookie in the
|
|
256
|
+
### creating transaction's outgoing headers.
|
|
257
|
+
def save
|
|
258
|
+
begin
|
|
259
|
+
self.log.debug "Saving session data"
|
|
260
|
+
@store.save
|
|
261
|
+
self.log.debug "Writing session cookie (%p)" % [ @cookie ]
|
|
262
|
+
@txn.cookies[ self.class.session_cookie_name ] = @cookie
|
|
263
|
+
ensure
|
|
264
|
+
self.log.debug "Releasing all locks"
|
|
265
|
+
@lock.release_all_locks
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
### Tell the session that it will not be used again in the current
|
|
271
|
+
### session.
|
|
272
|
+
def finish
|
|
273
|
+
@lock.release_all_locks
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
### Delegate the common hash methods to the session store, which
|
|
278
|
+
### re-delegates them to its data.
|
|
279
|
+
def_delegated_readers :[], :default, :default=, :each, :each_key,
|
|
280
|
+
:each_pair, :each_value, :empty?, :fetch, :has_key?, :has_value?,
|
|
281
|
+
:include?, :index, :invert, :key?, :keys, :length, :member?, :merge,
|
|
282
|
+
:rehash, :reject, :select, :size, :sort, :to_a, :value?, :values
|
|
283
|
+
|
|
284
|
+
def_delegated_writers :[]=, :delete, :clear, :merge!, :replace,
|
|
285
|
+
:delete_if, :reject!
|
|
286
|
+
|
|
287
|
+
end # class Arrow::Session
|
|
288
|
+
|
|
289
|
+
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'sequel'
|
|
4
|
+
|
|
5
|
+
require 'arrow/exceptions'
|
|
6
|
+
require 'arrow/session/store'
|
|
7
|
+
|
|
8
|
+
# The Arrow::Session::DbStore class, a derivative of Arrow::Session::Store.
|
|
9
|
+
# Instances of this class store a session object in a database.
|
|
10
|
+
#
|
|
11
|
+
# == Authors
|
|
12
|
+
#
|
|
13
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
|
14
|
+
#
|
|
15
|
+
# :include: LICENSE
|
|
16
|
+
#
|
|
17
|
+
#--
|
|
18
|
+
#
|
|
19
|
+
# Please see the file LICENSE in the BASE directory for licensing details.
|
|
20
|
+
#
|
|
21
|
+
class Arrow::Session::DbStore < Arrow::Session::Store
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
#################################################################
|
|
25
|
+
### I N S T A N C E M E T H O D S
|
|
26
|
+
#################################################################
|
|
27
|
+
|
|
28
|
+
### Create a new Arrow::Session::DbStore object.
|
|
29
|
+
def initialize( uri, idobj )
|
|
30
|
+
|
|
31
|
+
db = Sequel.connect( uri )
|
|
32
|
+
dataset = db[]
|
|
33
|
+
@id = idobj
|
|
34
|
+
super
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
######
|
|
39
|
+
public
|
|
40
|
+
######
|
|
41
|
+
|
|
42
|
+
# The database handle
|
|
43
|
+
attr_reader :db
|
|
44
|
+
|
|
45
|
+
# The session ID this store was created for
|
|
46
|
+
attr_reader :id
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
### Insert the specified +data+ hash into whatever permanent storage the
|
|
50
|
+
### Store object is acting as an interface to.
|
|
51
|
+
def insert
|
|
52
|
+
super {|data|
|
|
53
|
+
self.log.debug "Inserting data into session table for session %s" % [ self.id ]
|
|
54
|
+
self.
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
### Update the current data hash stored in permanent storage with the
|
|
60
|
+
### values contained in +data+.
|
|
61
|
+
def update
|
|
62
|
+
super {|data|
|
|
63
|
+
self.log.debug "Updating data in session file"
|
|
64
|
+
ofh = self.open
|
|
65
|
+
ofh.seek( 0, File::SEEK_SET )
|
|
66
|
+
ofh.print( data )
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
### Retrieve the data hash stored in permanent storage associated with
|
|
72
|
+
### the id the object was created with.
|
|
73
|
+
def retrieve
|
|
74
|
+
super {
|
|
75
|
+
self.log.debug "Reading data in session file"
|
|
76
|
+
ofh = self.open( File::RDWR )
|
|
77
|
+
ofh.seek( 0, File::SEEK_SET )
|
|
78
|
+
ofh.read
|
|
79
|
+
}
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
### Permanently remove the data hash associated with the id used in the
|
|
84
|
+
### receiver's creation from permanent storage.
|
|
85
|
+
def remove
|
|
86
|
+
super
|
|
87
|
+
self.close
|
|
88
|
+
file = self.session_file
|
|
89
|
+
if File.exists?( file )
|
|
90
|
+
File.delete( file )
|
|
91
|
+
else
|
|
92
|
+
raise Arrow::SessionError,
|
|
93
|
+
"Session file #{file} does not exist in the data store"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
end # class Arrow::Session::DbStore
|
|
99
|
+
|
|
100
|
+
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'ftools'
|
|
4
|
+
|
|
5
|
+
require 'arrow/session/lock'
|
|
6
|
+
|
|
7
|
+
# The Arrow::Session::FileLock class, a derivative of Arrow::Session::Lock.
|
|
8
|
+
# Instances of this class provide file-based locking for Arrow sessions
|
|
9
|
+
# using the flock(2) system call. It (obviously) won't work on platforms
|
|
10
|
+
# which don't support flock(2), or on filesystems which don't provide
|
|
11
|
+
# flock-based locking semantics (e.g., NFS).
|
|
12
|
+
#
|
|
13
|
+
# == Authors
|
|
14
|
+
#
|
|
15
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
|
16
|
+
#
|
|
17
|
+
# Please see the file LICENSE in the top-level directory for licensing details.
|
|
18
|
+
#
|
|
19
|
+
class Arrow::Session::FileLock < Arrow::Session::Lock
|
|
20
|
+
|
|
21
|
+
# The path to the default lockdir
|
|
22
|
+
DefaultLockDir = '/tmp'
|
|
23
|
+
|
|
24
|
+
# The format string that will be used for the name of the lock file. The
|
|
25
|
+
# first '%s' will be replaced with a sanitized version of the session
|
|
26
|
+
# id.
|
|
27
|
+
LockfileFormat = "arrow-session-%s.lock"
|
|
28
|
+
|
|
29
|
+
# The mode to open the lockfile in
|
|
30
|
+
FileMode = File::RDWR|File::CREAT
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
#############################################################
|
|
34
|
+
### C L A S S M E T H O D S
|
|
35
|
+
#############################################################
|
|
36
|
+
|
|
37
|
+
### Clean the specified +directory+ of lock files older than +threshold+
|
|
38
|
+
### seconds.
|
|
39
|
+
def self::clean( directory=DefaultLockDir, threshold=3600 )
|
|
40
|
+
pat = File.join( directory, LockfileFormat.gsub(/%s/, '*') )
|
|
41
|
+
threshold = Time.now - threshold
|
|
42
|
+
Dir[ pat ].each do |file|
|
|
43
|
+
if File.mtime( file ) < threshold
|
|
44
|
+
Arrow::Logger[self].info \
|
|
45
|
+
"Removing stale lockfile '%s'" % file
|
|
46
|
+
begin
|
|
47
|
+
fh = File.open( file, FileMode )
|
|
48
|
+
fh.flock( File::LOCK_EX|File::LOCK_NB )
|
|
49
|
+
File.delete( file )
|
|
50
|
+
fh.flock( File::LOCK_UN )
|
|
51
|
+
fh.close
|
|
52
|
+
rescue => err
|
|
53
|
+
Arrow::Logger[self].warning \
|
|
54
|
+
"Could not clean up '%s': %s" %
|
|
55
|
+
[ file, err.message ]
|
|
56
|
+
next
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
#############################################################
|
|
65
|
+
### I N S T A N C E M E T H O D S
|
|
66
|
+
#############################################################
|
|
67
|
+
|
|
68
|
+
### Create a new Arrow::Session::FileLock object.
|
|
69
|
+
def initialize( uri, id )
|
|
70
|
+
@lockDir = uri.path || DefaultLockDir
|
|
71
|
+
super
|
|
72
|
+
|
|
73
|
+
# 'foo de barg blag 0x1f2eca'.gsub( /\W/, '_' )
|
|
74
|
+
# => foo_de_barg_blag_0x1f2eca
|
|
75
|
+
lockfilename = LockfileFormat % id.to_s.gsub( /\W/, '_' )
|
|
76
|
+
File.mkpath( @lockDir )
|
|
77
|
+
@filename = File.join( @lockDir, lockfilename ).untaint
|
|
78
|
+
@lockfile = nil
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
######
|
|
83
|
+
public
|
|
84
|
+
######
|
|
85
|
+
|
|
86
|
+
# The path to the directory where session lockfiles are kept.
|
|
87
|
+
attr_accessor :lockDir
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
### Indicate to the lock that the caller will no longer be using it, and
|
|
91
|
+
### it may free any resources it had been using.
|
|
92
|
+
def finish
|
|
93
|
+
super
|
|
94
|
+
self.close_lock_file
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
#########
|
|
99
|
+
protected
|
|
100
|
+
#########
|
|
101
|
+
|
|
102
|
+
### Get the File object for the lockfile belonging to this lock,
|
|
103
|
+
### creating it if necessary.
|
|
104
|
+
def lockfile
|
|
105
|
+
@lockfile ||= File.open( @filename, FileMode )
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
### Close the lockfile and destroy the File object belonging to this
|
|
110
|
+
### lock.
|
|
111
|
+
def close_lock_file
|
|
112
|
+
if @lockfile
|
|
113
|
+
path = @lockfile.path
|
|
114
|
+
@lockfile.close
|
|
115
|
+
@lockfile = nil
|
|
116
|
+
File.delete( path.untaint ) if File.exist?( path.untaint )
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
### Acquire a read (shared) lock on the lockfile.
|
|
122
|
+
def acquire_read_lock( blocking )
|
|
123
|
+
flags = File::LOCK_SH
|
|
124
|
+
flags |= File::LOCK_NB if !blocking
|
|
125
|
+
|
|
126
|
+
self.lockfile.flock( flags )
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
### Acquire a write (exclusive) lock on the lockfile.
|
|
131
|
+
def acquire_write_lock( blocking )
|
|
132
|
+
flags = File::LOCK_EX
|
|
133
|
+
flags |= File::LOCK_NB if !blocking
|
|
134
|
+
|
|
135
|
+
self.lockfile.flock( flags )
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
### Release a previously-acquired read lock.
|
|
140
|
+
def release_read_lock
|
|
141
|
+
if !self.write_locked?
|
|
142
|
+
self.lockfile.flock( File::LOCK_UN )
|
|
143
|
+
self.close_lock_file
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
### Release a previously-acquired write lock.
|
|
149
|
+
def release_write_lock
|
|
150
|
+
if self.read_locked?
|
|
151
|
+
self.lockfile.flock( File::LOCK_SH )
|
|
152
|
+
else
|
|
153
|
+
self.lockfile.flock( File::LOCK_UN )
|
|
154
|
+
self.close_lock_file
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
end # class Arrow::Session::FileLock
|
|
159
|
+
|
|
160
|
+
|