bible270 0.17.2 → 0.18.0
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 +4 -4
- data/CHANGELOG.md +70 -29
- data/README.md +28 -2
- data/app/controllers/bible270/admin_controller.rb +7 -1
- data/app/controllers/bible270/profiles_controller.rb +2 -0
- data/app/helpers/bible270/plan_helper.rb +15 -4
- data/app/models/bible270/reader.rb +14 -0
- data/app/views/bible270/admin/show.html.erb +2 -1
- data/app/views/bible270/days/_reading.html.erb +2 -2
- data/app/views/bible270/profiles/edit.html.erb +1 -0
- data/app/views/bible270/shared/_passage_source.html.erb +13 -0
- data/app/views/bible270/shared/_styles.html.erb +9 -0
- data/db/migrate/20260101000014_add_blue_letter_bible_to_bible270_readers.rb +7 -0
- data/db/migrate/20260101000015_add_passage_source_to_bible270_readers.rb +26 -0
- data/lib/bible270/configuration.rb +1 -6
- data/lib/bible270/migration_reconciler.rb +252 -0
- data/lib/bible270/translations.rb +61 -5
- data/lib/bible270/version.rb +1 -1
- data/lib/tasks/bible270_migrations.rake +57 -0
- metadata +6 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7ba4b388cfe909e84f06899d72159471aab1a506154fc72854659d64320a3390
|
|
4
|
+
data.tar.gz: ebcf629ca6a12f57bf87d72073115c9a4655cec15e018e9349813aaab11a2e41
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1b202223f06388e4b7f95d53b52f84cc84018ed7ac7ad8e49c48467525e7d76a0bd60185e46bd9b6223a83d7939666b6d7e7d6f1360ca625967452691d1fc528
|
|
7
|
+
data.tar.gz: 8c0b363492837cf5fb715ad13effdee46829cb34dad426e2bbfa8b24c4834f61ea16b2411b5c2e0fc0098d6b0fa397e8fe0cee487a763670f75f8f9fbb2bbb6c
|
data/CHANGELOG.md
CHANGED
|
@@ -6,7 +6,51 @@ All notable changes to bible270. Format follows [Keep a Changelog](https://keepa
|
|
|
6
6
|
|
|
7
7
|
See the changes since the last release:
|
|
8
8
|
|
|
9
|
-
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.
|
|
9
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.17.2...main)**
|
|
10
|
+
|
|
11
|
+
## [0.17.2] - 2026-08-08
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- reader Bible translation preferences editable by admin users
|
|
16
|
+
- admin broadcast email tools
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- extract first and last names from OmniAuth display names
|
|
21
|
+
- expand CI coverage reporting and add Ruby 4.0 coverage
|
|
22
|
+
|
|
23
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.17.1...v0.17.2)**
|
|
24
|
+
|
|
25
|
+
## [0.17.1] - 2026-08-06
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- likes for reflections
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- display timestamps using the local time zone
|
|
34
|
+
- rename reflections to 'posts' in UI headers, because we like alliteration
|
|
35
|
+
|
|
36
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.17.0...v0.17.1)**
|
|
37
|
+
|
|
38
|
+
## [0.17.0] - 2026-08-05
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- a reflections page for browsing recent comments
|
|
43
|
+
- the ability for readers to edit their reflections
|
|
44
|
+
|
|
45
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.16.1...v0.17.0)**
|
|
46
|
+
|
|
47
|
+
## [0.16.1] - 2026-08-05
|
|
48
|
+
|
|
49
|
+
### Changed
|
|
50
|
+
|
|
51
|
+
- refactor mailer URL helpers to use the engine's routes
|
|
52
|
+
|
|
53
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.16.0...v0.16.1)**
|
|
10
54
|
|
|
11
55
|
## [0.16.0] - 2026-08-05
|
|
12
56
|
|
|
@@ -19,8 +63,6 @@ See the changes since the last release:
|
|
|
19
63
|
|
|
20
64
|
## [0.15.3] - 2026-08-04
|
|
21
65
|
|
|
22
|
-
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.15.2...v0.15.3)**
|
|
23
|
-
|
|
24
66
|
### Added
|
|
25
67
|
|
|
26
68
|
- `Bible270::Checkoff.reset_column_information` to migrations to prevent `UnknownAttributeError` during backfills.
|
|
@@ -30,9 +72,9 @@ See the changes since the last release:
|
|
|
30
72
|
|
|
31
73
|
- 'Today' now derived from system time, not UTC
|
|
32
74
|
|
|
33
|
-
|
|
75
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.15.2...v0.15.3)**
|
|
34
76
|
|
|
35
|
-
|
|
77
|
+
## [0.15.2] - 2026-08-04
|
|
36
78
|
|
|
37
79
|
### Added
|
|
38
80
|
|
|
@@ -42,9 +84,9 @@ See the changes since the last release:
|
|
|
42
84
|
|
|
43
85
|
- consistent reader sort order by first name in 'People' and 'Admin'
|
|
44
86
|
|
|
45
|
-
|
|
87
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.15.1...v0.15.2)**
|
|
46
88
|
|
|
47
|
-
|
|
89
|
+
## [0.15.1] - 2026-08-03
|
|
48
90
|
|
|
49
91
|
### Added
|
|
50
92
|
|
|
@@ -59,9 +101,9 @@ See the changes since the last release:
|
|
|
59
101
|
|
|
60
102
|
- old zip file of tests
|
|
61
103
|
|
|
62
|
-
|
|
104
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.15.0...v0.15.1)**
|
|
63
105
|
|
|
64
|
-
|
|
106
|
+
## [0.15.0] - 2026-08-02
|
|
65
107
|
|
|
66
108
|
### Added
|
|
67
109
|
|
|
@@ -73,9 +115,9 @@ See the changes since the last release:
|
|
|
73
115
|
|
|
74
116
|
- mobile session requiring sign-in every time: use persistent sessions using 'remember-me' cookie
|
|
75
117
|
|
|
76
|
-
|
|
118
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.14.1...v0.15.0)**
|
|
77
119
|
|
|
78
|
-
|
|
120
|
+
## [0.14.1] - 2026-07-30
|
|
79
121
|
|
|
80
122
|
### Changed
|
|
81
123
|
|
|
@@ -87,9 +129,9 @@ See the changes since the last release:
|
|
|
87
129
|
- display 'Start' not 'Continue' if reader is on day 1
|
|
88
130
|
- adjust badge vertical alignment in styles
|
|
89
131
|
|
|
90
|
-
|
|
132
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.14.0...v0.14.1)**
|
|
91
133
|
|
|
92
|
-
|
|
134
|
+
## [0.14.0] - 2026-07-30
|
|
93
135
|
|
|
94
136
|
### Added
|
|
95
137
|
|
|
@@ -107,16 +149,10 @@ See the changes since the last release:
|
|
|
107
149
|
wrote an initializer that crashed the host app on boot. A test now renders every generator template
|
|
108
150
|
and compiles the result, across three mount points.
|
|
109
151
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.13.0...v0.13.1)**
|
|
113
|
-
|
|
114
|
-
Nothing consequential.
|
|
152
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.13.0...v0.14.0)**
|
|
115
153
|
|
|
116
154
|
## [0.13.0] — 2026-07-29
|
|
117
155
|
|
|
118
|
-
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.12.0...v0.13.0)**
|
|
119
|
-
|
|
120
156
|
### Added
|
|
121
157
|
|
|
122
158
|
- favicon support
|
|
@@ -127,9 +163,9 @@ Nothing consequential.
|
|
|
127
163
|
|
|
128
164
|
- Correct verb for reading prompt: 'Start' and 'Continue
|
|
129
165
|
|
|
130
|
-
|
|
166
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.12.0...v0.13.0)**
|
|
131
167
|
|
|
132
|
-
|
|
168
|
+
## [0.12.0] — 2026-07-29
|
|
133
169
|
|
|
134
170
|
### Added
|
|
135
171
|
|
|
@@ -141,9 +177,9 @@ Nothing consequential.
|
|
|
141
177
|
- Top menu shows 'Progress' in place of user display name
|
|
142
178
|
- Dependabot gem updates to minitest and rubocop
|
|
143
179
|
|
|
144
|
-
|
|
180
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.11.0...v0.12.0)**
|
|
145
181
|
|
|
146
|
-
|
|
182
|
+
## [0.11.0] — 2026-07-28
|
|
147
183
|
|
|
148
184
|
### Added
|
|
149
185
|
|
|
@@ -195,9 +231,9 @@ Nothing consequential.
|
|
|
195
231
|
- Migrations: `20260101000007` (settings). Active Storage tables are the host app's
|
|
196
232
|
(`bin/rails active_storage:install`).
|
|
197
233
|
|
|
198
|
-
|
|
234
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.10.0...v0.11.0)**
|
|
199
235
|
|
|
200
|
-
|
|
236
|
+
## [0.10.0] — 2026-07-26
|
|
201
237
|
|
|
202
238
|
### Reading plan
|
|
203
239
|
|
|
@@ -270,9 +306,9 @@ Nothing consequential.
|
|
|
270
306
|
|
|
271
307
|
- Table names changed from `bible_reading_plan_*` in the 0.9.0 line; this is a fresh install.
|
|
272
308
|
|
|
273
|
-
|
|
309
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.9.0...v0.10.0)**
|
|
274
310
|
|
|
275
|
-
|
|
311
|
+
## [0.9.0] — 2026-07-25
|
|
276
312
|
|
|
277
313
|
### Changed
|
|
278
314
|
|
|
@@ -290,6 +326,8 @@ Nothing consequential.
|
|
|
290
326
|
* `Plan.nt_groups`, `format_pp_segment`, `pp_segment_length` (superseded by the shared `format_segment` and `segment_length`), and `totals[:nt_rest_days]`.
|
|
291
327
|
* `nt_rest_days` remains but now always returns an empty array.
|
|
292
328
|
|
|
329
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.6.2...v0.9.0)**
|
|
330
|
+
|
|
293
331
|
## [0.6.2] — 2026-07-25
|
|
294
332
|
|
|
295
333
|
This is the initial public release.
|
|
@@ -352,6 +390,10 @@ This is the initial public release.
|
|
|
352
390
|
|
|
353
391
|
* Email sign-in requires working Action Mailer delivery in the host application. Set `config.mailer_from`; delivery is inline by default, or set `email_sign_in_deliver_later` when a queue backend is available.
|
|
354
392
|
|
|
393
|
+
[0.17.2]: https://github.com/avonderluft/bible270/releases/tag/v0.17.2
|
|
394
|
+
[0.17.1]: https://github.com/avonderluft/bible270/releases/tag/v0.17.1
|
|
395
|
+
[0.17.0]: https://github.com/avonderluft/bible270/releases/tag/v0.17.0
|
|
396
|
+
[0.16.1]: https://github.com/avonderluft/bible270/releases/tag/v0.16.1
|
|
355
397
|
[0.16.0]: https://github.com/avonderluft/bible270/releases/tag/v0.16.0
|
|
356
398
|
[0.15.3]: https://github.com/avonderluft/bible270/releases/tag/v0.15.3
|
|
357
399
|
[0.15.2]: https://github.com/avonderluft/bible270/releases/tag/v0.15.2
|
|
@@ -359,7 +401,6 @@ This is the initial public release.
|
|
|
359
401
|
[0.15.0]: https://github.com/avonderluft/bible270/releases/tag/v0.15.0
|
|
360
402
|
[0.14.1]: https://github.com/avonderluft/bible270/releases/tag/v0.14.1
|
|
361
403
|
[0.14.0]: https://github.com/avonderluft/bible270/releases/tag/v0.14.0
|
|
362
|
-
[0.13.1]: https://github.com/avonderluft/bible270/releases/tag/v0.13.1
|
|
363
404
|
[0.13.0]: https://github.com/avonderluft/bible270/releases/tag/v0.13.0
|
|
364
405
|
[0.12.0]: https://github.com/avonderluft/bible270/releases/tag/v0.12.0
|
|
365
406
|
[0.11.0]: https://github.com/avonderluft/bible270/releases/tag/v0.11.0
|
data/README.md
CHANGED
|
@@ -117,6 +117,29 @@ bin/rails bible270:install:migrations # skips ones you already have
|
|
|
117
117
|
bin/rails db:migrate
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
+
### Reconciling a copied database
|
|
121
|
+
|
|
122
|
+
A copied database can contain the Bible270 tables while its `schema_migrations` versions do not match
|
|
123
|
+
the timestamped migration copies in the local app. In that case, preview the reconciliation:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
bin/rails bible270:migrations:reconcile
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The task recognizes Bible270 migrations—and the related generated Active Storage migration used for
|
|
130
|
+
reader avatars—by name, then verifies their tables, columns, current indexes, and foreign keys. It
|
|
131
|
+
does not treat an existing table alone as sufficient. After reviewing the list and backing up the
|
|
132
|
+
database, record only the migrations already reflected in the schema:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
APPLY=1 bin/rails bible270:migrations:reconcile
|
|
136
|
+
bin/rails db:migrate
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Preview mode never writes to the database. Apply mode only inserts verified local versions into
|
|
140
|
+
`schema_migrations`; it does not create, remove, or alter application tables. Migrations whose changes
|
|
141
|
+
are missing remain pending for the final `db:migrate` command.
|
|
142
|
+
|
|
120
143
|
## Bible270 Mount point in your Rails app
|
|
121
144
|
|
|
122
145
|
The path lives in exactly **one** place, `config.mount_at`:
|
|
@@ -361,7 +384,10 @@ config.favicon = false # none; the host's favicon applies
|
|
|
361
384
|
It's emitted by the engine's layout, so if you point `config.layout` at your own layout, add
|
|
362
385
|
`<%= b270_favicon_tag %>` to its `<head>`.
|
|
363
386
|
|
|
364
|
-
References link to Bible Gateway by default
|
|
387
|
+
References link to Bible Gateway by default. Readers choose Bible Gateway or Blue Letter Bible from
|
|
388
|
+
their profile, and an admin can set the same source for any reader. The `HEB/GRK` option always uses
|
|
389
|
+
Blue Letter Bible: WLC for Old Testament, Psalms, and Proverbs readings, and mGNT for New Testament
|
|
390
|
+
readings. Set `passage_url_builder` to use another default reader, or a reader you host yourself.
|
|
365
391
|
|
|
366
392
|
## With ComfortableMediaSurfer
|
|
367
393
|
|
|
@@ -371,7 +397,7 @@ The CMS serves your content; this is a separate mounted app. Either **just link
|
|
|
371
397
|
|
|
372
398
|
| Table | Holds |
|
|
373
399
|
|-------|-------|
|
|
374
|
-
| `bible270_readers` | identity
|
|
400
|
+
| `bible270_readers` | identity, avatar, provider/uid or polymorphic `owner`, start date, translation and passage-reader preferences |
|
|
375
401
|
| `bible270_checkoffs` | one row per reader per day per track (`ot`/`nt`/`pp`), unique-indexed |
|
|
376
402
|
| `bible270_comments` | a reflection on a day, optionally scoped to a track, public to all |
|
|
377
403
|
| `bible270_sign_in_tokens` | magic-link tokens: email, **digest only**, expiry, consumed-at |
|
|
@@ -105,16 +105,22 @@ module Bible270
|
|
|
105
105
|
return redirect_to(admin_path, alert: 'No such reader.') if reader.nil?
|
|
106
106
|
|
|
107
107
|
code = params[:bible_version].to_s
|
|
108
|
+
source = params[:passage_source].presence || Reader::DEFAULT_PASSAGE_SOURCE
|
|
109
|
+
unless Reader::PASSAGE_SOURCES.include?(source)
|
|
110
|
+
return redirect_to admin_reader_path(reader), alert: 'That is not a reading-link source on the list.'
|
|
111
|
+
end
|
|
112
|
+
|
|
108
113
|
# Blank means "no preference": the reader follows the site default, which is
|
|
109
114
|
# not the same as choosing whichever translation the site happens to use now.
|
|
110
115
|
if code.empty?
|
|
111
|
-
reader.update(bible_version: nil)
|
|
116
|
+
reader.update(bible_version: nil, passage_source: source)
|
|
112
117
|
return redirect_to admin_reader_path(reader),
|
|
113
118
|
notice: "#{reader.display_name} now follows the site default " \
|
|
114
119
|
"(#{Bible270.config.bible_version})."
|
|
115
120
|
end
|
|
116
121
|
|
|
117
122
|
if reader.update_bible_version(code)
|
|
123
|
+
reader.update_column(:passage_source, source)
|
|
118
124
|
redirect_to admin_reader_path(reader),
|
|
119
125
|
notice: "#{reader.display_name} now reads the #{reader.bible_version_label}."
|
|
120
126
|
else
|
|
@@ -21,6 +21,8 @@ module Bible270
|
|
|
21
21
|
if params[:bible_version].present? && !current_reader.update_bible_version(params[:bible_version])
|
|
22
22
|
problems << 'a translation from the list'
|
|
23
23
|
end
|
|
24
|
+
source = params[:passage_source].presence || Reader::DEFAULT_PASSAGE_SOURCE
|
|
25
|
+
problems << 'a reading-link source from the list' unless current_reader.update_passage_source(source)
|
|
24
26
|
if params[:avatar].present? && !current_reader.attach_avatar(params[:avatar])
|
|
25
27
|
problems << (current_reader.errors[:avatar].first || 'a valid image')
|
|
26
28
|
end
|
|
@@ -112,13 +112,24 @@ module Bible270
|
|
|
112
112
|
def b270_passage_url(reference, version = nil)
|
|
113
113
|
return '#' if reference.blank?
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
selected_version = version || b270_bible_version
|
|
116
|
+
if selected_version != Translations::ORIGINAL_LANGUAGES && current_reader&.blue_letter_bible?
|
|
117
|
+
return Translations.passage_url(reference, selected_version, blue_letter: true)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
Bible270.config.passage_url_builder.call(reference, selected_version)
|
|
116
121
|
end
|
|
117
122
|
|
|
118
123
|
# Small grey "(NKJV)" after a reading, so it's clear which translation the
|
|
119
|
-
# link opens in.
|
|
120
|
-
|
|
121
|
-
|
|
124
|
+
# link opens in. The original-language preference names the actual language
|
|
125
|
+
# used for each track rather than repeating the combined preference code.
|
|
126
|
+
def b270_version_tag(version = nil, track: nil)
|
|
127
|
+
label = version || b270_bible_version
|
|
128
|
+
if label == Translations::ORIGINAL_LANGUAGES && track
|
|
129
|
+
label = track.to_s == 'nt' ? 'Greek' : 'Hebrew'
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
content_tag :span, "(#{label})", class: 'b270-version'
|
|
122
133
|
end
|
|
123
134
|
|
|
124
135
|
def b270_avatar_src(reader)
|
|
@@ -25,8 +25,12 @@ module Bible270
|
|
|
25
25
|
# transaction rolls back.
|
|
26
26
|
after_create_commit :notify_of_registration
|
|
27
27
|
|
|
28
|
+
PASSAGE_SOURCES = %w[bible_gateway blue_letter_bible].freeze
|
|
29
|
+
DEFAULT_PASSAGE_SOURCE = 'bible_gateway'
|
|
30
|
+
|
|
28
31
|
validates :display_name, presence: true
|
|
29
32
|
validates :uid, uniqueness: { scope: :provider }, allow_nil: true
|
|
33
|
+
validates :passage_source, inclusion: { in: PASSAGE_SOURCES }
|
|
30
34
|
|
|
31
35
|
# Build/refresh a reader from an OmniAuth auth hash. Tolerant of the various
|
|
32
36
|
# shapes strategies return (OmniAuth::AuthHash, plain Hash, missing info).
|
|
@@ -164,6 +168,16 @@ module Bible270
|
|
|
164
168
|
update(bible_version: Translations.normalize(code))
|
|
165
169
|
end
|
|
166
170
|
|
|
171
|
+
def update_passage_source(source)
|
|
172
|
+
source = source.to_s
|
|
173
|
+
return false unless PASSAGE_SOURCES.include?(source)
|
|
174
|
+
|
|
175
|
+
update(passage_source: source)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def bible_gateway? = passage_source == 'bible_gateway'
|
|
179
|
+
def blue_letter_bible? = passage_source == 'blue_letter_bible'
|
|
180
|
+
|
|
167
181
|
# Readers are listed by first name, matching how they are shown — the display
|
|
168
182
|
# name is "First Last", so sorting on it needs only case folding. Surname
|
|
169
183
|
# order was inconsistent with the community page and read oddly next to names
|
|
@@ -109,8 +109,9 @@
|
|
|
109
109
|
Bible270::Translations.options,
|
|
110
110
|
@reader.bible_version.to_s),
|
|
111
111
|
class: "b270-select", style: "width:auto" %>
|
|
112
|
-
<%= submit_tag "Save", class: "b270-btn" %>
|
|
113
112
|
</div>
|
|
113
|
+
<%= render "bible270/shared/passage_source", reader: @reader %>
|
|
114
|
+
<%= submit_tag "Save", class: "b270-btn" %>
|
|
114
115
|
<% end %>
|
|
115
116
|
</div>
|
|
116
117
|
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
<% end %>
|
|
36
36
|
</ul>
|
|
37
37
|
<div class="b270-parts-meta">
|
|
38
|
-
<%= read_parts.size %> of <%= parts.size %> chapters <%= b270_version_tag %>
|
|
38
|
+
<%= read_parts.size %> of <%= parts.size %> chapters <%= b270_version_tag(track: track) %>
|
|
39
39
|
</div>
|
|
40
40
|
</div>
|
|
41
41
|
<% else %>
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
<% end %>
|
|
51
51
|
<div class="b270-rbody">
|
|
52
52
|
<div class="b270-cat"><%= t[:label] %></div>
|
|
53
|
-
<div class="b270-ref"><%= link_to ref, b270_passage_url(ref), target: "_blank", rel: "noopener" %> <%= b270_version_tag %></div>
|
|
53
|
+
<div class="b270-ref"><%= link_to ref, b270_passage_url(ref), target: "_blank", rel: "noopener" %> <%= b270_version_tag(track: track) %></div>
|
|
54
54
|
</div>
|
|
55
55
|
<% end %>
|
|
56
56
|
</div>
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
class: "b270-select" %>
|
|
40
40
|
<span class="b270-hint" style="margin:0">Reading links open in this translation.</span>
|
|
41
41
|
</label>
|
|
42
|
+
<%= render "bible270/shared/passage_source", reader: current_reader %>
|
|
42
43
|
|
|
43
44
|
<%= submit_tag "Save", class: "b270-btn" %>
|
|
44
45
|
<% end %>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<% original_languages = reader.effective_bible_version == Bible270::Translations::ORIGINAL_LANGUAGES %>
|
|
2
|
+
<div class="b270-source-choices" role="radiogroup" aria-label="Reading link source">
|
|
3
|
+
<label class="b270-source-choice <%= 'disabled' if original_languages %>">
|
|
4
|
+
<%= radio_button_tag :passage_source, "bible_gateway",
|
|
5
|
+
!original_languages && reader.bible_gateway?, disabled: original_languages %>
|
|
6
|
+
<span>Bible Gateway</span>
|
|
7
|
+
</label>
|
|
8
|
+
<label class="b270-source-choice <%= 'disabled' if original_languages %>">
|
|
9
|
+
<%= radio_button_tag :passage_source, "blue_letter_bible",
|
|
10
|
+
original_languages || reader.blue_letter_bible?, disabled: original_languages %>
|
|
11
|
+
<span>Blue Letter Bible</span>
|
|
12
|
+
</label>
|
|
13
|
+
</div>
|
|
@@ -134,6 +134,15 @@
|
|
|
134
134
|
.b270-parts-meta{margin-top:10px;font-size:12.5px;color:var(--faint)}
|
|
135
135
|
.b270-version{font-size:11.5px;color:var(--faint);font-weight:400;letter-spacing:.02em;white-space:nowrap}
|
|
136
136
|
.b270-select{font:inherit;font-size:14px;padding:10px 30px 10px 12px;border:1px solid var(--line);border-radius:9px;background:#fff;color:var(--ink);width:100%;max-width:100%;text-overflow:ellipsis}
|
|
137
|
+
.b270-source-choices{display:flex;align-items:center;gap:18px;flex-wrap:wrap;margin:8px 0 2px;color:var(--muted);font-size:12.5px}
|
|
138
|
+
.b270-source-choice{display:inline-flex;align-items:center;gap:7px;cursor:pointer;white-space:nowrap}
|
|
139
|
+
.b270-source-choice input{appearance:none;width:17px;height:17px;margin:0;border:1px solid var(--line);border-radius:3px;background:#fff;display:grid;place-content:center;cursor:pointer}
|
|
140
|
+
.b270-source-choice input::before{content:"";width:9px;height:9px;transform:scale(0);transition:transform .1s;clip-path:polygon(14% 44%,0 59%,38% 100%,100% 18%,82% 0,37% 65%);background:var(--paper)}
|
|
141
|
+
.b270-source-choice input:checked{background:var(--gold);border-color:var(--gold)}
|
|
142
|
+
.b270-source-choice input:checked::before{transform:scale(1)}
|
|
143
|
+
.b270-source-choice input:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
|
|
144
|
+
.b270-source-choice.disabled{cursor:default;opacity:.55}
|
|
145
|
+
.b270-source-choice input:disabled{cursor:default}
|
|
137
146
|
.b270-ref a{text-decoration:none;border-bottom:1px solid transparent}
|
|
138
147
|
.b270-ref a:hover{border-bottom-color:var(--gold)}
|
|
139
148
|
.b270-reading.done .b270-ref{color:var(--muted)}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class AddPassageSourceToBible270Readers < ActiveRecord::Migration[7.0]
|
|
4
|
+
def up
|
|
5
|
+
add_column :bible270_readers, :passage_source, :string, default: 'bible_gateway', null: false
|
|
6
|
+
|
|
7
|
+
return unless column_exists?(:bible270_readers, :blue_letter_bible)
|
|
8
|
+
|
|
9
|
+
execute <<~SQL.squish
|
|
10
|
+
UPDATE bible270_readers
|
|
11
|
+
SET passage_source = 'blue_letter_bible'
|
|
12
|
+
WHERE blue_letter_bible = TRUE
|
|
13
|
+
SQL
|
|
14
|
+
remove_column :bible270_readers, :blue_letter_bible
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def down
|
|
18
|
+
add_column :bible270_readers, :blue_letter_bible, :boolean, default: false, null: false
|
|
19
|
+
execute <<~SQL.squish
|
|
20
|
+
UPDATE bible270_readers
|
|
21
|
+
SET blue_letter_bible = TRUE
|
|
22
|
+
WHERE passage_source = 'blue_letter_bible'
|
|
23
|
+
SQL
|
|
24
|
+
remove_column :bible270_readers, :passage_source
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -347,12 +347,7 @@ module Bible270
|
|
|
347
347
|
@after_sign_out_path = nil
|
|
348
348
|
@bible_version = 'NKJV'
|
|
349
349
|
@bible_versions = Translations::VERSIONS.keys
|
|
350
|
-
@passage_url_builder = ->(reference, version)
|
|
351
|
-
search = URI.encode_www_form_component(reference)
|
|
352
|
-
# Translations.gateway_code maps 'NASB95' to Bible Gateway's 'NASB1995'.
|
|
353
|
-
version = URI.encode_www_form_component(Translations.gateway_code(version))
|
|
354
|
-
"https://www.biblegateway.com/passage/?search=#{search}&version=#{version}"
|
|
355
|
-
end
|
|
350
|
+
@passage_url_builder = ->(reference, version) { Translations.passage_url(reference, version) }
|
|
356
351
|
@app_name = 'Daily Bread'
|
|
357
352
|
@tagline = 'Journeying through Scripture in 9 months'
|
|
358
353
|
@time_zone = nil
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bible270
|
|
4
|
+
# Repairs schema_migrations after a database is copied without matching migration
|
|
5
|
+
# bookkeeping. A migration is safe to mark only when every table, column and
|
|
6
|
+
# current index it contributes is already present.
|
|
7
|
+
class MigrationReconciler
|
|
8
|
+
Status = Struct.new(:migration, :missing, keyword_init: true) do
|
|
9
|
+
def complete? = missing.empty?
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
CHECKS = {
|
|
13
|
+
'CreateBible270Readers' => :check_readers,
|
|
14
|
+
'CreateBible270Checkoffs' => :check_checkoffs,
|
|
15
|
+
'CreateBible270Comments' => :check_comments,
|
|
16
|
+
'CreateBible270SignInTokens' => :check_sign_in_tokens,
|
|
17
|
+
'AddNamesToBible270Readers' => :check_names,
|
|
18
|
+
'AddApprovalToBible270Comments' => :check_approval,
|
|
19
|
+
'CreateBible270Settings' => :check_settings,
|
|
20
|
+
'AddBibleVersionToBible270Readers' => :check_bible_version,
|
|
21
|
+
'AddPartToBible270Checkoffs' => :check_checkoff_parts,
|
|
22
|
+
'AddRememberTokenToBible270Readers' => :check_remember_token,
|
|
23
|
+
'AddMentionNoticesToBible270Readers' => :check_mention_notices,
|
|
24
|
+
'AddParentToBible270Comments' => :check_comment_parent,
|
|
25
|
+
'CreateBible270Likes' => :check_likes,
|
|
26
|
+
'AddBlueLetterBibleToBible270Readers' => :check_blue_letter_bible,
|
|
27
|
+
'AddPassageSourceToBible270Readers' => :check_passage_source,
|
|
28
|
+
'CreateActiveStorageTables' => :check_active_storage
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
31
|
+
def self.for_current_database
|
|
32
|
+
context = ActiveRecord::MigrationContext.new(ActiveRecord::Migrator.migrations_paths)
|
|
33
|
+
new(connection: ActiveRecord::Base.connection, migration_context: context)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def initialize(connection:, migration_context:)
|
|
37
|
+
@connection = connection
|
|
38
|
+
@migration_context = migration_context
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def statuses
|
|
42
|
+
applied = migration_context.get_all_versions.map(&:to_i)
|
|
43
|
+
migration_context.migrations.filter_map do |migration|
|
|
44
|
+
check = CHECKS[migration.name]
|
|
45
|
+
next unless check
|
|
46
|
+
next if applied.include?(migration.version.to_i)
|
|
47
|
+
|
|
48
|
+
Status.new(migration: migration, missing: public_send(check))
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def mark_applied!(status)
|
|
53
|
+
raise ArgumentError, "#{status.migration.name} is not reflected in the schema" unless status.complete?
|
|
54
|
+
|
|
55
|
+
migration_context.schema_migration.create_version(status.migration.version.to_s)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def check_readers
|
|
59
|
+
table_missing(
|
|
60
|
+
:bible270_readers,
|
|
61
|
+
columns: %i[id display_name email avatar_url provider uid owner_type owner_id started_on created_at updated_at],
|
|
62
|
+
indexes: [
|
|
63
|
+
{ columns: %i[owner_type owner_id] },
|
|
64
|
+
{ columns: %i[provider uid], unique: true }
|
|
65
|
+
]
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def check_checkoffs
|
|
70
|
+
table_missing(
|
|
71
|
+
:bible270_checkoffs,
|
|
72
|
+
columns: %i[id reader_id day track created_at updated_at],
|
|
73
|
+
indexes: [{ columns: %i[reader_id] }, { columns: %i[day] }],
|
|
74
|
+
foreign_keys: [{ column: :reader_id, to_table: :bible270_readers }]
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def check_comments
|
|
79
|
+
table_missing(
|
|
80
|
+
:bible270_comments,
|
|
81
|
+
columns: %i[id reader_id day track body created_at updated_at],
|
|
82
|
+
indexes: [{ columns: %i[reader_id] }, { columns: %i[day created_at] }],
|
|
83
|
+
foreign_keys: [{ column: :reader_id, to_table: :bible270_readers }]
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def check_sign_in_tokens
|
|
88
|
+
table_missing(
|
|
89
|
+
:bible270_sign_in_tokens,
|
|
90
|
+
columns: %i[id email token_digest display_name expires_at consumed_at created_at updated_at],
|
|
91
|
+
indexes: [
|
|
92
|
+
{ columns: %i[token_digest], unique: true },
|
|
93
|
+
{ columns: %i[email created_at] }
|
|
94
|
+
]
|
|
95
|
+
)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def check_names
|
|
99
|
+
table_missing(
|
|
100
|
+
:bible270_readers,
|
|
101
|
+
columns: %i[first_name last_name],
|
|
102
|
+
indexes: [{ columns: %i[last_name first_name] }]
|
|
103
|
+
) + table_missing(:bible270_sign_in_tokens, columns: %i[first_name last_name])
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def check_approval
|
|
107
|
+
table_missing(
|
|
108
|
+
:bible270_comments,
|
|
109
|
+
columns: %i[approved moderated_at],
|
|
110
|
+
indexes: [{ columns: %i[approved day] }]
|
|
111
|
+
)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def check_settings
|
|
115
|
+
table_missing(
|
|
116
|
+
:bible270_settings,
|
|
117
|
+
columns: %i[id key value created_at updated_at],
|
|
118
|
+
indexes: [{ columns: %i[key], unique: true }]
|
|
119
|
+
)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def check_bible_version
|
|
123
|
+
table_missing(:bible270_readers, columns: %i[bible_version])
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def check_checkoff_parts
|
|
127
|
+
table_missing(
|
|
128
|
+
:bible270_checkoffs,
|
|
129
|
+
columns: %i[part],
|
|
130
|
+
indexes: [{ columns: %i[reader_id day track part], unique: true }]
|
|
131
|
+
)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def check_remember_token
|
|
135
|
+
table_missing(
|
|
136
|
+
:bible270_readers,
|
|
137
|
+
columns: %i[remember_token],
|
|
138
|
+
indexes: [{ columns: %i[remember_token] }]
|
|
139
|
+
)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def check_mention_notices
|
|
143
|
+
table_missing(:bible270_readers, columns: %i[notify_on_mention])
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def check_comment_parent
|
|
147
|
+
table_missing(
|
|
148
|
+
:bible270_comments,
|
|
149
|
+
columns: %i[parent_id],
|
|
150
|
+
indexes: [{ columns: %i[parent_id] }],
|
|
151
|
+
foreign_keys: [{ column: :parent_id, to_table: :bible270_comments }]
|
|
152
|
+
)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def check_likes
|
|
156
|
+
table_missing(
|
|
157
|
+
:bible270_likes,
|
|
158
|
+
columns: %i[id reader_id comment_id created_at updated_at],
|
|
159
|
+
indexes: [
|
|
160
|
+
{ columns: %i[reader_id comment_id], unique: true },
|
|
161
|
+
{ columns: %i[comment_id] }
|
|
162
|
+
],
|
|
163
|
+
foreign_keys: [
|
|
164
|
+
{ column: :reader_id, to_table: :bible270_readers },
|
|
165
|
+
{ column: :comment_id, to_table: :bible270_comments }
|
|
166
|
+
]
|
|
167
|
+
)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def check_blue_letter_bible
|
|
171
|
+
return [] if connection.column_exists?(:bible270_readers, :passage_source)
|
|
172
|
+
|
|
173
|
+
table_missing(:bible270_readers, columns: %i[blue_letter_bible])
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def check_passage_source
|
|
177
|
+
table_missing(:bible270_readers, columns: %i[passage_source])
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# The Bible270 installer can install Active Storage for reader avatars. A
|
|
181
|
+
# copied database can therefore have the same timestamp mismatch for Rails'
|
|
182
|
+
# generated Active Storage migration as it has for the engine migrations.
|
|
183
|
+
def check_active_storage
|
|
184
|
+
table_missing(
|
|
185
|
+
:active_storage_blobs,
|
|
186
|
+
columns: %i[id key filename content_type metadata service_name byte_size checksum created_at],
|
|
187
|
+
indexes: [{ columns: %i[key], unique: true }]
|
|
188
|
+
) + table_missing(
|
|
189
|
+
:active_storage_attachments,
|
|
190
|
+
columns: %i[id name record_type record_id blob_id created_at],
|
|
191
|
+
indexes: [
|
|
192
|
+
{ columns: %i[blob_id] },
|
|
193
|
+
{ columns: %i[record_type record_id name blob_id], unique: true }
|
|
194
|
+
],
|
|
195
|
+
foreign_keys: [{ column: :blob_id, to_table: :active_storage_blobs }]
|
|
196
|
+
) + table_missing(
|
|
197
|
+
:active_storage_variant_records,
|
|
198
|
+
columns: %i[id blob_id variation_digest],
|
|
199
|
+
indexes: [{ columns: %i[blob_id variation_digest], unique: true }],
|
|
200
|
+
foreign_keys: [{ column: :blob_id, to_table: :active_storage_blobs }]
|
|
201
|
+
)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
private
|
|
205
|
+
|
|
206
|
+
attr_reader :connection, :migration_context
|
|
207
|
+
|
|
208
|
+
def table_missing(table, columns:, indexes: [], foreign_keys: [])
|
|
209
|
+
return ["table #{table}"] unless connection.data_source_exists?(table)
|
|
210
|
+
|
|
211
|
+
missing_columns(table, columns) +
|
|
212
|
+
missing_indexes(table, indexes) +
|
|
213
|
+
missing_foreign_keys(table, foreign_keys)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def missing_columns(table, expected_columns)
|
|
217
|
+
existing = connection.columns(table).map { |column| column.name.to_s }
|
|
218
|
+
expected_columns.filter_map do |column|
|
|
219
|
+
"column #{table}.#{column}" unless existing.include?(column.to_s)
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def missing_indexes(table, expected_indexes)
|
|
224
|
+
existing = connection.indexes(table)
|
|
225
|
+
expected_indexes.filter_map do |expected|
|
|
226
|
+
columns = expected.fetch(:columns).map(&:to_s)
|
|
227
|
+
next if existing.any? { |index| matching_index?(index, columns, expected) }
|
|
228
|
+
|
|
229
|
+
qualifier = expected[:unique] ? 'unique index' : 'index'
|
|
230
|
+
"#{qualifier} #{table}(#{columns.join(', ')})"
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def matching_index?(index, columns, expected)
|
|
235
|
+
index.columns == columns && (!expected.key?(:unique) || index.unique == expected[:unique])
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def missing_foreign_keys(table, expected_foreign_keys)
|
|
239
|
+
existing = connection.foreign_keys(table)
|
|
240
|
+
expected_foreign_keys.filter_map do |expected|
|
|
241
|
+
next if existing.any? { |foreign_key| matching_foreign_key?(foreign_key, expected) }
|
|
242
|
+
|
|
243
|
+
"foreign key #{table}.#{expected[:column]} -> #{expected[:to_table]}"
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def matching_foreign_key?(foreign_key, expected)
|
|
248
|
+
foreign_key.column.to_s == expected.fetch(:column).to_s &&
|
|
249
|
+
foreign_key.to_table.to_s == expected.fetch(:to_table).to_s
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'uri'
|
|
4
|
+
require 'bible270/versification'
|
|
5
|
+
|
|
3
6
|
module Bible270
|
|
4
7
|
# The translations a reader may choose between, and how each maps onto the
|
|
5
8
|
# external reader's own code. Kept free of Rails so it can be unit tested.
|
|
@@ -11,14 +14,22 @@ module Bible270
|
|
|
11
14
|
# `label` is the full name, for prose. `short` is what goes in the select,
|
|
12
15
|
# where "New American Standard Bible 1995" overflows the box.
|
|
13
16
|
VERSIONS = {
|
|
14
|
-
'NKJV' => { label: 'New King James Version', short: 'New King James Version', gateway: 'NKJV' },
|
|
15
|
-
'NASB95' => { label: 'New American Standard Bible 1995', short: 'New American Standard 1995', gateway: 'NASB1995' },
|
|
16
|
-
'LSB' => { label: 'Legacy Standard Bible', short: 'Legacy Standard Version', gateway: 'LSB' },
|
|
17
|
-
'ESV' => { label: 'English Standard Version', short: 'English Standard Version', gateway: 'ESV' },
|
|
18
|
-
'KJV' => { label: 'King James Version', short: 'King James Version', gateway: 'KJV' }
|
|
17
|
+
'NKJV' => { label: 'New King James Version', short: 'New King James Version', gateway: 'NKJV', blue_letter: 'nkjv' },
|
|
18
|
+
'NASB95' => { label: 'New American Standard Bible 1995', short: 'New American Standard 1995', gateway: 'NASB1995', blue_letter: 'nasb95' },
|
|
19
|
+
'LSB' => { label: 'Legacy Standard Bible', short: 'Legacy Standard Version', gateway: 'LSB', blue_letter: 'lsb' },
|
|
20
|
+
'ESV' => { label: 'English Standard Version', short: 'English Standard Version', gateway: 'ESV', blue_letter: 'esv' },
|
|
21
|
+
'KJV' => { label: 'King James Version', short: 'King James Version', gateway: 'KJV', blue_letter: 'kjv' },
|
|
22
|
+
'HEB/GRK' => { label: 'Hebrew and Greek', short: 'Hebrew and Greek', gateway: 'WLC', blue_letter: 'wlc/mgnt' }
|
|
19
23
|
}.freeze
|
|
20
24
|
|
|
21
25
|
DEFAULT = 'NKJV'
|
|
26
|
+
ORIGINAL_LANGUAGES = 'HEB/GRK'
|
|
27
|
+
BLUE_LETTER_BASE_URL = 'https://www.blueletterbible.org'
|
|
28
|
+
BLUE_LETTER_BOOK_CODES = %w[
|
|
29
|
+
gen exo lev num deu jos jdg rut 1sa 2sa 1ki 2ki 1ch 2ch ezr neh est job psa pro ecc sng
|
|
30
|
+
isa jer lam eze dan hos joe amo oba jon mic nah hab zep hag zec mal mat mar luk joh act rom
|
|
31
|
+
1co 2co gal eph php col 1th 2th 1ti 2ti tit phm heb jas 1pe 2pe 1jo 2jo 3jo jud rev
|
|
32
|
+
].freeze
|
|
22
33
|
|
|
23
34
|
module_function
|
|
24
35
|
|
|
@@ -57,6 +68,51 @@ module Bible270
|
|
|
57
68
|
codes.map { |code| ["#{code} — #{short_label(code)}", code] }
|
|
58
69
|
end
|
|
59
70
|
|
|
71
|
+
def passage_url(reference, version, blue_letter: false)
|
|
72
|
+
if blue_letter || normalize(version) == ORIGINAL_LANGUAGES
|
|
73
|
+
return blue_letter_url(reference, version)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
search = URI.encode_www_form_component(reference)
|
|
77
|
+
gateway = URI.encode_www_form_component(gateway_code(version))
|
|
78
|
+
"https://www.biblegateway.com/passage/?search=#{search}&version=#{gateway}"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Blue Letter Bible identifies a verse with its canonical chapter number times
|
|
82
|
+
# 1,000 plus the verse number: Genesis 1:1 is 1001 and Matthew 1:1 is 930001.
|
|
83
|
+
# A plan reference can span chapters or books; BLB has no equivalent passage
|
|
84
|
+
# query URL, so link to the first chapter/verse and let its chapter navigation
|
|
85
|
+
# carry the reader onward.
|
|
86
|
+
def blue_letter_url(reference, version = ORIGINAL_LANGUAGES)
|
|
87
|
+
books = Versification::VERSES.keys
|
|
88
|
+
book_index = books.index { |book| reference.to_s.start_with?("#{book} ") }
|
|
89
|
+
return "#{BLUE_LETTER_BASE_URL}/" unless book_index
|
|
90
|
+
|
|
91
|
+
book = books[book_index]
|
|
92
|
+
location = reference.to_s.delete_prefix("#{book} ")
|
|
93
|
+
match = location.match(%r{\A(\d+)(?::(\d+))?})
|
|
94
|
+
return "#{BLUE_LETTER_BASE_URL}/" unless match
|
|
95
|
+
|
|
96
|
+
chapter = match[1].to_i
|
|
97
|
+
verse = match[2]&.to_i || 1
|
|
98
|
+
chapter_verses = Versification::VERSES[book]
|
|
99
|
+
return "#{BLUE_LETTER_BASE_URL}/" unless chapter.between?(1, chapter_verses.length)
|
|
100
|
+
return "#{BLUE_LETTER_BASE_URL}/" unless verse.between?(1, chapter_verses[chapter - 1])
|
|
101
|
+
|
|
102
|
+
canonical_chapter = books.first(book_index).sum { |prior| Versification::VERSES[prior].length } + chapter
|
|
103
|
+
new_testament = book_index >= books.index('Matthew')
|
|
104
|
+
reader = if normalize(version) == ORIGINAL_LANGUAGES
|
|
105
|
+
new_testament ? 'mgnt' : 'wlc'
|
|
106
|
+
else
|
|
107
|
+
VERSIONS.dig(normalize(version), :blue_letter)
|
|
108
|
+
end
|
|
109
|
+
return "#{BLUE_LETTER_BASE_URL}/" if reader.to_s.empty?
|
|
110
|
+
|
|
111
|
+
anchor = (canonical_chapter * 1000) + verse
|
|
112
|
+
"#{BLUE_LETTER_BASE_URL}/#{reader}/#{BLUE_LETTER_BOOK_CODES[book_index]}/" \
|
|
113
|
+
"#{chapter}/#{verse}/s_#{anchor}"
|
|
114
|
+
end
|
|
115
|
+
|
|
60
116
|
# Falls back to the configured default, then to ESV, so a link is never built
|
|
61
117
|
# with a blank version.
|
|
62
118
|
def resolve(code)
|
data/lib/bible270/version.rb
CHANGED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bible270/migration_reconciler'
|
|
4
|
+
|
|
5
|
+
namespace :bible270 do
|
|
6
|
+
namespace :migrations do
|
|
7
|
+
desc 'Reconcile copied Bible270/Active Storage tables with schema_migrations (set APPLY=1 to write)'
|
|
8
|
+
task reconcile: :environment do
|
|
9
|
+
reconciler = Bible270::MigrationReconciler.for_current_database
|
|
10
|
+
statuses = reconciler.statuses
|
|
11
|
+
|
|
12
|
+
if statuses.empty?
|
|
13
|
+
puts 'No pending Bible270 or related Active Storage migrations were found.'
|
|
14
|
+
next
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
complete, incomplete = statuses.partition(&:complete?)
|
|
18
|
+
|
|
19
|
+
puts 'Already reflected in the database schema:'
|
|
20
|
+
if complete.empty?
|
|
21
|
+
puts ' (none)'
|
|
22
|
+
else
|
|
23
|
+
complete.each do |status|
|
|
24
|
+
puts " #{status.migration.version} #{status.migration.name}"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
unless incomplete.empty?
|
|
29
|
+
puts
|
|
30
|
+
puts 'Still missing schema changes (these will remain pending):'
|
|
31
|
+
incomplete.each do |status|
|
|
32
|
+
puts " #{status.migration.version} #{status.migration.name}"
|
|
33
|
+
status.missing.each { |item| puts " - #{item}" }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
puts
|
|
38
|
+
unless ENV['APPLY'] == '1'
|
|
39
|
+
puts 'Preview only; schema_migrations was not changed.'
|
|
40
|
+
puts 'Review the list, back up the database, then rerun with APPLY=1.'
|
|
41
|
+
next
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if complete.empty?
|
|
45
|
+
puts 'Nothing was safe to mark as applied.'
|
|
46
|
+
next
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
ActiveRecord::Base.transaction do
|
|
50
|
+
complete.each { |status| reconciler.mark_applied!(status) }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
puts "Marked #{complete.size} verified #{'migration'.pluralize(complete.size)} as applied."
|
|
54
|
+
puts 'Run bin/rails db:migrate to apply the migrations whose schema changes are still missing.'
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bible270
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.18.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew vonderLuft
|
|
@@ -136,6 +136,7 @@ files:
|
|
|
136
136
|
- app/views/bible270/shared/_footer.html.erb
|
|
137
137
|
- app/views/bible270/shared/_grid_legend.html.erb
|
|
138
138
|
- app/views/bible270/shared/_header.html.erb
|
|
139
|
+
- app/views/bible270/shared/_passage_source.html.erb
|
|
139
140
|
- app/views/bible270/shared/_progress.html.erb
|
|
140
141
|
- app/views/bible270/shared/_reflections.html.erb
|
|
141
142
|
- app/views/bible270/shared/_styles.html.erb
|
|
@@ -157,6 +158,8 @@ files:
|
|
|
157
158
|
- db/migrate/20260101000011_add_mention_notices_to_bible270_readers.rb
|
|
158
159
|
- db/migrate/20260101000012_add_parent_to_bible270_comments.rb
|
|
159
160
|
- db/migrate/20260101000013_create_bible270_likes.rb
|
|
161
|
+
- db/migrate/20260101000014_add_blue_letter_bible_to_bible270_readers.rb
|
|
162
|
+
- db/migrate/20260101000015_add_passage_source_to_bible270_readers.rb
|
|
160
163
|
- lib/bible270.rb
|
|
161
164
|
- lib/bible270/avatars.rb
|
|
162
165
|
- lib/bible270/configuration.rb
|
|
@@ -164,6 +167,7 @@ files:
|
|
|
164
167
|
- lib/bible270/engine.rb
|
|
165
168
|
- lib/bible270/favicon.rb
|
|
166
169
|
- lib/bible270/mentions.rb
|
|
170
|
+
- lib/bible270/migration_reconciler.rb
|
|
167
171
|
- lib/bible270/names.rb
|
|
168
172
|
- lib/bible270/plan.rb
|
|
169
173
|
- lib/bible270/translations.rb
|
|
@@ -172,6 +176,7 @@ files:
|
|
|
172
176
|
- lib/generators/bible270/install/install_generator.rb
|
|
173
177
|
- lib/generators/bible270/install/templates/bible270.rb.tt
|
|
174
178
|
- lib/generators/bible270/install/templates/omniauth.rb.tt
|
|
179
|
+
- lib/tasks/bible270_migrations.rake
|
|
175
180
|
homepage: https://github.com/avonderluft/bible270
|
|
176
181
|
licenses:
|
|
177
182
|
- MIT
|