erector 0.8.1 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|