engine_of_war 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rbenv-version +1 -0
- data/.yardopts +2 -0
- data/Gemfile +4 -0
- data/Rakefile +10 -0
- data/Readme.textile +0 -0
- data/engine_of_war.gemspec +34 -0
- data/lib/engine_of_war.rb +24 -0
- data/lib/engine_of_war/app.rb +67 -0
- data/lib/engine_of_war/extensions/string.rb +10 -0
- data/lib/engine_of_war/layout.rb +29 -0
- data/lib/engine_of_war/my_red_cloth_template.rb +78 -0
- data/lib/engine_of_war/page.rb +68 -0
- data/lib/engine_of_war/page_collection.rb +47 -0
- data/lib/engine_of_war/recommendation.rb +34 -0
- data/lib/engine_of_war/version.rb +3 -0
- data/spec/assets_spec.rb +59 -0
- data/spec/atom_spec.rb +55 -0
- data/spec/blog_spec.rb +140 -0
- data/spec/code_filter_spec.rb +46 -0
- data/spec/frontmatter_spec.rb +41 -0
- data/spec/image_filter_spec.rb +73 -0
- data/spec/page_collection_spec.rb +67 -0
- data/spec/page_spec.rb +129 -0
- data/spec/recommendations_spec.rb +44 -0
- data/spec/rendering_spec.rb +49 -0
- data/spec/root_spec.rb +25 -0
- data/spec/spec_helper.rb +71 -0
- metadata +246 -0
data/.gitignore
ADDED
data/.rbenv-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.9.3-p0
|
data/.yardopts
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
data/Readme.textile
ADDED
File without changes
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/engine_of_war/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Thunderbolt Labs"]
|
6
|
+
gem.email = ["us@thunderboltlabs.com"]
|
7
|
+
gem.description = "Semi-static site engine."
|
8
|
+
gem.summary = "Semi-static site endine based on Padrino"
|
9
|
+
gem.homepage = "http://thunderboltlabs.com"
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "engine_of_war"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = EngineOfWar::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency 'sinatra'
|
19
|
+
gem.add_dependency "compass"
|
20
|
+
gem.add_dependency "padrino"
|
21
|
+
gem.add_dependency "active_support"
|
22
|
+
gem.add_dependency "builder"
|
23
|
+
gem.add_dependency 'haml'
|
24
|
+
gem.add_dependency 'sass', ">= 3.1.7"
|
25
|
+
gem.add_dependency "RedCloth"
|
26
|
+
gem.add_dependency "coffee-script"
|
27
|
+
|
28
|
+
gem.add_development_dependency "yard"
|
29
|
+
gem.add_development_dependency "RedCloth"
|
30
|
+
gem.add_development_dependency "rake"
|
31
|
+
gem.add_development_dependency "rspec"
|
32
|
+
gem.add_development_dependency "capybara"
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'compass'
|
3
|
+
require "builder"
|
4
|
+
require 'haml'
|
5
|
+
require 'sass'
|
6
|
+
require "RedCloth"
|
7
|
+
require "coffee-script"
|
8
|
+
require 'padrino-core/application/rendering'
|
9
|
+
require 'padrino-helpers'
|
10
|
+
require 'active_support/hash_with_indifferent_access'
|
11
|
+
require 'active_support/core_ext/object/blank'
|
12
|
+
require 'json'
|
13
|
+
|
14
|
+
module EngineOfWar; end
|
15
|
+
|
16
|
+
require 'engine_of_war/version'
|
17
|
+
require 'engine_of_war/extensions/string'
|
18
|
+
require 'engine_of_war/my_red_cloth_template'
|
19
|
+
require 'engine_of_war/layout'
|
20
|
+
require 'engine_of_war/page'
|
21
|
+
require 'engine_of_war/page_collection'
|
22
|
+
require 'engine_of_war/recommendation'
|
23
|
+
require 'engine_of_war/app'
|
24
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class EngineOfWar::App < Sinatra::Base
|
2
|
+
register Padrino::Rendering
|
3
|
+
register Padrino::Helpers
|
4
|
+
|
5
|
+
Compass.configuration do |config|
|
6
|
+
config.project_path = File.dirname(__FILE__)
|
7
|
+
config.sass_dir = 'views/css'
|
8
|
+
end
|
9
|
+
|
10
|
+
set :haml, { :format => :html5 }
|
11
|
+
set :scss, Compass.sass_engine_options
|
12
|
+
# set :root, File.expand_path(File.dirname(__FILE__) + '/../')
|
13
|
+
set(:config) { File.expand_path(root + '/config/') }
|
14
|
+
|
15
|
+
def render_page_with_layout(page)
|
16
|
+
render_page(page, :layout => "layouts/#{page.layout}", :layout_engine => :haml)
|
17
|
+
end
|
18
|
+
|
19
|
+
get "/posts.atom" do
|
20
|
+
content_type :rss
|
21
|
+
builder do |xml|
|
22
|
+
xml.instruct! :xml, :version => '1.0'
|
23
|
+
xml.rss :version => "2.0" do
|
24
|
+
xml.channel do
|
25
|
+
xml.title "Thunderbolt Labs"
|
26
|
+
xml.link "http://#{request.host}"
|
27
|
+
|
28
|
+
collection("posts").each do |post|
|
29
|
+
xml.item do
|
30
|
+
xml.title post.meta[:title]
|
31
|
+
xml.link "http://#{request.host}#{post.url}"
|
32
|
+
xml.description render_page(post)
|
33
|
+
xml.pubDate post.meta[:date].rfc822
|
34
|
+
xml.guid "http://#{request.host}#{post.url}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
get %r{^/([^.]+)$} do |name|
|
43
|
+
render_page_with_layout(EngineOfWar::Page.new(name))
|
44
|
+
end
|
45
|
+
|
46
|
+
get "/" do
|
47
|
+
render_page_with_layout(EngineOfWar::Page.new(:index))
|
48
|
+
end
|
49
|
+
|
50
|
+
get "/*.*" do |name, ext|
|
51
|
+
content_type ext
|
52
|
+
render :"#{name}", :layout => false
|
53
|
+
end
|
54
|
+
|
55
|
+
helpers do
|
56
|
+
def render_page(page, opts = {})
|
57
|
+
opts[:locals] ||= {}
|
58
|
+
opts[:locals][:meta] = page.meta
|
59
|
+
opts[:locals][:page] = page
|
60
|
+
send(page.engine, page.source, opts)
|
61
|
+
end
|
62
|
+
|
63
|
+
def collection(dir)
|
64
|
+
EngineOfWar::PageCollection.new(dir)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Layout
|
2
|
+
attr_reader :page
|
3
|
+
|
4
|
+
def initialize(page)
|
5
|
+
@page = page
|
6
|
+
end
|
7
|
+
|
8
|
+
def name
|
9
|
+
potential_layouts.first + ".html"
|
10
|
+
end
|
11
|
+
|
12
|
+
def potential_layouts
|
13
|
+
[layout_from_directory, layout_from_meta, default_layout].reject(&:blank?).compact.select do |f|
|
14
|
+
File.exists?(File.join(page.views_root, "layouts", "#{f}.html.haml"))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def layout_from_directory
|
19
|
+
File.dirname(page.request_path).sub!(%r{^[/.]}, "")
|
20
|
+
end
|
21
|
+
|
22
|
+
def layout_from_meta
|
23
|
+
page.meta[:layout]
|
24
|
+
end
|
25
|
+
|
26
|
+
def default_layout
|
27
|
+
"application"
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
class EngineOfWar::RedClothTemplate < Tilt::RedClothTemplate
|
2
|
+
Tilt.register self, "textile"
|
3
|
+
|
4
|
+
BASE_IMAGE_URL = "/images"
|
5
|
+
include Padrino::Helpers::TagHelpers
|
6
|
+
|
7
|
+
def prepare
|
8
|
+
@engine = RedCloth.new(image_filter(code_filter(data)))
|
9
|
+
@output = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def code_filter(txt)
|
15
|
+
txt.gsub(/@@@ *(\w*)\r?\n? *(.+?)\r?\n?@@@/m) do
|
16
|
+
klass = $1.present? ? " class=\"#{$1.downcase}\"" : ""
|
17
|
+
"<pre><code#{klass}>#{$2}</code></pre>"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def image_filter(txt)
|
22
|
+
txt.gsub(/^%([<>]?)([0-9a-zA-Z_.-]+)([^%]*)%/) do
|
23
|
+
float = $1
|
24
|
+
image_id = $2
|
25
|
+
size = $3
|
26
|
+
|
27
|
+
float = :right if float == '>'
|
28
|
+
float = :left if float == '<'
|
29
|
+
|
30
|
+
uploaded_image_tag(image_id, :size => size, :float => float)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def uploaded_image_tag(image_name, opts = {})
|
35
|
+
opts.symbolize_keys
|
36
|
+
|
37
|
+
opts[:size] = "original" if opts[:size].blank?
|
38
|
+
opts[:size].strip!
|
39
|
+
|
40
|
+
basename, ext = image_name.split('.')
|
41
|
+
image_src = "#{BASE_IMAGE_URL}/#{basename}/#{opts[:size]}.#{ext}"
|
42
|
+
meta = meta_for_image(basename)
|
43
|
+
|
44
|
+
return "<div class='warning'>unknown image #{image_name}</div>" unless File.directory?(image_basedir(basename))
|
45
|
+
|
46
|
+
image_options = { :src => image_src }
|
47
|
+
image_options[:class] = "float-#{opts[:float]}" if opts[:float]
|
48
|
+
image_options[:alt] = meta[:description] if meta[:description]
|
49
|
+
|
50
|
+
href = meta[:source_url]
|
51
|
+
|
52
|
+
img_tag = content_tag :img, "", image_options
|
53
|
+
if href
|
54
|
+
return "<a href='#{href}'>#{img_tag}</a>"
|
55
|
+
else
|
56
|
+
return img_tag
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def image_basedir(basename)
|
61
|
+
File.join(EngineOfWar::App.settings.public, BASE_IMAGE_URL, basename)
|
62
|
+
end
|
63
|
+
|
64
|
+
def meta_for_image(basename)
|
65
|
+
file = File.join(image_basedir(basename), "meta.yml")
|
66
|
+
if File.exist?(file)
|
67
|
+
YAML.load_file(file).delete_if {|k, v| v.blank? }
|
68
|
+
else
|
69
|
+
{}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Required to confuse padrino
|
74
|
+
def block_is_template?(_)
|
75
|
+
false
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
@@ -0,0 +1,68 @@
|
|
1
|
+
class EngineOfWar::Page
|
2
|
+
YAML_REGEXP = /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
|
3
|
+
|
4
|
+
attr_reader :request_path, :views_root, :meta, :source
|
5
|
+
|
6
|
+
def initialize(request_path)
|
7
|
+
@views_root = EngineOfWar::App.settings.views
|
8
|
+
@request_path = normalize_path(request_path)
|
9
|
+
@entire_source = File.read(template_path)
|
10
|
+
@meta, @source = split_body
|
11
|
+
end
|
12
|
+
|
13
|
+
def layout
|
14
|
+
Layout.new(self).name
|
15
|
+
end
|
16
|
+
|
17
|
+
def url
|
18
|
+
request_path
|
19
|
+
end
|
20
|
+
|
21
|
+
def engine
|
22
|
+
template_path.split(".").last.to_sym
|
23
|
+
end
|
24
|
+
|
25
|
+
def github_edit_url
|
26
|
+
relative_template_path = template_path.sub(views_root, "views")
|
27
|
+
"https://github.com/thunderboltlabs/thunderboltlabs/edit/master/#{relative_template_path}"
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def normalize_path(path)
|
33
|
+
path = path.to_s
|
34
|
+
path.sub!(%r{/$}, "")
|
35
|
+
path.start_with?("/") ? path : "/#{path}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def split_body
|
39
|
+
data = {}
|
40
|
+
body = @entire_source
|
41
|
+
if @entire_source =~ YAML_REGEXP
|
42
|
+
data = hash_from_yaml($1)
|
43
|
+
body = @entire_source.split(YAML_REGEXP).last
|
44
|
+
end
|
45
|
+
|
46
|
+
[HashWithIndifferentAccess.new(data), body]
|
47
|
+
end
|
48
|
+
|
49
|
+
def hash_from_yaml(yaml)
|
50
|
+
begin
|
51
|
+
return YAML.load(yaml)
|
52
|
+
rescue ArgumentError => e
|
53
|
+
raise e, "#{e.message}:\n#{yaml.inspect}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def template_path
|
58
|
+
first_template_matching(request_path)
|
59
|
+
end
|
60
|
+
|
61
|
+
def first_template_matching(name)
|
62
|
+
pattern = File.join(views_root, "#{name}.html.*")
|
63
|
+
candidates = Dir[pattern]
|
64
|
+
raise Sinatra::NotFound, "Could find no files matching #{pattern}" if candidates.empty?
|
65
|
+
candidates.first
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class EngineOfWar::PageCollection < Array
|
2
|
+
attr_accessor :collection_root
|
3
|
+
|
4
|
+
def initialize(collection_root)
|
5
|
+
super()
|
6
|
+
self.collection_root = collection_root
|
7
|
+
self.concat(pages)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def pages
|
13
|
+
unsorted_pages.sort_by do |page|
|
14
|
+
page.meta[:date]
|
15
|
+
end.reverse
|
16
|
+
end
|
17
|
+
|
18
|
+
def unsorted_pages
|
19
|
+
page_template_paths.map do |file_path|
|
20
|
+
EngineOfWar::Page.new(file_path)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def page_template_paths
|
25
|
+
page_files.map do |full_path|
|
26
|
+
template_path_for(full_path)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def template_path_for(full_path)
|
31
|
+
full_path[views_root] = ""
|
32
|
+
full_path[%r{\.[^/]*}] = ""
|
33
|
+
full_path
|
34
|
+
end
|
35
|
+
|
36
|
+
def page_files
|
37
|
+
Dir[File.join(pages_root, "**/*")]
|
38
|
+
end
|
39
|
+
|
40
|
+
def pages_root
|
41
|
+
File.join(views_root, collection_root)
|
42
|
+
end
|
43
|
+
|
44
|
+
def views_root
|
45
|
+
EngineOfWar::App.settings.views
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class EngineOfWar::Recommendation
|
2
|
+
def self.random
|
3
|
+
new(recommendations_data.sample)
|
4
|
+
end
|
5
|
+
|
6
|
+
def initialize(values = {})
|
7
|
+
@values = HashWithIndifferentAccess.new(values)
|
8
|
+
end
|
9
|
+
|
10
|
+
def method_missing(*args, &block)
|
11
|
+
if key = args.first and @values.has_key?(key)
|
12
|
+
@values[key]
|
13
|
+
else
|
14
|
+
raise NoMethodError.new("#{key} not in #{@values.inspect}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
"Recommendation: #{@values.inspect}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s; inspect; end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def self.recommendations_data
|
27
|
+
JSON.parse(File.read(json_file))
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.json_file
|
31
|
+
File.join(EngineOfWar::App.settings.config, "recommendations.json")
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|