active_component 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +4 -0
- data/.rspec +1 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +30 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +65 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/active_component.gemspec +104 -0
- data/init.rb +1 -0
- data/lib/active_component.rb +196 -0
- data/lib/active_component/base.rb +930 -0
- data/lib/active_component/components/block.rb +20 -0
- data/lib/active_component/components/empty_tag.rb +26 -0
- data/lib/active_component/components/heading.rb +60 -0
- data/lib/active_component/components/inline_tag.rb +24 -0
- data/lib/active_component/components/section.rb +39 -0
- data/lib/active_component/components/table.rb +61 -0
- data/lib/active_component/config.rb +33 -0
- data/lib/active_component/core_extensions.rb +99 -0
- data/lib/active_component/template_handler.rb +20 -0
- data/pkg/active_component-0.1.2.gem +0 -0
- data/spec/active_component_spec.rb +156 -0
- data/spec/active_component_spec_helper.rb +10 -0
- data/spec/base_spec.rb +154 -0
- data/spec/components/block_spec.rb +51 -0
- data/spec/components/heading_spec.rb +95 -0
- data/spec/components/section_spec.rb +35 -0
- data/spec/components/table_spec.rb +88 -0
- data/spec/core_extensions_spec.rb +62 -0
- data/spec/factories.rb +23 -0
- data/spec/rcov.opts +2 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +15 -0
- metadata +166 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Block < ActiveComponent::Base
|
4
|
+
|
5
|
+
attr_accessor :tag_type
|
6
|
+
|
7
|
+
def initialize(*args, &content_block)
|
8
|
+
init_component(args, [:content, :title, :tag_type, :attributes], &content_block)
|
9
|
+
|
10
|
+
# Defaults
|
11
|
+
@tag_type ||= :div
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_html
|
15
|
+
wrap_contents(@tag_type, content, nil, @attributes)
|
16
|
+
end
|
17
|
+
|
18
|
+
def_html_sub_components ActiveComponent::BLOCK_ELEMENTS, self
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class EmptyTag < ActiveComponent::Base
|
4
|
+
|
5
|
+
attr_accessor :tag_type
|
6
|
+
|
7
|
+
# Content can be passed as a block
|
8
|
+
def initialize(*args)
|
9
|
+
init_component(args, [:title, :tag_type, :attributes])
|
10
|
+
|
11
|
+
# Defaults
|
12
|
+
@tag_type ||= :br
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_html
|
16
|
+
if ActiveComponent::Config.component_options[:validate_html]
|
17
|
+
raise InvalidHtmlError, "Empty HTML elements must not have content." if content.present?
|
18
|
+
end
|
19
|
+
|
20
|
+
name, attrs = merge_name_and_attributes(@tag_type.to_s, @attributes)
|
21
|
+
attrs = Haml::Precompiler.build_attributes(@haml_buffer.html?, @haml_buffer.options[:attr_wrapper], attrs)
|
22
|
+
"<#{name}#{attrs} />"
|
23
|
+
end
|
24
|
+
|
25
|
+
def_html_sub_components ActiveComponent::EMPTY_ELEMENTS, self
|
26
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Heading < ActiveComponent::Base
|
4
|
+
|
5
|
+
attr_reader :level
|
6
|
+
|
7
|
+
def level=(level)
|
8
|
+
unless level.nil?
|
9
|
+
raise ArgumentError, "heading_level must be numeric (given #{level.inspect} in heading #{@content})" unless level.is_a? Numeric
|
10
|
+
puts "warning: heading_level should be an integer and between 1 and 6 (given #{level} in heading #{@content})" unless level.between?(1,6)
|
11
|
+
@level = [[level.to_i, 6].min, 1].max
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(*args, &content_block)
|
16
|
+
init_component(args, [:content, :title, :level, :attributes], &content_block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_html
|
20
|
+
@level ||= determine_level
|
21
|
+
|
22
|
+
wrap_contents("h" + @level.to_s, content, nil, @attributes)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Determines the heading level by adopting the siblings' one
|
26
|
+
# or by determining the parent's one recursively
|
27
|
+
def determine_level
|
28
|
+
return 1 if is_root?
|
29
|
+
return level if level.present?
|
30
|
+
|
31
|
+
siblings_level or (
|
32
|
+
if Heading.has_parent_heading?(self)
|
33
|
+
Heading.parent_heading(self).determine_level + 1
|
34
|
+
else
|
35
|
+
1
|
36
|
+
end
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Collects the level of sibling headings
|
41
|
+
def siblings_level
|
42
|
+
siblings.collect {|sib| sib.level if sib.is_a?(Heading)}.compact.min
|
43
|
+
end
|
44
|
+
|
45
|
+
# Retrieves the next Heading of the node hierarchy above a given node
|
46
|
+
def Heading.parent_heading(node)
|
47
|
+
raise ArgumentException, "Node has no heading parent." unless Heading.has_parent_heading?(node)
|
48
|
+
node.parent.siblings.find_a(Heading) or Heading.parent_heading(node.parent)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Checks whether a Heading exists in the node hierarchy above a given node
|
52
|
+
def Heading.has_parent_heading?(node)
|
53
|
+
!node.is_root? && (
|
54
|
+
node.parent.siblings.includes_a?(Heading) ||
|
55
|
+
Heading.has_parent_heading?(node.parent)
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
def_html_sub_components ActiveComponent::HEADING_ELEMENTS, self
|
60
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class InlineTag < ActiveComponent::Base
|
4
|
+
|
5
|
+
attr_accessor :tag_type
|
6
|
+
|
7
|
+
# Content can be passed as a block
|
8
|
+
def initialize(*args, &content_block)
|
9
|
+
init_component(args, [:content, :title, :tag_type, :attributes], &content_block)
|
10
|
+
|
11
|
+
# Defaults
|
12
|
+
@tag_type ||= :span
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_html
|
16
|
+
if ActiveComponent::Config.component_options[:validate_html]
|
17
|
+
raise InvalidHtmlError, "Inline tags must not have blocks as inner content." if content.includes_a? Block
|
18
|
+
end
|
19
|
+
|
20
|
+
wrap_contents(@tag_type, content, nil, @attributes)
|
21
|
+
end
|
22
|
+
|
23
|
+
def_html_sub_components ActiveComponent::PHRASING_ELEMENTS, self
|
24
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Section < ActiveComponent::Base
|
4
|
+
|
5
|
+
attr_accessor :tag_type, :heading, :heading_level, :heading_attrs
|
6
|
+
|
7
|
+
def initialize(*args, &content_block)
|
8
|
+
init_component(args, [:content, :title, :tag_type, :heading, :heading_level, :heading_attrs, :attributes], &content_block)
|
9
|
+
|
10
|
+
# Defaults
|
11
|
+
@tag_type ||= :section
|
12
|
+
|
13
|
+
# Validations
|
14
|
+
raise ArgumentError, "attributes must be a hash (given #{@attributes.inspect} in section #{@title})" unless @attributes.is_a? Hash
|
15
|
+
# TODO: Heading rank
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_html
|
19
|
+
if @heading.present? && !@heading.is_a?(ActiveComponent)
|
20
|
+
@heading = Heading.new @heading, @heading_level, @heading_attrs
|
21
|
+
children.nil? ? (self << @heading) : self.prepend(@heading)
|
22
|
+
end
|
23
|
+
|
24
|
+
# TODO: Is this clean? Is there a better way that hides buffer operations?
|
25
|
+
# wrap_contents(@tag_type, :attributes => @attributes, :content => [@heading, content]
|
26
|
+
|
27
|
+
print_buffer do
|
28
|
+
tag_to_buffer @tag_type, @attributes do
|
29
|
+
write_to_buffer print_object(@heading)
|
30
|
+
content.transmogrify do |content|
|
31
|
+
write_to_buffer print_object(content)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def_html_sub_components ActiveComponent::SECTION_ELEMENTS, self
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Table < ActiveComponent::Base
|
4
|
+
|
5
|
+
attr_accessor :cols, :headers, :row_attrs, :header_attrs, :field_attrs
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
init_component(args, [:content, :title, :cols, :headers, :attributes, :row_attrs, :header_attrs, :field_attrs])
|
9
|
+
|
10
|
+
# Defaults
|
11
|
+
if @title.nil?
|
12
|
+
@title = content.first.class.to_s.hyphenize.pluralize
|
13
|
+
@attributes[:class].to_a.unshift @title
|
14
|
+
end
|
15
|
+
if @cols.nil? && content.first.respond_to?(:attributes)
|
16
|
+
@cols = content.first.attributes.keys
|
17
|
+
@headers ||= @cols.collect {|col| col.to_s.humanize}
|
18
|
+
end
|
19
|
+
@attributes[:cellspacing] ||= 0
|
20
|
+
@row_attrs ||= {}
|
21
|
+
@header_attrs ||= {}
|
22
|
+
@field_attrs ||= {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_html
|
26
|
+
print_buffer do
|
27
|
+
tag_to_buffer :table, @attributes do
|
28
|
+
row_count = 0
|
29
|
+
unless @headers.blank?
|
30
|
+
tag_to_buffer :tr, get_row_attrs(row_count) do
|
31
|
+
@headers.each_with_index do |header, i|
|
32
|
+
tag_to_buffer :th, header, get_header_attrs(i)
|
33
|
+
end
|
34
|
+
row_count = 1
|
35
|
+
end
|
36
|
+
end
|
37
|
+
content.each_with_index do |row, i|
|
38
|
+
unless row.blank?
|
39
|
+
tag_to_buffer :tr, get_row_attrs(row_count + i) do
|
40
|
+
print_contents(:td, row, @cols, @field_attrs)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def get_attrs(attrs_collection, index = nil)
|
50
|
+
attrs = attrs_collection[index] || attrs_collection
|
51
|
+
attrs.is_a?(Hash) ? attrs : {}
|
52
|
+
end
|
53
|
+
|
54
|
+
def get_row_attrs(index = nil)
|
55
|
+
get_attrs(@row_attrs, index)
|
56
|
+
end
|
57
|
+
|
58
|
+
def get_header_attrs(index = nil)
|
59
|
+
get_attrs(@header_attrs, index)
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module ActiveComponent
|
4
|
+
# The module for all global ActiveComponent configurations
|
5
|
+
module Config
|
6
|
+
extend self
|
7
|
+
|
8
|
+
@component_options = {}
|
9
|
+
# The options hash for Haml when used within Rails.
|
10
|
+
# See {file:HAML_REFERENCE.md#haml_options the Haml options documentation}.
|
11
|
+
#
|
12
|
+
# @return [{Symbol => Object}]
|
13
|
+
attr_accessor :component_options
|
14
|
+
|
15
|
+
def template_engine_options
|
16
|
+
Haml::Template.options
|
17
|
+
end
|
18
|
+
|
19
|
+
def template_engine_options=(options)
|
20
|
+
Haml::Template.options = options
|
21
|
+
end
|
22
|
+
|
23
|
+
template_engine_options[:format] ||= :html5
|
24
|
+
|
25
|
+
if Haml::Util.rails_env == "development"
|
26
|
+
component_options[:validate_html] ||= true
|
27
|
+
template_engine_options[:ugly] ||= false
|
28
|
+
else
|
29
|
+
component_options[:validate_html] ||= false
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Object
|
4
|
+
|
5
|
+
# Transmogrify yields self to the given block by default
|
6
|
+
def transmogrify(*ignored_args)
|
7
|
+
yield self
|
8
|
+
end
|
9
|
+
|
10
|
+
# Wrapper for enumerable transmogrify
|
11
|
+
def transmogrify_with_index(&block)
|
12
|
+
transmogrify(:yield_index, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
alias :includes_a? :is_a?
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
class String
|
20
|
+
|
21
|
+
# Performs same transformation as underscore, but with hyphens
|
22
|
+
def hyphenize
|
23
|
+
gsub(/::/, '/').
|
24
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
25
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
26
|
+
tr("_", "-").
|
27
|
+
tr(" ", "-").
|
28
|
+
downcase
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
class Symbol
|
34
|
+
|
35
|
+
def to_class_constant
|
36
|
+
to_s.camelize.constantize
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
module Enumerable
|
42
|
+
|
43
|
+
# Transmogrify yields each element to the given block
|
44
|
+
def transmogrify(*options)
|
45
|
+
if options.include? :yield_index
|
46
|
+
each_with_index do |element, index|
|
47
|
+
yield element, index
|
48
|
+
end
|
49
|
+
else
|
50
|
+
each do |element|
|
51
|
+
yield element
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Determines if enumerable contains an object of the specified class
|
57
|
+
def includes_a?(klass)
|
58
|
+
each do |e|
|
59
|
+
return true if e.is_a? klass
|
60
|
+
end
|
61
|
+
false
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the first object of the specified class contained in enumerable
|
65
|
+
def find_a(klass)
|
66
|
+
each do |e|
|
67
|
+
return e if e.is_a? klass
|
68
|
+
end
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
# FIXME: There should be a better way to provide the module to ActionView.
|
75
|
+
class ActionView::Base
|
76
|
+
include ActiveComponent
|
77
|
+
end
|
78
|
+
|
79
|
+
if defined? ActiveSupport::CoreExtensions::Hash::ReverseMerge
|
80
|
+
# Rails 2
|
81
|
+
module ActiveSupport::CoreExtensions::Hash::ReverseMerge
|
82
|
+
alias :set_defaults :reverse_merge
|
83
|
+
alias :set_defaults! :reverse_merge!
|
84
|
+
end
|
85
|
+
else
|
86
|
+
# Rails 3
|
87
|
+
class Hash
|
88
|
+
alias :set_defaults :reverse_merge
|
89
|
+
alias :set_defaults! :reverse_merge!
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
module Haml::Helpers
|
94
|
+
alias :init_buffer :init_haml_helpers
|
95
|
+
alias :print_buffer :capture_haml
|
96
|
+
alias :tag_to_buffer :haml_tag
|
97
|
+
alias :write_to_buffer :haml_concat
|
98
|
+
alias :string_to_buffer :haml_concat
|
99
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module ActiveComponent
|
4
|
+
class TemplateHandler
|
5
|
+
include ActionView::TemplateHandlers::Compilable
|
6
|
+
|
7
|
+
def compile(template)
|
8
|
+
# For Rails < 2.1.0, template is a string
|
9
|
+
# For Rails >= 2.1.0, template is a Template object
|
10
|
+
if template.respond_to? :source
|
11
|
+
# # For Rails >=3.0.0, there is a generic identifier
|
12
|
+
# options[:filename] = template.respond_to?(:identifier) ? template.identifier : template.filename
|
13
|
+
template.source
|
14
|
+
else
|
15
|
+
template
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
Binary file
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'active_component_spec_helper'
|
4
|
+
|
5
|
+
describe ActiveComponent do
|
6
|
+
|
7
|
+
before(:each) { @comp = Block.new }
|
8
|
+
after(:each) { @comp = nil }
|
9
|
+
|
10
|
+
describe "print_contents" do
|
11
|
+
it "should print content and wrap it with a tag" do
|
12
|
+
content = "content"
|
13
|
+
@comp.print_contents(:span, content).should == "<span>content</span>\n"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should print multiple contents and wrap each item with a tag" do
|
17
|
+
content = [:a, "b", 3]
|
18
|
+
Factory.sequence(:content) {|n| content[n]}
|
19
|
+
@comp.print_contents(:span, content).should == "<span>a</span>\n<span>b</span>\n<span>3</span>\n"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should print content using a method and wrap the output with a tag" do
|
23
|
+
deep_thought = mock :content
|
24
|
+
deep_thought.should_receive(:question).once.and_return(42)
|
25
|
+
@comp.print_contents("div.the-answer", deep_thought, :question).should == "<div class='the-answer'>42</div>\n"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should print multiple contents using a method and wrap each one with a tag" do
|
29
|
+
names = %w(Aron Noel Trebor)
|
30
|
+
transformation = Proc.new {|x| x.downcase.reverse.humanize}
|
31
|
+
@comp.print_contents(:li, names, transformation).should == "<li>Nora</li>\n<li>Leon</li>\n<li>Robert</li>\n"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should print multiple contents using a set of methods and wrap each of the method outputs with a tag" do
|
35
|
+
things = []
|
36
|
+
things << Factory.build(:thing, :name => "thingamabob", :color => :yellow)
|
37
|
+
things << Factory.build(:thing, :name => "whatchamacallit", :color => :red)
|
38
|
+
things << Factory.build(:thing, :name => "gizmo", :color => :blue)
|
39
|
+
@comp.print_contents(:p, things, things.first.attributes.keys).should == "<p>thingamabob</p>\n<p>yellow</p>\n<p>whatchamacallit</p>\n<p>red</p>\n<p>gizmo</p>\n<p>blue</p>\n"
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should print multiple contents, each item being paired with its own method and wrap each of the method outputs with a tag" do
|
43
|
+
accomodations = []
|
44
|
+
accomodations << mock(:hostel)
|
45
|
+
accomodations << mock(:campground)
|
46
|
+
accomodations[0].should_receive(:rooms).and_return(15)
|
47
|
+
accomodations[1].should_receive(:campsites).and_return(25)
|
48
|
+
capacity_information = [
|
49
|
+
Proc.new {|h| "There are #{h.rooms} hostel rooms available."},
|
50
|
+
Proc.new {|c| "The campground can take #{c.campsites} tents."}
|
51
|
+
]
|
52
|
+
@comp.print_contents(:p, accomodations, capacity_information, :couple_methods_with_contents).should == "<p>There are 15 hostel rooms available.</p>\n<p>The campground can take 25 tents.</p>\n"
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should merge attributes"
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "wrap_contents" do
|
60
|
+
it "should wrap text content with a tag" do
|
61
|
+
content = "content"
|
62
|
+
@comp.print_contents(:span, content, nil, :wrap_whole_content).should == "<span>\n content\n</span>\n"
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should wrap HTML content with a tag" do
|
66
|
+
content = "<span>\n content\n</span>\n"
|
67
|
+
@comp.wrap_contents(:p, content).should == "<p>\n <span>\n content\n </span>\n</p>\n"
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should wrap multiple contents with a tag" do
|
71
|
+
names_list = ["<li>Nora</li>", "<li>Leon</li>", "<li>Robert</li>"]
|
72
|
+
@comp.wrap_contents(:ul, names_list).should == "<ul>\n <li>Nora</li>\n <li>Leon</li>\n <li>Robert</li>\n</ul>\n"
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should print content using a method and wrap the output with a tag" do
|
76
|
+
deep_thought = mock :content
|
77
|
+
deep_thought.should_receive(:question).once.and_return(42)
|
78
|
+
@comp.wrap_contents("div.the-answer", deep_thought, :question).should == "<div class='the-answer'>\n 42\n</div>\n"
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should print multiple contents using a method and wrap the whole output into a tag" do
|
82
|
+
names = %w(Aron Noel Trebor)
|
83
|
+
transformation = Proc.new {|x| x.downcase.reverse.humanize}
|
84
|
+
@comp.wrap_contents(:p, names, transformation).should == "<p>\n Nora\n Leon\n Robert\n</p>\n"
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should wrap multiple contents with a tag, each item being printed using its own method" do
|
88
|
+
accomodations = []
|
89
|
+
accomodations << mock(:hostel)
|
90
|
+
accomodations << mock(:campground)
|
91
|
+
accomodations[0].should_receive(:rooms).and_return(15)
|
92
|
+
accomodations[1].should_receive(:campsites).and_return(25)
|
93
|
+
capacity_information = [
|
94
|
+
Proc.new {|h| "There are #{h.rooms} hostel rooms available."},
|
95
|
+
Proc.new {|c| "The campground can take #{c.campsites} tents."}
|
96
|
+
]
|
97
|
+
@comp.wrap_contents(:p, accomodations, capacity_information, :couple_methods_with_contents).should == "<p>\n There are 15 hostel rooms available.\n The campground can take 25 tents.\n</p>\n"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "print_object" do
|
102
|
+
it "should print primitive data" do
|
103
|
+
for primitive in ["test", 1.0, 7, true, '']
|
104
|
+
@comp.print_object(primitive).should == primitive.to_s
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should call callable objects" do
|
109
|
+
callable = mock :method
|
110
|
+
callable.should_receive(:call).at_least(:once).and_return(42)
|
111
|
+
@comp.print_object(callable).should == callable.call.to_s
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should render components" do
|
115
|
+
renderable = mock :component
|
116
|
+
html = "<div>\n Component content\n</div"
|
117
|
+
renderable.should_receive(:to_html).any_number_of_times.and_return(html)
|
118
|
+
renderable.should_receive(:to_s).any_number_of_times.and_return(html)
|
119
|
+
@comp.print_object(renderable).should == html
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should capture Haml buffers" do
|
123
|
+
p = lambda { @comp.haml_tag(:span, "haml") }
|
124
|
+
@comp.print_object(p).should == "<span>haml</span>\n"
|
125
|
+
p = lambda { @comp.haml_concat("written-to-buffer-and-captured") }
|
126
|
+
@comp.print_object(p).should == "written-to-buffer-and-captured\n"
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should utilize receiver capabilities of object if applicable" do
|
130
|
+
receiver = mock :object
|
131
|
+
receiver.should_receive(:message).at_least(:once).and_return(42)
|
132
|
+
@comp.print_object(receiver, :message).should == receiver.message.to_s
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should yield non-receiver objects if a suitable method is given" do
|
136
|
+
callable = mock :method
|
137
|
+
object = 42
|
138
|
+
callable.should_receive(:arity).at_least(:once).and_return(1, -1, -2)
|
139
|
+
callable.should_receive(:call).with(object).at_least(:once).and_return(42)
|
140
|
+
3.times do
|
141
|
+
@comp.print_object(object, callable).should == callable.call(object).to_s
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should not try to print non-receiver objects if an unsuitable method is given" do
|
146
|
+
callable = mock :method
|
147
|
+
object = 42
|
148
|
+
callable.should_receive(:arity).at_least(:once).and_return(0, 2, -3)
|
149
|
+
callable.should_not_receive(:call)
|
150
|
+
3.times do
|
151
|
+
lambda {@comp.print_object(object, callable)}.should raise_error(ArgumentError)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|