erector 0.8.1 → 0.8.2
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/VERSION.yml +1 -1
- data/lib/erector.rb +1 -0
- data/lib/erector/abstract_widget.rb +172 -0
- data/lib/erector/dependency.rb +9 -0
- data/lib/erector/erect/erected.rb +4 -4
- data/lib/erector/html.rb +15 -12
- data/lib/erector/needs.rb +2 -2
- data/lib/erector/widget.rb +12 -175
- data/spec/erector/convenience_spec.rb +1 -0
- data/spec/erector/dependency_spec.rb +33 -12
- data/spec/erector/externals_spec.rb +1 -0
- data/spec/erector/hello_from_readme.rb +18 -0
- data/spec/erector/hello_from_readme_spec.rb +12 -0
- data/spec/erector/html_spec.rb +41 -0
- data/spec/erector/output_spec.rb +1 -1
- data/spec/spec_helper.rb +35 -7
- metadata +92 -66
data/VERSION.yml
CHANGED
data/lib/erector.rb
CHANGED
@@ -0,0 +1,172 @@
|
|
1
|
+
module Erector
|
2
|
+
|
3
|
+
# Abstract base class for Widget. This pattern allows Widget to include lots of nicely organized modules and still
|
4
|
+
# have proper semantics for "super" in subclasses. See the rdoc for Widget for the list of all the included modules.
|
5
|
+
class AbstractWidget
|
6
|
+
@@prettyprint_default = false
|
7
|
+
def prettyprint_default
|
8
|
+
@@prettyprint_default
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.prettyprint_default
|
12
|
+
@@prettyprint_default
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.prettyprint_default=(enabled)
|
16
|
+
@@prettyprint_default = enabled
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.inline(*args, &block)
|
20
|
+
Class.new(self) do
|
21
|
+
include Erector::Inline
|
22
|
+
end.new(*args, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
[:helpers, :assigns, :output, :parent, :block].each do |attr|
|
26
|
+
class_eval(<<-SRC, __FILE__, __LINE__ + 1)
|
27
|
+
def #{attr}
|
28
|
+
@_#{attr}
|
29
|
+
end
|
30
|
+
SRC
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(assigns = {}, &block)
|
34
|
+
unless assigns.is_a? Hash
|
35
|
+
raise "Erector widgets are initialized with only a parameter hash. (Other parameters are passed to to_html, or the #widget method.)"
|
36
|
+
end
|
37
|
+
|
38
|
+
@_assigns = assigns
|
39
|
+
|
40
|
+
assigns.each do |name, value|
|
41
|
+
instance_variable_set(name.to_s[0..0] == '@' ? name : "@#{name}", value)
|
42
|
+
end
|
43
|
+
|
44
|
+
@_parent = eval("self", block.binding) if block
|
45
|
+
@_block = block
|
46
|
+
end
|
47
|
+
|
48
|
+
# Entry point for rendering a widget (and all its children). This method
|
49
|
+
# creates a new output string (if necessary), calls this widget's #content
|
50
|
+
# method and returns the string.
|
51
|
+
#
|
52
|
+
# Options:
|
53
|
+
# output:: the string to output to. Default: a new empty string
|
54
|
+
# prettyprint:: whether Erector should add newlines and indentation.
|
55
|
+
# Default: the value of prettyprint_default (which is false
|
56
|
+
# by default).
|
57
|
+
# indentation:: the amount of spaces to indent. Ignored unless prettyprint
|
58
|
+
# is true.
|
59
|
+
# max_length:: preferred maximum length of a line. Line wraps will only
|
60
|
+
# occur at space characters, so a long word may end up creating
|
61
|
+
# a line longer than this. If nil (default), then there is no
|
62
|
+
# arbitrary limit to line lengths, and only internal newline
|
63
|
+
# characters and prettyprinting will determine newlines in the
|
64
|
+
# output.
|
65
|
+
# helpers:: a helpers object containing utility methods. Usually this is a
|
66
|
+
# Rails view object.
|
67
|
+
# content_method_name:: in case you want to call a method other than
|
68
|
+
# #content, pass its name in here.
|
69
|
+
def to_html(options = {})
|
70
|
+
raise "Erector::Widget#to_html takes an options hash, not a symbol. Try calling \"to_html(:content_method_name=> :#{options})\"" if options.is_a? Symbol
|
71
|
+
_render(options).to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
# alias for #to_html
|
75
|
+
# @deprecated Please use {#to_html} instead
|
76
|
+
def to_s(*args)
|
77
|
+
unless defined? @@already_warned_to_s
|
78
|
+
$stderr.puts "Erector::Widget#to_s is deprecated. Please use #to_html instead. Called from #{caller.first}"
|
79
|
+
@@already_warned_to_s = true
|
80
|
+
end
|
81
|
+
to_html(*args)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Entry point for rendering a widget (and all its children). Same as #to_html
|
85
|
+
# only it returns an array, for theoretical performance improvements when using a
|
86
|
+
# Rack server (like Sinatra or Rails Metal).
|
87
|
+
#
|
88
|
+
# # Options: see #to_html
|
89
|
+
def to_a(options = {})
|
90
|
+
_render(options).to_a
|
91
|
+
end
|
92
|
+
|
93
|
+
# Template method which must be overridden by all widget subclasses.
|
94
|
+
# Inside this method you call the magic #element methods which emit HTML
|
95
|
+
# and text to the output string. If you call "super" (or don't override
|
96
|
+
# +content+, or explicitly call "call_block") then your widget will
|
97
|
+
# execute the block that was passed into its constructor. The semantics of
|
98
|
+
# this block are confusing; make sure to read the rdoc for Erector#call_block
|
99
|
+
def content
|
100
|
+
call_block
|
101
|
+
end
|
102
|
+
|
103
|
+
# When this method is executed, the default block that was passed in to
|
104
|
+
# the widget's constructor will be executed. The semantics of this
|
105
|
+
# block -- that is, what "self" is, and whether it has access to
|
106
|
+
# Erector methods like "div" and "text", and the widget's instance
|
107
|
+
# variables -- can be quite confusing. The rule is, most of the time the
|
108
|
+
# block is evaluated using "call" or "yield", which means that its scope
|
109
|
+
# is that of the caller. So if that caller is not an Erector widget, it
|
110
|
+
# will *not* have access to the Erector methods, but it *will* have access
|
111
|
+
# to instance variables and methods of the calling object.
|
112
|
+
#
|
113
|
+
# If you want this block to have access to Erector methods then use
|
114
|
+
# Erector::Inline#content or Erector#inline.
|
115
|
+
def call_block
|
116
|
+
@_block.call(self) if @_block
|
117
|
+
end
|
118
|
+
|
119
|
+
# Emits a (nested) widget onto the current widget's output stream. Accepts
|
120
|
+
# either a class or an instance. If the first argument is a class, then
|
121
|
+
# the second argument is a hash used to populate its instance variables.
|
122
|
+
# If the first argument is an instance then the hash must be unspecified
|
123
|
+
# (or empty). If a block is passed to this method, then it gets set as the
|
124
|
+
# rendered widget's block.
|
125
|
+
#
|
126
|
+
# This is the preferred way to call one widget from inside another. This
|
127
|
+
# method assures that the same output string is used, which gives better
|
128
|
+
# performance than using +capture+ or +to_html+.
|
129
|
+
def widget(target, assigns = {}, options = {}, &block)
|
130
|
+
if target.is_a? Class
|
131
|
+
target.new(assigns, &block)._render_via(self, options)
|
132
|
+
else
|
133
|
+
unless assigns.empty?
|
134
|
+
raise "Unexpected second parameter. Did you mean to pass in assigns when you instantiated the #{target.class.to_s}?"
|
135
|
+
end
|
136
|
+
target._render_via(self, options, &block)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Creates a whole new output string, executes the block, then converts the
|
141
|
+
# output string to a string and returns it as raw text. If at all possible
|
142
|
+
# you should avoid this method since it hurts performance, and use +widget+
|
143
|
+
# instead.
|
144
|
+
def capture
|
145
|
+
original, @_output = output, Output.new
|
146
|
+
yield
|
147
|
+
original.widgets.concat(output.widgets) # todo: test!!!
|
148
|
+
output.to_s
|
149
|
+
ensure
|
150
|
+
@_output = original
|
151
|
+
end
|
152
|
+
|
153
|
+
protected
|
154
|
+
def _render(options = {}, &block)
|
155
|
+
@_block = block if block
|
156
|
+
@_parent = options[:parent] || parent
|
157
|
+
@_helpers = options[:helpers] || parent
|
158
|
+
@_output = options[:output]
|
159
|
+
@_output = Output.new(options) unless output.is_a?(Output)
|
160
|
+
|
161
|
+
output.widgets << self.class
|
162
|
+
send(options[:content_method_name] || :content)
|
163
|
+
output
|
164
|
+
end
|
165
|
+
|
166
|
+
def _render_via(parent, options = {}, &block)
|
167
|
+
_render(options.merge(:parent => parent,
|
168
|
+
:output => parent.output,
|
169
|
+
:helpers => parent.helpers), &block)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
data/lib/erector/dependency.rb
CHANGED
@@ -17,5 +17,14 @@ module Erector
|
|
17
17
|
self.text == other.text and
|
18
18
|
self.options == other.options) ? true : false
|
19
19
|
end
|
20
|
+
|
21
|
+
def eql?(other)
|
22
|
+
self == other
|
23
|
+
end
|
24
|
+
|
25
|
+
def hash
|
26
|
+
# this is a fairly inefficient hash function but it does the trick for now
|
27
|
+
"#{type}#{text}#{options}".hash
|
28
|
+
end
|
20
29
|
end
|
21
30
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'treetop'
|
3
|
-
|
4
|
-
require "#{
|
5
|
-
Treetop.load("#{
|
3
|
+
|
4
|
+
require "#{File.dirname(__FILE__)}/indenting"
|
5
|
+
Treetop.load("#{File.dirname(__FILE__)}/rhtml.treetop")
|
6
6
|
|
7
7
|
module Erector
|
8
8
|
class Erected
|
@@ -72,4 +72,4 @@ module Erector
|
|
72
72
|
filename.split("_").map{|part| part.capitalize}.join
|
73
73
|
end
|
74
74
|
end
|
75
|
-
end
|
75
|
+
end
|
data/lib/erector/html.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
module Erector
|
2
2
|
module HTML
|
3
3
|
module ClassMethods
|
4
|
-
# Tags which are always self-closing. Click "[
|
4
|
+
# Tags which are always self-closing. Click "[show source]" to see the full list.
|
5
5
|
def empty_tags
|
6
6
|
['area', 'base', 'br', 'col', 'embed', 'frame',
|
7
7
|
'hr', 'img', 'input', 'link', 'meta', 'param']
|
8
8
|
end
|
9
9
|
|
10
|
-
# Tags which can contain other stuff. Click "[
|
10
|
+
# Tags which can contain other stuff. Click "[show source]" to see the full list.
|
11
11
|
def full_tags
|
12
12
|
[
|
13
13
|
'a', 'abbr', 'acronym', 'address', 'article', 'aside', 'audio',
|
@@ -281,17 +281,20 @@ module Erector
|
|
281
281
|
end
|
282
282
|
attributes ||= {}
|
283
283
|
open_tag tag_name, attributes
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
block
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
284
|
+
begin
|
285
|
+
if block && value
|
286
|
+
raise ArgumentError, "You can't pass both a block and a value to #{tag_name} -- please choose one."
|
287
|
+
end
|
288
|
+
if block
|
289
|
+
block.call
|
290
|
+
elsif raw
|
291
|
+
text! value
|
292
|
+
else
|
293
|
+
text value
|
294
|
+
end
|
295
|
+
ensure
|
296
|
+
close_tag tag_name
|
293
297
|
end
|
294
|
-
close_tag tag_name
|
295
298
|
end
|
296
299
|
|
297
300
|
def __empty_element__(tag_name, attributes={})
|
data/lib/erector/needs.rb
CHANGED
@@ -82,12 +82,12 @@ module Erector
|
|
82
82
|
|
83
83
|
missing = self.class.needed_variables - assigned
|
84
84
|
unless missing.empty? || missing == [nil]
|
85
|
-
raise "Missing parameter#{missing.size == 1 ? '' : 's'}: #{missing.join(', ')}"
|
85
|
+
raise "Missing parameter#{missing.size == 1 ? '' : 's'} for #{self.class.name}: #{missing.join(', ')}"
|
86
86
|
end
|
87
87
|
|
88
88
|
excess = assigned - self.class.needed_variables
|
89
89
|
unless self.class.needed_variables.empty? || excess.empty?
|
90
|
-
raise("Excess parameter#{excess.size == 1 ? '' : 's'}: #{excess.join(', ')}")
|
90
|
+
raise("Excess parameter#{excess.size == 1 ? '' : 's'} for #{self.class.name}: #{excess.join(', ')}")
|
91
91
|
end
|
92
92
|
end
|
93
93
|
end
|
data/lib/erector/widget.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
module Erector
|
2
2
|
|
3
|
-
# A Widget is the center of the Erector universe.
|
3
|
+
# A Widget is the center of the Erector universe.
|
4
4
|
#
|
5
5
|
# To create a widget, extend Erector::Widget and implement the +content+
|
6
6
|
# method. Inside this method you may call any of the tag methods like +span+
|
7
|
-
# or +p+ to emit HTML/XML tags.
|
8
|
-
#
|
7
|
+
# or +p+ to emit HTML/XML tags.
|
8
|
+
#
|
9
9
|
# You can also define a widget on the fly by passing a block to +new+. This
|
10
10
|
# block will get executed when the widget's +content+ method is called. See
|
11
11
|
# the userguide for important details about the scope of this block when run --
|
@@ -17,193 +17,30 @@ module Erector
|
|
17
17
|
# A widget's +new+ method optionally accepts an options hash. Entries in
|
18
18
|
# this hash are converted to instance variables.
|
19
19
|
#
|
20
|
-
# You can add runtime input checking via the +needs+ macro. See #needs.
|
20
|
+
# You can add runtime input checking via the +needs+ macro. See #needs.
|
21
21
|
# This mechanism is meant to ameliorate development-time confusion about
|
22
22
|
# exactly what parameters are supported by a given widget, avoiding
|
23
23
|
# confusing runtime NilClass errors.
|
24
|
-
#
|
24
|
+
#
|
25
25
|
# To call one widget from another, inside the parent widget's +content+
|
26
26
|
# method, instantiate the child widget and call the +widget+ method. This
|
27
27
|
# assures that the same output stream is used, which gives better
|
28
28
|
# performance than using +capture+ or +to_html+. It also preserves the
|
29
29
|
# indentation and helpers of the enclosing class.
|
30
|
-
#
|
30
|
+
#
|
31
31
|
# In this documentation we've tried to keep the distinction clear between
|
32
32
|
# methods that *emit* text and those that *return* text. "Emit" means that
|
33
33
|
# it writes to the output stream; "return" means that it returns a string
|
34
34
|
# like a normal method and leaves it up to the caller to emit that string if
|
35
35
|
# it wants.
|
36
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
|
+
#
|
37
42
|
# Now, seriously, after playing around a bit, go read the user guide. It's
|
38
|
-
# fun!
|
39
|
-
class AbstractWidget
|
40
|
-
@@prettyprint_default = false
|
41
|
-
def prettyprint_default
|
42
|
-
@@prettyprint_default
|
43
|
-
end
|
44
|
-
|
45
|
-
def self.prettyprint_default
|
46
|
-
@@prettyprint_default
|
47
|
-
end
|
48
|
-
|
49
|
-
def self.prettyprint_default=(enabled)
|
50
|
-
@@prettyprint_default = enabled
|
51
|
-
end
|
52
|
-
|
53
|
-
def self.inline(*args, &block)
|
54
|
-
Class.new(self) do
|
55
|
-
include Erector::Inline
|
56
|
-
end.new(*args, &block)
|
57
|
-
end
|
58
|
-
|
59
|
-
[:helpers, :assigns, :output, :parent, :block].each do |attr|
|
60
|
-
class_eval(<<-SRC, __FILE__, __LINE__ + 1)
|
61
|
-
def #{attr}
|
62
|
-
@_#{attr}
|
63
|
-
end
|
64
|
-
SRC
|
65
|
-
end
|
66
|
-
|
67
|
-
def initialize(assigns = {}, &block)
|
68
|
-
unless assigns.is_a? Hash
|
69
|
-
raise "Erector widgets are initialized with only a parameter hash. (Other parameters are passed to to_html, or the #widget method.)"
|
70
|
-
end
|
71
|
-
|
72
|
-
@_assigns = assigns
|
73
|
-
|
74
|
-
assigns.each do |name, value|
|
75
|
-
instance_variable_set(name.to_s[0..0] == '@' ? name : "@#{name}", value)
|
76
|
-
end
|
77
|
-
|
78
|
-
@_parent = eval("self", block.binding) if block
|
79
|
-
@_block = block
|
80
|
-
end
|
81
|
-
|
82
|
-
# Entry point for rendering a widget (and all its children). This method
|
83
|
-
# creates a new output string (if necessary), calls this widget's #content
|
84
|
-
# method and returns the string.
|
85
|
-
#
|
86
|
-
# Options:
|
87
|
-
# output:: the string to output to. Default: a new empty string
|
88
|
-
# prettyprint:: whether Erector should add newlines and indentation.
|
89
|
-
# Default: the value of prettyprint_default (which is false
|
90
|
-
# by default).
|
91
|
-
# indentation:: the amount of spaces to indent. Ignored unless prettyprint
|
92
|
-
# is true.
|
93
|
-
# max_length:: preferred maximum length of a line. Line wraps will only
|
94
|
-
# occur at space characters, so a long word may end up creating
|
95
|
-
# a line longer than this. If nil (default), then there is no
|
96
|
-
# arbitrary limit to line lengths, and only internal newline
|
97
|
-
# characters and prettyprinting will determine newlines in the
|
98
|
-
# output.
|
99
|
-
# helpers:: a helpers object containing utility methods. Usually this is a
|
100
|
-
# Rails view object.
|
101
|
-
# content_method_name:: in case you want to call a method other than
|
102
|
-
# #content, pass its name in here.
|
103
|
-
def to_html(options = {})
|
104
|
-
raise "Erector::Widget#to_html takes an options hash, not a symbol. Try calling \"to_html(:content_method_name=> :#{options})\"" if options.is_a? Symbol
|
105
|
-
_render(options).to_s
|
106
|
-
end
|
107
|
-
|
108
|
-
# alias for #to_html
|
109
|
-
# @deprecated Please use {#to_html} instead
|
110
|
-
def to_s(*args)
|
111
|
-
unless defined? @@already_warned_to_s
|
112
|
-
$stderr.puts "Erector::Widget#to_s is deprecated. Please use #to_html instead. Called from #{caller.first}"
|
113
|
-
@@already_warned_to_s = true
|
114
|
-
end
|
115
|
-
to_html(*args)
|
116
|
-
end
|
117
|
-
|
118
|
-
# Entry point for rendering a widget (and all its children). Same as #to_html
|
119
|
-
# only it returns an array, for theoretical performance improvements when using a
|
120
|
-
# Rack server (like Sinatra or Rails Metal).
|
121
|
-
#
|
122
|
-
# # Options: see #to_html
|
123
|
-
def to_a(options = {})
|
124
|
-
_render(options).to_a
|
125
|
-
end
|
126
|
-
|
127
|
-
# Template method which must be overridden by all widget subclasses.
|
128
|
-
# Inside this method you call the magic #element methods which emit HTML
|
129
|
-
# and text to the output string. If you call "super" (or don't override
|
130
|
-
# +content+, or explicitly call "call_block") then your widget will
|
131
|
-
# execute the block that was passed into its constructor. The semantics of
|
132
|
-
# this block are confusing; make sure to read the rdoc for Erector#call_block
|
133
|
-
def content
|
134
|
-
call_block
|
135
|
-
end
|
136
|
-
|
137
|
-
# When this method is executed, the default block that was passed in to
|
138
|
-
# the widget's constructor will be executed. The semantics of this
|
139
|
-
# block -- that is, what "self" is, and whether it has access to
|
140
|
-
# Erector methods like "div" and "text", and the widget's instance
|
141
|
-
# variables -- can be quite confusing. The rule is, most of the time the
|
142
|
-
# block is evaluated using "call" or "yield", which means that its scope
|
143
|
-
# is that of the caller. So if that caller is not an Erector widget, it
|
144
|
-
# will *not* have access to the Erector methods, but it *will* have access
|
145
|
-
# to instance variables and methods of the calling object.
|
146
|
-
#
|
147
|
-
# If you want this block to have access to Erector methods then use
|
148
|
-
# Erector::Inline#content or Erector#inline.
|
149
|
-
def call_block
|
150
|
-
@_block.call(self) if @_block
|
151
|
-
end
|
152
|
-
|
153
|
-
# Emits a (nested) widget onto the current widget's output stream. Accepts
|
154
|
-
# either a class or an instance. If the first argument is a class, then
|
155
|
-
# the second argument is a hash used to populate its instance variables.
|
156
|
-
# If the first argument is an instance then the hash must be unspecified
|
157
|
-
# (or empty). If a block is passed to this method, then it gets set as the
|
158
|
-
# rendered widget's block.
|
159
|
-
#
|
160
|
-
# This is the preferred way to call one widget from inside another. This
|
161
|
-
# method assures that the same output string is used, which gives better
|
162
|
-
# performance than using +capture+ or +to_html+.
|
163
|
-
def widget(target, assigns = {}, options = {}, &block)
|
164
|
-
if target.is_a? Class
|
165
|
-
target.new(assigns, &block)._render_via(self, options)
|
166
|
-
else
|
167
|
-
unless assigns.empty?
|
168
|
-
raise "Unexpected second parameter. Did you mean to pass in assigns when you instantiated the #{target.class.to_s}?"
|
169
|
-
end
|
170
|
-
target._render_via(self, options, &block)
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
# Creates a whole new output string, executes the block, then converts the
|
175
|
-
# output string to a string and returns it as raw text. If at all possible
|
176
|
-
# you should avoid this method since it hurts performance, and use +widget+
|
177
|
-
# instead.
|
178
|
-
def capture
|
179
|
-
original, @_output = output, Output.new
|
180
|
-
yield
|
181
|
-
original.widgets.concat(output.widgets) # todo: test!!!
|
182
|
-
output.to_s
|
183
|
-
ensure
|
184
|
-
@_output = original
|
185
|
-
end
|
186
|
-
|
187
|
-
protected
|
188
|
-
def _render(options = {}, &block)
|
189
|
-
@_block = block if block
|
190
|
-
@_parent = options[:parent] || parent
|
191
|
-
@_helpers = options[:helpers] || parent
|
192
|
-
@_output = options[:output]
|
193
|
-
@_output = Output.new(options) unless output.is_a?(Output)
|
194
|
-
|
195
|
-
output.widgets << self.class
|
196
|
-
send(options[:content_method_name] || :content)
|
197
|
-
output
|
198
|
-
end
|
199
|
-
|
200
|
-
def _render_via(parent, options = {}, &block)
|
201
|
-
_render(options.merge(:parent => parent,
|
202
|
-
:output => parent.output,
|
203
|
-
:helpers => parent.helpers), &block)
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
43
|
+
# fun!
|
207
44
|
class Widget < AbstractWidget
|
208
45
|
include Erector::HTML
|
209
46
|
include Erector::Needs
|
@@ -28,19 +28,40 @@ module DependencySpec
|
|
28
28
|
x.text.should == "sample file contents, 2 + 2 = 4\n"
|
29
29
|
end
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
31
|
+
describe "comparison methods" do
|
32
|
+
before do
|
33
|
+
@castor = Erector::Dependency.new(:foo, "abc", {:bar => 7})
|
34
|
+
@pollux = Erector::Dependency.new(:foo, "abc", {:bar => 7})
|
35
|
+
@leo = Erector::Dependency.new(:foo, "abc")
|
36
|
+
@pisces = Erector::Dependency.new(:foo, "xyz", {:bar => 7})
|
37
|
+
end
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
it "is equal to an identical external" do
|
40
|
+
@castor.should == @pollux
|
41
|
+
[@castor].should include(@pollux)
|
42
|
+
@castor.eql?(@pollux).should be_true
|
43
|
+
@castor.hash.should == @pollux.hash
|
44
|
+
end
|
44
45
|
|
46
|
+
it "is not equal to an otherwise identical external with different options" do
|
47
|
+
@castor.should_not == @leo
|
48
|
+
[@castor].should_not include(@leo)
|
49
|
+
@castor.eql?(@leo).should_not be_true
|
50
|
+
@castor.hash.should_not == @leo.hash
|
51
|
+
end
|
52
|
+
|
53
|
+
it "is not equal to a different external with the same options" do
|
54
|
+
@castor.should_not == @pisces
|
55
|
+
[@castor].should_not include(@pisces)
|
56
|
+
@castor.eql?(@pisces).should_not be_true
|
57
|
+
@castor.hash.should_not == @pisces.hash
|
58
|
+
end
|
59
|
+
|
60
|
+
# see http://blog.nathanielbibler.com/post/73525836/using-the-ruby-array-uniq-with-custom-classes
|
61
|
+
it "works with uniq" do
|
62
|
+
[@castor, @pollux, @leo].uniq.should == [@castor, @leo]
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
45
66
|
end
|
46
67
|
end
|
@@ -16,6 +16,7 @@ module ExternalsSpec
|
|
16
16
|
end
|
17
17
|
|
18
18
|
it "calls #interpret_args with given arguments and passes result to #push_dependency" do
|
19
|
+
pending "RR problem with Ruby 1.9" if RUBY_VERSION >= "1.9.0"
|
19
20
|
mock(Erector::Widget).interpret_args(*@args).returns(@result)
|
20
21
|
Erector::Widget.depends_on *@args
|
21
22
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
here = File.expand_path(File.dirname(__FILE__))
|
2
|
+
$: << "#{here}/../../lib"
|
3
|
+
|
4
|
+
require "erector"
|
5
|
+
|
6
|
+
class Hello < Erector::Widget
|
7
|
+
def content
|
8
|
+
html do
|
9
|
+
head do
|
10
|
+
title "Welcome page"
|
11
|
+
end
|
12
|
+
body do
|
13
|
+
p "Hello, world"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
puts Hello.new.to_html
|
@@ -0,0 +1,12 @@
|
|
1
|
+
here = File.expand_path(File.dirname(__FILE__))
|
2
|
+
require File.expand_path("#{here}/../spec_helper")
|
3
|
+
|
4
|
+
describe "Hello World example from README" do
|
5
|
+
it "works" do
|
6
|
+
Dir.chdir(here) do
|
7
|
+
clear_bundler_env
|
8
|
+
html = sys "ruby hello_from_readme.rb"
|
9
|
+
html.should == "<html><head><title>Welcome page</title></head><body><p>Hello, world</p></body></html>\n"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/spec/erector/html_spec.rb
CHANGED
@@ -506,4 +506,45 @@ describe Erector::HTML do
|
|
506
506
|
erector { close_tag :foo; close_tag :bar }.should == "</foo></bar>"
|
507
507
|
end
|
508
508
|
end
|
509
|
+
|
510
|
+
describe "exception handling" do
|
511
|
+
class RenderWithReturn < Erector::Widget
|
512
|
+
def content
|
513
|
+
h2 do
|
514
|
+
return "returned_value"
|
515
|
+
text "don't get here"
|
516
|
+
end
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
it "closes tags when a block returns" do
|
521
|
+
RenderWithReturn.new.to_html.should == "<h2></h2>"
|
522
|
+
end
|
523
|
+
|
524
|
+
it "closes tags when a block throws and the exception is caught" do
|
525
|
+
erector do
|
526
|
+
begin
|
527
|
+
div do
|
528
|
+
raise "no way"
|
529
|
+
text "not reached"
|
530
|
+
end
|
531
|
+
rescue
|
532
|
+
end
|
533
|
+
end.should == "<div></div>"
|
534
|
+
end
|
535
|
+
|
536
|
+
it "closes tags when throwing block versus text exception" do
|
537
|
+
erector do
|
538
|
+
begin
|
539
|
+
span "a value" do
|
540
|
+
text "a block"
|
541
|
+
end
|
542
|
+
rescue ArgumentError => e
|
543
|
+
e.to_s.should include(
|
544
|
+
"You can't pass both a block and a value")
|
545
|
+
end
|
546
|
+
end.should == "<span></span>"
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
509
550
|
end
|
data/spec/erector/output_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -7,17 +7,17 @@ RAILS_LOAD_PATHS.each do |path|
|
|
7
7
|
end
|
8
8
|
|
9
9
|
require "rubygems"
|
10
|
+
require "bundler"
|
11
|
+
Bundler.setup
|
12
|
+
|
10
13
|
require "erector"
|
11
14
|
require "nokogiri"
|
12
15
|
require "rr"
|
13
16
|
require 'tempfile'
|
14
17
|
require 'ostruct'
|
15
|
-
require "
|
16
|
-
require "
|
17
|
-
|
18
|
-
Spec::Runner.configure do |config|
|
19
|
-
config.mock_with :rr
|
20
|
-
end
|
18
|
+
require "rspec"
|
19
|
+
require "rspec/autorun"
|
20
|
+
require "open3"
|
21
21
|
|
22
22
|
unless '1.9'.respond_to?(:force_encoding)
|
23
23
|
String.class_eval do
|
@@ -68,8 +68,9 @@ module Matchers
|
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
-
|
71
|
+
RSpec.configure do |config|
|
72
72
|
include Matchers
|
73
|
+
config.mock_with :rr
|
73
74
|
end
|
74
75
|
|
75
76
|
def capturing_output
|
@@ -90,3 +91,30 @@ ensure
|
|
90
91
|
$stderr = STDERR
|
91
92
|
end
|
92
93
|
|
94
|
+
def sys(cmd, expected_status = 0)
|
95
|
+
start_time = Time.now
|
96
|
+
$stderr.print cmd
|
97
|
+
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thread|
|
98
|
+
# in Ruby 1.8, wait_thread is nil :-( so just pretend the process was successful (status 0)
|
99
|
+
exit_status = (wait_thread.value.exitstatus if wait_thread) || 0
|
100
|
+
output = stdout.read # + stderr.read #todo: make stderr optional
|
101
|
+
unless expected_status.nil?
|
102
|
+
unless exit_status == expected_status
|
103
|
+
$stderr.puts " => #{exit_status}"
|
104
|
+
puts output
|
105
|
+
exit_status.should == expected_status
|
106
|
+
end
|
107
|
+
# assert { output and exit_status == expected_status }
|
108
|
+
end
|
109
|
+
yield output if block_given?
|
110
|
+
output
|
111
|
+
end
|
112
|
+
ensure
|
113
|
+
$stderr.puts " (#{"%.2f" % (Time.now - start_time)} sec)"
|
114
|
+
end
|
115
|
+
|
116
|
+
def clear_bundler_env
|
117
|
+
# Bundler inherits its environment by default, so clear it here
|
118
|
+
%w{BUNDLE_PATH BUNDLE_BIN_PATH BUNDLE_GEMFILE}.each { |var| ENV.delete(var) }
|
119
|
+
end
|
120
|
+
|
metadata
CHANGED
@@ -1,49 +1,58 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: erector
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 8
|
9
|
-
- 1
|
10
|
-
version: 0.8.1
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.8.2
|
5
|
+
prerelease:
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
13
|
-
-
|
7
|
+
authors:
|
8
|
+
- Alex Chaffee
|
9
|
+
- Brian Takita
|
10
|
+
- Jeff Dean
|
11
|
+
- Jim Kingdon
|
14
12
|
autorequire:
|
15
13
|
bindir: bin
|
16
14
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
15
|
+
date: 2011-02-16 00:00:00.000000000 -08:00
|
16
|
+
default_executable: erector
|
17
|
+
dependencies:
|
18
|
+
- !ruby/object:Gem::Dependency
|
19
|
+
name: treetop
|
20
|
+
requirement: &2158316440 !ruby/object:Gem::Requirement
|
21
|
+
none: false
|
22
|
+
requirements:
|
23
|
+
- - ! '>='
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: 1.2.3
|
26
|
+
type: :runtime
|
23
27
|
prerelease: false
|
24
|
-
|
28
|
+
version_requirements: *2158316440
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: rake
|
31
|
+
requirement: &2158315880 !ruby/object:Gem::Requirement
|
25
32
|
none: false
|
26
|
-
requirements:
|
27
|
-
- -
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
|
30
|
-
segments:
|
31
|
-
- 1
|
32
|
-
- 5
|
33
|
-
- 0
|
34
|
-
version: 1.5.0
|
33
|
+
requirements:
|
34
|
+
- - ! '>='
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
35
37
|
type: :runtime
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: *2158315880
|
40
|
+
description: Erector is a Builder-like view framework, inspired by Markaby but overcoming
|
41
|
+
some of its flaws. In Erector all views are objects, not template files, which allows
|
42
|
+
the full power of object-oriented programming (inheritance, modular decomposition,
|
43
|
+
encapsulation) in views.
|
44
|
+
email: erector@googlegroups.com
|
45
|
+
executables:
|
40
46
|
- erector
|
41
47
|
extensions: []
|
42
|
-
|
43
|
-
extra_rdoc_files:
|
48
|
+
extra_rdoc_files:
|
44
49
|
- README.txt
|
45
|
-
|
46
|
-
|
50
|
+
files:
|
51
|
+
- README.txt
|
52
|
+
- VERSION.yml
|
53
|
+
- bin/erector
|
54
|
+
- lib/erector.rb
|
55
|
+
- lib/erector/abstract_widget.rb
|
47
56
|
- lib/erector/after_initialize.rb
|
48
57
|
- lib/erector/caching.rb
|
49
58
|
- lib/erector/convenience.rb
|
@@ -63,6 +72,7 @@ files:
|
|
63
72
|
- lib/erector/mixin.rb
|
64
73
|
- lib/erector/needs.rb
|
65
74
|
- lib/erector/output.rb
|
75
|
+
- lib/erector/rails.rb
|
66
76
|
- lib/erector/rails/extensions/action_controller.rb
|
67
77
|
- lib/erector/rails/extensions/rails_helpers.rb
|
68
78
|
- lib/erector/rails/extensions/rails_widget.rb
|
@@ -70,25 +80,20 @@ files:
|
|
70
80
|
- lib/erector/rails/rails_version.rb
|
71
81
|
- lib/erector/rails/template_handlers/ert_handler.rb
|
72
82
|
- lib/erector/rails/template_handlers/rb_handler.rb
|
73
|
-
- lib/erector/rails.rb
|
74
83
|
- lib/erector/raw_string.rb
|
75
84
|
- lib/erector/sass.rb
|
76
85
|
- lib/erector/unicode.rb
|
77
86
|
- lib/erector/unicode_builder.rb
|
78
87
|
- lib/erector/version.rb
|
79
88
|
- lib/erector/widget.rb
|
89
|
+
- lib/erector/widgets.rb
|
80
90
|
- lib/erector/widgets/environment_badge.rb
|
81
91
|
- lib/erector/widgets/external_renderer.rb
|
82
92
|
- lib/erector/widgets/field_table.rb
|
83
93
|
- lib/erector/widgets/form.rb
|
84
94
|
- lib/erector/widgets/page.rb
|
85
95
|
- lib/erector/widgets/table.rb
|
86
|
-
- lib/erector/widgets.rb
|
87
|
-
- lib/erector.rb
|
88
96
|
- rails/init.rb
|
89
|
-
- README.txt
|
90
|
-
- VERSION.yml
|
91
|
-
- bin/erector
|
92
97
|
- spec/erect/erect_rails_spec.rb
|
93
98
|
- spec/erect/erect_spec.rb
|
94
99
|
- spec/erect/erected_spec.rb
|
@@ -97,6 +102,8 @@ files:
|
|
97
102
|
- spec/erector/convenience_spec.rb
|
98
103
|
- spec/erector/dependency_spec.rb
|
99
104
|
- spec/erector/externals_spec.rb
|
105
|
+
- spec/erector/hello_from_readme.rb
|
106
|
+
- spec/erector/hello_from_readme_spec.rb
|
100
107
|
- spec/erector/html_spec.rb
|
101
108
|
- spec/erector/indentation_spec.rb
|
102
109
|
- spec/erector/inline_spec.rb
|
@@ -114,39 +121,58 @@ files:
|
|
114
121
|
- spec/erector/widgets/table_spec.rb
|
115
122
|
- spec/spec_helper.rb
|
116
123
|
has_rdoc: true
|
117
|
-
homepage: http://erector.rubyforge.org
|
124
|
+
homepage: http://erector.rubyforge.org/
|
118
125
|
licenses: []
|
119
|
-
|
120
126
|
post_install_message:
|
121
|
-
rdoc_options:
|
122
|
-
- --
|
123
|
-
|
124
|
-
require_paths:
|
127
|
+
rdoc_options:
|
128
|
+
- --charset=UTF-8
|
129
|
+
require_paths:
|
125
130
|
- lib
|
126
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
131
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
127
132
|
none: false
|
128
|
-
requirements:
|
129
|
-
- -
|
130
|
-
- !ruby/object:Gem::Version
|
131
|
-
|
132
|
-
segments:
|
133
|
+
requirements:
|
134
|
+
- - ! '>='
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
segments:
|
133
138
|
- 0
|
134
|
-
|
135
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
139
|
+
hash: 3942824524895088224
|
140
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
136
141
|
none: false
|
137
|
-
requirements:
|
138
|
-
- -
|
139
|
-
- !ruby/object:Gem::Version
|
140
|
-
|
141
|
-
segments:
|
142
|
-
- 0
|
143
|
-
version: "0"
|
142
|
+
requirements:
|
143
|
+
- - ! '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
144
146
|
requirements: []
|
145
|
-
|
146
147
|
rubyforge_project: erector
|
147
|
-
rubygems_version: 1.
|
148
|
+
rubygems_version: 1.5.2
|
148
149
|
signing_key:
|
149
150
|
specification_version: 3
|
150
151
|
summary: Html Builder library.
|
151
|
-
test_files:
|
152
|
-
|
152
|
+
test_files:
|
153
|
+
- spec/erect/erect_rails_spec.rb
|
154
|
+
- spec/erect/erect_spec.rb
|
155
|
+
- spec/erect/erected_spec.rb
|
156
|
+
- spec/erect/rhtml_parser_spec.rb
|
157
|
+
- spec/erector/caching_spec.rb
|
158
|
+
- spec/erector/convenience_spec.rb
|
159
|
+
- spec/erector/dependency_spec.rb
|
160
|
+
- spec/erector/externals_spec.rb
|
161
|
+
- spec/erector/hello_from_readme.rb
|
162
|
+
- spec/erector/hello_from_readme_spec.rb
|
163
|
+
- spec/erector/html_spec.rb
|
164
|
+
- spec/erector/indentation_spec.rb
|
165
|
+
- spec/erector/inline_spec.rb
|
166
|
+
- spec/erector/jquery_spec.rb
|
167
|
+
- spec/erector/mixin_spec.rb
|
168
|
+
- spec/erector/needs_spec.rb
|
169
|
+
- spec/erector/output_spec.rb
|
170
|
+
- spec/erector/sample-file.txt
|
171
|
+
- spec/erector/sass_spec.rb
|
172
|
+
- spec/erector/unicode_builder_spec.rb
|
173
|
+
- spec/erector/widget_spec.rb
|
174
|
+
- spec/erector/widgets/field_table_spec.rb
|
175
|
+
- spec/erector/widgets/form_spec.rb
|
176
|
+
- spec/erector/widgets/page_spec.rb
|
177
|
+
- spec/erector/widgets/table_spec.rb
|
178
|
+
- spec/spec_helper.rb
|