erector 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+