bible270 0.13.1 → 0.14.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1371e2f082f0d627cd177afcb78aef1f9b396b76c307a4b901e66a3e4ce16f56
4
- data.tar.gz: 380d7774095d789cb125baa69fa72a7ee34d4c888a399c444415ed2a3da71446
3
+ metadata.gz: 54e58fbb0af2af30cb7cdfd8f47c2d57526d464ece9c987365cec2dcb2331d43
4
+ data.tar.gz: b508bbb3ea2cd450cade1173173c680a4fcb34ad41fd3a498bef32cb2b2d9fe5
5
5
  SHA512:
6
- metadata.gz: 8da758eedfe495574fccd33037c18233c3b1f58fdb0515799dce22573bbf3f0d7023bfd3387087efc0f5af9783073681278e76d620a3925546b917957c5ad1b6
7
- data.tar.gz: f8533f349e9571a7fbd37a991cc592823be96d42764fdc69ece284bfc3ee35368f6f8678e7325beb69377eb588af7532aeae7c2958cbb8f5c11d1497265777dc
6
+ metadata.gz: b7880113db1ceb742f5857e004279ce00674b2adb385f2b3ebfaa16a5eaf910c31f2d452e29db91211f77d622d207a3bff3df64091c1d3b7908a4de93f72674c
7
+ data.tar.gz: a7b080c48d3072eab3554ac4fe4696bf2d8bd0ad69ef202fb7248c134a2b8685597d950222e72b80ba15f7c9449f8007c45bf1f9ec99be38d0228007ddfa23bb
data/CHANGELOG.md CHANGED
@@ -6,11 +6,52 @@ 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.12.0...main)**
9
+ **[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.14.0...main)**
10
+
11
+
12
+ ## [0.14.0] - 2026-07-30
13
+
14
+ **[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.13.1...v0.14.0)**
15
+
16
+ ### Added
17
+
18
+ - **Registration notices.** `config.registration_notice_emails` takes an explicit list or `:admins`
19
+ (following `admin_emails`); off unless set. The notice names the reader, their email, how they
20
+ signed in, when they joined and the running total, and links to their admin page when
21
+ `config.mailer_host` is set. Hooked to reader creation rather than to a controller, so email
22
+ sign-in, OmniAuth and bridged host users are all covered. Failures are logged, never raised —
23
+ nobody is blocked from joining by an SMTP problem. `registration_notice_deliver_later` moves
24
+ delivery off the request.
25
+
26
+ ### Fixed
27
+
28
+ - The installer's `bible270.rb` template was missing its closing `end`, so `generate bible270:install`
29
+ wrote an initializer that crashed the host app on boot. A test now renders every generator template
30
+ and compiles the result, across three mount points.
31
+
32
+ ## [0.13.1] — 2026-07-29
33
+
34
+ **[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.13.0...v0.13.1)**
35
+
36
+ Nothing consequential.
37
+
38
+ ## [0.13.0] — 2026-07-29
39
+
40
+ **[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.12.0...v0.13.0)**
41
+
42
+ ### Added
43
+
44
+ - favicon support
45
+ - admin ability to update any user's profile, including avatar
46
+ - custom footer options, to replace standard footer, or append to it
47
+
48
+ ### Fixed
49
+
50
+ - Correct verb for reading prompt: 'Start' and 'Continue
10
51
 
11
52
  ## [0.12.0] — 2026-07-29
12
53
 
13
- **[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.11.0...v0.12.0)**
54
+ **[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.11.0...v0.12.0)**
14
55
 
15
56
  ### Added
16
57
 
@@ -249,6 +290,8 @@ This is the initial public release.
249
290
 
250
291
  * 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.
251
292
 
293
+ [0.13.1]: https://github.com/avonderluft/bible270/releases/tag/v0.13.1
294
+ [0.13.0]: https://github.com/avonderluft/bible270/releases/tag/v0.13.0
252
295
  [0.12.0]: https://github.com/avonderluft/bible270/releases/tag/v0.12.0
253
296
  [0.11.0]: https://github.com/avonderluft/bible270/releases/tag/v0.11.0
254
297
  [0.10.0]: https://github.com/avonderluft/bible270/releases/tag/v0.10.0
data/README.md CHANGED
@@ -49,9 +49,10 @@ That's the whole install. The generator is interactive and walks you through it:
49
49
 
50
50
  1. asks where to mount the plan;
51
51
  2. asks which sign-in methods you want — email link, OmniAuth, or both — and adds any OmniAuth strategy gems to your `Gemfile`;
52
- 3. copies the four migrations into your `db/migrate` and offers to run them;
53
- 4. writes `config/initializers/bible270.rb` (and `omniauth.rb` if you chose social sign-in); and
54
- 5. adds `mount Bible270::Engine, at: Bible270.config.mount_at` to `config/routes.rb`.
52
+ 3. Prompts to install ActiveStorage for user avatars
53
+ 4. copies migrations into your `db/migrate` and offers to run them;
54
+ 5. writes `config/initializers/bible270.rb` (and `omniauth.rb` if you chose social sign-in); and
55
+ 6. adds `mount Bible270::Engine, at: Bible270.config.mount_at` to `config/routes.rb`.
55
56
 
56
57
  (Migrations run before the config is written, so a problem in a generated initializer can't block the database work.)
57
58
 
@@ -76,6 +77,7 @@ bin/rails generate bible270:install --defaults \
76
77
  | `--mailer-from=ADDR` | From: address for sign-in emails |
77
78
  | `--bundle` / `--no-bundle` | Run `bundle install` after adding strategy gems |
78
79
  | `--migrate` / `--no-migrate` | Run `db:migrate` at the end |
80
+ | `--active-storage` / `--no-active-storage` | Install Active Storage if missing (for picture uploads) |
79
81
 
80
82
  The installer is safe to re-run: it leaves an existing `mount` line alone, skips strategy gems already in your Gemfile, and Rails prompts before overwriting an initializer.
81
83
 
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bible270
4
+ # Notices to whoever runs the plan, as opposed to mail sent to readers.
5
+ class NoticeMailer < ApplicationMailer
6
+ def new_reader(reader_id:, recipients:)
7
+ @reader = Reader.find_by(id: reader_id)
8
+ return message.perform_deliveries = false if @reader.nil? || recipients.blank?
9
+
10
+ @app_name = Bible270.config.app_name
11
+ @total = Reader.count
12
+ @admin_url = admin_url_for(@reader)
13
+
14
+ mail to: recipients, subject: "#{@app_name}: #{@reader.display_name} has joined"
15
+ end
16
+
17
+ private
18
+
19
+ # A mailer has no request, so the host is config.mailer_host or nothing. No
20
+ # link is better than a broken one.
21
+ def admin_url_for(reader)
22
+ host = Bible270.config.mailer_host
23
+ return nil if host.blank?
24
+
25
+ Bible270::Engine.routes.url_helpers.admin_reader_url(
26
+ reader, host: host, protocol: host.start_with?('localhost') ? 'http' : 'https'
27
+ )
28
+ rescue StandardError
29
+ nil
30
+ end
31
+ end
32
+ end
@@ -18,6 +18,12 @@ module Bible270
18
18
 
19
19
  def self.avatar_uploads? = AVATAR_UPLOADS
20
20
 
21
+ # Fires for every way a reader comes into existence — email sign-in, OmniAuth,
22
+ # or a bridged host user — rather than in each controller, so no path can
23
+ # quietly skip it. after_create_commit, so nothing is sent for a reader whose
24
+ # transaction rolls back.
25
+ after_create_commit :notify_of_registration
26
+
21
27
  validates :display_name, presence: true
22
28
  validates :uid, uniqueness: { scope: :provider }, allow_nil: true
23
29
 
@@ -276,6 +282,25 @@ module Bible270
276
282
  update!(started_on: Plan.to_date(on) - (day - 1))
277
283
  end
278
284
 
285
+ # Delivery problems must never stop someone joining, so this logs rather than
286
+ # raises. Registration happens mid-request, hence the deliver_later option.
287
+ def notify_of_registration
288
+ recipients = Bible270.config.registration_notice_recipients
289
+ return if recipients.empty?
290
+
291
+ notice = NoticeMailer.new_reader(reader_id: id, recipients: recipients)
292
+ if Bible270.config.registration_notice_deliver_later
293
+ notice.deliver_later
294
+ else
295
+ notice.deliver_now
296
+ end
297
+ rescue StandardError => e
298
+ Rails.logger.error("[bible270] could not send the registration notice for #{id}: #{e.class}: #{e.message}")
299
+ end
300
+ # Private, but declared this way rather than with a `private` section: methods
301
+ # defined below here are called from controllers and views.
302
+ private :notify_of_registration
303
+
279
304
  def reload_progress
280
305
  @checked_counts = nil
281
306
  self
@@ -51,7 +51,8 @@
51
51
  <%= b270_avatar(r) %>
52
52
  <div>
53
53
  <div class="name"><%= link_to r.display_name, reader_path(r), style: "text-decoration:none" %></div>
54
- <div class="sub"><%= @days_completed[r.id] %> days read</div>
54
+ <% @day_number = @days_completed[r.id] == 1 ? "day" : "days" %>
55
+ <div class="sub"><%= @days_completed[r.id] %> <%= @day_number %> read</div>
55
56
  </div>
56
57
  <div class="stat"><span class="big"><%= (@days_completed[r.id].to_f / Bible270::Plan::DAYS * 100).round %>%</span></div>
57
58
  </div>
@@ -0,0 +1,28 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <body style="margin:0;padding:24px;background:#ece6da;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;color:#231f18">
4
+ <div style="max-width:520px;margin:0 auto;background:#f4efe6;border:1px solid #d7cfbf;border-radius:14px;padding:28px">
5
+ <h1 style="margin:0 0 6px;font-family:Georgia,serif;font-size:20px;font-weight:600">
6
+ <%= @reader.display_name %> has joined
7
+ </h1>
8
+ <p style="margin:0 0 18px;font-size:14px;color:#6d6558"><%= @app_name %></p>
9
+
10
+ <table style="font-size:14px;border-collapse:collapse">
11
+ <tr><td style="padding:3px 12px 3px 0;color:#6d6558">Name</td><td><%= @reader.display_name %></td></tr>
12
+ <tr><td style="padding:3px 12px 3px 0;color:#6d6558">Email</td><td><%= @reader.email.presence || '(none given)' %></td></tr>
13
+ <tr><td style="padding:3px 12px 3px 0;color:#6d6558">Signed in with</td><td><%= @reader.provider.presence || 'the host application' %></td></tr>
14
+ <tr><td style="padding:3px 12px 3px 0;color:#6d6558">Joined</td><td><%= @reader.created_at.strftime('%B %-d, %Y at %-I:%M %p') %></td></tr>
15
+ </table>
16
+
17
+ <p style="margin:18px 0 0;font-size:14px;color:#6d6558">
18
+ That makes <%= @total %> reader<%= 's' unless @total == 1 %> taking part.
19
+ </p>
20
+
21
+ <% if @admin_url %>
22
+ <p style="margin:18px 0 0">
23
+ <a href="<%= @admin_url %>" style="display:inline-block;background:#231f18;color:#ece6da;text-decoration:none;padding:10px 18px;border-radius:10px;font-weight:600;font-size:14px">Manage this reader</a>
24
+ </p>
25
+ <% end %>
26
+ </div>
27
+ </body>
28
+ </html>
@@ -0,0 +1,11 @@
1
+ <%= @reader.display_name %> has joined <%= @app_name %>.
2
+
3
+ Name: <%= @reader.display_name %>
4
+ Email: <%= @reader.email.presence || '(none given)' %>
5
+ Signed in with: <%= @reader.provider.presence || 'the host application' %>
6
+ Joined: <%= @reader.created_at.strftime('%B %-d, %Y at %-I:%M %p') %>
7
+
8
+ That makes <%= @total %> reader<%= 's' unless @total == 1 %> taking part.
9
+ <% if @admin_url %>
10
+ Manage them: <%= @admin_url %>
11
+ <% end %>
@@ -211,6 +211,20 @@ module Bible270
211
211
  Array(admin_emails).map { |e| e.to_s.downcase }.include?(reader.email.to_s.downcase)
212
212
  end
213
213
 
214
+ # The addresses to notify, normalised and de-duplicated. :admins follows
215
+ # admin_emails, the common case — but an explicit list wins, so a secretary
216
+ # who isn't an admin can still be told.
217
+ def registration_notice_recipients
218
+ requested = registration_notice_emails
219
+ requested = admin_emails if requested.to_s == 'admins'
220
+
221
+ Array(requested).map { |address| EmailSignIn.normalize_email(address) }.compact.uniq
222
+ end
223
+
224
+ def notify_on_registration?
225
+ registration_notice_recipients.any?
226
+ end
227
+
214
228
  def admin_configured?
215
229
  admin_resolver.respond_to?(:call) || Array(admin_emails).any?
216
230
  end
@@ -261,6 +275,23 @@ module Bible270
261
275
  # Bible270::Translations::VERSIONS; narrow it to offer fewer.
262
276
  attr_accessor :bible_versions
263
277
 
278
+ # Who to email when someone new joins. Three forms:
279
+ #
280
+ # config.registration_notice_emails = %w[andrew@example.org] # an explicit list
281
+ # config.registration_notice_emails = :admins # everyone in admin_emails
282
+ # config.registration_notice_emails = [] # off (default)
283
+ #
284
+ # An explicit list is used as given, so you can notify someone who isn't an admin.
285
+ attr_accessor :registration_notice_emails
286
+
287
+ # Send the notice through Active Job rather than inline: registration happens
288
+ # during a request, so inline delivery makes the new reader wait for SMTP.
289
+ attr_accessor :registration_notice_deliver_later
290
+
291
+ # Host for links in notification mail. A mailer has no request to derive it
292
+ # from, so without this the notice names the reader but cannot link to them.
293
+ attr_accessor :mailer_host
294
+
264
295
  attr_accessor :after_sign_out_path, :email_sign_in_max_per_window, :passage_url_builder, :tagline, :footer_html, :footer, :favicon, :avatar_max_bytes, :chapter_breaks, :admin_emails, :admin_resolver, :bible_version
265
296
 
266
297
  # Public labels.
@@ -321,6 +352,9 @@ module Bible270
321
352
  @avatar_max_bytes = Avatars::DEFAULT_MAX_BYTES
322
353
  @chapter_breaks = {}
323
354
  @chapter_breaks_path = nil
355
+ @registration_notice_emails = []
356
+ @registration_notice_deliver_later = false
357
+ @mailer_host = nil
324
358
  @admin_emails = []
325
359
  @admin_resolver = nil
326
360
  @email_sign_in_deliver_later = false
@@ -56,7 +56,13 @@ module Bible270
56
56
  # Best effort at splitting a single name, for readers who arrived through
57
57
  # OmniAuth with only a display name. Everything after the first word is the
58
58
  # surname, so "Andrew von der Luft" keeps its particles together.
59
- def split(display_name)
59
+ #
60
+ # Deliberately not called `split`: a module method with the same name as
61
+ # String#split invites a bare `split(...)` inside this module to resolve here
62
+ # instead, and RuboCop's Style/StringChars autocorrect has been known to
63
+ # rewrite `split` calls into a receiverless `chars`, which then resolves
64
+ # against the module and fails confusingly.
65
+ def split_display_name(display_name)
60
66
  parts = squish(display_name).split
61
67
  return nil if parts.size < 2
62
68
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bible270
4
- VERSION = '0.13.1'
4
+ VERSION = '0.14.0'
5
5
  end
@@ -39,6 +39,8 @@ module Bible270
39
39
  class_option :email, type: :boolean, desc: 'Enable passwordless email sign-in'
40
40
  class_option :mailer_from, type: :string, desc: 'From: address for sign-in emails'
41
41
  class_option :migrate, type: :boolean, desc: 'Run db:migrate at the end'
42
+ class_option :active_storage, type: :boolean,
43
+ desc: 'Install Active Storage if missing (needed for picture uploads)'
42
44
  class_option :bundle, type: :boolean, desc: 'Run bundle install after adding strategy gems'
43
45
  class_option :defaults, type: :boolean, default: false, desc: 'Accept all defaults, ask nothing'
44
46
 
@@ -112,7 +114,41 @@ module Bible270
112
114
  run 'bundle install' if @bundled
113
115
  end
114
116
 
115
- # ---- 5. migrations ----------------------------------------------------
117
+ # ---- 5. Active Storage ------------------------------------------------
118
+ #
119
+ # Picture uploads need it. `rails new` enables the framework but the tables
120
+ # come from a migration that must be installed, so check for that migration
121
+ # rather than assuming a fresh app has it.
122
+
123
+ def ensure_active_storage
124
+ return if skip_rails_commands?
125
+
126
+ unless active_storage_enabled?
127
+ @active_storage = :unavailable
128
+ say ''
129
+ say ' Active Storage is not enabled here, so picture uploads will be off.', :yellow
130
+ say ' (Was this app generated with --skip-active-storage?)'
131
+ return
132
+ end
133
+
134
+ if active_storage_installed?
135
+ @active_storage = :present
136
+ say ' Active Storage already installed.', :green
137
+ return
138
+ end
139
+
140
+ unless decide(:active_storage, 'Install Active Storage, so readers can upload a picture?', true)
141
+ @active_storage = :declined
142
+ return
143
+ end
144
+
145
+ say ''
146
+ say 'Installing Active Storage', :yellow
147
+ rails_command 'active_storage:install'
148
+ @active_storage = :installed
149
+ end
150
+
151
+ # ---- 6. migrations ----------------------------------------------------
116
152
 
117
153
  # Migrations run before the initializers are written, so a problem in the
118
154
  # generated config can never block the database work.
@@ -139,7 +175,7 @@ module Bible270
139
175
  end
140
176
  end
141
177
 
142
- # ---- 6. initializers ---------------------------------------------------
178
+ # ---- 7. initializers ---------------------------------------------------
143
179
 
144
180
  def create_engine_initializer
145
181
  create_file 'config/initializers/bible270.rb', engine_initializer
@@ -151,7 +187,7 @@ module Bible270
151
187
  create_file 'config/initializers/omniauth.rb', omniauth_initializer
152
188
  end
153
189
 
154
- # ---- 7. routing -------------------------------------------------------
190
+ # ---- 8. routing -------------------------------------------------------
155
191
 
156
192
  def add_route
157
193
  if routes_mounted?
@@ -175,7 +211,7 @@ module Bible270
175
211
  end
176
212
  end
177
213
 
178
- # ---- 8. what is left to do -------------------------------------------
214
+ # ---- 9. what is left to do -------------------------------------------
179
215
 
180
216
  def summary
181
217
  say ''
@@ -185,6 +221,7 @@ module Bible270
185
221
  say " Sign-in #{auth_summary}"
186
222
  say " Initializers #{initializer_summary}"
187
223
  say " Migrations #{@migrated ? 'copied and run' : 'copied (not yet run)'}"
224
+ say " Picture uploads #{active_storage_summary}"
188
225
  say ''
189
226
 
190
227
  remaining = outstanding_steps
@@ -217,6 +254,20 @@ module Bible270
217
254
  true
218
255
  end
219
256
 
257
+ # config/storage.yml is written by `rails new` unless --skip-active-storage.
258
+ def active_storage_enabled?
259
+ return true if File.exist?(File.join(destination_root, 'config', 'storage.yml'))
260
+
261
+ application = File.join(destination_root, 'config', 'application.rb')
262
+ File.exist?(application) && File.read(application).include?('active_storage')
263
+ rescue StandardError
264
+ false
265
+ end
266
+
267
+ def active_storage_installed?
268
+ Dir.glob(File.join(destination_root, 'db', 'migrate', '*create_active_storage_tables*')).any?
269
+ end
270
+
220
271
  # ---- prompting --------------------------------------------------------
221
272
 
222
273
  def quiet? = options[:defaults]
@@ -383,6 +434,16 @@ module Bible270
383
434
  body.include?('path_prefix') && !body.include?('Bible270.config.auth_path_prefix')
384
435
  end
385
436
 
437
+ def active_storage_summary
438
+ case @active_storage
439
+ when :installed then 'Active Storage installed'
440
+ when :present then 'Active Storage already present'
441
+ when :declined then 'off — Active Storage not installed'
442
+ when :unavailable then 'off — Active Storage not enabled in this app'
443
+ else 'unchanged'
444
+ end
445
+ end
446
+
386
447
  def outstanding_steps
387
448
  steps = []
388
449
  steps << "Add to config/routes.rb: #{MOUNT_LINE}" if @route_failed
@@ -395,6 +456,9 @@ module Bible270
395
456
  steps << 'bundle install' if @providers.any? && !@bundled
396
457
  steps << 'bin/rails bible270:install:migrations' if skip_rails_commands?
397
458
  steps << 'bin/rails db:migrate' unless @migrated
459
+ if @active_storage == :declined
460
+ steps << 'bin/rails active_storage:install && bin/rails db:migrate — for picture uploads'
461
+ end
398
462
 
399
463
  if @providers.any?
400
464
  steps << 'Set each provider\'s client id/secret in credentials or ENV'
@@ -37,6 +37,19 @@ Bible270.configure do |config|
37
37
  # config.after_sign_in_path = "<%= mount_at %>"
38
38
  # config.after_sign_out_path = "/"
39
39
 
40
+ # --- Admin ----------------------------------------------------------------
41
+ # config.admin_emails = %w[andrew@example.org]
42
+ # or
43
+ # config.admin_resolver = ->(reader) { reader.email.to_s.end_with?('@example.org') }
44
+ #
45
+ # --- Notify admin of new user sign-ins -------------------------------------
46
+ # Who to email when someone new joins. :admins follows admin_emails, or give an
47
+ # explicit list. Leave empty for no notices.
48
+ # config.registration_notice_emails = :admins # everyone in admin_emails
49
+ # config.registration_notice_emails = %w[admin@example.com] # or an explicit list
50
+ # config.mailer_host = 'example.com' # lets the notice link to the reader
51
+ # config.registration_notice_deliver_later = true # needs Active Job
52
+
40
53
  # --- Start date ------------------------------------------------------------
41
54
  # Community-wide start date (Date, Time, or "YYYY-MM-DD"), or nil for undated.
42
55
  # config.start_date = Date.new(2026, 9, 6)
@@ -44,4 +57,23 @@ Bible270.configure do |config|
44
57
 
45
58
  # --- Reading links ---------------------------------------------------------
46
59
  config.bible_version = "NKJV"
47
- end
60
+
61
+ # --- Custom chapter breaks -------------------------------------------------
62
+ # config.chapter_breaks = { 'Psalm 78' => [20, 39, 55] }
63
+ #
64
+ # or YAML file:
65
+ # config.chapter_breaks_path = Rails.root.join('config/bible270_breaks.yml')
66
+ # config/bible270_breaks.yml — re-read whenever it changes
67
+ # Psalm 35: []
68
+ # Psalm 78: [20, 39, 55]
69
+
70
+ # --- Custom footer options -------------------------------------------------
71
+ # The engine's pages end with a short note about how the plan works. Replace it with your own
72
+ # - a partial in your app at /app/views/layouts/_bible270_footer.html.erb
73
+ # config.footer_partial = 'layouts/bible270_footer'
74
+ # config.footer_html = '<p>&copy; 2026 …</p>'
75
+ # config.footer = false # no footer at all
76
+ #_
77
+ # config.footer_placement = :after # engine's note, then yours
78
+ # config.footer_placement = :before # yours, then the engine's note
79
+ # config.footer_placement = :replace # default
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.13.1
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew vonderLuft
@@ -76,6 +76,7 @@ files:
76
76
  - app/controllers/bible270/sessions_controller.rb
77
77
  - app/helpers/bible270/plan_helper.rb
78
78
  - app/mailers/bible270/application_mailer.rb
79
+ - app/mailers/bible270/notice_mailer.rb
79
80
  - app/mailers/bible270/sign_in_mailer.rb
80
81
  - app/models/bible270/application_record.rb
81
82
  - app/models/bible270/checkoff.rb
@@ -95,6 +96,8 @@ files:
95
96
  - app/views/bible270/days/_start_date.html.erb
96
97
  - app/views/bible270/days/index.html.erb
97
98
  - app/views/bible270/days/show.html.erb
99
+ - app/views/bible270/notice_mailer/new_reader.html.erb
100
+ - app/views/bible270/notice_mailer/new_reader.text.erb
98
101
  - app/views/bible270/profiles/edit.html.erb
99
102
  - app/views/bible270/readers/index.html.erb
100
103
  - app/views/bible270/readers/show.html.erb