pericope 0.7.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/pericope.rb +131 -436
- data/lib/pericope/cli.rb +1 -27
- data/lib/pericope/parsing.rb +264 -0
- data/lib/pericope/range.rb +58 -0
- data/lib/pericope/verse.rb +74 -0
- data/lib/pericope/version.rb +1 -1
- metadata +19 -3
- data/lib/tasks/pericope_tasks.rake +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 145908194097992ca18effe78d077c9ab4056e75
|
4
|
+
data.tar.gz: 88d93e6cb4e92b429ee3b0d3e559bd4c1f874aa1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72b6bc51a94abdbfbdcb842359d98ca9ca68c8c802c7e4c27c63237ddf66952fda859b64a9782c5b1f909658c4bbf2ac6d3ec073c9ea9f166885a10f8179725a
|
7
|
+
data.tar.gz: 109fe8d1a3eaffa62485d55db3febc29f68a5410592b8d1334610c6d7729575ea59cd2a9118e7e218e2311138356354023966501802f3730b222c5053721cf11
|
data/lib/pericope.rb
CHANGED
@@ -1,187 +1,171 @@
|
|
1
1
|
require "pericope/version"
|
2
2
|
require "pericope/data"
|
3
|
+
require "pericope/parsing"
|
3
4
|
|
4
5
|
class Pericope
|
5
|
-
|
6
|
+
extend Pericope::Parsing
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
attr_reader :book, :original_string, :ranges
|
10
|
+
|
6
11
|
|
7
12
|
def initialize(arg)
|
8
13
|
case arg
|
9
14
|
when String
|
10
15
|
attributes = Pericope.match_one(arg)
|
11
|
-
raise "no pericope found in #{arg}
|
16
|
+
raise ArgumentError, "no pericope found in #{arg}" if attributes.nil?
|
12
17
|
|
13
18
|
@original_string = attributes[:original_string]
|
14
|
-
|
19
|
+
@book = attributes[:book]
|
15
20
|
@ranges = attributes[:ranges]
|
16
21
|
|
17
22
|
when Array
|
18
|
-
|
19
|
-
|
20
|
-
@ranges = Pericope.group_array_into_ranges(arg)
|
21
|
-
|
22
|
-
when Range
|
23
|
-
STDERR.puts "DEPRECATION WARNING: instantiating a pericope with a single range is deprecated and will be removed in pericope 1.0.",
|
24
|
-
"",
|
25
|
-
" You can change `Pericope.new(range)` to `Pericope.new(range.to_a)`",
|
26
|
-
""
|
27
|
-
set_book Pericope.get_book(arg.begin)
|
28
|
-
@ranges = [arg]
|
23
|
+
@ranges = group_array_into_ranges(arg)
|
24
|
+
@book = @ranges.first.begin.book
|
29
25
|
|
30
26
|
else
|
31
|
-
|
32
|
-
@
|
33
|
-
|
34
|
-
@ranges = attributes[:ranges]
|
27
|
+
@original_string = arg[:original_string]
|
28
|
+
@book = arg[:book]
|
29
|
+
@ranges = arg[:ranges]
|
35
30
|
|
36
31
|
end
|
32
|
+
raise ArgumentError, "must specify book" unless @book
|
37
33
|
end
|
38
34
|
|
39
35
|
|
40
|
-
|
41
|
-
def self.book_has_chapters?(book)
|
42
|
-
BOOK_CHAPTER_COUNTS[book] > 1
|
43
|
-
end
|
44
|
-
|
45
36
|
def book_has_chapters?
|
46
37
|
book_chapter_count > 1
|
47
38
|
end
|
48
39
|
|
49
|
-
|
50
|
-
|
51
|
-
# Differs from Pericope.new in that it won't raise an exception
|
52
|
-
# if text does not contain a pericope but will return nil instead.
|
53
|
-
def self.parse_one(text)
|
54
|
-
parse(text) do |pericope|
|
55
|
-
return pericope
|
56
|
-
end
|
57
|
-
nil
|
40
|
+
def book_name
|
41
|
+
@book_name ||= Pericope::BOOK_NAMES[@book]
|
58
42
|
end
|
59
43
|
|
44
|
+
def book_chapter_count
|
45
|
+
@book_chapter_count ||= Pericope::BOOK_CHAPTER_COUNTS[@book]
|
46
|
+
end
|
60
47
|
|
61
48
|
|
62
|
-
def
|
63
|
-
|
64
|
-
match_all(text) do |attributes|
|
65
|
-
pericope = Pericope.new(attributes)
|
66
|
-
if block_given?
|
67
|
-
yield pericope
|
68
|
-
else
|
69
|
-
pericopes << pericope
|
70
|
-
end
|
71
|
-
end
|
72
|
-
block_given? ? text : pericopes
|
49
|
+
def to_s(options={})
|
50
|
+
"#{book_name} #{well_formatted_reference(options)}"
|
73
51
|
end
|
74
52
|
|
53
|
+
def inspect
|
54
|
+
"Pericope(#{to_s})"
|
55
|
+
end
|
75
56
|
|
76
57
|
|
77
|
-
def
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
match_all(text) do |attributes, match|
|
58
|
+
def ==(other)
|
59
|
+
other.is_a?(self.class) && [book, ranges] == [other.book, other.ranges]
|
60
|
+
end
|
82
61
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
yield pretext if block_given?
|
87
|
-
end
|
62
|
+
def hash
|
63
|
+
[book, ranges].hash
|
64
|
+
end
|
88
65
|
|
89
|
-
|
90
|
-
|
91
|
-
|
66
|
+
def <=>(other)
|
67
|
+
to_a <=> other.to_a
|
68
|
+
end
|
92
69
|
|
93
|
-
|
94
|
-
|
70
|
+
def intersects?(other)
|
71
|
+
return false unless other.is_a?(Pericope)
|
72
|
+
return false unless book == other.book
|
95
73
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
74
|
+
ranges.each do |self_range|
|
75
|
+
other.ranges.each do |other_range|
|
76
|
+
return true if (self_range.end >= other_range.begin) and (self_range.begin <= other_range.end)
|
77
|
+
end
|
100
78
|
end
|
101
79
|
|
102
|
-
|
80
|
+
false
|
103
81
|
end
|
104
82
|
|
105
83
|
|
84
|
+
def each
|
85
|
+
return to_enum unless block_given?
|
106
86
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
" You can use `Pericope.split` with `inject` to replace pericopes in a block of text",
|
111
|
-
""
|
112
|
-
|
113
|
-
split(text).inject("") do |text, segment|
|
114
|
-
if segment.is_a?(String)
|
115
|
-
text << segment
|
116
|
-
else
|
117
|
-
text << "{{#{segment.to_a.join(" ")}}}"
|
87
|
+
ranges.each do |range|
|
88
|
+
range.each do |verse|
|
89
|
+
yield verse
|
118
90
|
end
|
119
91
|
end
|
92
|
+
|
93
|
+
self
|
120
94
|
end
|
121
95
|
|
122
96
|
|
97
|
+
class << self
|
98
|
+
attr_reader :max_letter
|
123
99
|
|
124
|
-
|
125
|
-
|
100
|
+
def max_letter=(value)
|
101
|
+
unless @max_letter == value
|
102
|
+
@max_letter = value.freeze
|
103
|
+
@_letters = nil
|
104
|
+
@_regexp = nil
|
105
|
+
@_normalizations = nil
|
106
|
+
@_letter_regexp = nil
|
107
|
+
@_fragment_regexp = nil
|
108
|
+
end
|
109
|
+
end
|
126
110
|
|
127
|
-
|
128
|
-
|
129
|
-
Pericope.new(ids).to_s
|
111
|
+
def book_has_chapters?(book)
|
112
|
+
BOOK_CHAPTER_COUNTS[book] > 1
|
130
113
|
end
|
131
|
-
end
|
132
114
|
|
115
|
+
def get_max_verse(book, chapter)
|
116
|
+
id = (book * 1000000) + (chapter * 1000)
|
117
|
+
CHAPTER_VERSE_COUNTS[id]
|
118
|
+
end
|
133
119
|
|
120
|
+
def get_max_chapter(book)
|
121
|
+
BOOK_CHAPTER_COUNTS[book]
|
122
|
+
end
|
134
123
|
|
135
|
-
|
136
|
-
|
137
|
-
|
124
|
+
def regexp
|
125
|
+
@_regexp ||= /#{book_pattern}\.?\s*(#{reference_pattern})/i
|
126
|
+
end
|
138
127
|
|
139
|
-
|
140
|
-
|
141
|
-
|
128
|
+
def normalizations
|
129
|
+
@_normalizations ||= [
|
130
|
+
[/(\d+)[".](\d+)/, '\1:\2'], # 12"5 and 12.5 -> 12:5
|
131
|
+
[/[–—]/, '-'], # convert em dash and en dash to -
|
132
|
+
[/[^0-9,:;\-–—#{letters}]/, ''] ] # remove everything but recognized symbols
|
133
|
+
end
|
142
134
|
|
143
|
-
|
144
|
-
|
145
|
-
|
135
|
+
def letter_regexp
|
136
|
+
@_letter_regexp ||= /[#{letters}]$/
|
137
|
+
end
|
146
138
|
|
147
|
-
|
148
|
-
|
149
|
-
|
139
|
+
def fragment_regexp
|
140
|
+
@_fragment_regexp ||= /^(?:(?<chapter>\d{1,3}):)?(?<verse>\d{1,3})?(?<letter>[#{letters}])?$/
|
141
|
+
end
|
150
142
|
|
151
|
-
|
152
|
-
to_a <=> other.to_a
|
153
|
-
end
|
143
|
+
private
|
154
144
|
|
145
|
+
def book_pattern
|
146
|
+
BOOK_PATTERN.source.gsub(/[ \n]/, "")
|
147
|
+
end
|
155
148
|
|
149
|
+
def reference_pattern
|
150
|
+
number = '\d{1,3}'
|
151
|
+
verse = "#{number}[#{letters}]?"
|
152
|
+
chapter_verse_separator = '\s*[:"\.]\s*'
|
153
|
+
list_or_range_separator = '\s*[\-–—,;]\s*'
|
154
|
+
chapter_and_verse = "(?:#{number + chapter_verse_separator})?" + verse
|
155
|
+
chapter_and_verse_or_letter = "(?:#{chapter_and_verse}|[#{letters}])"
|
156
|
+
chapter_and_verse + "(?:#{list_or_range_separator + chapter_and_verse_or_letter})*"
|
157
|
+
end
|
156
158
|
|
157
|
-
|
158
|
-
|
159
|
-
chapter_ranges = []
|
160
|
-
ranges.each do |range|
|
161
|
-
min_chapter = Pericope.get_chapter(range.begin)
|
162
|
-
max_chapter = Pericope.get_chapter(range.end)
|
163
|
-
if min_chapter == max_chapter
|
164
|
-
chapter_ranges << range
|
165
|
-
else
|
166
|
-
chapter_ranges << Range.new(range.begin, Pericope.get_last_verse(book, min_chapter))
|
167
|
-
for chapter in (min_chapter+1)...max_chapter
|
168
|
-
chapter_ranges << Range.new(
|
169
|
-
Pericope.get_first_verse(book, chapter),
|
170
|
-
Pericope.get_last_verse(book, chapter))
|
171
|
-
end
|
172
|
-
chapter_ranges << Range.new(Pericope.get_first_verse(book, max_chapter), range.end)
|
173
|
-
end
|
159
|
+
def letters
|
160
|
+
@_letters ||= ("a"..max_letter).to_a.join
|
174
161
|
end
|
175
162
|
|
176
|
-
chapter_ranges.inject([]) {|array, range| array.concat(range.to_a)}
|
177
163
|
end
|
178
164
|
|
179
165
|
|
166
|
+
private
|
180
167
|
|
181
168
|
def well_formatted_reference(options={})
|
182
|
-
recent_chapter = nil # e.g. in 12:1-8, remember that 12 is the chapter when we parse the 8
|
183
|
-
recent_chapter = 1 unless book_has_chapters?
|
184
|
-
|
185
169
|
verse_range_separator = options.fetch(:verse_range_separator, "–") # en-dash
|
186
170
|
chapter_range_separator = options.fetch(:chapter_range_separator, "—") # em-dash
|
187
171
|
verse_list_separator = options.fetch(:verse_list_separator, ", ")
|
@@ -189,353 +173,64 @@ class Pericope
|
|
189
173
|
always_print_verse_range = options.fetch(:always_print_verse_range, false)
|
190
174
|
always_print_verse_range = true unless book_has_chapters?
|
191
175
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
min_verse = Pericope.get_verse(range.begin)
|
196
|
-
max_chapter = Pericope.get_chapter(range.end)
|
197
|
-
max_verse = Pericope.get_verse(range.end)
|
176
|
+
recent_chapter = nil # e.g. in 12:1-8, remember that 12 is the chapter when we parse the 8
|
177
|
+
recent_chapter = 1 unless book_has_chapters?
|
178
|
+
recent_verse = nil
|
198
179
|
|
180
|
+
ranges.each_with_index.each_with_object("") do |(range, i), s|
|
199
181
|
if i > 0
|
200
|
-
if recent_chapter ==
|
182
|
+
if recent_chapter == range.begin.chapter
|
201
183
|
s << verse_list_separator
|
202
184
|
else
|
203
185
|
s << chapter_list_separator
|
204
186
|
end
|
205
187
|
end
|
206
188
|
|
207
|
-
|
208
|
-
|
209
|
-
s <<
|
189
|
+
last_verse = Pericope.get_max_verse(book, range.end.chapter)
|
190
|
+
if !always_print_verse_range && range.begin.verse == 1 && range.begin.whole? && (range.end.verse > last_verse || range.end.whole? && range.end.verse == last_verse)
|
191
|
+
s << range.begin.chapter.to_s
|
192
|
+
s << "#{chapter_range_separator}#{range.end.chapter}" if range.end.chapter > range.begin.chapter
|
210
193
|
else
|
211
|
-
if
|
212
|
-
s <<
|
194
|
+
if range.begin.partial? && range.begin.verse == recent_verse
|
195
|
+
s << range.begin.letter
|
213
196
|
else
|
214
|
-
recent_chapter
|
215
|
-
s << "#{min_chapter}:#{min_verse}"
|
197
|
+
s << range.begin.to_s(with_chapter: recent_chapter != range.begin.chapter)
|
216
198
|
end
|
217
199
|
|
218
|
-
if range.
|
219
|
-
|
220
|
-
|
221
|
-
s << "#{verse_range_separator}#{max_verse}"
|
200
|
+
if range.begin != range.end
|
201
|
+
if range.begin.chapter == range.end.chapter
|
202
|
+
s << "#{verse_range_separator}#{range.end}"
|
222
203
|
else
|
223
|
-
|
224
|
-
s << "#{chapter_range_separator}#{max_chapter}:#{max_verse}"
|
204
|
+
s << "#{chapter_range_separator}#{range.end.to_s(with_chapter: true)}"
|
225
205
|
end
|
226
206
|
end
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
s
|
231
|
-
end
|
232
|
-
|
233
|
-
|
234
207
|
|
235
|
-
|
236
|
-
|
237
|
-
return false unless book == pericope.book
|
238
|
-
|
239
|
-
ranges.each do |self_range|
|
240
|
-
pericope.ranges.each do |other_range|
|
241
|
-
return true if (self_range.end >= other_range.begin) and (self_range.begin <= other_range.end)
|
208
|
+
recent_chapter = range.end.chapter
|
209
|
+
recent_verse = range.end.verse if range.end.partial?
|
242
210
|
end
|
243
211
|
end
|
244
|
-
|
245
|
-
false
|
246
|
-
end
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
def self.get_max_verse(book, chapter)
|
251
|
-
id = (book * 1000000) + (chapter * 1000)
|
252
|
-
CHAPTER_VERSE_COUNTS[id]
|
253
|
-
end
|
254
|
-
|
255
|
-
def self.get_max_chapter(book)
|
256
|
-
BOOK_CHAPTER_COUNTS[book]
|
257
|
-
end
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
private
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
def set_book(value)
|
266
|
-
@book = value || raise(ArgumentError, "must specify book")
|
267
|
-
@book_name = Pericope::BOOK_NAMES[@book]
|
268
|
-
@book_chapter_count = Pericope::BOOK_CHAPTER_COUNTS[@book]
|
269
|
-
end
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
def self.get_first_verse(book, chapter)
|
274
|
-
get_id(book, chapter, 1)
|
275
212
|
end
|
276
213
|
|
277
|
-
def
|
278
|
-
|
279
|
-
end
|
280
|
-
|
281
|
-
def self.get_next_verse(id)
|
282
|
-
id + 1
|
283
|
-
end
|
284
|
-
|
285
|
-
def self.get_start_of_next_chapter(id)
|
286
|
-
book = get_book(id)
|
287
|
-
chapter = get_chapter(id) + 1
|
288
|
-
verse = 1
|
289
|
-
get_id(book, chapter, verse)
|
290
|
-
end
|
291
|
-
|
292
|
-
def self.to_valid_book(book)
|
293
|
-
coerce_to_range(book, 1..66)
|
294
|
-
end
|
295
|
-
|
296
|
-
def self.to_valid_chapter(book, chapter)
|
297
|
-
coerce_to_range(chapter, 1..get_max_chapter(book))
|
298
|
-
end
|
299
|
-
|
300
|
-
def self.to_valid_verse(book, chapter, verse)
|
301
|
-
coerce_to_range(verse, 1..get_max_verse(book, chapter))
|
302
|
-
end
|
214
|
+
def group_array_into_ranges(verses)
|
215
|
+
return [] if verses.nil? or verses.empty?
|
303
216
|
|
304
|
-
|
305
|
-
return range.begin if number < range.begin
|
306
|
-
return range.end if number > range.end
|
307
|
-
number
|
308
|
-
end
|
309
|
-
|
310
|
-
def self.get_id(book, chapter, verse)
|
311
|
-
book = to_valid_book(book)
|
312
|
-
chapter = to_valid_chapter(book, chapter)
|
313
|
-
verse = to_valid_verse(book, chapter, verse)
|
314
|
-
|
315
|
-
(book * 1000000) + (chapter * 1000) + verse
|
316
|
-
end
|
317
|
-
|
318
|
-
def self.get_book(id)
|
319
|
-
id / 1000000 # the book is everything left of the least significant 6 digits
|
320
|
-
end
|
321
|
-
|
322
|
-
def self.get_chapter(id)
|
323
|
-
(id % 1000000) / 1000 # the chapter is the 3rd through 6th most significant digits
|
324
|
-
end
|
325
|
-
|
326
|
-
def self.get_verse(id)
|
327
|
-
id % 1000 # the verse is the 3 least significant digits
|
328
|
-
end
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
def self.group_array_into_ranges(array)
|
333
|
-
return [] if array.nil? or array.empty?
|
334
|
-
|
335
|
-
array.flatten!
|
336
|
-
array.compact!
|
337
|
-
array.sort!
|
217
|
+
verses = verses.flatten.compact.sort.map { |verse| Verse.parse(verse) }
|
338
218
|
|
339
219
|
ranges = []
|
340
|
-
|
341
|
-
range_end =
|
342
|
-
while
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
if (next_value == get_next_verse(range_end)) ||
|
347
|
-
(next_value == get_start_of_next_chapter(range_end))
|
348
|
-
range_end = next_value
|
220
|
+
range_begin = verses.shift
|
221
|
+
range_end = range_begin
|
222
|
+
while verse = verses.shift
|
223
|
+
if verse > range_end.next
|
224
|
+
ranges << Range.new(range_begin, range_end)
|
225
|
+
range_begin = range_end = verse
|
349
226
|
else
|
350
|
-
|
351
|
-
range_start = range_end = next_value
|
227
|
+
range_end = verse
|
352
228
|
end
|
353
229
|
end
|
354
|
-
ranges << (range_start..range_end)
|
355
|
-
|
356
|
-
ranges
|
357
|
-
end
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
# matches the first valid Bible reference in the supplied string
|
362
|
-
def self.match_one(text)
|
363
|
-
match_all(text) do |attributes|
|
364
|
-
return attributes
|
365
|
-
end
|
366
|
-
nil
|
367
|
-
end
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
# matches all valid Bible references in the supplied string
|
372
|
-
def self.match_all(text)
|
373
|
-
text.scan(Pericope::PERICOPE_PATTERN) do
|
374
|
-
match = Regexp.last_match
|
375
|
-
book = BOOK_IDS[match.captures.find_index(&:itself)]
|
376
|
-
|
377
|
-
ranges = parse_reference(book, match[67])
|
378
|
-
next if ranges.empty?
|
379
|
-
|
380
|
-
attributes = {
|
381
|
-
:original_string => match.to_s,
|
382
|
-
:book => book,
|
383
|
-
:ranges => ranges
|
384
|
-
}
|
385
|
-
|
386
|
-
yield attributes, match
|
387
|
-
end
|
388
|
-
end
|
389
|
-
|
390
|
-
def self.parse_reference(book, reference)
|
391
|
-
parse_ranges(book, normalize_reference(reference).split(/[,;]/))
|
392
|
-
end
|
393
230
|
|
394
|
-
|
395
|
-
NORMALIZATIONS.reduce(reference.to_s) { |reference, (regex, replacement)| reference.gsub(regex, replacement) }
|
231
|
+
ranges << Range.new(range_begin, range_end)
|
396
232
|
end
|
397
233
|
|
398
|
-
def self.parse_ranges(book, ranges)
|
399
|
-
recent_chapter = nil # e.g. in 12:1-8, remember that 12 is the chapter when we parse the 8
|
400
|
-
recent_chapter = 1 if !book_has_chapters?(book)
|
401
|
-
ranges.map do |range|
|
402
|
-
range = range.split('-') # parse the low end of a verse range and the high end separately
|
403
|
-
range << range[0] if (range.length < 2) # treat 12:4 as 12:4-12:4
|
404
|
-
lower_chapter_verse = range[0].split(':').map(&:to_i) # parse "3:28" to [3,28]
|
405
|
-
upper_chapter_verse = range[1].split(':').map(&:to_i) # parse "3:28" to [3,28]
|
406
|
-
|
407
|
-
# treat Mark 3-1 as Mark 3-3 and, eventually, Mark 3:1-35
|
408
|
-
if (lower_chapter_verse.length == 1) &&
|
409
|
-
(upper_chapter_verse.length == 1) &&
|
410
|
-
(upper_chapter_verse[0] < lower_chapter_verse[0])
|
411
|
-
upper_chapter_verse = lower_chapter_verse.dup
|
412
|
-
end
|
413
|
-
|
414
|
-
# make sure the low end of the range and the high end of the range
|
415
|
-
# are composed of arrays with two appropriate values: [chapter, verse]
|
416
|
-
chapter_range = false
|
417
|
-
if lower_chapter_verse.length < 2
|
418
|
-
if recent_chapter
|
419
|
-
lower_chapter_verse.unshift recent_chapter # e.g. parsing 11 in 12:1-8,11 => remember that 12 is the chapter
|
420
|
-
else
|
421
|
-
lower_chapter_verse[0] = Pericope.to_valid_chapter(book, lower_chapter_verse[0])
|
422
|
-
lower_chapter_verse << 1 # no verse specified; this is a range of chapters, start with verse 1
|
423
|
-
chapter_range = true
|
424
|
-
end
|
425
|
-
else
|
426
|
-
lower_chapter_verse[0] = Pericope.to_valid_chapter(book, lower_chapter_verse[0])
|
427
|
-
end
|
428
|
-
lower_chapter_verse[1] = Pericope.to_valid_verse(book, *lower_chapter_verse)
|
429
|
-
|
430
|
-
if upper_chapter_verse.length < 2
|
431
|
-
if chapter_range
|
432
|
-
upper_chapter_verse[0] = Pericope.to_valid_chapter(book, upper_chapter_verse[0])
|
433
|
-
upper_chapter_verse << Pericope.get_max_verse(book, upper_chapter_verse[0]) # this is a range of chapters, end with the last verse
|
434
|
-
else
|
435
|
-
upper_chapter_verse.unshift lower_chapter_verse[0] # e.g. parsing 8 in 12:1-8 => remember that 12 is the chapter
|
436
|
-
end
|
437
|
-
else
|
438
|
-
upper_chapter_verse[0] = Pericope.to_valid_chapter(book, upper_chapter_verse[0])
|
439
|
-
end
|
440
|
-
upper_chapter_verse[1] = Pericope.to_valid_verse(book, *upper_chapter_verse)
|
441
|
-
|
442
|
-
recent_chapter = upper_chapter_verse[0] # remember the last chapter
|
443
|
-
|
444
|
-
Range.new(
|
445
|
-
Pericope.get_id(book, *lower_chapter_verse),
|
446
|
-
Pericope.get_id(book, *upper_chapter_verse))
|
447
|
-
end
|
448
|
-
end
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
BOOK_PATTERN = %r{\b(?:
|
453
|
-
(?:(?:3|iii|third|3rd)\s*(?:
|
454
|
-
(john|joh|jon|jhn|jh|jo|jn)
|
455
|
-
))|
|
456
|
-
(?:(?:2|ii|second|2nd)\s*(?:
|
457
|
-
(samuels|samuel|sam|sa|sm)|
|
458
|
-
(kings|king|kngs|kgs|kg|k)|
|
459
|
-
(chronicles|chronicle|chron|chrn|chr)|
|
460
|
-
(john|joh|jon|jhn|jh|jo|jn)|
|
461
|
-
(corinthians?|cor?|corint?h?|corth)|
|
462
|
-
(thessalonians?|thes{1,}|the?s?)|
|
463
|
-
(timothy|tim|tm|ti)|
|
464
|
-
(peter|pete|pet|ptr|pe|pt|pr)
|
465
|
-
))|
|
466
|
-
(?:(?:1|i|first|1st)\s*(?:
|
467
|
-
(samuels|samuel|sam|sa|sm)|
|
468
|
-
(kings|king|kngs|kgs|kg|k)|
|
469
|
-
(chronicles|chronicle|chron|chrn|chr)|
|
470
|
-
(john|joh|jon|jhn|jh|jo|jn)|
|
471
|
-
(corinthians?|cor?|corint?h?|corth)|
|
472
|
-
(thessalonians?|thes{1,}|the?s?)|
|
473
|
-
(timothy|tim|tm|ti)|
|
474
|
-
(peter|pete|pet|ptr|pe|pt|pr)
|
475
|
-
))|
|
476
|
-
(genesis|gen|gn|ge)|
|
477
|
-
(exodus|exod|exo|exd|ex)|
|
478
|
-
(leviticus|lev|levi|le|lv)|
|
479
|
-
(numbers|number|numb|num|nmb|nu|nm)|
|
480
|
-
(deuteronomy|deut|deu|dt)|
|
481
|
-
(joshua|josh|jsh|jos)|
|
482
|
-
(judges|jdgs|judg|jdg)|
|
483
|
-
(ruth|rut|rth|ru)|
|
484
|
-
(isaiah|isa|is|ia|isai|isah)|
|
485
|
-
(ezra|ezr)|
|
486
|
-
(nehemiah|neh|ne)|
|
487
|
-
(esther|esth|est|es)|
|
488
|
-
(job|jb)|
|
489
|
-
(psalms|psalm|pslms|pslm|psm|psa|ps)|
|
490
|
-
(proverbs|proverb|prov|prv|prvb|prvbs|pv)|
|
491
|
-
(ecclesiastes|eccles|eccl|ecc|ecl)|
|
492
|
-
((?:the\s?)?song\s?of\s?solomon|(?:the\s?)?song\s?of\s?songs|sn?gs?|songs?|so?s|sol?|son|s\s?of\s?\ss)|
|
493
|
-
(jeremiah?|jer?|jr|jere)|
|
494
|
-
(lamentations?|lam?|lm)|
|
495
|
-
(ezekiel|ezek|eze|ezk)|
|
496
|
-
(daniel|dan|dn|dl|da)|
|
497
|
-
(hosea|hos|ho|hs)|
|
498
|
-
(joel|jl)|
|
499
|
-
(amos|amo|ams|am)|
|
500
|
-
(obadiah|obadia|obad|oba|obd|ob)|
|
501
|
-
(jonah|jon)|
|
502
|
-
(micah|mica|mic|mi)|
|
503
|
-
(nahum|nah|nahu|na)|
|
504
|
-
(habakk?uk|habk?)|
|
505
|
-
(zephaniah?|ze?ph?)|
|
506
|
-
(haggai|ha?gg?)|
|
507
|
-
(zechariah?|ze?ch?)|
|
508
|
-
(malachi|mal)|
|
509
|
-
(matthew|matt|mat|ma|mt)|
|
510
|
-
(mark|mrk|mk)|
|
511
|
-
(luke|luk|lk|lu)|
|
512
|
-
(john|joh|jon|jhn|jh|jo|jn)|
|
513
|
-
(acts|act|ac)|
|
514
|
-
(romans|roman|roms|rom|rms|ro|rm)|
|
515
|
-
(galatians|galatian|galat|gala|gal|ga)|
|
516
|
-
(ephesians?|eph?|ephe?s?)|
|
517
|
-
(philippians?|phi?l|php|phi|philipp?)|
|
518
|
-
(colossi?ans?|col?)|
|
519
|
-
(titus|tit|ti)|
|
520
|
-
(philemon|phl?mn?|philem?)|
|
521
|
-
(hebrews|hebrew|heb)|
|
522
|
-
(james|jam|jas|jm|js|ja)|
|
523
|
-
(jude)|
|
524
|
-
(revelations|revelation|revel|rev|rv|re)
|
525
|
-
)}ix.freeze
|
526
|
-
|
527
|
-
# The order books of the Bible are matched
|
528
|
-
BOOK_IDS = [ 64, 10, 12, 14, 63, 47, 53, 55, 61, 9, 11, 13, 62, 46, 52, 54, 60, 1, 2, 3, 4, 5, 6, 7, 8, 23, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 48, 49, 50, 51, 56, 57, 58, 59, 65, 66 ].freeze
|
529
|
-
|
530
|
-
BOOK_NAMES = [nil, "Genesis", "Exodus", "Leviticus", "Numbers", "Deuteronomy", "Joshua", "Judges", "Ruth", "1 Samuel", "2 Samuel", "1 Kings", "2 Kings", "1 Chronicles", "2 Chronicles", "Ezra", "Nehemiah", "Esther", "Job", "Psalm", "Proverbs", "Ecclesiastes", "Song of Solomon", "Isaiah", "Jeremiah", "Lamentations", "Ezekiel", "Daniel", "Hosea", "Joel", "Amos", "Obadiah", "Jonah", "Micah", "Nahum", "Habakkuk", "Zephaniah", "Haggai", "Zechariah", "Malachi", "Matthew", "Mark", "Luke", "John", "Acts", "Romans", "1 Corinthians", "2 Corinthians", "Galatians", "Ephesians", "Philippians", "Colossians", "1 Thessalonians", "2 Thessalonians", "1 Timothy", "2 Timothy", "Titus", "Philemon", "Hebrews", "James", "1 Peter", "2 Peter", "1 John", "2 John", "3 John", "Jude", "Revelation"].freeze
|
531
|
-
|
532
|
-
REFERENCE_PATTERN = '(?:\s*\d{1,3})(?:\s*[:\"\.]\s*\d{1,3}[ab]?(?:\s*[,;]\s*(?:\d{1,3}[:\"\.])?\s*\d{1,3}[ab]?)*)?(?:\s*[-–—]\s*(?:\d{1,3}\s*[:\"\.])?(?:\d{1,3}[ab]?)(?:\s*[,;]\s*(?:\d{1,3}\s*[:\"\.])?\s*\d{1,3}[ab]?)*)*'
|
533
|
-
|
534
|
-
PERICOPE_PATTERN = /#{BOOK_PATTERN.source.gsub(/[ \n]/, "")}\.?(#{REFERENCE_PATTERN})/i
|
535
|
-
|
536
|
-
NORMALIZATIONS = [
|
537
|
-
[/(\d+)[".](\d+)/, '\1:\2'], # 12"5 and 12.5 -> 12:5
|
538
|
-
[/[–—]/, '-'], # convert em dash and en dash to -
|
539
|
-
[/[^0-9,:;\-–—]/, ''] # remove everything but [0-9,;:-]
|
540
|
-
]
|
541
234
|
end
|
235
|
+
|
236
|
+
Pericope.max_letter = "d"
|
data/lib/pericope/cli.rb
CHANGED
@@ -4,11 +4,7 @@ require "cli/base"
|
|
4
4
|
class Pericope
|
5
5
|
class CLI
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
ALLOWED_COMMANDS = %w{help normalize parse substitute reverse-substitute usage}
|
10
|
-
|
11
|
-
|
7
|
+
ALLOWED_COMMANDS = %w{help normalize parse usage}
|
12
8
|
|
13
9
|
def self.run(command, *args)
|
14
10
|
if ALLOWED_COMMANDS.member?(command)
|
@@ -56,26 +52,6 @@ Glossary
|
|
56
52
|
|
57
53
|
|
58
54
|
|
59
|
-
def substitute
|
60
|
-
begin
|
61
|
-
print Pericope.sub(input)
|
62
|
-
rescue
|
63
|
-
print $!.to_s
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
def reverse_substitute
|
70
|
-
begin
|
71
|
-
print Pericope.rsub(input)
|
72
|
-
rescue
|
73
|
-
print $!.to_s
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
|
78
|
-
|
79
55
|
def usage
|
80
56
|
print <<-USAGE
|
81
57
|
|
@@ -88,8 +64,6 @@ Commands
|
|
88
64
|
help Prints more information about pericope
|
89
65
|
normalize Accepts a pericope and returns a properly-formatted pericope
|
90
66
|
parse Accepts a pericope and returns a list of verse IDs
|
91
|
-
substitute Accepts a block of text and replaces all pericopes in the text with verse IDs
|
92
|
-
reverse-substitute Accepts a block of text and replaces collections of verse IDs with pericopes
|
93
67
|
usage Prints this message
|
94
68
|
|
95
69
|
USAGE
|
@@ -0,0 +1,264 @@
|
|
1
|
+
require "pericope/range"
|
2
|
+
require "pericope/verse"
|
3
|
+
|
4
|
+
class Pericope
|
5
|
+
module Parsing
|
6
|
+
|
7
|
+
# Differs from Pericope.new in that it won't raise an exception
|
8
|
+
# if text does not contain a pericope but will return nil instead.
|
9
|
+
def parse_one(text)
|
10
|
+
parse(text) do |pericope|
|
11
|
+
return pericope
|
12
|
+
end
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse(text)
|
17
|
+
pericopes = []
|
18
|
+
match_all(text) do |attributes|
|
19
|
+
pericope = Pericope.new(attributes)
|
20
|
+
if block_given?
|
21
|
+
yield pericope
|
22
|
+
else
|
23
|
+
pericopes << pericope
|
24
|
+
end
|
25
|
+
end
|
26
|
+
block_given? ? text : pericopes
|
27
|
+
end
|
28
|
+
|
29
|
+
def split(text)
|
30
|
+
segments = []
|
31
|
+
start = 0
|
32
|
+
|
33
|
+
match_all(text) do |attributes, match|
|
34
|
+
|
35
|
+
pretext = text.slice(start...match.begin(0))
|
36
|
+
if pretext.length > 0
|
37
|
+
segments << pretext
|
38
|
+
yield pretext if block_given?
|
39
|
+
end
|
40
|
+
|
41
|
+
pericope = Pericope.new(attributes)
|
42
|
+
segments << pericope
|
43
|
+
yield pericope if block_given?
|
44
|
+
|
45
|
+
start = match.end(0)
|
46
|
+
end
|
47
|
+
|
48
|
+
pretext = text.slice(start...text.length)
|
49
|
+
if pretext.length > 0
|
50
|
+
segments << pretext
|
51
|
+
yield pretext if block_given?
|
52
|
+
end
|
53
|
+
|
54
|
+
segments
|
55
|
+
end
|
56
|
+
|
57
|
+
def match_one(text)
|
58
|
+
match_all(text) do |attributes|
|
59
|
+
return attributes
|
60
|
+
end
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def match_all(text)
|
65
|
+
text.scan(Pericope.regexp) do
|
66
|
+
match = Regexp.last_match
|
67
|
+
book = BOOK_IDS[match.captures.find_index(&:itself)]
|
68
|
+
|
69
|
+
ranges = parse_reference(book, match[67])
|
70
|
+
next if ranges.empty?
|
71
|
+
|
72
|
+
attributes = {
|
73
|
+
:original_string => match.to_s,
|
74
|
+
:book => book,
|
75
|
+
:ranges => ranges
|
76
|
+
}
|
77
|
+
|
78
|
+
yield attributes, match
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def parse_reference(book, reference)
|
83
|
+
parse_ranges(book, normalize_reference(reference).split(/[,;]/))
|
84
|
+
end
|
85
|
+
|
86
|
+
def normalize_reference(reference)
|
87
|
+
normalizations.reduce(reference.to_s) { |reference, (regex, replacement)| reference.gsub(regex, replacement) }
|
88
|
+
end
|
89
|
+
|
90
|
+
def parse_ranges(book, ranges)
|
91
|
+
default_chapter = nil
|
92
|
+
default_chapter = 1 unless book_has_chapters?(book)
|
93
|
+
default_verse = nil
|
94
|
+
|
95
|
+
ranges.map do |range|
|
96
|
+
range_begin_string, range_end_string = range.split("-")
|
97
|
+
|
98
|
+
# treat 12:4 as 12:4-12:4
|
99
|
+
range_end_string ||= range_begin_string
|
100
|
+
|
101
|
+
range_begin = parse_reference_fragment(range_begin_string, default_chapter: default_chapter, default_verse: default_verse)
|
102
|
+
|
103
|
+
# no verse specified; this is a range of chapters, start with verse 1
|
104
|
+
chapter_range = false
|
105
|
+
if range_begin.needs_verse?
|
106
|
+
range_begin.verse = 1
|
107
|
+
chapter_range = true
|
108
|
+
end
|
109
|
+
|
110
|
+
range_begin.chapter = to_valid_chapter(book, range_begin.chapter)
|
111
|
+
range_begin.verse = to_valid_verse(book, range_begin.chapter, range_begin.verse)
|
112
|
+
|
113
|
+
if range_begin_string == range_end_string && !chapter_range
|
114
|
+
range_end = range_begin.dup
|
115
|
+
else
|
116
|
+
range_end = parse_reference_fragment(range_end_string, default_chapter: (range_begin.chapter unless chapter_range))
|
117
|
+
range_end.chapter = to_valid_chapter(book, range_end.chapter)
|
118
|
+
|
119
|
+
# treat Mark 3-1 as Mark 3-3 and, eventually, Mark 3:1-35
|
120
|
+
range_end.chapter = range_begin.chapter if range_end.chapter < range_begin.chapter
|
121
|
+
|
122
|
+
# this is a range of chapters, end with the last verse
|
123
|
+
if range_end.needs_verse?
|
124
|
+
range_end.verse = get_max_verse(book, range_end.chapter)
|
125
|
+
else
|
126
|
+
range_end.verse = to_valid_verse(book, range_end.chapter, range_end.verse)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# e.g. parsing 11 in 12:1-8,11 => remember that 12 is the chapter
|
131
|
+
default_chapter = range_end.chapter
|
132
|
+
|
133
|
+
# e.g. parsing c in 9:12a, c => remember that 12 is the verse
|
134
|
+
default_verse = range_end.verse
|
135
|
+
|
136
|
+
range = Range.new(range_begin.to_verse(book: book), range_end.to_verse(book: book))
|
137
|
+
|
138
|
+
# an 'a' at the beginning of a range is redundant
|
139
|
+
range.begin.letter = nil if range.begin.letter == "a" && range.end.to_i > range.begin.to_i
|
140
|
+
|
141
|
+
# a 'c' at the end of a range is redundant
|
142
|
+
range.end.letter = nil if range.end.letter == max_letter && range.end.to_i > range.begin.to_i
|
143
|
+
|
144
|
+
range
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def parse_reference_fragment(input, default_chapter: nil, default_verse: nil)
|
149
|
+
chapter, verse, letter = input.match(Pericope.fragment_regexp).captures
|
150
|
+
chapter = default_chapter unless chapter
|
151
|
+
chapter, verse = [verse, nil] unless chapter
|
152
|
+
verse = default_verse unless verse
|
153
|
+
letter = nil unless verse
|
154
|
+
ReferenceFragment.new(chapter.to_i, verse&.to_i, letter)
|
155
|
+
end
|
156
|
+
|
157
|
+
def to_valid_chapter(book, chapter)
|
158
|
+
coerce_to_range(chapter, 1..get_max_chapter(book))
|
159
|
+
end
|
160
|
+
|
161
|
+
def to_valid_verse(book, chapter, verse)
|
162
|
+
coerce_to_range(verse, 1..get_max_verse(book, chapter))
|
163
|
+
end
|
164
|
+
|
165
|
+
def coerce_to_range(number, range)
|
166
|
+
return range.begin if number < range.begin
|
167
|
+
return range.end if number > range.end
|
168
|
+
number
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
ReferenceFragment = Struct.new(:chapter, :verse, :letter) do
|
173
|
+
def needs_verse?
|
174
|
+
verse.nil?
|
175
|
+
end
|
176
|
+
|
177
|
+
def to_verse(book:)
|
178
|
+
Verse.new(book, chapter, verse, letter)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
|
184
|
+
BOOK_PATTERN = %r{\b(?:
|
185
|
+
(?:(?:3|iii|third|3rd)\s*(?:
|
186
|
+
(john|joh|jon|jhn|jh|jo|jn)
|
187
|
+
))|
|
188
|
+
(?:(?:2|ii|second|2nd)\s*(?:
|
189
|
+
(samuels|samuel|sam|sa|sm)|
|
190
|
+
(kings|king|kngs|kgs|kg|k)|
|
191
|
+
(chronicles|chronicle|chron|chrn|chr)|
|
192
|
+
(john|joh|jon|jhn|jh|jo|jn)|
|
193
|
+
(corinthians?|cor?|corint?h?|corth)|
|
194
|
+
(thessalonians?|thes{1,}|the?s?)|
|
195
|
+
(timothy|tim|tm|ti)|
|
196
|
+
(peter|pete|pet|ptr|pe|pt|pr)
|
197
|
+
))|
|
198
|
+
(?:(?:1|i|first|1st)\s*(?:
|
199
|
+
(samuels|samuel|sam|sa|sm)|
|
200
|
+
(kings|king|kngs|kgs|kg|k)|
|
201
|
+
(chronicles|chronicle|chron|chrn|chr)|
|
202
|
+
(john|joh|jon|jhn|jh|jo|jn)|
|
203
|
+
(corinthians?|cor?|corint?h?|corth)|
|
204
|
+
(thessalonians?|thes{1,}|the?s?)|
|
205
|
+
(timothy|tim|tm|ti)|
|
206
|
+
(peter|pete|pet|ptr|pe|pt|pr)
|
207
|
+
))|
|
208
|
+
(genesis|gen|gn|ge)|
|
209
|
+
(exodus|exod|exo|exd|ex)|
|
210
|
+
(leviticus|lev|levi|le|lv)|
|
211
|
+
(numbers|number|numb|num|nmb|nu|nm)|
|
212
|
+
(deuteronomy|deut|deu|dt)|
|
213
|
+
(joshua|josh|jsh|jos)|
|
214
|
+
(judges|jdgs|judg|jdg)|
|
215
|
+
(ruth|rut|rth|ru)|
|
216
|
+
(isaiah|isa|is|ia|isai|isah)|
|
217
|
+
(ezra|ezr)|
|
218
|
+
(nehemiah|neh|ne)|
|
219
|
+
(esther|esth|est|es)|
|
220
|
+
(job|jb)|
|
221
|
+
(psalms|psalm|pslms|pslm|psm|psa|ps)|
|
222
|
+
(proverbs|proverb|prov|prv|prvb|prvbs|pv)|
|
223
|
+
(ecclesiastes|eccles|eccl|ecc|ecl)|
|
224
|
+
((?:the\s?)?song\s?of\s?solomon|(?:the\s?)?song\s?of\s?songs|sn?gs?|songs?|so?s|sol?|son|s\s?of\s?\ss)|
|
225
|
+
(jeremiah?|jer?|jr|jere)|
|
226
|
+
(lamentations?|lam?|lm)|
|
227
|
+
(ezekiel|ezek|eze|ezk)|
|
228
|
+
(daniel|dan|dn|dl|da)|
|
229
|
+
(hosea|hos|ho|hs)|
|
230
|
+
(joel|jl)|
|
231
|
+
(amos|amo|ams|am)|
|
232
|
+
(obadiah|obadia|obad|oba|obd|ob)|
|
233
|
+
(jonah|jon)|
|
234
|
+
(micah|mica|mic|mi)|
|
235
|
+
(nahum|nah|nahu|na)|
|
236
|
+
(habakk?uk|habk?)|
|
237
|
+
(zephaniah?|ze?ph?)|
|
238
|
+
(haggai|ha?gg?)|
|
239
|
+
(zechariah?|ze?ch?)|
|
240
|
+
(malachi|mal)|
|
241
|
+
(matthew|matt|mat|ma|mt)|
|
242
|
+
(mark|mrk|mk)|
|
243
|
+
(luke|luk|lk|lu)|
|
244
|
+
(john|joh|jon|jhn|jh|jo|jn)|
|
245
|
+
(acts|act|ac)|
|
246
|
+
(romans|roman|roms|rom|rms|ro|rm)|
|
247
|
+
(galatians|galatian|galat|gala|gal|ga)|
|
248
|
+
(ephesians?|eph?|ephe?s?)|
|
249
|
+
(philippians?|phi?l|php|phi|philipp?)|
|
250
|
+
(colossi?ans?|col?)|
|
251
|
+
(titus|tit|ti)|
|
252
|
+
(philemon|phl?mn?|philem?)|
|
253
|
+
(hebrews|hebrew|heb)|
|
254
|
+
(james|jam|jas|jm|js|ja)|
|
255
|
+
(jude)|
|
256
|
+
(revelations|revelation|revel|rev|rv|re)
|
257
|
+
)}ix.freeze
|
258
|
+
|
259
|
+
# The order books of the Bible are matched
|
260
|
+
BOOK_IDS = [ 64, 10, 12, 14, 63, 47, 53, 55, 61, 9, 11, 13, 62, 46, 52, 54, 60, 1, 2, 3, 4, 5, 6, 7, 8, 23, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 48, 49, 50, 51, 56, 57, 58, 59, 65, 66 ].freeze
|
261
|
+
|
262
|
+
BOOK_NAMES = [nil, "Genesis", "Exodus", "Leviticus", "Numbers", "Deuteronomy", "Joshua", "Judges", "Ruth", "1 Samuel", "2 Samuel", "1 Kings", "2 Kings", "1 Chronicles", "2 Chronicles", "Ezra", "Nehemiah", "Esther", "Job", "Psalm", "Proverbs", "Ecclesiastes", "Song of Solomon", "Isaiah", "Jeremiah", "Lamentations", "Ezekiel", "Daniel", "Hosea", "Joel", "Amos", "Obadiah", "Jonah", "Micah", "Nahum", "Habakkuk", "Zephaniah", "Haggai", "Zechariah", "Malachi", "Matthew", "Mark", "Luke", "John", "Acts", "Romans", "1 Corinthians", "2 Corinthians", "Galatians", "Ephesians", "Philippians", "Colossians", "1 Thessalonians", "2 Thessalonians", "1 Timothy", "2 Timothy", "Titus", "Philemon", "Hebrews", "James", "1 Peter", "2 Peter", "1 John", "2 John", "3 John", "Jude", "Revelation"].freeze
|
263
|
+
|
264
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class Pericope
|
2
|
+
class Range
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize(first, last)
|
6
|
+
@begin = first
|
7
|
+
@end = last
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :begin
|
11
|
+
attr_reader :end
|
12
|
+
|
13
|
+
def ==(other)
|
14
|
+
return true if equal? other
|
15
|
+
|
16
|
+
other.kind_of?(Pericope::Range) and
|
17
|
+
self.begin == other.begin and
|
18
|
+
self.end == other.end
|
19
|
+
end
|
20
|
+
|
21
|
+
alias_method :eql?, :==
|
22
|
+
|
23
|
+
def hash
|
24
|
+
self.begin.hash ^ self.end.hash
|
25
|
+
end
|
26
|
+
|
27
|
+
def each
|
28
|
+
return to_enum unless block_given?
|
29
|
+
|
30
|
+
if self.begin == self.end
|
31
|
+
yield self.begin
|
32
|
+
return self
|
33
|
+
end
|
34
|
+
|
35
|
+
current = self.begin
|
36
|
+
last_verse = self.end.whole
|
37
|
+
while current < last_verse
|
38
|
+
yield current
|
39
|
+
current = current.succ
|
40
|
+
end
|
41
|
+
|
42
|
+
if self.end.partial?
|
43
|
+
"a".upto(self.end.letter).each do |letter|
|
44
|
+
yield Verse.new(self.end.book, self.end.chapter, self.end.verse, letter)
|
45
|
+
end
|
46
|
+
else
|
47
|
+
yield self.end
|
48
|
+
end
|
49
|
+
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
def inspect
|
54
|
+
"#{self.begin.to_id}..#{self.end.to_id}"
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
class Pericope
|
2
|
+
Verse = Struct.new(:book, :chapter, :verse, :letter) do
|
3
|
+
include Comparable
|
4
|
+
|
5
|
+
def initialize(book, chapter, verse, letter=nil)
|
6
|
+
super
|
7
|
+
|
8
|
+
raise ArgumentError, "#{book} is not a valid book" if book < 1 || book > 66
|
9
|
+
raise ArgumentError, "#{chapter} is not a valid chapter" if chapter < 1 || chapter > Pericope.get_max_chapter(book)
|
10
|
+
raise ArgumentError, "#{verse} is not a valid verse" if verse < 1 || verse > Pericope.get_max_verse(book, chapter)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.parse(input)
|
14
|
+
return nil unless input
|
15
|
+
id = input.to_i
|
16
|
+
book = id / 1000000 # the book is everything left of the least significant 6 digits
|
17
|
+
chapter = (id % 1000000) / 1000 # the chapter is the 3rd through 6th most significant digits
|
18
|
+
verse = id % 1000 # the verse is the 3 least significant digits
|
19
|
+
letter = input[Pericope.letter_regexp] if input.is_a?(String)
|
20
|
+
new(book, chapter, verse, letter)
|
21
|
+
end
|
22
|
+
|
23
|
+
def <=>(other)
|
24
|
+
[ book, chapter, verse, letter || "a" ] <=> [ other.book, other.chapter, other.verse, other.letter || "a" ]
|
25
|
+
end
|
26
|
+
|
27
|
+
def ==(other)
|
28
|
+
to_a == other.to_a
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_i
|
32
|
+
book * 1000000 + chapter * 1000 + verse
|
33
|
+
end
|
34
|
+
alias :number :to_i
|
35
|
+
|
36
|
+
def to_id
|
37
|
+
"#{to_i}#{letter}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s(with_chapter: false)
|
41
|
+
with_chapter ? "#{chapter}:#{verse}#{letter}" : "#{verse}#{letter}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def partial?
|
45
|
+
!letter.nil?
|
46
|
+
end
|
47
|
+
|
48
|
+
def whole?
|
49
|
+
letter.nil?
|
50
|
+
end
|
51
|
+
|
52
|
+
def whole
|
53
|
+
return self unless partial?
|
54
|
+
self.class.new(book, chapter, verse)
|
55
|
+
end
|
56
|
+
|
57
|
+
def next
|
58
|
+
if partial? && (next_letter = letter.succ) <= Pericope.max_letter
|
59
|
+
return self.class.new(book, chapter, verse, next_letter)
|
60
|
+
end
|
61
|
+
|
62
|
+
next_verse = verse + 1
|
63
|
+
if next_verse > Pericope.get_max_verse(book, chapter)
|
64
|
+
next_chapter = chapter + 1
|
65
|
+
return nil if next_chapter > Pericope.get_max_chapter(book)
|
66
|
+
self.class.new(book, next_chapter, 1)
|
67
|
+
else
|
68
|
+
self.class.new(book, chapter, next_verse)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
alias :succ :next
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
data/lib/pericope/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pericope
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bob Lail
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-09-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: shoulda-context
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: minitest
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -100,8 +114,10 @@ files:
|
|
100
114
|
- lib/pericope.rb
|
101
115
|
- lib/pericope/cli.rb
|
102
116
|
- lib/pericope/data.rb
|
117
|
+
- lib/pericope/parsing.rb
|
118
|
+
- lib/pericope/range.rb
|
119
|
+
- lib/pericope/verse.rb
|
103
120
|
- lib/pericope/version.rb
|
104
|
-
- lib/tasks/pericope_tasks.rake
|
105
121
|
homepage: http://github.com/boblail/pericope
|
106
122
|
licenses: []
|
107
123
|
metadata: {}
|