inkcite 1.2.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/assets/blueprint.png +0 -0
- data/assets/example/helpers.tsv +37 -0
- data/assets/example/source.html +32 -0
- data/assets/{init → example}/source.txt +0 -0
- data/assets/init/config.yml +42 -4
- data/assets/init/helpers.tsv +12 -26
- data/assets/init/image_optim.yml +37 -0
- data/assets/init/source.html +0 -60
- data/inkcite.gemspec +2 -0
- data/lib/inkcite/cli/base.rb +5 -1
- data/lib/inkcite/cli/build.rb +1 -1
- data/lib/inkcite/cli/init.rb +31 -16
- data/lib/inkcite/cli/server.rb +5 -0
- data/lib/inkcite/cli/test.rb +16 -31
- data/lib/inkcite/email.rb +2 -2
- data/lib/inkcite/minifier.rb +46 -31
- data/lib/inkcite/renderer/button.rb +17 -8
- data/lib/inkcite/renderer/footnote.rb +39 -13
- data/lib/inkcite/renderer/litmus_analytics.rb +79 -0
- data/lib/inkcite/renderer/span.rb +5 -0
- data/lib/inkcite/renderer/table_base.rb +43 -52
- data/lib/inkcite/renderer/td.rb +0 -3
- data/lib/inkcite/renderer.rb +2 -2
- data/lib/inkcite/uploader.rb +4 -0
- data/lib/inkcite/util.rb +4 -0
- data/lib/inkcite/version.rb +1 -1
- data/lib/inkcite/view.rb +50 -8
- data/lib/inkcite.rb +10 -1
- data/test/renderer/span_spec.rb +97 -0
- data/test/renderer/td_spec.rb +10 -10
- metadata +38 -4
- data/lib/inkcite/renderer/litmus.rb +0 -33
data/lib/inkcite/view.rb
CHANGED
@@ -151,7 +151,32 @@ module Inkcite
|
|
151
151
|
end
|
152
152
|
|
153
153
|
def footnotes
|
154
|
-
|
154
|
+
|
155
|
+
if @footnotes.nil?
|
156
|
+
@footnotes = []
|
157
|
+
|
158
|
+
# Preload the array of footnotes if they exist
|
159
|
+
footnotes_tsv_file = @email.project_file(FOOTNOTES_TSV_FILE)
|
160
|
+
if File.exists?(footnotes_tsv_file)
|
161
|
+
CSV.foreach(footnotes_tsv_file, { :col_sep => "\t" }) do |fn|
|
162
|
+
|
163
|
+
id = fn[0]
|
164
|
+
next if id.blank?
|
165
|
+
|
166
|
+
text = fn[2]
|
167
|
+
next if text.blank?
|
168
|
+
|
169
|
+
# Read the symbol and replace it with nil (so that one will be auto-generated)
|
170
|
+
symbol = fn[1]
|
171
|
+
symbol = nil if symbol.blank?
|
172
|
+
|
173
|
+
@footnotes << Renderer::Footnote::Instance.new(id, symbol, text, false)
|
174
|
+
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
@footnotes
|
155
180
|
end
|
156
181
|
|
157
182
|
def file_name ext=nil
|
@@ -190,7 +215,12 @@ module Inkcite
|
|
190
215
|
|
191
216
|
# Prepend the image host onto the src if one is specified in the properties.
|
192
217
|
# During local development, images are always expected in an images/ subdirectory.
|
193
|
-
image_host = development
|
218
|
+
image_host = if development?
|
219
|
+
(@email.optimize_images?? Minifier::IMAGE_CACHE : Email::IMAGES) + '/'
|
220
|
+
else
|
221
|
+
self[Email::IMAGE_HOST]
|
222
|
+
end
|
223
|
+
|
194
224
|
src_url << image_host unless image_host.blank?
|
195
225
|
|
196
226
|
# Add the source of the image.
|
@@ -344,13 +374,19 @@ module Inkcite
|
|
344
374
|
html << '</style>'
|
345
375
|
html << '</head>'
|
346
376
|
|
347
|
-
#
|
348
|
-
bgcolor = Renderer.hex(self[BACKGROUND])
|
349
|
-
|
350
|
-
# Intentially not setting the link colors because those should be entirely
|
377
|
+
# Intentionally not setting the link colors because those should be entirely
|
351
378
|
# controlled by the styles and attributes of the links themselves. By not
|
352
379
|
# setting it, links created sans-helper should be visually distinct.
|
353
|
-
html <<
|
380
|
+
html << '<body style="width: 100% !important; min-width: 100% !important; margin: 0 !important; padding: 0; -webkit-text-size-adjust: none; -ms-text-size-adjust: none;'
|
381
|
+
|
382
|
+
# A pleasing but obvious background exposed in development mode to alert
|
383
|
+
# the designer that they have exposed the body background - which means
|
384
|
+
# unpredictable results if sent.
|
385
|
+
if development?
|
386
|
+
html << " background: #ccc url('data:image/png;base64,#{Inkcite.blueprint_image64}');"
|
387
|
+
end
|
388
|
+
|
389
|
+
html << %q(">)
|
354
390
|
|
355
391
|
html << minified
|
356
392
|
|
@@ -479,7 +515,6 @@ module Inkcite
|
|
479
515
|
private
|
480
516
|
|
481
517
|
ASSETS = 'assets'
|
482
|
-
BACKGROUND = :'#background'
|
483
518
|
FILE_SCHEME = 'file'
|
484
519
|
FILE_NAME = :'file-name'
|
485
520
|
HTML_EXTENSION = '.html'
|
@@ -522,6 +557,9 @@ module Inkcite
|
|
522
557
|
# Tab-separated file containing links declarations.
|
523
558
|
LINKS_TSV_FILE = 'links.tsv'
|
524
559
|
|
560
|
+
# Tab-separated file containing footnote declarations.
|
561
|
+
FOOTNOTES_TSV_FILE = 'footnotes.tsv'
|
562
|
+
|
525
563
|
def assert_in_browser msg
|
526
564
|
raise msg if email? && !development?
|
527
565
|
end
|
@@ -716,6 +754,10 @@ module Inkcite
|
|
716
754
|
# Ensure that telephone numbers are displayed using the same style as links.
|
717
755
|
reset << "a[href^=tel] { color: #{self[Renderer::Base::LINK_COLOR]}; text-decoration:none;}"
|
718
756
|
|
757
|
+
# Remove extraneous left-margins on Android 4.4
|
758
|
+
# https://litmus.com/community/code/4194-why-is-email-not-centered-on-android-4-4#comment-5727
|
759
|
+
reset << 'div[style*="margin: 16px 0"] { margin:0 !important; }'
|
760
|
+
|
719
761
|
end
|
720
762
|
|
721
763
|
# Reset the font on every cell to the default family.
|
data/lib/inkcite.rb
CHANGED
@@ -23,7 +23,16 @@ require 'inkcite/renderer'
|
|
23
23
|
module Inkcite
|
24
24
|
|
25
25
|
def self.asset_path
|
26
|
-
File.expand_path('
|
26
|
+
File.join(File.expand_path('../', File.dirname(__FILE__)), 'assets')
|
27
|
+
end
|
28
|
+
|
29
|
+
# Loads (and caches) the base64-encoded PNG data for the subtle background
|
30
|
+
# texture that Inkcite installs on the <body> tag in development mode.
|
31
|
+
def self.blueprint_image64
|
32
|
+
@blueprint64 ||= begin
|
33
|
+
blueprint_path = File.join(asset_path, 'blueprint.png')
|
34
|
+
Base64.encode64(File.read(blueprint_path)).gsub(/[\r\f\n]/, '')
|
35
|
+
end
|
27
36
|
end
|
28
37
|
|
29
38
|
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'minitest/spec'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'inkcite'
|
4
|
+
|
5
|
+
describe Inkcite::Renderer::Span do
|
6
|
+
|
7
|
+
before do
|
8
|
+
@view = Inkcite::Email.new('test/project/').view(:development, :email)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'can have empty parameters' do
|
12
|
+
Inkcite::Renderer.render('{span}{/span}', @view).must_equal('<span></span>')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'can have a custom font color' do
|
16
|
+
Inkcite::Renderer.render('{span color=#f90}{/span}', @view).must_equal('<span style="color:#ff9900"></span>')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'can have a custom font family' do
|
20
|
+
Inkcite::Renderer.render('{span font-family="Comic Sans"}{/span}', @view).must_equal('<span style="font-family:Comic Sans"></span>')
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'can have a custom font size' do
|
24
|
+
Inkcite::Renderer.render('{span font-size=18}{/span}', @view).must_equal('<span style="font-size:18px"></span>')
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'can have a custom font weight' do
|
28
|
+
Inkcite::Renderer.render('{span font-weight=bold}{/span}', @view).must_equal('<span style="font-weight:bold"></span>')
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'can have a custom line height' do
|
32
|
+
Inkcite::Renderer.render('{span line-height=15}{/span}', @view).must_equal('<span style="line-height:15px"></span>')
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'can inherit a font from the context' do
|
36
|
+
Inkcite::Renderer.render('{span font=large}{/span}', @view).must_equal('<span style="color:#ff0000;font-family:serif;font-size:24px;font-weight:bold;line-height:24px"></span>')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'can override the font size of an inherited font' do
|
40
|
+
Inkcite::Renderer.render('{span font=large font-size=8}{/span}', @view).must_equal('<span style="color:#ff0000;font-family:serif;font-size:8px;font-weight:bold;line-height:24px"></span>')
|
41
|
+
Inkcite::Renderer.render('{span font=large font-size=none}{/span}', @view).must_equal('<span style="color:#ff0000;font-family:serif;font-weight:bold;line-height:24px"></span>')
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'can override the color of an inherited font' do
|
45
|
+
Inkcite::Renderer.render('{span font=large color=#00f}{/span}', @view).must_equal('<span style="color:#0000ff;font-family:serif;font-size:24px;font-weight:bold;line-height:24px"></span>')
|
46
|
+
Inkcite::Renderer.render('{span font=large color=none}{/span}', @view).must_equal('<span style="font-family:serif;font-size:24px;font-weight:bold;line-height:24px"></span>')
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'can override the font weight of an inherited font' do
|
50
|
+
Inkcite::Renderer.render('{span font=large font-weight=normal}{/span}', @view).must_equal('<span style="color:#ff0000;font-family:serif;font-size:24px;font-weight:normal;line-height:24px"></span>')
|
51
|
+
Inkcite::Renderer.render('{span font=large font-weight=none}{/span}', @view).must_equal('<span style="color:#ff0000;font-family:serif;font-size:24px;line-height:24px"></span>')
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'can override the line height of an inherited font' do
|
55
|
+
Inkcite::Renderer.render('{span font=large line-height=12}{/span}', @view).must_equal('<span style="color:#ff0000;font-family:serif;font-size:24px;font-weight:bold;line-height:12px"></span>')
|
56
|
+
Inkcite::Renderer.render('{span font=large line-height=normal}{/span}', @view).must_equal('<span style="color:#ff0000;font-family:serif;font-size:24px;font-weight:bold;line-height:normal"></span>')
|
57
|
+
Inkcite::Renderer.render('{span font=large line-height=none}{/span}', @view).must_equal('<span style="color:#ff0000;font-family:serif;font-size:24px;font-weight:bold"></span>')
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'can have a text shadow' do
|
61
|
+
Inkcite::Renderer.render('{span shadow=#99c}{/span}', @view).must_equal('<span style="text-shadow:0 1px 0 #9999cc"></span>')
|
62
|
+
Inkcite::Renderer.render('{span shadow=#9c9 shadow-blur=2}{/span}', @view).must_equal('<span style="text-shadow:0 1px 2px #99cc99"></span>')
|
63
|
+
Inkcite::Renderer.render('{span shadow=#c99 shadow-offset=-1}{/span}', @view).must_equal('<span style="text-shadow:0 -1px 0 #cc9999"></span>')
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'can have a background color' do
|
67
|
+
Inkcite::Renderer.render('{span bgcolor=#06c}{/span}', @view).must_equal('<span style="background-color:#0066cc"></span>')
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'can be responsive' do
|
71
|
+
Inkcite::Renderer.render('{span mobile=hide}{/span}', @view).must_equal('<span class="hide"></span>')
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'can have custom letter spacing' do
|
75
|
+
Inkcite::Renderer.render('{span letter-spacing=3}{/span}', @view).must_equal('<span style="letter-spacing:3px"></span>')
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'can have a custom font size on mobile' do
|
79
|
+
Inkcite::Renderer.render('{span font-size=15 mobile-font-size=20}{/span}', @view).must_equal('<span class="m1" style="font-size:15px"></span>')
|
80
|
+
@view.media_query.find_by_klass('m1').declarations.must_match('font-size:20px !important')
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'can have a custom line height on mobile' do
|
84
|
+
Inkcite::Renderer.render('{span line-height=15 mobile-line-height=20}{/span}', @view).must_equal('<span class="m1" style="line-height:15px"></span>')
|
85
|
+
@view.media_query.find_by_klass('m1').to_css.must_equal('span[class~="m1"] { line-height:20px !important }')
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'can inherit a custom font size on mobile from the context' do
|
89
|
+
Inkcite::Renderer.render('{span font=responsive}{/span}', @view).must_equal('<span class="m1" style="font-size:20px"></span>')
|
90
|
+
@view.media_query.find_by_klass('m1').declarations.must_match('font-size:40px')
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'supports padding' do
|
94
|
+
Inkcite::Renderer.render('{span padding=15}{/span}', @view).must_equal('<span style="padding:15px"></span>')
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
data/test/renderer/td_spec.rb
CHANGED
@@ -91,12 +91,12 @@ describe Inkcite::Renderer::Td do
|
|
91
91
|
|
92
92
|
it 'can have a custom background color on mobile' do
|
93
93
|
Inkcite::Renderer.render('{td mobile-bgcolor=#f09}', @view).must_equal('<td class="m1">')
|
94
|
-
@view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background
|
94
|
+
@view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background:#ff0099 }')
|
95
95
|
end
|
96
96
|
|
97
97
|
it 'can override background color on mobile' do
|
98
98
|
Inkcite::Renderer.render('{td bgcolor=#f00 mobile-bgcolor=#00f}', @view).must_equal('<td bgcolor=#ff0000 class="m1">')
|
99
|
-
@view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background
|
99
|
+
@view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background:#0000ff !important }')
|
100
100
|
end
|
101
101
|
|
102
102
|
it 'can have a background image' do
|
@@ -109,18 +109,18 @@ describe Inkcite::Renderer::Td do
|
|
109
109
|
end
|
110
110
|
|
111
111
|
it 'can override background image on mobile' do
|
112
|
-
Inkcite::Renderer.render('{td background=floor.jpg
|
113
|
-
@view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background:url(images/sky.jpg)
|
112
|
+
Inkcite::Renderer.render('{td background=floor.jpg mobile-background-image=sky.jpg }', @view).must_equal('<td class="m1" style="background:url(images/floor.jpg)">')
|
113
|
+
@view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background:url(images/sky.jpg) !important }')
|
114
114
|
end
|
115
115
|
|
116
|
-
it 'can
|
117
|
-
Inkcite::Renderer.render('{td
|
118
|
-
@view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background
|
116
|
+
it 'can override background position on mobile' do
|
117
|
+
Inkcite::Renderer.render('{td background=floor.jpg background-position=bottom mobile-background-position=top}', @view).must_equal('<td class="m1" style="background:url(images/floor.jpg) bottom no-repeat">')
|
118
|
+
@view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background:url(images/floor.jpg) top no-repeat !important }')
|
119
119
|
end
|
120
120
|
|
121
|
-
it '
|
122
|
-
Inkcite::Renderer.render('{td background=floor.jpg
|
123
|
-
@view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background:
|
121
|
+
it 'can disable background image on mobile' do
|
122
|
+
Inkcite::Renderer.render('{td background=floor.jpg mobile-background-image=none}', @view).must_equal('<td class="m1" style="background:url(images/floor.jpg)">')
|
123
|
+
@view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background:none !important }')
|
124
124
|
end
|
125
125
|
|
126
126
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inkcite
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeffrey D. Hoffman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-10-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -66,6 +66,34 @@ dependencies:
|
|
66
66
|
- - '>='
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: image_optim
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: image_optim_pack
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
69
97
|
- !ruby/object:Gem::Dependency
|
70
98
|
name: litmus
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -232,12 +260,16 @@ files:
|
|
232
260
|
- LICENSE
|
233
261
|
- README.md
|
234
262
|
- Rakefile
|
263
|
+
- assets/blueprint.png
|
264
|
+
- assets/example/helpers.tsv
|
265
|
+
- assets/example/source.html
|
266
|
+
- assets/example/source.txt
|
235
267
|
- assets/facebook-like.css
|
236
268
|
- assets/facebook-like.js
|
237
269
|
- assets/init/config.yml
|
238
270
|
- assets/init/helpers.tsv
|
271
|
+
- assets/init/image_optim.yml
|
239
272
|
- assets/init/source.html
|
240
|
-
- assets/init/source.txt
|
241
273
|
- bin/inkcite
|
242
274
|
- bin/release-major
|
243
275
|
- bin/release-minor
|
@@ -267,7 +299,7 @@ files:
|
|
267
299
|
- lib/inkcite/renderer/increment.rb
|
268
300
|
- lib/inkcite/renderer/like.rb
|
269
301
|
- lib/inkcite/renderer/link.rb
|
270
|
-
- lib/inkcite/renderer/
|
302
|
+
- lib/inkcite/renderer/litmus_analytics.rb
|
271
303
|
- lib/inkcite/renderer/lorem.rb
|
272
304
|
- lib/inkcite/renderer/mobile_image.rb
|
273
305
|
- lib/inkcite/renderer/mobile_style.rb
|
@@ -304,6 +336,7 @@ files:
|
|
304
336
|
- test/renderer/link_spec.rb
|
305
337
|
- test/renderer/mobile_image_spec.rb
|
306
338
|
- test/renderer/mobile_style_spec.rb
|
339
|
+
- test/renderer/span_spec.rb
|
307
340
|
- test/renderer/table_spec.rb
|
308
341
|
- test/renderer/td_spec.rb
|
309
342
|
- test/renderer_spec.rb
|
@@ -349,6 +382,7 @@ test_files:
|
|
349
382
|
- test/renderer/link_spec.rb
|
350
383
|
- test/renderer/mobile_image_spec.rb
|
351
384
|
- test/renderer/mobile_style_spec.rb
|
385
|
+
- test/renderer/span_spec.rb
|
352
386
|
- test/renderer/table_spec.rb
|
353
387
|
- test/renderer/td_spec.rb
|
354
388
|
- test/renderer_spec.rb
|
@@ -1,33 +0,0 @@
|
|
1
|
-
module Inkcite
|
2
|
-
module Renderer
|
3
|
-
class Litmus < Base
|
4
|
-
|
5
|
-
def render tag, opt, ctx
|
6
|
-
|
7
|
-
# Litmus tracking is enabled only for production emails.
|
8
|
-
return nil unless ctx.production? && ctx.email?
|
9
|
-
|
10
|
-
code = opt[:code] || opt[:id]
|
11
|
-
return nil if code.blank?
|
12
|
-
|
13
|
-
merge_tag = opt[MERGE_TAG] || ctx[MERGE_TAG]
|
14
|
-
|
15
|
-
ctx.styles << "@media print{#_t { background-image: url('https://#{code}.emltrk.com/#{code}?p&d=#{merge_tag}');}}"
|
16
|
-
ctx.styles << "div.OutlookMessageHeader {background-image:url('https://#{code}.emltrk.com/#{code}?f&d=#{merge_tag}')}"
|
17
|
-
ctx.styles << "table.moz-email-headers-table {background-image:url('https://#{code}.emltrk.com/#{code}?f&d=#{merge_tag}')}"
|
18
|
-
ctx.styles << "blockquote #_t {background-image:url('https://#{code}.emltrk.com/#{code}?f&d=#{merge_tag}')}"
|
19
|
-
ctx.styles << "#MailContainerBody #_t {background-image:url('https://#{code}.emltrk.com/#{code}?f&d=#{merge_tag}')}"
|
20
|
-
|
21
|
-
ctx.footer << '<div id="_t"></div>'
|
22
|
-
ctx.footer << "<img src=\"https://#{code}.emltrk.com/#{code}?d=#{merge_tag}\" width=1 height=1 border=0 />"
|
23
|
-
|
24
|
-
nil
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
MERGE_TAG = :'merge-tag'
|
30
|
-
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|