nitro 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +8 -0
- data/ChangeLog +1546 -0
- data/LICENCE +32 -0
- data/README +278 -0
- data/RELEASES +7 -0
- data/Rakefile +79 -0
- data/bin/cluster.rb +219 -0
- data/doc/architecture.txt +28 -0
- data/doc/bugs.txt +7 -0
- data/doc/css.txt +20 -0
- data/doc/ideas.txt +120 -0
- data/doc/pg.txt +47 -0
- data/doc/svn.txt +82 -0
- data/doc/todo.txt +30 -0
- data/etc/new-project.rb +18 -0
- data/examples/simple/README +15 -0
- data/examples/simple/app.rb +31 -0
- data/examples/simple/conf/apache.conf +100 -0
- data/examples/simple/conf/config.rb +89 -0
- data/examples/simple/conf/debug-config.rb +53 -0
- data/examples/simple/conf/live-config.rb +48 -0
- data/examples/simple/conf/overrides.rb +9 -0
- data/examples/simple/conf/requires.rb +51 -0
- data/examples/simple/ctl +32 -0
- data/examples/simple/env.rb +33 -0
- data/examples/simple/install.rb +12 -0
- data/examples/simple/lib/articles/entities.rb +35 -0
- data/examples/simple/lib/articles/lc-en.rb +36 -0
- data/examples/simple/lib/articles/methods.rb +55 -0
- data/examples/simple/lib/articles/part.rb +58 -0
- data/examples/simple/logs/access_log +2 -0
- data/examples/simple/logs/apache.log +3 -0
- data/examples/simple/logs/app.log +1 -0
- data/examples/simple/logs/events.log +1 -0
- data/examples/simple/root/add-article.sx +15 -0
- data/examples/simple/root/article-form.ss +20 -0
- data/examples/simple/root/comments-form.ss +16 -0
- data/examples/simple/root/comments.si +30 -0
- data/examples/simple/root/index.sx +44 -0
- data/examples/simple/root/shader/shader.xsl +100 -0
- data/examples/simple/root/shader/style.css +9 -0
- data/examples/simple/root/view-article.sx +30 -0
- data/examples/tiny/app.rb +30 -0
- data/examples/tiny/conf/apache.conf +100 -0
- data/examples/tiny/conf/config.rb +67 -0
- data/examples/tiny/conf/requires.rb +40 -0
- data/examples/tiny/ctl +31 -0
- data/examples/tiny/logs/access_log +9 -0
- data/examples/tiny/logs/apache.log +9 -0
- data/examples/tiny/root/index.sx +35 -0
- data/lib/n/app/cluster.rb +219 -0
- data/lib/n/app/cookie.rb +86 -0
- data/lib/n/app/filters/autologin.rb +50 -0
- data/lib/n/app/fragment.rb +67 -0
- data/lib/n/app/handlers.rb +120 -0
- data/lib/n/app/handlers/code-handler.rb +184 -0
- data/lib/n/app/handlers/page-handler.rb +612 -0
- data/lib/n/app/request-part.rb +59 -0
- data/lib/n/app/request.rb +653 -0
- data/lib/n/app/script.rb +398 -0
- data/lib/n/app/server.rb +53 -0
- data/lib/n/app/session.rb +224 -0
- data/lib/n/app/user.rb +47 -0
- data/lib/n/app/webrick-servlet.rb +213 -0
- data/lib/n/app/webrick.rb +70 -0
- data/lib/n/application.rb +187 -0
- data/lib/n/config.rb +31 -0
- data/lib/n/db.rb +217 -0
- data/lib/n/db/README +232 -0
- data/lib/n/db/connection.rb +369 -0
- data/lib/n/db/make-release.sh +26 -0
- data/lib/n/db/managed.rb +235 -0
- data/lib/n/db/mixins.rb +282 -0
- data/lib/n/db/mysql.rb +342 -0
- data/lib/n/db/psql.rb +378 -0
- data/lib/n/db/tools.rb +110 -0
- data/lib/n/db/utils.rb +99 -0
- data/lib/n/events.rb +118 -0
- data/lib/n/l10n.rb +22 -0
- data/lib/n/logger.rb +33 -0
- data/lib/n/macros.rb +53 -0
- data/lib/n/mixins.rb +46 -0
- data/lib/n/parts.rb +154 -0
- data/lib/n/properties.rb +194 -0
- data/lib/n/server.rb +61 -0
- data/lib/n/server/PLAYBACK.txt +8 -0
- data/lib/n/server/RESEARCH.txt +13 -0
- data/lib/n/server/filter.rb +77 -0
- data/lib/n/shaders.rb +167 -0
- data/lib/n/sitemap.rb +188 -0
- data/lib/n/std.rb +69 -0
- data/lib/n/sync/clc.rb +108 -0
- data/lib/n/sync/handler.rb +221 -0
- data/lib/n/sync/server.rb +170 -0
- data/lib/n/tools/README +11 -0
- data/lib/n/ui/date-select.rb +74 -0
- data/lib/n/ui/pager.rb +187 -0
- data/lib/n/ui/popup.rb +45 -0
- data/lib/n/ui/select.rb +41 -0
- data/lib/n/ui/tabs.rb +34 -0
- data/lib/n/utils/array.rb +92 -0
- data/lib/n/utils/cache.rb +144 -0
- data/lib/n/utils/gfx.rb +108 -0
- data/lib/n/utils/hash.rb +148 -0
- data/lib/n/utils/html.rb +147 -0
- data/lib/n/utils/http.rb +98 -0
- data/lib/n/utils/mail.rb +28 -0
- data/lib/n/utils/number.rb +31 -0
- data/lib/n/utils/pool.rb +66 -0
- data/lib/n/utils/string.rb +297 -0
- data/lib/n/utils/template.rb +38 -0
- data/lib/n/utils/time.rb +91 -0
- data/lib/n/utils/uri.rb +193 -0
- data/lib/xsl/base.xsl +205 -0
- data/lib/xsl/ce.xsl +30 -0
- data/lib/xsl/localization.xsl +23 -0
- data/lib/xsl/xforms.xsl +26 -0
- data/test/run.rb +95 -0
- metadata +187 -0
data/lib/n/ui/pager.rb
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
# = Pager
|
2
|
+
#
|
3
|
+
# code: gmosx
|
4
|
+
#
|
5
|
+
# (c) 2004 Navel, all rights reserved.
|
6
|
+
# $Id: pager.rb 71 2004-10-18 10:50:22Z gmosx $
|
7
|
+
|
8
|
+
module N; module UI
|
9
|
+
|
10
|
+
# === Design:
|
11
|
+
#
|
12
|
+
# The new version is carefully designed for scaleability. It stores
|
13
|
+
# only the items for one page.
|
14
|
+
# The name parameter is needed, multiple pagers can coexist in
|
15
|
+
# a single page. Unlike the v1/v2 pagers this pager leverages the
|
16
|
+
# SQL LIMIT option to optimize database interaction.
|
17
|
+
#
|
18
|
+
# The pager does not extend Array (it includes an Array instead) to
|
19
|
+
# avoid a concat() in the initialization step.
|
20
|
+
#
|
21
|
+
# TODO:
|
22
|
+
# - Extend from array? (would be more elegant)
|
23
|
+
#
|
24
|
+
# === Example:
|
25
|
+
#
|
26
|
+
# INVESTIGATE:
|
27
|
+
# mysql> SELECT SQL_CALC_FOUND_ROWS * FROM tbl_name
|
28
|
+
# -> WHERE id > 100 LIMIT 10;
|
29
|
+
# mysql> SELECT FOUND_ROWS();
|
30
|
+
#
|
31
|
+
class Pager < Array
|
32
|
+
attr_accessor :name, :idx, :page, :page_count
|
33
|
+
# total count of items
|
34
|
+
attr_accessor :total_count
|
35
|
+
# page items
|
36
|
+
attr_accessor :page_items
|
37
|
+
# read needed variables from the request.
|
38
|
+
attr_accessor :request
|
39
|
+
|
40
|
+
def initialize(name, request, items_per_page, items = nil)
|
41
|
+
raise "items_per_page should be > 0" unless items_per_page > 0
|
42
|
+
|
43
|
+
@request, @name = request, name
|
44
|
+
@page = request.get("__pg#{@name}", 1)
|
45
|
+
@items_per_page = items_per_page
|
46
|
+
@start_idx = (@page - 1) * items_per_page
|
47
|
+
|
48
|
+
if items
|
49
|
+
set(items.size())
|
50
|
+
# gmosx, FIXME: not exactly what i want!
|
51
|
+
items.slice!(@start_idx, @items_per_page)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def set(total_count)
|
56
|
+
@total_count = total_count
|
57
|
+
@page_count = (@total_count.to_f() / @items_per_page).ceil()
|
58
|
+
end
|
59
|
+
|
60
|
+
def first_page
|
61
|
+
return 1
|
62
|
+
end
|
63
|
+
|
64
|
+
def last_page
|
65
|
+
return @page_count
|
66
|
+
end
|
67
|
+
|
68
|
+
def previous_page
|
69
|
+
return [@page - 1, 1].max()
|
70
|
+
end
|
71
|
+
|
72
|
+
def next_page
|
73
|
+
return [@page + 1, @page_count].min()
|
74
|
+
end
|
75
|
+
|
76
|
+
# Iterator
|
77
|
+
#
|
78
|
+
def each(&block)
|
79
|
+
@page_items.each(&block)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Iterator
|
83
|
+
# Returns 1-based index.
|
84
|
+
#
|
85
|
+
def each_with_index
|
86
|
+
idx = @start_idx
|
87
|
+
for item in @page_items
|
88
|
+
yield(idx + 1, item)
|
89
|
+
idx += 1
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def empty?
|
94
|
+
return @items.empty?
|
95
|
+
end
|
96
|
+
|
97
|
+
def size
|
98
|
+
return @items.size()
|
99
|
+
end
|
100
|
+
|
101
|
+
# Returns the range of the current page.
|
102
|
+
#
|
103
|
+
def page_range
|
104
|
+
s = @idx
|
105
|
+
e = [@idx + @items_per_page - 1, all_total_count].min
|
106
|
+
|
107
|
+
return [s, e]
|
108
|
+
end
|
109
|
+
|
110
|
+
# Override if needed.
|
111
|
+
#
|
112
|
+
def nav_range
|
113
|
+
# effective range = 10 pages.
|
114
|
+
s = [@page - 5, 1].max()
|
115
|
+
e = [@page + 9, @page_count].min()
|
116
|
+
|
117
|
+
d = 9 - (e - s)
|
118
|
+
e += d if d < 0
|
119
|
+
|
120
|
+
return (s..e)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Override this method in your application
|
124
|
+
# if needed.
|
125
|
+
# TODO: better markup.
|
126
|
+
#
|
127
|
+
def navigation
|
128
|
+
nav = ""
|
129
|
+
|
130
|
+
unless @page == first_page()
|
131
|
+
nav << %{
|
132
|
+
<div class="first"><a href="#{target_uri(first_page())}">����</a></div>
|
133
|
+
<div class="previous"><a href="#{target_uri(previous_page())}">�����������</a></div>
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
unless @page == last_page()
|
138
|
+
nav << %{
|
139
|
+
<div class="last"><a href="#{target_uri(last_page())}">�����</a></div>
|
140
|
+
<div class="next"><a href="#{target_uri(next_page())}">�������</a></div>
|
141
|
+
}
|
142
|
+
end
|
143
|
+
|
144
|
+
nav << %{<ul>}
|
145
|
+
|
146
|
+
for i in nav_range()
|
147
|
+
if i == @page
|
148
|
+
nav << %{
|
149
|
+
<li class="active">#{i}</li>
|
150
|
+
}
|
151
|
+
else
|
152
|
+
nav << %{
|
153
|
+
<li><a href="#{target_uri(i)}">#{i}</a></li>
|
154
|
+
}
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
nav << %{</ul>}
|
159
|
+
|
160
|
+
return nav
|
161
|
+
end
|
162
|
+
|
163
|
+
# Create an appropriate SQL limit clause.
|
164
|
+
# Returns postgres/mysql compatible limit.
|
165
|
+
#
|
166
|
+
def sql_limit
|
167
|
+
if @start_idx > 0
|
168
|
+
return "LIMIT #{@items_per_page} OFFSET #{@start_idx}"
|
169
|
+
else
|
170
|
+
# gmosx: perhaps this is optimized ? naaaaaah...
|
171
|
+
return "LIMIT #{@items_per_page}"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# ------------------------------------------------------------------
|
176
|
+
|
177
|
+
# Generate the target URI.
|
178
|
+
#
|
179
|
+
def target_uri(page)
|
180
|
+
params = {"__pg#{@name}" => page}
|
181
|
+
return N::UriUtils.update_query_string(@request.uri, params)
|
182
|
+
end
|
183
|
+
private :target_uri
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
end; end # module
|
data/lib/n/ui/popup.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# = Popup
|
2
|
+
#
|
3
|
+
# code: gmosx
|
4
|
+
#
|
5
|
+
# (c) 2004 Navel, all rights reserved.
|
6
|
+
# $Id: popup.rb 71 2004-10-18 10:50:22Z gmosx $
|
7
|
+
|
8
|
+
module N; module UI
|
9
|
+
|
10
|
+
# = Popup
|
11
|
+
#
|
12
|
+
class Popup
|
13
|
+
|
14
|
+
# Emit the needed javascript.
|
15
|
+
# Alternatively use this code: <script src="/r/js/std.js">#{}</script>
|
16
|
+
#
|
17
|
+
def self.script
|
18
|
+
%[
|
19
|
+
function newWin(url, name, w, h, scroll) {
|
20
|
+
var pleft = (screen.width - w) / 2;
|
21
|
+
var ptop = (screen.height - h) / 2;
|
22
|
+
var settings = 'height=' + h + ',width=' + w + ',top=' + ptop + ',left=' + pleft + ',scrollbars=' + scroll + ',resizable';
|
23
|
+
|
24
|
+
win = window.open(url, name, settings);
|
25
|
+
|
26
|
+
return false;
|
27
|
+
}
|
28
|
+
]
|
29
|
+
end
|
30
|
+
|
31
|
+
# gmosx: keep the leading / to be IE friendly.
|
32
|
+
#
|
33
|
+
def self.onclick(uri, width, height, title = "Popup", type="PAGE", options = "scrollbars=yes,resizable", container = "/p/glue/popup.sx")
|
34
|
+
%[javascript: var pwl = (screen.width - #{width}) / 2; var pwt = (screen.height - #{height}) / 2; window.open('#{container}?uri=#{uri};type=#{type}', '#{title}', 'width=#{width},height=#{height},top='+pwt+',left='+pwl+', #{options}'); return false"]
|
35
|
+
end
|
36
|
+
|
37
|
+
# gmosx: keep the leading / to be IE friendly.
|
38
|
+
#
|
39
|
+
def self.link(uri, width, height, link = "link", title = "Popup", type="PAGE", options = "scrollbars=yes,resizable", container = "/p/glue/popup.sx")
|
40
|
+
%[<a href="#" onclick="#{self.onclick(uri, width, height, title, type, options, container)}">#{link}</a>]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end; end # module
|
45
|
+
|
data/lib/n/ui/select.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# = Select
|
2
|
+
#
|
3
|
+
# code: gmosx
|
4
|
+
#
|
5
|
+
# (c) 2004 Navel, all rights reserved.
|
6
|
+
# $Id: select.rb 71 2004-10-18 10:50:22Z gmosx $
|
7
|
+
|
8
|
+
module N; module UI
|
9
|
+
|
10
|
+
class Select
|
11
|
+
|
12
|
+
# Generalized select. Dont create the enclosing selects to
|
13
|
+
# be flexible.
|
14
|
+
# Example:
|
15
|
+
#
|
16
|
+
# <select name="param" onchange="submit()">
|
17
|
+
# #{N::UI::Select.render_options(
|
18
|
+
# paramvalue,
|
19
|
+
# [-1, 1, 2, 3],
|
20
|
+
# ["-- Enter Options --", "Man", "Woman", "None"]
|
21
|
+
# )}
|
22
|
+
# </select>
|
23
|
+
#
|
24
|
+
def self.render_options(paramvalue, values, options)
|
25
|
+
str = ""
|
26
|
+
|
27
|
+
values.each_with_index { |val, idx|
|
28
|
+
if paramvalue == val
|
29
|
+
str << %{<option value="#{val}" selected="1">#{options[idx]}</option>}
|
30
|
+
else
|
31
|
+
str << %{<option value="#{val}">#{options[idx]}</option>}
|
32
|
+
end
|
33
|
+
}
|
34
|
+
|
35
|
+
return str
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end; end # module
|
41
|
+
|
data/lib/n/ui/tabs.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# = Tabs
|
2
|
+
#
|
3
|
+
# code: gmosx
|
4
|
+
#
|
5
|
+
# (c) 2004 Navel, all rights reserved.
|
6
|
+
# $Id: tabs.rb 71 2004-10-18 10:50:22Z gmosx $
|
7
|
+
|
8
|
+
module N; module UI
|
9
|
+
|
10
|
+
# Render a tabs bar
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
#
|
14
|
+
# #{N::UI.tabs(request,
|
15
|
+
# ["Page1", "Page2", "Page3"]
|
16
|
+
# }
|
17
|
+
#
|
18
|
+
def self.tabs(request, options, param)
|
19
|
+
tabs = []
|
20
|
+
|
21
|
+
selected = request.get(param, 0)
|
22
|
+
|
23
|
+
options.each_with_index { |opt, idx|
|
24
|
+
if idx == selected
|
25
|
+
tabs << %|<strong>#{opt}</strong>|
|
26
|
+
else
|
27
|
+
tabs << %|<a href="#{request.expand_uri(param => idx)}">#{opt}</a>|
|
28
|
+
end
|
29
|
+
}
|
30
|
+
|
31
|
+
return %|<div class="tabs">#{tabs.join('<span class="sep">|</span>')}</div>|
|
32
|
+
end
|
33
|
+
|
34
|
+
end; end # module
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# = General arrays utilities collection
|
2
|
+
#
|
3
|
+
# === Design:
|
4
|
+
#
|
5
|
+
# Implement as a module to avoid class polution. You can still Ruby's
|
6
|
+
# advanced features to include the module in your class.
|
7
|
+
# Passing the object to act upon allows to check for nil, which isn't
|
8
|
+
# possible if you use self.
|
9
|
+
#
|
10
|
+
# code:: gmosx, ekarak
|
11
|
+
#
|
12
|
+
# (c) 2002-2003 Navel, all rights reserved.
|
13
|
+
# $Id: array.rb 71 2004-10-18 10:50:22Z gmosx $
|
14
|
+
|
15
|
+
require "sync"
|
16
|
+
|
17
|
+
module N
|
18
|
+
|
19
|
+
# == SafeArray
|
20
|
+
#
|
21
|
+
# A thread-safe array. We use a sync object instead of a mutex,
|
22
|
+
# because it is re-entrant.
|
23
|
+
# An exclusive lock is needed when writing, a shared lock IS NEEDED
|
24
|
+
# when reading
|
25
|
+
|
26
|
+
class SafeArray < Array
|
27
|
+
|
28
|
+
attr :sync
|
29
|
+
|
30
|
+
# gmosx: delegator is not used.
|
31
|
+
|
32
|
+
def initialize(delegator = nil)
|
33
|
+
@sync = ::Sync.new()
|
34
|
+
end
|
35
|
+
|
36
|
+
def << (value)
|
37
|
+
return @sync.synchronize(::Sync::SH) {
|
38
|
+
super
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def delete_if(&block)
|
43
|
+
return @sync.synchronize(::Sync::SH) {
|
44
|
+
super
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def [](key)
|
49
|
+
return @sync.synchronize(::Sync::SH) {
|
50
|
+
super
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def []=(key, value)
|
55
|
+
return @sync.synchronize(::Sync::EX) {
|
56
|
+
super
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def delete(key)
|
61
|
+
return @sync.synchronize(::Sync::EX) {
|
62
|
+
super
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def clear
|
67
|
+
@sync.synchronize(::Sync::EX) {
|
68
|
+
super
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def size
|
73
|
+
return @sync.synchronize(::Sync::SH) {
|
74
|
+
super
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def shift
|
79
|
+
return @sync.synchronize(::Sync::EX) {
|
80
|
+
super
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
def unshift(el)
|
85
|
+
return @sync.synchronize(::Sync::EX) {
|
86
|
+
super
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
end # SafeArray
|
91
|
+
|
92
|
+
end # module
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# = Cache
|
2
|
+
#
|
3
|
+
# Various caching mechanisms.
|
4
|
+
#
|
5
|
+
# code:
|
6
|
+
# George Moschovitis <gm@navel.gr>
|
7
|
+
# Anastasios Koutoumanos <ak@navel.gr>
|
8
|
+
#
|
9
|
+
# (c) 2004 Navel, all rights reserved.
|
10
|
+
# $Id: cache.rb 71 2004-10-18 10:50:22Z gmosx $
|
11
|
+
|
12
|
+
module N;
|
13
|
+
|
14
|
+
# = LRUCache
|
15
|
+
#
|
16
|
+
# A cache utilizing a simple LRU (Least Recently Used) policy.
|
17
|
+
# The items managed by this cache must respond to the #key method.
|
18
|
+
# Attempts to optimize reads rather than inserts!
|
19
|
+
#
|
20
|
+
# LRU semantics are enforced by inserting the items in a queue.
|
21
|
+
# The lru item is always at the tail. Two special sentinels
|
22
|
+
# (head, tail) are used to simplify (?) the code.
|
23
|
+
#
|
24
|
+
class LRUCache < Hash
|
25
|
+
|
26
|
+
# Mix this in your class to make LRU-managable.
|
27
|
+
#
|
28
|
+
module Item
|
29
|
+
attr_accessor :lru_key, :lru_prev, :lru_next
|
30
|
+
end
|
31
|
+
|
32
|
+
# head-tail sentinels
|
33
|
+
#
|
34
|
+
class Sentinel; include Item; end
|
35
|
+
|
36
|
+
# the maximum number of items in the cache
|
37
|
+
attr_accessor :max_items
|
38
|
+
|
39
|
+
# the head sentinel
|
40
|
+
attr :head
|
41
|
+
# the tail sentinel, tail.prev points to the lru item.
|
42
|
+
attr :tail
|
43
|
+
|
44
|
+
#
|
45
|
+
#
|
46
|
+
def initialize(max_items)
|
47
|
+
@max_items = max_items
|
48
|
+
lru_clear()
|
49
|
+
end
|
50
|
+
|
51
|
+
# Lookup an item in the cache
|
52
|
+
#
|
53
|
+
def [](key)
|
54
|
+
if item = super
|
55
|
+
return lru_touch(item)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# The inserted item is considered mru!
|
60
|
+
#
|
61
|
+
def []=(key, item)
|
62
|
+
item = super
|
63
|
+
item.lru_key = key
|
64
|
+
lru_insert(item)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Delete an item from the cache
|
68
|
+
#
|
69
|
+
def delete(key)
|
70
|
+
if item = super
|
71
|
+
lru_delete(item)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Clear the cache
|
76
|
+
#
|
77
|
+
def clear
|
78
|
+
super
|
79
|
+
lru_clear()
|
80
|
+
end
|
81
|
+
|
82
|
+
# The first (mru) element in the cache
|
83
|
+
#
|
84
|
+
def first
|
85
|
+
@head.lru_next
|
86
|
+
end
|
87
|
+
|
88
|
+
# The last (lru) element in the cache
|
89
|
+
#
|
90
|
+
def last
|
91
|
+
@tail.lru_prev
|
92
|
+
end
|
93
|
+
alias_method :lru, :last
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
# Delete an item from the lru list.
|
98
|
+
#
|
99
|
+
def lru_delete(item)
|
100
|
+
lru_join(item.lru_prev, item.lru_next)
|
101
|
+
return item
|
102
|
+
end
|
103
|
+
|
104
|
+
# Join two items in the lru list
|
105
|
+
# Return y to allow for chaining
|
106
|
+
#
|
107
|
+
def lru_join(x, y)
|
108
|
+
x.lru_next = y
|
109
|
+
y.lru_prev = x
|
110
|
+
return y
|
111
|
+
end
|
112
|
+
|
113
|
+
# Append a child item to a parent item in the lru list
|
114
|
+
# (Re)inserts the child in the list.
|
115
|
+
#
|
116
|
+
def lru_append(parent, child)
|
117
|
+
lru_join(child, parent.lru_next)
|
118
|
+
lru_join(parent, child)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Insert an item
|
122
|
+
#
|
123
|
+
def lru_insert(item)
|
124
|
+
delete(last.lru_key) if size() > @max_items
|
125
|
+
lru_append(@head, item)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Touch an item, make mru!
|
129
|
+
# Returns the item
|
130
|
+
#
|
131
|
+
def lru_touch(item)
|
132
|
+
lru_append(@head, lru_delete(item))
|
133
|
+
end
|
134
|
+
|
135
|
+
# Clear the lru
|
136
|
+
#
|
137
|
+
def lru_clear
|
138
|
+
@head = Sentinel.new
|
139
|
+
@tail = Sentinel.new
|
140
|
+
lru_join(@head, @tail)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
end # module
|