wcc-scripture 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +13 -0
  3. data/Gemfile +17 -0
  4. data/README.md +1 -0
  5. data/Rakefile +10 -0
  6. data/data/esv/1 Chronicles.xml +2414 -0
  7. data/data/esv/1 Corinthians.xml +1162 -0
  8. data/data/esv/1 John.xml +306 -0
  9. data/data/esv/1 Kings.xml +2087 -0
  10. data/data/esv/1 Peter.xml +356 -0
  11. data/data/esv/1 Samuel.xml +2175 -0
  12. data/data/esv/1 Thessalonians.xml +246 -0
  13. data/data/esv/1 Timothy.xml +320 -0
  14. data/data/esv/2 Chronicles.xml +2168 -0
  15. data/data/esv/2 Corinthians.xml +715 -0
  16. data/data/esv/2 John.xml +47 -0
  17. data/data/esv/2 Kings.xml +1955 -0
  18. data/data/esv/2 Peter.xml +170 -0
  19. data/data/esv/2 Samuel.xml +2069 -0
  20. data/data/esv/2 Thessalonians.xml +141 -0
  21. data/data/esv/2 Timothy.xml +239 -0
  22. data/data/esv/3 John.xml +55 -0
  23. data/data/esv/Acts.xml +2717 -0
  24. data/data/esv/Amos.xml +1156 -0
  25. data/data/esv/Colossians.xml +261 -0
  26. data/data/esv/Daniel.xml +1054 -0
  27. data/data/esv/Deuteronomy.xml +2864 -0
  28. data/data/esv/Ecclesiastes.xml +835 -0
  29. data/data/esv/Ephesians.xml +411 -0
  30. data/data/esv/Esther.xml +439 -0
  31. data/data/esv/Exodus.xml +3186 -0
  32. data/data/esv/Ezekiel.xml +3862 -0
  33. data/data/esv/Ezra.xml +723 -0
  34. data/data/esv/Galatians.xml +410 -0
  35. data/data/esv/Genesis.xml +4195 -0
  36. data/data/esv/Habakkuk.xml +430 -0
  37. data/data/esv/Haggai.xml +103 -0
  38. data/data/esv/Hebrews.xml +1075 -0
  39. data/data/esv/Hosea.xml +1420 -0
  40. data/data/esv/Isaiah.xml +8953 -0
  41. data/data/esv/James.xml +298 -0
  42. data/data/esv/Jeremiah.xml +6691 -0
  43. data/data/esv/Job.xml +5792 -0
  44. data/data/esv/Joel.xml +575 -0
  45. data/data/esv/John.xml +2207 -0
  46. data/data/esv/Jonah.xml +184 -0
  47. data/data/esv/Joshua.xml +1721 -0
  48. data/data/esv/Jude.xml +81 -0
  49. data/data/esv/Judges.xml +1781 -0
  50. data/data/esv/Lamentations.xml +1203 -0
  51. data/data/esv/Leviticus.xml +2179 -0
  52. data/data/esv/Luke.xml +3131 -0
  53. data/data/esv/Malachi.xml +162 -0
  54. data/data/esv/Mark.xml +1771 -0
  55. data/data/esv/Matthew.xml +2979 -0
  56. data/data/esv/Micah.xml +854 -0
  57. data/data/esv/Nahum.xml +380 -0
  58. data/data/esv/Nehemiah.xml +1034 -0
  59. data/data/esv/Numbers.xml +3420 -0
  60. data/data/esv/Obadiah.xml +181 -0
  61. data/data/esv/Philemon.xml +81 -0
  62. data/data/esv/Philippians.xml +286 -0
  63. data/data/esv/Proverbs.xml +4931 -0
  64. data/data/esv/Psalms.xml +15591 -0
  65. data/data/esv/Revelation.xml +1453 -0
  66. data/data/esv/Romans.xml +1317 -0
  67. data/data/esv/Ruth.xml +224 -0
  68. data/data/esv/Song of Solomon.xml +948 -0
  69. data/data/esv/Titus.xml +135 -0
  70. data/data/esv/Zechariah.xml +789 -0
  71. data/data/esv/Zephaniah.xml +460 -0
  72. data/data/esv/_notes.text.xml +3983 -0
  73. data/lib/wcc/scripture/esv.rb +326 -0
  74. data/lib/wcc/scripture/exceptions.rb +4 -0
  75. data/lib/wcc/scripture/version.rb +5 -0
  76. data/lib/wcc/scripture.rb +4 -0
  77. data/lib/wcc-scripture.rb +2 -0
  78. data/wcc-scripture.gemspec +29 -0
  79. metadata +162 -0
@@ -0,0 +1,326 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nokogiri'
4
+ require 'pericope'
5
+ require 'forwardable'
6
+ require 'rails-html-sanitizer'
7
+
8
+ class WCC::Scripture::Esv # rubocop:disable Metrics/ClassLength
9
+ attr_reader :parsed_ref
10
+
11
+ DATA_LIB = File.join(__dir__, '../../../data/esv')
12
+
13
+ class Reference
14
+ extend Forwardable
15
+
16
+ SINGLE_CHAPTER_BOOK_REGEX = /^(obadiah|philemon|2 john|3 john|jude)$/i.freeze
17
+
18
+ attr_reader :pericope
19
+ def_delegators :pericope, :ranges, :book_name
20
+
21
+ def initialize(pericope, book_only = false)
22
+ @pericope = pericope
23
+ @book_only = book_only
24
+ end
25
+
26
+ def self.parse(ref)
27
+ if match = SINGLE_CHAPTER_BOOK_REGEX.match(ref)
28
+ # Pericope will resolve the upper range to the actual when 100 is used
29
+ new(Pericope.parse_one("#{match[0]} 1-100"), true)
30
+ elsif pericope = Pericope.parse_one(ref)
31
+ new(pericope)
32
+ end
33
+ end
34
+
35
+ def to_s
36
+ return book_name if @book_only
37
+
38
+ result = pericope.to_s(
39
+ verse_range_separator: '–',
40
+ chapter_range_separator: '–'
41
+ )
42
+
43
+ return result unless book_name == 'Psalm' && range_spans_multiple_chapters?
44
+
45
+ result.gsub('Psalm', 'Psalms')
46
+ end
47
+
48
+ private
49
+
50
+ def range_spans_multiple_chapters?
51
+ range = ranges.first
52
+
53
+ range.begin.chapter != range.end.chapter
54
+ end
55
+ end
56
+
57
+ def initialize(
58
+ reference,
59
+ footnotes: true,
60
+ data_lib: DATA_LIB
61
+ )
62
+ raise WCC::Scripture::ScriptureNotFoundError unless @parsed_ref = Reference.parse(reference)
63
+
64
+ @data_lib = data_lib
65
+ @body_nodes = []
66
+ @footnote_nodes = []
67
+ @display_options = { footnotes: footnotes }
68
+ end
69
+
70
+ def render_html
71
+ parse_nodes
72
+ [
73
+ render_body,
74
+ render_footnotes
75
+ ].join
76
+ end
77
+
78
+ # rubocop:disable Metrics/MethodLength
79
+ # rubocop:disable Metrics/CyclomaticComplexity
80
+ # rubocop:disable Metrics/AbcSize
81
+ def render_body
82
+ marker = nil
83
+ body = []
84
+ para_open = false
85
+
86
+ @body_nodes.each do |node|
87
+ marker = node if verse_marker?(node)
88
+ if node.name == 'heading'
89
+ body.push process_heading(marker, node)
90
+ elsif node.name == 'subheading'
91
+ body.push process_heading(marker, node)
92
+ elsif node.name == 'begin-paragraph'
93
+ if node.attributes['class']&.value == 'line-group'
94
+ body.push('<p class="block-indent">')
95
+ else
96
+ body.push '<p>'
97
+ end
98
+ para_open = true
99
+ elsif node.name == 'end-paragraph'
100
+ body.push '</p>'
101
+ para_open = false
102
+ elsif node.name == 'verse'
103
+ unless para_open
104
+ body.push '<p>'
105
+ para_open = true
106
+ end
107
+ body.push process_verse(marker, node)
108
+ end
109
+ end
110
+
111
+ # In case verse range didn't include end-paragraph
112
+ body.push('</p>') if para_open
113
+
114
+ body.compact.join
115
+ end
116
+ # rubocop:enable Metrics/MethodLength
117
+ # rubocop:enable Metrics/CyclomaticComplexity
118
+ # rubocop:enable Metrics/AbcSize
119
+
120
+ def render_footnotes
121
+ return '' if @footnote_nodes.empty? || !@display_options[:footnotes]
122
+
123
+ <<~HTML
124
+ <div class="footnotes extra_text">
125
+ <h3>Footnotes</h3>
126
+ #{@footnote_nodes.join('<br/>')}
127
+ </div>
128
+ HTML
129
+ end
130
+
131
+ def convert_node(node, name, css = nil)
132
+ Nokogiri::XML::Node.new(name, book_xml).tap do |heading|
133
+ heading['class'] = css || node['class'] if css || node['class']
134
+ heading.children = node.children
135
+ end
136
+ end
137
+
138
+ def parse_nodes
139
+ @parsed_ref.ranges.each do |range|
140
+ first, *_, last = range.to_a
141
+
142
+ link_traverse(last || first, book_xml.at_css("marker.begin-verse[mid*=\"#{first.to_id}\"]"))
143
+ end
144
+ end
145
+
146
+ def link_traverse(bounds, node = nil)
147
+ return unless node
148
+ return if out_of_bounds?(bounds, node)
149
+
150
+ @body_nodes.push(node) if node.name =~ /marker|heading|paragraph|verse/
151
+
152
+ link_traverse(bounds, node.first_element_child) if node.name == 'chapter'
153
+ link_traverse(bounds, node.next)
154
+ end
155
+
156
+ def process_heading(marker, node)
157
+ heading = convert_node(node, node.name == 'heading' ? 'h3' : 'h4')
158
+
159
+ preprocess(heading)
160
+
161
+ parse_footnotes(marker, heading)
162
+
163
+ postprocess(heading.to_xml)
164
+ end
165
+
166
+ def process_verse(marker_node, verse_node)
167
+ preprocess(verse_node)
168
+ parse_footnotes(marker_node, verse_node)
169
+
170
+ [
171
+ %(<b class="verse-num">#{verse_node.attributes['num'].value}&nbsp;</b>),
172
+ # We have to replace these strings in this fashion instead of using Nokogiri because the source markup
173
+ # is not well-formed. The nodes do not encapsulate the text and children they are intending to designate as children,
174
+ # but instead self-closing which presents a parsing nightmare. In an ideal world, we would be able to replace the
175
+ # pseudo-html markup with real html and be done, but alas. So, for now, this post-processing step is acheived by
176
+ # string replacement.
177
+ #
178
+ # Nokogiri HTML Serialization Options
179
+ # 2 - exclude XML Declaration tag
180
+ # 32 - output XML - prevents Nokogiri from closing our tags for us
181
+ #
182
+ # No option passed would assume HTML options, and disrupt our markup scheme, producing something like:
183
+ # '<begin-line/>' => '<begin-line></begin-line>'
184
+ postprocess(verse_node.inner_html(save_with: 2 | 32)),
185
+ ' '
186
+ ].join
187
+ end
188
+
189
+ def preprocess(node)
190
+ node.tap do |n|
191
+ n.search('crossref, crossrefs, begin-block-indent, end-block-indent, q.continue-double').remove
192
+ translate_quotes(n)
193
+ n.search('heading, subheading').each { |h| h.replace(convert_node(h, node.name == 'heading' ? 'h3' : 'h4')) }
194
+ n.search('span.divine-name').each { |q| q.replace('LORD') }
195
+ n.search('woc, selah').each { |span| span.replace(convert_node(span, 'span', span.name)) }
196
+ end
197
+ end
198
+
199
+ # (
200
+ # begin-double|continue-double|end-double
201
+ # begin-inner-double|continue-inner-double|end-inner-double
202
+ # begin-single|continue-single|end-single
203
+ # begin-inner-single|end-inner-single
204
+ # )
205
+ def translate_quotes(node)
206
+ node.tap do |n|
207
+ n.search('q.begin-double, q.continue-double, q.begin-inner-double, q.continue-inner-double')
208
+ .each { |q| q.replace('&#x201C;') }
209
+ n.search('q.end-double, q.end-inner-double').each { |q| q.replace('&#x201D;') }
210
+ n.search('q.begin-single, q.continue-single, q.begin-inner-single').each { |q| q.replace('&#x2018;') }
211
+ n.search('q.end-single, q.end-inner-single').each { |q| q.replace('&#x2019;') }
212
+ end
213
+ end
214
+
215
+ def postprocess(markup)
216
+ translate_entities(
217
+ translate_pseudo_markup(
218
+ markup
219
+ )
220
+ )
221
+ end
222
+
223
+ def translate_pseudo_markup(markup)
224
+ markup
225
+ .gsub(%r{<begin-paragraph class="line-group"/>}, '<p class="block-indent">')
226
+ .gsub(%r{<begin-paragraph(?:\s+class=".+")?/>}, '<p>')
227
+ .gsub(%r{<end-paragraph/>}, '</p>')
228
+ .gsub(%r{<end-line(?:\s+class=".+")?/>}, '</span><br/>')
229
+ .gsub(%r{<begin-line\s?/>}, '<span class="line">')
230
+ .gsub(%r{<begin-line\s+class="(.+)"/>}, '<span class="line \1">&nbsp;&nbsp;&nbsp;&nbsp;')
231
+ end
232
+
233
+ def translate_entities(markup)
234
+ markup
235
+ .gsub('&ldblquot;', '&#8220;')
236
+ .gsub('&rdblquot;', '&#8221;')
237
+ .gsub('&lquot;', '&#8216;')
238
+ .gsub('&rquot;', '&#8217;')
239
+ .gsub('&apos;', '&#8217;')
240
+ .gsub('&emdash;', '&#8212;')
241
+ .gsub('&endash;', '&#8211;')
242
+ .gsub('&ellipsis;', '&nbsp;.&nbsp;.&nbsp;.')
243
+ .gsub('&ellipsis4;', '.&nbsp;.&nbsp;.&nbsp;.')
244
+ end
245
+
246
+ def parse_footnotes(marker, node)
247
+ notes = node.search('note')
248
+
249
+ return if notes.empty?
250
+ return notes.remove unless @display_options[:footnotes]
251
+
252
+ notes.each do |note|
253
+ footnote_node = note_xml.at_css("note[id=\"#{note.attributes['nid'].value}\"]")
254
+
255
+ if footnote_node
256
+ footnote_id = @footnote_nodes.size + 1
257
+ @footnote_nodes.push(
258
+ footnote(marker, footnote_node, footnote_id)
259
+ )
260
+ note.replace(
261
+ footnote_anchor(footnote_node, footnote_id)
262
+ )
263
+ else
264
+ note.remove
265
+ end
266
+ end
267
+ end
268
+
269
+ FULL_SANITIZER = Rails::Html::FullSanitizer.new
270
+
271
+ def footnote_anchor(note_node, id)
272
+ <<~HTML
273
+ <sup class="footnote">
274
+ <a class="fn" href="#f#{id}-1" id="fb#{id}-1" title="#{FULL_SANITIZER.sanitize(note_node.inner_html)}">#{id}</a>
275
+ </sup>
276
+ HTML
277
+ end
278
+
279
+ def footnote(marker, note_node, id)
280
+ preprocess(note_node)
281
+
282
+ verse_ref = Pericope::Verse.parse(marker_numeric_value(marker))
283
+
284
+ postprocess(<<~HTML)
285
+ <span class="footnote">
286
+ <a href="#fb#{id}-1" id="f#{id}-1">[#{id}]</a>
287
+ </span>
288
+ <span class="footnote-ref">#{verse_ref.to_s(with_chapter: true)}</span>
289
+ #{note_node}
290
+ HTML
291
+ end
292
+
293
+ def out_of_bounds?(bounds, node)
294
+ return unless verse_marker?(node)
295
+
296
+ bounds.to_i < marker_numeric_value(node)
297
+ end
298
+
299
+ def verse_marker?(node)
300
+ node.name == 'marker' && node.attributes['class'].value == 'begin-verse'
301
+ end
302
+
303
+ def marker_numeric_value(node)
304
+ node.attributes['mid'].value.sub(/^v/, '').to_i
305
+ end
306
+
307
+ XML_LOOKUP = {
308
+ 'Psalm' => 'Psalms'
309
+ }.freeze
310
+
311
+ def self.lookup_xml(book_name, data_lib = DATA_LIB)
312
+ Nokogiri::XML(
313
+ File.open(File.join(data_lib, "#{XML_LOOKUP.fetch(book_name, book_name)}.xml")),
314
+ &:huge
315
+ )
316
+ end
317
+
318
+ def book_xml
319
+ @book_xml ||= self.class.lookup_xml(@parsed_ref.book_name, @data_lib)
320
+ end
321
+
322
+ def note_xml
323
+ @note_xml ||=
324
+ Nokogiri::XML(File.open(File.join(@data_lib, '_notes.text.xml')), &:huge)
325
+ end
326
+ end
@@ -0,0 +1,4 @@
1
+ module WCC::Scripture
2
+ class ScriptureNotFoundError < StandardError
3
+ end
4
+ end
@@ -0,0 +1,5 @@
1
+ module WCC
2
+ module Scripture
3
+ VERSION = '0.1.0'
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+
2
+ require_relative './scripture/version'
3
+ require_relative './scripture/exceptions'
4
+ require_relative './scripture/esv'
@@ -0,0 +1,2 @@
1
+
2
+ require_relative './wcc/scripture'
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'wcc/scripture/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'wcc-scripture'
9
+ spec.version = WCC::Scripture::VERSION
10
+ spec.authors = ['Watermark Dev']
11
+ spec.email = ['dev@watermark.org']
12
+
13
+ spec.summary = File.readlines(File.expand_path('README.md', __dir__)).join
14
+ spec.description = ''
15
+ spec.license = 'Not licensed for external use'
16
+
17
+ spec.required_ruby_version = '>= 2.3'
18
+
19
+ spec.files =
20
+ `git ls-files -z`.split("\x0").reject do |f|
21
+ f.match(%r{^(test|spec|features)/})
22
+ end
23
+
24
+ spec.require_paths = ['lib']
25
+
26
+ spec.add_runtime_dependency 'nokogiri', '>= 1.0'
27
+ spec.add_runtime_dependency 'pericope', '>= 1.0'
28
+ spec.add_runtime_dependency 'rails-html-sanitizer', '>= 1.0'
29
+ end
metadata ADDED
@@ -0,0 +1,162 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wcc-scripture
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Watermark Dev
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-02-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pericope
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rails-html-sanitizer
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ description: ''
56
+ email:
57
+ - dev@watermark.org
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".rubocop.yml"
63
+ - Gemfile
64
+ - README.md
65
+ - Rakefile
66
+ - data/esv/1 Chronicles.xml
67
+ - data/esv/1 Corinthians.xml
68
+ - data/esv/1 John.xml
69
+ - data/esv/1 Kings.xml
70
+ - data/esv/1 Peter.xml
71
+ - data/esv/1 Samuel.xml
72
+ - data/esv/1 Thessalonians.xml
73
+ - data/esv/1 Timothy.xml
74
+ - data/esv/2 Chronicles.xml
75
+ - data/esv/2 Corinthians.xml
76
+ - data/esv/2 John.xml
77
+ - data/esv/2 Kings.xml
78
+ - data/esv/2 Peter.xml
79
+ - data/esv/2 Samuel.xml
80
+ - data/esv/2 Thessalonians.xml
81
+ - data/esv/2 Timothy.xml
82
+ - data/esv/3 John.xml
83
+ - data/esv/Acts.xml
84
+ - data/esv/Amos.xml
85
+ - data/esv/Colossians.xml
86
+ - data/esv/Daniel.xml
87
+ - data/esv/Deuteronomy.xml
88
+ - data/esv/Ecclesiastes.xml
89
+ - data/esv/Ephesians.xml
90
+ - data/esv/Esther.xml
91
+ - data/esv/Exodus.xml
92
+ - data/esv/Ezekiel.xml
93
+ - data/esv/Ezra.xml
94
+ - data/esv/Galatians.xml
95
+ - data/esv/Genesis.xml
96
+ - data/esv/Habakkuk.xml
97
+ - data/esv/Haggai.xml
98
+ - data/esv/Hebrews.xml
99
+ - data/esv/Hosea.xml
100
+ - data/esv/Isaiah.xml
101
+ - data/esv/James.xml
102
+ - data/esv/Jeremiah.xml
103
+ - data/esv/Job.xml
104
+ - data/esv/Joel.xml
105
+ - data/esv/John.xml
106
+ - data/esv/Jonah.xml
107
+ - data/esv/Joshua.xml
108
+ - data/esv/Jude.xml
109
+ - data/esv/Judges.xml
110
+ - data/esv/Lamentations.xml
111
+ - data/esv/Leviticus.xml
112
+ - data/esv/Luke.xml
113
+ - data/esv/Malachi.xml
114
+ - data/esv/Mark.xml
115
+ - data/esv/Matthew.xml
116
+ - data/esv/Micah.xml
117
+ - data/esv/Nahum.xml
118
+ - data/esv/Nehemiah.xml
119
+ - data/esv/Numbers.xml
120
+ - data/esv/Obadiah.xml
121
+ - data/esv/Philemon.xml
122
+ - data/esv/Philippians.xml
123
+ - data/esv/Proverbs.xml
124
+ - data/esv/Psalms.xml
125
+ - data/esv/Revelation.xml
126
+ - data/esv/Romans.xml
127
+ - data/esv/Ruth.xml
128
+ - data/esv/Song of Solomon.xml
129
+ - data/esv/Titus.xml
130
+ - data/esv/Zechariah.xml
131
+ - data/esv/Zephaniah.xml
132
+ - data/esv/_notes.text.xml
133
+ - lib/wcc-scripture.rb
134
+ - lib/wcc/scripture.rb
135
+ - lib/wcc/scripture/esv.rb
136
+ - lib/wcc/scripture/exceptions.rb
137
+ - lib/wcc/scripture/version.rb
138
+ - wcc-scripture.gemspec
139
+ homepage:
140
+ licenses:
141
+ - Not licensed for external use
142
+ metadata: {}
143
+ post_install_message:
144
+ rdoc_options: []
145
+ require_paths:
146
+ - lib
147
+ required_ruby_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '2.3'
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ requirements: []
158
+ rubygems_version: 3.0.3
159
+ signing_key:
160
+ specification_version: 4
161
+ summary: Internal Watermark.org gem, not licensed for external use.
162
+ test_files: []