lib/laydown.rb in laydown-0.3.0 vs lib/laydown.rb in laydown-0.4.0
- old
+ new
@@ -1,51 +1,131 @@
-require 'instant_dsl'
+require 'backports'
+
module Laydown
+ TOPOBJECT = defined?(BasicObject) ? BasicObject : Object
- DSL_WORDS = [:title, :description, :favicon, :keywords, :css, :js, :head, :body]
- DSL = InstantDSL[*DSL_WORDS]
+ def self.new(&blk)
+ Template.new(&blk)
+ end
- class NoScope < Object; end
+ class Template
+ def initialize(&blk)
+ @layout = blk
+ end
- class TemplateScope
- def initialize(v)
- @title = v[:title].join
- @description = v[:description].join
- @favicon = v[:favicon].first
- @keywords = v[:keywords].join(', ')
- @stylesheets = v[:css].compact
- @javascripts = v[:js].compact
- @head = v[:head].join("\n")
- @body = v[:body].join("\n")
+ def render(scope=Object.new, &blk)
+ dsl = ::Laydown::DSL.new
+ scope.instance_exec(dsl, &@layout)
+ Renderer.new(dsl._captured_values, &blk).render
end
end
- def self.layout(&conf)
- Template.new(&conf)
- end
+ class DSL
+ TARGETS = %w{
+ charset lang title description favicon keywords css js
+ inline_js head body ga_code body_class
+ }
- class Template
- def initialize(&conf)
- @conf = conf
- @tpath = File.expand_path('../templates/default_layout.rb', __FILE__)
- @template = IO.read(@tpath)
+ attr_reader :_captured_values
+
+ def initialize
+ @_captured_values = Hash.new { [] }
end
- def render(source_scope=::Laydown::NoScope.new)
- dsl_scope = ::Laydown::DSL.new(source_scope)
+ def _laydown(name, *values)
+ @_captured_values[name] += values
+ end
- dsl_scope.send :instance_eval, &@conf
+ TARGETS.each do |name|
+ class_eval <<-RUBY
+ def #{name}(*a); _laydown('#{name}', *a); end
+ RUBY
+ end
+ end
- scope = ::Laydown::TemplateScope.new(dsl_scope.dsl_values)
- scope.send :instance_eval, @template, @tpath
+ class Renderer
+
+ def initialize(v)
+
+ @charset = v['charset'].first || 'utf-8'
+ @lang = v['lang'].first
+ @title = v['title'].compact.join(' – ')
+ @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
- 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
-end
+ # html helpers
+ 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
+ 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(' ')
+ 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
+ end
+
+ end
+end