Class: Bible270::Configuration

Inherits:
Object
  • Object
show all
Defined in:
lib/bible270/configuration.rb

Constant Summary collapse

PROVIDER_LABELS =

Human labels for providers we know; anything else is titleised.

{
  github: 'GitHub', gitlab: 'GitLab', google_oauth2: 'Google', google: 'Google',
  facebook: 'Facebook', twitter: 'Twitter', apple: 'Apple', discord: 'Discord',
  microsoft_graph: 'Microsoft', azure_activedirectory_v2: 'Microsoft',
  openid_connect: 'OpenID Connect', saml: 'SSO'
}.freeze
PLACEMENTS =
%i[replace after before].freeze
PLACEHOLDER_DOMAINS =

Reserved domains from RFC 2606 — a From: address at one of these will fail SPF for your real domain, and receiving servers routinely drop it without a bounce. The default value is one of them deliberately, so it has to be changed, but nothing was checking that it had been.

%w[example.com example.org example.net example.edu invalid localhost].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeConfiguration

Returns a new instance of Configuration.



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/bible270/configuration.rb', line 316

def initialize
  @parent_controller = 'ActionController::Base'
  @layout = 'bible270/application'
  @current_reader_resolver = nil
  @after_sign_in_path = nil
  @after_sign_out_path = nil
  @bible_version = 'NKJV'
  @bible_versions = Translations::VERSIONS.keys
  @passage_url_builder = ->(reference, version) do
    search  = URI.encode_www_form_component(reference)
    # Translations.gateway_code maps 'NASB95' to Bible Gateway's 'NASB1995'.
    version = URI.encode_www_form_component(Translations.gateway_code(version))
    "https://www.biblegateway.com/passage/?search=#{search}&version=#{version}"
  end
  @app_name = 'Daily Bread'
  @tagline = 'Journeying through Scripture in 9 months'
  @require_sign_in_to_participate = true
  self.mount_at = '/daily-bread'
  @start_date = nil
  @allow_reader_start_date = true
  self.omniauth_providers = [:github]
  @omniauth_path_prefix = nil
  @email_sign_in = true
  @mailer_from = 'no-reply@example.com'
  @email_sign_in_ttl = 20 * 60          # 20 minutes
  @email_sign_in_window = 15 * 60       # 15 minutes
  @email_sign_in_max_per_window = 5
  @email_sign_in_ask_name = true
  @email_sign_in_require_name = true
  @email_sign_in_log_link = nil
  @enrollment_open = true
  @footer_partial = nil
  @footer_placement = :replace
  @footer_html = nil
  @footer = nil
  @favicon = nil
  @avatar_max_bytes = Avatars::DEFAULT_MAX_BYTES
  @chapter_breaks = {}
  @chapter_breaks_path = nil
  @registration_notice_emails = []
  @registration_notice_deliver_later = false
  @mailer_host = nil
  @admin_emails = []
  @admin_resolver = nil
  @email_sign_in_deliver_later = false
end

Instance Attribute Details

#admin_emailsObject

Returns the value of attribute admin_emails.



295
296
297
# File 'lib/bible270/configuration.rb', line 295

def admin_emails
  @admin_emails
end

#admin_resolverObject

Returns the value of attribute admin_resolver.



295
296
297
# File 'lib/bible270/configuration.rb', line 295

def admin_resolver
  @admin_resolver
end

#after_sign_in_pathObject

Where to send the reader after a successful sign-in / sign-out.



63
64
65
# File 'lib/bible270/configuration.rb', line 63

def 
  @after_sign_in_path
end

#after_sign_out_pathObject

Returns the value of attribute after_sign_out_path.



295
296
297
# File 'lib/bible270/configuration.rb', line 295

def after_sign_out_path
  @after_sign_out_path
end

#allow_reader_start_dateObject

Whether an individual reader may set or change their own start date. When false, everyone is pinned to config.start_date.



311
312
313
# File 'lib/bible270/configuration.rb', line 311

def allow_reader_start_date
  @allow_reader_start_date
end

#app_nameObject

Public labels.



298
299
300
# File 'lib/bible270/configuration.rb', line 298

def app_name
  @app_name
end

#avatar_max_bytesObject

Returns the value of attribute avatar_max_bytes.



295
296
297
# File 'lib/bible270/configuration.rb', line 295

def avatar_max_bytes
  @avatar_max_bytes
end

#bible_versionObject

Returns the value of attribute bible_version.



295
296
297
# File 'lib/bible270/configuration.rb', line 295

def bible_version
  @bible_version
end

#bible_versionsObject

Default Bible translation and a builder for external passage links. Which translations readers may choose from. Defaults to all of Bible270::Translations::VERSIONS; narrow it to offer fewer.



276
277
278
# File 'lib/bible270/configuration.rb', line 276

def bible_versions
  @bible_versions
end

#chapter_breaksObject

Returns the value of attribute chapter_breaks.



295
296
297
# File 'lib/bible270/configuration.rb', line 295

def chapter_breaks
  @chapter_breaks
end

#chapter_breaks_pathObject

Optional YAML file holding the same thing, re-read whenever it changes so breaks can be tuned without restarting:

# config/bible270_breaks.yml
Psalm 35: []
Psalm 78: [20, 39, 55]


205
206
207
# File 'lib/bible270/configuration.rb', line 205

def chapter_breaks_path
  @chapter_breaks_path
end

#current_reader_resolverObject

A callable (lambda) that receives the current controller and returns a Bible270::Reader (or nil). Use this to bridge your host's users:

config.current_reader_resolver = ->(c) {
u = c.send(:current_user)
u && Bible270::Reader.for_owner(u, display_name: u.name, email: u.email)
}

Leave nil to use the built-in session-based sign-in (OmniAuth).



32
33
34
# File 'lib/bible270/configuration.rb', line 32

def current_reader_resolver
  @current_reader_resolver
end

#email_sign_inObject

Offer sign-in by email link, so readers who don't have (or don't want to use) a GitHub/Google/etc. account can still take part. Requires the host app to have Action Mailer delivery configured.



105
106
107
# File 'lib/bible270/configuration.rb', line 105

def 
  @email_sign_in
end

#email_sign_in_ask_nameObject

Whether a reader may type the display name shown beside their reflections when signing in by email (otherwise it's derived from the address).



118
119
120
# File 'lib/bible270/configuration.rb', line 118

def 
  @email_sign_in_ask_name
end

#email_sign_in_deliver_laterObject

Send the sign-in email via Active Job (deliver_later) instead of inline. Leave false unless you have a queue backend configured.



234
235
236
# File 'lib/bible270/configuration.rb', line 234

def 
  @email_sign_in_deliver_later
end

Write the sign-in link to the log. nil (default) means development and test only; true forces it on, for smoke-testing a production build locally before mail is wired up; false disables it everywhere. The link is a bearer credential, so true is not for a deployment real users can reach.



124
125
126
# File 'lib/bible270/configuration.rb', line 124

def 
  @email_sign_in_log_link
end

#email_sign_in_max_per_windowObject

Returns the value of attribute email_sign_in_max_per_window.



295
296
297
# File 'lib/bible270/configuration.rb', line 295

def 
  @email_sign_in_max_per_window
end

#email_sign_in_require_nameObject

Require a first AND last name when signing in by email. Names identify people to each other beside their reflections, so this is on by default.



128
129
130
# File 'lib/bible270/configuration.rb', line 128

def 
  @email_sign_in_require_name
end

#email_sign_in_ttlObject

How long a magic link stays valid (seconds).



111
112
113
# File 'lib/bible270/configuration.rb', line 111

def 
  @email_sign_in_ttl
end

#email_sign_in_windowObject

Simple abuse guard: at most N links per address per window (seconds).



114
115
116
# File 'lib/bible270/configuration.rb', line 114

def 
  @email_sign_in_window
end

#enrollment_openObject

Who may reach the admin panel. Either a list of email addresses, or a lambda taking the current reader and returning true/false:

config.admin_emails = %w[andrew@example.org]
config.admin_resolver = ->(reader) { reader.email.end_with?('@example.org') }

With neither set the panel is unreachable and its routes 404. Break points set from the host app, overriding Plan::CHAPTER_BREAKS. Keys may be ['Psalm', 18] or 'Psalm 18'; [] keeps a chapter whole. Largest avatar a reader may upload, in bytes. Uploading needs Active Storage in the host app; without it the field is hidden and readers keep whatever avatar their sign-in provider gave them. Whether new readers may join this run of the plan. An admin can close it from the panel at runtime; setting this to false launches closed.



144
145
146
# File 'lib/bible270/configuration.rb', line 144

def enrollment_open
  @enrollment_open
end

#faviconObject

Returns the value of attribute favicon.



295
296
297
# File 'lib/bible270/configuration.rb', line 295

def favicon
  @favicon
end

Returns the value of attribute footer.



295
296
297
# File 'lib/bible270/configuration.rb', line 295

def footer
  @footer
end

Returns the value of attribute footer_html.



295
296
297
# File 'lib/bible270/configuration.rb', line 295

def footer_html
  @footer_html
end

Favicon for the engine's pages. nil uses the built-in loaf, false renders no link tag at all (so the host's own favicon applies), and a string is used as the href — a path, a URL, or your own data URI. Footer for the engine's pages. Three ways to set it, and they're checked in this order:

config.footer_partial = 'shared/footer'   # a partial in your app
config.footer_html    = '<p>…</p>'        # raw HTML, marked safe
config.footer         = false             # no footer at all

With none of them set you get the engine's own note about how the plan works. Rails adds the leading underscore when looking a partial up, so 'layouts/_bible270_footer' would search for '__bible270_footer' and raise MissingTemplate. Accept either spelling and normalise.



160
161
162
# File 'lib/bible270/configuration.rb', line 160

def footer_partial
  @footer_partial
end

Where your footer goes relative to the engine's own: :replace (default), :after, or :before. Anything else is treated as :replace.



175
176
177
# File 'lib/bible270/configuration.rb', line 175

def footer_placement
  @footer_placement
end

#layoutObject

Layout used to render engine pages.



21
22
23
# File 'lib/bible270/configuration.rb', line 21

def layout
  @layout
end

#mailer_fromObject

From: address for the sign-in email.



108
109
110
# File 'lib/bible270/configuration.rb', line 108

def mailer_from
  @mailer_from
end

#mailer_hostObject

Host for links in notification mail. A mailer has no request to derive it from, so without this the notice names the reader but cannot link to them.



293
294
295
# File 'lib/bible270/configuration.rb', line 293

def mailer_host
  @mailer_host
end

#mount_atObject

Where the engine is mounted in the host application. Set this once, in config/initializers/bible270.rb, and use it everywhere the path is needed:

# config/routes.rb
mount Bible270::Engine, at: Bible270.config.mount_at

# config/initializers/omniauth.rb
path_prefix Bible270.config.auth_path_prefix

Accepts "daily-bread" or "/daily-bread"; stored with a leading slash and no trailing one.



45
46
47
# File 'lib/bible270/configuration.rb', line 45

def mount_at
  @mount_at
end

#omniauth_path_prefixObject

OmniAuth's path_prefix, if you need to override it. Normally leave this nil: the engine derives "/auth" from the request, which matches a builder configured with path_prefix: "/auth".



80
81
82
# File 'lib/bible270/configuration.rb', line 80

def omniauth_path_prefix
  @omniauth_path_prefix
end

#omniauth_providersObject

Providers to offer on the sign-in screen. Accepts symbols, or pairs of [provider, label] when you want to override the displayed name:

config.omniauth_providers = [:github]
config.omniauth_providers = [:github, [:google_oauth2, "Google"]]

The host application supplies the matching strategy gems and registers them in the OmniAuth builder (see the README, or run the install generator).



75
76
77
# File 'lib/bible270/configuration.rb', line 75

def omniauth_providers
  @omniauth_providers
end

#parent_controllerObject

Controller the engine's controllers inherit from. Set this to your host's "::ApplicationController" if you want to share layout, auth and helpers.



18
19
20
# File 'lib/bible270/configuration.rb', line 18

def parent_controller
  @parent_controller
end

#passage_url_builderObject

Returns the value of attribute passage_url_builder.



295
296
297
# File 'lib/bible270/configuration.rb', line 295

def passage_url_builder
  @passage_url_builder
end

#registration_notice_deliver_laterObject

Send the notice through Active Job rather than inline: registration happens during a request, so inline delivery makes the new reader wait for SMTP.



289
290
291
# File 'lib/bible270/configuration.rb', line 289

def registration_notice_deliver_later
  @registration_notice_deliver_later
end

#registration_notice_emailsObject

Who to email when someone new joins. Three forms:

config.registration_notice_emails = %w[andrew@example.org]  # an explicit list
config.registration_notice_emails = :admins                 # everyone in admin_emails
config.registration_notice_emails = []                      # off (default)

An explicit list is used as given, so you can notify someone who isn't an admin.



285
286
287
# File 'lib/bible270/configuration.rb', line 285

def registration_notice_emails
  @registration_notice_emails
end

#require_sign_in_to_participateObject

Only signed-in readers may check off and comment (viewing is always open).



314
315
316
# File 'lib/bible270/configuration.rb', line 314

def 
  @require_sign_in_to_participate
end

#start_dateObject

Optional community-wide start date for the plan. Set this when everyone reads together as a cohort (e.g. a church starting on a set Sunday):

config.start_date = Date.new(2026, 9, 6)   # or the string "2026-09-06"

Leave nil for an undated plan, where each reader's own start date applies (defaulting to the day they first check something off).



307
308
309
# File 'lib/bible270/configuration.rb', line 307

def start_date
  @start_date
end

#taglineObject

Returns the value of attribute tagline.



295
296
297
# File 'lib/bible270/configuration.rb', line 295

def tagline
  @tagline
end

Instance Method Details

#admin?(reader) ⇒ Boolean

Returns:

  • (Boolean)


207
208
209
210
211
212
# File 'lib/bible270/configuration.rb', line 207

def admin?(reader)
  return false if reader.nil?
  return !!admin_resolver.call(reader) if admin_resolver.respond_to?(:call)

  Array(admin_emails).map { |e| e.to_s.downcase }.include?(reader.email.to_s.downcase)
end

#admin_configured?Boolean

Returns:

  • (Boolean)


228
229
230
# File 'lib/bible270/configuration.rb', line 228

def admin_configured?
  admin_resolver.respond_to?(:call) || Array(admin_emails).any?
end

#any_sign_in_method?Boolean

Any way at all for a new person to sign in?

Returns:

  • (Boolean)


260
261
262
# File 'lib/bible270/configuration.rb', line 260

def 
  email_sign_in? || omniauth_providers.any?
end

#auth_path_prefixObject

The prefix OmniAuth's middleware should serve its routes under. Derived from mount_at unless omniauth_path_prefix is set explicitly.



56
57
58
59
60
# File 'lib/bible270/configuration.rb', line 56

def auth_path_prefix
  return omniauth_path_prefix if omniauth_path_prefix

  mount_at == '/' ? '/auth' : "#{mount_at}/auth"
end

#email_sign_in?Boolean

Returns:

  • (Boolean)


236
# File 'lib/bible270/configuration.rb', line 236

def email_sign_in? = !!@email_sign_in

:none, :partial, :html or :default.



190
191
192
193
194
195
196
197
# File 'lib/bible270/configuration.rb', line 190

def footer_style
  # Plain Ruby, not present?: this file has to load without ActiveSupport.
  return :none if footer == false
  return :partial unless footer_partial.to_s.strip.empty?
  return :html unless footer_html.to_s.strip.empty?

  :default
end

#keep_default_footer?Boolean

Whether the engine's own footer is shown alongside yours.

Returns:

  • (Boolean)


185
186
187
# File 'lib/bible270/configuration.rb', line 185

def keep_default_footer?
  footer_style != :none && %i[after before].include?(resolved_footer_placement)
end

#label_for_provider(key) ⇒ Object



266
267
268
269
270
271
# File 'lib/bible270/configuration.rb', line 266

def label_for_provider(key)
  found = omniauth_providers.find { |k, _| k.to_s == key.to_s }
  return found.last if found

  PROVIDER_LABELS[key.to_sym] || key.to_s.tr('_', ' ').split.map(&:capitalize).join(' ')
end

#mailer_from_problemObject

Why the sign-in mail is likely to vanish, or nil if it looks fine.



245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/bible270/configuration.rb', line 245

def mailer_from_problem
  return nil unless email_sign_in?

  address = mailer_from.to_s.strip
  return 'config.mailer_from is blank' if address.empty?
  return "config.mailer_from (#{address}) is not a valid address" unless EmailSignIn.valid_email?(address)

  domain = address.split('@').last.to_s.downcase
  return nil unless PLACEHOLDER_DOMAINS.include?(domain)

  "config.mailer_from is still the placeholder #{address} — mail from a reserved domain " \
    'fails SPF and is usually dropped without a bounce'
end

#notify_on_registration?Boolean

Returns:

  • (Boolean)


224
225
226
# File 'lib/bible270/configuration.rb', line 224

def notify_on_registration?
  registration_notice_recipients.any?
end

#omniauth_provider_keysObject



98
# File 'lib/bible270/configuration.rb', line 98

def omniauth_provider_keys = omniauth_providers.map(&:first)

#registration_notice_recipientsObject

The addresses to notify, normalised and de-duplicated. :admins follows admin_emails, the common case — but an explicit list wins, so a secretary who isn't an admin can still be told.



217
218
219
220
221
222
# File 'lib/bible270/configuration.rb', line 217

def registration_notice_recipients
  requested = registration_notice_emails
  requested = admin_emails if requested.to_s == 'admins'

  Array(requested).map { |address| EmailSignIn.normalize_email(address) }.compact.uniq
end


179
180
181
182
# File 'lib/bible270/configuration.rb', line 179

def resolved_footer_placement
  placement = footer_placement.to_s.downcase.to_sym
  PLACEMENTS.include?(placement) ? placement : :replace
end

#single_provider?Boolean

Returns:

  • (Boolean)


264
# File 'lib/bible270/configuration.rb', line 264

def single_provider? = omniauth_providers.size == 1