pericope 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,30 +1,30 @@
1
1
  module CLI
2
2
  class Base
3
-
4
-
5
-
3
+
4
+
5
+
6
6
  def self.command(name, description, &block)
7
-
7
+
8
8
  end
9
-
10
-
11
-
9
+
10
+
11
+
12
12
  def self.print_commands
13
13
  usage = ""
14
-
14
+
15
15
  end
16
-
17
-
18
-
16
+
17
+
18
+
19
19
  private
20
-
21
-
22
-
20
+
21
+
22
+
23
23
  def commands
24
24
  @commands || {}
25
25
  end
26
-
27
-
28
-
26
+
27
+
28
+
29
29
  end
30
- end
30
+ end
@@ -1,23 +1,23 @@
1
1
  module CLI
2
2
  class Command
3
-
4
-
5
-
3
+
4
+
5
+
6
6
  def initialize(name, description, &block)
7
7
  @name, @description, @block = name, description, &block
8
8
  end
9
-
10
-
11
-
9
+
10
+
11
+
12
12
  attr_reader :name, :description
13
-
14
-
15
-
13
+
14
+
15
+
16
16
  def execute
17
17
  @block.call
18
18
  end
19
-
20
-
21
-
19
+
20
+
21
+
22
22
  end
23
- end
23
+ end
@@ -8,53 +8,57 @@ class Pericope
8
8
  :book_name,
9
9
  :original_string,
10
10
  :ranges
11
-
12
-
11
+
12
+
13
+
13
14
  def self.book_names
14
- @book_names ||= ["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 Songs", "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"]
15
+ load_book_abbreviations! unless defined?(@book_names)
16
+ @book_names
15
17
  end
16
-
18
+
17
19
  def self.book_name_regexes
18
- @book_name_regexes ||= book_abbreviations.map { |book_number, book_regex| [book_number, /\b#{book_regex}\b/] }
20
+ load_book_abbreviations! unless defined?(@book_name_regexes)
21
+ @book_name_regexes
19
22
  end
20
-
21
-
22
-
23
+
24
+
25
+
23
26
  def initialize(arg)
24
27
  case arg
25
28
  when String
26
29
  attributes = Pericope.match_one(arg)
27
30
  raise "no pericope found in #{arg} (#{arg.class})" if attributes.nil?
28
-
31
+
29
32
  @original_string = attributes[:original_string]
30
33
  set_book attributes[:book]
31
34
  @ranges = attributes[:ranges]
32
-
35
+
33
36
  when Array
37
+ arg = arg.map(&:to_i)
34
38
  set_book Pericope.get_book(arg.first)
35
39
  @ranges = Pericope.group_array_into_ranges(arg)
36
-
40
+
37
41
  else
38
42
  attributes = arg
39
43
  @original_string = attributes[:original_string]
40
44
  set_book attributes[:book]
41
45
  @ranges = attributes[:ranges]
42
-
46
+
43
47
  end
44
48
  end
45
-
46
-
47
-
49
+
50
+
51
+
48
52
  def self.book_has_chapters?(book)
49
53
  book_chapter_counts[book - 1] > 1
50
54
  end
51
-
55
+
52
56
  def book_has_chapters?
53
57
  book_chapter_count > 1
54
58
  end
55
-
56
-
57
-
59
+
60
+
61
+
58
62
  # Differs from Pericope.new in that it won't raise an exception
59
63
  # if text does not contain a pericope but will return nil instead.
60
64
  def self.parse_one(text)
@@ -63,9 +67,9 @@ class Pericope
63
67
  end
64
68
  nil
65
69
  end
66
-
67
-
68
-
70
+
71
+
72
+
69
73
  def self.parse(text)
70
74
  pericopes = []
71
75
  match_all(text) do |attributes|
@@ -78,39 +82,39 @@ class Pericope
78
82
  end
79
83
  block_given? ? text : pericopes
80
84
  end
81
-
82
-
83
-
85
+
86
+
87
+
84
88
  def self.split(text, pattern=nil)
85
89
  puts "DEPRECATION NOTICE: split will no longer accept a 'pattern' argument in Pericope 0.7.0" if pattern
86
90
  segments = []
87
91
  start = 0
88
-
92
+
89
93
  match_all(text) do |attributes, match|
90
-
94
+
91
95
  pretext = text.slice(start...match.begin(0))
92
96
  if pretext.length > 0
93
97
  segments << pretext
94
98
  yield pretext if block_given?
95
99
  end
96
-
100
+
97
101
  pericope = Pericope.new(attributes)
98
102
  segments << pericope
99
103
  yield pericope if block_given?
100
-
104
+
101
105
  start = match.end(0)
102
106
  end
103
-
107
+
104
108
  pretext = text.slice(start...text.length)
105
109
  if pretext.length > 0
106
110
  segments << pretext
107
111
  yield pretext if block_given?
108
112
  end
109
-
113
+
110
114
  segments = ___split_segments_by_pattern(segments, pattern) if pattern
111
115
  segments
112
116
  end
113
-
117
+
114
118
  def self.___split_segments_by_pattern(segments, pattern)
115
119
  segments2 = []
116
120
  segments.each do |segment|
@@ -122,9 +126,9 @@ class Pericope
122
126
  end
123
127
  segments2
124
128
  end
125
-
126
-
127
-
129
+
130
+
131
+
128
132
  def self.extract(text)
129
133
  puts "DEPRECATION NOTICE: the 'extract' method will be removed in Pericope 0.7.0"
130
134
  segments = split(text)
@@ -139,9 +143,9 @@ class Pericope
139
143
  end
140
144
  {:text => text, :pericopes => pericopes}
141
145
  end
142
-
143
-
144
-
146
+
147
+
148
+
145
149
  def self.sub(text)
146
150
  segments = split(text)
147
151
  segments.inject("") do |text, segment|
@@ -152,31 +156,31 @@ class Pericope
152
156
  end
153
157
  end
154
158
  end
155
-
156
-
157
-
159
+
160
+
161
+
158
162
  def self.rsub(text)
159
163
  text.gsub(/\{\{(\d{7,8} ?)+\}\}/) do |match|
160
164
  ids = match[2...-2].split.collect(&:to_i)
161
165
  Pericope.new(ids).to_s
162
166
  end
163
167
  end
164
-
165
-
166
-
168
+
169
+
170
+
167
171
  def to_s
168
172
  "#{book_name} #{self.well_formatted_reference}"
169
173
  end
170
-
171
-
172
-
174
+
175
+
176
+
173
177
  def report
174
178
  puts "DEPRECATION NOTICE: the 'report' method will be removed in Pericope 0.7.0"
175
179
  " #{self.original_string} => #{self}"
176
180
  end
177
-
178
-
179
-
181
+
182
+
183
+
180
184
  def to_a
181
185
  # one range per chapter
182
186
  chapter_ranges = []
@@ -195,12 +199,12 @@ class Pericope
195
199
  chapter_ranges << Range.new(Pericope.get_first_verse(book, max_chapter), range.end)
196
200
  end
197
201
  end
198
-
202
+
199
203
  chapter_ranges.inject([]) {|array, range| array.concat(range.to_a)}
200
204
  end
201
-
202
-
203
-
205
+
206
+
207
+
204
208
  def well_formatted_reference
205
209
  recent_chapter = nil # e.g. in 12:1-8, remember that 12 is the chapter when we parse the 8
206
210
  recent_chapter = 1 unless book_has_chapters?
@@ -210,7 +214,7 @@ class Pericope
210
214
  max_chapter = Pericope.get_chapter(range.end)
211
215
  max_verse = Pericope.get_verse(range.end)
212
216
  s = ""
213
-
217
+
214
218
  if min_verse == 1 and max_verse >= Pericope.get_max_verse(book, max_chapter)
215
219
  s << min_chapter.to_s
216
220
  s << "-#{max_chapter}" if max_chapter > min_chapter
@@ -221,9 +225,9 @@ class Pericope
221
225
  recent_chapter = min_chapter
222
226
  s << "#{min_chapter}:#{min_verse}"
223
227
  end
224
-
228
+
225
229
  if range.count > 1
226
-
230
+
227
231
  s << "-"
228
232
  if min_chapter == max_chapter
229
233
  s << max_verse.to_s
@@ -233,124 +237,124 @@ class Pericope
233
237
  end
234
238
  end
235
239
  end
236
-
240
+
237
241
  s
238
242
  end.join(", ")
239
243
  end
240
-
241
-
242
-
244
+
245
+
246
+
243
247
  def intersects?(pericope)
244
248
  return false unless pericope.is_a?(Pericope)
245
249
  return false unless (self.book == pericope.book)
246
-
250
+
247
251
  self.ranges.each do |self_range|
248
252
  pericope.ranges.each do |other_range|
249
253
  return true if (self_range.end >= other_range.begin) and (self_range.begin <= other_range.end)
250
254
  end
251
255
  end
252
-
256
+
253
257
  false
254
258
  end
255
-
256
-
257
-
259
+
260
+
261
+
258
262
  def self.get_max_verse(book, chapter)
259
263
  id = (book * 1000000) + (chapter * 1000)
260
264
  chapter_verse_counts[id]
261
265
  end
262
-
266
+
263
267
  def self.get_max_chapter(book)
264
268
  book_chapter_counts[book - 1]
265
269
  end
266
-
267
-
268
-
270
+
271
+
272
+
269
273
  private
270
-
271
-
272
-
274
+
275
+
276
+
273
277
  def set_book(value)
274
278
  @book = value || raise(ArgumentError, "must specify book")
275
279
  @book_name = Pericope.book_names[@book - 1]
276
280
  @book_chapter_count = Pericope.book_chapter_counts[@book - 1]
277
281
  end
278
-
279
-
280
-
282
+
283
+
284
+
281
285
  def self.get_first_verse(book, chapter)
282
286
  get_id(book, chapter, 1)
283
287
  end
284
-
288
+
285
289
  def self.get_last_verse(book, chapter)
286
290
  get_id(book, chapter, get_max_verse(book, chapter))
287
291
  end
288
-
292
+
289
293
  def self.get_next_verse(id)
290
294
  id + 1
291
295
  end
292
-
296
+
293
297
  def self.get_start_of_next_chapter(id)
294
298
  book = get_book(id)
295
299
  chapter = get_chapter(id) + 1
296
300
  verse = 1
297
301
  get_id(book, chapter, verse)
298
302
  end
299
-
303
+
300
304
  def self.to_valid_book(book)
301
305
  coerce_to_range(book, 1..66)
302
306
  end
303
-
307
+
304
308
  def self.to_valid_chapter(book, chapter)
305
309
  coerce_to_range(chapter, 1..get_max_chapter(book))
306
310
  end
307
-
311
+
308
312
  def self.to_valid_verse(book, chapter, verse)
309
313
  coerce_to_range(verse, 1..get_max_verse(book, chapter))
310
314
  end
311
-
315
+
312
316
  def self.coerce_to_range(number, range)
313
317
  return range.begin if number < range.begin
314
318
  return range.end if number > range.end
315
319
  number
316
320
  end
317
-
321
+
318
322
  def self.get_id(book, chapter, verse)
319
323
  book = to_valid_book(book)
320
324
  chapter = to_valid_chapter(book, chapter)
321
325
  verse = to_valid_verse(book, chapter, verse)
322
-
326
+
323
327
  (book * 1000000) + (chapter * 1000) + verse
324
328
  end
325
-
329
+
326
330
  def self.get_book(id)
327
331
  id / 1000000 # the book is everything left of the least significant 6 digits
328
332
  end
329
-
333
+
330
334
  def self.get_chapter(id)
331
335
  (id % 1000000) / 1000 # the chapter is the 3rd through 6th most significant digits
332
336
  end
333
-
337
+
334
338
  def self.get_verse(id)
335
339
  id % 1000 # the verse is the 3 least significant digits
336
340
  end
337
-
338
-
339
-
341
+
342
+
343
+
340
344
  def self.group_array_into_ranges(array)
341
345
  return [] if array.nil? or array.empty?
342
-
346
+
343
347
  array.flatten!
344
348
  array.compact!
345
349
  array.sort!
346
-
350
+
347
351
  ranges = []
348
352
  range_start = array.shift
349
353
  range_end = range_start
350
354
  while true
351
355
  next_value = array.shift
352
356
  break if next_value.nil?
353
-
357
+
354
358
  if (next_value == get_next_verse(range_end)) ||
355
359
  (next_value == get_start_of_next_chapter(range_end))
356
360
  range_end = next_value
@@ -360,12 +364,12 @@ private
360
364
  end
361
365
  end
362
366
  ranges << (range_start..range_end)
363
-
367
+
364
368
  ranges
365
369
  end
366
-
367
-
368
-
370
+
371
+
372
+
369
373
  # matches the first valid Bible reference in the supplied string
370
374
  def self.match_one(text)
371
375
  match_all(text) do |attributes|
@@ -373,30 +377,30 @@ private
373
377
  end
374
378
  nil
375
379
  end
376
-
377
-
378
-
380
+
381
+
382
+
379
383
  # matches all valid Bible references in the supplied string
380
384
  def self.match_all(text)
381
385
  text.scan(Pericope::PERICOPE_PATTERN) do
382
386
  match = Regexp.last_match
383
-
387
+
384
388
  book = recognize_book(match[1])
385
389
  next unless book
386
-
390
+
387
391
  ranges = parse_reference(book, match[2])
388
392
  next if ranges.empty?
389
-
393
+
390
394
  attributes = {
391
395
  :original_string => match.to_s,
392
396
  :book => book,
393
397
  :ranges => ranges
394
398
  }
395
-
399
+
396
400
  yield attributes, match
397
401
  end
398
402
  end
399
-
403
+
400
404
  def self.recognize_book(book)
401
405
  book = book.to_s.downcase
402
406
  book_name_regexes.each do |book_regex|
@@ -404,18 +408,18 @@ private
404
408
  end
405
409
  nil
406
410
  end
407
-
411
+
408
412
  def self.parse_reference(book, reference)
409
413
  reference = normalize_reference(reference)
410
414
  parse_ranges(book, reference.split(/[,;]/))
411
415
  end
412
-
416
+
413
417
  def self.normalize_reference(reference)
414
418
  reference = reference.to_s
415
419
  NORMALIZATIONS.each { |(regex, replacement)| reference.gsub!(regex, replacement) }
416
420
  reference
417
421
  end
418
-
422
+
419
423
  def self.parse_ranges(book, ranges)
420
424
  recent_chapter = nil # e.g. in 12:1-8, remember that 12 is the chapter when we parse the 8
421
425
  recent_chapter = 1 if !book_has_chapters?(book)
@@ -424,14 +428,14 @@ private
424
428
  range << range[0] if (range.length < 2) # treat 12:4 as 12:4-12:4
425
429
  lower_chapter_verse = range[0].split(':').map(&:to_i) # parse "3:28" to [3,28]
426
430
  upper_chapter_verse = range[1].split(':').map(&:to_i) # parse "3:28" to [3,28]
427
-
431
+
428
432
  # treat Mark 3-1 as Mark 3-3 and, eventually, Mark 3:1-35
429
433
  if (lower_chapter_verse.length == 1) &&
430
434
  (upper_chapter_verse.length == 1) &&
431
435
  (upper_chapter_verse[0] < lower_chapter_verse[0])
432
436
  upper_chapter_verse = lower_chapter_verse.dup
433
437
  end
434
-
438
+
435
439
  # make sure the low end of the range and the high end of the range
436
440
  # are composed of arrays with two appropriate values: [chapter, verse]
437
441
  chapter_range = false
@@ -447,7 +451,7 @@ private
447
451
  lower_chapter_verse[0] = Pericope.to_valid_chapter(book, lower_chapter_verse[0])
448
452
  end
449
453
  lower_chapter_verse[1] = Pericope.to_valid_verse(book, *lower_chapter_verse)
450
-
454
+
451
455
  if upper_chapter_verse.length < 2
452
456
  if chapter_range
453
457
  upper_chapter_verse[0] = Pericope.to_valid_chapter(book, upper_chapter_verse[0])
@@ -459,147 +463,91 @@ private
459
463
  upper_chapter_verse[0] = Pericope.to_valid_chapter(book, upper_chapter_verse[0])
460
464
  end
461
465
  upper_chapter_verse[1] = Pericope.to_valid_verse(book, *upper_chapter_verse)
462
-
466
+
463
467
  recent_chapter = upper_chapter_verse[0] # remember the last chapter
464
-
468
+
465
469
  Range.new(
466
470
  Pericope.get_id(book, *lower_chapter_verse),
467
471
  Pericope.get_id(book, *upper_chapter_verse))
468
472
  end
469
473
  end
470
-
471
-
472
-
473
- def self.load_chapter_verse_counts
474
+
475
+
476
+
477
+ def self.load_chapter_verse_count_books!
478
+ current_book_id = nil
479
+ chapters = 0
480
+ @chapter_verse_counts = {}
481
+ @book_chapter_counts = []
482
+
474
483
  path = File.expand_path(File.dirname(__FILE__) + "/../data/chapter_verse_count.txt")
475
- chapter_verse_counts = []
476
484
  File.open(path) do |file|
477
485
  file.each do |text|
478
- chapter_verse_counts << text.chomp.split("\t")[0..1].map(&:to_i)
486
+ row = text.chomp.split("\t")
487
+ id, verses = row[0].to_i, row[1].to_i
488
+ book_id = get_book(id)
489
+
490
+ @chapter_verse_counts[id] = verses
491
+
492
+ unless current_book_id == book_id
493
+ @book_chapter_counts.push chapters if current_book_id
494
+ current_book_id = book_id
495
+ end
496
+
497
+ chapters = get_chapter(id)
479
498
  end
480
499
  end
481
- Hash[chapter_verse_counts]
500
+
501
+ @book_chapter_counts.push chapters
482
502
  end
483
-
484
-
485
-
486
- def self.load_book_abbreviations
503
+
504
+
505
+
506
+ def self.load_book_abbreviations!
507
+ @book_names = []
508
+ @book_name_regexes = {}
509
+
487
510
  path = File.expand_path(File.dirname(__FILE__) + "/../data/book_abbreviations.txt")
488
- book_abbreviations = []
489
511
  File.open(path) do |file|
490
512
  file.each do |text|
491
- unless text.start_with?("#") # skip comments
492
-
493
- # the file contains tab-separated values.
494
- # the first value is the ordinal of the book, subsequent values
495
- # represent abbreviations and misspellings that should be recognized
496
- # as the aforementioned book.
497
- segments = text.chomp.split("\t")
498
- book_abbreviations << [segments.shift.to_i, "(?:#{segments.join("|")})"]
499
- end
513
+ next if text.start_with?("#") # skip comments
514
+
515
+ # the file contains tab-separated values.
516
+ # the first value is the ordinal of the book, subsequent values
517
+ # represent abbreviations and misspellings that should be recognized
518
+ # as the aforementioned book.
519
+ segments = text.chomp.split("\t")
520
+ book_id = segments.shift.to_i
521
+ @book_names[book_id - 1] = segments.shift
522
+ @book_name_regexes[book_id] = /\b(?:#{segments.join("|")})\b/
500
523
  end
501
524
  end
502
- Hash[book_abbreviations]
503
525
  end
504
-
505
-
506
-
526
+
527
+
528
+
507
529
  def self.chapter_verse_counts
508
- @chapter_verse_counts ||= load_chapter_verse_counts
530
+ load_chapter_verse_count_books! unless defined?(@chapter_verse_counts)
531
+ @chapter_verse_counts
509
532
  end
510
-
511
-
512
-
513
- def self.book_abbreviations
514
- @book_abbreviations ||= load_book_abbreviations
515
- end
516
-
517
-
518
-
533
+
534
+
535
+
519
536
  def self.book_chapter_counts
520
- @book_chapter_counts ||= [
521
- # Chapters Book Name Book Number
522
- 50, # Genesis 1
523
- 40, # Exodus 2
524
- 27, # Leviticus 3
525
- 36, # Numbers 4
526
- 34, # Deuteronomy 5
527
- 24, # Joshua 6
528
- 21, # Judges 7
529
- 4, # Ruth 8
530
- 31, # 1 Samuel 9
531
- 24, # 2 Samuel 10
532
- 22, # 1 Kings 11
533
- 25, # 2 Kings 12
534
- 29, # 1 Chronicles 13
535
- 36, # 2 Chronicles 14
536
- 10, # Ezra 15
537
- 13, # Nehemiah 16
538
- 10, # Esther 17
539
- 42, # Job 18
540
- 150, # Psalm 19
541
- 31, # Proverbs 20
542
- 12, # Ecclesiastes 21
543
- 8, # Song of Songs 22
544
- 66, # Isaiah 23
545
- 52, # Jeremiah 24
546
- 5, # Lamentations 25
547
- 48, # Ezekiel 26
548
- 12, # Daniel 27
549
- 14, # Hosea 28
550
- 3, # Joel 29
551
- 9, # Amos 30
552
- 1, # Obadiah 31
553
- 4, # Jonah 32
554
- 7, # Micah 33
555
- 3, # Nahum 34
556
- 3, # Habakkuk 35
557
- 3, # Zephaniah 36
558
- 2, # Haggai 37
559
- 14, # Zechariah 38
560
- 4, # Malachi 39
561
- 28, # Matthew 40
562
- 16, # Mark 41
563
- 24, # Luke 42
564
- 21, # John 43
565
- 28, # Acts 44
566
- 16, # Romans 45
567
- 16, # 1 Corinthians 46
568
- 13, # 2 Corinthians 47
569
- 6, # Galatians 48
570
- 6, # Ephesians 49
571
- 4, # Philippians 50
572
- 4, # Colossians 51
573
- 5, # 1 Thessalonians 52
574
- 3, # 2 Thessalonians 53
575
- 6, # 1 Timothy 54
576
- 4, # 2 Timothy 55
577
- 3, # Titus 56
578
- 1, # Philemon 57
579
- 13, # Hebrews 58
580
- 5, # James 59
581
- 5, # 1 Peter 60
582
- 3, # 2 Peter 61
583
- 5, # 1 John 62
584
- 1, # 2 John 63
585
- 1, # 3 John 64
586
- 1, # Jude 65
587
- 22] # Revelation 66
588
- end
589
-
590
-
537
+ load_chapter_verse_count_books! unless defined?(@book_chapter_counts)
538
+ @book_chapter_counts
539
+ end
540
+
541
+
591
542
  BOOK_PATTERN = /\b(?:(?:1|2|3|i+|first|second|third|1st|2nd|3rd) )?(?:\w+| of )\b/
592
-
543
+
593
544
  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]?)*)*'
594
-
545
+
595
546
  PERICOPE_PATTERN = /(#{BOOK_PATTERN})\.? (#{REFERENCE_PATTERN})/i
596
-
547
+
597
548
  NORMALIZATIONS = [
598
549
  [/(\d+)[".](\d+)/, '\1:\2'], # 12"5 and 12.5 -> 12:5
599
550
  [/[–—]/, '-'], # convert em dash and en dash to -
600
551
  [/[^0-9,:;\-–—]/, ''] # remove everything but [0-9,;:-]
601
552
  ]
602
-
603
-
604
-
605
553
  end