honkster-erector 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +116 -0
- data/VERSION.yml +4 -0
- data/bin/erector +14 -0
- data/lib/erector.rb +34 -0
- data/lib/erector/abstract_widget.rb +172 -0
- data/lib/erector/after_initialize.rb +34 -0
- data/lib/erector/caching.rb +93 -0
- data/lib/erector/convenience.rb +58 -0
- data/lib/erector/dependencies.rb +24 -0
- data/lib/erector/dependency.rb +30 -0
- data/lib/erector/erect/erect.rb +160 -0
- data/lib/erector/erect/erected.rb +75 -0
- data/lib/erector/erect/indenting.rb +36 -0
- data/lib/erector/erect/rhtml.treetop +233 -0
- data/lib/erector/errors.rb +12 -0
- data/lib/erector/extensions/hash.rb +21 -0
- data/lib/erector/extensions/object.rb +18 -0
- data/lib/erector/externals.rb +97 -0
- data/lib/erector/html.rb +352 -0
- data/lib/erector/inline.rb +37 -0
- data/lib/erector/jquery.rb +36 -0
- data/lib/erector/mixin.rb +12 -0
- data/lib/erector/needs.rb +94 -0
- data/lib/erector/output.rb +117 -0
- data/lib/erector/rails.rb +27 -0
- data/lib/erector/rails/extensions/action_controller.rb +16 -0
- data/lib/erector/rails/extensions/rails_helpers.rb +159 -0
- data/lib/erector/rails/extensions/rails_widget.rb +126 -0
- data/lib/erector/rails/rails_form_builder.rb +24 -0
- data/lib/erector/rails/rails_version.rb +6 -0
- data/lib/erector/rails/template_handlers/ert_handler.rb +32 -0
- data/lib/erector/rails/template_handlers/rb_handler.rb +52 -0
- data/lib/erector/raw_string.rb +8 -0
- data/lib/erector/sass.rb +22 -0
- data/lib/erector/unicode.rb +18185 -0
- data/lib/erector/unicode_builder.rb +67 -0
- data/lib/erector/version.rb +12 -0
- data/lib/erector/widget.rb +54 -0
- data/lib/erector/widgets.rb +6 -0
- data/lib/erector/widgets/environment_badge.rb +29 -0
- data/lib/erector/widgets/external_renderer.rb +51 -0
- data/lib/erector/widgets/field_table.rb +110 -0
- data/lib/erector/widgets/form.rb +30 -0
- data/lib/erector/widgets/page.rb +165 -0
- data/lib/erector/widgets/table.rb +104 -0
- data/rails/init.rb +4 -0
- data/spec/erect/erect_rails_spec.rb +114 -0
- data/spec/erect/erect_spec.rb +175 -0
- data/spec/erect/erected_spec.rb +164 -0
- data/spec/erect/rhtml_parser_spec.rb +361 -0
- data/spec/erector/caching_spec.rb +269 -0
- data/spec/erector/convenience_spec.rb +259 -0
- data/spec/erector/dependency_spec.rb +67 -0
- data/spec/erector/externals_spec.rb +236 -0
- data/spec/erector/html_spec.rb +509 -0
- data/spec/erector/indentation_spec.rb +211 -0
- data/spec/erector/inline_spec.rb +94 -0
- data/spec/erector/jquery_spec.rb +35 -0
- data/spec/erector/mixin_spec.rb +65 -0
- data/spec/erector/needs_spec.rb +120 -0
- data/spec/erector/output_spec.rb +199 -0
- data/spec/erector/sample-file.txt +1 -0
- data/spec/erector/sass_spec.rb +33 -0
- data/spec/erector/unicode_builder_spec.rb +75 -0
- data/spec/erector/widget_spec.rb +250 -0
- data/spec/erector/widgets/field_table_spec.rb +133 -0
- data/spec/erector/widgets/form_spec.rb +31 -0
- data/spec/erector/widgets/page_spec.rb +85 -0
- data/spec/erector/widgets/table_spec.rb +99 -0
- data/spec/spec_helper.rb +95 -0
- metadata +191 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
# Note that this class is only used in building erector itself
|
2
|
+
# (and even then, only needs to be run when there is a new
|
3
|
+
# UnicodeData.txt file from unicode.org).
|
4
|
+
class Erector::UnicodeBuilder
|
5
|
+
|
6
|
+
def initialize(input, output)
|
7
|
+
@input = input
|
8
|
+
@output = output
|
9
|
+
@first = true
|
10
|
+
end
|
11
|
+
|
12
|
+
def generate()
|
13
|
+
@output.puts "Erector::CHARACTERS = {"
|
14
|
+
process_file
|
15
|
+
@output.puts "}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def process_file()
|
19
|
+
while !@input.eof
|
20
|
+
line = @input.gets.strip
|
21
|
+
if (line == "")
|
22
|
+
next;
|
23
|
+
end
|
24
|
+
|
25
|
+
process_line(line)
|
26
|
+
end
|
27
|
+
if (!@first)
|
28
|
+
@output.puts
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def output_line(line)
|
33
|
+
if (!@first)
|
34
|
+
@output.puts(',')
|
35
|
+
end
|
36
|
+
|
37
|
+
@output.print(line)
|
38
|
+
|
39
|
+
@first = false
|
40
|
+
end
|
41
|
+
|
42
|
+
def process_line(line)
|
43
|
+
fields = line.split(';')
|
44
|
+
code_point = fields[0]
|
45
|
+
name = fields[1]
|
46
|
+
alternate_name = fields[10]
|
47
|
+
|
48
|
+
if /^</.match(name)
|
49
|
+
return ""
|
50
|
+
end
|
51
|
+
|
52
|
+
output name, code_point
|
53
|
+
if (!alternate_name.nil? && alternate_name != "")
|
54
|
+
output alternate_name, code_point
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def output(name, code_point)
|
59
|
+
output_line " :#{namify(name)} => 0x#{code_point.downcase}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def namify(name)
|
63
|
+
name.downcase.gsub(/[- ]/, '_')
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
##
|
4
|
+
# Erector view framework
|
5
|
+
module Erector
|
6
|
+
if !Erector.const_defined?(:VERSION)
|
7
|
+
dir = File.dirname(__FILE__)
|
8
|
+
version = YAML.load_file(File.expand_path("#{dir}/../../VERSION.yml"))
|
9
|
+
VERSION = "#{version[:major]}.#{version[:minor]}.#{version[:patch]}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Erector
|
2
|
+
|
3
|
+
# A Widget is the center of the Erector universe.
|
4
|
+
#
|
5
|
+
# To create a widget, extend Erector::Widget and implement the +content+
|
6
|
+
# method. Inside this method you may call any of the tag methods like +span+
|
7
|
+
# or +p+ to emit HTML/XML tags.
|
8
|
+
#
|
9
|
+
# You can also define a widget on the fly by passing a block to +new+. This
|
10
|
+
# block will get executed when the widget's +content+ method is called. See
|
11
|
+
# the userguide for important details about the scope of this block when run --
|
12
|
+
# http://erector.rubyforge.org/userguide.html#blocks
|
13
|
+
#
|
14
|
+
# To render a widget from the outside, instantiate it and call its +to_html+
|
15
|
+
# method.
|
16
|
+
#
|
17
|
+
# A widget's +new+ method optionally accepts an options hash. Entries in
|
18
|
+
# this hash are converted to instance variables.
|
19
|
+
#
|
20
|
+
# You can add runtime input checking via the +needs+ macro. See #needs.
|
21
|
+
# This mechanism is meant to ameliorate development-time confusion about
|
22
|
+
# exactly what parameters are supported by a given widget, avoiding
|
23
|
+
# confusing runtime NilClass errors.
|
24
|
+
#
|
25
|
+
# To call one widget from another, inside the parent widget's +content+
|
26
|
+
# method, instantiate the child widget and call the +widget+ method. This
|
27
|
+
# assures that the same output stream is used, which gives better
|
28
|
+
# performance than using +capture+ or +to_html+. It also preserves the
|
29
|
+
# indentation and helpers of the enclosing class.
|
30
|
+
#
|
31
|
+
# In this documentation we've tried to keep the distinction clear between
|
32
|
+
# methods that *emit* text and those that *return* text. "Emit" means that
|
33
|
+
# it writes to the output stream; "return" means that it returns a string
|
34
|
+
# like a normal method and leaves it up to the caller to emit that string if
|
35
|
+
# it wants.
|
36
|
+
#
|
37
|
+
# This class extends AbstractWidget and includes several modules,
|
38
|
+
# so be sure to check all of those places for API documentation for the
|
39
|
+
# various methods of Widget. Also read the API Cheatsheet in the user guide
|
40
|
+
# at http://erector.rubyforge.org/userguide#apicheatsheet
|
41
|
+
#
|
42
|
+
# Now, seriously, after playing around a bit, go read the user guide. It's
|
43
|
+
# fun!
|
44
|
+
class Widget < AbstractWidget
|
45
|
+
include Erector::HTML
|
46
|
+
include Erector::Needs
|
47
|
+
# include Erector::Caching
|
48
|
+
include Erector::Externals
|
49
|
+
include Erector::Convenience
|
50
|
+
include Erector::JQuery
|
51
|
+
include Erector::AfterInitialize
|
52
|
+
include Erector::Sass if Object.const_defined?(:Sass)
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Erector
|
2
|
+
module Widgets #:nodoc:
|
3
|
+
|
4
|
+
# Displays a colored badge in the upper-left corner
|
5
|
+
# signifying the environment the app is running in.
|
6
|
+
# Inspired by Assaf Arkin
|
7
|
+
# <http://blog.labnotes.org/2009/10/08/using-a-badge-to-distinguish-development-and-production-environments/>
|
8
|
+
# Erectorized by Alex Chaffee
|
9
|
+
class EnvironmentBadge < Erector::Widget
|
10
|
+
def content
|
11
|
+
style <<-STYLE
|
12
|
+
#environment_badge { position: fixed; left: 1em; font-weight: bold; padding: .2em 0.9em; text-transform: uppercase; display: none }
|
13
|
+
#environment_badge.staging { color: #000; background: #ffff00; border: 2px solid #cccc20; }
|
14
|
+
#environment_badge.development { color: #fff; background: #ff0000; border: 2px solid #cc2020; }
|
15
|
+
#environment_badge.staging, #environment_badge.development { border-top: none; display: block; opacity: 0.6 }
|
16
|
+
STYLE
|
17
|
+
unless environment =~ /production/
|
18
|
+
p environment, :class => environment, :id => "environment_badge"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def environment
|
23
|
+
RAILS_ENV
|
24
|
+
rescue NameError
|
25
|
+
ENV['RAILS_ENV'] || ENV['RACK_ENV']
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
class ExternalRenderer < Erector::Widget
|
2
|
+
needs :classes
|
3
|
+
needs :included_stylesheets => true, :inline_styles => true, :included_scripts => true, :inline_scripts => true
|
4
|
+
|
5
|
+
def content
|
6
|
+
included_stylesheets if @included_stylesheets
|
7
|
+
inline_styles if @inline_styles
|
8
|
+
included_scripts if @included_scripts
|
9
|
+
inline_scripts if @inline_scripts
|
10
|
+
end
|
11
|
+
|
12
|
+
def rendered_externals(type)
|
13
|
+
@classes.map do |klass|
|
14
|
+
klass.dependencies(type)
|
15
|
+
end.flatten.uniq
|
16
|
+
end
|
17
|
+
|
18
|
+
def included_scripts
|
19
|
+
rendered_externals(:js).each do |external|
|
20
|
+
script({:type => "text/javascript", :src => external.text}.merge(external.options))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def included_stylesheets
|
25
|
+
rendered_externals(:css).each do |external|
|
26
|
+
link({:rel => "stylesheet", :href => external.text, :type => "text/css", :media => "all"}.merge(external.options))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def inline_styles
|
31
|
+
rendered_externals(:style).each do |external|
|
32
|
+
style({:type => "text/css", 'xml:space' => 'preserve'}.merge(external.options)) do
|
33
|
+
rawtext external.text
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def inline_scripts
|
39
|
+
rendered_externals(:script).each do |external|
|
40
|
+
javascript external.options do
|
41
|
+
rawtext external.text
|
42
|
+
end
|
43
|
+
end
|
44
|
+
# todo: allow :load or :ready per external script
|
45
|
+
rendered_externals(:jquery).each do |external|
|
46
|
+
jquery :load, external.text, external.options
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# A simple HTML table with three columns: label, contents, and (optionally) note.
|
2
|
+
# Each row is called a field.
|
3
|
+
# The last row can optionally contain "buttons" (actually any widget will do)
|
4
|
+
# that are all shown in the 2nd column.
|
5
|
+
# It's all surrounded with a fieldset element for a title.
|
6
|
+
#
|
7
|
+
# In ASCII art, it looks something like this:
|
8
|
+
#
|
9
|
+
# /-Meals----------------------------------------\
|
10
|
+
# | Breakfast | eggs | |
|
11
|
+
# | Lunch | sandwich | |
|
12
|
+
# | Dinner | lo mein | with shrimp! |
|
13
|
+
# | | [Save] [Cancel] | |
|
14
|
+
# \----------------------------------------------/
|
15
|
+
#
|
16
|
+
# There are two ways to create a FieldTable.
|
17
|
+
# 1. Pass a block in to the constructor.
|
18
|
+
# 2. Make a subclass.
|
19
|
+
# In both cases you'll want to call the "field" and "button" methods on the table.
|
20
|
+
# This sets up the contents which will be rendered later during FieldTable#content.
|
21
|
+
# If you make a subclass (#2) you can do this either in the constructor or in
|
22
|
+
# the content method *before* you call super.
|
23
|
+
#
|
24
|
+
# The FieldTable is surrounded by a fieldset element whose legend is the "title" instance variable.
|
25
|
+
# Inside this fieldset is a table element.
|
26
|
+
# Each field (row of the table) has a label (th), a content cell (td), and an optional note (td).
|
27
|
+
#
|
28
|
+
# If you call "button" you can pass in a block that'll get rendered inside the 2nd column of the last row. The idea here is that you might want to make an HTML form that has a bunch of buttons at the bottom (Save, Cancel, Clear) and these all go in the same cell, with no label for the row.
|
29
|
+
#
|
30
|
+
# TODO: error messages?
|
31
|
+
# @author Alex Chaffee
|
32
|
+
class FieldTable < Erector::Widget
|
33
|
+
|
34
|
+
include Erector::Inline
|
35
|
+
|
36
|
+
class Field < Erector::Widget
|
37
|
+
needs :label, :note => nil
|
38
|
+
|
39
|
+
def content
|
40
|
+
tr :class => "field_table_field" do
|
41
|
+
th do
|
42
|
+
text @label
|
43
|
+
text ":" unless @label.nil?
|
44
|
+
end
|
45
|
+
td do
|
46
|
+
super # calls the block
|
47
|
+
end
|
48
|
+
if @note
|
49
|
+
td do
|
50
|
+
text @note
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Define a field, containing a label, optional note, and block for the contents
|
58
|
+
#
|
59
|
+
# TODO: allow passing in a widget instead of a block
|
60
|
+
def field(label = nil, note = nil, &contents)
|
61
|
+
@fields << Field.new(:label => label, :note => note, &contents)
|
62
|
+
end
|
63
|
+
|
64
|
+
# If you call "button" you can pass in a block that'll get rendered inside the 2nd column of the
|
65
|
+
# last row. The idea here is that you might want to make an HTML form that has a bunch of buttons at
|
66
|
+
# the bottom (Save, Cancel, Clear) and these all go in the same cell, with no label for the row.
|
67
|
+
#
|
68
|
+
# TODO: allow passing in a widget instead of a block
|
69
|
+
def button(&button_proc)
|
70
|
+
@buttons << button_proc
|
71
|
+
end
|
72
|
+
|
73
|
+
needs :title
|
74
|
+
|
75
|
+
# Pass in a block and it'll get called with a pointer to this table, so you can call
|
76
|
+
# 'field' and 'button' to configure it
|
77
|
+
def initialize(*args)
|
78
|
+
super
|
79
|
+
@fields = []
|
80
|
+
@buttons = []
|
81
|
+
yield self if block_given? # invoke the configuration block
|
82
|
+
end
|
83
|
+
|
84
|
+
def content
|
85
|
+
fieldset :class => "field_table" do
|
86
|
+
legend @title
|
87
|
+
table :width => '100%' do
|
88
|
+
@fields.each do |f|
|
89
|
+
widget f
|
90
|
+
end
|
91
|
+
unless @buttons.empty?
|
92
|
+
tr :class => "field_table_buttons" do
|
93
|
+
td :colspan => 2, :align => "right" do
|
94
|
+
table :class => 'layout' do
|
95
|
+
tr do
|
96
|
+
@buttons.each do |button|
|
97
|
+
td :class => "field_table_button" do
|
98
|
+
button.call
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# todo: make more like http://github.com/justinfrench/formtastic
|
2
|
+
|
3
|
+
class Form < Erector::Widget
|
4
|
+
needs :action, :method => "post"
|
5
|
+
|
6
|
+
def content
|
7
|
+
form :method => form_method, :action => @action do
|
8
|
+
unless rest_method == form_method
|
9
|
+
input :type => "hidden", :name => "_method", :value => rest_method
|
10
|
+
end
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def method
|
16
|
+
@method.to_s.downcase
|
17
|
+
end
|
18
|
+
|
19
|
+
def form_method
|
20
|
+
if method == "get"
|
21
|
+
"get"
|
22
|
+
else
|
23
|
+
"post"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def rest_method
|
28
|
+
method
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# Erector Page base class.
|
2
|
+
#
|
3
|
+
# Allows for accumulation of script and style tags (see example below) with
|
4
|
+
# either external or inline content. External references are 'uniq'd, so it's
|
5
|
+
# a good idea to declare a js script in all widgets that use it, so you don't
|
6
|
+
# accidentally lose the script if you remove the one widget that happened to
|
7
|
+
# declare it.
|
8
|
+
#
|
9
|
+
# The script and style declarations are accumulated at class load time, as
|
10
|
+
# 'dependencies'. This technique allows all widgets to add their own requirements
|
11
|
+
# to the page header without extra logic for declaring which pages include
|
12
|
+
# which nested widgets. Fortunately, Page is now smart enough to figure out
|
13
|
+
# which widgets were actually rendered during the body_content run, so it only
|
14
|
+
# emits into its HEAD the dependencies that are relevant. If it misses some, or
|
15
|
+
# if you want to add some extra dependencies -- for instance, styles that apply
|
16
|
+
# to widgets that are rendered later via AJAX -- then return an array of those
|
17
|
+
# widget classes in your subclass by overriding the #extra_widgets method.
|
18
|
+
#
|
19
|
+
# If you want something to show up in the headers for just one page type
|
20
|
+
# (subclass), then override #head_content, call super, and then emit it
|
21
|
+
# yourself.
|
22
|
+
#
|
23
|
+
# Body content can be supplied in several ways:
|
24
|
+
#
|
25
|
+
# * In a Page subclass, by overriding the #body_content method:
|
26
|
+
#
|
27
|
+
# class MyPage < Erector::Widgets::Page
|
28
|
+
# def body_content
|
29
|
+
# text "body content"
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# * Or by overriding #content and passing a block to super:
|
34
|
+
#
|
35
|
+
# class MyPage < Erector::Widgets::Page
|
36
|
+
# def content
|
37
|
+
# super do
|
38
|
+
# text "body content"
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# * Or by passing a block to Page.new:
|
44
|
+
#
|
45
|
+
# Erector::Widgets::Page.new do
|
46
|
+
# text "body content"
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# This last trick (passing a block to Page.new) works because Page is an
|
50
|
+
# InlineWidget so its block is evaluated in the context of the newly
|
51
|
+
# instantiated widget object, and not in the context of its caller. But this
|
52
|
+
# means you can't access instance variables of the caller, e.g.
|
53
|
+
#
|
54
|
+
# @name = "fred"
|
55
|
+
# Erector::Widgets::Page.new do
|
56
|
+
# text "my name is #{@name}"
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# will emit "my name is " because @name is nil inside the new Page. However,
|
60
|
+
# you *can* call methods in the parent class, thanks to some method_missing
|
61
|
+
# magic. Confused? You should be. See Erector::Inline#content for more
|
62
|
+
# documentation.
|
63
|
+
#
|
64
|
+
# Author:: Alex Chaffee, alex@stinky.com
|
65
|
+
#
|
66
|
+
# = Example Usage:
|
67
|
+
#
|
68
|
+
# class MyPage < Page
|
69
|
+
# external :js, "lib/jquery.js"
|
70
|
+
# external :script, "$(document).ready(function(){...});"
|
71
|
+
# external :css, "stuff.css"
|
72
|
+
# external :style, "li.foo { color: red; }"
|
73
|
+
#
|
74
|
+
# def page_title
|
75
|
+
# "my app"
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# def body_content
|
79
|
+
# h1 "My App"
|
80
|
+
# p "welcome to my app"
|
81
|
+
# widget WidgetWithExternalStyle
|
82
|
+
# end
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# class WidgetWithExternalStyle < Erector::Widget
|
86
|
+
# external :style, "div.custom { border: 2px solid green; }"
|
87
|
+
#
|
88
|
+
# def content
|
89
|
+
# div :class => "custom" do
|
90
|
+
# text "green is good"
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# = Thoughts:
|
96
|
+
# * It may be desirable to unify #js and #script, and #css and #style, and have the routine be
|
97
|
+
# smart enough to analyze its parameter to decide whether to make it a file or a script.
|
98
|
+
#
|
99
|
+
class Erector::Widgets::Page < Erector::InlineWidget
|
100
|
+
|
101
|
+
# Emit the Transitional doctype.
|
102
|
+
# TODO: allow selection from among different standard doctypes
|
103
|
+
def doctype
|
104
|
+
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
105
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
|
106
|
+
end
|
107
|
+
|
108
|
+
def content
|
109
|
+
extra_head_slot = nil
|
110
|
+
rawtext doctype
|
111
|
+
html(html_attributes) do
|
112
|
+
head do
|
113
|
+
head_content
|
114
|
+
extra_head_slot = output.placeholder
|
115
|
+
end
|
116
|
+
body(body_attributes) do
|
117
|
+
if block_given?
|
118
|
+
yield
|
119
|
+
else
|
120
|
+
body_content
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
# after everything's been rendered, use the placeholder to
|
125
|
+
# insert all the head's dependencies
|
126
|
+
extra_head_slot << included_head_content
|
127
|
+
end
|
128
|
+
|
129
|
+
# override me to provide a page title (default = name of the Page subclass)
|
130
|
+
def page_title
|
131
|
+
self.class.name
|
132
|
+
end
|
133
|
+
|
134
|
+
# override me to change the attributes of the HTML element
|
135
|
+
def html_attributes
|
136
|
+
{:xmlns => 'http://www.w3.org/1999/xhtml', 'xml:lang' => 'en', :lang => 'en'}
|
137
|
+
end
|
138
|
+
|
139
|
+
# override me to add attributes (e.g. a css class) to the body
|
140
|
+
def body_attributes
|
141
|
+
{}
|
142
|
+
end
|
143
|
+
|
144
|
+
# override me (or instantiate Page with a block)
|
145
|
+
def body_content
|
146
|
+
call_block
|
147
|
+
end
|
148
|
+
|
149
|
+
# emit the contents of the head element. Override and call super if you want to put more stuff in there.
|
150
|
+
def head_content
|
151
|
+
meta 'http-equiv' => 'content-type', :content => 'text/html;charset=UTF-8'
|
152
|
+
title page_title
|
153
|
+
end
|
154
|
+
|
155
|
+
def included_head_content
|
156
|
+
# now that we've rendered the whole page, it's the right time
|
157
|
+
# to ask what all widgets were rendered to the output stream
|
158
|
+
included_widgets = [self.class] + output.widgets.to_a + extra_widgets
|
159
|
+
ExternalRenderer.new(:classes => included_widgets).to_html
|
160
|
+
end
|
161
|
+
|
162
|
+
def extra_widgets
|
163
|
+
[]
|
164
|
+
end
|
165
|
+
end
|