engine_of_war 0.0.1
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/.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
|