repubmark 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|