erector 0.3.110 → 0.4.191

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.
Files changed (38) hide show
  1. data/README.txt +1 -1
  2. data/bin/erect +0 -0
  3. data/lib/erector.rb +13 -10
  4. data/lib/erector/doc.rb +138 -0
  5. data/lib/erector/erected.rb +1 -1
  6. data/lib/erector/rails.rb +6 -0
  7. data/lib/erector/rails/extensions/action_controller.rb +17 -0
  8. data/lib/erector/rails/extensions/action_view.rb +21 -0
  9. data/lib/erector/{helpers.rb → rails/extensions/widget.rb} +21 -14
  10. data/lib/erector/rails/supported_rails_versions.rb +11 -0
  11. data/lib/erector/rails/template_handlers/action_view_template_handler.rb +92 -0
  12. data/lib/erector/rhtml.treetop +6 -1
  13. data/lib/erector/unicode.rb +18185 -0
  14. data/lib/erector/unicode_builder.rb +67 -0
  15. data/lib/erector/widget.rb +86 -82
  16. data/spec/core_spec_suite.rb +3 -0
  17. data/spec/erect/erect_spec.rb +1 -4
  18. data/spec/erect/erected_spec.rb +1 -4
  19. data/spec/erect/rhtml_parser_spec.rb +9 -7
  20. data/spec/erector/doc_spec.rb +55 -0
  21. data/spec/erector/indentation_spec.rb +127 -0
  22. data/spec/erector/unicode_builder_spec.rb +75 -0
  23. data/spec/erector/widget_spec.rb +339 -175
  24. data/spec/erector/widgets/table_spec.rb +26 -37
  25. data/spec/rails_spec_suite.rb +3 -0
  26. data/spec/spec_helper.rb +11 -6
  27. data/spec/spec_suite.rb +44 -3
  28. metadata +54 -28
  29. data/lib/erector/extensions/action_controller.rb +0 -24
  30. data/lib/erector/extensions/action_view_template_handler.rb +0 -66
  31. data/lib/erector/html_parts.rb +0 -63
  32. data/spec/erector/extensions/render_widget_spec.rb +0 -68
  33. data/spec/erector/extensions/template_handler_spec.rb +0 -29
  34. data/spec/erector/rails_helpers_spec.rb +0 -149
  35. data/spec/rails/standard_helpers_spec.rb +0 -102
  36. data/spec/rails/view_spec.rb +0 -33
  37. data/spec/view_caching.rb +0 -30
  38. 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
+
@@ -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 HtmlParts stream
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 HtmlParts to the doc stream; "return" means that it returns a string
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={}, doc = HtmlParts.new, &block)
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 = 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 HtmlParts doc stream,
80
- # calls this widget's #render method, converts the HtmlParts to a string, and returns the string.
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
- render(&blk)
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 HtmlParts stream.
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 HtmlParts stream
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. (Should we make this hidden? How is it used?)
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+ HtmlParts stream. See how
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 HtmlParts stream untouched. Note that
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&amp;lt;4" instead of
164
199
  # "2&lt;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 << {:type => :open, :tagName => tag_name, :attributes => attributes}
206
+ @doc.open_tag tag_name, attributes
172
207
  end
173
208
 
174
- # Emits text which will be HTML-escaped.
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 << {:type => :text, :value => value}
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
- # Returns text which will *not* be HTML-escaped. Same effect as text(raw(s))
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
- # The output uses the entity-escaping format '&#160;' since that works
229
+ # With no arguments, return a single non-breaking space.
230
+ # The output uses the escaping format '&#160;' since that works
192
231
  # in both HTML and XML (as opposed to '&nbsp;' which only works in HTML).
193
- def nbsp(value)
232
+ def nbsp(value = " ")
194
233
  raw(value.html_escape.gsub(/ /,'&#160;'))
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 << {:type => :close, :tagName => tag_name}
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 << {:type => :instruct, :attributes => attributes}
258
+ @doc.instruct attributes
205
259
  end
206
260
 
207
261
  # Deprecated synonym of instruct
208
- def instruct!(attributes={:version => "1.0", :encoding => "UTF-8"})
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 = HtmlParts.new
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 << {:type => :empty, :tagName => tag_name, :attributes => attributes}
390
+ @doc.empty_element tag_name, attributes
387
391
  end
388
392
 
389
393
  end
@@ -0,0 +1,3 @@
1
+ dir = File.dirname(__FILE__)
2
+ require "#{dir}/spec_suite"
3
+ SpecSuite.core
@@ -1,7 +1,4 @@
1
- dir = File.dirname(__FILE__)
2
- require "#{dir}/../spec_helper"
3
- require "#{dir}/../../lib/erector/erect"
4
- require 'tempfile'
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
5
2
 
6
3
  module Erector
7
4
  describe Erect do
@@ -1,7 +1,4 @@
1
- dir = File.dirname(__FILE__)
2
- require "#{dir}/../spec_helper"
3
- require "#{dir}/../../lib/erector/erected"
4
- require 'tempfile'
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
5
2
 
6
3
  module Erector
7
4
  describe Erected do
@@ -1,10 +1,4 @@
1
- dir = File.dirname(__FILE__)
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