sitepack 0.6
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/README +9 -0
- data/lib/sitepack.rb +189 -0
- data/test.rb +75 -0
- metadata +85 -0
data/README
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
Sinatra Static Pages
|
2
|
+
--------------------
|
3
|
+
|
4
|
+
Sinatra makes websites easy, but when it's time to deploy running ruby can be a pain.
|
5
|
+
|
6
|
+
ContentPack makes it easy. Just export your sinatra application as static html and upload to any webserver - Done!
|
7
|
+
|
8
|
+
All the power of ruby, but really how many times are you changing that content and why can't you traverse your content store
|
9
|
+
before deploying. Do you really have so many pages you need a database for every request?
|
data/lib/sitepack.rb
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
require 'hpricot'
|
5
|
+
require 'redcloth'
|
6
|
+
require 'erubis'
|
7
|
+
|
8
|
+
module SitePack
|
9
|
+
RackDefaultEnv = {
|
10
|
+
'REQUEST_METHOD' => 'GET',
|
11
|
+
'SCRIPT_NAME' => 'app.rb',
|
12
|
+
'SERVER_NAME' => 'localhost',
|
13
|
+
'SERVER_PORT' => 9999,
|
14
|
+
'rack.url_scheme' => 'http',
|
15
|
+
'rack.version' => [1,1],
|
16
|
+
'rack.input' => StringIO.new,
|
17
|
+
'rack.errors' => StringIO.new,
|
18
|
+
'rack.run_once' => true,
|
19
|
+
'rack.multiprocess' => false,
|
20
|
+
'rack.multithread' => false
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
#
|
24
|
+
# Generate a static site using sinatra
|
25
|
+
#
|
26
|
+
# Given a content root folder, Builder will auto generate a site.
|
27
|
+
#
|
28
|
+
# builder = SitePack::Builder.new(MyAppKlass, 'mydomain.com', './site/')
|
29
|
+
#
|
30
|
+
# # walk all .xml files in the './content/' directory
|
31
|
+
# # calling builder.save with the computed path will
|
32
|
+
# # write out a rendered .html file into the ./site/ directory at the path
|
33
|
+
# # ./content/#{path}.html
|
34
|
+
#
|
35
|
+
# builder.content('./content/') do|path|
|
36
|
+
# builder.save(path)
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
class Builder
|
40
|
+
|
41
|
+
def initialize(appklass, domain, sitedir)
|
42
|
+
@appklass = appklass
|
43
|
+
@domain = domain
|
44
|
+
@sitedir = sitedir
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# yield each sub path of each content file within the content directory
|
49
|
+
# or return an array of all paths
|
50
|
+
#
|
51
|
+
def content(dir)
|
52
|
+
@content_dir = dir
|
53
|
+
dir_path = Pathname.new(File.expand_path(dir))
|
54
|
+
paths = Dir["#{dir}/**/**.xml"].map{|p| Pathname.new(File.expand_path(p)).relative_path_from(dir_path) }
|
55
|
+
paths.each {|p| yield p } if block_given?
|
56
|
+
@content_dir = nil
|
57
|
+
paths
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# save a given path into the site output directory
|
62
|
+
#
|
63
|
+
def save(path)
|
64
|
+
doc = Hpricot.XML(File.read(File.expand_path(File.join(@content_dir, path))))
|
65
|
+
ext = doc.at(:page)['extension'] || 'html'
|
66
|
+
|
67
|
+
output_path = path.sub(/\.xml$/,".#{ext}")
|
68
|
+
file = "#{@sitedir}/#{output_path}"
|
69
|
+
url = "http://#{@domain}/#{output_path}"
|
70
|
+
env = RackDefaultEnv.dup
|
71
|
+
uri = URI.parse(url)
|
72
|
+
env['PATH_INFO'] = uri.path
|
73
|
+
env['QUERY_STRING'] = uri.query || ''
|
74
|
+
req = Rack::Request.new(env) #Rack::MockRequest.env_for(url, env)
|
75
|
+
status, headers, body = @appklass.call(req.env)
|
76
|
+
res = Rack::MockResponse.new(status, headers, body, req.env["rack.errors"].flush)
|
77
|
+
|
78
|
+
case status
|
79
|
+
when 200
|
80
|
+
puts "render: #{url} as #{file}"
|
81
|
+
FileUtils.mkdir_p(File.dirname(file)) unless File.exist?(File.dirname(file))
|
82
|
+
File.open(file,'wb') { |f| f << res.body }
|
83
|
+
return file
|
84
|
+
else
|
85
|
+
raise "error #{status} status code: #{status} when requesting: #{url}\n#{res.body}"
|
86
|
+
end
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# enable content by including this module in your sinatra application
|
93
|
+
#
|
94
|
+
# class MyApp < Sinatra::Application
|
95
|
+
# include SitePack::Content
|
96
|
+
# set :content_path, 'content'
|
97
|
+
# set :public, 'public'
|
98
|
+
#
|
99
|
+
# get '/:page.html' do
|
100
|
+
# erb site_content(options.content_path, params)
|
101
|
+
# end
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# sample page content format:
|
105
|
+
#
|
106
|
+
# <?xml version="1.0"?>
|
107
|
+
# <page>
|
108
|
+
# <title>Title Content</title>
|
109
|
+
# <body template="special" filter="redcloth">
|
110
|
+
# h1. Foo Bar
|
111
|
+
#
|
112
|
+
# A *simple* paragraph with
|
113
|
+
# a line break, some _emphasis_ and a "link":http://redcloth.org
|
114
|
+
#
|
115
|
+
# * an item
|
116
|
+
# * and another
|
117
|
+
#
|
118
|
+
# # one
|
119
|
+
# # two
|
120
|
+
#
|
121
|
+
# </body>
|
122
|
+
# </page>
|
123
|
+
#
|
124
|
+
# template = by default is page, e.g. views/page.erb
|
125
|
+
# filter = by default is redcloth, but can be html -> in which case use <![CDATA[ html ]]>
|
126
|
+
#
|
127
|
+
module Config
|
128
|
+
def site_pack_config(yaml_file)
|
129
|
+
config = YAML.load_file(yaml_file)
|
130
|
+
@vars = config['vars']
|
131
|
+
@filter = config['filter'] || 'redcloth'
|
132
|
+
@verbose = config['verbose']
|
133
|
+
puts "Site Pack!\nVars:#{@vars.inspect}\nFilter(default):#{@filter.inspect}" if @verbose
|
134
|
+
end
|
135
|
+
def _site_pack_get_opt(opt)
|
136
|
+
instance_variable_get("@#{opt}".to_sym)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
module Content
|
140
|
+
|
141
|
+
|
142
|
+
def site_content(content_path, params)
|
143
|
+
if params[:splat].nil?
|
144
|
+
file_path = "#{content_path}/index.xml"
|
145
|
+
else
|
146
|
+
file_path = "#{content_path}/#{params[:splat].join('/')}.xml"
|
147
|
+
end
|
148
|
+
halt 404, "page not found: #{file_path.inspect}" unless File.exists?(file_path)
|
149
|
+
doc = Hpricot.XML(File.read(file_path))
|
150
|
+
@title = doc.at(:title) ? doc.at(:title).inner_html : nil
|
151
|
+
body = doc.at(:body)
|
152
|
+
if body
|
153
|
+
template = body[:template] || 'page'
|
154
|
+
@body = content_processor(body)
|
155
|
+
else
|
156
|
+
halt 500, "Missing body tag!"
|
157
|
+
end
|
158
|
+
vars = self.class._site_pack_get_opt('vars')
|
159
|
+
if vars and vars.respond_to?(:each)
|
160
|
+
vars.each do|var|
|
161
|
+
node = doc.at(var.to_sym)
|
162
|
+
instance_variable_set("@#{var}".to_sym, content_processor(node)) if node
|
163
|
+
end
|
164
|
+
end
|
165
|
+
sidebar = doc.at(:sidebar)
|
166
|
+
@sidebar = content_processor(sidebar) if sidebar
|
167
|
+
|
168
|
+
template.to_sym
|
169
|
+
end
|
170
|
+
|
171
|
+
def content_processor(node)
|
172
|
+
filter = node[:filter] || self.class._site_pack_get_opt('filter')
|
173
|
+
puts "Render(#{node.name.inspect}) with #{filter.inspect}" if self.class._site_pack_get_opt('verbose')
|
174
|
+
output = case filter
|
175
|
+
when 'redcloth'
|
176
|
+
RedCloth.new(node.inner_html).to_html
|
177
|
+
when 'html'
|
178
|
+
node.inner_html
|
179
|
+
when 'erubis'
|
180
|
+
Erubis::Eruby.new(node.inner_html).result(binding())
|
181
|
+
else
|
182
|
+
halt 500, "Unsupported filter: #{filter} when rendering #{file_path}"
|
183
|
+
end
|
184
|
+
output
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
data/test.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
|
2
|
+
require 'app'
|
3
|
+
require 'test/unit'
|
4
|
+
require 'rack/test'
|
5
|
+
|
6
|
+
class TestApp < Test::Unit::TestCase
|
7
|
+
include Rack::Test::Methods
|
8
|
+
|
9
|
+
def app
|
10
|
+
App
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_index_redirect
|
14
|
+
get '/'
|
15
|
+
assert_equal 302, last_response.status
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_404
|
19
|
+
get '/foobar.html'
|
20
|
+
assert_equal 404, last_response.status
|
21
|
+
assert_match /That page is missing/, last_response.body
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_500
|
25
|
+
get '/badcontent.html'
|
26
|
+
assert_equal 500, last_response.status
|
27
|
+
assert_match /Missing body tag/, last_response.body
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_200
|
31
|
+
get '/index.html'
|
32
|
+
assert_equal 200, last_response.status
|
33
|
+
assert_match /Home Page/, last_response.body
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_html
|
37
|
+
get '/html.html'
|
38
|
+
assert_equal 200, last_response.status
|
39
|
+
assert_match /<h1>we have html tags<\/h1>/, last_response.body
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_erubis
|
43
|
+
get '/erubis.html'
|
44
|
+
assert_equal 200, last_response.status
|
45
|
+
assert_match /Yes this is erubis/, last_response.body
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_package
|
49
|
+
system("mv content/badcontent.xml .")
|
50
|
+
system("rake package")
|
51
|
+
assert File.exist?("site/index.html")
|
52
|
+
assert File.exist?("site/page2.html")
|
53
|
+
assert File.exist?("site/services/foo.html")
|
54
|
+
assert File.exist?("site/404.html")
|
55
|
+
assert File.exist?("site/altfile_extension.php")
|
56
|
+
ensure
|
57
|
+
system("mv badcontent.xml content/badcontent.xml")
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_alt_extensions
|
61
|
+
get '/altfile_extension.php'
|
62
|
+
assert_equal 200, last_response.status
|
63
|
+
assert_match /<\? echo 'hello' \?>/, last_response.body # /
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_extravars
|
67
|
+
get '/extravars.html'
|
68
|
+
assert_equal 200, last_response.status
|
69
|
+
|
70
|
+
assert_match(/footer/,last_response.body)
|
71
|
+
assert_match(/sidebar/,last_response.body)
|
72
|
+
assert_match(/head/,last_response.body)
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sitepack
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.6"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Todd A. Fisher
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-03-02 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hpricot
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: RedCloth
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: erubis
|
37
|
+
type: :runtime
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
description: Sinatra Static Pages
|
46
|
+
email: todd.fisher@gmail.com
|
47
|
+
executables: []
|
48
|
+
|
49
|
+
extensions: []
|
50
|
+
|
51
|
+
extra_rdoc_files: []
|
52
|
+
|
53
|
+
files:
|
54
|
+
- README
|
55
|
+
- lib/sitepack.rb
|
56
|
+
has_rdoc: true
|
57
|
+
homepage:
|
58
|
+
licenses: []
|
59
|
+
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: "0"
|
70
|
+
version:
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: "0"
|
76
|
+
version:
|
77
|
+
requirements: []
|
78
|
+
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 1.3.5
|
81
|
+
signing_key:
|
82
|
+
specification_version: 3
|
83
|
+
summary: Sinatra Static Pages
|
84
|
+
test_files:
|
85
|
+
- test.rb
|