potion 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +105 -0
- data/README.md +187 -0
- data/Rakefile +9 -0
- data/bin/potion +94 -0
- data/example_site/_config.yaml +2 -0
- data/example_site/_extensions/example_extension.rb +42 -0
- data/example_site/_layouts/default.haml +6 -0
- data/example_site/blog/_posts/2013-03-04-example-post-1/example-post-1.html.haml +10 -0
- data/example_site/blog/_posts/2013-03-04-example-post-1/kitten_and_duck.jpg +0 -0
- data/example_site/blog/_posts/2013-03-04-example-post-1/kitten_and_yarn.jpg +0 -0
- data/example_site/blog/_posts/2013-03-05-example-post-2/example-post-2.html.haml +4 -0
- data/example_site/index.html.haml +4 -0
- data/lib/potion.rb +23 -0
- data/lib/potion/extensions/category_helper.rb +9 -0
- data/lib/potion/extensions/deploy_to_gh_pages.rb +33 -0
- data/lib/potion/extensions/link_to_helper.rb +15 -0
- data/lib/potion/extensions/photo_helper.rb +21 -0
- data/lib/potion/extensions/photo_resize.rb +19 -0
- data/lib/potion/layout.rb +16 -0
- data/lib/potion/page.rb +2 -0
- data/lib/potion/post.rb +17 -0
- data/lib/potion/renderable.rb +78 -0
- data/lib/potion/site.rb +113 -0
- data/lib/potion/static_file.rb +33 -0
- data/lib/potion/version.rb +3 -0
- data/potion.gemspec +54 -0
- data/spec/extensions/category_spec.rb +15 -0
- data/spec/extensions/link_to_spec.rb +36 -0
- data/spec/fixtures/a-new-thing-2.html +4 -0
- data/spec/fixtures/test-site/_config.yaml +0 -0
- data/spec/fixtures/test-site/_extensions/test_extension.rb +0 -0
- data/spec/fixtures/test-site/_layouts/blog.haml +2 -0
- data/spec/fixtures/test-site/_layouts/main.haml +2 -0
- data/spec/fixtures/test-site/blog.html.haml +5 -0
- data/spec/fixtures/test-site/blog/_posts/2013-03-04-a-new-thing/a-new-thing.html.haml +5 -0
- data/spec/fixtures/test-site/blog/_posts/2013-03-04-a-new-thing/an-extra-thing.txt +0 -0
- data/spec/fixtures/test-site/css/main.css +0 -0
- data/spec/fixtures/test-site/javascript/main.js +1 -0
- data/spec/fixtures/test-site/portfolio/_posts/a-cool-thing/a-cool-thing.html.haml +4 -0
- data/spec/potion/layout_spec.rb +23 -0
- data/spec/potion/post_spec.rb +24 -0
- data/spec/potion/renderable_spec.rb +73 -0
- data/spec/potion/site_spec.rb +74 -0
- data/spec/potion/static_file_spec.rb +48 -0
- data/spec/spec_helper.rb +3 -0
- metadata +590 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
potion (0.0.2)
|
5
|
+
RedCloth
|
6
|
+
asciidoctor (>= 0.1.0)
|
7
|
+
bluecloth
|
8
|
+
builder
|
9
|
+
bundler (>= 1.0.0)
|
10
|
+
coffee-script
|
11
|
+
commander (>= 4.1.3)
|
12
|
+
contest
|
13
|
+
creole
|
14
|
+
directory_watcher
|
15
|
+
erubis
|
16
|
+
haml (>= 2.2.11)
|
17
|
+
kramdown
|
18
|
+
less
|
19
|
+
liquid
|
20
|
+
markaby
|
21
|
+
maruku
|
22
|
+
mini_magick
|
23
|
+
nokogiri
|
24
|
+
radius
|
25
|
+
rdiscount
|
26
|
+
rdoc
|
27
|
+
rdoc
|
28
|
+
redcarpet
|
29
|
+
require_all
|
30
|
+
sass
|
31
|
+
tilt (>= 1.3.4)
|
32
|
+
wikicloth
|
33
|
+
yajl-ruby
|
34
|
+
|
35
|
+
GEM
|
36
|
+
remote: https://rubygems.org/
|
37
|
+
specs:
|
38
|
+
RedCloth (4.2.9)
|
39
|
+
asciidoctor (0.1.3)
|
40
|
+
bluecloth (2.2.0)
|
41
|
+
builder (3.2.2)
|
42
|
+
coffee-script (2.2.0)
|
43
|
+
coffee-script-source
|
44
|
+
execjs
|
45
|
+
coffee-script-source (1.6.3)
|
46
|
+
commander (4.1.4)
|
47
|
+
highline (~> 1.6.11)
|
48
|
+
commonjs (0.2.6)
|
49
|
+
contest (0.1.3)
|
50
|
+
creole (0.5.0)
|
51
|
+
diff-lcs (1.2.1)
|
52
|
+
directory_watcher (1.5.1)
|
53
|
+
erubis (2.7.0)
|
54
|
+
execjs (1.4.0)
|
55
|
+
multi_json (~> 1.0)
|
56
|
+
expression_parser (0.9.0)
|
57
|
+
haml (4.0.3)
|
58
|
+
tilt
|
59
|
+
highline (1.6.19)
|
60
|
+
json (1.8.0)
|
61
|
+
kramdown (1.1.0)
|
62
|
+
less (2.3.2)
|
63
|
+
commonjs (~> 0.2.6)
|
64
|
+
liquid (2.5.1)
|
65
|
+
markaby (0.7.2)
|
66
|
+
builder (>= 2.0.0)
|
67
|
+
maruku (0.6.1)
|
68
|
+
syntax (>= 1.0.0)
|
69
|
+
mini_magick (3.6.0)
|
70
|
+
subexec (~> 0.2.1)
|
71
|
+
mini_portile (0.5.1)
|
72
|
+
multi_json (1.7.7)
|
73
|
+
nokogiri (1.6.0)
|
74
|
+
mini_portile (~> 0.5.0)
|
75
|
+
radius (0.7.3)
|
76
|
+
rake (10.0.3)
|
77
|
+
rdiscount (2.1.6)
|
78
|
+
rdoc (4.0.1)
|
79
|
+
json (~> 1.4)
|
80
|
+
redcarpet (3.0.0)
|
81
|
+
require_all (1.2.1)
|
82
|
+
rspec (2.13.0)
|
83
|
+
rspec-core (~> 2.13.0)
|
84
|
+
rspec-expectations (~> 2.13.0)
|
85
|
+
rspec-mocks (~> 2.13.0)
|
86
|
+
rspec-core (2.13.0)
|
87
|
+
rspec-expectations (2.13.0)
|
88
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
89
|
+
rspec-mocks (2.13.0)
|
90
|
+
sass (3.2.10)
|
91
|
+
subexec (0.2.3)
|
92
|
+
syntax (1.0.0)
|
93
|
+
tilt (1.4.1)
|
94
|
+
wikicloth (0.8.0)
|
95
|
+
builder
|
96
|
+
expression_parser
|
97
|
+
yajl-ruby (1.1.0)
|
98
|
+
|
99
|
+
PLATFORMS
|
100
|
+
ruby
|
101
|
+
|
102
|
+
DEPENDENCIES
|
103
|
+
potion!
|
104
|
+
rake
|
105
|
+
rspec (~> 2)
|
data/README.md
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
What is Potion?
|
2
|
+
===============
|
3
|
+
|
4
|
+
Potion is a simple, clean, and easily extensible static site generator. Potion is designed from the ground up to be friendly to any tech-savvy person running a blog or website of almost any kind.
|
5
|
+
|
6
|
+
Running a website via a static site generator is great because all your assets and files are able to be easily version-controlled and backed-up. Hosting for static sites is inexpensive, and a server that's only serving static files can handle a lot more load given the same resources as compared to a server that is serving a site made in a dynamic CMS.
|
7
|
+
|
8
|
+
Potion was inspired by Jekyll, however Jekyll was designed to be run as a service that does not allow arbitrary Ruby code to be run (hence it's use of Liquid and Markdown, as opposed to things like Haml). This suits it's purpose perfectly for GitHub, but imposes unnecessary restrictions for other users.
|
9
|
+
|
10
|
+
Additionally Jekyll has no concept of files being associated with Posts, which makes writing plugins to handle things like images on a per-post basis very difficult.
|
11
|
+
|
12
|
+
Potion was designed to address these issues.
|
13
|
+
|
14
|
+
Why would I use Potion?
|
15
|
+
-----------------------
|
16
|
+
|
17
|
+
* Potion provides support for nearly every major template and markup language including HTML, HAML, Markdown, Liquid, SASS and many more (thanks to Tilt!)
|
18
|
+
* Potion is extremely extensible: want to make every photo on your site black-and-white without using Photoshop? No problems. And this is only the beginning!
|
19
|
+
|
20
|
+
|
21
|
+
Who should use Potion?
|
22
|
+
----------------------
|
23
|
+
|
24
|
+
* Anyone who is comfortable programming, and wants to build a website or blog.
|
25
|
+
* Anyone who likes Jekyll, but needs 'more'.
|
26
|
+
|
27
|
+
Who should avoid Potion?
|
28
|
+
------------------------
|
29
|
+
|
30
|
+
* People that want a WYSIWYG editor.
|
31
|
+
* Anyone who just wants a template website.
|
32
|
+
|
33
|
+
Installing Potion
|
34
|
+
=================
|
35
|
+
|
36
|
+
Assuming that you already have Ruby and Rubygems installed, installing Potion is a snap!
|
37
|
+
|
38
|
+
gem install potion
|
39
|
+
|
40
|
+
Directory Structure
|
41
|
+
===================
|
42
|
+
|
43
|
+
Much like Jekyll, Potion uses the directory structure of your site to determine the relationships of files to one another. A notable difference is that in Potion each 'Post' is a folder rather than a single file. This allows you to associate files like photos and downloads with a post in a simple fashion.
|
44
|
+
|
45
|
+
A typical file structure for a Potion site would look like:
|
46
|
+
|
47
|
+
/
|
48
|
+
|- _config.yml
|
49
|
+
|- _extensions
|
50
|
+
| |- photo_resizer.rb
|
51
|
+
| |- more_cowbell_helper.rb
|
52
|
+
|
|
53
|
+
|- _layouts
|
54
|
+
| |- main.haml
|
55
|
+
| |- blog.haml
|
56
|
+
|
|
57
|
+
|- assets
|
58
|
+
| |- main.js
|
59
|
+
| |- main.sass
|
60
|
+
|
|
61
|
+
|- blog
|
62
|
+
| |-_posts
|
63
|
+
| |- 2013-04-03-post-1
|
64
|
+
| | |- post-1.html.haml <- Post
|
65
|
+
| | |- kittens.jpg
|
66
|
+
| |
|
67
|
+
| |- 2013-04-05-post-2
|
68
|
+
| |- post-2.html.haml <- Post
|
69
|
+
| |- not-a-virus.exe
|
70
|
+
|
|
71
|
+
|- index.html.haml <- Page
|
72
|
+
|
73
|
+
|
74
|
+
Types of content
|
75
|
+
================
|
76
|
+
|
77
|
+
Potion supports 3 distinct types of content: Pages, Posts and Static File.
|
78
|
+
|
79
|
+
Pages
|
80
|
+
-----
|
81
|
+
|
82
|
+
A 'Page' is any page on your website that will not be a part of a series. A contact page or about page for instance. For a file to be classified as a Page it must have a YAML header, but must not reside in a path that contains the '_posts' qualifier.
|
83
|
+
|
84
|
+
Posts
|
85
|
+
-----
|
86
|
+
|
87
|
+
A 'Post' is any page that will be part of a series. For instance a blog post, or one of the pages in a catalogue. A post can have multiple static files associated with it. For a file to be classified as a Post it must have a YAML header and have a '_posts' folder upstream in it's path.
|
88
|
+
|
89
|
+
Static Files
|
90
|
+
------------
|
91
|
+
|
92
|
+
A static file is any file that does not have a YAML header. Static Files are still processed by Potion to allow extensions the opportunity to perform operations like image resizing etc.
|
93
|
+
|
94
|
+
Built-in extensions and helpers
|
95
|
+
===============================
|
96
|
+
|
97
|
+
SOMETHING ABOUT EXTENSIONS AND HELPERS
|
98
|
+
|
99
|
+
Customizing Potion
|
100
|
+
==================
|
101
|
+
|
102
|
+
There are two main ways of customizing how Potion functions: Helpers and Extensions.
|
103
|
+
|
104
|
+
Helpers
|
105
|
+
-------
|
106
|
+
|
107
|
+
Helpers are analagous to Rails Helpers, they are methods that you call from a page or post that provide some functionality. Potion ships with a number of built-in helpers, eg:
|
108
|
+
|
109
|
+
= # Find the first photo associated with the current Post that
|
110
|
+
= # has the work 'kitten' in the filename and insert it here.
|
111
|
+
|
112
|
+
= photo("kitten")
|
113
|
+
|
114
|
+
Writing a helper is simple, just add a Ruby file to the `_extensions` directory in the root of your site:
|
115
|
+
|
116
|
+
# /_extensions/christmas_annoyance_helper.rb
|
117
|
+
module Potion::Helpers
|
118
|
+
def christmas_annoyance(repeat = 0)
|
119
|
+
output = ""
|
120
|
+
repeat.times do
|
121
|
+
output << "<p>Jingle, Jingle, Jingle bell rock<br />"
|
122
|
+
output << "What a bright time<br />"
|
123
|
+
output << "Jingle, Jingle, Jingle bell rock<br />"
|
124
|
+
output << "It's the right time</p>"
|
125
|
+
end
|
126
|
+
|
127
|
+
output
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
Whatever the helper returns will be inserted into the calling layout, page or post at the position the helper was called. Helpers have full access to all the information that Potion gathers about your site. Some useful examples include:
|
132
|
+
|
133
|
+
@metadata # a hash of the data loaded from the YAML header of the page or post
|
134
|
+
@output_path # the path that the current file will be output to
|
135
|
+
@relative_output_path # the path that the current will be output to relative to the root of the site
|
136
|
+
@static_files # the list of static files associated with a Post, not available for pages
|
137
|
+
@site # a reference to the Site object that is associated with the page or post
|
138
|
+
@layout # a reference to the Layout object that is associated with the page or post
|
139
|
+
|
140
|
+
@site.config # a hash of the data loaded from the _config.yml file in the site's root
|
141
|
+
@site.metadata # a hash that extensions and helpers can use to store data they generate
|
142
|
+
@site.base_path # the absolute path of the source site
|
143
|
+
@site.destination_path # the absolute path of the destination for the site
|
144
|
+
@site.posts # an array of all the posts in the site
|
145
|
+
@site.pages # an array of all the pages in the site
|
146
|
+
@site.static_files # an array of all the static files in the site
|
147
|
+
@site.layouts # an array of all the layouts in the site
|
148
|
+
@site.extensions # an array of all the extensions currently loaded for the site
|
149
|
+
|
150
|
+
For more information on available attributes it's probably best just to refer to the code (there's not much of it, and it's clean!)
|
151
|
+
|
152
|
+
Extensions
|
153
|
+
----------
|
154
|
+
|
155
|
+
Writing an extension gives you the ability to 'filter' each file before it is written to the generated site. This is a good way to resize photos or perform some transformation on all the posts in your site.
|
156
|
+
|
157
|
+
An extension is simply a Ruby class that responds to the `process` instance method and takes a single argument. Each extension also needs to register itself after it loads by calling the `Potion::Site.register_extension` method.
|
158
|
+
|
159
|
+
For instance if you wanted to apply some 'artsy' filters to all the photos in your website or blog you could write an extension like this:
|
160
|
+
|
161
|
+
#/_extensions/too_cheap_for_instagram.rb
|
162
|
+
require 'mini_magick'
|
163
|
+
|
164
|
+
class TooCheapForInstagram
|
165
|
+
def process(item)
|
166
|
+
return unless item.is_a?(Potion::StaticFile)
|
167
|
+
extensions = [".jpg", ".jpeg", ".gif", ".png"]
|
168
|
+
return unless extensions.include?(File.extname(item.output_path).downcase)
|
169
|
+
|
170
|
+
image = MiniMagick::Image.read(item.content)
|
171
|
+
image.sepia_tone("80%")
|
172
|
+
image.vignette("10")
|
173
|
+
item.content = image.to_blob
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
Potion::Site.register_extension(TooCheapForInstagram)
|
178
|
+
|
179
|
+
Extensions alter the site by writing data back to the posts, pages and static files using their accessor methods. Some of the most useful accessor methods are:
|
180
|
+
|
181
|
+
item.content # the content of the post, page, or static file
|
182
|
+
item.metadata # a hash of the data loaded from the YAML header of the page or post
|
183
|
+
item.output_path # the path where the item will be written when the site is generated
|
184
|
+
item.relative_output_path # the output path of the item relative to the destination root
|
185
|
+
|
186
|
+
All of these are getters AND setters. Strange things may happen if you set anything other than the metadata and content though, so beware! To get information on all the getters and setters available it is best to look at the source for the relevant objects (/lib/potion/*.rb).
|
187
|
+
|
data/Rakefile
ADDED
data/bin/potion
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'potion'
|
6
|
+
require 'fileutils'
|
7
|
+
require 'webrick'
|
8
|
+
require 'commander/import'
|
9
|
+
require 'directory_watcher'
|
10
|
+
|
11
|
+
include WEBrick
|
12
|
+
|
13
|
+
program :version, "0.0.1"
|
14
|
+
program :description, 'A simple, extensible, static site generator that supports code, photos and files'
|
15
|
+
|
16
|
+
command :preview do |c|
|
17
|
+
c.syntax = 'potion preview [options]'
|
18
|
+
c.summary = ''
|
19
|
+
c.description = ''
|
20
|
+
c.example 'description', 'command example'
|
21
|
+
c.option '--no-auto', 'Turn off automatic regeneration, only do this if you are having performance issues'
|
22
|
+
c.action do |args, options|
|
23
|
+
site_root = `pwd`.strip
|
24
|
+
destination = File.join(site_root, "_site")
|
25
|
+
FileUtils.rm_rf(destination)
|
26
|
+
if !options.no_auto.nil?
|
27
|
+
site = Potion::Site.new(site_root, destination)
|
28
|
+
site.write
|
29
|
+
else
|
30
|
+
site = Potion::Site.new(site_root, destination, true)
|
31
|
+
site.write
|
32
|
+
|
33
|
+
puts "Starting with automatic site regeneration..."
|
34
|
+
dw = DirectoryWatcher.new(site_root, :glob => '**/*', :pre_load => true)
|
35
|
+
dw.interval = 1
|
36
|
+
dw.add_observer do |*args|
|
37
|
+
unless args.first.path.include?("_site")
|
38
|
+
puts "File changes detected, regenerating site..."
|
39
|
+
FileUtils.rm_rf(destination)
|
40
|
+
site = Potion::Site.new(site_root, destination, true)
|
41
|
+
site.write
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
dw.start
|
46
|
+
end
|
47
|
+
|
48
|
+
server = HTTPServer.new(:Port => 4000, :BindAddress => "localhost")
|
49
|
+
server.mount("/", HTTPServlet::FileHandler, destination)
|
50
|
+
|
51
|
+
thread = Thread.new { server.start }
|
52
|
+
trap("INT") {
|
53
|
+
server.shutdown
|
54
|
+
exit 0
|
55
|
+
}
|
56
|
+
|
57
|
+
thread.run
|
58
|
+
loop { sleep 1000 }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
command :deploy do |c|
|
63
|
+
c.syntax = 'potion deploy TARGET'
|
64
|
+
c.summary = ''
|
65
|
+
c.description = ''
|
66
|
+
c.example 'description', 'command example'
|
67
|
+
c.action do |args, options|
|
68
|
+
unless `git status`.include?("working directory clean")
|
69
|
+
puts "\nERROR: You should commit all your local changes before deploying.\n\n"
|
70
|
+
exit
|
71
|
+
end
|
72
|
+
|
73
|
+
site_root = `pwd`.strip
|
74
|
+
destination = File.join(site_root, "_site")
|
75
|
+
FileUtils.rm_rf(destination)
|
76
|
+
site = Potion::Site.new(site_root, destination)
|
77
|
+
site.write
|
78
|
+
|
79
|
+
server = HTTPServer.new(:Port => 4000, :BindAddress => "localhost")
|
80
|
+
server.mount("/", HTTPServlet::FileHandler, destination)
|
81
|
+
|
82
|
+
thread = Thread.new { server.start }
|
83
|
+
thread.run
|
84
|
+
|
85
|
+
puts "\n\n*** The site has been built, please check it at localhost:4000 and then hit enter to continue with the deploy..."
|
86
|
+
puts "*** Press CTRL+C if the site is not as expected and you with to abort the deploy.\n\n"
|
87
|
+
$stdin.gets
|
88
|
+
|
89
|
+
deploy_target = args.first
|
90
|
+
site.deploy_to(deploy_target)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'mini_magick'
|
2
|
+
|
3
|
+
module Potion::Helpers
|
4
|
+
def gallery(*args)
|
5
|
+
"GALLERY!! #{args.inspect}"
|
6
|
+
end
|
7
|
+
|
8
|
+
def christmas_annoyance(repeat = 0)
|
9
|
+
output = ""
|
10
|
+
repeat.times do
|
11
|
+
output << "<p>Jingle, Jingle, Jingle bell rock<br />"
|
12
|
+
output << "What a bright time<br />"
|
13
|
+
output << "Jingle, Jingle, Jingle bell rock<br />"
|
14
|
+
output << "It's the right time</p>"
|
15
|
+
end
|
16
|
+
|
17
|
+
output
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class TooCheapForInstagram
|
22
|
+
def process(item)
|
23
|
+
return unless item.is_a?(Potion::StaticFile)
|
24
|
+
extensions = [".jpg", ".jpeg", ".gif", ".png"]
|
25
|
+
return unless extensions.include?(File.extname(item.output_path).downcase)
|
26
|
+
|
27
|
+
image = MiniMagick::Image.read(item.content)
|
28
|
+
image.sepia_tone("80%")
|
29
|
+
image.vignette("10")
|
30
|
+
item.content = image.to_blob
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class MoreCowbell
|
35
|
+
def process(item)
|
36
|
+
item.content = item.content + "\nMORE COWBELL"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
Potion::Site.register_extension(TooCheapForInstagram)
|
42
|
+
Potion::Site.register_extension(MoreCowbell)
|