mericope 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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: