erector 0.3.110 → 0.4.191
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +1 -1
- data/bin/erect +0 -0
- data/lib/erector.rb +13 -10
- data/lib/erector/doc.rb +138 -0
- data/lib/erector/erected.rb +1 -1
- data/lib/erector/rails.rb +6 -0
- data/lib/erector/rails/extensions/action_controller.rb +17 -0
- data/lib/erector/rails/extensions/action_view.rb +21 -0
- data/lib/erector/{helpers.rb → rails/extensions/widget.rb} +21 -14
- data/lib/erector/rails/supported_rails_versions.rb +11 -0
- data/lib/erector/rails/template_handlers/action_view_template_handler.rb +92 -0
- data/lib/erector/rhtml.treetop +6 -1
- data/lib/erector/unicode.rb +18185 -0
- data/lib/erector/unicode_builder.rb +67 -0
- data/lib/erector/widget.rb +86 -82
- data/spec/core_spec_suite.rb +3 -0
- data/spec/erect/erect_spec.rb +1 -4
- data/spec/erect/erected_spec.rb +1 -4
- data/spec/erect/rhtml_parser_spec.rb +9 -7
- data/spec/erector/doc_spec.rb +55 -0
- data/spec/erector/indentation_spec.rb +127 -0
- data/spec/erector/unicode_builder_spec.rb +75 -0
- data/spec/erector/widget_spec.rb +339 -175
- data/spec/erector/widgets/table_spec.rb +26 -37
- data/spec/rails_spec_suite.rb +3 -0
- data/spec/spec_helper.rb +11 -6
- data/spec/spec_suite.rb +44 -3
- metadata +54 -28
- data/lib/erector/extensions/action_controller.rb +0 -24
- data/lib/erector/extensions/action_view_template_handler.rb +0 -66
- data/lib/erector/html_parts.rb +0 -63
- data/spec/erector/extensions/render_widget_spec.rb +0 -68
- data/spec/erector/extensions/template_handler_spec.rb +0 -29
- data/spec/erector/rails_helpers_spec.rb +0 -149
- data/spec/rails/standard_helpers_spec.rb +0 -102
- data/spec/rails/view_spec.rb +0 -33
- data/spec/view_caching.rb +0 -30
- data/test/rails_root/app/views/template_handler_spec/test_page.rb +0 -8
@@ -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
|
+
|
data/lib/erector/widget.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'cgi'
|
2
|
-
|
3
1
|
module Erector
|
4
2
|
|
5
3
|
# A Widget is the center of the Erector universe.
|
@@ -14,11 +12,11 @@ module Erector
|
|
14
12
|
# To render a widget from the outside, instantiate it and call its +to_s+ method.
|
15
13
|
#
|
16
14
|
# To call one widget from another, inside the parent widget's render method, instantiate the child widget and call
|
17
|
-
# its +render_to+ method, passing in +self+ (or self.doc if you prefer). This assures that the same
|
15
|
+
# its +render_to+ method, passing in +self+ (or self.doc if you prefer). This assures that the same Doc stream
|
18
16
|
# is used, which gives better performance than using +capture+ or +to_s+.
|
19
17
|
#
|
20
18
|
# In this documentation we've tried to keep the distinction clear between methods that *emit* text and those that
|
21
|
-
# *return* text. "Emit" means that it writes
|
19
|
+
# *return* text. "Emit" means that it writes Doc to the doc stream; "return" means that it returns a string
|
22
20
|
# like a normal method and leaves it up to the caller to emit that string if it wants.
|
23
21
|
class Widget
|
24
22
|
class << self
|
@@ -45,23 +43,41 @@ module Erector
|
|
45
43
|
]
|
46
44
|
end
|
47
45
|
|
46
|
+
def after_initialize(instance=nil, &blk)
|
47
|
+
if blk
|
48
|
+
after_initialize_parts << blk
|
49
|
+
elsif instance
|
50
|
+
if superclass.respond_to?(:after_initialize)
|
51
|
+
superclass.after_initialize instance
|
52
|
+
end
|
53
|
+
after_initialize_parts.each do |part|
|
54
|
+
instance.instance_eval &part
|
55
|
+
end
|
56
|
+
else
|
57
|
+
raise ArgumentError, "You must provide either an instance or a block"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
protected
|
62
|
+
def after_initialize_parts
|
63
|
+
@after_initialize_parts ||= []
|
64
|
+
end
|
48
65
|
end
|
49
66
|
|
50
|
-
include ActionController::UrlWriter
|
51
|
-
include Helpers
|
52
67
|
attr_reader :helpers
|
53
68
|
attr_reader :assigns
|
54
69
|
attr_reader :doc
|
55
70
|
attr_reader :block
|
56
71
|
attr_reader :parent
|
57
72
|
|
58
|
-
def initialize(helpers=nil, assigns={},
|
73
|
+
def initialize(helpers=nil, assigns={}, io = StringIO.new(""), &block)
|
59
74
|
@assigns = assigns
|
60
75
|
assign_locals(assigns)
|
61
76
|
@helpers = helpers
|
62
77
|
@parent = block ? eval("self", block.binding) : nil
|
63
|
-
@doc =
|
78
|
+
@doc = Doc.new(io)
|
64
79
|
@block = block
|
80
|
+
self.class.after_initialize self
|
65
81
|
end
|
66
82
|
|
67
83
|
#-- methods for other classes to call, left public for ease of testing and documentation
|
@@ -75,26 +91,42 @@ module Erector
|
|
75
91
|
end
|
76
92
|
end
|
77
93
|
end
|
94
|
+
|
95
|
+
# Set whether Erector should add newlines and indentation in to_s.
|
96
|
+
# This is an experimental feature and is subject to change
|
97
|
+
# (either in terms of how it is enabled, or in terms of
|
98
|
+
# what decisions Erector makes about where to add whitespace).
|
99
|
+
# This flag should be set prior to any rendering being done
|
100
|
+
# (for example, calls to to_s or to_pretty).
|
101
|
+
def enable_prettyprint(enable)
|
102
|
+
@doc.enable_prettyprint = enable
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
# Render (like to_s) but adding newlines and indentation.
|
107
|
+
def to_pretty
|
108
|
+
enable_prettyprint(true).to_s
|
109
|
+
end
|
78
110
|
|
79
|
-
# Entry point for rendering a widget (and all its children). This method creates a new
|
80
|
-
# calls this widget's #render method, converts the
|
111
|
+
# Entry point for rendering a widget (and all its children). This method creates a new Doc doc stream,
|
112
|
+
# calls this widget's #render method, converts the Doc to a string, and returns the string.
|
81
113
|
#
|
82
114
|
# If it's called again later
|
83
115
|
# then it returns the earlier rendered string, which leads to higher performance, but may have confusing
|
84
116
|
# effects if some underlying state has changed. In general we recommend you create a new instance of every
|
85
117
|
# widget for each render, unless you know what you're doing.
|
86
|
-
def to_s(&blk)
|
118
|
+
def to_s(render_method_name=:render, &blk)
|
87
119
|
# The @__to_s variable is used as a cache.
|
88
120
|
# If it's useful we should add a test for it. -ac
|
89
121
|
return @__to_s if @__to_s
|
90
|
-
|
122
|
+
send(render_method_name, &blk)
|
91
123
|
@__to_s = @doc.to_s
|
92
124
|
end
|
93
|
-
|
125
|
+
|
94
126
|
alias_method :inspect, :to_s
|
95
127
|
|
96
128
|
# Template method which must be overridden by all widget subclasses. Inside this method you call the magic
|
97
|
-
# #element methods which emit HTML and text to the
|
129
|
+
# #element methods which emit HTML and text to the Doc stream.
|
98
130
|
def render
|
99
131
|
if @block
|
100
132
|
instance_eval(&@block)
|
@@ -102,7 +134,7 @@ module Erector
|
|
102
134
|
end
|
103
135
|
|
104
136
|
# To call one widget from another, inside the parent widget's render method, instantiate the child widget and call
|
105
|
-
# its +render_to+ method, passing in +self+ (or self.doc if you prefer). This assures that the same
|
137
|
+
# its +render_to+ method, passing in +self+ (or self.doc if you prefer). This assures that the same Doc stream
|
106
138
|
# is used, which gives better performance than using +capture+ or +to_s+.
|
107
139
|
def render_to(doc_or_widget)
|
108
140
|
if doc_or_widget.is_a?(Widget)
|
@@ -114,9 +146,12 @@ module Erector
|
|
114
146
|
render
|
115
147
|
end
|
116
148
|
|
117
|
-
# Convenience method for on-the-fly widgets.
|
149
|
+
# Convenience method for on-the-fly widgets. This is a way of making
|
150
|
+
# a sub-widget which still has access to the methods of the parent class.
|
151
|
+
# This is an experimental erector feature which may disappear in future
|
152
|
+
# versions of erector (see #widget in widget_spec in the Erector tests).
|
118
153
|
def widget(widget_class, assigns={}, &block)
|
119
|
-
child = widget_class.new(helpers, assigns, doc, &block)
|
154
|
+
child = widget_class.new(helpers, assigns, doc.output, &block)
|
120
155
|
child.render
|
121
156
|
end
|
122
157
|
|
@@ -139,7 +174,7 @@ module Erector
|
|
139
174
|
# When calling one of these magic methods, put attributes in the default hash. If there is a string parameter,
|
140
175
|
# then it is used as the contents. If there is a block, then it is executed (yielded), and the string parameter is ignored.
|
141
176
|
# The block will usually be in the scope of the child widget, which means it has access to all the
|
142
|
-
# methods of Widget, which will eventually end up appending text to the +doc+
|
177
|
+
# methods of Widget, which will eventually end up appending text to the +doc+ Doc stream. See how
|
143
178
|
# elegant it is? Not confusing at all if you don't think about it.
|
144
179
|
#
|
145
180
|
def element(*args, &block)
|
@@ -158,7 +193,7 @@ module Erector
|
|
158
193
|
__empty_element__(*args, &block)
|
159
194
|
end
|
160
195
|
|
161
|
-
# Returns an HTML-escaped version of its parameter. Leaves the
|
196
|
+
# Returns an HTML-escaped version of its parameter. Leaves the Doc stream untouched. Note that
|
162
197
|
# the #text method automatically HTML-escapes its parameter, so be careful *not* to do something like
|
163
198
|
# text(h("2<4")) since that will double-escape the less-than sign (you'll get "2&lt;4" instead of
|
164
199
|
# "2<4").
|
@@ -168,13 +203,16 @@ module Erector
|
|
168
203
|
|
169
204
|
# Emits an open tag, comprising '<', tag name, optional attributes, and '>'
|
170
205
|
def open_tag(tag_name, attributes={})
|
171
|
-
@doc
|
206
|
+
@doc.open_tag tag_name, attributes
|
172
207
|
end
|
173
208
|
|
174
|
-
# Emits text
|
209
|
+
# Emits text. If a string is passed in, it will be HTML-escaped.
|
210
|
+
# If a widget or the result of calling methods such as raw
|
211
|
+
# is passed in, the HTML will not be HTML-escaped again.
|
212
|
+
# If another kind of object is passed in, the result of calling
|
213
|
+
# its to_s method will be treated as a string would be.
|
175
214
|
def text(value)
|
176
|
-
@doc
|
177
|
-
nil
|
215
|
+
@doc.text value
|
178
216
|
end
|
179
217
|
|
180
218
|
# Returns text which will *not* be HTML-escaped.
|
@@ -182,32 +220,46 @@ module Erector
|
|
182
220
|
RawString.new(value.to_s)
|
183
221
|
end
|
184
222
|
|
185
|
-
#
|
223
|
+
# Emits text which will *not* be HTML-escaped. Same effect as text(raw(s))
|
186
224
|
def rawtext(value)
|
187
225
|
text raw(value)
|
188
226
|
end
|
189
227
|
|
190
228
|
# Returns a copy of value with spaces replaced by non-breaking space characters.
|
191
|
-
#
|
229
|
+
# With no arguments, return a single non-breaking space.
|
230
|
+
# The output uses the escaping format ' ' since that works
|
192
231
|
# in both HTML and XML (as opposed to ' ' which only works in HTML).
|
193
|
-
def nbsp(value)
|
232
|
+
def nbsp(value = " ")
|
194
233
|
raw(value.html_escape.gsub(/ /,' '))
|
195
234
|
end
|
235
|
+
|
236
|
+
# Return a character given its unicode code point or unicode name.
|
237
|
+
def character(code_point_or_name)
|
238
|
+
if code_point_or_name.is_a?(Symbol)
|
239
|
+
found = Erector::CHARACTERS[code_point_or_name]
|
240
|
+
if found.nil?
|
241
|
+
raise "Unrecognized character #{code_point_or_name}"
|
242
|
+
end
|
243
|
+
raw("&#x#{sprintf '%x', found};")
|
244
|
+
elsif code_point_or_name.is_a?(Integer)
|
245
|
+
raw("&#x#{sprintf '%x', code_point_or_name};")
|
246
|
+
else
|
247
|
+
raise "Unrecognized argument to character: #{code_point_or_name}"
|
248
|
+
end
|
249
|
+
end
|
196
250
|
|
197
251
|
# Emits a close tag, consisting of '<', tag name, and '>'
|
198
252
|
def close_tag(tag_name)
|
199
|
-
@doc
|
253
|
+
@doc.close_tag tag_name
|
200
254
|
end
|
201
255
|
|
202
256
|
# Emits an XML instruction, which looks like this: <?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
203
257
|
def instruct(attributes={:version => "1.0", :encoding => "UTF-8"})
|
204
|
-
@doc
|
258
|
+
@doc.instruct attributes
|
205
259
|
end
|
206
260
|
|
207
261
|
# Deprecated synonym of instruct
|
208
|
-
|
209
|
-
@doc << {:type => :instruct, :attributes => attributes}
|
210
|
-
end
|
262
|
+
alias_method :instruct!, :instruct
|
211
263
|
|
212
264
|
# Creates a whole new doc stream, executes the block, then converts the doc stream to a string and
|
213
265
|
# emits it as raw text. If at all possible you should avoid this method since it hurts performance,
|
@@ -215,7 +267,7 @@ module Erector
|
|
215
267
|
def capture(&block)
|
216
268
|
begin
|
217
269
|
original_doc = @doc
|
218
|
-
@doc =
|
270
|
+
@doc = Doc.new(StringIO.new)
|
219
271
|
yield
|
220
272
|
raw(@doc.to_s)
|
221
273
|
ensure
|
@@ -299,6 +351,7 @@ module Erector
|
|
299
351
|
|
300
352
|
protected
|
301
353
|
|
354
|
+
# This is part of the sub-widget/parent feature (see #widget method).
|
302
355
|
def method_missing(name, *args, &block)
|
303
356
|
block ||= lambda {} # captures self HERE
|
304
357
|
if @parent
|
@@ -308,55 +361,6 @@ protected
|
|
308
361
|
end
|
309
362
|
end
|
310
363
|
|
311
|
-
def fake_erbout(&blk)
|
312
|
-
# override concat on the helpers object (which is usually a Rails view object)
|
313
|
-
unless @helpers.respond_to?(:concat_without_erector)
|
314
|
-
@helpers.metaclass.class_eval do
|
315
|
-
alias_method :capture_without_erector, :capture
|
316
|
-
def capture(*args, &block)
|
317
|
-
result = nil
|
318
|
-
widget = @erector_widget_stack.first
|
319
|
-
begin
|
320
|
-
original_doc = widget.doc
|
321
|
-
widget.instance_eval do
|
322
|
-
@doc = HtmlParts.new
|
323
|
-
end
|
324
|
-
captured = capture_without_erector(*args, &block)
|
325
|
-
result = widget.raw(widget.doc.to_s)
|
326
|
-
ensure
|
327
|
-
widget.instance_eval do
|
328
|
-
@doc = original_doc
|
329
|
-
end
|
330
|
-
end
|
331
|
-
result
|
332
|
-
end
|
333
|
-
|
334
|
-
alias_method :concat_without_erector, :concat
|
335
|
-
define_method :concat do |*args|
|
336
|
-
some_text, binding = args
|
337
|
-
raise "widget stack too big" if @erector_widget_stack.size > 10
|
338
|
-
if @erector_widget_stack.empty?
|
339
|
-
concat_without_erector(some_text, binding)
|
340
|
-
else
|
341
|
-
@erector_widget_stack.first.rawtext(some_text)
|
342
|
-
end
|
343
|
-
end
|
344
|
-
define_method :erector_widget_stack do
|
345
|
-
@erector_widget_stack ||= []
|
346
|
-
end
|
347
|
-
end
|
348
|
-
end
|
349
|
-
|
350
|
-
@helpers.erector_widget_stack.unshift(self)
|
351
|
-
yield
|
352
|
-
ensure
|
353
|
-
if @helpers.respond_to?(:concat_without_erector)
|
354
|
-
@helpers.erector_widget_stack.shift
|
355
|
-
end
|
356
|
-
end
|
357
|
-
|
358
|
-
private
|
359
|
-
|
360
364
|
def __element__(tag_name, *args, &block)
|
361
365
|
if args.length > 2
|
362
366
|
raise ArgumentError, "Cannot accept more than three arguments"
|
@@ -383,7 +387,7 @@ private
|
|
383
387
|
end
|
384
388
|
|
385
389
|
def __empty_element__(tag_name, attributes={})
|
386
|
-
@doc
|
390
|
+
@doc.empty_element tag_name, attributes
|
387
391
|
end
|
388
392
|
|
389
393
|
end
|
data/spec/erect/erect_spec.rb
CHANGED
data/spec/erect/erected_spec.rb
CHANGED
@@ -1,10 +1,4 @@
|
|
1
|
-
|
2
|
-
require "#{dir}/../spec_helper"
|
3
|
-
|
4
|
-
# require 'test/unit'
|
5
|
-
require 'rubygems'
|
6
|
-
require 'treetop'
|
7
|
-
require "erector/erected" # pull this out so we don't recreate the grammar every time
|
1
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
|
8
2
|
|
9
3
|
module ParserTestHelper
|
10
4
|
def assert_evals_to_self(input)
|
@@ -189,6 +183,7 @@ describe RhtmlParser do
|
|
189
183
|
parse("<%= h hi \"mom\" %>").convert.should == "text hi(\"mom\")\n"
|
190
184
|
|
191
185
|
parse("<%= \"mom\" %>").convert.should == "rawtext \"mom\"\n"
|
186
|
+
parse("<%= \"hi mom\" %>").convert.should == "rawtext \"hi mom\"\n"
|
192
187
|
parse("<%= hi \"mom\" %>").convert.should == "rawtext hi(\"mom\")\n"
|
193
188
|
|
194
189
|
parse("<%= link_to blah %>").convert.should == "rawtext link_to(blah)\n"
|
@@ -202,6 +197,13 @@ describe RhtmlParser do
|
|
202
197
|
parse("<%= h foo / bar %>").convert.should == "text foo / bar\n"
|
203
198
|
end
|
204
199
|
|
200
|
+
it "converts yield so layouts work" do
|
201
|
+
pending("easy enough to make this pass, but the result doesn't seem to work as a layout")
|
202
|
+
parse("<%= yield %>").convert.should == "rawtext @content\n"
|
203
|
+
parse("<%= \"yield\" %>").convert.should == "rawtext \"yield\"\n"
|
204
|
+
parse("<%= \"the yield is good\" %>").convert.should == "rawtext \"the yield is good\"\n"
|
205
|
+
end
|
206
|
+
|
205
207
|
it "parses quoted strings" do
|
206
208
|
@parser.root = :quoted
|
207
209
|
parse("'foo'").value.should == "foo"
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
|
2
|
+
|
3
|
+
module Erector
|
4
|
+
describe Doc do
|
5
|
+
describe "#output" do
|
6
|
+
it "seeks to the end of the buffer" do
|
7
|
+
string = "Hello"
|
8
|
+
io = StringIO.new(string)
|
9
|
+
doc = Doc.new(io)
|
10
|
+
|
11
|
+
string.concat(" World")
|
12
|
+
doc.text " Again"
|
13
|
+
|
14
|
+
string.should == "Hello World Again"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#method_missing" do
|
19
|
+
context "when passed in io object raises a NoMethodError" do
|
20
|
+
context "when the passed in io object respond_to? method is false" do
|
21
|
+
attr_reader :io
|
22
|
+
before do
|
23
|
+
@io = StringIO.new
|
24
|
+
io.should_not respond_to(:foo)
|
25
|
+
lambda {io.foo}.should raise_error(NoMethodError, /undefined method `foo' for #<StringIO/)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "raises a NoMethodError that originates from within Doc#method_missing" do
|
29
|
+
doc = Doc.new(io)
|
30
|
+
lambda do
|
31
|
+
doc.foo
|
32
|
+
end.should raise_error(NoMethodError, /undefined method `foo' for #<Erector::Doc/)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when the passed in io object respond_to? method is true" do
|
37
|
+
attr_reader :io
|
38
|
+
before do
|
39
|
+
@io = StringIO.new
|
40
|
+
stub(io).foo {raise NoMethodError, "Stubbed NoMethodError"}
|
41
|
+
io.should respond_to(:foo)
|
42
|
+
lambda {io.foo}.should raise_error(NoMethodError, "Stubbed NoMethodError")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "raises a NoMethodError that originates from within Doc#method_missing" do
|
46
|
+
doc = Doc.new(io)
|
47
|
+
lambda do
|
48
|
+
doc.foo
|
49
|
+
end.should raise_error(NoMethodError, /undefined method `foo' for #<Erector::Doc/)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|