potion 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.gitignore +3 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +105 -0
  4. data/README.md +187 -0
  5. data/Rakefile +9 -0
  6. data/bin/potion +94 -0
  7. data/example_site/_config.yaml +2 -0
  8. data/example_site/_extensions/example_extension.rb +42 -0
  9. data/example_site/_layouts/default.haml +6 -0
  10. data/example_site/blog/_posts/2013-03-04-example-post-1/example-post-1.html.haml +10 -0
  11. data/example_site/blog/_posts/2013-03-04-example-post-1/kitten_and_duck.jpg +0 -0
  12. data/example_site/blog/_posts/2013-03-04-example-post-1/kitten_and_yarn.jpg +0 -0
  13. data/example_site/blog/_posts/2013-03-05-example-post-2/example-post-2.html.haml +4 -0
  14. data/example_site/index.html.haml +4 -0
  15. data/lib/potion.rb +23 -0
  16. data/lib/potion/extensions/category_helper.rb +9 -0
  17. data/lib/potion/extensions/deploy_to_gh_pages.rb +33 -0
  18. data/lib/potion/extensions/link_to_helper.rb +15 -0
  19. data/lib/potion/extensions/photo_helper.rb +21 -0
  20. data/lib/potion/extensions/photo_resize.rb +19 -0
  21. data/lib/potion/layout.rb +16 -0
  22. data/lib/potion/page.rb +2 -0
  23. data/lib/potion/post.rb +17 -0
  24. data/lib/potion/renderable.rb +78 -0
  25. data/lib/potion/site.rb +113 -0
  26. data/lib/potion/static_file.rb +33 -0
  27. data/lib/potion/version.rb +3 -0
  28. data/potion.gemspec +54 -0
  29. data/spec/extensions/category_spec.rb +15 -0
  30. data/spec/extensions/link_to_spec.rb +36 -0
  31. data/spec/fixtures/a-new-thing-2.html +4 -0
  32. data/spec/fixtures/test-site/_config.yaml +0 -0
  33. data/spec/fixtures/test-site/_extensions/test_extension.rb +0 -0
  34. data/spec/fixtures/test-site/_layouts/blog.haml +2 -0
  35. data/spec/fixtures/test-site/_layouts/main.haml +2 -0
  36. data/spec/fixtures/test-site/blog.html.haml +5 -0
  37. data/spec/fixtures/test-site/blog/_posts/2013-03-04-a-new-thing/a-new-thing.html.haml +5 -0
  38. data/spec/fixtures/test-site/blog/_posts/2013-03-04-a-new-thing/an-extra-thing.txt +0 -0
  39. data/spec/fixtures/test-site/css/main.css +0 -0
  40. data/spec/fixtures/test-site/javascript/main.js +1 -0
  41. data/spec/fixtures/test-site/portfolio/_posts/a-cool-thing/a-cool-thing.html.haml +4 -0
  42. data/spec/potion/layout_spec.rb +23 -0
  43. data/spec/potion/post_spec.rb +24 -0
  44. data/spec/potion/renderable_spec.rb +73 -0
  45. data/spec/potion/site_spec.rb +74 -0
  46. data/spec/potion/static_file_spec.rb +48 -0
  47. data/spec/spec_helper.rb +3 -0
  48. metadata +590 -0
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ example_site/_site
2
+ .DS_Store
3
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
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
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc "Run the specs for potion"
5
+ RSpec::Core::RakeTask.new do |t|
6
+ t.rspec_opts = "-c"
7
+ t.fail_on_error = false
8
+ t.verbose = false
9
+ end
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,2 @@
1
+ photo_resize:
2
+ size: "100x100"
@@ -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)