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
@@ -0,0 +1,6 @@
1
+ Default Template
2
+
3
+ - category("blog").each do |post|
4
+ = link_to(post.relative_output_path, post)
5
+
6
+ = yield
@@ -0,0 +1,10 @@
1
+ ---
2
+ layout: default
3
+ ---
4
+ Example Post 1
5
+ = @metadata["layout"]
6
+ = gallery(1,2,3)
7
+ = photo(0, "A kitten and a duckling!")
8
+ = photo("yarn", "A kitten taking a nap on some yarn!")
9
+ = photo("kitten_and_duck")
10
+ = christmas_annoyance(2)
@@ -0,0 +1,4 @@
1
+ ---
2
+ layout: default
3
+ ---
4
+ Example Post 2
@@ -0,0 +1,4 @@
1
+ ---
2
+ layout: default
3
+ ---
4
+ Index
data/lib/potion.rb ADDED
@@ -0,0 +1,23 @@
1
+ module Potion
2
+ module Helpers; end
3
+ module Deployers; end
4
+ end
5
+
6
+ require 'rubygems'
7
+ require 'bundler/setup'
8
+
9
+ require 'yaml'
10
+ require 'tilt'
11
+ require 'fileutils'
12
+
13
+ require 'potion/renderable'
14
+ require 'potion/layout'
15
+ require 'potion/static_file'
16
+ require 'potion/post'
17
+ require 'potion/page'
18
+ require 'potion/site'
19
+
20
+ Dir[File.expand_path(File.join(File.dirname(__FILE__), "/potion/extensions/*.rb"))].each do |file|
21
+ require file
22
+ end
23
+
@@ -0,0 +1,9 @@
1
+ module Potion::Helpers
2
+ def category(name)
3
+ posts = @site.posts.select do |post|
4
+ post.relative_output_path.split("/").select {|chunk| chunk != ""}[0] == name
5
+ end
6
+
7
+ posts.sort {|a,b| a.relative_output_path <=> b.relative_output_path}
8
+ end
9
+ end
@@ -0,0 +1,33 @@
1
+ module Potion::Deployers
2
+ def deploy_to_gh_pages(source_dir)
3
+ original_branch = `git status`.match(/# On branch (?<branch>\S*)/)['branch']
4
+
5
+ base_dir = `pwd`.strip
6
+ tmp_file_path = "/tmp/potion_#{Time.now.to_i}"
7
+ FileUtils.mkdir_p(tmp_file_path)
8
+ FileUtils.cp_r(File.join(source_dir, "."), tmp_file_path)
9
+
10
+ File.open(File.join(tmp_file_path, "potion_deploy.txt"), "w+") do |file|
11
+ file.puts "Built and deployed by Potion on: #{Time.now}"
12
+ end
13
+
14
+ switch_branches = `git checkout gh-pages 2>&1`.strip
15
+ raise "Could not switch branches: \n#{switch_branches}" unless switch_branches.include?("Switched to branch 'gh-pages'")
16
+
17
+ delete_current_files = `rm -rf * 2>&1`.strip
18
+ raise "Error clearing out old files: \n#{delete_current_files}" unless delete_current_files == ""
19
+
20
+ FileUtils.cp_r(File.join(tmp_file_path, "."), base_dir)
21
+
22
+ `git add .`
23
+ `git commit -a -m "Automatic commit by Potion as part of deploy: #{Time.now}"`
24
+
25
+ puts "*** Pushing to: origin/gh-pages"
26
+ `git push origin gh-pages`
27
+
28
+ puts
29
+
30
+ `git checkout #{original_branch}`
31
+ puts "\n*** Deploy complete."
32
+ end
33
+ end
@@ -0,0 +1,15 @@
1
+ module Potion::Helpers
2
+ def link_to(arg1, arg2 = nil, &block)
3
+ raise "link_to requires either an item and a block, or a content string and an item" if arg2.nil? && block.nil?
4
+
5
+ if block
6
+ content = block.call
7
+ item = arg1
8
+ else
9
+ content = arg1
10
+ item = arg2
11
+ end
12
+
13
+ "<a href=\"#{item.relative_output_path}\">#{content}</a>"
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ module Potion::Helpers
2
+ def photo(identifier, attributes = {})
3
+ raise "\n\nERROR: The 'photo' helper only works for posts.\n\n" unless self.is_a?(Potion::Post)
4
+
5
+ extensions = [".jpg", ".jpeg", ".png", ".gif"]
6
+ photos = @static_files.select {|file| extensions.include?(File.extname(file.path).downcase)}
7
+
8
+ if identifier.is_a?(Integer)
9
+ photo = photos.sort {|a,b| a.path <=> b.path }[identifier - 1]
10
+ else
11
+ candidates = photos.select{|photo| File.split(photo.path)[1].include?(identifier)}
12
+ raise "\n\nERROR: '#{identifier}' could refer to more than one photo: #{candidates.map{|x|File.split(x.path)[1]}.inspect} in #{self.path}\n\n" unless candidates.length == 1
13
+ photo = candidates.first
14
+ end
15
+
16
+ raise "\n\nERROR: No photo matching '#{identifier}' was found in: #{@path}\n\n" if photo.nil?
17
+ attributes_string = ""
18
+ attributes.each_pair {|k,v| attributes_string << "#{k}=\"#{v}\" "}
19
+ "<img #{attributes_string} src=\"#{photo.relative_output_path}\"/>"
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ require 'mini_magick'
2
+
3
+ class PhotoResize
4
+ def process(item)
5
+ return unless item.path.include?("_posts")
6
+ return if item.site.config["photo_resize"].nil?
7
+ return unless item.site.config["photo_resize"]["enabled"]
8
+ return if item.site.fast_build
9
+
10
+ extensions = [".jpg", ".jpeg", ".gif", ".png"]
11
+
12
+ return unless extensions.include?(File.extname(item.output_path).downcase)
13
+ image = MiniMagick::Image.read(item.content)
14
+ image.resize(item.site.config["photo_resize"]["size"])
15
+ item.content = image.to_blob
16
+ end
17
+ end
18
+
19
+ Potion::Site.register_extension(PhotoResize)
@@ -0,0 +1,16 @@
1
+ class Potion::Layout
2
+
3
+ attr_accessor :path, :name, :site, :content
4
+
5
+ def initialize(path, site)
6
+ @path = path
7
+ @site = site
8
+ @name = File.split(@path)[1].gsub(File.extname(@path), "")
9
+ @content = File.open(@path) {|stream| stream.read }
10
+ end
11
+
12
+ def ==(other)
13
+ @path == other.path &&
14
+ @site == other.site
15
+ end
16
+ end
@@ -0,0 +1,2 @@
1
+ class Potion::Page < Potion::Renderable
2
+ end
@@ -0,0 +1,17 @@
1
+ class Potion::Post < Potion::Renderable
2
+
3
+ attr_accessor :static_files
4
+
5
+ def initialize(path, site)
6
+ @static_files = Dir[File.dirname(path) + "/*.*"].reject {|x| x == path}.map {|x| Potion::StaticFile.new(x, site)}
7
+
8
+ @relative_output_path = path.gsub("_posts/", "")
9
+
10
+ super(path, site)
11
+ end
12
+
13
+ def write
14
+ @static_files.each {|file| file.write }
15
+ super
16
+ end
17
+ end
@@ -0,0 +1,78 @@
1
+ class Potion::Renderable
2
+ include Potion::Helpers
3
+
4
+ attr_accessor :path, :site, :metadata, :content, :layout, :output_path, :relative_output_path
5
+
6
+ def initialize(path, site)
7
+ @path = path
8
+ @site = site
9
+
10
+ load_content_and_metadata
11
+ @layout = @site.find_layout_by_name(@metadata["layout"])
12
+
13
+ @relative_output_path ||= @path
14
+ @relative_output_path = @relative_output_path.gsub(site.base_path, "")
15
+ @relative_output_path = @relative_output_path.gsub(File.extname(path), "") unless self.is_html?
16
+
17
+ @output_path = File.join(@site.destination_path, @relative_output_path)
18
+ end
19
+
20
+ def load_content_and_metadata
21
+ @content = File.open(path) {|file| file.read}
22
+ begin
23
+ @metadata = YAML.load(@content.slice!(/\A(---\s*\n.*?\n?)^(---\s*$\n?)/m, 0))
24
+ rescue Psych::SyntaxError
25
+ raise "\n\nERROR: Invalid YAML frontmatter in file: #{@path}\n\n"
26
+ end
27
+ end
28
+
29
+ def render
30
+ @site.class.extensions.each do |extension|
31
+ extension.new.process(self)
32
+ end
33
+
34
+ layout = Tilt.new(@layout.path) { @layout.content}
35
+
36
+ if self.is_html?
37
+ layout.render(self) do
38
+ @content
39
+ end
40
+ else
41
+ item = Tilt.new(@path) { @content }
42
+ layout.render(self) do
43
+ item.render(self)
44
+ end
45
+ end
46
+ end
47
+
48
+ def write
49
+ FileUtils.mkdir_p(File.split(@output_path)[0])
50
+ File.open(@output_path, "w+") do |stream|
51
+ stream.puts self.render
52
+ end
53
+ end
54
+
55
+ def ==(other)
56
+ return false unless self.class == other.class
57
+ self.instance_variables.each do |name|
58
+ return false unless self.instance_variable_get(name) == other.instance_variable_get(name)
59
+ end
60
+
61
+ true
62
+ end
63
+
64
+ def title
65
+ return self.metadata["title"] unless self.metadata["title"].nil?
66
+
67
+ filename = File.split(@path)[1]
68
+ filename.gsub!(/\d+-\d+-\d+-/, "")
69
+ filename.gsub!(File.extname(filename), "")
70
+ filename.gsub!(File.extname(filename), "") unless self.is_html?
71
+ filename.gsub!("-", " ")
72
+ filename[0].upcase + filename[1..-1]
73
+ end
74
+
75
+ def is_html?
76
+ File.extname(@path) == ".html"
77
+ end
78
+ end
@@ -0,0 +1,113 @@
1
+ class Potion::Site
2
+ include Potion
3
+ include Potion::Deployers
4
+ attr_accessor :base_path, :config, :pages, :posts, :static_files, :layouts, :files, :destination_path, :metadata, :fast_build
5
+
6
+ @@extensions = []
7
+
8
+ def self.register_extension(klass)
9
+ @@extensions << klass
10
+ end
11
+
12
+ def self.remove_extension(klass)
13
+ @@extensions = @@extensions - [klass]
14
+ end
15
+
16
+ def self.extensions
17
+ @@extensions
18
+ end
19
+
20
+ def initialize(base_path, destination_path, fast_build = false)
21
+ @base_path = base_path
22
+ @destination_path = destination_path
23
+ @fast_build = fast_build
24
+
25
+ @config = load_config
26
+ @metadata = {}
27
+
28
+ @files = find_all_files
29
+
30
+ load_extensions
31
+
32
+ @layouts = find_layouts
33
+ @posts = find_posts
34
+ @pages = find_pages
35
+ @static_files = find_static_files
36
+ end
37
+
38
+ def load_config
39
+ config_path = File.join(@base_path, "_config.yaml")
40
+ raise "No config file found at #{config_path}" unless File.exists?(config_path)
41
+ config = YAML.load(File.open(config_path))
42
+ return {} if config == false
43
+ config
44
+ end
45
+
46
+ def find_all_files
47
+ Dir[@base_path + "/**/*"].reject {|x| File.directory?(x)}
48
+
49
+ end
50
+
51
+ def load_extensions
52
+ site_extensions = @files.select {|path| path.include?("_extensions") && File.extname(path) == ".rb" }
53
+ site_extensions.each do |extension|
54
+ require extension
55
+ end
56
+ end
57
+
58
+ def find_layouts
59
+ layouts = @files.select {|path| path.include? "_layouts"}
60
+ layouts.map do |layout|
61
+ Layout.new(layout, self)
62
+ end
63
+ end
64
+
65
+ def find_layout_by_name(name)
66
+ @layouts.select {|layout| layout.name == name}.first
67
+ end
68
+
69
+ def find_posts
70
+ posts = @files.select {|path| path.include?("_posts")}
71
+ posts = filter_for_yaml_metadata(posts)
72
+ posts.map do |post|
73
+ Post.new(post, self)
74
+ end
75
+ end
76
+
77
+ def find_pages
78
+ pages = @files.reject {|path| path.include?("/_")}
79
+ pages = filter_for_yaml_metadata(pages)
80
+ pages.map do |page|
81
+ Page.new(page, self)
82
+ end
83
+ end
84
+
85
+ def find_static_files
86
+ static_files = @files.reject {|path| path.include?("/_")}
87
+ static_files = static_files - filter_for_yaml_metadata(static_files)
88
+ static_files.map do |static_file|
89
+ StaticFile.new(static_file, self)
90
+ end
91
+ end
92
+
93
+ def write
94
+ puts "*** Building...\n"
95
+
96
+ (@posts + @pages + @static_files).each do |item|
97
+ item.write
98
+ end
99
+ end
100
+
101
+ def deploy_to(target)
102
+ self.send("deploy_to_#{target}", @destination_path)
103
+ end
104
+
105
+ private
106
+
107
+ def filter_for_yaml_metadata(files)
108
+ files.select do |file|
109
+ prefix = File.open(file) {|stream| stream.read(3)}
110
+ prefix == "---"
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,33 @@
1
+ class Potion::StaticFile
2
+
3
+ attr_accessor :path, :site, :content, :relative_output_path, :output_path
4
+
5
+ def initialize(path, site)
6
+ @path = path
7
+ @site = site
8
+ @content = File.open(path) {|stream| stream.read }
9
+ @relative_output_path = @path.gsub(@site.base_path, "").gsub("_posts/", "")
10
+ @output_path = File.join(@site.destination_path, @relative_output_path)
11
+ end
12
+
13
+ def ==(other)
14
+ self.class.name == other.class.name &&
15
+ @path == other.path &&
16
+ @site == other.site
17
+ end
18
+
19
+ def render
20
+ @site.class.extensions.each do |extension|
21
+ extension.new.process(self)
22
+ end
23
+
24
+ @content
25
+ end
26
+
27
+ def write
28
+ FileUtils.mkdir_p(File.split(@output_path)[0])
29
+ File.open(@output_path, "w+") do |stream|
30
+ stream.puts self.render
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module Potion
2
+ VERSION = "0.0.2"
3
+ end
data/potion.gemspec ADDED
@@ -0,0 +1,54 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/potion/version", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "potion"
6
+ s.version = Potion::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ["Aaron Gough"]
9
+ s.email = ["aaron@aarongough.com"]
10
+ s.homepage = "http://github.com/aarongough/potion"
11
+ s.summary = "A static site generator that supports code, photos and files."
12
+ s.description = "A static site generator that supports code, photos and files."
13
+
14
+ s.required_rubygems_version = ">= 1.3.6"
15
+ s.rubyforge_project = "potion"
16
+
17
+ s.add_dependency "bundler", ">= 1.0.0"
18
+ s.add_dependency "tilt", ">= 1.3.4"
19
+ s.add_dependency "commander", ">= 4.1.3"
20
+ s.add_dependency "mini_magick"
21
+ s.add_dependency "directory_watcher"
22
+ s.add_dependency "require_all"
23
+
24
+ s.add_dependency 'asciidoctor', '>= 0.1.0'
25
+ s.add_dependency 'RedCloth'
26
+ s.add_dependency 'bluecloth'
27
+ s.add_dependency 'builder'
28
+ s.add_dependency 'coffee-script'
29
+ s.add_dependency 'contest'
30
+ s.add_dependency 'creole'
31
+ s.add_dependency 'erubis'
32
+ s.add_dependency 'haml', '>= 2.2.11'
33
+ s.add_dependency 'kramdown'
34
+ s.add_dependency 'less'
35
+ s.add_dependency 'liquid'
36
+ s.add_dependency 'markaby'
37
+ s.add_dependency 'maruku'
38
+ s.add_dependency 'nokogiri'
39
+ s.add_dependency 'radius'
40
+ s.add_dependency 'rdiscount'
41
+ s.add_dependency 'rdoc'
42
+ s.add_dependency 'redcarpet'
43
+ s.add_dependency 'sass'
44
+ s.add_dependency 'wikicloth'
45
+ s.add_dependency 'yajl-ruby'
46
+ s.add_dependency 'rdoc'
47
+
48
+ s.add_development_dependency "rspec", "~> 2"
49
+ s.add_development_dependency "rake"
50
+
51
+ s.files = `git ls-files`.split("\n")
52
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
53
+ s.require_path = 'lib'
54
+ end