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 ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .bin
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.rbenv-version ADDED
@@ -0,0 +1 @@
1
+ 1.9.3-p0
data/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ --readme README.textile
2
+ --markup textile
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in engine_of_war.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/setup'
3
+
4
+ require "bundler/gem_tasks"
5
+ require 'rspec/core/rake_task'
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ task :default => :spec
10
+
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,10 @@
1
+ class String
2
+ def unindent
3
+ gsub /^#{self[/\A\s*/]}/, ''
4
+ end
5
+
6
+ def uncomment(comment = "//")
7
+ gsub(%r{^#{comment}\s*}, '')
8
+ end
9
+ end
10
+
@@ -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