bluemark-smallcage 0.1.3
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/History.txt +48 -0
- data/License.txt +20 -0
- data/README.txt +1 -0
- data/Rakefile +140 -0
- data/bin/smc +16 -0
- data/lib/smallcage.rb +18 -0
- data/lib/smallcage/application.rb +164 -0
- data/lib/smallcage/commands/auto.rb +158 -0
- data/lib/smallcage/commands/base.rb +19 -0
- data/lib/smallcage/commands/clean.rb +32 -0
- data/lib/smallcage/commands/export.rb +41 -0
- data/lib/smallcage/commands/import.rb +217 -0
- data/lib/smallcage/commands/manifest.rb +39 -0
- data/lib/smallcage/commands/server.rb +15 -0
- data/lib/smallcage/commands/update.rb +121 -0
- data/lib/smallcage/document_path.rb +46 -0
- data/lib/smallcage/erb_base.rb +16 -0
- data/lib/smallcage/http_server.rb +66 -0
- data/lib/smallcage/loader.rb +278 -0
- data/lib/smallcage/misc.rb +13 -0
- data/lib/smallcage/renderer.rb +19 -0
- data/lib/smallcage/resources/Manifest.erb +19 -0
- data/lib/smallcage/resources/auto.html +119 -0
- data/lib/smallcage/runner.rb +51 -0
- data/lib/smallcage/version.rb +9 -0
- data/project/base/_smc/helpers/base_helper.rb +41 -0
- data/project/base/_smc/helpers/site_helper.rb +5 -0
- data/project/base/_smc/templates/default.rhtml +5 -0
- data/project/base/_smc/templates/footer.rhtml +0 -0
- data/project/base/_smc/templates/header.rhtml +0 -0
- data/project/bluecloth/_smc/helpers/blue_cloth_helper.rb +10 -0
- data/project/bluecloth/_smc/templates/markdown.rhtml +5 -0
- data/project/lang/_smc/helpers/lang_helper.rb +19 -0
- data/project/lang/_smc/templates/other_lang.rhtml +6 -0
- data/project/news/_smc/helpers/news_helper.rb +36 -0
- data/project/nkf/_smc/filters/filters.yml +3 -0
- data/project/nkf/_smc/filters/nkf_filter.rb +15 -0
- data/project/rake/_smc/Rakefile +68 -0
- data/project/redcloth/_smc/helpers/red_cloth_helper.rb +10 -0
- data/project/redcloth/_smc/templates/textile.rhtml +5 -0
- data/project/relpath/_smc/filters/filters.yml +2 -0
- data/project/relpath/_smc/filters/relpath_filter.rb +13 -0
- data/project/standard/_dir.smc +2 -0
- data/project/standard/_smc/helpers/base_helper.rb +34 -0
- data/project/standard/_smc/helpers/menu_helper.rb +23 -0
- data/project/standard/_smc/templates/default.rhtml +5 -0
- data/project/standard/_smc/templates/footer.rhtml +13 -0
- data/project/standard/_smc/templates/header.rhtml +37 -0
- data/project/standard/_smc/templates/menu.rhtml +6 -0
- data/project/standard/_smc/templates/redirect.rhtml +13 -0
- data/project/standard/_smc/templates/sidebar.rhtml +7 -0
- data/project/standard/_smc/templates/topic_path.rhtml +6 -0
- data/project/standard/common/css/default.css +145 -0
- data/project/standard/common/css/print.css +0 -0
- data/project/standard/index.html.smc +3 -0
- data/project/standard/sample/_dir.smc +1 -0
- data/project/standard/sample/index.html.smc +7 -0
- data/project/standard/sample/redirect.html.smc +2 -0
- data/project/standard/sample/sub/_dir.smc +1 -0
- data/project/standard/sample/sub/contents.html.smc +3 -0
- data/project/standard/sample/sub/index.html.smc +7 -0
- data/spec/data/htdocs1/_dir.smc +0 -0
- data/spec/data/htdocs1/a/b/c/index.html.smc +6 -0
- data/spec/data/htdocs2/_smc/templates/dummy.rhtml +0 -0
- data/spec/data/htdocs2/a/b/c/test.html +1 -0
- data/spec/data/htdocs2/a/b/test.html +1 -0
- data/spec/data/htdocs2/a/test.html.smc +2 -0
- data/spec/document_path_spec.rb +42 -0
- data/spec/export_spec.rb +45 -0
- data/spec/import_spec.rb +20 -0
- data/spec/loader_spec.rb +55 -0
- data/spec/manifest_spec.rb +39 -0
- data/spec/misc_spec.rb +25 -0
- data/spec/smallcage_spec.rb +40 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- data/test/test_helper.rb +2 -0
- data/test/test_smallcage.rb +11 -0
- metadata +193 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module SmallCage
|
|
2
|
+
class DocumentPath
|
|
3
|
+
|
|
4
|
+
attr_reader :root, :uri, :path
|
|
5
|
+
|
|
6
|
+
def initialize(root, path)
|
|
7
|
+
@root = Pathname.new(root).realpath;
|
|
8
|
+
|
|
9
|
+
@path = Pathname.new(path)
|
|
10
|
+
if @path.exist?
|
|
11
|
+
@path = @path.realpath
|
|
12
|
+
else
|
|
13
|
+
@path = @path.cleanpath
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
if @path.to_s[0...@root.to_s.length] != @root.to_s
|
|
17
|
+
raise "Illegal path: #{path.to_s}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
if @path == @root
|
|
21
|
+
@uri = "/"
|
|
22
|
+
else
|
|
23
|
+
@uri = @path.to_s[@root.to_s.length .. -1]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def smc?
|
|
28
|
+
return @path.extname == ".smc"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def outfile
|
|
32
|
+
return nil unless smc?
|
|
33
|
+
return self.class.new(@root, @path.to_s[0 .. -5])
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def outuri
|
|
37
|
+
return nil unless smc?
|
|
38
|
+
return uri[0 .. -5]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.to_uri(root, path)
|
|
42
|
+
return self.new(root,path).uri
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
class SmallCage::ErbBase
|
|
3
|
+
def initialize(loader, renderer, obj)
|
|
4
|
+
@loader, @renderer, @obj = loader, renderer, obj
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def method_missing(name)
|
|
8
|
+
n = name.to_s
|
|
9
|
+
|
|
10
|
+
return @obj[n] unless @obj[n].nil?
|
|
11
|
+
return @obj["strings"][0] if n == "body" && ! @obj["strings"][0].nil?
|
|
12
|
+
|
|
13
|
+
# render if template file exists. or return nil.
|
|
14
|
+
return @renderer.render(name, @obj)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require 'webrick'
|
|
2
|
+
|
|
3
|
+
module SmallCage
|
|
4
|
+
class HTTPServer
|
|
5
|
+
|
|
6
|
+
def initialize(document_root, port)
|
|
7
|
+
# logger = WEBrick::Log.new(nil, 1)
|
|
8
|
+
@server = WEBrick::HTTPServer.new({
|
|
9
|
+
:DocumentRoot => document_root,
|
|
10
|
+
:Port => port,
|
|
11
|
+
:AccessLog => []
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
WEBrick::HTTPServlet::FileHandler.remove_handler("cgi")
|
|
15
|
+
WEBrick::HTTPServlet::FileHandler.remove_handler("rhtml")
|
|
16
|
+
|
|
17
|
+
@server.mount("/_smc/update_uri", UpdateUriServlet)
|
|
18
|
+
@server.mount("/_smc/auto", AutoServlet)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def start
|
|
22
|
+
@server.start
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def shutdown
|
|
26
|
+
@server.shutdown
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def updated_uri=(uri)
|
|
30
|
+
UpdateUriServlet.uri = uri
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def reload
|
|
34
|
+
UpdateUriServlet.uri = ":reload"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class UpdateUriServlet < WEBrick::HTTPServlet::AbstractServlet
|
|
40
|
+
@@uri = "/index.html"
|
|
41
|
+
@@update_time = ""
|
|
42
|
+
|
|
43
|
+
def do_GET(req, res)
|
|
44
|
+
res['content-type'] = "text/plain"
|
|
45
|
+
res.body = @@uri + "\n" + @@update_time
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.uri=(uri)
|
|
49
|
+
@@uri = uri
|
|
50
|
+
update_time
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.update_time
|
|
54
|
+
@@update_time = Time.now.to_s
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class AutoServlet < WEBrick::HTTPServlet::AbstractServlet
|
|
60
|
+
def do_GET(req, res)
|
|
61
|
+
res['content-type'] = "text/html"
|
|
62
|
+
html = File.dirname(__FILE__) + "/resources/auto.html"
|
|
63
|
+
res.body = File.read(html)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
module SmallCage
|
|
2
|
+
class Loader
|
|
3
|
+
DEFAULT_TEMPLATE = "default"
|
|
4
|
+
DIR_PROP_FILE = "_dir.smc"
|
|
5
|
+
MAX_DEPTH = 100
|
|
6
|
+
|
|
7
|
+
attr_reader :root, :target, :erb_base
|
|
8
|
+
|
|
9
|
+
def initialize(target)
|
|
10
|
+
target = Pathname.new(target.to_s.strip.gsub(%r{(.+)/$}, '\1'))
|
|
11
|
+
target = real_target(target)
|
|
12
|
+
|
|
13
|
+
@target = target # absolute
|
|
14
|
+
@root = self.class.find_root(target) # absolute
|
|
15
|
+
@templates_dir = @root + "_smc/templates"
|
|
16
|
+
@helpers_dir = @root + "_smc/helpers"
|
|
17
|
+
@filters_dir = @root + "_smc/filters"
|
|
18
|
+
@erb_base = load_erb_base
|
|
19
|
+
@filters = load_filters
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# return root dir Pathname object.
|
|
23
|
+
def self.find_root(path, depth = MAX_DEPTH)
|
|
24
|
+
unless path.exist?
|
|
25
|
+
raise "Not found: " + path.to_s
|
|
26
|
+
end
|
|
27
|
+
d = path.realpath
|
|
28
|
+
|
|
29
|
+
if d.file?
|
|
30
|
+
d = d.parent
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
i = 0
|
|
34
|
+
loop do
|
|
35
|
+
if d.join("_smc").directory?
|
|
36
|
+
return d
|
|
37
|
+
end
|
|
38
|
+
break if d.root?
|
|
39
|
+
d = d.parent
|
|
40
|
+
|
|
41
|
+
i += 1
|
|
42
|
+
break if depth <= i
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
raise "Root not found: " + path
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def load(path)
|
|
49
|
+
unless path.exist?
|
|
50
|
+
raise "Not found: " + path.to_s
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
docpath = SmallCage::DocumentPath.new(@root, path)
|
|
54
|
+
|
|
55
|
+
result = {}
|
|
56
|
+
if path.file?
|
|
57
|
+
unless docpath.smc?
|
|
58
|
+
raise "Path is not smc file: " + docpath.to_s
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
path_smc = docpath.path
|
|
62
|
+
path_out = docpath.outfile.path
|
|
63
|
+
uri_smc = docpath.uri
|
|
64
|
+
uri_out = docpath.outuri
|
|
65
|
+
source_path = path_smc
|
|
66
|
+
|
|
67
|
+
result["dirs"] = load_dirs(path)
|
|
68
|
+
result["template"] = DEFAULT_TEMPLATE
|
|
69
|
+
else # directory
|
|
70
|
+
path_smc = nil
|
|
71
|
+
path_out = path
|
|
72
|
+
uri_smc = nil
|
|
73
|
+
uri_out = docpath.uri
|
|
74
|
+
uri_out += "/" unless uri_out =~ %r{/$}
|
|
75
|
+
source_path = path + DIR_PROP_FILE
|
|
76
|
+
|
|
77
|
+
if source_path.file?
|
|
78
|
+
path_smc = source_path
|
|
79
|
+
uri_smc = SmallCage::DocumentPath.to_uri(@root, source_path)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
add_smc_method(path_out, path_smc)
|
|
84
|
+
add_smc_method(uri_out, uri_smc)
|
|
85
|
+
|
|
86
|
+
result["path"] = path_out
|
|
87
|
+
result["uri"] = uri_out
|
|
88
|
+
result["arrays"] = []
|
|
89
|
+
result["strings"] = []
|
|
90
|
+
|
|
91
|
+
# target is directory and _dir.smc is not exist.
|
|
92
|
+
return result unless source_path.exist?
|
|
93
|
+
|
|
94
|
+
source = source_path.read
|
|
95
|
+
return result if source.strip.empty?
|
|
96
|
+
|
|
97
|
+
begin
|
|
98
|
+
obj = YAML.load_stream(source)
|
|
99
|
+
return result if obj.nil?
|
|
100
|
+
rescue => e
|
|
101
|
+
raise "Can't load file: #{source_path} / #{e}"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
obj.documents.each do |o|
|
|
105
|
+
if o.is_a? Hash
|
|
106
|
+
result = result.merge(o)
|
|
107
|
+
elsif o.is_a? Array
|
|
108
|
+
result["arrays"] << o
|
|
109
|
+
else
|
|
110
|
+
result["strings"] << o.to_s
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
return result
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def load_dirs(path)
|
|
118
|
+
result = []
|
|
119
|
+
loop do
|
|
120
|
+
path = path.parent
|
|
121
|
+
result.unshift load(path)
|
|
122
|
+
break if path.join("_smc").directory?
|
|
123
|
+
raise "Root directory not found!" if path.root?
|
|
124
|
+
end
|
|
125
|
+
return result
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def template_path(name)
|
|
129
|
+
result = @templates_dir + "#{name}.rhtml"
|
|
130
|
+
return nil unless result.file?
|
|
131
|
+
return result
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def each_smc_obj
|
|
135
|
+
each_smc_file do |path|
|
|
136
|
+
next if path.directory?
|
|
137
|
+
next if path.basename.to_s == DIR_PROP_FILE
|
|
138
|
+
obj = load(path)
|
|
139
|
+
yield obj
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def each_smc_file
|
|
144
|
+
if @target.directory?
|
|
145
|
+
p = Pathname.new(@target)
|
|
146
|
+
Dir.chdir(@target) do
|
|
147
|
+
Dir.glob("**/*.smc") do |f|
|
|
148
|
+
yield p + f
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
else
|
|
152
|
+
yield @target
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def each_not_smc_file
|
|
157
|
+
if @target.directory?
|
|
158
|
+
p = Pathname.new(@target)
|
|
159
|
+
Dir.chdir(@target) do
|
|
160
|
+
Dir.glob("**/*") do |f|
|
|
161
|
+
f = p + f
|
|
162
|
+
next if f.directory?
|
|
163
|
+
next if f.to_s =~ %r{/_smc/}
|
|
164
|
+
next if f.to_s =~ %r{\.smc$}
|
|
165
|
+
yield SmallCage::DocumentPath.new(@root, p + f)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
else
|
|
169
|
+
return if @target.to_s =~ %r{/_smc/}
|
|
170
|
+
return if @target.to_s =~ %r{\.smc$}
|
|
171
|
+
yield SmallCage::DocumentPath.new(@root, @target)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def real_target(target)
|
|
176
|
+
return target.realpath if target.directory?
|
|
177
|
+
return target.realpath if target.file? and target.to_s =~ /\.smc$/
|
|
178
|
+
|
|
179
|
+
tmp = Pathname.new(target.to_s + ".smc")
|
|
180
|
+
return tmp.realpath if tmp.file?
|
|
181
|
+
|
|
182
|
+
raise "Target not found: " + target.to_s
|
|
183
|
+
end
|
|
184
|
+
private :real_target
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def load_erb_base
|
|
188
|
+
result = Class.new(SmallCage::ErbBase)
|
|
189
|
+
class << result
|
|
190
|
+
def include_helpers(anon_module, mod_names)
|
|
191
|
+
smc_module = anon_module.const_get("SmallCage")
|
|
192
|
+
mod_names.each do |name|
|
|
193
|
+
helper_module = smc_module.const_get(name)
|
|
194
|
+
include helper_module
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
helpers = load_anonymous(@helpers_dir, %r{([^/]+_helper)\.rb$})
|
|
200
|
+
result.include_helpers(helpers[:module], helpers[:names])
|
|
201
|
+
|
|
202
|
+
return result
|
|
203
|
+
end
|
|
204
|
+
private :load_erb_base
|
|
205
|
+
|
|
206
|
+
def load_anonymous(dir, rex)
|
|
207
|
+
module_names = []
|
|
208
|
+
|
|
209
|
+
mod = Module.new
|
|
210
|
+
Dir.entries(dir).sort.each do |h|
|
|
211
|
+
next unless h =~ rex
|
|
212
|
+
|
|
213
|
+
# create anonymous module.
|
|
214
|
+
module_name = $1.camelize
|
|
215
|
+
|
|
216
|
+
src = File.read("#{dir}/#{h}")
|
|
217
|
+
begin
|
|
218
|
+
mod.module_eval(src, "#{dir}/#{h}")
|
|
219
|
+
rescue => ex
|
|
220
|
+
puts ex.to_s # TODO show error
|
|
221
|
+
load("#{dir}/#{h}", true) # try to know error line number.
|
|
222
|
+
raise "Can't load #{dir}/#{h} / line# unknown"
|
|
223
|
+
end
|
|
224
|
+
module_names << module_name
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
return { :module => mod, :names => module_names }
|
|
228
|
+
end
|
|
229
|
+
private :load_anonymous
|
|
230
|
+
|
|
231
|
+
def filters(name)
|
|
232
|
+
if @filters[name].nil?
|
|
233
|
+
return []
|
|
234
|
+
end
|
|
235
|
+
return @filters[name]
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def load_filters
|
|
239
|
+
result = {}
|
|
240
|
+
return {} unless @filters_dir.directory?
|
|
241
|
+
|
|
242
|
+
filters = load_anonymous(@filters_dir, %r{([^/]+_filter)\.rb$})
|
|
243
|
+
|
|
244
|
+
config = load_filters_config
|
|
245
|
+
config.each do |filter_type,filter_list|
|
|
246
|
+
result[filter_type] = []
|
|
247
|
+
smc_module = filters[:module].const_get("SmallCage")
|
|
248
|
+
filter_list.each do |fc|
|
|
249
|
+
fc = { "name" => fc } if fc.is_a? String
|
|
250
|
+
filter_class = smc_module.const_get(fc["name"].camelize)
|
|
251
|
+
result[filter_type] << filter_class.new(fc)
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
return result
|
|
255
|
+
end
|
|
256
|
+
private :load_filters
|
|
257
|
+
|
|
258
|
+
def load_filters_config
|
|
259
|
+
path = @filters_dir.join("filters.yml")
|
|
260
|
+
return {} unless path.file?
|
|
261
|
+
return YAML.load(path.read())
|
|
262
|
+
end
|
|
263
|
+
private :load_filters_config
|
|
264
|
+
|
|
265
|
+
def add_smc_method(obj, value)
|
|
266
|
+
obj.instance_eval do
|
|
267
|
+
@__smallcage ||= {}
|
|
268
|
+
@__smallcage[:smc] = value
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def obj.smc
|
|
272
|
+
return @__smallcage.nil? ? nil : @__smallcage[:smc]
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
private :add_smc_method
|
|
276
|
+
|
|
277
|
+
end
|
|
278
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
# From active-support/inflector.rb
|
|
3
|
+
class String
|
|
4
|
+
def camelize(first_letter_in_uppercase = true)
|
|
5
|
+
s = self
|
|
6
|
+
if first_letter_in_uppercase
|
|
7
|
+
s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
|
8
|
+
else
|
|
9
|
+
s[0..0] + s.camelize[1..-1]
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class SmallCage::Renderer
|
|
2
|
+
|
|
3
|
+
def initialize(loader)
|
|
4
|
+
@loader = loader
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def render(name, obj)
|
|
8
|
+
path = @loader.template_path(name)
|
|
9
|
+
return nil if path.nil?
|
|
10
|
+
return render_string(path.read, obj)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def render_string(str, obj)
|
|
14
|
+
erb_class = ERB.new(str, nil, '-').def_class(@loader.erb_base, "erb")
|
|
15
|
+
result = erb_class.new(@loader, self, obj).erb
|
|
16
|
+
return result
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
4
|
+
<head>
|
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
6
|
+
<title>SmallCage Project Files</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
|
|
10
|
+
<h1>SmallCage Project Files</h1>
|
|
11
|
+
|
|
12
|
+
<ul class="files">
|
|
13
|
+
<%- entries.each do |e| -%>
|
|
14
|
+
<li><a href="<%= e %>"><%= e %></a></li>
|
|
15
|
+
<%- end -%>
|
|
16
|
+
</ul>
|
|
17
|
+
|
|
18
|
+
</body>
|
|
19
|
+
</html>
|