active_component 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+
3
+ #require 'spec_helper'
4
+
5
+ #require 'haml'
6
+
7
+ require 'active_component'
8
+
9
+ require 'factory_girl'
10
+ require 'factories'
@@ -0,0 +1,154 @@
1
+ # encoding: utf-8
2
+
3
+ describe ActiveComponent::Base do
4
+
5
+ context "component initialization" do
6
+ describe "html_class" do
7
+ it "should include the hyphenized component title" do
8
+ g = GreetingBox.new
9
+ g.title = "Happy greeting box"
10
+ g.instance_eval {[html_class].flatten}.should be_include 'happy-greeting-box'
11
+ d = Div.new
12
+ d.title = "Happy div"
13
+ d.instance_eval {[html_class].flatten}.should be_include 'happy-div'
14
+ end
15
+ it "should include the class_name unless it is an html tag wrapper" do
16
+ g = GreetingBox.new
17
+ g.instance_eval {[html_class].flatten}.should be_include 'greeting-box'
18
+ d = Div.new
19
+ d.instance_eval {[html_class].flatten}.should_not be_include 'div'
20
+ end
21
+ end
22
+
23
+ class NewsWidget < ActiveComponent::Base
24
+ attr_accessor :author
25
+
26
+ def initialize(*args, &content_block)
27
+ init_component(args, [:content, :title, :author, :attributes], &content_block)
28
+ end
29
+ end
30
+
31
+ describe "init_component" do
32
+ context "custom component" do
33
+ before :all do
34
+ @news_content = "The pope claims that condoms are not necessarily evil. Where is this heading to?"
35
+ # TODO support for special character removal pending
36
+ #@news_title = "Scandal in the Vatican!"
37
+ @news_title = "Scandal in the Vatican"
38
+ @news_author = "Karl Atzinger"
39
+ @news_attributes = {:class => 'gossip', :title => 'click to read whole article'}
40
+ end
41
+
42
+ it "should set all parameters correctly when given in order" do
43
+ n = NewsWidget.new @news_content, @news_title, @news_author, {:class => 'gossip'}
44
+ n.content.should == @news_content
45
+ n.title.should == @news_title
46
+ n.author.should == @news_author
47
+ n.attributes.should == {:class => ['scandal-in-the-vatican', 'news-widget', 'gossip']}
48
+ end
49
+
50
+ it "should set all parameters correctly when given as key value pairs" do
51
+ n = NewsWidget.new :attributes => @news_attributes, :title => @news_title, :content => @news_content, :author => @news_author
52
+ n.content.should == @news_content
53
+ n.title.should == @news_title
54
+ n.author.should == @news_author
55
+ n.attributes.should == {:title => @news_attributes[:title], :class => ['scandal-in-the-vatican', 'news-widget', 'gossip']}
56
+ end
57
+
58
+ it "should set all parameters correctly when given in a mixed way" do
59
+ n = NewsWidget.new @news_title, @news_author, :content => @news_content, :attributes => @news_attributes
60
+ n.content.should == @news_content
61
+ n.title.should == @news_title
62
+ n.author.should == @news_author
63
+ n.attributes.should == {:title => @news_attributes[:title], :class => ['scandal-in-the-vatican', 'news-widget', 'gossip']}
64
+ end
65
+
66
+ it "should prioritize content blocks over content arguments" do
67
+ n = NewsWidget.new(:content => "ignorable") { @news_content }
68
+ n.content.should == @news_content
69
+ end
70
+
71
+ it "should fill @attributes with remaining arguments of the last hash" do
72
+ n = NewsWidget.new @news_title, :id => 'news', :content => @news_content, :class => @news_attributes[:class]
73
+ n.content.should == @news_content
74
+ n.title.should == @news_title
75
+ n.attributes.should == {:id => 'news', :class => ['scandal-in-the-vatican', 'news-widget', 'gossip']}
76
+ end
77
+ end
78
+
79
+ context "html wrapper component (grand-child of base)" do
80
+ before :all do
81
+ @bq_content = "It is better to be quotable than to be honest."
82
+ @bq_title = "Tom Stoppard on Quotations"
83
+ @bq_attributes = {:class => 'quotes', :cites => "http://www.quotationspage.com/quote/368.html"}
84
+ end
85
+
86
+ it "should set all parameters correctly when given in order" do
87
+ bq = Blockquote.new @bq_content, @bq_title, @bq_author, @bq_attributes
88
+ bq.content.should == @bq_content
89
+ bq.title.should == @bq_title
90
+ bq.attributes.should == {:cites => @bq_attributes[:cites], :class => [@bq_title.hyphenize, 'quotes']}
91
+ end
92
+
93
+ it "should set all parameters correctly when given as key value pairs" do
94
+ bq = Blockquote.new :attributes => @bq_attributes, :title => @bq_title, :content => @bq_content
95
+ bq.content.should == @bq_content
96
+ bq.title.should == @bq_title
97
+ bq.attributes.should == {:cites => @bq_attributes[:cites], :class => [@bq_title.hyphenize, 'quotes']}
98
+ end
99
+
100
+ it "should set all parameters correctly when given in a mixed way" do
101
+ bq = Blockquote.new @bq_title, :content => @bq_content, :attributes => @bq_attributes
102
+ bq.content.should == @bq_content
103
+ bq.title.should == @bq_title
104
+ bq.attributes.should == {:cites => @bq_attributes[:cites], :class => [@bq_title.hyphenize, 'quotes']}
105
+ end
106
+
107
+ it "should prioritize content blocks over content arguments" do
108
+ bq = Blockquote.new(:content => "ignorable") { @bq_content }
109
+ bq.content.should == @bq_content
110
+ end
111
+
112
+ it "should fill attributes with remaining arguments of the last hash" do
113
+ bq = Blockquote.new @bq_title, :cites => "http://www.quotationspage.com/quote/368.html", :content => @bq_content, :class => 'quotes'
114
+ bq.content.should == @bq_content
115
+ bq.title.should == @bq_title
116
+ bq.attributes.should == {:cites => @bq_attributes[:cites], :class => [@bq_title.hyphenize, 'quotes']}
117
+ end
118
+ end
119
+
120
+ end
121
+ end
122
+
123
+ context "tree node management" do
124
+ it "should enable to add nodes as children" do
125
+ a = Block.new
126
+ b = Block.new("foo")
127
+ c = Block.new("bar")
128
+ a << b
129
+ a << c
130
+ a[b.object_id].should == b
131
+ children = []
132
+ a.children {|child| children << child.node_name}
133
+ children.should == [b.object_id, c.object_id]
134
+ end
135
+ end
136
+
137
+ context "instantiation helpers" do
138
+ it "should be available after creating a component" do
139
+ class NewGreetingBox < ActiveComponent::Base; end
140
+ ActiveComponent.instance_methods.map(&:to_sym).should be_include(:new_greeting_box)
141
+ end
142
+
143
+ # TODO: How to test this?
144
+ it "should be available to ActionView and render output"
145
+ # class GreetingBox < ActiveComponent::Base
146
+ # def initialize(*args, &content_block); init_component(args, [:content], &content_block); end
147
+ # def to_html; Span.new("Hello " + print_object(content), :class => 'greeting').to_html; end
148
+ # end
149
+ # ActiveComponent::TemplateHandler.new.instance_eval { compile(greeting_box("world")) }.should == "not specified yet"
150
+ # end
151
+ end
152
+
153
+ end
154
+
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+
3
+ describe Block do
4
+ include ActiveComponent
5
+ include Haml::Helpers
6
+
7
+ before :each do
8
+ @content = "It is better to be quotable than to be honest."
9
+ @title = "Tom Stoppard on Quotations"
10
+ @tag_type = :blockquote
11
+ @attributes = {:class => 'quotes', :cites => "http://www.quotationspage.com/quote/368.html"}
12
+ end
13
+
14
+ describe "initialize" do
15
+ it "should assume :div as default block type" do
16
+ block = Block.new { @content }
17
+ block.tag_type.should == :div
18
+ end
19
+
20
+ it "should not be possible to overwrite tag_type for subclasses of Block" do
21
+ for elem in ActiveComponent::BLOCK_ELEMENTS
22
+ elem_class = elem.to_s.camelize.constantize
23
+ block = elem_class.new(:tag_type => :block) { @content }
24
+ block.tag_type.should == elem
25
+ end
26
+ end
27
+ end
28
+
29
+ describe "to_html" do
30
+ it "should render nested blocks" do
31
+ init_buffer
32
+ block = Block.new @title, @tag_type, @attributes do
33
+ [
34
+ Block.new(@title, :tag_type => :h1),
35
+ Block.new(@content, :tag_type => :p),
36
+ Block.new("Details", :tag_type => :aside) do
37
+ print_buffer do
38
+ tag_to_buffer :dl do
39
+ tag_to_buffer :dt, "Author"
40
+ tag_to_buffer :dd, "Tom Stoppard"
41
+ end
42
+ end
43
+ end
44
+ ]
45
+ end
46
+ block.to_html.should == "<#{@tag_type} cites='#{@attributes[:cites]}' class='#{@title.hyphenize} block #{@attributes[:class]}'>\n <h1 class='block'>\n #{@title}\n </h1>\n <p class='block'>\n #{@content}\n </p>\n <aside class='details block'>\n <dl>\n <dt>Author</dt>\n <dd>Tom Stoppard</dd>\n </dl>\n \n </aside>\n \n</#{@tag_type}>\n"
47
+ end
48
+ end
49
+
50
+ end
51
+
@@ -0,0 +1,95 @@
1
+ # encoding: utf-8
2
+
3
+ describe Heading do
4
+ include ActiveComponent
5
+ include Haml::Helpers
6
+
7
+ before :all do
8
+ @content = "Wall Street Retreats in the Face of a Slowdown"
9
+ @title = "Business News"
10
+ @level = 3
11
+ @attributes = {:class => 'news business'}
12
+ end
13
+
14
+ describe "siblings_level" do
15
+
16
+ it "should adopt correct siblings level" do
17
+ h1 = Heading.new('header 1', :level => 3)
18
+ h2 = Heading.new
19
+ h3 = Heading.new [h1, h2]
20
+ h2.siblings_level.should == 3
21
+ end
22
+
23
+ it "should return nil if no heading sibling exists" do
24
+ h1 = Heading.new
25
+ h2 = Heading.new h1
26
+ h1.siblings_level.should be_nil
27
+ end
28
+
29
+ it "should return nil if existing heading sibling has no rank" do
30
+ h1 = Heading.new('header 1')
31
+ h2 = Heading.new
32
+ h3 = Heading.new [h1, h2]
33
+ h2.siblings_level.should be_nil
34
+ end
35
+
36
+ end
37
+
38
+ describe "determine_level" do
39
+
40
+ it "should return the rank of a given sibling Heading" do
41
+ h1 = Heading.new(:level => 3)
42
+ h2 = Heading.new
43
+ h3 = Heading.new([h1, h2])
44
+
45
+ h2.determine_level.should == 3
46
+ end
47
+
48
+ it "should determine heading rank correctly based on the hierarchy" do
49
+ h1 = Heading.new('header 1')
50
+ h2 = Heading.new('header 2')
51
+ h3 = Heading.new('header 3')
52
+ div2 = Div.new(:content => [h2, h3])
53
+ div = Div.new(:content => [
54
+ h1,
55
+ div2
56
+ ])
57
+ h2.determine_level.should == 2
58
+ end
59
+
60
+ end
61
+
62
+
63
+ describe "to_html" do
64
+
65
+ it "should compute heading levels automatically based on the node hierarchy" do
66
+ section = Section.new(:heading => @title, :content => [
67
+ Heading.new("It's better to be honest!"),
68
+ Section.new(:heading => "Tom Stoppard", :content => [
69
+ Div.new(:title => "Some div inbetween", :content => [
70
+ Heading.new("Yet Another Heading"),
71
+ Section.new("Details", :heading => "Leaf Heading")
72
+ ])
73
+ ])
74
+ ])
75
+ section.to_html.should == "<section>\n <h1 class='heading'>\n Business News\n </h1>\n \n <h1 class='heading'>\n It's better to be honest!\n </h1>\n \n <section>\n <h2 class='heading'>\n Tom Stoppard\n </h2>\n \n <div class='some-div-inbetween'>\n <h3 class='heading'>\n Yet Another Heading\n </h3>\n \n <section>\n <h4 class='heading'>\n Leaf Heading\n </h4>\n \n Details\n </section>\n \n </div>\n \n </section>\n \n</section>\n"
76
+ end
77
+
78
+ it "should compute heading levels automatically also when block syntax used" do
79
+ section = Section.new(:heading => @title, :content => [
80
+ Heading.new("It's better to be honest!"),
81
+ Section.new(:heading => "Tom Stoppard", :content => [
82
+ Div.new(:title => "Some div inbetween") do
83
+ [
84
+ Heading.new("Yet Another Heading"),
85
+ Section.new("Details", :heading => "Leaf Heading")
86
+ ]
87
+ end
88
+ ])
89
+ ])
90
+ section.to_html.should == "<section>\n <h1 class='heading'>\n Business News\n </h1>\n \n <h1 class='heading'>\n It's better to be honest!\n </h1>\n \n <section>\n <h2 class='heading'>\n Tom Stoppard\n </h2>\n \n <div class='some-div-inbetween'>\n <h3 class='heading'>\n Yet Another Heading\n </h3>\n \n <section>\n <h4 class='heading'>\n Leaf Heading\n </h4>\n \n Details\n </section>\n \n </div>\n \n </section>\n \n</section>\n"
91
+ end
92
+
93
+ end
94
+
95
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ describe Section do
4
+
5
+ before :each do
6
+ @content = "It is better to be quotable than to be honest."
7
+ @title = "Tom Stoppard on Quotations"
8
+ @tag_type = :blockquote
9
+ @attributes = {:class => 'quotes', :cites => "http://www.quotationspage.com/quote/368.html"}
10
+ end
11
+
12
+ describe "initialize" do
13
+ it "should assume :section as default section type" do
14
+ section = Section.new @content
15
+ section.tag_type.should == :section
16
+ end
17
+
18
+ it "should not be possible to overwrite tag_type for subclasses of section" do
19
+ for elem in ActiveComponent::SECTION_ELEMENTS
20
+ elem_class = elem.to_s.camelize.constantize
21
+ section = elem_class.new(:tag_type => :section) { @content }
22
+ section.tag_type.should == elem
23
+ end
24
+ end
25
+ end
26
+
27
+ describe "to_html" do
28
+ it "should be able to render nested sections"
29
+
30
+ it "should be able to render section subclasses" do
31
+ b = Blockquote.new(@content, @title, @attributes)
32
+ b.to_html.should == "<blockquote cites='http://www.quotationspage.com/quote/368.html' class='tom-stoppard-on-quotations quotes'>\n It is better to be quotable than to be honest.\n</blockquote>"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,88 @@
1
+ # encoding: utf-8
2
+
3
+ describe Table do
4
+
5
+ def things
6
+ things = []
7
+ things << Factory.build(:thing, :name => "thingamabob", :color => :yellow)
8
+ things << Factory.build(:thing, :name => "whatchamacallit", :color => :red)
9
+ things << Factory.build(:thing, :name => "gizmo", :color => :blue)
10
+ things
11
+ end
12
+
13
+ context "rendering a matrix of primitive data" do
14
+ it "should render without further input" do
15
+ content = [["a", 1, "first entry"], ["b", 2, "second entry"]]
16
+ table = Table.new content
17
+ table.to_html.should == "<table cellspacing='0' class='arrays'>\n <tr>\n <td>a</td>\n <td>1</td>\n <td>first entry</td>\n </tr>\n <tr>\n <td>b</td>\n <td>2</td>\n <td>second entry</td>\n </tr>\n</table>\n"
18
+ end
19
+
20
+ it "should render correctly with all inputs set" do
21
+ content = [["a", 1, "first entry"], ["b", 2, "second entry"]]
22
+ table = Table.new content, 'examples', :cols => :to_s, :headers => ["char", "number", "entry"]
23
+ table.to_html.should == "<table cellspacing='0' class='examples'>\n <tr>\n <th>char</th>\n <th>number</th>\n <th>entry</th>\n </tr>\n <tr>\n <td>a</td>\n <td>1</td>\n <td>first entry</td>\n </tr>\n <tr>\n <td>b</td>\n <td>2</td>\n <td>second entry</td>\n </tr>\n</table>\n"
24
+ end
25
+ end
26
+
27
+ context "rendering a collection of complex objects (e.g. instances of an Active Record model)" do
28
+ it "should render without further input" do
29
+ things = [Factory.build(:thing), Factory.build(:thing, :name => "Whatchamacallit", :color => :red)]
30
+ table = Table.new things
31
+ table.to_html.should == "<table cellspacing='0' class='things'>\n <tr>\n <th>Name</th>\n <th>Color</th>\n </tr>\n <tr>\n <td>Thingamabob</td>\n <td>yellow</td>\n </tr>\n <tr>\n <td>Whatchamacallit</td>\n <td>red</td>\n </tr>\n</table>\n"
32
+ end
33
+
34
+ it "should render complex ojects correctly with all inputs set" do
35
+ color_temperature = proc {|thing| [:yellow, :orange, :red].include?(thing.color) ? "warm" : "cold"}
36
+ table = Table.new things, 'things-table', :cols => [:name, color_temperature], :headers => ["Name", "Color Temperature"]
37
+ table.to_html.should == "<table cellspacing='0' class='things-table'>\n <tr>\n <th>Name</th>\n <th>Color Temperature</th>\n </tr>\n <tr>\n <td>thingamabob</td>\n <td>warm</td>\n </tr>\n <tr>\n <td>whatchamacallit</td>\n <td>warm</td>\n </tr>\n <tr>\n <td>gizmo</td>\n <td>cold</td>\n </tr>\n</table>\n"
38
+ end
39
+
40
+ # According Haml template code
41
+ #-----------------------------
42
+ # %table
43
+ # %tr
44
+ # %th Name
45
+ # %th Reverse Name
46
+ # %th Color Temperature
47
+ # - @things.each |thing| do
48
+ # %tr
49
+ # %td= thing.name
50
+ # %td= thing.name.reverse
51
+ # %td.color-temp
52
+ # - temp = [:yellow, :orange, :red].include?(thing.color) ? "warm" : "cold"
53
+ # %span{:class => temp}
54
+ # = temp + "!"
55
+ it "should render complex ojects using other components" do
56
+ table = Table.new(things, 'things-table', :headers => ["Name", "Color Temperature"], :cols => [
57
+ :name,
58
+ proc {|thing| thing.name.reverse},
59
+ proc do |thing|
60
+ temp = [:yellow, :orange, :red].include?(thing.color) ? "warm" : "cold"
61
+ Span.new(temp + "!", :class => temp)
62
+ end
63
+ ])
64
+ table.to_html.should == "<table cellspacing='0' class='things-table'>\n <tr>\n <th>Name</th>\n <th>Color Temperature</th>\n </tr>\n <tr>\n <td>thingamabob</td>\n <td>bobamagniht</td>\n <td>\n <span class='warm'>\n warm!\n </span>\n \n </td>\n </tr>\n <tr>\n <td>whatchamacallit</td>\n <td>tillacamahctahw</td>\n <td>\n <span class='warm'>\n warm!\n </span>\n \n </td>\n </tr>\n <tr>\n <td>gizmo</td>\n <td>omzig</td>\n <td>\n <span class='cold'>\n cold!\n </span>\n \n </td>\n </tr>\n</table>\n"
65
+ end
66
+ #
67
+ # it "should render using short hand helper functions"
68
+ # pending {}
69
+ # #tab = table(things, 'color-table', :cols => [ col(:name, 'Name'), col(nil, 'Reverse Name') {|thing| thing.name.reverse} ])
70
+ # #col(:title => 'Color Temperature', :class => 'color-temp') { |thing|
71
+ # # temp = [:yellow, :orange, :red].include?(thing.color) ? "warm" : "cold"
72
+ # # span(temp + "!", :class => temp)
73
+ # #}
74
+ # #tab.to_html.should == "not specified yet"
75
+ # end
76
+ end
77
+
78
+ context "passing HTML attributes" do
79
+ it "should forward attributes for headers" do
80
+ table = Table.new things, :title => "my-things",
81
+ :style => "padding: 10px", :class => "custom-class", :cellpadding => 0,
82
+ :header_attrs => [{:class => 'name-header', :width => '200px'}, {:id => 'temp-col-header', :style => 'color: red'}]
83
+ table.row_attrs = [{:class => 'head-row'}] + table.content.map {|thing| {:class => thing.name}}
84
+ table.to_html.should == "<table cellpadding='0' cellspacing='0' class='my-things custom-class' style='padding: 10px'>\n <tr class='head-row'>\n <th class='name-header' width='200px'>Name</th>\n <th id='temp-col-header' style='color: red'>Color</th>\n </tr>\n <tr class='thingamabob'>\n <td>thingamabob</td>\n <td>yellow</td>\n </tr>\n <tr class='whatchamacallit'>\n <td>whatchamacallit</td>\n <td>red</td>\n </tr>\n <tr class='gizmo'>\n <td>gizmo</td>\n <td>blue</td>\n </tr>\n</table>\n"
85
+ end
86
+ end
87
+
88
+ end
@@ -0,0 +1,62 @@
1
+ # encoding: utf-8
2
+
3
+ #TODO transmogrify on more than 2 levels
4
+
5
+ describe Object do
6
+
7
+ context "transmogrify" do
8
+ it "should yield self if its not enumerable" do
9
+ block = Proc.new {|x| x.to_s.to_sym}
10
+ obj = Object.new
11
+ obj.transmogrify(&block).should eql(block.call(obj))
12
+ end
13
+ end
14
+ context "transmogrify_with_index" do
15
+ it "should not differ from transmogrify" do
16
+ block = Proc.new {|x| x.to_s.to_sym}
17
+ obj = Object.new
18
+ obj.transmogrify_with_index(&block).should eql(obj.transmogrify(&block))
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ describe Enumerable do
25
+
26
+ context "transmogrify" do
27
+ before(:each) do
28
+ @has_yielded = false
29
+ @results = []
30
+ @block = Proc.new do |x|
31
+ @results << x.to_s.to_sym
32
+ @has_yielded = true
33
+ end
34
+ end
35
+ it "should yield each element" do
36
+ %w(a b c).transmogrify(&@block)
37
+ @has_yielded.should be_true
38
+ @results.should eql([:a, :b, :c])
39
+ end
40
+ after(:each) do
41
+ @has_yielded, @results, @block = nil
42
+ end
43
+ end
44
+
45
+ context "transmogrify_with_index" do
46
+ before(:each) do
47
+ @has_yielded = false
48
+ @results = {}
49
+ @block = Proc.new do |elem, index|
50
+ @results[index] = elem.to_s.to_sym
51
+ end
52
+ end
53
+ it "should yield each element with index" do
54
+ %w(a b c).transmogrify_with_index(&@block)
55
+ @results.should == { 0 => :a, 1 => :b, 2 => :c }
56
+ end
57
+ after(:each) do
58
+ @result, @results, @block = nil
59
+ end
60
+ end
61
+
62
+ end