erector 0.1.0

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.
@@ -0,0 +1,93 @@
1
+ = Erector
2
+
3
+ * http://erector.rubyforge.org
4
+ * mailto:alex@pivotallabs.com
5
+
6
+
7
+ == DESCRIPTION
8
+
9
+ With Erector, you define views without templates, in natural Ruby code, with all the power of objects, functions, modular
10
+ decomposition, etc.
11
+
12
+ == FEATURES/PROBLEMS:
13
+
14
+ * FIX (list of features or problems)
15
+
16
+ == SYNOPSIS
17
+
18
+ TODO
19
+
20
+ == REQUIREMENTS
21
+
22
+ * treetop
23
+
24
+ == INSTALL
25
+
26
+ To install as a gem:
27
+
28
+ * sudo gem install hoe
29
+
30
+ To install as a plugin:
31
+
32
+ * ???
33
+
34
+ == LICENSE:
35
+
36
+ (The MIT License)
37
+
38
+ Copyright (c) 2007-8 Pivotal Labs
39
+
40
+ Permission is hereby granted, free of charge, to any person obtaining
41
+ a copy of this software and associated documentation files (the
42
+ "Software"), to deal in the Software without restriction, including
43
+ without limitation the rights to use, copy, modify, merge, publish,
44
+ distribute, sublicense, and/or sell copies of the Software, and to
45
+ permit persons to whom the Software is furnished to do so, subject to
46
+ the following conditions:
47
+
48
+ The above copyright notice and this permission notice shall be
49
+ included in all copies or substantial portions of the Software.
50
+
51
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
52
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
53
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
54
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
55
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
56
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
57
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
58
+
59
+ == DOCUMENTATION
60
+
61
+ TODO
62
+
63
+ === Layout Inheritance
64
+
65
+ Erector replaces the typical Rails layout mechanism with a more natural construct, the use of inheritance. Want a common
66
+ layout? Just implement a layout superclass and inherit from it. Implement render in the superclass and implement template
67
+ methods in its subclasses. There's one trick you'll need to use this layout for non-erector templates. Here's an example.
68
+
69
+ `application.rb` - The Erector layout superclass
70
+
71
+ class Views::Layouts::Application < Erector::Widget
72
+ attr_accessor :content
73
+
74
+ def render
75
+ html do
76
+ head { } # head content here
77
+ # body content here
78
+ body do
79
+ text content
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ `application.mab` - The markaby template (adjust for other appropriately templating technologies)
86
+
87
+ widget = Views::Layouts::Application.new(self)
88
+ widget.content = content_for_layout
89
+ self << widget.to_s
90
+
91
+ Here the abstract layout widget is used in a concrete fashion by the template-based layout. Normally, the `content` method
92
+ would be implemented by subclassing widgets, but the layout template sets it directly and then calls to_s on the layout widget.
93
+ This allows the same layout to be shared in a backward compatible way.
@@ -0,0 +1,7 @@
1
+ require "action_controller"
2
+ require "erector/extensions/action_controller"
3
+ require "erector/extensions/object"
4
+ require "erector/helpers"
5
+ require "erector/html_parts"
6
+ require "erector/widget"
7
+ require "erector/widgets"
@@ -0,0 +1,13 @@
1
+ class ActionController::Base
2
+ def render_widget(widget_class, assigns=@assigns)
3
+ render :text => render_widget_to_string(widget_class, assigns)
4
+ end
5
+
6
+ def render_widget_to_string(widget_class, assigns = @assigns)
7
+ add_variables_to_assigns
8
+ @rendered_widget = widget_class.new(@template, assigns.merge(:params => params))
9
+ @rendered_widget.to_s
10
+ end
11
+
12
+ attr_reader :rendered_widget
13
+ end
@@ -0,0 +1,5 @@
1
+ class Object
2
+ def metaclass
3
+ class << self; self; end
4
+ end
5
+ end
@@ -0,0 +1,51 @@
1
+ module Erector
2
+ module Helpers
3
+ [
4
+ :link_to,
5
+ :image_tag,
6
+ :javascript_include_tag,
7
+ :stylesheet_link_tag,
8
+ :link_to_function,
9
+ :link_to_remote,
10
+ :sortable_element,
11
+ :sortable_element_js,
12
+ :mail_to
13
+ ].each do |helper_name|
14
+ define_method helper_name do |*args|
15
+ text helpers.send(helper_name, *args)
16
+ end
17
+ end
18
+
19
+ def javascript_include_merged(key)
20
+ helpers.javascript_include_merged(key)
21
+ end
22
+
23
+ def stylesheet_link_merged(key)
24
+ helpers.stylesheet_link_merged(key)
25
+ end
26
+
27
+ def flash
28
+ helpers.controller.send(:flash)
29
+ end
30
+
31
+ def session
32
+ helpers.controller.session
33
+ end
34
+
35
+ def cycle(*args)
36
+ helpers.cycle(*args)
37
+ end
38
+
39
+ def simple_format(*args)
40
+ helpers.simple_format(*args)
41
+ end
42
+
43
+ def time_ago_in_words(*args)
44
+ helpers.time_ago_in_words(*args)
45
+ end
46
+
47
+ def pluralize(*args)
48
+ helpers.pluralize(*args)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,34 @@
1
+ module Erector
2
+ class HtmlParts < Array
3
+ def to_s
4
+ map do |part|
5
+ case part['type']
6
+ when 'open'
7
+ part['attributes'] ?
8
+ "<#{part['tagName']}#{format_attributes(part['attributes'])}>" :
9
+ "<#{part['tagName']}>"
10
+ when 'close'
11
+ "</#{part['tagName']}>"
12
+ when 'standalone'
13
+ part['attributes'] ?
14
+ "<#{part['tagName']}#{format_attributes(part['attributes'])} />" :
15
+ "<#{part['tagName']} />"
16
+ when 'text'
17
+ part['value'].to_s
18
+ when 'instruct'
19
+ "<?xml#{format_attributes(part['attributes'])}?>"
20
+ end
21
+ end.join
22
+ end
23
+
24
+ protected
25
+ def format_attributes(attributes)
26
+ return "" if !attributes || attributes.empty?
27
+ results = ['']
28
+ attributes.each do |key, value|
29
+ results << "#{key}=#{value.to_s.inspect}" if value
30
+ end
31
+ results.join ' '
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,180 @@
1
+ module Erector
2
+ class Widget
3
+ class << self
4
+ def all_tags
5
+ Erector::Widget.full_tags + Erector::Widget.standalone_tags
6
+ end
7
+
8
+ def standalone_tags
9
+ ['area', 'base', 'br', 'hr', 'img', 'input', 'link', 'meta']
10
+ end
11
+
12
+ def full_tags
13
+ [
14
+ 'a', 'acronym', 'address', 'b', 'bdo', 'big', 'blockquote', 'body',
15
+ 'button', 'caption', 'cite', 'code', 'dd', 'del', 'div', 'dl', 'dt', 'em',
16
+ 'fieldset', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'html', 'i',
17
+ 'iframe', 'ins', 'kbd', 'label', 'legend', 'li', 'map',
18
+ 'noframes', 'noscript', 'ol', 'optgroup', 'option', 'p', 'param', 'pre',
19
+ 'samp', 'script', 'select', 'small', 'span', 'strong', 'style', 'sub', 'sup',
20
+ 'table', 'tbody', 'td', 'textarea', 'th', 'thead', 'title', 'tr', 'tt', 'u', 'ul', 'var'
21
+ ]
22
+ end
23
+ end
24
+
25
+ include ActionController::UrlWriter, Helpers
26
+ attr_reader :helpers
27
+ attr_reader :assigns
28
+ attr_reader :doc
29
+ attr_reader :block
30
+ attr_reader :parent
31
+
32
+ # Each item in @doc is an array containing three values: type, value, attributes
33
+ def initialize(helpers=nil, assigns={}, doc = HtmlParts.new, &block)
34
+ @assigns = assigns
35
+ assigns.each do |name, value|
36
+ instance_variable_set("@#{name}", value)
37
+ metaclass.module_eval do
38
+ attr_reader name
39
+ end
40
+ end
41
+ @helpers = helpers
42
+ fake_erbout
43
+ @parent = block ? eval("self", block.binding) : nil
44
+ @doc = doc
45
+ @block = block
46
+ end
47
+
48
+ def render
49
+ if @block
50
+ instance_eval(&@block)
51
+ end
52
+ end
53
+
54
+ def widget(widget_class, assigns={}, &block)
55
+ child = widget_class.new(helpers, assigns, doc, &block)
56
+ child.render
57
+ end
58
+
59
+ def h(content)
60
+ text CGI.escapeHTML(content)
61
+ end
62
+
63
+ def open_tag(tag_name, attributes={})
64
+ @doc << {'type' => 'open', 'tagName' => tag_name, 'attributes' => attributes}
65
+ end
66
+
67
+ def text(value)
68
+ @doc << {'type' => 'text', 'value' => value}
69
+ nil
70
+ end
71
+
72
+ def close_tag(tag_name)
73
+ @doc << {'type' => 'close', 'tagName' => tag_name}
74
+ end
75
+
76
+ def instruct!(attributes={:version => "1.0", :encoding => "UTF-8"})
77
+ @doc << {'type' => 'instruct', 'attributes' => attributes}
78
+ end
79
+
80
+ def javascript(*args, &blk)
81
+ params = args[0] if args[0].is_a?(Hash)
82
+ params ||= args[1] if args[1].is_a?(Hash)
83
+ unless params
84
+ params = {}
85
+ args << params
86
+ end
87
+ params[:type] = "text/javascript"
88
+ script(*args, &blk)
89
+ end
90
+
91
+ def __element__(tag_name, *args, &block)
92
+ if args.length > 2
93
+ raise ArgumentError, "Cannot accept more than three arguments"
94
+ end
95
+ attributes, value = nil, nil
96
+ arg0 = args[0]
97
+ if arg0.is_a?(Hash)
98
+ attributes = arg0
99
+ else
100
+ value = arg0.to_s
101
+ arg1 = args[1]
102
+ if arg1.is_a?(Hash)
103
+ attributes = arg1
104
+ end
105
+ end
106
+ attributes ||= {}
107
+ open_tag tag_name, attributes
108
+ if block
109
+ instance_eval(&block)
110
+ else
111
+ text value
112
+ end
113
+ close_tag tag_name
114
+ end
115
+ alias_method :element, :__element__
116
+
117
+ def __standalone_element__(tag_name, attributes={})
118
+ @doc << {'type' => 'standalone', 'tagName' => tag_name, 'attributes' => attributes}
119
+ end
120
+ alias_method :standalone_element, :__standalone_element__
121
+
122
+ def capture(&block)
123
+ begin
124
+ original_doc = @doc
125
+ @doc = HtmlParts.new
126
+ yield
127
+ @doc.to_s
128
+ ensure
129
+ @doc = original_doc
130
+ end
131
+ end
132
+
133
+ def to_s(&blk)
134
+ return @__to_s if @__to_s
135
+ render(&blk)
136
+ @__to_s = @doc.to_s
137
+ end
138
+
139
+ alias_method :inspect, :to_s
140
+
141
+ full_tags.each do |tag_name|
142
+ self.class_eval(
143
+ "def #{tag_name}(*args, &block)\n" <<
144
+ " __element__('#{tag_name}', *args, &block)\n" <<
145
+ "end",
146
+ __FILE__,
147
+ __LINE__ - 4
148
+ )
149
+ end
150
+
151
+ standalone_tags.each do |tag_name|
152
+ self.class_eval(
153
+ "def #{tag_name}(*args, &block)\n" <<
154
+ " __standalone_element__('#{tag_name}', *args, &block)\n" <<
155
+ "end",
156
+ __FILE__,
157
+ __LINE__ - 4
158
+ )
159
+ end
160
+
161
+ protected
162
+ def method_missing(name, *args, &block)
163
+ block ||= lambda {} # captures self HERE
164
+ if @parent
165
+ @parent.send(name, *args, &block)
166
+ else
167
+ super
168
+ end
169
+ end
170
+
171
+ def fake_erbout
172
+ widget = self
173
+ @helpers.metaclass.class_eval do
174
+ define_method :concat do |some_text, binding|
175
+ widget.text some_text
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1 @@
1
+ require "erector/widgets/table"
@@ -0,0 +1,45 @@
1
+ module Erector
2
+ module Widgets
3
+ class Table < Erector::Widget
4
+ ColumnDefinition = Struct.new(:id, :name, :cell_proc)
5
+ class << self
6
+ def column(id, name=id.to_s.humanize.titleize, &cell_proc)
7
+ cell_proc ||= proc {|object| text object.__send__(id)}
8
+ column_definitions << ColumnDefinition.new(id, name, cell_proc)
9
+ end
10
+
11
+ def column_definitions
12
+ @column_definitions ||= []
13
+ end
14
+ end
15
+
16
+ def render
17
+ table do
18
+ tr do
19
+ column_definitions.each do |column_def|
20
+ th do
21
+ h column_def.name
22
+ end
23
+ end
24
+ end
25
+ tbody do
26
+ @row_objects.each do |object|
27
+ tr do
28
+ column_definitions.each do |column_def|
29
+ td do
30
+ self.instance_exec(object, &column_def.cell_proc)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ protected
40
+ def column_definitions
41
+ self.class.column_definitions
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,49 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper")
2
+
3
+ module BaseSpec
4
+ class TestWidgetController < ActionController::Base
5
+ def index_with_implicit_assigns
6
+ @foobar = "foobar"
7
+ render_widget Erector::TestWidget
8
+ end
9
+
10
+ def index_with_explicit_assigns
11
+ render_widget Erector::TestWidget, :foobar => "foobar"
12
+ end
13
+ end
14
+
15
+ class Erector::TestWidget < Erector::Widget
16
+ def render
17
+ text @foobar
18
+ end
19
+ end
20
+
21
+ describe TestWidgetController, "#render_widget" do
22
+ before do
23
+ @controller = BaseSpec::TestWidgetController.new
24
+ @request = ActionController::TestRequest.new
25
+ @response = ActionController::TestResponse.new
26
+ @controller.send(:initialize_template_class, @response)
27
+ @controller.send(:assign_shortcuts, @request, @response)
28
+ class << @controller
29
+ public :rendered_widget
30
+ end
31
+ end
32
+
33
+ it "assigns to @rendered_widget" do
34
+ @controller.rendered_widget.should be_nil
35
+ @controller.render_widget Erector::TestWidget
36
+ @controller.rendered_widget.should be_instance_of(Erector::TestWidget)
37
+ end
38
+
39
+ it "instantiates a widget with implicit assigns" do
40
+ @controller.index_with_implicit_assigns
41
+ @response.body.should == "foobar"
42
+ end
43
+
44
+ it "instantiates a widget with explicit assigns" do
45
+ @controller.index_with_explicit_assigns
46
+ @response.body.should == "foobar"
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,226 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
2
+
3
+ module WidgetSpec
4
+ describe Erector::Widget do
5
+ describe ".all_tags" do
6
+ it "returns set of full and standalone tags" do
7
+ Erector::Widget.all_tags.class.should == Array
8
+ Erector::Widget.all_tags.should == Erector::Widget.full_tags + Erector::Widget.standalone_tags
9
+ end
10
+ end
11
+
12
+ describe "#instruct!" do
13
+ it "when passed no arguments; returns an instruct element with version 1 and utf-8" do
14
+ html = Erector::Widget.new do
15
+ instruct!
16
+ end.to_s
17
+ html.should include("<?xml")
18
+ html.should include('encoding="UTF-8"')
19
+ html.should include('version="1.0')
20
+ html.should include("?>")
21
+ end
22
+ end
23
+
24
+ describe "#element" do
25
+ it "when receiving one argument; returns an empty element" do
26
+ Erector::Widget.new do
27
+ element('div')
28
+ end.to_s.should == "<div></div>"
29
+ end
30
+
31
+ it "with a attribute hash; returns an empty element with the attributes" do
32
+ html = Erector::Widget.new do
33
+ element(
34
+ 'div',
35
+ :class => "foo bar",
36
+ :style => "display: none; color: white; float: left;",
37
+ :nil_attribute => nil
38
+ )
39
+ end.to_s
40
+ doc = Hpricot(html)
41
+ div = doc.at('div')
42
+ div[:class].should == "foo bar"
43
+ div[:style].should == "display: none; color: white; float: left;"
44
+ div[:nil_attribute].should be_nil
45
+ end
46
+
47
+ it "with inner tags; returns nested tags" do
48
+ widget = Erector::Widget.new do
49
+ element 'div' do
50
+ element 'div'
51
+ end
52
+ end
53
+ widget.to_s.should == '<div><div></div></div>'
54
+ end
55
+
56
+ it "with text; returns element with inner text" do
57
+ Erector::Widget.new do
58
+ element 'div', 'test text'
59
+ end.to_s.should == "<div>test text</div>"
60
+ end
61
+
62
+ it "with object other than hash; returns element with inner text == object.to_s" do
63
+ object = ['a', 'b']
64
+ Erector::Widget.new do
65
+ element 'div', object
66
+ end.to_s.should == "<div>#{object.to_s}</div>"
67
+ end
68
+
69
+ it "with parameters and block; returns element with inner html and attributes" do
70
+ Erector::Widget.new do
71
+ element 'div', 'class' => "foobar" do
72
+ element 'span', 'style' => 'display: none;'
73
+ end
74
+ end.to_s.should == '<div class="foobar"><span style="display: none;"></span></div>'
75
+ end
76
+
77
+ it "with content and parameters; returns element with content as inner html and attributes" do
78
+ Erector::Widget.new do
79
+ element 'div', 'test text', :style => "display: none;"
80
+ end.to_s.should == '<div style="display: none;">test text</div>'
81
+ end
82
+
83
+ it "with more than three arguments; raises ArgumentError" do
84
+ proc do
85
+ Erector::Widget.new do
86
+ element 'div', 'foobar', {}, 'fourth'
87
+ end.to_s
88
+ end.should raise_error(ArgumentError)
89
+ end
90
+
91
+ it "renders the proper full tags" do
92
+ Erector::Widget.full_tags.each do |tag_name|
93
+ expected = "<#{tag_name}></#{tag_name}>"
94
+ actual = Erector::Widget.new do
95
+ send(tag_name)
96
+ end.to_s
97
+ begin
98
+ actual.should == expected
99
+ rescue Spec::Expectations::ExpectationNotMetError => e
100
+ puts "Expected #{tag_name} to be a full element. Expected #{expected}, got #{actual}"
101
+ raise e
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ describe "#standalone_element" do
108
+ it "when receiving attributes, renders a standalone_element with the attributes" do
109
+ Erector::Widget.new do
110
+ standalone_element 'input', :name => 'foo[bar]'
111
+ end.to_s.should == '<input name="foo[bar]" />'
112
+ end
113
+
114
+ it "when not receiving attributes, renders a standalone_element without attributes" do
115
+ Erector::Widget.new do
116
+ standalone_element 'br'
117
+ end.to_s.should == '<br />'
118
+ end
119
+
120
+ it "renders the proper standalone tags" do
121
+ ['area', 'base', 'br', 'hr', 'img', 'input', 'link', 'meta'].each do |tag_name|
122
+ expected = "<#{tag_name} />"
123
+ actual = Erector::Widget.new do
124
+ send(tag_name)
125
+ end.to_s
126
+ begin
127
+ actual.should == expected
128
+ rescue Spec::Expectations::ExpectationNotMetError => e
129
+ puts "Expected #{tag_name} to be a standalone element. Expected #{expected}, got #{actual}"
130
+ raise e
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ describe "#javascript" do
137
+ it "when receiving a block; renders the content inside of a script text/javascript element" do
138
+ body = Erector::Widget.new do
139
+ javascript do
140
+ text 'alert("hello");'
141
+ end
142
+ end.to_s
143
+ doc = Hpricot(body)
144
+ script_tag = doc.at("script")
145
+ script_tag[:type].should == "text/javascript"
146
+ script_tag.inner_html.should include('alert("hello");')
147
+ end
148
+
149
+ it "when receiving a params hash; renders a source file" do
150
+ html = Erector::Widget.new do
151
+ javascript(:src => "/my/js/file.js")
152
+ end.to_s
153
+ doc = Hpricot(html)
154
+ doc.at('/')[:src].should == "/my/js/file.js"
155
+ end
156
+
157
+ it "when receiving text and a params hash; renders a source file" do
158
+ html = Erector::Widget.new do
159
+ javascript('alert("hello");', :src => "/my/js/file.js")
160
+ end.to_s
161
+ doc = Hpricot(html)
162
+ script_tag = doc.at('script')
163
+ script_tag[:src].should == "/my/js/file.js"
164
+ script_tag.inner_html.should include('alert("hello");')
165
+ end
166
+ end
167
+
168
+ describe '#capture' do
169
+ it "should return content rather than write it to the buffer" do
170
+ widget = Erector::Widget.new do
171
+ captured = capture do
172
+ p 'Captured Content'
173
+ end
174
+ div do
175
+ text captured
176
+ end
177
+ end
178
+ widget.to_s.should == '<div><p>Captured Content</p></div>'
179
+ end
180
+
181
+ it "works with nested captures" do
182
+ widget = Erector::Widget.new do
183
+ captured = capture do
184
+ captured = capture do
185
+ p 'Nested Capture'
186
+ end
187
+ p 'Captured Content'
188
+ text captured
189
+ end
190
+ div do
191
+ text captured
192
+ end
193
+ end
194
+ widget.to_s.should == '<div><p>Captured Content</p><p>Nested Capture</p></div>'
195
+ end
196
+ end
197
+
198
+ describe '#widget' do
199
+ before do
200
+ class Parent < Erector::Widget
201
+ def render
202
+ text 1
203
+ widget Child do
204
+ text 2
205
+ third
206
+ end
207
+ end
208
+
209
+ def third
210
+ text 3
211
+ end
212
+ end
213
+
214
+ class Child < Erector::Widget
215
+ def render
216
+ super
217
+ end
218
+ end
219
+ end
220
+
221
+ it "renders nested widgets in the correct order" do
222
+ Parent.new.to_s.should == '123'
223
+ end
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,104 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper")
2
+
3
+ module TableSpec
4
+ class DefaultsTestTable < Erector::Widgets::Table
5
+ column :column_a
6
+ column :column_b
7
+ column :column_c
8
+ end
9
+
10
+ class CustomHeadingTable < Erector::Widgets::Table
11
+ column :a, "Column - A"
12
+ end
13
+
14
+ class CustomCellTable < Erector::Widgets::Table
15
+ column :a do |obj|
16
+ span obj.a
17
+ end
18
+ end
19
+
20
+ describe Erector::Widgets::Table do
21
+ before do
22
+ view_cache do
23
+ widget = CustomHeadingTable.new(
24
+ nil,
25
+ :row_objects => []
26
+ )
27
+ widget.to_s
28
+ end
29
+ end
30
+
31
+ it "renders a tbody to be compatible with IE6" do
32
+ doc.at("tbody").should_not be_nil
33
+ end
34
+ end
35
+
36
+ describe Erector::Widgets::Table, "with custom heading" do
37
+ before do
38
+ view_cache do
39
+ widget = CustomHeadingTable.new(
40
+ nil,
41
+ :row_objects => []
42
+ )
43
+ widget.to_s
44
+ end
45
+ end
46
+
47
+ it "renders a custom heading" do
48
+ table = doc.at("table")
49
+ table.at("th").inner_html.should == "Column - A"
50
+ end
51
+ end
52
+
53
+ describe Erector::Widgets::Table, "with custom cell content" do
54
+ before do
55
+ @object1 = Struct.new(:a).new("Hello")
56
+ view_cache do
57
+ widget = CustomCellTable.new(
58
+ nil,
59
+ :row_objects => [@object1]
60
+ )
61
+ widget.to_s
62
+ end
63
+ end
64
+
65
+ it "renders custom cell html" do
66
+ table = doc.at("table")
67
+ row = table.search("tr")[1]
68
+ row.at("td").inner_html.should == "<span>Hello</span>"
69
+ end
70
+ end
71
+
72
+ describe Erector::Widgets::Table, "with default heading and cell definitions" do
73
+ before do
74
+ @object1 = Struct.new(:column_a, :column_b, :column_c).new(1, 2, 3)
75
+ @object2 = Struct.new(:column_a, :column_b, :column_c).new(4, 5, 6)
76
+ view_cache do
77
+ widget = DefaultsTestTable.new(
78
+ nil,
79
+ :row_objects => [@object1, @object2]
80
+ )
81
+ widget.to_s
82
+ end
83
+ @table = doc.at("table")
84
+ end
85
+
86
+ it "renders column titles" do
87
+ title_row = @table.at("tr")
88
+ titles = title_row.search("th").collect {|heading| heading.inner_html}
89
+ titles.should == [ "Column A", "Column B", "Column C" ]
90
+ end
91
+
92
+ it "renders data" do
93
+ data_rows = @table.search("tr")[1..-1]
94
+ cell_values = data_rows.collect do |row|
95
+ row.search("td").collect {|col| col.inner_html}
96
+ end
97
+
98
+ cell_values.should == [
99
+ ['1', '2', '3'],
100
+ ['4', '5', '6']
101
+ ]
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,13 @@
1
+ dir = File.dirname(__FILE__)
2
+ require "rubygems"
3
+ require "active_record"
4
+ require "spec"
5
+ require "#{dir}/view_caching"
6
+ $LOAD_PATH.unshift("#{dir}/../lib")
7
+ require "erector"
8
+ require "hpricot"
9
+ require "action_controller/test_process"
10
+
11
+ Spec::Runner.configure do |config|
12
+ config.include ViewCaching
13
+ end
@@ -0,0 +1,4 @@
1
+ dir = File.dirname(__FILE__)
2
+ Dir["#{dir}/**/*_spec.rb"].each do |file|
3
+ require file
4
+ end
@@ -0,0 +1,30 @@
1
+ module ViewCaching
2
+ def self.included(mod)
3
+ mod.extend ClassMethods
4
+ end
5
+ module ClassMethods
6
+ def view_cache
7
+ @view_cache ||= {}
8
+ end
9
+ end
10
+
11
+ def view_cache(&blk)
12
+ cache = self.class.view_cache
13
+ if cache.empty?
14
+ cache[:body] = @body = yield
15
+ cache[:doc] = @doc = Hpricot(@body)
16
+ else
17
+ @body = cache[:body]
18
+ @doc = cache[:doc]
19
+ end
20
+ end
21
+
22
+ def doc
23
+ @doc
24
+ end
25
+
26
+ def body
27
+ @body
28
+ end
29
+ alias_method :html, :body
30
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: erector
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Pivotal Labs
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-02-13 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.5.0
23
+ version:
24
+ description: With Erector, you define views without templates, in natural Ruby code, with all the power of objects, functions, modular decomposition, etc.
25
+ email:
26
+ - alex@pivotallabs.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README.txt
33
+ files:
34
+ - spec/erector
35
+ - spec/erector/extensions
36
+ - spec/erector/extensions/render_widget_spec.rb
37
+ - spec/erector/widget_spec.rb
38
+ - spec/erector/widgets
39
+ - spec/erector/widgets/table_spec.rb
40
+ - spec/spec_helper.rb
41
+ - spec/spec_suite.rb
42
+ - spec/view_caching.rb
43
+ - lib/erector
44
+ - lib/erector/extensions
45
+ - lib/erector/extensions/action_controller.rb
46
+ - lib/erector/extensions/object.rb
47
+ - lib/erector/helpers.rb
48
+ - lib/erector/html_parts.rb
49
+ - lib/erector/widget.rb
50
+ - lib/erector/widgets
51
+ - lib/erector/widgets/table.rb
52
+ - lib/erector/widgets.rb
53
+ - lib/erector.rb
54
+ - README.txt
55
+ has_rdoc: true
56
+ homepage: http://erector.rubyforge.org
57
+ post_install_message:
58
+ rdoc_options:
59
+ - --main
60
+ - README.txt
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ version:
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: "0"
74
+ version:
75
+ requirements: []
76
+
77
+ rubyforge_project: erector
78
+ rubygems_version: 1.0.1
79
+ signing_key:
80
+ specification_version: 2
81
+ summary: With Erector, you define views without templates, in natural Ruby code, with all the power of objects, functions, modular decomposition, etc.
82
+ test_files: []
83
+