nanoc-toolbox 0.0.7 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +13 -2
- data/Gemfile +3 -0
- data/Gemfile.lock +36 -13
- data/Guardfile +9 -0
- data/README.md +22 -5
- data/lib/nanoc/toolbox/filters/add_sections.rb +2 -2
- data/lib/nanoc/toolbox/filters/html_tidy.rb +5 -5
- data/lib/nanoc/toolbox/filters/js_minify.rb +2 -2
- data/lib/nanoc/toolbox/filters.rb +2 -1
- data/lib/nanoc/toolbox/helpers/blogging_extra.rb +105 -0
- data/lib/nanoc/toolbox/helpers/disqus.rb +47 -0
- data/lib/nanoc/toolbox/helpers/google_analytics.rb +7 -5
- data/lib/nanoc/toolbox/helpers/gravatar.rb +2 -2
- data/lib/nanoc/toolbox/helpers/html_tag.rb +13 -8
- data/lib/nanoc/toolbox/helpers/navigation.rb +61 -67
- data/lib/nanoc/toolbox/helpers/tagging_extra.rb +135 -0
- data/lib/nanoc/toolbox/helpers.rb +4 -1
- data/lib/nanoc/toolbox/version.rb +2 -2
- data/nanoc-toolbox.gemspec +15 -14
- data/spec/filters/add_sections_spec.rb +7 -29
- data/spec/filters/js_minify_spec.rb +14 -0
- data/spec/helpers/blogging_extra_spec.rb +134 -0
- data/spec/helpers/disqus_spec.rb +47 -0
- data/spec/helpers/google_analytics_spec.rb +27 -7
- data/spec/helpers/gravatar_spec.rb +25 -25
- data/spec/helpers/html_tag_spec.rb +8 -1
- data/spec/helpers/navigation_spec.rb +110 -49
- data/spec/helpers/tagging_extra_spec.rb +155 -0
- data/spec/spec_helper.rb +5 -1
- metadata +109 -119
data/CHANGELOG.md
CHANGED
@@ -2,14 +2,25 @@
|
|
2
2
|
|
3
3
|
## developement
|
4
4
|
|
5
|
-
|
5
|
+
* CHANGE: Code refactoring, Bug Fixes and Better test coverage
|
6
|
+
* DELETE: Unused helpers
|
7
|
+
|
8
|
+
## Release 0.0.7
|
9
|
+
|
10
|
+
* NEW: TRAVIS CI
|
11
|
+
* FIXED: Compatibility Issue with 1.8.7
|
12
|
+
|
13
|
+
## Release 0.0.6
|
14
|
+
|
6
15
|
* NEW: Analytics Helper
|
7
16
|
* NEW: JS Minify Filter
|
8
17
|
|
9
18
|
## Release 0.0.5
|
19
|
+
|
10
20
|
* CHANGED: add the ability to filter by an attributes of an item on navigation_for
|
11
21
|
|
12
22
|
## Release 0.0.4
|
23
|
+
|
13
24
|
* NEW: add_section Filter
|
14
25
|
* CHANGED: bugfix on navigation helper
|
15
26
|
|
@@ -29,4 +40,4 @@
|
|
29
40
|
* NEW: Add Yardoc
|
30
41
|
|
31
42
|
## Release 0.0.1
|
32
|
-
* First Release
|
43
|
+
* First Release
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,23 +1,39 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
nanoc-toolbox (0.0.
|
5
|
-
jsmin (
|
6
|
-
nanoc (
|
7
|
-
nokogiri (
|
4
|
+
nanoc-toolbox (0.0.8)
|
5
|
+
jsmin (~> 1.0)
|
6
|
+
nanoc (~> 3.4)
|
7
|
+
nokogiri (~> 1.4)
|
8
8
|
|
9
9
|
GEM
|
10
10
|
remote: http://rubygems.org/
|
11
11
|
specs:
|
12
|
-
|
12
|
+
colored (1.2)
|
13
|
+
cri (2.3.0)
|
14
|
+
colored (>= 1.2)
|
13
15
|
diff-lcs (1.1.2)
|
16
|
+
ffi (1.0.11)
|
17
|
+
guard (1.2.3)
|
18
|
+
listen (>= 0.4.2)
|
19
|
+
thor (>= 0.14.6)
|
20
|
+
guard-rspec (1.1.0)
|
21
|
+
guard (>= 1.1)
|
14
22
|
jsmin (1.0.1)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
23
|
+
listen (0.4.7)
|
24
|
+
rb-fchange (~> 0.0.5)
|
25
|
+
rb-fsevent (~> 0.9.1)
|
26
|
+
rb-inotify (~> 0.8.8)
|
27
|
+
multi_json (1.3.6)
|
28
|
+
nanoc (3.4.0)
|
29
|
+
cri (~> 2.2)
|
30
|
+
nokogiri (1.5.5)
|
20
31
|
rake (0.9.2.2)
|
32
|
+
rb-fchange (0.0.5)
|
33
|
+
ffi
|
34
|
+
rb-fsevent (0.9.1)
|
35
|
+
rb-inotify (0.8.8)
|
36
|
+
ffi (>= 0.5.0)
|
21
37
|
rspec (2.4.0)
|
22
38
|
rspec-core (~> 2.4.0)
|
23
39
|
rspec-expectations (~> 2.4.0)
|
@@ -26,12 +42,19 @@ GEM
|
|
26
42
|
rspec-expectations (2.4.0)
|
27
43
|
diff-lcs (~> 1.1.2)
|
28
44
|
rspec-mocks (2.4.0)
|
45
|
+
simplecov (0.6.4)
|
46
|
+
multi_json (~> 1.0)
|
47
|
+
simplecov-html (~> 0.5.3)
|
48
|
+
simplecov-html (0.5.3)
|
49
|
+
thor (0.15.4)
|
29
50
|
|
30
51
|
PLATFORMS
|
31
52
|
ruby
|
32
53
|
|
33
54
|
DEPENDENCIES
|
34
|
-
bundler (
|
55
|
+
bundler (~> 1.1)
|
56
|
+
guard-rspec
|
35
57
|
nanoc-toolbox!
|
36
|
-
rake
|
37
|
-
rspec (
|
58
|
+
rake (~> 0.9)
|
59
|
+
rspec (~> 2.4)
|
60
|
+
simplecov
|
data/Guardfile
ADDED
data/README.md
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
# nanoc-toolbox
|
2
2
|
|
3
3
|
[![Build Status](https://secure.travis-ci.org/aadlani/nanoc-toolbox.png)](http://travis-ci.org/aadlani/nanoc-toolbox)
|
4
|
+
[![Dependency Status](https://gemnasium.com/aadlani/nanoc-toolbox.png)](https://gemnasium.com/aadlani/nanoc-toolbox)
|
5
|
+
[![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/aadlani/nanoc-toolbox)
|
4
6
|
|
5
7
|
## Presentation
|
6
8
|
|
7
9
|
The nanoc-toolbox is a collection of filters and helpers for the static site generator tool nanoc.
|
10
|
+
I have created this gem for my personal need on [my blog/resume website]( http://anouar.im ).
|
11
|
+
If you feel something's missing, feel free to contribute.
|
8
12
|
|
9
13
|
## Features
|
10
14
|
|
@@ -13,7 +17,10 @@ The nanoc-toolbox is a collection of filters and helpers for the static site gen
|
|
13
17
|
* **Navigation**: Menu, Breadcrumb, Table of contents,
|
14
18
|
* **Gravatar**: Avatar Using the Gravatar System
|
15
19
|
* **HtmlTag**: HTML Tag helper for other helpers
|
16
|
-
* **Google Analytics
|
20
|
+
* **Google Analytics**: Generate the JS code snipet for Analytics
|
21
|
+
* **Blogging Extra**: Add extra blog post behavior
|
22
|
+
* **Tagging Extra**: Add extra tagging behavior
|
23
|
+
* **Disqus**: Disqus comments helper
|
17
24
|
|
18
25
|
### Filters
|
19
26
|
|
@@ -24,7 +31,7 @@ The nanoc-toolbox is a collection of filters and helpers for the static site gen
|
|
24
31
|
## Requirements and compatibility
|
25
32
|
|
26
33
|
* **ruby >= 1.8.7**: This gem has been tested against ruby 1.8.7 and 1.9.2
|
27
|
-
* **
|
34
|
+
* **nanoc**: It requires obviously the Nanoc gem
|
28
35
|
* **Nokogiri**: For the HTML Tidy Filter
|
29
36
|
* **jsmin**: For the JS Minify Filter
|
30
37
|
|
@@ -32,7 +39,7 @@ The nanoc-toolbox is a collection of filters and helpers for the static site gen
|
|
32
39
|
|
33
40
|
To use the nanoc-toolbox, you have to start by installing the gem.
|
34
41
|
|
35
|
-
gem install
|
42
|
+
gem install nanoc-toolbox
|
36
43
|
|
37
44
|
Then require the project main file in your default.rb file in the lib directory of your nanoc project.
|
38
45
|
|
@@ -43,7 +50,7 @@ require "nanoc/toolbox"
|
|
43
50
|
And the last step is to include the required helper or filter, anywhere in the lib directory of your lib directory.
|
44
51
|
The usage wants you to put it in the default.rb or the helpers.rb file.
|
45
52
|
|
46
|
-
The following example shows a sample helpers_.rb file in the lib directory
|
53
|
+
The following example shows a sample `helpers_.rb` file in the lib directory
|
47
54
|
|
48
55
|
```ruby
|
49
56
|
# Default Helpers provided By Nanoc
|
@@ -55,11 +62,21 @@ include Nanoc::Toolbox::Helpers::Navigation
|
|
55
62
|
include Nanoc::Toolbox::Helpers::Gravatar
|
56
63
|
```
|
57
64
|
|
65
|
+
## Documentation
|
66
|
+
|
67
|
+
### API Documentation
|
68
|
+
|
69
|
+
The API documentation could be found on [RubyDoc.info](http://rubydoc.info/github/aadlani/nanoc-toolbox/frames)
|
70
|
+
|
71
|
+
### Usage Guides
|
72
|
+
|
73
|
+
Some usage guides could be found on the [GitHub wiki](https://github.com/aadlani/nanoc-toolbox/wiki)
|
74
|
+
|
58
75
|
## Acknowledgments
|
59
76
|
|
60
77
|
All the people in [this list](https://github.com/aadlani/nanoc-toolbox/contributors)
|
61
78
|
|
62
|
-
##
|
79
|
+
## Author
|
63
80
|
|
64
81
|
* Anouar ADLANI <anouar@adlani.com>
|
65
82
|
|
@@ -31,7 +31,7 @@ module Nanoc::Toolbox::Filters
|
|
31
31
|
# section
|
32
32
|
# p
|
33
33
|
# @author Denis Defreyne <denis.defreyne@stoneship.org>
|
34
|
-
class AddSections <
|
34
|
+
class AddSections < Nanoc::Filter
|
35
35
|
|
36
36
|
identifiers :add_sections
|
37
37
|
|
@@ -89,4 +89,4 @@ module Nanoc::Toolbox::Filters
|
|
89
89
|
doc.to_s
|
90
90
|
end
|
91
91
|
end
|
92
|
-
end
|
92
|
+
end
|
@@ -2,12 +2,12 @@ module Nanoc::Toolbox::Filters
|
|
2
2
|
# NANOC Filter for html output tidy
|
3
3
|
#
|
4
4
|
# @author Anouar ADLANI <anouar@adlani.com>
|
5
|
-
class HtmlTidy <
|
6
|
-
|
5
|
+
class HtmlTidy < Nanoc::Filter
|
6
|
+
|
7
7
|
identifier :html_tidy
|
8
|
-
|
8
|
+
|
9
9
|
# Tidy the HTML output
|
10
|
-
# Runs the content through Nokogiry document parser, and request the html output,
|
10
|
+
# Runs the content through Nokogiry document parser, and request the html output,
|
11
11
|
# which is tidied by default.
|
12
12
|
#
|
13
13
|
# @param [String] content The content to filter
|
@@ -18,4 +18,4 @@ module Nanoc::Toolbox::Filters
|
|
18
18
|
Nokogiri::HTML::Document.parse(content).to_html
|
19
19
|
end
|
20
20
|
end
|
21
|
-
end
|
21
|
+
end
|
@@ -5,11 +5,11 @@ module Nanoc::Toolbox::Filters
|
|
5
5
|
# using the JSMin gem
|
6
6
|
# @see http://rubygems.org/gems/jsmin
|
7
7
|
# @author Anouar ADLANI <anouar@adlani.com>
|
8
|
-
class JsMinify <
|
8
|
+
class JsMinify < Nanoc::Filter
|
9
9
|
identifier :js_minify
|
10
10
|
|
11
11
|
def run(content, args = {})
|
12
12
|
JSMin.minify(content)
|
13
13
|
end
|
14
14
|
end
|
15
|
-
end
|
15
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Nanoc::Toolbox::Helpers
|
2
|
+
# NANOC Helper for giving items extra blog post behavior.
|
3
|
+
#
|
4
|
+
# This module contains features to the default Nanoc::Helpers::Blogging
|
5
|
+
# module, like tagging, slug, etc...
|
6
|
+
# @author Anouar ADLANI
|
7
|
+
module BloggingExtra
|
8
|
+
include Nanoc::Helpers::Blogging
|
9
|
+
|
10
|
+
# Enable blog post behavior on all the items located in the post folder(s)
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# # In your config.yaml
|
14
|
+
# # post_dirs: [ 'posts' ]
|
15
|
+
#
|
16
|
+
# # In your Rules file
|
17
|
+
# preprocess do
|
18
|
+
# add_post_attributes
|
19
|
+
# end
|
20
|
+
def add_post_attributes
|
21
|
+
@config[:post_dirs] ||= ['_posts', '_articles']
|
22
|
+
items.each do |item|
|
23
|
+
# check the item's parent directory against the post_dirs
|
24
|
+
if (item[:filename] && @config[:post_dirs].include?(File.dirname(item[:filename]).split('/').last))
|
25
|
+
act_as_post(item)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Enable blog post behavior on an item
|
31
|
+
#
|
32
|
+
# @param [Nanoc::Item] item the item that will act as a post
|
33
|
+
# @return [Nanoc::Item] the adapted item
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# items.each do |item|
|
37
|
+
# act_as_post(item)
|
38
|
+
# end
|
39
|
+
def act_as_post(item)
|
40
|
+
fname = slug_for(item)
|
41
|
+
|
42
|
+
if fname =~ /^\d+-\d+-\d+-/
|
43
|
+
# get the date from the filename
|
44
|
+
item[:year], item[:month], item[:day] = fname.split("-", 4)[0..2]
|
45
|
+
|
46
|
+
# Set creation date param from the values in the filename
|
47
|
+
item[:created_at] = Time.local(item[:year], item[:month], item[:day])
|
48
|
+
|
49
|
+
# Strip the date from the filename
|
50
|
+
item[:slug] = fname.sub(/^\d+-\d+-\d+-/,'')
|
51
|
+
else
|
52
|
+
item[:created_at] ||= Time.now
|
53
|
+
item[:created_at] = attribute_to_time(item[:created_at])
|
54
|
+
|
55
|
+
# get the date from
|
56
|
+
item[:year] = item[:created_at].year
|
57
|
+
item[:month] = item[:created_at].month
|
58
|
+
item[:day] = item[:created_at].day
|
59
|
+
end
|
60
|
+
|
61
|
+
# Add additional meta data
|
62
|
+
item[:kind] = 'article'
|
63
|
+
|
64
|
+
# Enable comments unless specifically turned off
|
65
|
+
item[:comments] = true unless item[:comments] === false
|
66
|
+
item
|
67
|
+
end
|
68
|
+
|
69
|
+
# Get an item's slug if it has one, or use its filename sans extension
|
70
|
+
#
|
71
|
+
# @param [Nanoc::Item] item the item that will act as a post
|
72
|
+
# @return [String] either the slug specified in the item or generate one
|
73
|
+
#
|
74
|
+
# @example
|
75
|
+
# # item[:filename] = "abc def geh.html"
|
76
|
+
# slug_for(item) # => "abc-def-geh"
|
77
|
+
def slug_for(item)
|
78
|
+
return item[:slug] if item[:slug]
|
79
|
+
|
80
|
+
item[:slug] = File.basename(item[:filename], File.extname(item[:filename]))
|
81
|
+
item[:slug].gsub!(/\s+/, '-')
|
82
|
+
item[:slug]
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns n number of recent posts, optionally omits the current one
|
86
|
+
#
|
87
|
+
# @param [Integer] count the number of item to return
|
88
|
+
# @param [Nanoc::Item] current_item the current item to exclude from the list
|
89
|
+
#
|
90
|
+
# #return [Array<Nanoc::Item>] an array of recent items
|
91
|
+
def recent_posts(count=5, current_item=nil)
|
92
|
+
(sorted_articles - [current_item])[0, count]
|
93
|
+
end
|
94
|
+
|
95
|
+
# Retreive the list of existing articles grouped by years and months
|
96
|
+
#
|
97
|
+
# @return [Hash] Items grouped in a hash
|
98
|
+
# @example
|
99
|
+
# posts_by_date # => { 2011 => { 12 => [item0, item1], 3 => [item0, item2]}, 2010 => {12 => [...]}}
|
100
|
+
def posts_by_date
|
101
|
+
Hash[sorted_articles.group_by{|item| item[:year]}.map{ |y, items|
|
102
|
+
[y, items.group_by{|i| i[:month]}]}]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'nanoc/toolbox/helpers/blogging_extra'
|
2
|
+
|
3
|
+
module Nanoc::Toolbox::Helpers
|
4
|
+
|
5
|
+
# NANOC Helper for Disqus comments
|
6
|
+
#
|
7
|
+
# This module contains functions for ...
|
8
|
+
#
|
9
|
+
# @see http://disqus.com/
|
10
|
+
module Disqus
|
11
|
+
include Nanoc::Toolbox::Helpers::HtmlTag
|
12
|
+
include Nanoc::Toolbox::Helpers::BloggingExtra
|
13
|
+
|
14
|
+
# Creates an id out of the page slug that disqus uses for an id
|
15
|
+
def disqus_id_for(item, options={})
|
16
|
+
id = ''
|
17
|
+
id += options[:disqus_id_prefix] if options[:disqus_id_prefix]
|
18
|
+
id += slug_for(item)
|
19
|
+
id.gsub(/-/, '_')
|
20
|
+
end
|
21
|
+
|
22
|
+
# @see http://help.disqus.com/customer/portal/articles/472098-javascript-configuration-variables
|
23
|
+
def disqus_js_snippet(variables={})
|
24
|
+
configuration_variables = ""
|
25
|
+
|
26
|
+
variables.each do |k, v|
|
27
|
+
configuration_variables += "var disqus_#{k} = '#{v}';\n" unless v.nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
js_string = <<-EOS
|
31
|
+
#{configuration_variables}
|
32
|
+
(function() {
|
33
|
+
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
|
34
|
+
dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js';
|
35
|
+
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
|
36
|
+
})();
|
37
|
+
EOS
|
38
|
+
|
39
|
+
content_tag('script', js_string, { :type => 'text/javascript' })
|
40
|
+
end
|
41
|
+
|
42
|
+
def disqus_nojs_snippet(message=nil)
|
43
|
+
message ||= "Please enable JavaScript to view the <a href=\"http://disqus.com/?ref_noscript\">comments powered by Disqus.</a>"
|
44
|
+
content_tag('noscript', message)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -12,10 +12,13 @@ module Nanoc::Toolbox::Helpers
|
|
12
12
|
module GoogleAnalytics
|
13
13
|
include Nanoc::Toolbox::Helpers::HtmlTag
|
14
14
|
|
15
|
+
# Return the javascript code snipet to use in your layout or views
|
16
|
+
#
|
17
|
+
# @param [String] ga_tracking_code the Google Analytics Tracking Code
|
18
|
+
# @return [String] the script tag to place in your layout
|
15
19
|
def ga_tracking_snippet(ga_tracking_code=nil)
|
16
|
-
ga_tracking_code ||= @
|
20
|
+
ga_tracking_code ||= @config[:ga_tracking_code] || "UA-xxxxxx-x"
|
17
21
|
js = <<-EOS
|
18
|
-
|
19
22
|
var _gaq = _gaq || [];
|
20
23
|
_gaq.push(['_setAccount', '#{ga_tracking_code}']);
|
21
24
|
_gaq.push(['_trackPageview']);
|
@@ -23,11 +26,10 @@ module Nanoc::Toolbox::Helpers
|
|
23
26
|
(function() {
|
24
27
|
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
25
28
|
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
26
|
-
var s
|
29
|
+
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
27
30
|
})();
|
28
|
-
|
29
31
|
EOS
|
30
32
|
content_tag('script', js, { :type => 'text/javascript' })
|
31
33
|
end
|
32
34
|
end
|
33
|
-
end
|
35
|
+
end
|
@@ -27,8 +27,8 @@ module Nanoc::Toolbox::Helpers
|
|
27
27
|
SIZE = 1..512
|
28
28
|
|
29
29
|
# Available options that could be configure
|
30
|
-
HELPER_OPTION
|
31
|
-
GRAVATAR_OPTIONS
|
30
|
+
HELPER_OPTION = [:ext, :secure]
|
31
|
+
GRAVATAR_OPTIONS = [:default_icon, :size, :rating]
|
32
32
|
AVAILABLE_OPTIONS = HELPER_OPTION + GRAVATAR_OPTIONS
|
33
33
|
|
34
34
|
# Default values set to the options
|
@@ -8,15 +8,15 @@ module Nanoc::Toolbox::Helpers
|
|
8
8
|
module HtmlTag
|
9
9
|
# Simple tag
|
10
10
|
#
|
11
|
-
# @
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
# tag("hr", class => "thin", true)
|
16
|
-
# # => <br class="thin">
|
11
|
+
# @param [String] name the tag name
|
12
|
+
# @param [Hash] options options to set to the html element
|
13
|
+
# @param [Boolean] open flag to set if the tag is self closing or not
|
14
|
+
# @return [String] the requested tag string
|
17
15
|
#
|
18
|
-
#
|
19
|
-
#
|
16
|
+
# @example
|
17
|
+
# tag("br") # => <br />
|
18
|
+
# tag("hr", class => "thin", true) # => <br class="thin">
|
19
|
+
# tag("input", :type => 'text') # => <input type="text" />
|
20
20
|
#
|
21
21
|
def tag(name, options={}, open=false)
|
22
22
|
"<#{name}#{tag_options(options) if options}#{open ? ">" : " />"}"
|
@@ -24,6 +24,11 @@ module Nanoc::Toolbox::Helpers
|
|
24
24
|
|
25
25
|
# Content tag
|
26
26
|
#
|
27
|
+
# @param [String] name the tag name
|
28
|
+
# @param [Hash] options options to set to the html element
|
29
|
+
# @param [Boolean] open flag to set if the tag is self closing or not
|
30
|
+
# @return [String] the requested tag string
|
31
|
+
#
|
27
32
|
# @example
|
28
33
|
# content_tag(:p, "Hello world!")
|
29
34
|
# # => <p>Hello world!</p>
|