pubba 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +7 -0
- data/README.md +68 -23
- data/lib/pubba.rb +42 -0
- data/lib/pubba/assets/configuration.rb +25 -0
- data/lib/pubba/assets/handler.rb +21 -0
- data/lib/pubba/assets/minifier.rb +9 -0
- data/lib/pubba/assets/sprockets_handler.rb +53 -0
- data/lib/pubba/assets/yui_minifier.rb +26 -0
- data/lib/pubba/errors.rb +3 -0
- data/lib/pubba/html/helpers.rb +50 -0
- data/lib/pubba/locale.rb +16 -0
- data/lib/pubba/monitor.rb +29 -0
- data/lib/pubba/page.rb +133 -0
- data/lib/pubba/site.rb +97 -0
- data/lib/{sinatra/pubba → pubba}/version.rb +1 -1
- data/pubba.gemspec +3 -3
- data/test/helper.rb +12 -7
- data/test/pubba/assets/test_configuration.rb +1 -1
- data/test/pubba/assets/test_handler.rb +4 -4
- data/test/pubba/assets/test_minifier.rb +1 -1
- data/test/pubba/html/test_helpers.rb +11 -2
- data/test/pubba/test_page.rb +12 -12
- data/test/pubba/test_site.rb +6 -6
- metadata +36 -36
- data/lib/sinatra/pubba.rb +0 -20
- data/lib/sinatra/pubba/assets/configuration.rb +0 -27
- data/lib/sinatra/pubba/assets/handler.rb +0 -23
- data/lib/sinatra/pubba/assets/minifier.rb +0 -11
- data/lib/sinatra/pubba/assets/sprockets_handler.rb +0 -55
- data/lib/sinatra/pubba/assets/yui_minifier.rb +0 -28
- data/lib/sinatra/pubba/errors.rb +0 -5
- data/lib/sinatra/pubba/html/helpers.rb +0 -52
- data/lib/sinatra/pubba/locale.rb +0 -18
- data/lib/sinatra/pubba/monitor.rb +0 -39
- data/lib/sinatra/pubba/page.rb +0 -135
- data/lib/sinatra/pubba/site.rb +0 -147
data/lib/sinatra/pubba.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'sinatra/base'
|
2
|
-
require 'rack/statica_server'
|
3
|
-
|
4
|
-
require_relative 'pubba/errors'
|
5
|
-
require_relative 'pubba/site'
|
6
|
-
require_relative 'pubba/html/helpers'
|
7
|
-
|
8
|
-
module Sinatra
|
9
|
-
module Pubba
|
10
|
-
def self.registered(app)
|
11
|
-
unless app.settings.test?
|
12
|
-
use Rack::StaticaServer
|
13
|
-
end
|
14
|
-
|
15
|
-
Site.configure(app)
|
16
|
-
|
17
|
-
app.helpers Sinatra::Pubba::HTML::Helpers
|
18
|
-
end
|
19
|
-
end # Pubba
|
20
|
-
end # Sinatra
|
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'psych'
|
2
|
-
|
3
|
-
module Sinatra
|
4
|
-
module Pubba
|
5
|
-
module Assets
|
6
|
-
class Configuration
|
7
|
-
attr_reader :yaml, :name, :disclaimer
|
8
|
-
|
9
|
-
def initialize(config_file)
|
10
|
-
@yaml = Psych.load_file(config_file)
|
11
|
-
@name = File.basename(config_file)
|
12
|
-
@disclaimer = "// This file is automatically generated from the contents\n// in #{name}\n//\n"
|
13
|
-
end
|
14
|
-
|
15
|
-
def global_config!
|
16
|
-
@global_config ||= (yaml.delete("global") || {})
|
17
|
-
end
|
18
|
-
|
19
|
-
def process
|
20
|
-
yaml.each do |page, config|
|
21
|
-
yield page, config
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end # Configuration
|
25
|
-
end # Assets
|
26
|
-
end # Pubba
|
27
|
-
end # Sinatra
|
@@ -1,23 +0,0 @@
|
|
1
|
-
module Sinatra
|
2
|
-
module Pubba
|
3
|
-
module Assets
|
4
|
-
class Handler
|
5
|
-
def self.asset(file)
|
6
|
-
raise NotImplementedError
|
7
|
-
end
|
8
|
-
|
9
|
-
def save_as(file)
|
10
|
-
raise NotImplementedError
|
11
|
-
end
|
12
|
-
|
13
|
-
def process(pattern, destination)
|
14
|
-
raise NotImplementedError
|
15
|
-
end
|
16
|
-
|
17
|
-
def build(name, type, ext, urls)
|
18
|
-
raise NotImplementedError
|
19
|
-
end
|
20
|
-
end # Handler
|
21
|
-
end # Assets
|
22
|
-
end # Pubba
|
23
|
-
end # Sinatra
|
@@ -1,55 +0,0 @@
|
|
1
|
-
require 'fileutils'
|
2
|
-
require 'sprockets'
|
3
|
-
require_relative 'handler'
|
4
|
-
|
5
|
-
module Sinatra
|
6
|
-
module Pubba
|
7
|
-
module Assets
|
8
|
-
class SprocketsHandler < Handler
|
9
|
-
def self.find(file)
|
10
|
-
SprocketsHandler.new(sprockets.find_asset(file))
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.asset_paths(*paths)
|
14
|
-
paths.each do |path|
|
15
|
-
sprockets.append_path path
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.sprockets
|
20
|
-
@sprockets ||= Sprockets::Environment.new()
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.process(source, destination)
|
24
|
-
FileUtils.mkdir_p destination
|
25
|
-
|
26
|
-
Dir.glob("#{source}/*") do |file|
|
27
|
-
asset = find(file)
|
28
|
-
asset.save_as "#{destination}/#{File.basename(file)}"
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.build(name, type, ext, urls)
|
33
|
-
content = urls.collect{|url| "//= require #{url}.#{ext}"}.compact.join("\n")
|
34
|
-
out_folder = File.join(Site.asset_folder, "out", ext)
|
35
|
-
FileUtils.mkdir_p out_folder
|
36
|
-
fname = File.join(out_folder, "#{name}-#{type}.#{ext}")
|
37
|
-
File.open(fname, 'w') do |f|
|
38
|
-
f.write Site.asset_configuration.disclaimer
|
39
|
-
f.write content
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
attr_reader :asset
|
44
|
-
|
45
|
-
def initialize(asset)
|
46
|
-
@asset = asset
|
47
|
-
end
|
48
|
-
|
49
|
-
def save_as(file)
|
50
|
-
asset.write_to(file)
|
51
|
-
end
|
52
|
-
end # SprocketsHandler
|
53
|
-
end # Assets
|
54
|
-
end # Pubba
|
55
|
-
end # Sinatra
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require "yui/compressor"
|
2
|
-
require_relative 'minifier'
|
3
|
-
|
4
|
-
module Sinatra
|
5
|
-
module Pubba
|
6
|
-
module Assets
|
7
|
-
class YUIMinifier < Minifier
|
8
|
-
def self.minify(folder, handler)
|
9
|
-
compressor = get_compressor(handler)
|
10
|
-
Dir.glob("#{folder}/*.*") do |file|
|
11
|
-
contents = File.read(file)
|
12
|
-
File.open(file, "w") {|f| f.write(compressor.compress(contents))}
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
private
|
17
|
-
|
18
|
-
def self.get_compressor(handler)
|
19
|
-
case handler
|
20
|
-
when :js then YUI::JavaScriptCompressor.new
|
21
|
-
when :css then YUI::CssCompressor.new
|
22
|
-
else raise ArgumentError, "minify handler must be one of [:js, :css]"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end # YUIMinifier
|
26
|
-
end # Assets
|
27
|
-
end # Pubba
|
28
|
-
end # Sinatra
|
data/lib/sinatra/pubba/errors.rb
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
module Sinatra
|
2
|
-
module Pubba
|
3
|
-
module HTML
|
4
|
-
module Helpers
|
5
|
-
def page_head_tags
|
6
|
-
process_tags(@page.head_tags)
|
7
|
-
end
|
8
|
-
|
9
|
-
def page_body_tags
|
10
|
-
process_tags(@page.body_tags)
|
11
|
-
end
|
12
|
-
|
13
|
-
def digest_url(url)
|
14
|
-
url.start_with?('http') ? url : ::Statica.digest_url(url)
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
def process_tags(tags)
|
20
|
-
array = []
|
21
|
-
tags.each do |tag|
|
22
|
-
t = tag.dup
|
23
|
-
set_url(type = t.delete(:tag), t)
|
24
|
-
array << tag_content(type, '', t)
|
25
|
-
end
|
26
|
-
array.join('')
|
27
|
-
end
|
28
|
-
|
29
|
-
def set_url(type, t)
|
30
|
-
if type == 'script'
|
31
|
-
t[:src] = digest_url(t[:src])
|
32
|
-
else
|
33
|
-
t[:href] = digest_url(t[:href])
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def tag_content(tag, content, attrs={}, self_closing=false)
|
38
|
-
base = "<#{tag}#{tag_attrs(attrs)}"
|
39
|
-
self_closing ? "#{base}/>":"#{base}>#{content}</#{tag}>"
|
40
|
-
end
|
41
|
-
|
42
|
-
def tag_attrs(attrs)
|
43
|
-
return '' if attrs.empty?
|
44
|
-
|
45
|
-
return " " + attrs.keys.sort.collect do |k|
|
46
|
-
%|#{k}="#{attrs[k]}"|
|
47
|
-
end.join(' ')
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end # HTML
|
51
|
-
end # Pubba
|
52
|
-
end # Sinatra
|
data/lib/sinatra/pubba/locale.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
require 'r18n-desktop'
|
2
|
-
|
3
|
-
module Sinatra
|
4
|
-
module Pubba
|
5
|
-
class Locale
|
6
|
-
include R18n::Helpers
|
7
|
-
|
8
|
-
R18n.from_env Site.r18n_folder, Site.r18n_locale
|
9
|
-
|
10
|
-
def get(category, name, *args)
|
11
|
-
res = t.send(category).send(name, *args)
|
12
|
-
res = t.send(name, *args) if R18n::Untranslated === res
|
13
|
-
|
14
|
-
R18n::Untranslated === res ? nil : res
|
15
|
-
end
|
16
|
-
end # Locale
|
17
|
-
end # Pubba
|
18
|
-
end # Sinatra
|
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'fssm'
|
2
|
-
|
3
|
-
module Sinatra
|
4
|
-
module Pubba
|
5
|
-
module Monitor
|
6
|
-
extend self
|
7
|
-
|
8
|
-
def do
|
9
|
-
start_monitor unless @running
|
10
|
-
end
|
11
|
-
|
12
|
-
def start_monitor
|
13
|
-
@running = Thread.new do
|
14
|
-
|
15
|
-
|
16
|
-
script_asset_folder = File.join(Site.asset_folder, Site.script_folder)
|
17
|
-
style_asset_folder = File.join(Site.asset_folder, Site.style_folder)
|
18
|
-
|
19
|
-
puts ">> Pubba is now monitoring:\n>> #{script_asset_folder}\n>> #{style_asset_folder}"
|
20
|
-
FSSM.monitor do
|
21
|
-
path script_asset_folder do
|
22
|
-
glob '**/*'
|
23
|
-
update {|base, relative, type| Site.process}
|
24
|
-
delete {|base, relative, type| Site.process}
|
25
|
-
create {|base, relative, type| Site.process}
|
26
|
-
end
|
27
|
-
|
28
|
-
path style_asset_folder do
|
29
|
-
glob '**/*'
|
30
|
-
update {|base, relative, type| Site.process}
|
31
|
-
delete {|base, relative, type| Site.process}
|
32
|
-
create {|base, relative, type| Site.process}
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end # Monitor
|
38
|
-
end # Pubba
|
39
|
-
end # Sinatra
|
data/lib/sinatra/pubba/page.rb
DELETED
@@ -1,135 +0,0 @@
|
|
1
|
-
module Sinatra
|
2
|
-
module Pubba
|
3
|
-
class Page
|
4
|
-
attr_accessor :name
|
5
|
-
attr_reader :assets, :head_tags, :body_tags
|
6
|
-
|
7
|
-
def initialize(name, global_configuration = {})
|
8
|
-
@name = name
|
9
|
-
@assets = {"styles" => {}, "scripts" => {}}
|
10
|
-
@head_tags = []
|
11
|
-
@body_tags = []
|
12
|
-
|
13
|
-
global_configuration["styles"].each do |key, value|
|
14
|
-
@assets["styles"][key] = value.dup
|
15
|
-
end
|
16
|
-
|
17
|
-
script_groups do |group|
|
18
|
-
@assets["scripts"][group] = global_configuration["scripts"][group] || []
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def add_asset(type, section)
|
23
|
-
if type == "styles"
|
24
|
-
section.each do |section_name, hash|
|
25
|
-
hash.each do |key, value|
|
26
|
-
if key == "urls"
|
27
|
-
@assets[type][section_name][key] += value
|
28
|
-
else
|
29
|
-
@assets[type][section_name][key] = value
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
else
|
34
|
-
script_groups do |group|
|
35
|
-
@assets["scripts"][group] += section[group] if section[group]
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def assetize
|
41
|
-
create_style_assets
|
42
|
-
create_script_assets
|
43
|
-
end
|
44
|
-
|
45
|
-
def tagify
|
46
|
-
style_groups do |group, hash|
|
47
|
-
style_urls(group) do |url|
|
48
|
-
add_style_tag(group, hash, url)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
script_groups do |group|
|
53
|
-
script_urls(group) do |url|
|
54
|
-
add_script_tag(group, url)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def method_missing(meth, *args)
|
60
|
-
if Site.locale && (t = Site.locale.get(name, meth, *args))
|
61
|
-
return t
|
62
|
-
end
|
63
|
-
super
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
|
-
|
68
|
-
def process_scripts(array)
|
69
|
-
return [] if array.nil? || array.empty?
|
70
|
-
|
71
|
-
array.each{|ele| scripts << ele }
|
72
|
-
end
|
73
|
-
|
74
|
-
def create_style_assets
|
75
|
-
style_groups do |group, hash|
|
76
|
-
urls = []
|
77
|
-
style_urls(group) do |url|
|
78
|
-
next if url.start_with?("http")
|
79
|
-
urls << url
|
80
|
-
end
|
81
|
-
Site.asset_handler.build(name, group, "css", urls)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def create_script_assets
|
86
|
-
script_groups do |group|
|
87
|
-
urls = []
|
88
|
-
script_urls(group) do |url|
|
89
|
-
next if url.start_with?("http")
|
90
|
-
urls << url
|
91
|
-
end
|
92
|
-
Site.asset_handler.build(name, group, "js", urls)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
def style_groups
|
97
|
-
@assets["styles"].each{|group, hash| yield group, hash }
|
98
|
-
end
|
99
|
-
|
100
|
-
def style_urls(group)
|
101
|
-
@assets["styles"][group]["urls"].each{|url| yield url }
|
102
|
-
end
|
103
|
-
|
104
|
-
def script_groups
|
105
|
-
["head", "body"].each{|group| yield group }
|
106
|
-
end
|
107
|
-
|
108
|
-
def script_urls(group)
|
109
|
-
@assets["scripts"][group].each{|url| yield url }
|
110
|
-
end
|
111
|
-
|
112
|
-
def add_style_tag(group, hash, url)
|
113
|
-
h = { tag: 'link', type: 'text/css', rel: 'stylesheet' }
|
114
|
-
h[:media] = hash['media'] if hash['media']
|
115
|
-
h[:href] = url.start_with?("http") ? url : "/#{Site.style_folder}/#{name}-#{group}.css"
|
116
|
-
|
117
|
-
maybe_add_tag(@head_tags, h, :href)
|
118
|
-
end
|
119
|
-
|
120
|
-
def add_script_tag(group, url)
|
121
|
-
h = { tag: 'script', type: "text/javascript" }
|
122
|
-
h[:src] = url.start_with?("http") ? url : "/#{Site.script_folder}/#{name}-#{group}.js"
|
123
|
-
|
124
|
-
tag_set = (group == "head") ? @head_tags : @body_tags
|
125
|
-
maybe_add_tag(tag_set, h, :src)
|
126
|
-
end
|
127
|
-
|
128
|
-
def maybe_add_tag(tag_set, hash, key)
|
129
|
-
found = false
|
130
|
-
tag_set.each{|tag| found = true if tag[key] == hash[key]}
|
131
|
-
tag_set << hash unless found
|
132
|
-
end
|
133
|
-
end # Page
|
134
|
-
end # Pubba
|
135
|
-
end # Sinatra
|
data/lib/sinatra/pubba/site.rb
DELETED
@@ -1,147 +0,0 @@
|
|
1
|
-
require 'statica'
|
2
|
-
|
3
|
-
require_relative 'assets/configuration'
|
4
|
-
require_relative 'assets/sprockets_handler'
|
5
|
-
require_relative 'assets/yui_minifier'
|
6
|
-
require_relative 'page'
|
7
|
-
|
8
|
-
module Sinatra
|
9
|
-
module Pubba
|
10
|
-
module Site
|
11
|
-
extend self
|
12
|
-
attr_reader :public_folder, :asset_folder, :script_folder, :style_folder
|
13
|
-
attr_reader :asset_configuration, :asset_handler, :asset_minifier
|
14
|
-
attr_reader :locale, :r18n_folder, :r18n_locale
|
15
|
-
|
16
|
-
def configure(app)
|
17
|
-
return if @configured
|
18
|
-
|
19
|
-
puts "Configuring Pubba..."
|
20
|
-
|
21
|
-
settings = app.settings
|
22
|
-
|
23
|
-
validate_settings(settings)
|
24
|
-
|
25
|
-
set_folder_defaults(settings)
|
26
|
-
|
27
|
-
maybe_init_r18n(settings)
|
28
|
-
|
29
|
-
# Load pubba_config
|
30
|
-
@asset_configuration = Sinatra::Pubba::Assets::Configuration.new(settings.pubba_config)
|
31
|
-
|
32
|
-
# Set assset handler
|
33
|
-
configure_asset_handler(settings)
|
34
|
-
|
35
|
-
# Set compressor
|
36
|
-
configure_asset_compressor(settings)
|
37
|
-
|
38
|
-
# Process the remaining @pubba_config sections
|
39
|
-
asset_configuration.process do |p, config|
|
40
|
-
add_page(p, config)
|
41
|
-
end
|
42
|
-
|
43
|
-
if app.settings.development? || app.settings.test?
|
44
|
-
process
|
45
|
-
|
46
|
-
require_relative 'monitor'
|
47
|
-
Monitor.do
|
48
|
-
end
|
49
|
-
|
50
|
-
@configured = true
|
51
|
-
end
|
52
|
-
|
53
|
-
def process
|
54
|
-
pages.each{|name, p| p.assetize }
|
55
|
-
|
56
|
-
asset_script_folder = File.join(asset_folder, 'out', script_folder)
|
57
|
-
asset_style_folder = File.join(asset_folder, 'out', style_folder)
|
58
|
-
|
59
|
-
public_script_folder = File.join(public_folder, script_folder)
|
60
|
-
public_style_folder = File.join(public_folder, style_folder)
|
61
|
-
|
62
|
-
asset_handler.process(asset_script_folder, public_script_folder)
|
63
|
-
asset_handler.process(asset_style_folder, public_style_folder)
|
64
|
-
|
65
|
-
asset_minifier.minify(public_script_folder, :js)
|
66
|
-
asset_minifier.minify(public_style_folder, :css)
|
67
|
-
end
|
68
|
-
|
69
|
-
def validate_settings(settings)
|
70
|
-
missing_settings = []
|
71
|
-
unless settings.respond_to? :public_folder
|
72
|
-
missing_settings << ":public_folder has not been set!"
|
73
|
-
end
|
74
|
-
|
75
|
-
unless settings.respond_to? :asset_folder
|
76
|
-
missing_settings << ":asset_folder has not been set!"
|
77
|
-
end
|
78
|
-
|
79
|
-
if missing_settings.length > 0
|
80
|
-
messages = missing_settings.join("\n")
|
81
|
-
raise Pubba::ConfigurationError.new("Missing configuration options:\n#{messages}")
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def set_folder_defaults(settings)
|
86
|
-
@public_folder = settings.public_folder
|
87
|
-
@asset_folder = settings.asset_folder
|
88
|
-
@script_folder = 'js'
|
89
|
-
@style_folder = 'css'
|
90
|
-
|
91
|
-
Statica.root_dir = settings.public_folder
|
92
|
-
end
|
93
|
-
|
94
|
-
def maybe_init_r18n(settings)
|
95
|
-
return unless settings.respond_to?(:r18n_folder)
|
96
|
-
|
97
|
-
locale = 'en'
|
98
|
-
if settings.respond_to?(:r18n_locale)
|
99
|
-
locale = settings.r18n_locale
|
100
|
-
end
|
101
|
-
|
102
|
-
@r18n_folder = settings.r18n_folder
|
103
|
-
@r18n_locale = locale
|
104
|
-
|
105
|
-
require_relative 'locale'
|
106
|
-
|
107
|
-
@locale = Locale.new
|
108
|
-
end
|
109
|
-
|
110
|
-
def configure_asset_handler(settings)
|
111
|
-
@asset_handler = Sinatra::Pubba::Assets::SprocketsHandler
|
112
|
-
if settings.respond_to?(:asset_handler) && (handler = settings.asset_handler)
|
113
|
-
@asset_handler = handler
|
114
|
-
end
|
115
|
-
@asset_handler.asset_paths asset_folder,
|
116
|
-
File.join(asset_folder, style_folder),
|
117
|
-
File.join(asset_folder, script_folder)
|
118
|
-
end
|
119
|
-
|
120
|
-
def configure_asset_compressor(settings)
|
121
|
-
@asset_minifier = Sinatra::Pubba::Assets::YUIMinifier
|
122
|
-
if settings.respond_to?(:asset_minifier) && (minifier = settings.asset_minifier)
|
123
|
-
@asset_minifier = minifier
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
def page(name)
|
128
|
-
pages[name]
|
129
|
-
end
|
130
|
-
|
131
|
-
def pages
|
132
|
-
@pages ||= {}
|
133
|
-
end
|
134
|
-
|
135
|
-
def add_page(name, hsh)
|
136
|
-
p = Page.new(name, @asset_configuration.global_config!)
|
137
|
-
|
138
|
-
p.add_asset('styles', hsh['styles']) if hsh['styles']
|
139
|
-
p.add_asset('scripts', hsh['scripts']) if hsh['scripts']
|
140
|
-
|
141
|
-
p.tagify
|
142
|
-
|
143
|
-
pages[name] = p
|
144
|
-
end
|
145
|
-
end # Site
|
146
|
-
end # Pubba
|
147
|
-
end # Sinatra
|