erector 0.1.25 → 0.2.42
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +90 -18
- data/lib/erector.rb +1 -0
- data/lib/erector/extensions/action_controller.rb +9 -0
- data/lib/erector/extensions/action_view_template_handler.rb +26 -0
- data/lib/erector/widget.rb +12 -5
- data/lib/erector/widgets/table.rb +25 -14
- data/spec/erect/erect_spec.rb +7 -0
- data/spec/erector/extensions/render_widget_spec.rb +33 -14
- data/spec/erector/widgets/table_spec.rb +2 -0
- metadata +17 -14
data/README.txt
CHANGED
@@ -6,20 +6,33 @@
|
|
6
6
|
|
7
7
|
== DESCRIPTION
|
8
8
|
|
9
|
-
Erector is a Builder-
|
10
|
-
|
9
|
+
Erector is a Builder-like view framework, inspired by Markaby but overcoming
|
10
|
+
some of its flaws. In Erector all views are objects, not template files,
|
11
|
+
which allows the full power of object-oriented programming (inheritance,
|
12
|
+
modular decomposition, encapsulation) in views.
|
11
13
|
|
12
14
|
== FEATURES/PROBLEMS:
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
+
While Erector is in use on several projects, it is still at a relatively
|
17
|
+
early stage. In particular, not all features are documented (although
|
18
|
+
the most important ones are).
|
16
19
|
|
17
20
|
== SYNOPSIS
|
18
21
|
|
19
|
-
|
22
|
+
require 'erector'
|
23
|
+
|
24
|
+
class Hello < Erector::Widget
|
25
|
+
def render
|
26
|
+
div do
|
27
|
+
text "Hello!"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
20
31
|
|
21
32
|
== REQUIREMENTS
|
22
33
|
|
34
|
+
The gem depends on hoe and rake, although this is just for building
|
35
|
+
erector (those who just use erector won't need these).
|
23
36
|
|
24
37
|
== INSTALL
|
25
38
|
|
@@ -27,9 +40,14 @@ To install as a gem:
|
|
27
40
|
|
28
41
|
* sudo gem install erector
|
29
42
|
|
30
|
-
|
43
|
+
Then add "require 'erector'" to any files which need erector.
|
44
|
+
|
45
|
+
To install as a Rails plugin:
|
31
46
|
|
32
|
-
*
|
47
|
+
* Copy the erector source to vendor/plugins/erector in your Rails directory.
|
48
|
+
|
49
|
+
When installing this way, erector is automatically available to your Rails code
|
50
|
+
(no require directive is needed).
|
33
51
|
|
34
52
|
== LICENSE:
|
35
53
|
|
@@ -56,19 +74,29 @@ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
56
74
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
57
75
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
58
76
|
|
59
|
-
==
|
77
|
+
== MOTIVATION
|
60
78
|
|
61
|
-
|
79
|
+
Why use Erector? This section will soon become a real essay or blog post,
|
80
|
+
but briefly:
|
62
81
|
|
63
|
-
|
64
|
-
|
82
|
+
* Markaby-style DOM Domain language
|
83
|
+
* Your views are real classes, written in a real language, allowing
|
84
|
+
* Functional decomposition
|
85
|
+
* Inheritance
|
86
|
+
* Composition, not partials
|
87
|
+
* Well-defined semantics for variables, loops, blocks
|
88
|
+
* Dependency injection via constructor params
|
89
|
+
* As little magic as possible (e.g. no automagic copying of "assigns" variable from your controller)
|
90
|
+
* yield works again (Markaby broke it)
|
91
|
+
* Very testable
|
92
|
+
* form_for ERB code is craaaaazy (not to mention the quagmire of options vs. htmloptions)
|
93
|
+
* Output is streamed, improving performance over string copy
|
65
94
|
|
66
|
-
html = Erector::Widget.new do
|
67
|
-
p "Hello, world!"
|
68
|
-
end
|
69
|
-
html.to_s #=> <p>Hello, world!</p>
|
70
95
|
|
71
|
-
|
96
|
+
== USER DOCUMENTATION
|
97
|
+
|
98
|
+
The basic way to construct some HTML/XML with erector is to
|
99
|
+
subclass Erector::Widget and implement a render method:
|
72
100
|
|
73
101
|
class Hello < Erector::Widget
|
74
102
|
def render
|
@@ -108,6 +136,39 @@ Here are the basics:
|
|
108
136
|
|
109
137
|
TODO: document more obscure features like capture, Table, :class => ['one', 'two']
|
110
138
|
|
139
|
+
=== Using erector from Ruby on Rails
|
140
|
+
|
141
|
+
Your views are just ruby classes. Your controller instantiates the
|
142
|
+
relevant view and calls render. For example:
|
143
|
+
|
144
|
+
app/controllers/welcome_controller.rb:
|
145
|
+
|
146
|
+
class WelcomeController < ApplicationController
|
147
|
+
|
148
|
+
def index
|
149
|
+
render :template => 'welcome/show'
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
app/views/welcome/show.rb:
|
155
|
+
|
156
|
+
class Views::Welcome::Show < Erector::Widget
|
157
|
+
|
158
|
+
def render
|
159
|
+
html do
|
160
|
+
head do
|
161
|
+
title "Welcome page"
|
162
|
+
end
|
163
|
+
|
164
|
+
body do
|
165
|
+
p "Hello, world"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
111
172
|
=== Layout Inheritance
|
112
173
|
|
113
174
|
Erector replaces the typical Rails layout mechanism with a more natural construct, the use of inheritance. Want a common
|
@@ -140,6 +201,16 @@ Here the abstract layout widget is used in a concrete fashion by the template-ba
|
|
140
201
|
would be implemented by subclassing widgets, but the layout template sets it directly and then calls to_s on the layout widget.
|
141
202
|
This allows the same layout to be shared in a backward compatible way.
|
142
203
|
|
204
|
+
=== Other ways to call erector
|
205
|
+
|
206
|
+
Instead of subclassing Erector::Widget and implementing a render
|
207
|
+
method, you can pass a block to Erector::Widget.new. For example:
|
208
|
+
|
209
|
+
html = Erector::Widget.new do
|
210
|
+
p "Hello, world!"
|
211
|
+
end
|
212
|
+
html.to_s #=> <p>Hello, world!</p>
|
213
|
+
|
143
214
|
== DEVELOPER NOTES
|
144
215
|
|
145
216
|
* Check out project from rubyforge:
|
@@ -163,12 +234,13 @@ This allows the same layout to be shared in a backward compatible way.
|
|
163
234
|
|
164
235
|
* Versions are of the form major.minor.tiny
|
165
236
|
* Tiny revisions fix bugs or documentation
|
237
|
+
* Tiny revisions are roughly equal to the svn revision number when they were made
|
166
238
|
* Minor revisions add API calls, or change behavior
|
167
239
|
* Minor revisions may also remove API calls, but these must be clearly announced in History.txt, with instructions on how to migrate
|
168
240
|
* Major revisions are about marketing more than technical needs. We will stay in major version 0 until we're happy taking the "alpha" label off it. And if we ever do a major overhaul of the API, especially one that breaks backwards compatibility, we will probably want to increment the major version.
|
169
|
-
* We will not be shy about incrementing version numbers -- if we end up going to version 0.943.
|
241
|
+
* We will not be shy about incrementing version numbers -- if we end up going to version 0.943.67454 then so be it.
|
170
242
|
* Developers should attempt to add lines in History.txt to reflect their checkins. These should reflect feature-level changes, not just one line per checkin. The top section of History.txt is used as the Release Notes by the "rake publish" task and will appear on the RubyForge file page.
|
171
|
-
* Someone making a release must fill in the version number in History.txt as well as in Rakefile. Note that "rake
|
243
|
+
* Someone making a release must fill in the version number in History.txt as well as in Rakefile. Note that "rake release" requires a "VERSION=1.2.3" parameter to confirm you're releasing the version you intend.
|
172
244
|
* As soon as a release is made and published, the publisher should go into History.txt and make a new section. Since we won't yet know what the next version will be called, the new section will be noted by a single "==" at the top of the file.
|
173
245
|
|
174
246
|
|
data/lib/erector.rb
CHANGED
@@ -8,6 +8,15 @@ class ActionController::Base
|
|
8
8
|
@rendered_widget = widget_class.new(@template, assigns.merge(:params => params))
|
9
9
|
@rendered_widget.to_s
|
10
10
|
end
|
11
|
+
|
12
|
+
def render_with_erector_widget(*options, &block)
|
13
|
+
if options.first.is_a?(Hash) && widget = options.first.delete(:widget)
|
14
|
+
render_widget widget, *options, &block
|
15
|
+
else
|
16
|
+
render_without_erector_widget *options, &block
|
17
|
+
end
|
18
|
+
end
|
19
|
+
alias_method_chain :render, :erector_widget
|
11
20
|
|
12
21
|
attr_reader :rendered_widget
|
13
22
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ActionView
|
2
|
+
module TemplateHandlers
|
3
|
+
class Erector
|
4
|
+
def initialize(view)
|
5
|
+
@view = view
|
6
|
+
end
|
7
|
+
|
8
|
+
def render(template, local_assigns)
|
9
|
+
paths = @view.first_render.split('/')
|
10
|
+
dot_rb = /\.rb$/
|
11
|
+
widget_class = paths.inject(Views) do |current_module, node|
|
12
|
+
current_module.const_get(node.gsub(dot_rb, '').camelize)
|
13
|
+
end
|
14
|
+
|
15
|
+
rendered_widget = widget_class.new(@view, @view.assigns)
|
16
|
+
rendered_widget.to_s
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
ActionView::Base.instance_eval do
|
23
|
+
if respond_to?(:register_template_handler)
|
24
|
+
register_template_handler :rb, ActionView::TemplateHandlers::Erector
|
25
|
+
end
|
26
|
+
end
|
data/lib/erector/widget.rb
CHANGED
@@ -39,7 +39,6 @@ module Erector
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
@helpers = helpers
|
42
|
-
fake_erbout
|
43
42
|
@parent = block ? eval("self", block.binding) : nil
|
44
43
|
@doc = doc
|
45
44
|
@block = block
|
@@ -50,7 +49,7 @@ module Erector
|
|
50
49
|
instance_eval(&@block)
|
51
50
|
end
|
52
51
|
end
|
53
|
-
|
52
|
+
|
54
53
|
def render_to(doc)
|
55
54
|
@doc = doc
|
56
55
|
render
|
@@ -128,7 +127,7 @@ module Erector
|
|
128
127
|
# (maybe, but the syntax is specific to javascript; it isn't
|
129
128
|
# really a generic XML CDATA section. Specifically,
|
130
129
|
# ]]> within value is not treated as ending the
|
131
|
-
# CDATA section by Firefox2 when parsing text/html,
|
130
|
+
# CDATA section by Firefox2 when parsing text/html,
|
132
131
|
# although I guess we could refuse to generate ]]>
|
133
132
|
# there, for the benefit of XML/XHTML parsers).
|
134
133
|
rawtext "\n// <![CDATA[\n"
|
@@ -226,14 +225,22 @@ module Erector
|
|
226
225
|
super
|
227
226
|
end
|
228
227
|
end
|
229
|
-
|
230
|
-
def fake_erbout
|
228
|
+
|
229
|
+
def fake_erbout(&blk)
|
231
230
|
widget = self
|
232
231
|
@helpers.metaclass.class_eval do
|
232
|
+
raise "Cannot nest fake_erbout" if instance_methods.include?('concat_without_erector')
|
233
|
+
alias_method :concat_without_erector, :concat
|
233
234
|
define_method :concat do |some_text, binding|
|
234
235
|
widget.text widget.raw(some_text)
|
235
236
|
end
|
236
237
|
end
|
238
|
+
yield
|
239
|
+
ensure
|
240
|
+
@helpers.metaclass.class_eval do
|
241
|
+
alias_method :concat, :concat_without_erector
|
242
|
+
remove_method :concat_without_erector
|
243
|
+
end
|
237
244
|
end
|
238
245
|
end
|
239
246
|
end
|
@@ -20,32 +20,43 @@ module Erector
|
|
20
20
|
|
21
21
|
def render
|
22
22
|
table do
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
23
|
+
thead do
|
24
|
+
tr do
|
25
|
+
column_definitions.each do |column_def|
|
26
|
+
th do
|
27
|
+
if column_def.name.is_a?(Proc)
|
28
|
+
self.instance_exec(column_def.id, &column_def.name)
|
29
|
+
else
|
30
|
+
text column_def.name
|
31
|
+
end
|
30
32
|
end
|
31
33
|
end
|
32
34
|
end
|
33
35
|
end
|
34
36
|
tbody do
|
35
37
|
@row_objects.each_with_index do |object, index|
|
36
|
-
|
37
|
-
column_definitions.each do |column_def|
|
38
|
-
td do
|
39
|
-
self.instance_exec(object, &column_def.cell_proc)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
38
|
+
row object, index
|
43
39
|
end
|
44
40
|
end
|
45
41
|
end
|
46
42
|
end
|
47
43
|
|
48
44
|
protected
|
45
|
+
def row(object, index)
|
46
|
+
tr(:class => row_css_class(object, index)) do
|
47
|
+
column_definitions.each do |column_def|
|
48
|
+
td do
|
49
|
+
self.instance_exec(object, &column_def.cell_proc)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Im overridable
|
56
|
+
def row_css_class(object, index)
|
57
|
+
cycle(index)
|
58
|
+
end
|
59
|
+
|
49
60
|
def column_definitions
|
50
61
|
self.class.column_definitions
|
51
62
|
end
|
@@ -10,6 +10,10 @@ module BaseSpec
|
|
10
10
|
def index_with_explicit_assigns
|
11
11
|
render_widget Erector::TestWidget, :foobar => "foobar"
|
12
12
|
end
|
13
|
+
|
14
|
+
def index_with_render_colon_widget
|
15
|
+
render :widget => Erector::TestWidget, :foobar => "foobar"
|
16
|
+
end
|
13
17
|
end
|
14
18
|
|
15
19
|
class Erector::TestWidget < Erector::Widget
|
@@ -18,7 +22,7 @@ module BaseSpec
|
|
18
22
|
end
|
19
23
|
end
|
20
24
|
|
21
|
-
describe TestWidgetController, "
|
25
|
+
describe TestWidgetController, "rendering widgets" do
|
22
26
|
before do
|
23
27
|
@controller = BaseSpec::TestWidgetController.new
|
24
28
|
@request = ActionController::TestRequest.new
|
@@ -26,24 +30,39 @@ module BaseSpec
|
|
26
30
|
@controller.send(:initialize_template_class, @response)
|
27
31
|
@controller.send(:assign_shortcuts, @request, @response)
|
28
32
|
class << @controller
|
29
|
-
public :rendered_widget
|
33
|
+
public :rendered_widget, :render
|
30
34
|
end
|
31
35
|
end
|
32
36
|
|
33
|
-
|
34
|
-
@
|
35
|
-
|
36
|
-
|
37
|
-
|
37
|
+
describe "#render_widget" do
|
38
|
+
it "assigns to @rendered_widget" do
|
39
|
+
@controller.rendered_widget.should be_nil
|
40
|
+
@controller.render_widget Erector::TestWidget
|
41
|
+
@controller.rendered_widget.should be_instance_of(Erector::TestWidget)
|
42
|
+
end
|
38
43
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
44
|
+
it "instantiates a widget with implicit assigns" do
|
45
|
+
@controller.index_with_implicit_assigns
|
46
|
+
@response.body.should == "foobar"
|
47
|
+
end
|
43
48
|
|
44
|
-
|
45
|
-
|
46
|
-
|
49
|
+
it "instantiates a widget with explicit assigns" do
|
50
|
+
@controller.index_with_explicit_assigns
|
51
|
+
@response.body.should == "foobar"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#render :widget" do
|
56
|
+
it "assigns to @rendered_widget" do
|
57
|
+
@controller.rendered_widget.should be_nil
|
58
|
+
@controller.render :widget => Erector::TestWidget
|
59
|
+
@controller.rendered_widget.should be_instance_of(Erector::TestWidget)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "instantiates a widget with explicit assigns" do
|
63
|
+
@controller.index_with_render_colon_widget
|
64
|
+
@response.body.should == "foobar"
|
65
|
+
end
|
47
66
|
end
|
48
67
|
end
|
49
68
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper")
|
2
2
|
|
3
|
+
#TODO: This is very confusing with column_a etc. Rewrite to use LastName, FirstName, Email or something like that.
|
4
|
+
|
3
5
|
module TableSpec
|
4
6
|
class DefaultsTestTable < Erector::Widgets::Table
|
5
7
|
column :column_a
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: erector
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
platform:
|
4
|
+
version: 0.2.42
|
5
|
+
platform: ""
|
6
6
|
authors:
|
7
7
|
- Pivotal Labs
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-04-11 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
- !ruby/object:Gem::Version
|
22
22
|
version: 1.5.0
|
23
23
|
version:
|
24
|
-
description: Erector is a Builder-
|
24
|
+
description: Erector is a Builder-like view framework, inspired by Markaby but overcoming some of its flaws. In Erector all views are objects, not template files, which allows the full power of object-oriented programming (inheritance, modular decomposition, encapsulation) in views.
|
25
25
|
email:
|
26
26
|
- alex@pivotallabs.com
|
27
27
|
executables: []
|
@@ -31,26 +31,29 @@ extensions: []
|
|
31
31
|
extra_rdoc_files:
|
32
32
|
- README.txt
|
33
33
|
files:
|
34
|
+
- spec/erect
|
34
35
|
- spec/erector
|
36
|
+
- spec/spec_helper.rb
|
37
|
+
- spec/spec_suite.rb
|
38
|
+
- spec/view_caching.rb
|
39
|
+
- spec/erect/erect_spec.rb
|
35
40
|
- spec/erector/extensions
|
36
|
-
- spec/erector/extensions/render_widget_spec.rb
|
37
41
|
- spec/erector/widget_spec.rb
|
38
42
|
- spec/erector/widgets
|
43
|
+
- spec/erector/extensions/render_widget_spec.rb
|
39
44
|
- spec/erector/widgets/table_spec.rb
|
40
|
-
- spec/spec_helper.rb
|
41
|
-
- spec/spec_suite.rb
|
42
|
-
- spec/view_caching.rb
|
43
45
|
- lib/erector
|
46
|
+
- lib/erector.rb
|
44
47
|
- lib/erector/extensions
|
45
|
-
- lib/erector/extensions/action_controller.rb
|
46
|
-
- lib/erector/extensions/object.rb
|
47
48
|
- lib/erector/helpers.rb
|
48
49
|
- lib/erector/html_parts.rb
|
49
50
|
- lib/erector/widget.rb
|
50
51
|
- lib/erector/widgets
|
51
|
-
- lib/erector/widgets/table.rb
|
52
52
|
- lib/erector/widgets.rb
|
53
|
-
- lib/erector.rb
|
53
|
+
- lib/erector/extensions/action_controller.rb
|
54
|
+
- lib/erector/extensions/action_view_template_handler.rb
|
55
|
+
- lib/erector/extensions/object.rb
|
56
|
+
- lib/erector/widgets/table.rb
|
54
57
|
- README.txt
|
55
58
|
has_rdoc: true
|
56
59
|
homepage: http://erector.rubyforge.org
|
@@ -75,9 +78,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
75
78
|
requirements: []
|
76
79
|
|
77
80
|
rubyforge_project: erector
|
78
|
-
rubygems_version:
|
81
|
+
rubygems_version: 0.9.5
|
79
82
|
signing_key:
|
80
83
|
specification_version: 2
|
81
|
-
summary: Erector is a Builder-
|
84
|
+
summary: Erector is a Builder-like view framework, inspired by Markaby but overcoming some of its flaws
|
82
85
|
test_files: []
|
83
86
|
|