Skip to content

Commit

Permalink
major rewrite. hash syntax. slim template. use tilt.
Browse files Browse the repository at this point in the history
  • Loading branch information
jbe committed Feb 20, 2011
1 parent 4916b93 commit 5bc0c05
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 165 deletions.
2 changes: 1 addition & 1 deletion LICENSE
@@ -1,4 +1,4 @@
Copyright (c) 2009 jbe
Copyright (c) 2011 jbe

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
Expand Down
57 changes: 36 additions & 21 deletions README.md
Expand Up @@ -14,34 +14,49 @@
# | | | oo |
#'''''''''''''''''''''''''''''******

Laydown is a simple template language for defining quick HTML5 layouts in Ruby. Never write the same old boilerplate again.
Bored of writing the same html boilerplate every time you make a new app?

How about something like this?

# $ gem install laydown

 

require 'laydown'

layout = Laydown.new do |_|
_.charset 'some crazy charset'
_.title 'A man in a cave'
_.description 'A man is sitting in a cave.'
_.keywords 'man, cave, mystery', @keywords
_.favicon '/maninacave.png'
_.css '/maninacave/style.css'
_.css @css, '/site.css'
_.js @js
_.inline_js 'alert("Grrr");'
_.head '<meta generator="FlushFlox Super Deluxe">'
_.ga_code 'UA-8079526-5' # google analytics
_.body_class @body_class
end

@css = 'somesheet.css'
@body_class = 'front'

layout.render(self) { '<p>this comes in the body</p>' }
layout = Laydown.new(
charset: 'utf-8' # default
title: 'A man in a #{@where}',
description: 'Very interesting',
favicon: 'pill.png',
keywords: 'man, #{@keywords}',

css: ['site.css', '#{@css}'],
js: ['app.js', '#{@js}'],
inline_js: ['alert("#{msg}");'],

head: '<meta soundtrack="Piazzolla">',
body: '#{yield}', # default
body_class: 'dark',
ga_code: 'ga-some-number'
)

# ----------------------------

@where = 'cave'
@keywords = 'cave, interesting'
@css = 'somesheet.css'

layout.render(self, :msg => 'hello') { '<p>body text</p>' }
# => your html5 layout

The block given to `Laydown.new` will be evaluated in the context passed to the `render` method.
### Features

* Fast
* Small footprint
* Built on Temple and Tilt

### Copyright

Copyright (c) 2011 Jostein Berre Eliassen.
See LICENCE for details. (MIT licence)
32 changes: 11 additions & 21 deletions Rakefile
Expand Up @@ -5,13 +5,13 @@ begin
require 'jeweler'
Jeweler::Tasks.new do |gem|
gem.name = "laydown"
gem.summary = %Q{Pure Ruby HTML5 layout DSL for microframeworks.}
gem.description = %Q{Provides a simple Ruby DSL for defining HTML5 layouts for web apps. For those of us who has written basically the same html head 200 times and feels like minimalism.}
gem.summary = %Q{Make HTML5 layouts quickly with plain Ruby.}
gem.description = %Q{Provides a simple Ruby DSL for defining HTML5 layouts for web apps. For those who has written the same layout too many times.}
gem.email = "post@jostein.be"
gem.homepage = "http://github.com/jbe/laydown"
gem.authors = ["jbe"]
gem.add_dependency "backports" #, ">= 0"
#gem.add_development_dependency "tenjin", ">= 0"
gem.add_dependency "temple" #, ">= 0"
gem.add_development_dependency "slim", ">= 0"
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
end
Jeweler::GemcutterTasks.new
Expand All @@ -20,25 +20,15 @@ rescue LoadError
end

namespace :build do
desc 'Builds Tenjin templates'
desc 'Builds slim template'
task :template do
require 'slim'
src = File.read('template.slim')
engine = Slim::Engine.new()
compiled = engine.compile(src)

require 'haml'
src = File.read('template/default.haml')
compiled = Haml::Engine.new(src).precompiled
File.open('lib/laydown/template.rb', 'w') do |f|
f.write <<-RUBY
require 'haml'
#require 'haml/helpers'
#require 'haml/util'
#require 'haml/buffer'
extend Haml::Helpers
_hamlout = @haml_buffer = Haml::Buffer.new(@haml_buffer)
RUBY
f.write(compiled)
f.write "@haml_buffer = @haml_buffer.upper"
File.open('lib/template.rb', 'w') do |f|
f.write compiled
end

#sh 'rbtenjin -s templates/default_layout.tenjin.html > lib/templates/default_layout.rb'
end
end
169 changes: 63 additions & 106 deletions lib/laydown.rb
@@ -1,131 +1,88 @@

require 'backports'
require 'tilt'
require 'temple/utils'


module Laydown
TOPOBJECT = defined?(BasicObject) ? BasicObject : Object

def self.new(&blk)
Template.new(&blk)
end

class Template
def initialize(&blk)
@layout = blk
DEFAULT_TEMPLATE = {
:charset => 'utf-8',
:title => nil,
:description => nil,
:favicon => nil,
:keywords => nil,
:css => [],
:js => [],
:inline_js => [],
:head => [],
:body_class => nil,
:body => '#{yield}',
:ga_code => nil
}

def self.compile(template={})

template = DEFAULT_TEMPLATE.merge(template)

[:charset, :title, :description, :favicon,
:keywords, :body_class, :body, :ga_code
].each do |k|
template[k] = case template[k]
when Array then template[k].join(template[k] == :keywords ? ', ' : '')
when String then template[k]
else template[k].to_s
end
end

def render(scope=Object.new, &blk)
dsl = ::Laydown::DSL.new
scope.instance_exec(dsl, &@layout)
Renderer.new(dsl._captured_values, &blk).render
[:css, :js, :inline_js, :head].each do |k|
template[k] = Array(template[k]).flatten.compact
end
end

class DSL
TARGETS = %w{
charset lang title description favicon keywords css js
inline_js head body ga_code body_class
}

attr_reader :_captured_values

def initialize
@_captured_values = Hash.new { [] }
end

def _laydown(name, *values)
@_captured_values[name] += values
compiled = read_raw_template.gsub(/data\[:([a-zA-Z0-9_]+)\]/) do |m|
val = template[:"#{$1}"]
case val
when String then interpolatize(val)
when nil then 'nil'
when Array then interpolatize(
val.map {|v| v.to_s }
)
else val.to_s
end
end

TARGETS.each do |name|
class_eval <<-RUBY
def #{name}(*a); _laydown('#{name}', *a); end
RUBY
end
puts compiled
compiled
end

class Renderer

def initialize(v)

@charset = v['charset'].first || 'utf-8'
@lang = v['lang'].first
@title = v['title'].compact.join(' &ndash; ')
@favicon = v['favicon'].first
@description = v['description'].compact.join(' ') if v['description']
@keywords = v['keywords'].compact.join(', ') if v['keywords']
@css = v['css'].compact
@js = v['js'].compact
@inline_js = v['inline_js'].compact
@head = v['head'].compact.join("\n")
@ga_code = v['ga_code'].first
@body_class = v['body_class'].compact.join(' ') if v['body_class']
@body = [block_given? ? yield : nil, v['body']].
flatten.compact.join("\n")
end
def self.new(hsh={})
Template.new(hsh)
end

def render
"<!DOCTYPE html>\n" +
_(:html, {:lang => @lang},
_(:head, {},
_(:meta, {:charset => @charset}) +
_(:title, {}, @title) +
(@favicon ?
_(:link, {:rel => 'shortcut icon', :href => @favicon}) : '') +
(@description ?
_(:meta, {:description => @description}) : '') +
(@keywords ?
_(:meta, {:keywords => @keywords}) : '') +
@css.map do |url|
_(:link, {:rel => :stylesheet, :type => 'text/css', :href => url})
end.join +
@js.map do |url|
_(:script, {:type => 'text/javascript', :src => url}, '')
end.join +
@inline_js.map do |code|
_(:script, {:type => 'text/javascript'}, code)
end.join +
(@ga_code ? google_analytics_js : '') +
@head
) +
_(:body, {:class => @body_class},
@body
)
)
end
def self.read_raw_template
File.read(File.join(
File.dirname(__FILE__), 'template.rb'
))
end

def self.interpolatize(obj)
puts obj
obj.inspect.gsub(/\\#\{/, '#{')
end

# html helpers
class Template < Tilt::Template

def _(name, props={}, contents=nil)
tag_str = name.to_s + prop_str(props)
if contents
"<#{tag_str}>\n#{contents}\n</#{name}>\n"
else
"<#{tag_str}/>\n"
end
def initialize(hsh)
super(nil, 1, hsh) { '' } # tilt hack
end

def prop_str(hsh)
return '' if hsh.values.compact.empty?
' ' + hsh.map do |k,v|
"#{k.to_s}='#{v.to_s}'" if v
end.compact.join(' ')
def prepare
@src = Laydown.compile(options)
end

def google_analytics_js
<<-JS
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '#{@ga_code}']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
JS
def precompiled_template(locals)
@src
end

end
end


46 changes: 46 additions & 0 deletions lib/template.rb
@@ -0,0 +1,46 @@
_buf = [] ; _temple_pre_tags = /<pre|<textarea/ ; _buf << ("<!DOCTYPE html><html><head>") ;
;
;
; if data[:title] ;
; _buf << ("<title>#{data[:title]}"\
""\
"</title>") ; end ; if data[:favicon] ;
; _buf << ("<link rel=\"shortcut icon\" url=\"#{Temple::Utils.escape_html((data[:favicon]))}\">"\
""\
"") ; end ; if data[:description] ;
; _buf << ("<meta content=\"#{Temple::Utils.escape_html((data[:description]))}\" name=\"description\">"\
""\
"") ; end ; if data[:keywords] ;
; _buf << ("<meta content=\"#{Temple::Utils.escape_html((data[:keywords]))}\" name=\"keywords\">"\
""\
"") ; end ; data[:css].flatten.each do |url| ;
; unless url == '' ;
; _buf << ("<link href=\"#{Temple::Utils.escape_html((url))}\" rel=\"stylesheet\" type=\"text/css\">"\
""\
"") ; end ; end ; data[:js].flatten.each do |url| ;
; unless url == '' ;
; _buf << ("<script src=\"#{Temple::Utils.escape_html((url))}\" type=\"text/javascript\"></script>"\
""\
"") ; end ; end ; if data[:inline_js] && !data[:inline_js].empty? ;
; _buf << ("<script type=\"text/javascript\">") ;
; data[:inline_js].flatten.each do |code| ;
; unless code == '' ;
; _buf << (code) ;
;
; end ; end ; _buf << ("</script>") ; end ; if data[:head] ;
; _buf << (data[:head]) ;
;
; end ; _buf << ("</head><body class=\"#{Temple::Utils.escape_html((data[:body_class]))}\">"\
"#{data[:body]}"\
""\
"") ; if data[:ga_code] ;
;
; _buf << ("<script type=\"text/javascript\">var _gaq = _gaq || [];\n_gaq.push(['_setAccount', '"\
"#{Temple::Utils.escape_html((data[:ga_code]))}']);\n_gaq.push(['_trackPageview']);\n(function() {\nvar ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;\nga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';\nvar s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);\n})();</script>"\
""\
""\
""\
""\
""\
""\
"") ; end ; _buf << ("</body></html>") ; _buf = _buf.join

0 comments on commit 5bc0c05

Please sign in to comment.