repubmark 0.0.0
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.
- checksums.yaml +7 -0
- data/.rubocop.yml +46 -0
- data/.rubocop_todo.yml +129 -0
- data/.yardopts +4 -0
- data/LICENSE +21 -0
- data/README.md +21 -0
- data/Rakefile +60 -0
- data/examples/config.yml +71 -0
- data/examples/full.chapters.json +22 -0
- data/examples/full.gmi +44 -0
- data/examples/full.html +114 -0
- data/examples/full.repub +91 -0
- data/examples/full.summary.txt +2 -0
- data/examples/hello.chapters.json +3 -0
- data/examples/hello.gmi +1 -0
- data/examples/hello.html +5 -0
- data/examples/hello.repub +3 -0
- data/examples/hello.summary.txt +1 -0
- data/exe/repubmark +46 -0
- data/lib/repubmark/config.rb +53 -0
- data/lib/repubmark/elems/abbrev.rb +52 -0
- data/lib/repubmark/elems/annotation.rb +48 -0
- data/lib/repubmark/elems/article.rb +96 -0
- data/lib/repubmark/elems/base.rb +104 -0
- data/lib/repubmark/elems/blockquote.rb +80 -0
- data/lib/repubmark/elems/canvas.rb +104 -0
- data/lib/repubmark/elems/caption.rb +69 -0
- data/lib/repubmark/elems/chapter.rb +148 -0
- data/lib/repubmark/elems/code_block.rb +70 -0
- data/lib/repubmark/elems/code_inline.rb +35 -0
- data/lib/repubmark/elems/figure.rb +73 -0
- data/lib/repubmark/elems/figures.rb +47 -0
- data/lib/repubmark/elems/footnote.rb +87 -0
- data/lib/repubmark/elems/footnotes_category.rb +48 -0
- data/lib/repubmark/elems/fraction.rb +31 -0
- data/lib/repubmark/elems/iframe.rb +62 -0
- data/lib/repubmark/elems/joint.rb +176 -0
- data/lib/repubmark/elems/link.rb +56 -0
- data/lib/repubmark/elems/list.rb +84 -0
- data/lib/repubmark/elems/list_item.rb +147 -0
- data/lib/repubmark/elems/note.rb +40 -0
- data/lib/repubmark/elems/paragraph.rb +42 -0
- data/lib/repubmark/elems/power.rb +34 -0
- data/lib/repubmark/elems/quote.rb +53 -0
- data/lib/repubmark/elems/section.rb +58 -0
- data/lib/repubmark/elems/separator.rb +17 -0
- data/lib/repubmark/elems/special.rb +37 -0
- data/lib/repubmark/elems/text.rb +43 -0
- data/lib/repubmark/highlight.rb +85 -0
- data/lib/repubmark/titled_ref.rb +16 -0
- data/lib/repubmark/version.rb +6 -0
- data/lib/repubmark.rb +125 -0
- data/tests/examples.rb +51 -0
- metadata +129 -0
@@ -0,0 +1,148 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Chapter < Base
|
6
|
+
parents :Article, :Chapter
|
7
|
+
|
8
|
+
def initialize(parent, level = 1, slug = nil, title = nil)
|
9
|
+
super parent
|
10
|
+
self.level = level
|
11
|
+
self.slug = slug
|
12
|
+
self.title = title
|
13
|
+
verify!
|
14
|
+
|
15
|
+
@canvas = Canvas.new self
|
16
|
+
@chapters = []
|
17
|
+
end
|
18
|
+
|
19
|
+
#################
|
20
|
+
# Basic methods #
|
21
|
+
#################
|
22
|
+
|
23
|
+
def word_count
|
24
|
+
(@title ? count_words(@title) : 0) +
|
25
|
+
@canvas.word_count +
|
26
|
+
@chapters.sum(&:word_count)
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_summary_plain
|
30
|
+
str = [
|
31
|
+
@title,
|
32
|
+
@canvas.to_summary_plain,
|
33
|
+
*@chapters.map(&:to_summary_plain),
|
34
|
+
].compact.join(' ').strip.freeze
|
35
|
+
|
36
|
+
str.empty? ? nil : str
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_html
|
40
|
+
[
|
41
|
+
build_title_html,
|
42
|
+
@canvas.to_html,
|
43
|
+
*@chapters.map(&:to_html),
|
44
|
+
].join.freeze
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_gemtext
|
48
|
+
[
|
49
|
+
build_title_gemtext,
|
50
|
+
@canvas.to_gemtext,
|
51
|
+
*@chapters.map(&:to_gemtext),
|
52
|
+
].join.freeze
|
53
|
+
end
|
54
|
+
|
55
|
+
def chapters
|
56
|
+
{
|
57
|
+
slug: anchor,
|
58
|
+
title: @title,
|
59
|
+
chapters: @chapters.map(&:chapters).freeze,
|
60
|
+
}.freeze
|
61
|
+
end
|
62
|
+
|
63
|
+
##################
|
64
|
+
# Helper methods #
|
65
|
+
##################
|
66
|
+
|
67
|
+
def anchor
|
68
|
+
if parent.instance_of?(self.class) && @level > 2
|
69
|
+
"#{parent.anchor}--#@slug"
|
70
|
+
elsif anchor_prefix.empty?
|
71
|
+
@slug
|
72
|
+
else
|
73
|
+
"#{anchor_prefix}--#@slug"
|
74
|
+
end.freeze
|
75
|
+
end
|
76
|
+
|
77
|
+
###################
|
78
|
+
# Builder methods #
|
79
|
+
###################
|
80
|
+
|
81
|
+
def chapter(slug, title)
|
82
|
+
chapter = Chapter.new self, @level + 1, slug, title
|
83
|
+
@chapters << chapter
|
84
|
+
yield chapter
|
85
|
+
nil
|
86
|
+
end
|
87
|
+
|
88
|
+
def respond_to_missing?(method_name, _include_private)
|
89
|
+
@canvas.respond_to?(method_name) || super
|
90
|
+
end
|
91
|
+
|
92
|
+
def method_missing(method_name, ...)
|
93
|
+
if @canvas.respond_to? method_name
|
94
|
+
raise 'Intro after chapters' unless @chapters.empty?
|
95
|
+
|
96
|
+
@canvas.public_send(method_name, ...)
|
97
|
+
else
|
98
|
+
super
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def level=(level)
|
105
|
+
level = Integer level
|
106
|
+
raise unless level.positive?
|
107
|
+
|
108
|
+
@level = level
|
109
|
+
end
|
110
|
+
|
111
|
+
def slug=(slug)
|
112
|
+
@slug = Repubmark.validate_slug! slug if slug
|
113
|
+
end
|
114
|
+
|
115
|
+
def title=(title)
|
116
|
+
return @title = nil if title.nil?
|
117
|
+
|
118
|
+
title = String(title).split.join(' ').strip.freeze
|
119
|
+
raise 'Empty title' if title.empty?
|
120
|
+
|
121
|
+
@title = title
|
122
|
+
end
|
123
|
+
|
124
|
+
def verify!
|
125
|
+
if @level == 1
|
126
|
+
raise 'Non-empty slug for level 1' if @slug
|
127
|
+
raise 'Non-empty title for level 1' if @title
|
128
|
+
else
|
129
|
+
raise 'Empty slug for level >= 2' if @slug.nil?
|
130
|
+
raise 'Empty title for level >= 2' if @title.nil?
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def build_title_html
|
135
|
+
id_attr = %( id="#{anchor}") unless anchor_prefix.empty?
|
136
|
+
%(<h#@level#{id_attr}>#@title</h#@level>\n) if @level != 1
|
137
|
+
end
|
138
|
+
|
139
|
+
def build_title_gemtext
|
140
|
+
"\n#{'#' * @level} #@title\n\n" if @level != 1
|
141
|
+
end
|
142
|
+
|
143
|
+
def anchor_prefix
|
144
|
+
@anchor_prefix ||= String(config[:chapter_anchor_prefix]).strip.freeze
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class CodeBlock < Base
|
6
|
+
parents :Canvas
|
7
|
+
|
8
|
+
attr_reader :syntax, :str, :indent
|
9
|
+
|
10
|
+
def initialize(parent, syntax, str, indent: 0)
|
11
|
+
super parent
|
12
|
+
|
13
|
+
self.syntax = syntax
|
14
|
+
self.str = str
|
15
|
+
self.indent = indent
|
16
|
+
end
|
17
|
+
|
18
|
+
#################
|
19
|
+
# Basic methods #
|
20
|
+
#################
|
21
|
+
|
22
|
+
def to_html = highlighted_str
|
23
|
+
|
24
|
+
def to_gemtext
|
25
|
+
[
|
26
|
+
"```\n",
|
27
|
+
"#{str.strip}\n",
|
28
|
+
"```\n",
|
29
|
+
].join.freeze
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def syntax=(syntax)
|
35
|
+
return @syntax = nil if syntax.nil?
|
36
|
+
|
37
|
+
unless syntax.instance_of? Symbol
|
38
|
+
raise TypeError, "Expected #{Symbol}, got #{syntax.class}"
|
39
|
+
end
|
40
|
+
|
41
|
+
@syntax = syntax
|
42
|
+
end
|
43
|
+
|
44
|
+
def str=(str)
|
45
|
+
str = String(str).freeze
|
46
|
+
raise 'Expected non-blank string' if str.strip.empty?
|
47
|
+
|
48
|
+
@str = str
|
49
|
+
end
|
50
|
+
|
51
|
+
def indent=(indent)
|
52
|
+
@indent = Integer indent
|
53
|
+
raise 'Expected non-negative number' if @indent.negative?
|
54
|
+
end
|
55
|
+
|
56
|
+
def highlighted_str
|
57
|
+
@highlighted_str ||= Highlight.call syntax, transformed_str
|
58
|
+
end
|
59
|
+
|
60
|
+
def transformed_str
|
61
|
+
@transformed_str ||=
|
62
|
+
str.lines.map { |line| "#{indentation}#{line}" }.join.freeze
|
63
|
+
end
|
64
|
+
|
65
|
+
def indentation
|
66
|
+
@indentation ||= (' ' * indent).freeze
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class CodeInline < Base
|
6
|
+
parents :Joint
|
7
|
+
|
8
|
+
def initialize(parent, str)
|
9
|
+
super parent
|
10
|
+
self.str = str
|
11
|
+
end
|
12
|
+
|
13
|
+
#################
|
14
|
+
# Basic methods #
|
15
|
+
#################
|
16
|
+
|
17
|
+
def to_summary_plain = "«#{@str}»".freeze
|
18
|
+
|
19
|
+
def to_html = "<code>#{CGI.escape_html(@str)}</code>".freeze
|
20
|
+
|
21
|
+
def to_gemtext = "«#{@str}»".freeze
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def str=(str)
|
26
|
+
str = String(str).freeze
|
27
|
+
if str.include?("\n") || str.include?('«') || str.include?('»')
|
28
|
+
raise 'Invalid str'
|
29
|
+
end
|
30
|
+
|
31
|
+
@str = str
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Figure < Base
|
6
|
+
parents :Canvas, :Figures
|
7
|
+
|
8
|
+
def initialize(parent, name, alt)
|
9
|
+
super parent
|
10
|
+
|
11
|
+
alt = String(alt).strip.split.join(' ').freeze
|
12
|
+
raise 'Empty alt text' if alt.empty?
|
13
|
+
|
14
|
+
@name = String(name).strip.freeze
|
15
|
+
@alt = alt
|
16
|
+
@caption = Caption.new self
|
17
|
+
end
|
18
|
+
|
19
|
+
#################
|
20
|
+
# Basic methods #
|
21
|
+
#################
|
22
|
+
|
23
|
+
def to_html
|
24
|
+
[
|
25
|
+
"<div#{html_class(:figure_wrap)}>\n",
|
26
|
+
"<figure#{html_class(:figure_self)}>\n",
|
27
|
+
"<img src=\"#{src}\" alt=\"#{CGI.escape_html(@alt)}\"/>\n",
|
28
|
+
"<figcaption>\n",
|
29
|
+
caption_html,
|
30
|
+
"</figcaption>\n",
|
31
|
+
"</figure>\n",
|
32
|
+
"</div>\n",
|
33
|
+
].join.freeze
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_gemtext
|
37
|
+
caption = @caption.to_gemtext.to_s.strip
|
38
|
+
caption = @alt if caption.empty?
|
39
|
+
"=> #{src} #{caption}\n".freeze
|
40
|
+
end
|
41
|
+
|
42
|
+
###################
|
43
|
+
# Builder methods #
|
44
|
+
###################
|
45
|
+
|
46
|
+
def respond_to_missing?(method_name, _include_private)
|
47
|
+
@caption.respond_to?(method_name) || super
|
48
|
+
end
|
49
|
+
|
50
|
+
def method_missing(method_name, ...)
|
51
|
+
if @caption.respond_to? method_name
|
52
|
+
@caption.public_send(method_name, ...)
|
53
|
+
else
|
54
|
+
super
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def src
|
61
|
+
@src ||= own_url "#{config[:images_prefix]}/#@name"
|
62
|
+
end
|
63
|
+
|
64
|
+
def caption_html
|
65
|
+
if @caption.empty?
|
66
|
+
"#{CGI.escape_html(@alt)}\n"
|
67
|
+
else
|
68
|
+
@caption.to_html
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Figures < Base
|
6
|
+
parents :Canvas
|
7
|
+
|
8
|
+
def initialize(parent)
|
9
|
+
super parent
|
10
|
+
@figures = []
|
11
|
+
end
|
12
|
+
|
13
|
+
#################
|
14
|
+
# Basic methods #
|
15
|
+
#################
|
16
|
+
|
17
|
+
def to_html
|
18
|
+
raise 'Expected two figures' unless @figures.size == 2
|
19
|
+
|
20
|
+
[
|
21
|
+
"<div#{html_class(:figures_wrap)}>\n",
|
22
|
+
*@figures.flat_map do |figure|
|
23
|
+
[
|
24
|
+
"<div#{html_class(:figures_left)}>\n",
|
25
|
+
figure.to_html,
|
26
|
+
"</div>\n",
|
27
|
+
]
|
28
|
+
end,
|
29
|
+
"</div>\n",
|
30
|
+
].join.freeze
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_gemtext = @figures.map(&:to_gemtext).join.freeze
|
34
|
+
|
35
|
+
###################
|
36
|
+
# Builder methods #
|
37
|
+
###################
|
38
|
+
|
39
|
+
def figure(name, alt)
|
40
|
+
figure = Figure.new self, name, alt
|
41
|
+
@figures << figure
|
42
|
+
yield figure if block_given?
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Footnote < Base
|
6
|
+
parents :FootnotesCategory
|
7
|
+
|
8
|
+
attr_reader :category, :slug, :index, :url, :link_text, :date, :alt_urls
|
9
|
+
|
10
|
+
def initialize(parent, **kwargs)
|
11
|
+
super parent
|
12
|
+
kwargs.each { |k, v| send :"#{k}=", v }
|
13
|
+
@caption = Caption.new self
|
14
|
+
end
|
15
|
+
|
16
|
+
#################
|
17
|
+
# Basic methods #
|
18
|
+
#################
|
19
|
+
|
20
|
+
def to_html
|
21
|
+
result = ''
|
22
|
+
result +=
|
23
|
+
%(<li id="#{category}-#{slug}" value="#{index}" class="fragment-highlight">\n)
|
24
|
+
if date
|
25
|
+
result += %(<span class="text-muted">\n)
|
26
|
+
result += %(<time datetime="#{date}">\n)
|
27
|
+
result += "#{I18n.localize(Date.parse(date), format: :long)}\n"
|
28
|
+
result += "</time>\n"
|
29
|
+
result += "</span>\n"
|
30
|
+
end
|
31
|
+
if url
|
32
|
+
scheme = Addressable::URI.parse(url).scheme&.downcase
|
33
|
+
if scheme
|
34
|
+
unless %w[http https].include? scheme
|
35
|
+
result += %(<span class="badge text-bg-secondary">\n)
|
36
|
+
result += %(#{scheme}://\n)
|
37
|
+
result += "</span>\n"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
result += %(<a href="#{url}">#{link_text}</a>\n)
|
41
|
+
else
|
42
|
+
result += "#{link_text}\n" if link_text
|
43
|
+
end
|
44
|
+
result += @caption.to_html.to_s
|
45
|
+
if alt_urls&.any?
|
46
|
+
result += %[(#{
|
47
|
+
alt_urls.map do |alt_url|
|
48
|
+
%(<a href=\"#{alt_url[:url]}\">#{alt_url[:name]}</a>)
|
49
|
+
end.join ', '
|
50
|
+
})\n]
|
51
|
+
end
|
52
|
+
result += '</li>'
|
53
|
+
result.freeze
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_gemtext
|
57
|
+
result = ''
|
58
|
+
result += url ? "=> #{url}" : '*'
|
59
|
+
result += " #{index})"
|
60
|
+
result += " #{Date.parse(date)}" if date
|
61
|
+
result += " #{link_text}" if link_text
|
62
|
+
result += " #{@caption.to_gemtext}"
|
63
|
+
result.freeze
|
64
|
+
end
|
65
|
+
|
66
|
+
###################
|
67
|
+
# Builder methods #
|
68
|
+
###################
|
69
|
+
|
70
|
+
def respond_to_missing?(method_name, _include_private)
|
71
|
+
@caption.respond_to?(method_name) || super
|
72
|
+
end
|
73
|
+
|
74
|
+
def method_missing(method_name, ...)
|
75
|
+
if @caption.respond_to? method_name
|
76
|
+
@caption.public_send(method_name, ...)
|
77
|
+
else
|
78
|
+
super
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
attr_writer :category, :slug, :index, :url, :link_text, :date, :alt_urls
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class FootnotesCategory < Base
|
6
|
+
parents :Article
|
7
|
+
|
8
|
+
def initialize(parent, title)
|
9
|
+
super parent
|
10
|
+
@title = title
|
11
|
+
@footnotes = []
|
12
|
+
end
|
13
|
+
|
14
|
+
#################
|
15
|
+
# Basic methods #
|
16
|
+
#################
|
17
|
+
|
18
|
+
def to_html
|
19
|
+
<<~HTML
|
20
|
+
<h2>#@title</h2>
|
21
|
+
|
22
|
+
<ol>
|
23
|
+
#{@footnotes.map(&:to_html).map(&:strip).join("\n")}
|
24
|
+
</ol>
|
25
|
+
HTML
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_gemtext
|
29
|
+
<<~GEMTEXT
|
30
|
+
## #@title
|
31
|
+
|
32
|
+
#{@footnotes.map(&:to_gemtext).map(&:strip).join("\n")}
|
33
|
+
GEMTEXT
|
34
|
+
end
|
35
|
+
|
36
|
+
###################
|
37
|
+
# Builder methods #
|
38
|
+
###################
|
39
|
+
|
40
|
+
def footnote(**kwargs)
|
41
|
+
footnote = Footnote.new(self, **kwargs)
|
42
|
+
@footnotes << footnote
|
43
|
+
yield footnote if block_given?
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Fraction < Base
|
6
|
+
parents :Joint
|
7
|
+
|
8
|
+
def initialize(parent, top, bottom)
|
9
|
+
super parent
|
10
|
+
|
11
|
+
@top = Integer top
|
12
|
+
@bottom = Integer bottom
|
13
|
+
|
14
|
+
raise 'Expected top to be non-negative' if @top.negative?
|
15
|
+
raise 'Expected bottom to be positive' unless @bottom.positive?
|
16
|
+
end
|
17
|
+
|
18
|
+
#################
|
19
|
+
# Basic methods #
|
20
|
+
#################
|
21
|
+
|
22
|
+
def word_count = 1
|
23
|
+
|
24
|
+
def to_summary_plain = "#@top/#@bottom".freeze
|
25
|
+
|
26
|
+
def to_html = "<sup>#@top</sup>⁄<sub>#@bottom</sub>".freeze
|
27
|
+
|
28
|
+
def to_gemtext = "#@top/#@bottom".freeze
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Repubmark
|
4
|
+
module Elems
|
5
|
+
class Iframe < Base
|
6
|
+
parents :Canvas
|
7
|
+
|
8
|
+
attr_reader :title, :src, :url
|
9
|
+
|
10
|
+
def initialize(parent, title, src, url = nil)
|
11
|
+
super parent
|
12
|
+
|
13
|
+
self.title = title
|
14
|
+
self.src = src
|
15
|
+
self.url = url || src
|
16
|
+
end
|
17
|
+
|
18
|
+
#################
|
19
|
+
# Basic methods #
|
20
|
+
#################
|
21
|
+
|
22
|
+
def to_html
|
23
|
+
[
|
24
|
+
"<div#{html_class(:iframe_wrap)}>\n",
|
25
|
+
"<iframe\n",
|
26
|
+
%(title="#{title}"\n),
|
27
|
+
%(src="#{src}"\n),
|
28
|
+
%(allowfullscreen=""\n),
|
29
|
+
%(frameborder="0"\n),
|
30
|
+
%(sandbox="allow-same-origin allow-scripts allow-popups"\n),
|
31
|
+
"></iframe>\n",
|
32
|
+
"</div>\n",
|
33
|
+
].join.freeze
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_gemtext = "=> #{url} #{title}\n"
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def title=(title)
|
41
|
+
title = String(title).strip.freeze
|
42
|
+
raise 'Empty title' if title.empty?
|
43
|
+
|
44
|
+
@title = title
|
45
|
+
end
|
46
|
+
|
47
|
+
def src=(src)
|
48
|
+
src = String(src).strip.freeze
|
49
|
+
raise 'Empty src' if src.empty?
|
50
|
+
|
51
|
+
@src = src
|
52
|
+
end
|
53
|
+
|
54
|
+
def url=(url)
|
55
|
+
url = String(url).strip.freeze
|
56
|
+
raise 'Empty url' if url.empty?
|
57
|
+
|
58
|
+
@url = url
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|