erector 0.5.1 → 0.6.3
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/README.txt +6 -5
- data/VERSION.yml +5 -0
- data/bin/{erect → erector} +0 -0
- data/lib/erector.rb +3 -1
- data/lib/erector/erect.rb +1 -1
- data/lib/erector/erected.rb +1 -1
- data/lib/erector/rails.rb +2 -1
- data/lib/erector/rails/extensions/action_controller.rb +25 -7
- data/lib/erector/rails/extensions/rails_widget.rb +38 -0
- data/lib/erector/rails/extensions/{widget.rb → rails_widget/helpers.rb} +2 -9
- data/lib/erector/rails/rails_version.rb +6 -0
- data/lib/erector/rails/template_handlers/action_view_template_handler.rb +43 -11
- data/lib/erector/version.rb +4 -2
- data/lib/erector/widget.rb +221 -74
- data/lib/erector/widgets/table.rb +34 -8
- data/spec/erector/indentation_spec.rb +39 -24
- data/spec/erector/widget_spec.rb +197 -64
- data/spec/erector/widgets/table_spec.rb +3 -3
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +2 -4
- data/spec/spec_suite.rb +6 -12
- metadata +24 -70
- data/lib/erector/rails/extensions/action_controller/1.2.5/action_controller.rb +0 -17
- data/lib/erector/rails/extensions/action_controller/2.2.0/action_controller.rb +0 -26
- data/lib/erector/rails/extensions/widget/1.2.5/widget.rb +0 -18
- data/lib/erector/rails/extensions/widget/2.2.0/widget.rb +0 -23
- data/lib/erector/rails/supported_rails_versions.rb +0 -13
- data/lib/erector/rails/template_handlers/1.2.5/action_view_template_handler.rb +0 -32
- data/lib/erector/rails/template_handlers/2.0.0/action_view_template_handler.rb +0 -36
- data/lib/erector/rails/template_handlers/2.1.0/action_view_template_handler.rb +0 -31
- data/lib/erector/rails/template_handlers/2.2.0/action_view_template_handler.rb +0 -46
- data/spec/erect/erect_spec.rb +0 -145
- data/spec/erect/erected_spec.rb +0 -80
- data/spec/erect/rhtml_parser_spec.rb +0 -318
data/README.txt
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
= Erector
|
2
2
|
|
3
3
|
* http://erector.rubyforge.org
|
4
|
-
* mailto:erector
|
4
|
+
* mailto:erector@googlegroups.com
|
5
|
+
* http://www.pivotaltracker.com/projects/482
|
5
6
|
|
6
7
|
== DESCRIPTION
|
7
8
|
|
@@ -17,20 +18,20 @@ project site at http://erector.rubyforge.org for more documentation.
|
|
17
18
|
require 'erector'
|
18
19
|
|
19
20
|
class Hello < Erector::Widget
|
20
|
-
def
|
21
|
+
def content
|
21
22
|
html do
|
22
23
|
head do
|
23
24
|
title "Hello"
|
24
25
|
end
|
25
26
|
body do
|
26
27
|
text "Hello, "
|
27
|
-
b "
|
28
|
+
b "#{target}!", :class => 'big'
|
28
29
|
end
|
29
30
|
end
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
33
|
-
Hello.new.to_s
|
34
|
+
Hello.new(:target => 'world').to_s
|
34
35
|
=> "<html><head><title>Hello</title></head><body>Hello, <b class=\"big\">world!</b></body></html>"
|
35
36
|
|
36
37
|
== REQUIREMENTS
|
@@ -58,7 +59,7 @@ When installing this way, erector is automatically available to your Rails code
|
|
58
59
|
|
59
60
|
(The MIT License)
|
60
61
|
|
61
|
-
Copyright (c) 2007-
|
62
|
+
Copyright (c) 2007-2009 Pivotal Labs
|
62
63
|
|
63
64
|
Permission is hereby granted, free of charge, to any person obtaining
|
64
65
|
a copy of this software and associated documentation files (the
|
data/bin/{erect → erector}
RENAMED
File without changes
|
data/lib/erector.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require "rubygems"
|
2
2
|
dir = File.dirname(__FILE__)
|
3
3
|
require 'cgi'
|
4
|
-
require
|
4
|
+
require 'yaml'
|
5
|
+
require "active_support/inflector"
|
6
|
+
require "active_support/inflections"
|
5
7
|
require "#{dir}/erector/extensions/object"
|
6
8
|
require "#{dir}/erector/raw_string"
|
7
9
|
require "#{dir}/erector/widget"
|
data/lib/erector/erect.rb
CHANGED
@@ -11,7 +11,7 @@ module Erector
|
|
11
11
|
@output_dir = nil
|
12
12
|
|
13
13
|
opts = OptionParser.new do |opts|
|
14
|
-
opts.banner = "Usage:
|
14
|
+
opts.banner = "Usage: erector [options] [file|dir]*"
|
15
15
|
|
16
16
|
opts.separator "Converts from html/rhtml files to erector widgets, or from erector widgets to html files"
|
17
17
|
opts.separator ""
|
data/lib/erector/erected.rb
CHANGED
data/lib/erector/rails.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
dir = File.dirname(__FILE__)
|
2
2
|
require "action_controller"
|
3
|
-
require "#{dir}/rails/
|
3
|
+
require "#{dir}/rails/rails_version"
|
4
|
+
require "#{dir}/rails/extensions/rails_widget"
|
4
5
|
require "#{dir}/rails/extensions/action_controller"
|
5
6
|
require "#{dir}/rails/extensions/action_view"
|
6
7
|
require "#{dir}/rails/template_handlers/action_view_template_handler"
|
@@ -1,8 +1,26 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
else
|
7
|
-
|
1
|
+
ActionController::Base.class_eval do
|
2
|
+
def render_widget(widget_class, assigns=nil)
|
3
|
+
@__widget_class = widget_class
|
4
|
+
if assigns
|
5
|
+
@__widget_assigns = assigns
|
6
|
+
else
|
7
|
+
@__widget_assigns = {}
|
8
|
+
variables = instance_variable_names
|
9
|
+
variables -= protected_instance_variables
|
10
|
+
variables.each do |name|
|
11
|
+
@__widget_assigns[name.sub('@', "")] = instance_variable_get(name)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
response.template.send(:_evaluate_assigns_and_ivars)
|
15
|
+
render :inline => "<% @__widget_class.new(@__widget_assigns).to_s(:output => output_buffer, :helpers => self) %>"
|
16
|
+
end
|
17
|
+
|
18
|
+
def render_with_erector_widget(*options, &block)
|
19
|
+
if options.first.is_a?(Hash) && widget = options.first.delete(:widget)
|
20
|
+
render_widget widget, @assigns, &block
|
21
|
+
else
|
22
|
+
render_without_erector_widget *options, &block
|
23
|
+
end
|
24
|
+
end
|
25
|
+
alias_method_chain :render, :erector_widget
|
8
26
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Erector
|
2
|
+
class RailsWidget < Widget
|
3
|
+
def output
|
4
|
+
process_output_buffer || @output
|
5
|
+
end
|
6
|
+
|
7
|
+
def capture_with_helpers(&block)
|
8
|
+
helpers ? helpers.capture(&block) : capture_without_helpers(&block)
|
9
|
+
end
|
10
|
+
|
11
|
+
alias_method_chain :capture, :helpers
|
12
|
+
|
13
|
+
# This is here to force #helpers.capture to return the output
|
14
|
+
def __in_erb_template; end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def process_output_buffer
|
19
|
+
if helpers.respond_to?(:output_buffer)
|
20
|
+
buffer = helpers.output_buffer
|
21
|
+
buffer.is_a?(String) ? buffer : handle_rjs_buffer
|
22
|
+
else
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def handle_rjs_buffer
|
28
|
+
returning buffer = helpers.output_buffer.dup.to_s do
|
29
|
+
helpers.output_buffer.clear
|
30
|
+
helpers.with_output_buffer(buffer) do
|
31
|
+
buffer << helpers.output_buffer.to_s
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
require "#{File.dirname(__FILE__)}/rails_widget/helpers"
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Erector
|
2
|
-
Widget
|
2
|
+
class RailsWidget < Widget
|
3
3
|
include ActionController::UrlWriter
|
4
4
|
|
5
5
|
# helpers returning raw text
|
@@ -105,13 +105,6 @@ module Erector
|
|
105
105
|
|
106
106
|
def pluralize(*args)
|
107
107
|
helpers.pluralize(*args)
|
108
|
-
end
|
108
|
+
end
|
109
109
|
end
|
110
110
|
end
|
111
|
-
|
112
|
-
dir = File.dirname(__FILE__)
|
113
|
-
if ActionView::Base.instance_methods.include?("output_buffer")
|
114
|
-
require "#{dir}/widget/2.2.0/widget"
|
115
|
-
else
|
116
|
-
require "#{dir}/widget/1.2.5/widget"
|
117
|
-
end
|
@@ -1,14 +1,46 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
module ActionView #:nodoc:
|
2
|
+
module TemplateHandlers #:nodoc:
|
3
|
+
class Erector < TemplateHandler
|
4
|
+
include Compilable
|
5
|
+
def self.line_offset
|
6
|
+
2
|
7
|
+
end
|
8
|
+
|
9
|
+
ActionView::Template.instance_eval do
|
10
|
+
register_template_handler :rb, ActionView::TemplateHandlers::Erector
|
11
|
+
end
|
12
|
+
|
13
|
+
def compile(template)
|
14
|
+
relative_path_parts = template.path.split('/')
|
15
|
+
|
16
|
+
is_partial = relative_path_parts.last =~ /^_/
|
17
|
+
require_dependency File.expand_path(template.filename)
|
18
|
+
|
19
|
+
widget_class_parts = relative_path_parts.inject(['Views']) do |class_parts, node|
|
20
|
+
class_parts << node.gsub(/^_/, "").gsub(/(\.html)?\.rb$/, '').camelize
|
21
|
+
class_parts
|
22
|
+
end
|
23
|
+
widget_class_name = widget_class_parts.join("::")
|
24
|
+
render_method = is_partial ? 'render_partial' : 'content'
|
25
|
+
|
26
|
+
erb_template = <<-ERB
|
27
|
+
<%
|
28
|
+
assigns = instance_variables.inject({}) do |hash, name|
|
29
|
+
hash[name.sub('@', "")] = instance_variable_get(name)
|
30
|
+
hash
|
31
|
+
end
|
32
|
+
|
33
|
+
widget = #{widget_class_name}.new(assigns)
|
34
|
+
widget.to_s(:output => output_buffer, :helpers => self, :content_method_name => :#{render_method})
|
35
|
+
%>
|
36
|
+
ERB
|
37
|
+
::ERB.new(
|
38
|
+
erb_template,
|
39
|
+
nil,
|
40
|
+
::ActionView::TemplateHandlers::ERB.erb_trim_mode,
|
41
|
+
"@output_buffer"
|
42
|
+
).src
|
43
|
+
end
|
8
44
|
end
|
9
|
-
else
|
10
|
-
require File.expand_path("#{dir}/2.0.0/action_view_template_handler")
|
11
45
|
end
|
12
|
-
else
|
13
|
-
require File.expand_path("#{dir}/1.2.5/action_view_template_handler")
|
14
46
|
end
|
data/lib/erector/version.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
|
1
|
+
##
|
2
2
|
# Erector view framework
|
3
3
|
module Erector
|
4
4
|
if !Erector.const_defined?(:VERSION)
|
5
|
-
|
5
|
+
dir = File.dirname(__FILE__)
|
6
|
+
version = YAML.load_file(File.expand_path("#{dir}/../../VERSION.yml"))
|
7
|
+
VERSION = "#{version[:major]}.#{version[:minor]}.#{version[:patch]}"
|
6
8
|
end
|
7
9
|
end
|
8
10
|
|
data/lib/erector/widget.rb
CHANGED
@@ -3,17 +3,27 @@ module Erector
|
|
3
3
|
# A Widget is the center of the Erector universe.
|
4
4
|
#
|
5
5
|
# To create a widget, extend Erector::Widget and implement
|
6
|
-
# the +
|
6
|
+
# the +content+ method. Inside this method you may call any of the tag methods like +span+ or +p+ to emit HTML/XML
|
7
7
|
# tags.
|
8
8
|
#
|
9
9
|
# You can also define a widget on the fly by passing a block to +new+. This block will get executed when the widget's
|
10
|
-
# +
|
10
|
+
# +content+ method is called.
|
11
11
|
#
|
12
12
|
# To render a widget from the outside, instantiate it and call its +to_s+ method.
|
13
|
+
#
|
14
|
+
# A widget's +new+ method optionally accepts an options hash. Entries in this hash are converted to instance
|
15
|
+
# variables, and +attr_reader+ accessors are defined for each.
|
16
|
+
#
|
17
|
+
# TODO: You can add runtime input checking via the +needs+ macro. If any of the variables named via
|
18
|
+
# +needs+ are absent, an exception is thrown. Optional variables are specified with +wants+. If a variable appears
|
19
|
+
# in the options hash that is in neither the +needs+ nor +wants+ lists, then that too provokes an exception.
|
20
|
+
# This mechanism is meant to ameliorate development-time confusion about exactly what parameters are supported
|
21
|
+
# by a given widget, avoiding confusing runtime NilClass errors.
|
13
22
|
#
|
14
|
-
# To call one widget from another, inside the parent widget's
|
15
|
-
#
|
16
|
-
# is used, which gives better performance than using +capture+ or +to_s+.
|
23
|
+
# To call one widget from another, inside the parent widget's +content+ method, instantiate the child widget and call
|
24
|
+
# the +widget+ method. This assures that the same output stream
|
25
|
+
# is used, which gives better performance than using +capture+ or +to_s+. It also preserves the indentation and
|
26
|
+
# helpers of the enclosing class.
|
17
27
|
#
|
18
28
|
# In this documentation we've tried to keep the distinction clear between methods that *emit* text and those that
|
19
29
|
# *return* text. "Emit" means that it writes to the output stream; "return" means that it returns a string
|
@@ -64,14 +74,72 @@ module Erector
|
|
64
74
|
raise ArgumentError, "You must provide either an instance or a block"
|
65
75
|
end
|
66
76
|
end
|
67
|
-
|
77
|
+
|
68
78
|
protected
|
69
79
|
def after_initialize_parts
|
70
80
|
@after_initialize_parts ||= []
|
71
81
|
end
|
72
82
|
end
|
73
83
|
|
74
|
-
|
84
|
+
# Class method by which widget classes can declare that they need certain parameters.
|
85
|
+
# If needed parameters are not passed in to #new, then an exception will be thrown
|
86
|
+
# (with a hopefully useful message about which parameters are missing). This is intended
|
87
|
+
# to catch silly bugs like passing in a parameter called 'name' to a widget that expects
|
88
|
+
# a parameter called 'title'. Every variable declared in 'needs' will get an attr_reader
|
89
|
+
# accessor declared for it.
|
90
|
+
#
|
91
|
+
# You can also declare default values for parameters using hash syntax. You can put #needs
|
92
|
+
# declarations on multiple lines or on the same line; the only caveat is that if there are
|
93
|
+
# default values, they all have to be at the end of the line (so they go into the magic
|
94
|
+
# hash parameter).
|
95
|
+
#
|
96
|
+
# If a widget has no #needs declaration then it will accept any combination of parameters
|
97
|
+
# (and make accessors for them) just like normal. In that case there will be no 'attr_reader's
|
98
|
+
# declared.
|
99
|
+
# If a widget wants to declare that it
|
100
|
+
# takes no parameters, use the special incantation "needs nil" (and don't declare any other
|
101
|
+
# needs, or kittens will cry).
|
102
|
+
#
|
103
|
+
# Usage:
|
104
|
+
# class FancyForm < Erector::Widget
|
105
|
+
# needs :title, :show_okay => true, :show_cancel => false
|
106
|
+
# ...
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# That means that
|
110
|
+
# FancyForm.new(:title => 'Login')
|
111
|
+
# will succeed, as will
|
112
|
+
# FancyForm.new(:title => 'Login', :show_cancel => true)
|
113
|
+
# but
|
114
|
+
# FancyForm.new(:name => 'Login')
|
115
|
+
# will fail.
|
116
|
+
#
|
117
|
+
def self.needs(*args)
|
118
|
+
args.each do |arg|
|
119
|
+
(@needs ||= []) << (arg.nil? ? nil : (arg.is_a? Hash) ? arg : arg.to_sym)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
protected
|
124
|
+
def self.get_needs
|
125
|
+
@needs ||= []
|
126
|
+
parent = self.ancestors[1]
|
127
|
+
if parent.respond_to? :get_needs
|
128
|
+
parent.get_needs + @needs
|
129
|
+
else
|
130
|
+
@needs
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
public
|
135
|
+
@@prettyprint_default = false
|
136
|
+
def prettyprint_default
|
137
|
+
@@prettyprint_default
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.prettyprint_default=(enabled)
|
141
|
+
@@prettyprint_default = enabled
|
142
|
+
end
|
75
143
|
|
76
144
|
NON_NEWLINEY = {'i' => true, 'b' => true, 'small' => true,
|
77
145
|
'img' => true, 'span' => true, 'a' => true,
|
@@ -80,95 +148,163 @@ module Erector
|
|
80
148
|
|
81
149
|
SPACES_PER_INDENT = 2
|
82
150
|
|
83
|
-
attr_reader :helpers, :assigns, :block, :parent, :output
|
84
|
-
attr_accessor :enable_prettyprint
|
151
|
+
attr_reader :helpers, :assigns, :block, :parent, :output, :prettyprint, :indentation
|
85
152
|
|
86
|
-
def initialize(
|
153
|
+
def initialize(assigns={}, &block)
|
154
|
+
unless assigns.is_a? Hash
|
155
|
+
raise "Erector's API has changed. Now you should pass only an options hash into Widget.new; the rest come in via to_s, or by using #widget."
|
156
|
+
end
|
157
|
+
if (respond_to? :render) &&
|
158
|
+
!self.method(:render).to_s.include?("(RailsWidget)")
|
159
|
+
raise "Erector's API has changed. You should rename #{self.class}#render to #content."
|
160
|
+
end
|
87
161
|
@assigns = assigns
|
88
162
|
assign_locals(assigns)
|
89
|
-
@helpers = helpers
|
90
163
|
@parent = block ? eval("self", block.binding) : nil
|
91
|
-
@output = output
|
92
164
|
@block = block
|
93
|
-
@at_start_of_line = true
|
94
|
-
@indent = 0
|
95
|
-
@enable_prettyprint = prettyprint_default
|
96
165
|
self.class.after_initialize self
|
97
166
|
end
|
98
167
|
|
99
168
|
#-- methods for other classes to call, left public for ease of testing and documentation
|
100
169
|
#++
|
101
170
|
|
171
|
+
protected
|
172
|
+
def context(output, prettyprint = false, indentation = 0, helpers = nil)
|
173
|
+
#TODO: pass in options hash, maybe, instead of parameters
|
174
|
+
original_output = @output
|
175
|
+
original_indendation = @indentation
|
176
|
+
original_helpers = @helpers
|
177
|
+
original_prettyprint = @prettyprint
|
178
|
+
@output = output
|
179
|
+
@at_start_of_line = true
|
180
|
+
raise "indentation must be a number, not #{indentation.inspect}" unless indentation.is_a? Fixnum
|
181
|
+
@indentation = indentation
|
182
|
+
@helpers = helpers
|
183
|
+
@prettyprint = prettyprint
|
184
|
+
yield
|
185
|
+
ensure
|
186
|
+
@output = original_output
|
187
|
+
@indentation = original_indendation
|
188
|
+
@helpers = original_helpers
|
189
|
+
@prettyprint = original_prettyprint
|
190
|
+
end
|
191
|
+
|
192
|
+
public
|
102
193
|
def assign_locals(local_assigns)
|
103
|
-
|
104
|
-
|
194
|
+
needed = self.class.get_needs.map{|need| need.is_a?(Hash) ? need.keys : need}.flatten
|
195
|
+
assigned = []
|
196
|
+
local_assigns.each do |name, value|
|
197
|
+
unless needed.empty? || needed.include?(name)
|
198
|
+
raise "Unknown parameter '#{name}'. #{self.class.name} only accepts #{needed.join(', ')}"
|
199
|
+
end
|
200
|
+
assign_local(name, value)
|
201
|
+
assigned << name
|
202
|
+
end
|
203
|
+
|
204
|
+
# set variables with default values
|
205
|
+
self.class.get_needs.select{|var| var.is_a? Hash}.each do |hash|
|
206
|
+
hash.each_pair do |name, value|
|
207
|
+
unless assigned.include?(name)
|
208
|
+
assign_local(name, value)
|
209
|
+
assigned << name
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
missing = needed - assigned
|
215
|
+
unless missing.empty? || missing == [nil]
|
216
|
+
raise "Missing parameter#{missing.size == 1 ? '' : 's'}: #{missing.join(', ')}"
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def assign_local(name, value)
|
221
|
+
instance_variable_set("@#{name}", value)
|
222
|
+
if any_are_needed?
|
105
223
|
metaclass.module_eval do
|
106
224
|
attr_reader name
|
107
225
|
end
|
108
226
|
end
|
109
227
|
end
|
110
228
|
|
111
|
-
|
112
|
-
|
113
|
-
# (either in terms of how it is enabled, or in terms of
|
114
|
-
# what decisions Erector makes about where to add whitespace).
|
115
|
-
# This flag should be set prior to any rendering being done
|
116
|
-
# (for example, calls to to_s or to_pretty).
|
117
|
-
def enable_prettyprint(enable)
|
118
|
-
self.enable_prettyprint = enable
|
119
|
-
self
|
229
|
+
def any_are_needed?
|
230
|
+
!self.class.get_needs.empty?
|
120
231
|
end
|
121
|
-
|
232
|
+
|
122
233
|
# Render (like to_s) but adding newlines and indentation.
|
234
|
+
# This is a convenience method; you may just want to call to_s(:prettyprint => true)
|
235
|
+
# so you can pass in other rendering options as well.
|
123
236
|
def to_pretty
|
124
|
-
|
237
|
+
to_s(:prettyprint => true)
|
125
238
|
end
|
126
239
|
|
127
|
-
# Entry point for rendering a widget (and all its children). This method creates a new output string,
|
128
|
-
# calls this widget's #
|
240
|
+
# Entry point for rendering a widget (and all its children). This method creates a new output string (if necessary),
|
241
|
+
# calls this widget's #content method and returns the string.
|
129
242
|
#
|
130
|
-
#
|
131
|
-
#
|
132
|
-
#
|
133
|
-
#
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
243
|
+
# Options:
|
244
|
+
# output:: the string to output to. Default: a new empty string
|
245
|
+
# prettyprint:: whether Erector should add newlines and indentation. Default: the value of prettyprint_default (which is false by default).
|
246
|
+
# indentation:: the amount of spaces to indent. Ignored unless prettyprint is true.
|
247
|
+
# helpers:: a helpers object containing utility methods. Usually this is a Rails view object.
|
248
|
+
# content_method_name:: in case you want to call a method other than #content, pass its name in here.
|
249
|
+
#
|
250
|
+
# Note: Prettyprinting is an experimental feature and is subject to change
|
251
|
+
# (either in terms of how it is enabled, or in terms of
|
252
|
+
# what decisions Erector makes about where to add whitespace).
|
253
|
+
def to_s(options = {}, &blk)
|
254
|
+
|
255
|
+
raise "Erector::Widget#to_s now takes an options hash, not a symbol. Try calling \"to_s(:content_method_name=> :#{options})\"" if options.is_a? Symbol
|
256
|
+
|
257
|
+
options = {
|
258
|
+
:output => "",
|
259
|
+
:prettyprint => prettyprint_default,
|
260
|
+
:indentation => 0,
|
261
|
+
:helpers => nil,
|
262
|
+
:content_method_name => :content,
|
263
|
+
}.merge(options)
|
264
|
+
context(options[:output], options[:prettyprint], options[:indentation], options[:helpers]) do
|
265
|
+
send(options[:content_method_name], &blk)
|
266
|
+
output.to_s
|
267
|
+
end
|
140
268
|
end
|
141
269
|
|
142
270
|
alias_method :inspect, :to_s
|
143
|
-
|
271
|
+
|
144
272
|
# Template method which must be overridden by all widget subclasses. Inside this method you call the magic
|
145
273
|
# #element methods which emit HTML and text to the output string.
|
146
|
-
def
|
274
|
+
def content
|
147
275
|
if @block
|
148
276
|
instance_eval(&@block)
|
149
277
|
end
|
150
278
|
end
|
151
279
|
|
152
|
-
# To call one widget from another, inside the parent widget's
|
153
|
-
# its +
|
280
|
+
# To call one widget from another, inside the parent widget's +content+ method, instantiate the child widget and call
|
281
|
+
# its +write_via+ method, passing in +self+. This assures that the same output string
|
154
282
|
# is used, which gives better performance than using +capture+ or +to_s+.
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
@output = output_or_widget
|
283
|
+
# You can also use the +widget+ method.
|
284
|
+
def write_via(parent)
|
285
|
+
@parent = parent
|
286
|
+
context(parent.output, parent.prettyprint, parent.indentation, parent.helpers) do
|
287
|
+
content
|
161
288
|
end
|
162
|
-
render
|
163
289
|
end
|
164
290
|
|
165
|
-
#
|
166
|
-
# a
|
167
|
-
#
|
168
|
-
#
|
169
|
-
|
170
|
-
|
171
|
-
|
291
|
+
# Emits a (nested) widget onto the current widget's output stream. Accepts either
|
292
|
+
# a class or an instance. If the first argument is a class, then the second argument
|
293
|
+
# is a hash used to populate its instance variables. If the first argument is an
|
294
|
+
# instance then the hash must be unspecified (or empty).
|
295
|
+
#
|
296
|
+
# The sub-widget will have access to the methods of the parent class, via some method_missing
|
297
|
+
# magic and a "parent" pointer.
|
298
|
+
def widget(target, assigns={}, &block)
|
299
|
+
child = if target.is_a? Class
|
300
|
+
target.new(assigns, &block)
|
301
|
+
else
|
302
|
+
unless assigns.empty?
|
303
|
+
raise "Unexpected second parameter. Did you mean to pass in variables when you instantiated the #{target.class.to_s}?"
|
304
|
+
end
|
305
|
+
target
|
306
|
+
end
|
307
|
+
child.write_via(self)
|
172
308
|
end
|
173
309
|
|
174
310
|
# (Should we make this hidden?)
|
@@ -220,7 +356,7 @@ module Erector
|
|
220
356
|
# Emits an open tag, comprising '<', tag name, optional attributes, and '>'
|
221
357
|
def open_tag(tag_name, attributes={})
|
222
358
|
indent_for_open_tag(tag_name)
|
223
|
-
@
|
359
|
+
@indentation += SPACES_PER_INDENT
|
224
360
|
|
225
361
|
output.concat "<#{tag_name}#{format_attributes(attributes)}>"
|
226
362
|
@at_start_of_line = false
|
@@ -232,7 +368,11 @@ module Erector
|
|
232
368
|
# If another kind of object is passed in, the result of calling
|
233
369
|
# its to_s method will be treated as a string would be.
|
234
370
|
def text(value)
|
235
|
-
|
371
|
+
if value.is_a? Widget
|
372
|
+
widget value
|
373
|
+
else
|
374
|
+
output.concat(value.html_escape)
|
375
|
+
end
|
236
376
|
@at_start_of_line = false
|
237
377
|
nil
|
238
378
|
end
|
@@ -272,20 +412,19 @@ module Erector
|
|
272
412
|
|
273
413
|
# Emits a close tag, consisting of '<', tag name, and '>'
|
274
414
|
def close_tag(tag_name)
|
275
|
-
@
|
415
|
+
@indentation -= SPACES_PER_INDENT
|
276
416
|
indent()
|
277
417
|
|
278
418
|
output.concat("</#{tag_name}>")
|
279
419
|
|
280
420
|
if newliney(tag_name)
|
281
|
-
|
282
|
-
@at_start_of_line = true
|
421
|
+
_newline
|
283
422
|
end
|
284
423
|
end
|
285
424
|
|
286
425
|
# Emits the result of joining the elements in array with the separator.
|
287
426
|
# The array elements and separator can be Erector::Widget objects,
|
288
|
-
# which are rendered, or strings, which are
|
427
|
+
# which are rendered, or strings, which are html-escaped and output.
|
289
428
|
def join(array, separator)
|
290
429
|
first = true
|
291
430
|
array.each do |widget_or_text|
|
@@ -304,7 +443,7 @@ module Erector
|
|
304
443
|
|
305
444
|
# Creates a whole new output string, executes the block, then converts the output string to a string and
|
306
445
|
# emits it as raw text. If at all possible you should avoid this method since it hurts performance,
|
307
|
-
# and use
|
446
|
+
# and use +content+ or +write_via+ instead.
|
308
447
|
def capture(&block)
|
309
448
|
begin
|
310
449
|
original_output = output
|
@@ -375,10 +514,11 @@ module Erector
|
|
375
514
|
rawtext "\n"
|
376
515
|
end
|
377
516
|
|
378
|
-
# Convenience method to emit a css file link, which looks like this:
|
517
|
+
# Convenience method to emit a css file link, which looks like this:
|
518
|
+
# <link href="erector.css" rel="stylesheet" type="text/css" />
|
379
519
|
# The parameter is the full contents of the href attribute, including any ".css" extension.
|
380
520
|
#
|
381
|
-
# If you want to emit raw CSS inline, use the #
|
521
|
+
# If you want to emit raw CSS inline, use the #style method instead.
|
382
522
|
def css(href)
|
383
523
|
link :rel => 'stylesheet', :type => 'text/css', :href => href
|
384
524
|
end
|
@@ -389,7 +529,7 @@ module Erector
|
|
389
529
|
end
|
390
530
|
|
391
531
|
def newliney(tag_name)
|
392
|
-
if @
|
532
|
+
if @prettyprint
|
393
533
|
!NON_NEWLINEY.include?(tag_name)
|
394
534
|
else
|
395
535
|
false
|
@@ -427,6 +567,9 @@ protected
|
|
427
567
|
end
|
428
568
|
attributes ||= {}
|
429
569
|
open_tag tag_name, attributes
|
570
|
+
if block && value
|
571
|
+
raise ArgumentError, "You can't pass both a block and a value to #{tag_name} -- please choose one."
|
572
|
+
end
|
430
573
|
if block
|
431
574
|
instance_eval(&block)
|
432
575
|
else
|
@@ -441,23 +584,27 @@ protected
|
|
441
584
|
output.concat "<#{tag_name}#{format_attributes(attributes)} />"
|
442
585
|
|
443
586
|
if newliney(tag_name)
|
444
|
-
|
445
|
-
@at_start_of_line = true
|
587
|
+
_newline
|
446
588
|
end
|
447
589
|
end
|
590
|
+
|
591
|
+
def _newline
|
592
|
+
return unless @prettyprint
|
593
|
+
output.concat "\n"
|
594
|
+
@at_start_of_line = true
|
595
|
+
end
|
448
596
|
|
449
597
|
def indent_for_open_tag(tag_name)
|
598
|
+
return unless @prettyprint
|
450
599
|
if !@at_start_of_line && newliney(tag_name)
|
451
|
-
|
452
|
-
@at_start_of_line = true
|
600
|
+
_newline
|
453
601
|
end
|
454
|
-
|
455
602
|
indent()
|
456
603
|
end
|
457
604
|
|
458
605
|
def indent()
|
459
606
|
if @at_start_of_line
|
460
|
-
output.concat " " * @
|
607
|
+
output.concat " " * @indentation
|
461
608
|
end
|
462
609
|
end
|
463
610
|
|