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