hensel 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.travis.yml +20 -0
- data/Gemfile +4 -0
- data/Guardfile +13 -0
- data/LICENSE.txt +22 -0
- data/README.md +337 -0
- data/Rakefile +9 -0
- data/hensel.gemspec +29 -0
- data/lib/hensel/builder/item.rb +43 -0
- data/lib/hensel/builder/node.rb +53 -0
- data/lib/hensel/builder.rb +105 -0
- data/lib/hensel/configuration.rb +37 -0
- data/lib/hensel/filters.rb +47 -0
- data/lib/hensel/helpers/tag_helpers.rb +113 -0
- data/lib/hensel/helpers.rb +9 -0
- data/lib/hensel/version.rb +3 -0
- data/lib/hensel.rb +29 -0
- data/spec/builder_spec.rb +202 -0
- data/spec/configuration_spec.rb +18 -0
- data/spec/helpers_spec.rb +8 -0
- data/spec/item_spec.rb +40 -0
- data/spec/node_spec.rb +75 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/tag_helpers_spec.rb +41 -0
- metadata +188 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
module Hensel
|
2
|
+
class Configuration
|
3
|
+
# Define the accessor as boolean method
|
4
|
+
def self.attr_boolean_accessor(*keys)
|
5
|
+
keys.each do |key|
|
6
|
+
attr_accessor key
|
7
|
+
define_method("#{key}?"){ !!__send__(key) }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_boolean_accessor :bootstrap
|
12
|
+
attr_boolean_accessor :escape_html
|
13
|
+
attr_boolean_accessor :indentation
|
14
|
+
attr_boolean_accessor :last_item_link
|
15
|
+
|
16
|
+
attr_accessor :attr_wrapper, :whitespace, :parent_element, :richsnippet
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@bootstrap = false
|
20
|
+
@escape_html = true
|
21
|
+
@indentation = true
|
22
|
+
@last_item_link = false
|
23
|
+
@richsnippet = :microdata # [:microdata, :rdfa, :nil]
|
24
|
+
@attr_wrapper = "'"
|
25
|
+
@whitespace = " "
|
26
|
+
@parent_element = :ul
|
27
|
+
end
|
28
|
+
|
29
|
+
def [](key)
|
30
|
+
instance_variable_get(:"@#{key}")
|
31
|
+
end
|
32
|
+
|
33
|
+
def []=(key, value)
|
34
|
+
instance_variable_set(:"@#{key}", value)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Hensel
|
2
|
+
module Filters
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def register(key, name, block)
|
6
|
+
filters[key][name] = block
|
7
|
+
end
|
8
|
+
|
9
|
+
def filters
|
10
|
+
@filters ||= { filters: {}, richsnippets: {} }
|
11
|
+
end
|
12
|
+
|
13
|
+
register :richsnippets, :microdata, ->(this){
|
14
|
+
return if this.renderer
|
15
|
+
this.renderer = ->(that){
|
16
|
+
node(:li, options.merge(itemtype: "http://data-vocabulary.org/Breadcrumb", itemscope: true)) do
|
17
|
+
if !Hensel.configuration.last_item_link? && item.last?
|
18
|
+
node(:span, itemprop: :title){ item.text }
|
19
|
+
else
|
20
|
+
node(:a, href: item.url, itemprop: :url) do
|
21
|
+
node(:span, itemprop: :title){ item.text }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
register :richsnippets, :rdfa, ->(this){
|
29
|
+
return if this.renderer
|
30
|
+
append_attribute(:"xmlns:v", "http://rdf.data-vocabulary.org/#", this.parent.options)
|
31
|
+
this.renderer = ->(that){
|
32
|
+
node(:li, options.merge(typeof: "v:Breadcrumb")) do
|
33
|
+
if !Hensel.configuration.last_item_link? && item.last?
|
34
|
+
node(:span, property: "v:title"){ item.text }
|
35
|
+
else
|
36
|
+
node(:a, href: item.url, rel: "v:url", property: "v:title"){ item.text }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
register :filters, :bootstrap, ->(this){
|
43
|
+
append_attribute(:class, "breadcrumb", options)
|
44
|
+
append_attribute(:class, "active", items.last.options)
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Hensel
|
2
|
+
module Helpers
|
3
|
+
module TagHelpers
|
4
|
+
ESCAPE_VALUES = {
|
5
|
+
"'" => "'",
|
6
|
+
"&" => "&",
|
7
|
+
"<" => "<",
|
8
|
+
">" => ">",
|
9
|
+
'"' => """
|
10
|
+
}
|
11
|
+
ESCAPE_REGEXP = Regexp.union(*ESCAPE_VALUES.keys)
|
12
|
+
BOOLEAN_ATTRIBUTES = [
|
13
|
+
:autoplay,
|
14
|
+
:autofocus,
|
15
|
+
:formnovalidate,
|
16
|
+
:checked,
|
17
|
+
:disabled,
|
18
|
+
:hidden,
|
19
|
+
:loop,
|
20
|
+
:multiple,
|
21
|
+
:muted,
|
22
|
+
:readonly,
|
23
|
+
:required,
|
24
|
+
:selected,
|
25
|
+
:declare,
|
26
|
+
:defer,
|
27
|
+
:ismap,
|
28
|
+
:itemscope,
|
29
|
+
:noresize,
|
30
|
+
:novalidate
|
31
|
+
]
|
32
|
+
|
33
|
+
def content_tag(name, content = nil, **options, &block)
|
34
|
+
base = tag(name, options)
|
35
|
+
content = instance_eval(&block) if !content && block_given?
|
36
|
+
if indentation?
|
37
|
+
indent = options.fetch(:indent, 0)
|
38
|
+
base << "\n"
|
39
|
+
base << (content.strip.start_with?("<") ? content : (whitespace(indent + 1)) + content)
|
40
|
+
base << "\n"
|
41
|
+
base << "#{whitespace(indent)}</#{name}>"
|
42
|
+
else
|
43
|
+
"#{base}#{content}</#{name}>"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def tag(name, indent: 0, **options)
|
48
|
+
"#{indentation? ? whitespace(indent) : ""}<#{name}#{tag_attributes(options)}>"
|
49
|
+
end
|
50
|
+
|
51
|
+
def append_attribute(attribute, value, hash)
|
52
|
+
if hash[attribute]
|
53
|
+
unless hash[attribute].to_s.split(" ").include?(value)
|
54
|
+
hash[attribute] = (Array(hash[attribute]) << value) * " "
|
55
|
+
end
|
56
|
+
else
|
57
|
+
hash.merge!(attribute => value)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def tag_attributes(options)
|
64
|
+
return '' if options.nil?
|
65
|
+
attributes = options.map do |k, v|
|
66
|
+
next if v.nil? || v == false
|
67
|
+
if v.is_a?(Hash)
|
68
|
+
nested_values(k, v)
|
69
|
+
elsif BOOLEAN_ATTRIBUTES.include?(k)
|
70
|
+
%(#{k}=#{attr_wrapper}#{k}#{attr_wrapper})
|
71
|
+
else
|
72
|
+
%(#{k}=#{attr_wrapper}#{h(v)}#{attr_wrapper})
|
73
|
+
end
|
74
|
+
end.compact
|
75
|
+
attributes.empty? ? '' : " #{attributes * ' '}"
|
76
|
+
end
|
77
|
+
|
78
|
+
def nested_values(attribute, hash)
|
79
|
+
hash.map do |k, v|
|
80
|
+
if v.is_a?(Hash)
|
81
|
+
nested_values("#{attribute}-#{k.to_s}", v)
|
82
|
+
else
|
83
|
+
%(#{attribute}-#{k.to_s}=#{attr_wrapper}#{h(v)}#{attr_wrapper}")
|
84
|
+
end
|
85
|
+
end * ' '
|
86
|
+
end
|
87
|
+
|
88
|
+
def h(string)
|
89
|
+
Hensel.configuration.escape_html? ? escape_html(string) : string
|
90
|
+
end
|
91
|
+
|
92
|
+
def escape_html(string)
|
93
|
+
string.to_s.gsub(ESCAPE_REGEXP) { |c| ESCAPE_VALUES[c] }
|
94
|
+
end
|
95
|
+
|
96
|
+
def attr_wrapper
|
97
|
+
@attr_wrapper ||= Hensel.configuration.attr_wrapper
|
98
|
+
end
|
99
|
+
|
100
|
+
def whitespace(indent = nil)
|
101
|
+
if indent.nil?
|
102
|
+
@whitespace ||= Hensel.configuration.whitespace
|
103
|
+
else
|
104
|
+
whitespace * indent
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def indentation?
|
109
|
+
@indentation ||= Hensel.configuration.indentation?
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
data/lib/hensel.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require "hensel/version"
|
2
|
+
require "hensel/configuration"
|
3
|
+
require "hensel/builder"
|
4
|
+
require "hensel/helpers"
|
5
|
+
|
6
|
+
module Hensel
|
7
|
+
extend self
|
8
|
+
|
9
|
+
# Yields Hensel configuration block
|
10
|
+
# @example
|
11
|
+
# Hensel.configure do |config|
|
12
|
+
# config.attr_wrapper = '"'
|
13
|
+
# end
|
14
|
+
# @see Hensel::Configuration
|
15
|
+
def configure
|
16
|
+
yield configuration
|
17
|
+
configuration
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns Hensel configuration
|
21
|
+
def configuration
|
22
|
+
@configuration ||= Configuration.new
|
23
|
+
end
|
24
|
+
|
25
|
+
# Resets Hensel configuration
|
26
|
+
def reset_configuration!
|
27
|
+
@configuration = nil
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hensel::Builder do
|
4
|
+
let(:builder){ Hensel::Builder.new }
|
5
|
+
|
6
|
+
describe "#add" do
|
7
|
+
context "with basic usage" do
|
8
|
+
it "returns an instance of Builder::Item" do
|
9
|
+
expect(builder.add("Index", "/")).to be_an_instance_of(Hensel::Builder::Item)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "adds an instance of Builder::Item to items" do
|
13
|
+
builder.add("Boom", "/boom")
|
14
|
+
expect(builder.items.last).to be_an_instance_of(Hensel::Builder::Item)
|
15
|
+
expect(builder.items.last.text).to eq("Boom")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "allows to set options as the attribute of Builder::Item" do
|
19
|
+
builder.add("Foo", "/foo", class: "optional-class", id: "foo-id")
|
20
|
+
expect(builder.render).to have_tag(:li, class: "optional-class", id: "foo-id")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "with optional usage" do
|
25
|
+
it "returns an instance of Builder::Item" do
|
26
|
+
expect(builder.add(text: "Index", url: "/")).to be_an_instance_of(Hensel::Builder::Item)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "adds an instance of Builder::Item to items" do
|
30
|
+
builder.add(text: "Boom", url: "/boom")
|
31
|
+
expect(builder.items.last).to be_an_instance_of(Hensel::Builder::Item)
|
32
|
+
expect(builder.items.last.text).to eq("Boom")
|
33
|
+
end
|
34
|
+
|
35
|
+
it "allows to set options as the attribute of Builder::Item" do
|
36
|
+
builder.add(text: "Foo", url: "/foo", class: "optional-class", id: "foo-id")
|
37
|
+
expect(builder.render).to have_tag(:li, class: "optional-class", id: "foo-id")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#remove" do
|
43
|
+
context "with text" do
|
44
|
+
it "removes the item by text from items" do
|
45
|
+
tested = builder.add("Tested", "/tested")
|
46
|
+
builder.add("Sample", "/sample")
|
47
|
+
builder.remove("Tested")
|
48
|
+
expect(builder.items.any?{|x| x.text == tested.text }).to be_false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "with block" do
|
53
|
+
it "removes the item by result of block from items" do
|
54
|
+
tested = builder.add("Tested", "/tested")
|
55
|
+
sample = builder.add("Sample", "/sample")
|
56
|
+
builder.remove{|x| x.url == "/sample" }
|
57
|
+
expect(builder.items.any?{|x| x.text == tested.text }).to be_true
|
58
|
+
expect(builder.items.any?{|x| x.text == sample.text }).to be_false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#render" do
|
64
|
+
context "with bootstrap" do
|
65
|
+
before { Hensel.configuration.bootstrap = true }
|
66
|
+
before(:each) do
|
67
|
+
builder.add("Index", "/", class: "dummy-class")
|
68
|
+
builder.add("Dummy", "/dummy", class: "dummy-class")
|
69
|
+
end
|
70
|
+
subject { builder.render }
|
71
|
+
|
72
|
+
it "respects the bootstrap style" do
|
73
|
+
expect(subject).to have_tag(:ul, with: { class: "breadcrumb" }) do
|
74
|
+
with_tag "li:last-child", class: "active"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
it "respects the microdata rule" do
|
79
|
+
expect(subject).to have_tag(:ul, with: { class: "breadcrumb" }) do
|
80
|
+
with_tag :li, with: { itemtype: "http://data-vocabulary.org/Breadcrumb", itemscope: "itemscope" }
|
81
|
+
with_tag "li > a", with: { href: "/", itemprop: "url" }
|
82
|
+
with_tag "li > a > span", with: { itemprop: "title" }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
after { Hensel.configuration.bootstrap = false }
|
87
|
+
end
|
88
|
+
|
89
|
+
context "with bootstrap and without richsnippet" do
|
90
|
+
before(:all) do
|
91
|
+
Hensel.configuration.richsnippet = nil
|
92
|
+
Hensel.configuration.bootstrap = true
|
93
|
+
end
|
94
|
+
before(:each) do
|
95
|
+
builder.add("Index", "/", class: "dummy-class")
|
96
|
+
builder.add("Dummy", "/dummy", class: "dummy-class")
|
97
|
+
end
|
98
|
+
subject { builder.render }
|
99
|
+
|
100
|
+
it "respects the bootstrap style" do
|
101
|
+
expect(subject).to have_tag(:ul, with: { class: "breadcrumb" }) do
|
102
|
+
with_tag "li:last-child", class: "active"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
it "does not respect the microdata rule" do
|
107
|
+
expect(subject).not_to have_tag(:li, with: { itemtype: "http://data-vocabulary.org/Breadcrumb", itemscope: "itemscope" })
|
108
|
+
expect(subject).not_to have_tag("li > a", text: "Index", with: { href: "/", itemprop: "url" })
|
109
|
+
expect(subject).not_to have_tag("li > a", text: "Dummy", with: { href: "/dummy", itemprop: "url" })
|
110
|
+
end
|
111
|
+
|
112
|
+
after { Hensel.configuration.bootstrap = false }
|
113
|
+
end
|
114
|
+
|
115
|
+
context "without bootstrap" do
|
116
|
+
before(:each) do
|
117
|
+
builder.add("Index", "/", class: "dummy-class")
|
118
|
+
builder.add("Dummy", "/dummy", class: "dummy-class")
|
119
|
+
end
|
120
|
+
subject { builder.render }
|
121
|
+
it "does not respect the bootstrap style" do
|
122
|
+
expect(subject).not_to have_tag(:ul, with: { class: "breadcrumb" })
|
123
|
+
expect(subject).not_to have_tag("li:last-child", with: { class: "active" })
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context "attr_wrapper" do
|
128
|
+
before { Hensel.configuration.attr_wrapper = '"' }
|
129
|
+
before(:each){ builder.add("Index", "/", class: "dummy-class") }
|
130
|
+
subject { builder.render }
|
131
|
+
it { should_not match(/'/) }
|
132
|
+
it { should match(/"/) }
|
133
|
+
after { Hensel.reset_configuration! }
|
134
|
+
end
|
135
|
+
|
136
|
+
context "whitespace" do
|
137
|
+
before do
|
138
|
+
Hensel.configuration.richsnippet = nil
|
139
|
+
Hensel.configuration.whitespace = " "
|
140
|
+
end
|
141
|
+
before(:each){ builder.add("Index", "/", class: "dummy-class") }
|
142
|
+
let(:fixture){
|
143
|
+
<<-FIXTURE.chomp
|
144
|
+
<ul>
|
145
|
+
<li class='dummy-class'>
|
146
|
+
<span>
|
147
|
+
Index
|
148
|
+
</span>
|
149
|
+
</li>
|
150
|
+
</ul>
|
151
|
+
FIXTURE
|
152
|
+
}
|
153
|
+
subject { builder.render }
|
154
|
+
it { should eq(fixture) }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe "customized breadcrumbs" do
|
159
|
+
context "use customizable renderer instead of default renderer" do
|
160
|
+
before do
|
161
|
+
builder.add("Index", "/")
|
162
|
+
builder.add("Dummy", "/dummy")
|
163
|
+
end
|
164
|
+
subject do
|
165
|
+
builder.render do
|
166
|
+
node(:custom, href: item.url){ item.text }
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
it "can create the html of breadcrumb correctly" do
|
171
|
+
builder.add("Index", "/")
|
172
|
+
actual_html = builder.render do
|
173
|
+
node(:span){ "text: #{item.text}, url: #{item.url}" }
|
174
|
+
end
|
175
|
+
expect(subject).to have_tag(:ul) do
|
176
|
+
with_tag :custom, href: "/"
|
177
|
+
with_tag :custom, href: "/dummy"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
context "parent element is not ul but div" do
|
183
|
+
before do
|
184
|
+
Hensel.configuration.parent_element = :div
|
185
|
+
Hensel.configuration.bootstrap = false
|
186
|
+
builder.add("Index", "/")
|
187
|
+
end
|
188
|
+
subject { builder.render }
|
189
|
+
|
190
|
+
it "can set parent element for customizable breadcrumb" do
|
191
|
+
expect(subject).to have_tag(:div, { class: "breadcrumb" }) do
|
192
|
+
with_tag "li", class: "active"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
after do
|
197
|
+
Hensel.configuration.parent_element = :ul
|
198
|
+
Hensel.configuration.bootstrap = false
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hensel::Configuration do
|
4
|
+
describe ".attr_boolean_accessor" do
|
5
|
+
let(:configuration){ Hensel::Configuration.new }
|
6
|
+
it "can define an accessor and boolean methods" do
|
7
|
+
expect(configuration.respond_to?(:sample)).to be_false
|
8
|
+
Hensel::Configuration.attr_boolean_accessor :sample
|
9
|
+
expect(configuration.respond_to?(:sample)).to be_true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#[]" do
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#[]=" do
|
17
|
+
end
|
18
|
+
end
|
data/spec/item_spec.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hensel::Builder::Item do
|
4
|
+
let(:builder){ Hensel::Builder.new }
|
5
|
+
|
6
|
+
context "with escape_html" do
|
7
|
+
before(:all) do
|
8
|
+
Hensel.configuration.escape_html = true
|
9
|
+
Hensel.configuration.indentation = false
|
10
|
+
end
|
11
|
+
before(:each){ builder.add('\'&"<>', '/') }
|
12
|
+
subject { builder.items.last.text }
|
13
|
+
it { should eq(''&"<>') }
|
14
|
+
end
|
15
|
+
|
16
|
+
context "without escape_html" do
|
17
|
+
before(:all) do
|
18
|
+
Hensel.configuration.escape_html = false
|
19
|
+
Hensel.configuration.indentation = false
|
20
|
+
end
|
21
|
+
before(:each){ builder.add('\'&"<>', '/') }
|
22
|
+
subject { builder.items.last.text }
|
23
|
+
it { should eq('\'&"<>') }
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#render" do
|
27
|
+
let(:item) { Hensel::Builder::Item.new("index", "/") }
|
28
|
+
subject { item.render }
|
29
|
+
context "basic usage" do
|
30
|
+
it { should have_tag(:li){ with_tag(:a, href: "/") } }
|
31
|
+
end
|
32
|
+
|
33
|
+
context "with customized renderer" do
|
34
|
+
it "can be set to renderer" do
|
35
|
+
item.renderer = ->(this){ node(:custom, data: "sample"){ item.text } }
|
36
|
+
should have_tag(:custom, text: "index", data: "sample")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/spec/node_spec.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hensel::Builder::Node do
|
4
|
+
let(:item) { Hensel::Builder::Item.new("index", "/") }
|
5
|
+
before { Hensel.configuration.indentation = false }
|
6
|
+
|
7
|
+
describe "#render" do
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "#node" do
|
11
|
+
context "with block" do
|
12
|
+
subject { item.render }
|
13
|
+
it "can build html correctly" do
|
14
|
+
item.renderer = ->(this){ node(:div, class: "hey"){ "sample text" }}
|
15
|
+
should have_tag(:div, text: "sample text", class: "hey")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "can build html correctly even when node is nested" do
|
19
|
+
item.renderer = ->(this){ node(:div, class: "hey"){ node(:span){ "nested text" } }}
|
20
|
+
should have_tag(:div, class: "hey") do
|
21
|
+
with_tag(:span, text: "nested text")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should support same hierarchy elements" do
|
26
|
+
item.renderer = ->(this){
|
27
|
+
node(:div) do
|
28
|
+
node(:span){ "one1" }
|
29
|
+
node(:span){ "one2" }
|
30
|
+
end
|
31
|
+
}
|
32
|
+
should have_tag(:div) do
|
33
|
+
with_tag(:span, text: "one1")
|
34
|
+
with_tag(:span, text: "one2")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "without block" do
|
40
|
+
subject { item.render }
|
41
|
+
it "can build html correctly" do
|
42
|
+
item.renderer = ->(this){ node(:div, "sample text", class: "hey") }
|
43
|
+
should have_tag(:div, content: "sample text", class: "hey")
|
44
|
+
end
|
45
|
+
|
46
|
+
it "can build html correctly even when node is nested" do
|
47
|
+
item.renderer = ->(this){ node(:div, class: "hey"){ node(:span, "nested text") }}
|
48
|
+
should have_tag(:div, class: "hey") do
|
49
|
+
with_tag(:span, content: "nested text")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should support same hierarchy elements" do
|
54
|
+
item.renderer = ->(this){
|
55
|
+
node(:div) do
|
56
|
+
node(:span, "one1")
|
57
|
+
node(:span, "one2")
|
58
|
+
end
|
59
|
+
}
|
60
|
+
should have_tag(:div) do
|
61
|
+
with_tag(:span, text: "one1")
|
62
|
+
with_tag(:span, text: "one2")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#item" do
|
69
|
+
subject { item.render }
|
70
|
+
it "can be referred from block" do
|
71
|
+
item.renderer = ->(this){ node(:div, item.text) }
|
72
|
+
should have_tag(:div, text: "index")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hensel::Helpers::TagHelpers do
|
4
|
+
let(:helpers){ Object.new }
|
5
|
+
before { helpers.extend Hensel::Helpers::TagHelpers }
|
6
|
+
|
7
|
+
describe "#contegt_tag" do
|
8
|
+
context "with block" do
|
9
|
+
subject { helpers.content_tag(:div, class: "dummy-class"){ "hello" }}
|
10
|
+
it { should have_tag(:div, content: "hello", class: "dummy-class") }
|
11
|
+
end
|
12
|
+
|
13
|
+
context "without block" do
|
14
|
+
subject { helpers.content_tag(:div, "hello", class: "dummy-class")}
|
15
|
+
it { should have_tag(:div, content: "hello", class: "dummy-class") }
|
16
|
+
end
|
17
|
+
|
18
|
+
context "with indentation" do
|
19
|
+
before { Hensel.configuration.indentation = true }
|
20
|
+
subject { helpers.content_tag(:div, "hello", indent: 2, class: "dummy-class")}
|
21
|
+
it { expect(subject.start_with?(" ")).to be_true }
|
22
|
+
end
|
23
|
+
|
24
|
+
context "without indentation" do
|
25
|
+
before { Hensel.configuration.indentation = false }
|
26
|
+
subject { helpers.content_tag(:div, "hello", indent: 2, class: "dummy-class")}
|
27
|
+
it { expect(subject.start_with?("<div")).to be_true }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#tag" do
|
32
|
+
subject { helpers.tag(:img, class: "sample-image", src: "sample.jpg", alt: "sample image") }
|
33
|
+
it { should have_tag(:img, class: "sample-image", src: "sample.jpg", alt: "sample image") }
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#append_attribute" do
|
37
|
+
subject { Hash.new }
|
38
|
+
before { helpers.append_attribute(:key, :value, subject) }
|
39
|
+
it { subject[:key].should eq(:value) }
|
40
|
+
end
|
41
|
+
end
|