pekky 0.2 → 0.4
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 +2 -5
- data/lib/pekky/builder.rb +12 -19
- data/lib/pekky/config.rb +84 -0
- data/lib/pekky/context.rb +94 -63
- data/lib/pekky/pages.rb +71 -78
- data/lib/pekky/server.rb +12 -6
- data/lib/pekky/tree.rb +6 -0
- data/lib/pekky.rb +41 -19
- metadata +58 -49
- data/Thorfile +0 -39
- data/lib/pekky/site_config.rb +0 -60
data/README
CHANGED
@@ -35,17 +35,14 @@
|
|
35
35
|
|
36
36
|
TODO
|
37
37
|
* Get specs up to speed
|
38
|
-
* Add the option of naming pages
|
39
|
-
* Support shared content
|
40
|
-
* Add helpers for slurping up a collection of shared content i.e. news feed
|
41
|
-
* Support alternate formats correctly
|
42
38
|
* Compile stylesheets with less or sass
|
43
39
|
* Compile javascripts with Coffee
|
44
40
|
|
45
41
|
|
46
42
|
CAVEATS
|
47
43
|
* Not actually finished soz
|
48
|
-
*
|
44
|
+
* Windows support with cygwin only. If someone wants to fix this, that’s cool,
|
45
|
+
but I’m lazy.
|
49
46
|
* Limited Mustache support. It won’t play with arbitrary contexts.
|
50
47
|
* May potentially be slow
|
51
48
|
|
data/lib/pekky/builder.rb
CHANGED
@@ -1,20 +1,13 @@
|
|
1
1
|
module Pekky
|
2
2
|
module Builder
|
3
3
|
def self.build
|
4
|
-
Context.flush!
|
5
4
|
check_for_output_dir
|
6
5
|
copy_static_assets
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
# This ensures that every page will have complete access to the entire
|
13
|
-
# site and it's content as it is being rendered, calling helpers etc.
|
14
|
-
pages = SiteConfig.page_configs.collect {|p| p.prepare}
|
15
|
-
pages.flatten!
|
16
|
-
Tree.add(pages)
|
17
|
-
pages.each {|p| p.render}
|
6
|
+
|
7
|
+
Pekky::Content.flush!
|
8
|
+
|
9
|
+
Tree.add(Config.page_configs)
|
10
|
+
Config.page_configs.each {|p| p.render}
|
18
11
|
end
|
19
12
|
|
20
13
|
def self.build!
|
@@ -23,17 +16,17 @@ module Pekky
|
|
23
16
|
end
|
24
17
|
|
25
18
|
def self.clear_output_dir
|
26
|
-
system("rm -rf '#{
|
19
|
+
system("rm -rf '#{Config[:output_dir]}/*'") if File.exists?(Config[:output_dir])
|
27
20
|
end
|
28
21
|
|
29
|
-
def self.check_for_output_dir
|
30
|
-
FileUtils.mkdir(
|
22
|
+
def self.check_for_output_dir
|
23
|
+
FileUtils.mkdir(Config[:output_dir]) unless File.exists?(Config[:output_dir])
|
31
24
|
end
|
32
25
|
|
33
26
|
def self.directory(path)
|
34
|
-
unless File.directory?(File.join(
|
27
|
+
unless File.directory?(File.join(Config[:output_dir], path))
|
35
28
|
parts = path.split("/")
|
36
|
-
fragment =
|
29
|
+
fragment = Config[:output_dir].clone
|
37
30
|
while parts.length > 0
|
38
31
|
fragment = File.join(fragment, parts.shift)
|
39
32
|
FileUtils.mkdir(fragment) unless File.directory?(fragment)
|
@@ -42,13 +35,13 @@ module Pekky
|
|
42
35
|
end
|
43
36
|
|
44
37
|
def self.copy_static_assets
|
45
|
-
system("cp -fR '#{
|
38
|
+
system("cp -fR '#{Config[:public_dir]}/.' '#{Config[:output_dir]}/.'")
|
46
39
|
end
|
47
40
|
|
48
41
|
def self.generate(destination)
|
49
42
|
destination ||= Dir.pwd
|
50
43
|
FileUtils.mkdir(destination) unless File.exists?(destination)
|
51
|
-
dirs = %w(content views public public/stylesheets public/javascripts public/images)
|
44
|
+
dirs = %w(content views views/layouts views/partials public public/stylesheets public/javascripts public/images)
|
52
45
|
dirs.each do |d|
|
53
46
|
FileUtils.mkdir(File.join(destination, d))
|
54
47
|
end
|
data/lib/pekky/config.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
module Pekky
|
2
|
+
module Config
|
3
|
+
@default_config = {
|
4
|
+
:content_dir => '/content',
|
5
|
+
:view_dir => '/views',
|
6
|
+
:layouts_dir => '/views/layouts',
|
7
|
+
:partials_dir => '/views/partials',
|
8
|
+
:public_dir => '/public',
|
9
|
+
:output_dir => '/output',
|
10
|
+
:javascripts_path => '/javascripts',
|
11
|
+
:stylesheets_path => '/stylesheets',
|
12
|
+
:images_path => '/images',
|
13
|
+
:templating => 'haml',
|
14
|
+
:css_processor => 'sass'
|
15
|
+
}
|
16
|
+
|
17
|
+
@config = {}
|
18
|
+
@loaded = false
|
19
|
+
|
20
|
+
def self.load(p)
|
21
|
+
unless @loaded
|
22
|
+
@config = @default_config.merge(@config)
|
23
|
+
@config[:root] = p
|
24
|
+
@config.each do |k, v|
|
25
|
+
@config[k] = File.join(p, v) if k.to_s.match(/_dir$/)
|
26
|
+
end
|
27
|
+
@loaded = true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
@page_configs = []
|
32
|
+
@routes = {}
|
33
|
+
|
34
|
+
def self.page_configs
|
35
|
+
@page_configs
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.[](name)
|
39
|
+
@config[name]
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.[]=(option, val)
|
43
|
+
set(option, val)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def self.set(option, val)
|
49
|
+
if option.to_s.match(/_dir$/)
|
50
|
+
@config[option] = File.join(self[:root], val)
|
51
|
+
else
|
52
|
+
@config[option] = val
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.page(name, path, content = nil, opts = {})
|
57
|
+
@page_configs << if content.is_a? Hash
|
58
|
+
Page.new(name, path, content, opts)
|
59
|
+
else
|
60
|
+
Page.new(name, path, Content.get(content), opts)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.route(name, path)
|
65
|
+
placeholders = path.scan(/:(\S+)/).flatten!
|
66
|
+
@routes[name.to_sym] = {:path => path, :placeholders => placeholders}
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.pages(name, path, contents, opts = {})
|
70
|
+
route(name, path)
|
71
|
+
placeholders = path.scan(/:(\S+)/).flatten!
|
72
|
+
Content.get(contents).each do |c|
|
73
|
+
page_path = path.dup
|
74
|
+
page_name = name.to_s
|
75
|
+
placeholders.each do |p|
|
76
|
+
page_path.sub!(':' + p, c[p])
|
77
|
+
page_name += ('_' + c[p])
|
78
|
+
end
|
79
|
+
|
80
|
+
@page_configs << Page.new(page_name, page_path, c, opts.dup)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/pekky/context.rb
CHANGED
@@ -1,72 +1,70 @@
|
|
1
1
|
module Pekky
|
2
|
+
# For every page instance in the site there is a corresponding context
|
3
|
+
# instance. The context is used when rendering the layout and views,
|
4
|
+
# encapsulating the page content and providing the built-in helpers.
|
5
|
+
#
|
6
|
+
# It also hosts any helpers provided in the helpers.rb file.
|
2
7
|
class Context
|
8
|
+
# Some attribute writers for storing information in contexts attached to
|
9
|
+
# layouts. This is because the layout needs to have access to the rendered
|
10
|
+
# view in order to inject it into itself.
|
3
11
|
attr_writer :view_output, :is_layout
|
4
12
|
|
5
|
-
|
6
|
-
|
7
|
-
def self.flush!
|
8
|
-
@shared.clear
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.get(name, sort)
|
12
|
-
@shared[name] ||= begin
|
13
|
-
path = File.join(SiteConfig[:content_dir], name)
|
14
|
-
if File.directory?(path)
|
15
|
-
collection = Dir[path + '/*.yml'].inject([]) do |a, y|
|
16
|
-
a << YAML.load_file(y)
|
17
|
-
a
|
18
|
-
end
|
19
|
-
collection.sort! {|a, b| a[sort] <=> b[sort]} if sort
|
20
|
-
collection
|
21
|
-
else
|
22
|
-
YAML.load_file(path + ".yml")
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
13
|
+
# Initialize stores the reference to the page instance so that it can
|
14
|
+
# expose it to the view when rendering.
|
27
15
|
def initialize(page)
|
28
16
|
@page = page
|
29
17
|
@is_layout = false
|
30
18
|
end
|
31
19
|
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
-
|
20
|
+
# A shortcut to the site configuration. This is here to allow users to
|
21
|
+
# store and access common, re-used details like the site title.
|
36
22
|
def config
|
37
|
-
|
23
|
+
Config
|
38
24
|
end
|
39
25
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
26
|
+
# A shortcut to the Formatters module. Users call methods on the module,
|
27
|
+
# which then calls the relevant formatter.
|
28
|
+
#
|
29
|
+
# format.textile(content[:body])
|
30
|
+
#
|
31
|
+
# Formatters accept a format like textile or haml and transform it into
|
32
|
+
# HTML markup.
|
33
|
+
def format
|
34
|
+
Format::Formatters
|
46
35
|
end
|
47
36
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
37
|
+
# Shortcut to the filters module. Filters accept HTML markup and manipulate
|
38
|
+
# it in some way. For example, extracting an modifying links. They're called
|
39
|
+
# in the same way as the formatters.
|
40
|
+
#
|
41
|
+
# filter.links(markup)
|
42
|
+
#
|
43
|
+
def filter
|
44
|
+
Format::Filters
|
54
45
|
end
|
55
46
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
RedCloth.new(val).to_html
|
47
|
+
# Makes a call to Content#get and returns the content specified by the path
|
48
|
+
# argument. An argument of a directory name will return an array of content
|
49
|
+
# objects. The path to a single YAML file will return just the one object.
|
50
|
+
def get(path)
|
51
|
+
Content.get(path)
|
62
52
|
end
|
63
|
-
|
53
|
+
|
54
|
+
# Renders the specified partial. The partial should only have it's name
|
55
|
+
# specified, not it's entire extension i.e. no '.html.haml'
|
56
|
+
#
|
57
|
+
# partial('profiles/full')
|
58
|
+
#
|
64
59
|
def partial(path)
|
65
|
-
file_name = "#{path}.html.#{
|
66
|
-
path = File.join(
|
60
|
+
file_name = "#{path}.html.#{Config[:templating]}"
|
61
|
+
path = File.join(Config[:partials_dir], file_name)
|
67
62
|
Tilt.new(path).render(self)
|
68
63
|
end
|
69
64
|
|
65
|
+
# A helper for injecting views into layouts. This method should be called
|
66
|
+
# at least once inside a layout. If you try calling it inside a view it will
|
67
|
+
# output a warning.
|
70
68
|
def view
|
71
69
|
if @is_layout
|
72
70
|
@view_output
|
@@ -75,29 +73,52 @@ module Pekky
|
|
75
73
|
end
|
76
74
|
end
|
77
75
|
|
76
|
+
# Generates a link to a stylesheet. At it's most basic you only need to
|
77
|
+
# specify the name, but you can also optionally specify the media, name etc.
|
78
|
+
#
|
79
|
+
# stylesheet('print', :media => 'print')
|
80
|
+
#
|
78
81
|
def stylesheet(name, opts = {})
|
79
82
|
opts['media'] ||= 'screen'
|
80
83
|
opts['rel'] ||= 'stylesheet'
|
81
84
|
opts['type'] = 'text/css'
|
82
|
-
opts['href'] = "#{qualify_path("#{
|
85
|
+
opts['href'] = "#{Format::Util.qualify_path("#{Config[:stylesheets_path]}/#{name}")}.css"
|
83
86
|
|
84
87
|
self_closing_tag('link', opts)
|
85
88
|
end
|
86
89
|
|
90
|
+
# Generates a javascript tag.
|
87
91
|
def javascript(name)
|
88
|
-
tag('script', '', :src => "#{qualify_path("#{
|
92
|
+
tag('script', '', :src => "#{Format::Util.qualify_path("#{Config[:javascripts_path]}/#{name}")}.js", :type => 'text/javascript')
|
89
93
|
end
|
90
94
|
|
95
|
+
# Generates an image tag. As a bare minimum, only the name is required, but
|
96
|
+
# you can optionally provide additional attributes.
|
97
|
+
#
|
98
|
+
# image('pix.jpg', :alt => 'Wicked Cool')
|
99
|
+
#
|
91
100
|
def image(path, opts = {})
|
92
|
-
opts['src'] = qualify_path("#{
|
101
|
+
opts['src'] = Format::Util.qualify_path("#{Config[:images_path]}/#{path}")
|
93
102
|
self_closing_tag('img', opts)
|
94
103
|
end
|
95
|
-
|
104
|
+
|
105
|
+
# Creates an anchor tag with the specified URL. It will also accept
|
106
|
+
# additional options for the anchor attributes. If you pass in a link with
|
107
|
+
# the 'pekky://' protocol, the string will be used as a name look up for an
|
108
|
+
# internal page and the URL will be replaced with a real path to the page.
|
109
|
+
#
|
110
|
+
# link('ZOMG', 'pekky://news_archive') #=> '<a href="/news/archive">ZOMG</a>'
|
111
|
+
#
|
96
112
|
def link(text, url, opts = {})
|
97
|
-
|
113
|
+
if url.index("pekky://") == 0
|
114
|
+
url = Format::Util.look_up_page(url)
|
115
|
+
end
|
116
|
+
opts['href'] = Format::Util.qualify_path(url)
|
98
117
|
tag('a', text, opts)
|
99
118
|
end
|
100
119
|
|
120
|
+
# Generates a content tag of the specified type. The child can either be
|
121
|
+
# text or HTML markup.
|
101
122
|
def tag(type, child, opts = nil)
|
102
123
|
if opts
|
103
124
|
"<#{type} #{attrs(opts)}>#{child}</#{type}>"
|
@@ -106,14 +127,16 @@ module Pekky
|
|
106
127
|
end
|
107
128
|
end
|
108
129
|
|
130
|
+
# Generates a self closing tag of the specified type.
|
109
131
|
def self_closing_tag(type, opts = nil)
|
110
132
|
if opts
|
111
133
|
"<#{type} #{attrs(opts)}/>"
|
112
134
|
else
|
113
|
-
"<#{type}
|
135
|
+
"<#{type}/>"
|
114
136
|
end
|
115
137
|
end
|
116
138
|
|
139
|
+
# A utility method for converting a hash into a string of attributes.
|
117
140
|
def attrs(opts)
|
118
141
|
out = opts.inject([]) do |a, o|
|
119
142
|
a << "#{o[0]}=\"#{o[1]}\""
|
@@ -123,54 +146,62 @@ module Pekky
|
|
123
146
|
out.join(" ").chomp
|
124
147
|
end
|
125
148
|
|
126
|
-
|
127
|
-
|
128
|
-
SiteConfig[:path_prefix] + path
|
129
|
-
else
|
130
|
-
path
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
149
|
+
# A convenience method which returns the top-level pages in the site. It
|
150
|
+
# excludes any what have been set as hidden.
|
134
151
|
def nav
|
135
152
|
Pekky.tree.pages.reject {|p| p.hidden? }
|
136
153
|
end
|
137
154
|
|
155
|
+
# Returns the page with the specified name. This name is set in the site.rb
|
156
|
+
# or is derived from a sanitised version of the page's path.
|
138
157
|
def page(name)
|
139
158
|
Pekky.tree[name]
|
140
159
|
end
|
141
160
|
|
161
|
+
# Returns the tree of all pages in the site. You can recurse through the
|
162
|
+
# entire tree by accessing the #children method on each page.
|
142
163
|
def pages
|
143
164
|
Pekky.tree.pages
|
144
165
|
end
|
145
166
|
|
167
|
+
# The content assigned to the current page.
|
146
168
|
def content
|
147
169
|
@page.content
|
148
170
|
end
|
149
171
|
|
172
|
+
# The path to the current page.
|
150
173
|
def path
|
151
|
-
qualify_path(@page.path)
|
174
|
+
Format::Util.qualify_path(@page.path)
|
152
175
|
end
|
153
176
|
|
177
|
+
# The children of the current page.
|
154
178
|
def children
|
155
179
|
@page.children
|
156
180
|
end
|
157
181
|
|
182
|
+
# The pages which sit above this page in the hierarchy i.e. parent,
|
183
|
+
# grant-parent etc.
|
158
184
|
def ancestors
|
159
185
|
collect_ancestors(@page)
|
160
186
|
end
|
161
187
|
|
188
|
+
# The current page's parent.
|
162
189
|
def parent
|
163
190
|
@page.parent
|
164
191
|
end
|
165
192
|
|
193
|
+
# Returns a boolean specifying if this page is the child of another.
|
166
194
|
def child?
|
167
195
|
@page.child?
|
168
196
|
end
|
169
197
|
|
198
|
+
# Returns a boolean specifying if this page is hidden.
|
170
199
|
def hidden?
|
171
200
|
@page.hidden?
|
172
201
|
end
|
173
202
|
|
203
|
+
# A helper method used to recursively collect the ancestors of the current
|
204
|
+
# page. It should never be called directly.
|
174
205
|
def collect_ancestors(page, ancestors = [])
|
175
206
|
if page.parent
|
176
207
|
collect_ancestors(page.parent, ancestors)
|
data/lib/pekky/pages.rb
CHANGED
@@ -1,76 +1,90 @@
|
|
1
1
|
module Pekky
|
2
|
-
class
|
3
|
-
|
4
|
-
|
5
|
-
@content = content
|
6
|
-
@opts = opts
|
7
|
-
end
|
8
|
-
|
9
|
-
def prepare
|
10
|
-
Page.new(@path, @content, @opts)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
2
|
+
# The page class encapsulates everything needed to render the actual content
|
3
|
+
# to disk -- the destination path, content and templates. It also contains
|
4
|
+
# the meta-data for a page -- parents, children, name etc.
|
14
5
|
class Page
|
15
|
-
|
6
|
+
# Various attribute accessors for storing all the values we reuse across
|
7
|
+
# the different methods.
|
8
|
+
attr_reader :path, :directory, :children, :content, :opts, :file_name, :name
|
9
|
+
|
10
|
+
# This accessor will be used to store a reference to the parent page --
|
11
|
+
# if there is one. The parent page will be updated when the tree gets
|
12
|
+
# built. See the Builder class for more info.
|
16
13
|
attr_accessor :parent
|
17
14
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
elsif path == '/'
|
26
|
-
:root
|
15
|
+
# The Page class is initialized with the path, the content -- either a
|
16
|
+
# Content instance or a hash -- and a hash of options. It then determines
|
17
|
+
# the name of the output file and it's path -- pages without extensions
|
18
|
+
# default to 'index.html'. It also determines the format if specified.
|
19
|
+
def initialize(name, path, content, opts)
|
20
|
+
if content.is_a?(Pekky::Content)
|
21
|
+
@content = content
|
27
22
|
else
|
28
|
-
|
29
|
-
path.match(/^\/(\S+)/)[1].gsub(/[\/\-]/, '_').to_sym
|
23
|
+
opts = content
|
30
24
|
end
|
25
|
+
|
26
|
+
@path = path
|
27
|
+
@opts = opts
|
28
|
+
@children = []
|
29
|
+
@opts[:name] = name
|
31
30
|
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
if match = @path.match(/^([\S+]?\/)(\S+)\.(\S+)$/)
|
32
|
+
@directory = match[1]
|
33
|
+
@format = match[3]
|
34
|
+
@file_name = "#{match[2]}.#{@format}"
|
35
35
|
else
|
36
|
-
|
36
|
+
@directory = path
|
37
|
+
@format = 'html'
|
38
|
+
@file_name = "index.html"
|
37
39
|
end
|
38
|
-
|
39
|
-
@file_name = "index.#{opts[:ext] || 'html'}"
|
40
|
-
|
41
|
-
@view = view(:view, opts)
|
42
|
-
@layout = view(:layout, opts)
|
43
|
-
|
44
|
-
@children = []
|
45
40
|
end
|
46
41
|
|
42
|
+
# Compares one page instance with another. It does this by comparing the
|
43
|
+
# name -- which will be unique.
|
47
44
|
def ==(other)
|
48
|
-
|
45
|
+
name == other.name
|
49
46
|
end
|
50
47
|
|
48
|
+
# Checks to see if the page is hidden i.e. it doesn't appear in the main
|
49
|
+
# navigation. The user can specify this when declaring a page in the
|
50
|
+
# site.rb
|
51
51
|
def hidden?
|
52
52
|
!!opts[:hidden]
|
53
53
|
end
|
54
54
|
|
55
|
+
# Is this page a child or does it sit at the top level of the site? It just
|
56
|
+
# checks to see if there is only one leading slash. Cheaty, but it works ;)
|
55
57
|
def child?
|
56
|
-
@path.
|
58
|
+
@path.count("/") == 1
|
57
59
|
end
|
58
60
|
|
61
|
+
# The business part of the page. It renders the page and outputs it to
|
62
|
+
# disk. Firstly it asks the Builder to ensure the target directory is on
|
63
|
+
# disk. Then it sets up the context -- encapsulating the content -- and the
|
64
|
+
# view and layout. Finally it renders the page and writes it to disk.
|
59
65
|
def render
|
60
|
-
Builder.directory(@
|
66
|
+
Builder.directory(@directory)
|
61
67
|
|
68
|
+
@content.reload if @content
|
62
69
|
context = Context.new(self)
|
63
|
-
view_output = @
|
64
|
-
context.view_output = view_output
|
65
|
-
context.is_layout = true
|
70
|
+
view_output = view(:view, @opts).render(context)
|
66
71
|
|
67
|
-
File.open(File.join(
|
68
|
-
|
72
|
+
File.open(File.join(Config[:output_dir], @directory, @file_name), 'w') do |f|
|
73
|
+
if @opts[:layout] == false
|
74
|
+
f.write(view_output)
|
75
|
+
else
|
76
|
+
layout = view(:layout, opts)
|
77
|
+
context.view_output = view_output
|
78
|
+
context.is_layout = true
|
79
|
+
f.write(layout.render(context))
|
80
|
+
end
|
69
81
|
end
|
70
82
|
end
|
71
83
|
|
72
84
|
private
|
73
85
|
|
86
|
+
# A helper for creating the instances of Tilt for rendering the templates.
|
87
|
+
# It handles both the view and the layout.
|
74
88
|
def view(type, opts)
|
75
89
|
if type == :view
|
76
90
|
name = opts[:view] ||= 'default'
|
@@ -80,43 +94,22 @@ module Pekky
|
|
80
94
|
dir = :layouts_dir
|
81
95
|
end
|
82
96
|
|
83
|
-
file_name = "#{name}.#{opts[:
|
84
|
-
path = File.join(
|
97
|
+
file_name = "#{name}.#{@format}.#{opts[:templating] || Config[:templating]}"
|
98
|
+
path = File.join(Config[dir], file_name)
|
85
99
|
Tilt.new(path)
|
86
100
|
end
|
87
|
-
end
|
88
|
-
|
89
|
-
class
|
90
|
-
|
101
|
+
end # Page
|
102
|
+
|
103
|
+
# The directory class is used to act as a parent for pages which don't have
|
104
|
+
# an actual direct parent i.e. /posts/* don't have a corresponding
|
105
|
+
# /posts/index.html
|
106
|
+
class Directory
|
107
|
+
def initialize(path)
|
91
108
|
@path = path
|
92
|
-
@blk = blk
|
93
|
-
@pages = []
|
94
|
-
matches = path.match(/:([a-zA-Z0-9_]+)/)
|
95
|
-
@path_params = matches[1..matches.length]
|
96
|
-
end
|
97
|
-
|
98
|
-
def prepare
|
99
|
-
instance_eval(&@blk)
|
100
|
-
@pages
|
101
|
-
end
|
102
|
-
|
103
|
-
def render(template, opts = {})
|
104
|
-
content = opts[:content] || {}
|
105
|
-
|
106
|
-
opts[:view] = template
|
107
|
-
|
108
|
-
path = @path.dup
|
109
|
-
@path_params.each do |p|
|
110
|
-
path.gsub!(/:#{p}/, opts[p.to_sym].to_s)
|
111
|
-
end
|
112
|
-
|
113
|
-
@pages << Page.new(path, content, opts)
|
114
109
|
end
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
def get(name, sort = nil)
|
119
|
-
Context.get(name, sort)
|
110
|
+
|
111
|
+
def render
|
120
112
|
end
|
121
|
-
end
|
122
|
-
end
|
113
|
+
end # Directory
|
114
|
+
end # Pekky
|
115
|
+
|
data/lib/pekky/server.rb
CHANGED
@@ -1,22 +1,28 @@
|
|
1
1
|
module Pekky
|
2
2
|
module Server
|
3
3
|
def self.start(port)
|
4
|
-
require 'rack'
|
5
4
|
Pekky.load
|
6
5
|
app = Rack::Builder.app {
|
7
|
-
use Rack::Static, :urls => %w(/stylesheets /javascripts /images), :root => Pekky::
|
6
|
+
use Rack::Static, :urls => %w(/stylesheets /javascripts /images), :root => Pekky::Config[:output_dir]
|
8
7
|
run Pekky::Server
|
9
8
|
}
|
10
|
-
Rack::
|
9
|
+
Rack::Server.new(:app => app, :Port => port || 3000).start
|
11
10
|
end
|
12
11
|
|
13
|
-
def self.call(env)
|
12
|
+
def self.call(env)
|
14
13
|
Pekky.build
|
15
14
|
path = env['PATH_INFO']
|
16
|
-
|
15
|
+
if path.match(/\.(\w+)$/)
|
16
|
+
file = File.join(Config[:output_dir], path)
|
17
|
+
type = Rack::Mime.mime_type(File.extname(path))
|
18
|
+
else
|
19
|
+
file = File.join(Config[:output_dir], path, "index.html")
|
20
|
+
type = "text/html"
|
21
|
+
end
|
22
|
+
|
17
23
|
if File.exists?(file)
|
18
24
|
output = File.open(file).read
|
19
|
-
[200, {"Content-Type" =>
|
25
|
+
[200, {"Content-Type" => type}, [output]]
|
20
26
|
else
|
21
27
|
[200, {"Content-Type" => "text/html"}, "Oh, file is not found; Pekky cries for you."]
|
22
28
|
end
|
data/lib/pekky/tree.rb
CHANGED
data/lib/pekky.rb
CHANGED
@@ -1,8 +1,18 @@
|
|
1
|
-
# [Tilt]:
|
1
|
+
# [Tilt]:http://github.com/rtomayko/tilt is used to hanndle the template
|
2
2
|
# rendering. Pekky assumes that you have the relevant template libraries
|
3
3
|
# installed.
|
4
|
+
require 'rubygems'
|
4
5
|
require 'tilt'
|
5
6
|
|
7
|
+
# [Nokogiri]:http://nokogiri.org is required for both parsing output and
|
8
|
+
# updating links and also for the tests -- checking that the correct markup is
|
9
|
+
# output.
|
10
|
+
require 'nokogiri'
|
11
|
+
|
12
|
+
# Rack is used to run the server. It'll use the default server — generally
|
13
|
+
# webbrick.
|
14
|
+
require 'rack'
|
15
|
+
|
6
16
|
# We need file utils for some of the generation steps, mainly adding and
|
7
17
|
# deleting directories.
|
8
18
|
require 'fileutils'
|
@@ -10,24 +20,35 @@ require 'fileutils'
|
|
10
20
|
# The actual content is stored in YAML files, so we need to require the lib.
|
11
21
|
require 'yaml'
|
12
22
|
|
13
|
-
# Require all the bits of Pekky. Without these, it is nothing. True friends!
|
14
|
-
dir = File.expand_path(File.dirname(__FILE__))
|
15
|
-
require dir + '/pekky/site_config'
|
16
|
-
require dir + '/pekky/pages'
|
17
|
-
require dir + '/pekky/tree'
|
18
|
-
require dir + '/pekky/context'
|
19
|
-
require dir + '/pekky/builder'
|
20
|
-
require dir + '/pekky/server'
|
21
|
-
require dir + '/pekky/console'
|
22
|
-
|
23
23
|
# # Pekky!
|
24
24
|
# The main module, which contains all the various kick-off methods. The majority
|
25
25
|
# of the actual logic is defined in the other source files. This module just
|
26
26
|
# serves as a container and shortcut.
|
27
27
|
module Pekky
|
28
|
-
# The
|
29
|
-
VERSION = '0.
|
30
|
-
VERSION_NAME = "
|
28
|
+
# The third release, dedicated to stunt-men everywhere.
|
29
|
+
VERSION = '0.3'
|
30
|
+
VERSION_NAME = "Shit-scared"
|
31
|
+
|
32
|
+
# A base class for all the errors in Pekky. We’re using custom errors so that
|
33
|
+
# we can filter out errors concerning configuration in one place, while still
|
34
|
+
# letting other errors filter through like normal. In other words, using a
|
35
|
+
# rescue block won't be an impediment to development.
|
36
|
+
class PekkyError < StandardError
|
37
|
+
attr_reader :message, :title
|
38
|
+
end
|
39
|
+
|
40
|
+
# Require all the bits of Pekky. Without these, it is nothing. True friends!
|
41
|
+
# This is done after the declaration of PekkyError to stop things blowing up.
|
42
|
+
dir = File.expand_path(File.dirname(__FILE__))
|
43
|
+
require dir + '/pekky/config'
|
44
|
+
require dir + '/pekky/pages'
|
45
|
+
require dir + '/pekky/content'
|
46
|
+
require dir + '/pekky/tree'
|
47
|
+
require dir + '/pekky/format'
|
48
|
+
require dir + '/pekky/context'
|
49
|
+
require dir + '/pekky/builder'
|
50
|
+
require dir + '/pekky/server'
|
51
|
+
require dir + '/pekky/console'
|
31
52
|
|
32
53
|
# Returns the tree module. The tree module represents the sites pages and
|
33
54
|
# contents arranged into a tree.
|
@@ -53,15 +74,16 @@ module Pekky
|
|
53
74
|
# Requires the site file and if defined the helpers file. #load can be called
|
54
75
|
# with an optional root. This is generally not necessary and is mainly used
|
55
76
|
# when testing.
|
56
|
-
def self.load(root = Dir.pwd)
|
57
|
-
|
58
|
-
require 'site'
|
59
|
-
|
77
|
+
def self.load(root = Dir.pwd)
|
78
|
+
Config.load(root)
|
79
|
+
require File.join(root, 'site')
|
80
|
+
helpers_path = File.join(root, 'helpers.rb')
|
81
|
+
require helpers_path if File.exists?(helpers_path)
|
60
82
|
end
|
61
83
|
|
62
84
|
# Used by the site.rb file to define all the options in the site.
|
63
85
|
def self.site(&blk)
|
64
|
-
|
86
|
+
Config.class_eval(&blk)
|
65
87
|
end
|
66
88
|
|
67
89
|
# Used bu the helpers.rb file to define additional helpers.
|
metadata
CHANGED
@@ -1,45 +1,58 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: pekky
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 2
|
8
|
-
version: "0.2"
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.4'
|
5
|
+
prerelease:
|
9
6
|
platform: ruby
|
10
|
-
authors:
|
7
|
+
authors:
|
11
8
|
- Luke Matthew Sutton
|
12
9
|
autorequire:
|
13
10
|
bindir: bin
|
14
11
|
cert_chain: []
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
dependencies:
|
19
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2011-11-23 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
20
15
|
name: tilt
|
16
|
+
requirement: &70364329519300 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
21
23
|
prerelease: false
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
24
|
+
version_requirements: *70364329519300
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: nokogiri
|
27
|
+
requirement: &70364329518840 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
29
33
|
type: :runtime
|
30
|
-
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70364329518840
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rack
|
38
|
+
requirement: &70364329518420 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70364329518420
|
31
47
|
description: A friendly static site builder
|
32
48
|
email: me@lukematthewsutton.com
|
33
|
-
executables:
|
49
|
+
executables:
|
34
50
|
- pekky
|
35
51
|
extensions: []
|
36
|
-
|
37
52
|
extra_rdoc_files: []
|
38
|
-
|
39
|
-
files:
|
53
|
+
files:
|
40
54
|
- LICENSE
|
41
55
|
- README
|
42
|
-
- Thorfile
|
43
56
|
- bin/pekky
|
44
57
|
- lib/pekky.rb
|
45
58
|
- lib/pekky/builder.rb
|
@@ -47,39 +60,35 @@ files:
|
|
47
60
|
- lib/pekky/context.rb
|
48
61
|
- lib/pekky/pages.rb
|
49
62
|
- lib/pekky/server.rb
|
50
|
-
- lib/pekky/
|
63
|
+
- lib/pekky/config.rb
|
51
64
|
- lib/pekky/tree.rb
|
52
65
|
- lib/pekky/templates/site.rb
|
53
66
|
- lib/pekky/templates/helpers.rb
|
54
|
-
has_rdoc: true
|
55
67
|
homepage: http://lukematthewsutton.com/pekky
|
56
68
|
licenses: []
|
57
|
-
|
58
69
|
post_install_message:
|
59
70
|
rdoc_options: []
|
60
|
-
|
61
|
-
require_paths:
|
71
|
+
require_paths:
|
62
72
|
- lib
|
63
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
requirements:
|
72
|
-
- -
|
73
|
-
- !ruby/object:Gem::Version
|
74
|
-
|
75
|
-
- 0
|
76
|
-
version: "0"
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ! '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
77
85
|
requirements: []
|
78
|
-
|
79
86
|
rubyforge_project:
|
80
|
-
rubygems_version: 1.
|
87
|
+
rubygems_version: 1.8.10
|
81
88
|
signing_key:
|
82
89
|
specification_version: 2
|
83
|
-
summary:
|
90
|
+
summary: Pekky’s main strengths come from the fact that it chooses explicitness over
|
91
|
+
magic, that it’s defaults are configurable and that it doesn’t assume all content
|
92
|
+
is derived from files on disk. For example Pekky can generate pages from databases,
|
93
|
+
CSV files or web API calls.
|
84
94
|
test_files: []
|
85
|
-
|
data/Thorfile
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
class App < Thor
|
2
|
-
desc "docs", "generate Docco documentation"
|
3
|
-
def docs
|
4
|
-
if File.exists?('docs')
|
5
|
-
system('rm -rf doc/*')
|
6
|
-
else
|
7
|
-
system('mkdir docs')
|
8
|
-
end
|
9
|
-
system('rocco lib/pekky.rb -o docs')
|
10
|
-
system('rocco lib/pekky/*.rb -o docs')
|
11
|
-
end
|
12
|
-
|
13
|
-
desc "gem", "build the gemfile"
|
14
|
-
def gem
|
15
|
-
system("gem build pekky.gemspec")
|
16
|
-
end
|
17
|
-
|
18
|
-
desc "install", "build the gem and install it"
|
19
|
-
def install
|
20
|
-
gem
|
21
|
-
f = Dir["pekky-*.gem"][0]
|
22
|
-
system("gem install #{f}")
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
class Spec < Thor
|
27
|
-
desc "all", "runs all of the specs for Pekky"
|
28
|
-
def all
|
29
|
-
path = File.expand_path(File.dirname(__FILE__)) + '/'
|
30
|
-
Dir["spec/*_spec.rb"].each do |s|
|
31
|
-
require path + s
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
desc "single SPEC_PREFIX", "runs the specified spec file"
|
36
|
-
def single(prefix)
|
37
|
-
require "spec/#{prefix}_spec"
|
38
|
-
end
|
39
|
-
end
|
data/lib/pekky/site_config.rb
DELETED
@@ -1,60 +0,0 @@
|
|
1
|
-
module Pekky
|
2
|
-
module SiteConfig
|
3
|
-
@config = {
|
4
|
-
:content_dir => '/content',
|
5
|
-
:view_dir => '/views',
|
6
|
-
:layouts_dir => '/views/layouts',
|
7
|
-
:partials_dir => '/views/partials',
|
8
|
-
:public_dir => '/public',
|
9
|
-
:output_dir => '/output',
|
10
|
-
:javascripts_path => '/javascripts',
|
11
|
-
:stylesheets_path => '/stylesheets',
|
12
|
-
:images_path => '/images',
|
13
|
-
:templating => 'haml',
|
14
|
-
:css_processor => 'sass'
|
15
|
-
}
|
16
|
-
|
17
|
-
def self.set_root(p)
|
18
|
-
@config[:root] = p
|
19
|
-
@config.each do |k, v|
|
20
|
-
if k.to_s.match(/_dir$/)
|
21
|
-
self[k] = File.join(p, v)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
@page_configs = []
|
27
|
-
|
28
|
-
def self.page_configs
|
29
|
-
@page_configs
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.[](name)
|
33
|
-
@config[name]
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.[]=(name, val)
|
37
|
-
@config[name] = val
|
38
|
-
end
|
39
|
-
|
40
|
-
def self.set(option, val)
|
41
|
-
if option.to_s.match(/_dir$/)
|
42
|
-
@config[option] = File.join(self[:root], val)
|
43
|
-
else
|
44
|
-
@config[option] = val
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.page(path, content, opts = {})
|
49
|
-
@page_configs << PageConfig.new(path, content, opts)
|
50
|
-
end
|
51
|
-
|
52
|
-
def self.generate(path, &blk)
|
53
|
-
@page_configs << GeneratorConfig.new(path, &blk)
|
54
|
-
end
|
55
|
-
|
56
|
-
def self.get(name, sort = nil)
|
57
|
-
Context.get(name, sort)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|