zuk-picnic 0.7.999.20090212
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.txt +1 -0
- data/History.txt +68 -0
- data/LICENSE.txt +165 -0
- data/Manifest.txt +29 -0
- data/README.txt +31 -0
- data/Rakefile +62 -0
- data/lib/picnic/authentication.rb +218 -0
- data/lib/picnic/cli.rb +165 -0
- data/lib/picnic/conf.rb +135 -0
- data/lib/picnic/controllers.rb +4 -0
- data/lib/picnic/logger.rb +41 -0
- data/lib/picnic/server.rb +98 -0
- data/lib/picnic/service_control.rb +274 -0
- data/lib/picnic/version.rb +9 -0
- data/lib/picnic.rb +48 -0
- data/setup.rb +1585 -0
- data/test/picnic_test.rb +11 -0
- data/test/test_helper.rb +2 -0
- data/vendor/camping-2.0.20090212/CHANGELOG +118 -0
- data/vendor/camping-2.0.20090212/COPYING +18 -0
- data/vendor/camping-2.0.20090212/README +119 -0
- data/vendor/camping-2.0.20090212/Rakefile +174 -0
- data/vendor/camping-2.0.20090212/bin/camping +99 -0
- data/vendor/camping-2.0.20090212/doc/camping.1.gz +0 -0
- data/vendor/camping-2.0.20090212/examples/README +5 -0
- data/vendor/camping-2.0.20090212/examples/blog.rb +375 -0
- data/vendor/camping-2.0.20090212/examples/campsh.rb +629 -0
- data/vendor/camping-2.0.20090212/examples/tepee.rb +242 -0
- data/vendor/camping-2.0.20090212/extras/Camping.gif +0 -0
- data/vendor/camping-2.0.20090212/extras/flipbook_rdoc.rb +491 -0
- data/vendor/camping-2.0.20090212/extras/permalink.gif +0 -0
- data/vendor/camping-2.0.20090212/lib/camping/ar/session.rb +132 -0
- data/vendor/camping-2.0.20090212/lib/camping/ar.rb +78 -0
- data/vendor/camping-2.0.20090212/lib/camping/mab.rb +26 -0
- data/vendor/camping-2.0.20090212/lib/camping/reloader.rb +163 -0
- data/vendor/camping-2.0.20090212/lib/camping/server.rb +158 -0
- data/vendor/camping-2.0.20090212/lib/camping/session.rb +74 -0
- data/vendor/camping-2.0.20090212/lib/camping-unabridged.rb +638 -0
- data/vendor/camping-2.0.20090212/lib/camping.rb +54 -0
- data/vendor/camping-2.0.20090212/setup.rb +1551 -0
- data/vendor/camping-2.0.20090212/test/apps/env_debug.rb +65 -0
- data/vendor/camping-2.0.20090212/test/apps/forms.rb +95 -0
- data/vendor/camping-2.0.20090212/test/apps/misc.rb +86 -0
- data/vendor/camping-2.0.20090212/test/apps/sessions.rb +38 -0
- data/vendor/camping-2.0.20090212/test/test_camping.rb +54 -0
- metadata +128 -0
@@ -0,0 +1,491 @@
|
|
1
|
+
CAMPING_EXTRAS_DIR = File.expand_path(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
module Generators
|
4
|
+
class HTMLGenerator
|
5
|
+
def generate_html
|
6
|
+
@files_and_classes = {
|
7
|
+
'allfiles' => gen_into_index(@files),
|
8
|
+
'allclasses' => gen_into_index(@classes),
|
9
|
+
"initial_page" => main_url,
|
10
|
+
'realtitle' => CGI.escapeHTML(@options.title),
|
11
|
+
'charset' => @options.charset
|
12
|
+
}
|
13
|
+
|
14
|
+
# the individual descriptions for files and classes
|
15
|
+
gen_into(@files)
|
16
|
+
gen_into(@classes)
|
17
|
+
gen_main_index
|
18
|
+
|
19
|
+
# this method is defined in the template file
|
20
|
+
write_extra_pages if defined? write_extra_pages
|
21
|
+
end
|
22
|
+
|
23
|
+
def gen_into(list)
|
24
|
+
hsh = @files_and_classes.dup
|
25
|
+
list.each do |item|
|
26
|
+
if item.document_self
|
27
|
+
op_file = item.path
|
28
|
+
hsh['root'] = item.path.split("/").map { ".." }[1..-1].join("/")
|
29
|
+
item.instance_variable_set("@values", hsh)
|
30
|
+
File.makedirs(File.dirname(op_file))
|
31
|
+
File.open(op_file, "w") { |file| item.write_on(file) }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def gen_into_index(list)
|
37
|
+
res = []
|
38
|
+
list.each do |item|
|
39
|
+
hsh = item.value_hash
|
40
|
+
hsh['href'] = item.path
|
41
|
+
hsh['name'] = item.index_name
|
42
|
+
res << hsh
|
43
|
+
end
|
44
|
+
res
|
45
|
+
end
|
46
|
+
|
47
|
+
def gen_main_index
|
48
|
+
template = TemplatePage.new(RDoc::Page::INDEX)
|
49
|
+
File.open("index.html", "w") do |f|
|
50
|
+
values = @files_and_classes.dup
|
51
|
+
if @options.inline_source
|
52
|
+
values['inline_source'] = true
|
53
|
+
end
|
54
|
+
template.write_html_on(f, values)
|
55
|
+
end
|
56
|
+
['Camping.gif', 'permalink.gif'].each do |img|
|
57
|
+
ipath = File.join(CAMPING_EXTRAS_DIR, img)
|
58
|
+
File.copy(ipath, img)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
module RDoc
|
66
|
+
module Page
|
67
|
+
######################################################################
|
68
|
+
#
|
69
|
+
# The following is used for the -1 option
|
70
|
+
#
|
71
|
+
|
72
|
+
FONTS = "verdana,arial,'Bitstream Vera Sans',helvetica,sans-serif"
|
73
|
+
|
74
|
+
STYLE = %{
|
75
|
+
body, th, td {
|
76
|
+
font: normal 14px verdana,arial,'Bitstream Vera Sans',helvetica,sans-serif;
|
77
|
+
line-height: 160%;
|
78
|
+
padding: 0; margin: 0;
|
79
|
+
margin-bottom: 30px;
|
80
|
+
/* background-color: #402; */
|
81
|
+
background-color: #694;
|
82
|
+
}
|
83
|
+
h1, h2, h3, h4 {
|
84
|
+
font-family: Utopia, Georgia, serif;
|
85
|
+
font-weight: bold;
|
86
|
+
letter-spacing: -0.018em;
|
87
|
+
}
|
88
|
+
h1 { font-size: 24px; margin: .15em 1em 0 0 }
|
89
|
+
h2 { font-size: 24px }
|
90
|
+
h3 { font-size: 19px }
|
91
|
+
h4 { font-size: 17px; font-weight: normal; }
|
92
|
+
h4.ruled { border-bottom: solid 1px #CC9; }
|
93
|
+
h2.ruled { padding-top: 35px; border-top: solid 1px #AA5; }
|
94
|
+
|
95
|
+
/* Link styles */
|
96
|
+
:link, :visited {
|
97
|
+
color: #00b;
|
98
|
+
}
|
99
|
+
:link:hover, :visited:hover {
|
100
|
+
background-color: #eee;
|
101
|
+
color: #B22;
|
102
|
+
}
|
103
|
+
#fullpage {
|
104
|
+
width: 720px;
|
105
|
+
margin: 0 auto;
|
106
|
+
}
|
107
|
+
.page_shade, .page {
|
108
|
+
padding: 0px 5px 5px 0px;
|
109
|
+
background-color: #fcfcf9;
|
110
|
+
border: solid 1px #983;
|
111
|
+
}
|
112
|
+
.page {
|
113
|
+
margin-left: -5px;
|
114
|
+
margin-top: -5px;
|
115
|
+
padding: 20px 35px;
|
116
|
+
}
|
117
|
+
.page .header {
|
118
|
+
float: right;
|
119
|
+
color: #777;
|
120
|
+
font-size: 10px;
|
121
|
+
}
|
122
|
+
.page h1, .page h2, .page h3 {
|
123
|
+
clear: both;
|
124
|
+
text-align: center;
|
125
|
+
}
|
126
|
+
#pager {
|
127
|
+
padding: 10px 4px;
|
128
|
+
color: white;
|
129
|
+
font-size: 11px;
|
130
|
+
}
|
131
|
+
#pager :link, #pager :visited {
|
132
|
+
color: #bfb;
|
133
|
+
padding: 0px 5px;
|
134
|
+
}
|
135
|
+
#pager :link:hover, #pager :visited:hover {
|
136
|
+
background-color: #262;
|
137
|
+
color: white;
|
138
|
+
}
|
139
|
+
#logo { float: left; }
|
140
|
+
#menu { background-color: #dfa; padding: 4px 12px; margin: 0; }
|
141
|
+
#menu h3 { padding: 0; margin: 0; }
|
142
|
+
#menu #links { float: right; }
|
143
|
+
pre { font-weight: bold; color: #730; }
|
144
|
+
tt { color: #703; font-size: 12pt; }
|
145
|
+
.dyn-source { background-color: #775915; padding: 4px 8px; margin: 0; display: none; }
|
146
|
+
.dyn-source pre { color: #DDDDDD; font-size: 8pt; }
|
147
|
+
.source-link { text-align: right; font-size: 8pt; }
|
148
|
+
.ruby-comment { color: green; font-style: italic }
|
149
|
+
.ruby-constant { color: #CCDDFF; font-weight: bold; }
|
150
|
+
.ruby-identifier { color: #CCCCCC; }
|
151
|
+
.ruby-ivar { color: #BBCCFF; }
|
152
|
+
.ruby-keyword { color: #EEEEFF; font-weight: bold }
|
153
|
+
.ruby-node { color: #FFFFFF; }
|
154
|
+
.ruby-operator { color: #CCCCCC; }
|
155
|
+
.ruby-regexp { color: #DDFFDD; }
|
156
|
+
.ruby-value { color: #FFAAAA; font-style: italic }
|
157
|
+
.kw { color: #DDDDFF; font-weight: bold }
|
158
|
+
.cmt { color: #CCFFCC; font-style: italic }
|
159
|
+
.str { color: #EECCCC; font-style: italic }
|
160
|
+
.re { color: #EECCCC; }
|
161
|
+
}
|
162
|
+
|
163
|
+
CONTENTS_XML = %{
|
164
|
+
IF:description
|
165
|
+
%description%
|
166
|
+
ENDIF:description
|
167
|
+
|
168
|
+
IF:requires
|
169
|
+
<h4>Requires:</h4>
|
170
|
+
<ul>
|
171
|
+
START:requires
|
172
|
+
IF:aref
|
173
|
+
<li><a href="%aref%">%name%</a></li>
|
174
|
+
ENDIF:aref
|
175
|
+
IFNOT:aref
|
176
|
+
<li>%name%</li>
|
177
|
+
ENDIF:aref
|
178
|
+
END:requires
|
179
|
+
</ul>
|
180
|
+
ENDIF:requires
|
181
|
+
|
182
|
+
IF:attributes
|
183
|
+
<h4>Attributes</h4>
|
184
|
+
<table>
|
185
|
+
START:attributes
|
186
|
+
<tr><td>%name%</td><td>%rw%</td><td>%a_desc%</td></tr>
|
187
|
+
END:attributes
|
188
|
+
</table>
|
189
|
+
ENDIF:attributes
|
190
|
+
|
191
|
+
IF:includes
|
192
|
+
<h4>Includes</h4>
|
193
|
+
<ul>
|
194
|
+
START:includes
|
195
|
+
IF:aref
|
196
|
+
<li><a href="%aref%">%name%</a></li>
|
197
|
+
ENDIF:aref
|
198
|
+
IFNOT:aref
|
199
|
+
<li>%name%</li>
|
200
|
+
ENDIF:aref
|
201
|
+
END:includes
|
202
|
+
</ul>
|
203
|
+
ENDIF:includes
|
204
|
+
|
205
|
+
START:sections
|
206
|
+
IF:method_list
|
207
|
+
<h2 class="ruled">Methods</h2>
|
208
|
+
START:method_list
|
209
|
+
IF:methods
|
210
|
+
START:methods
|
211
|
+
<h4 class="ruled">%type% %category% method:
|
212
|
+
IF:callseq
|
213
|
+
<strong><a name="%aref%">%callseq%</a></strong> <a href="#%aref%"><img src="%root%/permalink.gif" border="0" title="Permalink to %callseq%" /></a>
|
214
|
+
ENDIF:callseq
|
215
|
+
IFNOT:callseq
|
216
|
+
<strong><a name="%aref%">%name%%params%</a></strong> <a href="#%aref%"><img src="%root%/permalink.gif" border="0" title="Permalink to %type% %category% method: %name%" /></a></h4>
|
217
|
+
ENDIF:callseq
|
218
|
+
|
219
|
+
IF:m_desc
|
220
|
+
%m_desc%
|
221
|
+
ENDIF:m_desc
|
222
|
+
|
223
|
+
IF:sourcecode
|
224
|
+
<div class="sourcecode">
|
225
|
+
<p class="source-link">[ <a href="javascript:toggleSource('%aref%_source')" id="l_%aref%_source">show source</a> ]</p>
|
226
|
+
<div id="%aref%_source" class="dyn-source">
|
227
|
+
<pre>
|
228
|
+
%sourcecode%
|
229
|
+
</pre>
|
230
|
+
</div>
|
231
|
+
</div>
|
232
|
+
ENDIF:sourcecode
|
233
|
+
END:methods
|
234
|
+
ENDIF:methods
|
235
|
+
END:method_list
|
236
|
+
ENDIF:method_list
|
237
|
+
END:sections
|
238
|
+
}
|
239
|
+
|
240
|
+
############################################################################
|
241
|
+
|
242
|
+
|
243
|
+
BODY = %{
|
244
|
+
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
245
|
+
<html>
|
246
|
+
<head>
|
247
|
+
<title>
|
248
|
+
IF:title
|
249
|
+
%realtitle% » %title%
|
250
|
+
ENDIF:title
|
251
|
+
IFNOT:title
|
252
|
+
%realtitle%
|
253
|
+
ENDIF:title
|
254
|
+
</title>
|
255
|
+
<meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
|
256
|
+
<link rel="stylesheet" href="%style_url%" type="text/css" media="screen" />
|
257
|
+
<script language="JavaScript" type="text/javascript">
|
258
|
+
// <![CDATA[
|
259
|
+
|
260
|
+
function toggleSource( id )
|
261
|
+
{
|
262
|
+
var elem
|
263
|
+
var link
|
264
|
+
|
265
|
+
if( document.getElementById )
|
266
|
+
{
|
267
|
+
elem = document.getElementById( id )
|
268
|
+
link = document.getElementById( "l_" + id )
|
269
|
+
}
|
270
|
+
else if ( document.all )
|
271
|
+
{
|
272
|
+
elem = eval( "document.all." + id )
|
273
|
+
link = eval( "document.all.l_" + id )
|
274
|
+
}
|
275
|
+
else
|
276
|
+
return false;
|
277
|
+
|
278
|
+
if( elem.style.display == "block" )
|
279
|
+
{
|
280
|
+
elem.style.display = "none"
|
281
|
+
link.innerHTML = "show source"
|
282
|
+
}
|
283
|
+
else
|
284
|
+
{
|
285
|
+
elem.style.display = "block"
|
286
|
+
link.innerHTML = "hide source"
|
287
|
+
}
|
288
|
+
}
|
289
|
+
|
290
|
+
function openCode( url )
|
291
|
+
{
|
292
|
+
window.open( url, "SOURCE_CODE", "width=400,height=400,scrollbars=yes" )
|
293
|
+
}
|
294
|
+
// ]]>
|
295
|
+
</script>
|
296
|
+
</head>
|
297
|
+
<body>
|
298
|
+
<div id="menu">
|
299
|
+
<div id="links">
|
300
|
+
<a href="http://redhanded.hobix.com/bits/campingAMicroframework.html">backstory</a> |
|
301
|
+
<a href="http://code.whytheluckystiff.net/camping/">wiki</a> |
|
302
|
+
<a href="http://code.whytheluckystiff.net/camping/newticket">bugs</a> |
|
303
|
+
<a href="http://code.whytheluckystiff.net/svn/camping/">svn</a>
|
304
|
+
</div>
|
305
|
+
<h3 class="title">%title%</h3>
|
306
|
+
</div>
|
307
|
+
<div id="fullpage">
|
308
|
+
<div id="logo"><img src="%root%/Camping.gif" /></div>
|
309
|
+
<div id="pager">
|
310
|
+
<strong>Files:</strong>
|
311
|
+
START:allfiles
|
312
|
+
<a href="%root%/%href%" value="%title%">%name%</a>
|
313
|
+
END:allfiles
|
314
|
+
IF:allclasses
|
315
|
+
|
|
316
|
+
<strong>classes:</strong>
|
317
|
+
START:allclasses
|
318
|
+
<a href="%root%/%href%" title="%title%">%name%</a>
|
319
|
+
END:allclasses
|
320
|
+
ENDIF:allclasses
|
321
|
+
</ul>
|
322
|
+
</div>
|
323
|
+
|
324
|
+
!INCLUDE!
|
325
|
+
|
326
|
+
</div>
|
327
|
+
</body>
|
328
|
+
</html>
|
329
|
+
}
|
330
|
+
|
331
|
+
###############################################################################
|
332
|
+
|
333
|
+
FILE_PAGE = <<_FILE_PAGE_
|
334
|
+
<div id="%full_path%" class="page_shade">
|
335
|
+
<div class="page">
|
336
|
+
<div class="header">
|
337
|
+
<div class="path">%full_path% / %dtm_modified%</div>
|
338
|
+
</div>
|
339
|
+
#{CONTENTS_XML}
|
340
|
+
</div>
|
341
|
+
</div>
|
342
|
+
_FILE_PAGE_
|
343
|
+
|
344
|
+
###################################################################
|
345
|
+
|
346
|
+
CLASS_PAGE = %{
|
347
|
+
<div id="%full_name%" class="page_shade">
|
348
|
+
<div class="page">
|
349
|
+
IF:parent
|
350
|
+
<h3>%classmod% %full_name% < HREF:par_url:parent:</h3>
|
351
|
+
ENDIF:parent
|
352
|
+
IFNOT:parent
|
353
|
+
<h3>%classmod% %full_name%</h3>
|
354
|
+
ENDIF:parent
|
355
|
+
|
356
|
+
IF:infiles
|
357
|
+
(in files
|
358
|
+
START:infiles
|
359
|
+
HREF:full_path_url:full_path:
|
360
|
+
END:infiles
|
361
|
+
)
|
362
|
+
ENDIF:infiles
|
363
|
+
} + CONTENTS_XML + %{
|
364
|
+
</div>
|
365
|
+
</div>
|
366
|
+
}
|
367
|
+
|
368
|
+
###################################################################
|
369
|
+
|
370
|
+
METHOD_LIST = %{
|
371
|
+
IF:includes
|
372
|
+
<div class="tablesubsubtitle">Included modules</div><br>
|
373
|
+
<div class="name-list">
|
374
|
+
START:includes
|
375
|
+
<span class="method-name">HREF:aref:name:</span>
|
376
|
+
END:includes
|
377
|
+
</div>
|
378
|
+
ENDIF:includes
|
379
|
+
|
380
|
+
IF:method_list
|
381
|
+
START:method_list
|
382
|
+
IF:methods
|
383
|
+
<table cellpadding=5 width="100%">
|
384
|
+
<tr><td class="tablesubtitle">%type% %category% methods</td></tr>
|
385
|
+
</table>
|
386
|
+
START:methods
|
387
|
+
<table width="100%" cellspacing = 0 cellpadding=5 border=0>
|
388
|
+
<tr><td class="methodtitle">
|
389
|
+
<a name="%aref%">
|
390
|
+
IF:callseq
|
391
|
+
<b>%callseq%</b>
|
392
|
+
ENDIF:callseq
|
393
|
+
IFNOT:callseq
|
394
|
+
<b>%name%</b>%params%
|
395
|
+
ENDIF:callseq
|
396
|
+
IF:codeurl
|
397
|
+
<a href="%codeurl%" target="source" class="srclink">src</a>
|
398
|
+
ENDIF:codeurl
|
399
|
+
</a></td></tr>
|
400
|
+
</table>
|
401
|
+
IF:m_desc
|
402
|
+
<div class="description">
|
403
|
+
%m_desc%
|
404
|
+
</div>
|
405
|
+
ENDIF:m_desc
|
406
|
+
IF:aka
|
407
|
+
<div class="aka">
|
408
|
+
This method is also aliased as
|
409
|
+
START:aka
|
410
|
+
<a href="%aref%">%name%</a>
|
411
|
+
END:aka
|
412
|
+
</div>
|
413
|
+
ENDIF:aka
|
414
|
+
IF:sourcecode
|
415
|
+
<div class="sourcecode">
|
416
|
+
<p class="source-link">[ <a href="javascript:toggleSource('%aref%_source')" id="l_%aref%_source">show source</a> ]</p>
|
417
|
+
<div id="%aref%_source" class="dyn-source">
|
418
|
+
<pre>
|
419
|
+
%sourcecode%
|
420
|
+
</pre>
|
421
|
+
</div>
|
422
|
+
</div>
|
423
|
+
ENDIF:sourcecode
|
424
|
+
END:methods
|
425
|
+
ENDIF:methods
|
426
|
+
END:method_list
|
427
|
+
ENDIF:method_list
|
428
|
+
}
|
429
|
+
|
430
|
+
|
431
|
+
########################## Index ################################
|
432
|
+
|
433
|
+
FR_INDEX_BODY = %{
|
434
|
+
!INCLUDE!
|
435
|
+
}
|
436
|
+
|
437
|
+
FILE_INDEX = %{
|
438
|
+
<html>
|
439
|
+
<head>
|
440
|
+
<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
|
441
|
+
<style>
|
442
|
+
<!--
|
443
|
+
body {
|
444
|
+
background-color: #ddddff;
|
445
|
+
font-family: #{FONTS};
|
446
|
+
font-size: 11px;
|
447
|
+
font-style: normal;
|
448
|
+
line-height: 14px;
|
449
|
+
color: #000040;
|
450
|
+
}
|
451
|
+
div.banner {
|
452
|
+
background: #0000aa;
|
453
|
+
color: white;
|
454
|
+
padding: 1;
|
455
|
+
margin: 0;
|
456
|
+
font-size: 90%;
|
457
|
+
font-weight: bold;
|
458
|
+
line-height: 1.1;
|
459
|
+
text-align: center;
|
460
|
+
width: 100%;
|
461
|
+
}
|
462
|
+
|
463
|
+
-->
|
464
|
+
</style>
|
465
|
+
<base target="docwin">
|
466
|
+
</head>
|
467
|
+
<body>
|
468
|
+
<div class="banner">%list_title%</div>
|
469
|
+
START:entries
|
470
|
+
<a href="%href%">%name%</a><br>
|
471
|
+
END:entries
|
472
|
+
</body></html>
|
473
|
+
}
|
474
|
+
|
475
|
+
CLASS_INDEX = FILE_INDEX
|
476
|
+
METHOD_INDEX = FILE_INDEX
|
477
|
+
|
478
|
+
INDEX = %{
|
479
|
+
<HTML>
|
480
|
+
<HEAD>
|
481
|
+
<META HTTP-EQUIV="refresh" content="0;URL=%initial_page%">
|
482
|
+
<TITLE>%realtitle%</TITLE>
|
483
|
+
</HEAD>
|
484
|
+
<BODY>
|
485
|
+
Click <a href="%initial_page%">here</a> to open the Camping docs.
|
486
|
+
</BODY>
|
487
|
+
</HTML>
|
488
|
+
}
|
489
|
+
|
490
|
+
end
|
491
|
+
end
|
Binary file
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# == About camping/ar/session.rb
|
2
|
+
#
|
3
|
+
# This file contains two modules which supply basic sessioning to your Camping app.
|
4
|
+
# Again, we're dealing with a pretty little bit of code: approx. 60 lines.
|
5
|
+
#
|
6
|
+
# * Camping::Models::Session is a module which adds a single <tt>sessions</tt> table
|
7
|
+
# to your database.
|
8
|
+
# * Camping::ARSession is a module which you will mix into your application (or into
|
9
|
+
# specific controllers which require sessions) to supply a <tt>@state</tt> variable
|
10
|
+
# you can use in controllers and views.
|
11
|
+
#
|
12
|
+
# For a basic tutorial, see the *Getting Started* section of the Camping::ARSession module.
|
13
|
+
require 'camping'
|
14
|
+
require 'camping/ar'
|
15
|
+
|
16
|
+
module Camping::Models
|
17
|
+
# A database table for storing Camping sessions. Contains a unique 32-character hashid, a
|
18
|
+
# creation timestamp, and a column of serialized data called <tt>ivars</tt>.
|
19
|
+
class Session < Base
|
20
|
+
serialize :ivars
|
21
|
+
set_primary_key :hashid
|
22
|
+
|
23
|
+
def []=(k, v) # :nodoc:
|
24
|
+
self.ivars[k] = v
|
25
|
+
end
|
26
|
+
def [](k) # :nodoc:
|
27
|
+
self.ivars[k] rescue nil
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
RAND_CHARS = [*'A'..'Z'] + [*'0'..'9'] + [*'a'..'z']
|
32
|
+
def before_create
|
33
|
+
rand_max = RAND_CHARS.size
|
34
|
+
sid = (0...32).inject("") { |ret,_| ret << RAND_CHARS[rand(rand_max)] }
|
35
|
+
write_attribute('hashid', sid)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Generates a new session ID and creates a row for the new session in the database.
|
39
|
+
def self.generate cookies
|
40
|
+
sess = Session.create :ivars => Camping::H[]
|
41
|
+
cookies.camping_sid = sess.hashid
|
42
|
+
sess
|
43
|
+
end
|
44
|
+
|
45
|
+
# Gets the existing session based on the <tt>camping_sid</tt> available in cookies.
|
46
|
+
# If none is found, generates a new session.
|
47
|
+
def self.persist cookies
|
48
|
+
session = nil
|
49
|
+
if cookies.camping_sid
|
50
|
+
session = Camping::Models::Session.find_by_hashid cookies.camping_sid
|
51
|
+
end
|
52
|
+
unless session
|
53
|
+
session = Camping::Models::Session.generate cookies
|
54
|
+
end
|
55
|
+
session
|
56
|
+
end
|
57
|
+
|
58
|
+
# Builds the session table in the database. To be used in your application's
|
59
|
+
# <tt>create</tt> method.
|
60
|
+
#
|
61
|
+
# Like so:
|
62
|
+
#
|
63
|
+
# def Blog.create
|
64
|
+
# Camping::Models::Session.create_schema
|
65
|
+
# unless Blog::Models::Post.table_exists?
|
66
|
+
# ActiveRecord::Schema.define(&Blog::Models.schema)
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
def self.create_schema
|
71
|
+
unless table_exists?
|
72
|
+
ActiveRecord::Schema.define do
|
73
|
+
create_table :sessions, :force => true, :id => false do |t|
|
74
|
+
t.column :hashid, :string, :limit => 32, :null => false
|
75
|
+
t.column :created_at, :datetime
|
76
|
+
t.column :ivars, :text
|
77
|
+
end
|
78
|
+
add_index :sessions, [:hashid], :unique => true
|
79
|
+
end
|
80
|
+
reset_column_information
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
Session.partial_updates = false if Session.respond_to?(:partial_updates=)
|
85
|
+
end
|
86
|
+
|
87
|
+
module Camping
|
88
|
+
# The Camping::ARSession module is designed to be mixed into your application or into specific
|
89
|
+
# controllers which require sessions. This module defines a <tt>service</tt> method which
|
90
|
+
# intercepts all requests handed to those controllers.
|
91
|
+
#
|
92
|
+
# == Getting Started
|
93
|
+
#
|
94
|
+
# To get sessions working for your application:
|
95
|
+
#
|
96
|
+
# 1. <tt>require 'camping/session'</tt>
|
97
|
+
# 2. Mixin the module: <tt>module YourApp; include Camping::ARSession end</tt>
|
98
|
+
# 3. In your application's <tt>create</tt> method, add a call to <tt>Camping::Models::Session.create_schema</tt>
|
99
|
+
# 4. Throughout your application, use the <tt>@state</tt> var like a hash to store your application's data.
|
100
|
+
#
|
101
|
+
# If you are unfamiliar with the <tt>create</tt> method, see
|
102
|
+
# http://code.whytheluckystiff.net/camping/wiki/GiveUsTheCreateMethod.
|
103
|
+
#
|
104
|
+
# == A Few Notes
|
105
|
+
#
|
106
|
+
# * The session ID is stored in a cookie. Look in <tt>@cookies.camping_sid</tt>.
|
107
|
+
# * The session data is stored in the <tt>sessions</tt> table in your database.
|
108
|
+
# * All mounted Camping apps using this class will use the same database table.
|
109
|
+
# * However, your application's data is stored in its own hash.
|
110
|
+
# * Session data is only saved if it has changed.
|
111
|
+
module ARSession
|
112
|
+
# This <tt>service</tt> method, when mixed into controllers, intercepts requests
|
113
|
+
# and wraps them with code to start and close the session. If a session isn't found
|
114
|
+
# in the database it is created. The <tt>@state</tt> variable is set and if it changes,
|
115
|
+
# it is saved back into the database.
|
116
|
+
def service(*a)
|
117
|
+
session = Camping::Models::Session.persist @cookies
|
118
|
+
app = self.class.name.gsub(/^(\w+)::.+$/, '\1')
|
119
|
+
@state = (session[app] ||= Camping::H[])
|
120
|
+
hash_before = Marshal.dump(@state).hash
|
121
|
+
return super(*a)
|
122
|
+
ensure
|
123
|
+
if session
|
124
|
+
hash_after = Marshal.dump(@state).hash
|
125
|
+
unless hash_before == hash_after
|
126
|
+
session[app] = @state
|
127
|
+
session.save
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
class MissingLibrary < Exception #:nodoc: all
|
2
|
+
end
|
3
|
+
begin
|
4
|
+
require 'active_record'
|
5
|
+
rescue LoadError => e
|
6
|
+
raise MissingLibrary, "ActiveRecord could not be loaded (is it installed?): #{e.message}"
|
7
|
+
end
|
8
|
+
|
9
|
+
$AR_EXTRAS = %{
|
10
|
+
Base = ActiveRecord::Base unless const_defined? :Base
|
11
|
+
|
12
|
+
def Y; ActiveRecord::Base.verify_active_connections!; self; end
|
13
|
+
|
14
|
+
class SchemaInfo < Base
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.V(n)
|
18
|
+
@final = [n, @final.to_i].max
|
19
|
+
m = (@migrations ||= [])
|
20
|
+
Class.new(ActiveRecord::Migration) do
|
21
|
+
meta_def(:version) { n }
|
22
|
+
meta_def(:inherited) { |k| m << k }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.create_schema(opts = {})
|
27
|
+
opts[:assume] ||= 0
|
28
|
+
opts[:version] ||= @final
|
29
|
+
if @migrations
|
30
|
+
unless SchemaInfo.table_exists?
|
31
|
+
ActiveRecord::Schema.define do
|
32
|
+
create_table SchemaInfo.table_name do |t|
|
33
|
+
t.column :version, :float
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
si = SchemaInfo.find(:first) || SchemaInfo.new(:version => opts[:assume])
|
39
|
+
if si.version < opts[:version]
|
40
|
+
@migrations.each do |k|
|
41
|
+
k.migrate(:up) if si.version < k.version and k.version <= opts[:version]
|
42
|
+
k.migrate(:down) if si.version > k.version and k.version > opts[:version]
|
43
|
+
end
|
44
|
+
si.update_attributes(:version => opts[:version])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
}
|
49
|
+
|
50
|
+
module Camping
|
51
|
+
module Models
|
52
|
+
A = ActiveRecord
|
53
|
+
# Base is an alias for ActiveRecord::Base. The big warning I'm going to give you
|
54
|
+
# about this: *Base overloads table_name_prefix.* This means that if you have a
|
55
|
+
# model class Blog::Models::Post, it's table name will be <tt>blog_posts</tt>.
|
56
|
+
#
|
57
|
+
# ActiveRecord is not loaded if you never reference this class. The minute you
|
58
|
+
# use the ActiveRecord or Camping::Models::Base class, then the ActiveRecord library
|
59
|
+
# is loaded.
|
60
|
+
Base = A::Base
|
61
|
+
|
62
|
+
# The default prefix for Camping model classes is the topmost module name lowercase
|
63
|
+
# and followed with an underscore.
|
64
|
+
#
|
65
|
+
# Tepee::Models::Page.table_name_prefix
|
66
|
+
# #=> "tepee_pages"
|
67
|
+
#
|
68
|
+
def Base.table_name_prefix
|
69
|
+
"#{name[/\w+/]}_".downcase.sub(/^(#{A}|camping)_/i,'')
|
70
|
+
end
|
71
|
+
module_eval $AR_EXTRAS
|
72
|
+
end
|
73
|
+
end
|
74
|
+
Camping::S.sub! /autoload\s*:Base\s*,\s*['"]camping\/ar['"]/, ""
|
75
|
+
Camping::S.sub! /def\s*Y[;\s]*self[;\s]*end/, $AR_EXTRAS
|
76
|
+
Camping::Apps.each do |c|
|
77
|
+
c::Models.module_eval $AR_EXTRAS
|
78
|
+
end
|