mericope 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 570f05fdc93711d844e2cf6a74b80f4012582870
4
- data.tar.gz: 296642223ee90fa83818fab295234ef67df8df47
3
+ metadata.gz: 9626825b91d88875f6a69e7979225ae6e5c1e989
4
+ data.tar.gz: ec622bc610ff43ded6c733ea2e3af5d81faa41b7
5
5
  SHA512:
6
- metadata.gz: 342bd01c20d6d69d0bbb56d6f7b924c442a6b26e4dc146237bd343e0ec9679a36faa2c034ee54cd539413c128598ce8cd9010076ff2939833bd9a41be6093fe0
7
- data.tar.gz: 7a86fed1dd64ec86469c20cf7259d83a9f4182083cbd20b4d69453ef60893c6e92d223278d14a5925037c85a9ceb5b36c928eabd4fe09a8e0ef8a4084ca802a8
6
+ metadata.gz: 4ebc2ac328ccedb62b548ca935d8e2b1f3006e0dd5f9c4585df4b5a390a4146f36290f4805742e3a980bd94e86049ed6c0f6a3d7d761a8528f5b8b525a221486
7
+ data.tar.gz: 157f7e8d62a36e5e4bc35b0dd5356ab0bfa1759635ae7e57cf03e845ed545bde0bd137eb827b6c9a4553407a3a04189aadbf10aa5afa3e383a6db3fdb4e70c05
@@ -8,56 +8,49 @@ class Mericope
8
8
  :book_name,
9
9
  :original_string,
10
10
  :ranges
11
-
12
-
11
+
13
12
  def self.book_names
14
13
  @book_names ||= chapter_verse_count_books.
15
14
  map{ |r| r[2] }.uniq
16
15
  end
17
-
16
+
18
17
  def self.book_name_regexes
19
18
  @book_name_regexes ||= book_abbreviations.
20
19
  map { |book_number, book_regex| [book_number, /\b#{book_regex}\b/] }
21
20
  end
22
-
23
-
24
-
21
+
25
22
  def initialize(arg)
26
23
  case arg
27
24
  when String
28
25
  attributes = Mericope.match_one(arg)
29
26
  raise "no mericope found in #{arg} (#{arg.class})" if attributes.nil?
30
-
27
+
31
28
  @original_string = attributes[:original_string]
32
29
  set_book attributes[:book]
33
30
  @ranges = attributes[:ranges]
34
-
31
+
35
32
  when Array
36
33
  arg = arg.map(&:to_i)
37
34
  set_book Mericope.get_book(arg.first)
38
35
  @ranges = Mericope.group_array_into_ranges(arg)
39
-
36
+
40
37
  else
41
38
  attributes = arg
42
39
  @original_string = attributes[:original_string]
43
40
  set_book attributes[:book]
44
41
  @ranges = attributes[:ranges]
45
-
42
+
46
43
  end
47
44
  end
48
-
49
-
50
-
45
+
51
46
  def self.book_has_chapters?(book)
52
47
  book_chapter_counts[book - 1] > 1
53
48
  end
54
-
49
+
55
50
  def book_has_chapters?
56
51
  book_chapter_count > 1
57
52
  end
58
-
59
-
60
-
53
+
61
54
  # Differs from Mericope.new in that it won't raise an exception
62
55
  # if text does not contain a mericope but will return nil instead.
63
56
  def self.parse_one(text)
@@ -66,9 +59,7 @@ class Mericope
66
59
  end
67
60
  nil
68
61
  end
69
-
70
-
71
-
62
+
72
63
  def self.parse(text)
73
64
  mericopes = []
74
65
  match_all(text) do |attributes|
@@ -81,39 +72,37 @@ class Mericope
81
72
  end
82
73
  block_given? ? text : mericopes
83
74
  end
84
-
85
-
86
-
75
+
87
76
  def self.split(text, pattern=nil)
88
77
  puts "DEPRECATION NOTICE: split will no longer accept a 'pattern' argument in Mericope 0.7.0" if pattern
89
78
  segments = []
90
79
  start = 0
91
-
80
+
92
81
  match_all(text) do |attributes, match|
93
-
82
+
94
83
  pretext = text.slice(start...match.begin(0))
95
84
  if pretext.length > 0
96
85
  segments << pretext
97
86
  yield pretext if block_given?
98
87
  end
99
-
88
+
100
89
  mericope = Mericope.new(attributes)
101
90
  segments << mericope
102
91
  yield mericope if block_given?
103
-
92
+
104
93
  start = match.end(0)
105
94
  end
106
-
95
+
107
96
  pretext = text.slice(start...text.length)
108
97
  if pretext.length > 0
109
98
  segments << pretext
110
99
  yield pretext if block_given?
111
100
  end
112
-
101
+
113
102
  segments = ___split_segments_by_pattern(segments, pattern) if pattern
114
103
  segments
115
104
  end
116
-
105
+
117
106
  def self.___split_segments_by_pattern(segments, pattern)
118
107
  segments2 = []
119
108
  segments.each do |segment|
@@ -125,9 +114,7 @@ class Mericope
125
114
  end
126
115
  segments2
127
116
  end
128
-
129
-
130
-
117
+
131
118
  def self.extract(text)
132
119
  puts "DEPRECATION NOTICE: the 'extract' method will be removed in Mericope 0.7.0"
133
120
  segments = split(text)
@@ -142,9 +129,7 @@ class Mericope
142
129
  end
143
130
  {:text => text, :mericopes => mericopes}
144
131
  end
145
-
146
-
147
-
132
+
148
133
  def self.sub(text)
149
134
  segments = split(text)
150
135
  segments.inject("") do |text, segment|
@@ -155,31 +140,23 @@ class Mericope
155
140
  end
156
141
  end
157
142
  end
158
-
159
-
160
-
143
+
161
144
  def self.rsub(text)
162
145
  text.gsub(/\{\{(\d{7,8} ?)+\}\}/) do |match|
163
146
  ids = match[2...-2].split.collect(&:to_i)
164
147
  Mericope.new(ids).to_s
165
148
  end
166
149
  end
167
-
168
-
169
-
150
+
170
151
  def to_s
171
152
  "#{book_name} #{self.well_formatted_reference}"
172
153
  end
173
-
174
-
175
-
154
+
176
155
  def report
177
156
  puts "DEPRECATION NOTICE: the 'report' method will be removed in Mericope 0.7.0"
178
157
  " #{self.original_string} => #{self}"
179
158
  end
180
-
181
-
182
-
159
+
183
160
  def to_a
184
161
  # one range per chapter
185
162
  chapter_ranges = []
@@ -198,12 +175,10 @@ class Mericope
198
175
  chapter_ranges << Range.new(Mericope.get_first_verse(book, max_chapter), range.end)
199
176
  end
200
177
  end
201
-
178
+
202
179
  chapter_ranges.inject([]) {|array, range| array.concat(range.to_a)}
203
180
  end
204
-
205
-
206
-
181
+
207
182
  def well_formatted_reference
208
183
  recent_chapter = nil # e.g. in 12:1-8, remember that 12 is the chapter when we parse the 8
209
184
  recent_chapter = 1 unless book_has_chapters?
@@ -213,7 +188,7 @@ class Mericope
213
188
  max_chapter = Mericope.get_chapter(range.end)
214
189
  max_verse = Mericope.get_verse(range.end)
215
190
  s = ""
216
-
191
+
217
192
  if min_verse == 1 and max_verse >= Mericope.get_max_verse(book, max_chapter)
218
193
  s << min_chapter.to_s
219
194
  s << "-#{max_chapter}" if max_chapter > min_chapter
@@ -224,9 +199,9 @@ class Mericope
224
199
  recent_chapter = min_chapter
225
200
  s << "#{min_chapter}:#{min_verse}"
226
201
  end
227
-
202
+
228
203
  if range.count > 1
229
-
204
+
230
205
  s << "-"
231
206
  if min_chapter == max_chapter
232
207
  s << max_verse.to_s
@@ -236,124 +211,116 @@ class Mericope
236
211
  end
237
212
  end
238
213
  end
239
-
214
+
240
215
  s
241
216
  end.join(", ")
242
217
  end
243
-
244
-
245
-
218
+
246
219
  def intersects?(mericope)
247
220
  return false unless mericope.is_a?(Mericope)
248
221
  return false unless (self.book == mericope.book)
249
-
222
+
250
223
  self.ranges.each do |self_range|
251
224
  mericope.ranges.each do |other_range|
252
225
  return true if (self_range.end >= other_range.begin) and (self_range.begin <= other_range.end)
253
226
  end
254
227
  end
255
-
228
+
256
229
  false
257
230
  end
258
-
259
-
260
-
231
+
261
232
  def self.get_max_verse(book, chapter)
262
233
  id = (book * 1000000) + (chapter * 1000)
263
234
  chapter_verse_counts[id]
264
235
  end
265
-
236
+
266
237
  def self.get_max_chapter(book)
267
238
  book_chapter_counts[book - 1]
268
239
  end
269
-
270
-
271
-
272
- private
273
-
274
-
275
-
276
- def set_book(value)
277
- @book = value || raise(ArgumentError, "must specify book")
278
- @book_name = Mericope.book_names[@book - 1]
279
- @book_chapter_count = Mericope.book_chapter_counts[@book - 1]
280
- end
281
-
282
-
283
-
240
+
284
241
  def self.get_first_verse(book, chapter)
285
242
  get_id(book, chapter, 1)
286
243
  end
287
-
244
+
288
245
  def self.get_last_verse(book, chapter)
289
246
  get_id(book, chapter, get_max_verse(book, chapter))
290
247
  end
291
-
248
+
292
249
  def self.get_next_verse(id)
293
250
  id + 1
294
251
  end
295
-
252
+
296
253
  def self.get_start_of_next_chapter(id)
297
254
  book = get_book(id)
298
255
  chapter = get_chapter(id) + 1
299
256
  verse = 1
300
257
  get_id(book, chapter, verse)
301
258
  end
302
-
259
+
260
+ private
261
+
262
+ def set_book(value)
263
+ @book = value || raise(ArgumentError, "must specify book")
264
+ @book_name = Mericope.book_names[@book - 1]
265
+ @book_chapter_count = Mericope.book_chapter_counts[@book - 1]
266
+ end
267
+
268
+ def self.get_max_book
269
+ @max_book ||= chapter_verse_counts.keys.max / 1000000
270
+ end
271
+
303
272
  def self.to_valid_book(book)
304
- coerce_to_range(book, 1..66)
273
+ coerce_to_range(book, 1..get_max_book)
305
274
  end
306
-
275
+
307
276
  def self.to_valid_chapter(book, chapter)
308
277
  coerce_to_range(chapter, 1..get_max_chapter(book))
309
278
  end
310
-
279
+
311
280
  def self.to_valid_verse(book, chapter, verse)
312
281
  coerce_to_range(verse, 1..get_max_verse(book, chapter))
313
282
  end
314
-
283
+
315
284
  def self.coerce_to_range(number, range)
316
285
  return range.begin if number < range.begin
317
286
  return range.end if number > range.end
318
287
  number
319
288
  end
320
-
289
+
321
290
  def self.get_id(book, chapter, verse)
322
291
  book = to_valid_book(book)
323
292
  chapter = to_valid_chapter(book, chapter)
324
293
  verse = to_valid_verse(book, chapter, verse)
325
-
294
+
326
295
  (book * 1000000) + (chapter * 1000) + verse
327
296
  end
328
-
297
+
329
298
  def self.get_book(id)
330
299
  id / 1000000 # the book is everything left of the least significant 6 digits
331
300
  end
332
-
301
+
333
302
  def self.get_chapter(id)
334
303
  (id % 1000000) / 1000 # the chapter is the 3rd through 6th most significant digits
335
304
  end
336
-
305
+
337
306
  def self.get_verse(id)
338
307
  id % 1000 # the verse is the 3 least significant digits
339
308
  end
340
-
341
-
342
-
309
+
343
310
  def self.group_array_into_ranges(array)
344
311
  return [] if array.nil? or array.empty?
345
-
312
+
346
313
  array.flatten!
347
314
  array.compact!
348
315
  array.sort!
349
-
316
+
350
317
  ranges = []
351
318
  range_start = array.shift
352
319
  range_end = range_start
353
320
  while true
354
321
  next_value = array.shift
355
322
  break if next_value.nil?
356
-
323
+
357
324
  if (next_value == get_next_verse(range_end)) ||
358
325
  (next_value == get_start_of_next_chapter(range_end))
359
326
  range_end = next_value
@@ -363,12 +330,10 @@ private
363
330
  end
364
331
  end
365
332
  ranges << (range_start..range_end)
366
-
333
+
367
334
  ranges
368
335
  end
369
-
370
-
371
-
336
+
372
337
  # matches the first valid Bible reference in the supplied string
373
338
  def self.match_one(text)
374
339
  match_all(text) do |attributes|
@@ -376,30 +341,28 @@ private
376
341
  end
377
342
  nil
378
343
  end
379
-
380
-
381
-
344
+
382
345
  # matches all valid Bible references in the supplied string
383
346
  def self.match_all(text)
384
347
  text.scan(Mericope::MERICOPE_PATTERN) do
385
348
  match = Regexp.last_match
386
-
349
+
387
350
  book = recognize_book(match[1])
388
351
  next unless book
389
-
352
+
390
353
  ranges = parse_reference(book, match[2])
391
354
  next if ranges.empty?
392
-
355
+
393
356
  attributes = {
394
357
  :original_string => match.to_s,
395
358
  :book => book,
396
359
  :ranges => ranges
397
360
  }
398
-
361
+
399
362
  yield attributes, match
400
363
  end
401
364
  end
402
-
365
+
403
366
  def self.recognize_book(book)
404
367
  book = book.to_s.downcase
405
368
  book_name_regexes.each do |book_regex|
@@ -407,18 +370,18 @@ private
407
370
  end
408
371
  nil
409
372
  end
410
-
373
+
411
374
  def self.parse_reference(book, reference)
412
375
  reference = normalize_reference(reference)
413
376
  parse_ranges(book, reference.split(/[,;]/))
414
377
  end
415
-
378
+
416
379
  def self.normalize_reference(reference)
417
380
  reference = reference.to_s
418
381
  NORMALIZATIONS.each { |(regex, replacement)| reference.gsub!(regex, replacement) }
419
382
  reference
420
383
  end
421
-
384
+
422
385
  def self.parse_ranges(book, ranges)
423
386
  recent_chapter = nil # e.g. in 12:1-8, remember that 12 is the chapter when we parse the 8
424
387
  recent_chapter = 1 if !book_has_chapters?(book)
@@ -427,14 +390,14 @@ private
427
390
  range << range[0] if (range.length < 2) # treat 12:4 as 12:4-12:4
428
391
  lower_chapter_verse = range[0].split(':').map(&:to_i) # parse "3:28" to [3,28]
429
392
  upper_chapter_verse = range[1].split(':').map(&:to_i) # parse "3:28" to [3,28]
430
-
393
+
431
394
  # treat Mark 3-1 as Mark 3-3 and, eventually, Mark 3:1-35
432
395
  if (lower_chapter_verse.length == 1) &&
433
396
  (upper_chapter_verse.length == 1) &&
434
397
  (upper_chapter_verse[0] < lower_chapter_verse[0])
435
398
  upper_chapter_verse = lower_chapter_verse.dup
436
399
  end
437
-
400
+
438
401
  # make sure the low end of the range and the high end of the range
439
402
  # are composed of arrays with two appropriate values: [chapter, verse]
440
403
  chapter_range = false
@@ -450,7 +413,7 @@ private
450
413
  lower_chapter_verse[0] = Mericope.to_valid_chapter(book, lower_chapter_verse[0])
451
414
  end
452
415
  lower_chapter_verse[1] = Mericope.to_valid_verse(book, *lower_chapter_verse)
453
-
416
+
454
417
  if upper_chapter_verse.length < 2
455
418
  if chapter_range
456
419
  upper_chapter_verse[0] = Mericope.to_valid_chapter(book, upper_chapter_verse[0])
@@ -462,17 +425,15 @@ private
462
425
  upper_chapter_verse[0] = Mericope.to_valid_chapter(book, upper_chapter_verse[0])
463
426
  end
464
427
  upper_chapter_verse[1] = Mericope.to_valid_verse(book, *upper_chapter_verse)
465
-
428
+
466
429
  recent_chapter = upper_chapter_verse[0] # remember the last chapter
467
-
430
+
468
431
  Range.new(
469
432
  Mericope.get_id(book, *lower_chapter_verse),
470
433
  Mericope.get_id(book, *upper_chapter_verse))
471
434
  end
472
435
  end
473
-
474
-
475
-
436
+
476
437
  def self.load_chapter_verse_count_books
477
438
  path = File.expand_path(File.dirname(__FILE__) + "/../data/chapter_verse_count.txt")
478
439
  [].tap do |data|
@@ -485,16 +446,14 @@ private
485
446
  end
486
447
  end
487
448
  end
488
-
489
-
490
-
449
+
491
450
  def self.load_book_abbreviations
492
451
  path = File.expand_path(File.dirname(__FILE__) + "/../data/book_abbreviations.txt")
493
452
  book_abbreviations = []
494
453
  File.open(path) do |file|
495
454
  file.each do |text|
496
455
  unless text.start_with?("#") # skip comments
497
-
456
+
498
457
  # the file contains tab-separated values.
499
458
  # the first value is the ordinal of the book, subsequent values
500
459
  # represent abbreviations and misspellings that should be recognized
@@ -506,27 +465,19 @@ private
506
465
  end
507
466
  Hash[book_abbreviations]
508
467
  end
509
-
510
-
511
-
468
+
512
469
  def self.chapter_verse_count_books
513
470
  @chapter_verse_count_books ||= load_chapter_verse_count_books
514
471
  end
515
472
 
516
-
517
-
518
473
  def self.chapter_verse_counts
519
474
  @chapter_verse_counts ||= Hash[chapter_verse_count_books.map{ |r| [r[0], r[1]] }]
520
475
  end
521
-
522
-
523
-
476
+
524
477
  def self.book_abbreviations
525
478
  @book_abbreviations ||= load_book_abbreviations
526
479
  end
527
-
528
-
529
-
480
+
530
481
  def self.book_chapter_counts
531
482
  @book_chapter_counts ||= chapter_verse_count_books.
532
483
  # Group by book
@@ -534,14 +485,13 @@ private
534
485
  # Take the highest chapter number of each book
535
486
  map{ |book, grp| grp.last.last }
536
487
  end
537
-
538
-
539
- BOOK_PATTERN = /\b(?:(?:1|2|3|i+|first|second|third|1st|2nd|3rd) )?(?:\w+| of )\b/
540
-
488
+
489
+ BOOK_PATTERN = /\b(?:(?:1|2|3|4|i+|first|second|third|fourth|1st|2nd|3rd|4th|words of) )?(?:\w+| of )\b/i
490
+
541
491
  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]?)*)*'
542
-
492
+
543
493
  MERICOPE_PATTERN = /(#{BOOK_PATTERN})\.? (#{REFERENCE_PATTERN})/i
544
-
494
+
545
495
  NORMALIZATIONS = [
546
496
  [/(\d+)[".](\d+)/, '\1:\2'], # 12"5 and 12.5 -> 12:5
547
497
  [/[–—]/, '-'], # convert em dash and en dash to -
@@ -3,13 +3,10 @@ require 'cli/base'
3
3
 
4
4
  class Mericope
5
5
  class CLI
6
-
7
-
8
-
6
+ attr_reader :options, :input
7
+
9
8
  ALLOWED_COMMANDS = %w{help normalize parse substitute reverse-substitute usage}
10
-
11
-
12
-
9
+
13
10
  def self.run(command, *args)
14
11
  if ALLOWED_COMMANDS.member?(command)
15
12
  command = command.gsub(/-/, '_').to_sym
@@ -18,9 +15,7 @@ class Mericope
18
15
  CLI.new(*args).usage
19
16
  end
20
17
  end
21
-
22
-
23
-
18
+
24
19
  def help
25
20
  print <<-HELP
26
21
 
@@ -32,9 +27,7 @@ Glossary
32
27
 
33
28
  HELP
34
29
  end
35
-
36
-
37
-
30
+
38
31
  def normalize
39
32
  begin
40
33
  mericope = Mericope.new(input)
@@ -43,9 +36,7 @@ Glossary
43
36
  print $!.to_s
44
37
  end
45
38
  end
46
-
47
-
48
-
39
+
49
40
  def parse
50
41
  begin
51
42
  mericope = Mericope.new(input)
@@ -54,9 +45,7 @@ Glossary
54
45
  print $!.to_s
55
46
  end
56
47
  end
57
-
58
-
59
-
48
+
60
49
  def substitute
61
50
  begin
62
51
  print Mericope.sub(input)
@@ -64,9 +53,7 @@ Glossary
64
53
  print $!.to_s
65
54
  end
66
55
  end
67
-
68
-
69
-
56
+
70
57
  def reverse_substitute
71
58
  begin
72
59
  print Mericope.rsub(input)
@@ -74,9 +61,7 @@ Glossary
74
61
  print $!.to_s
75
62
  end
76
63
  end
77
-
78
-
79
-
64
+
80
65
  def usage
81
66
  print <<-USAGE
82
67
 
@@ -95,30 +80,17 @@ Commands
95
80
 
96
81
  USAGE
97
82
  end
98
-
99
-
100
-
83
+
101
84
  private
102
-
103
-
104
-
85
+
105
86
  def initialize(*args)
106
87
  @options = extract_options!(*args)
107
88
  @input = args.first
108
89
  @input = $stdin.read if $stdin.stat.pipe?
109
90
  end
110
-
111
-
112
-
113
- attr_reader :options, :input
114
-
115
-
116
-
91
+
117
92
  def extract_options!(*args)
118
93
  {} # No options accepted yet
119
94
  end
120
-
121
-
122
-
123
95
  end
124
96
  end
@@ -1,3 +1,3 @@
1
1
  class Mericope
2
- VERSION = "0.1.0" unless defined?(::Mericope::Version)
2
+ VERSION = "0.1.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mericope
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bob Lail
@@ -67,6 +67,20 @@ dependencies:
67
67
  - - "~>"
68
68
  - !ruby/object:Gem::Version
69
69
  version: '0.10'
70
+ - !ruby/object:Gem::Dependency
71
+ name: byebug
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
70
84
  description: It recognizes common abbreviations of the books of the Book of Mormon
71
85
  and a variety of ways of denoting ranges of chapters and verses. Based on Pericope.
72
86
  email: