active_component 0.1.2

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,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