Class: Moderate::Configuration
- Inherits:
-
Object
- Object
- Moderate::Configuration
- Defined in:
- lib/moderate/configuration.rb
Overview
The single configuration object the host populates in ‘config/initializers/moderate.rb` via `Moderate.configure do |config| … end`.
Design rules, straight from docs/configuration.md:
- Config is read AT THE POINT OF USE, not frozen at boot. Class names are
stored as strings and constantized lazily, so the initializer works no
matter when the app loads (the User model may not exist yet at boot).
- Validating setters normalize their input (`to_s.strip.downcase.to_sym`) so
"Block", :block and " block " all mean the same thing, and raise a
plain-English ArgumentError on a bad value — failing fast at the assignment
line. `validate!` runs once more at the end of `configure` for cross-field
checks (e.g. a :block filter pointed at an async adapter).
- Every hook (`audit`/`notify`/`on_block`/`ban_handler`) defaults to a no-op,
so the gem works untouched and a host wires hooks only as needed.
This mirrors the validating-setter convention across the ecosystem (usage_credits’ ‘default_currency=`, wallets’ ‘default_asset=`).
Defined Under Namespace
Classes: FilterPolicy
Constant Summary collapse
- FILTER_MODES =
The three filter modes a ‘moderates :field` / `config.filter` can use.
:off — no check :block — reject the save with a validation error if the filter trips :flag — allow the save, create a Moderate::Flag after commit for reviewOrder matters for the error message (“must be one of: off, block, flag”).
%i[off block flag].freeze
Instance Attribute Summary collapse
-
#adapters ⇒ Object
readonly
Returns the value of attribute adapters.
-
#additional_words ⇒ Object
Returns the value of attribute additional_words.
-
#appeal_form_enabled ⇒ Object
Returns the value of attribute appeal_form_enabled.
-
#appeal_guard ⇒ Object
Returns the value of attribute appeal_guard.
-
#appeal_human_verification_skip_if ⇒ Object
Returns the value of attribute appeal_human_verification_skip_if.
-
#appeal_rate_limit ⇒ Object
Returns the value of attribute appeal_rate_limit.
-
#appeal_return_path ⇒ Object
Returns the value of attribute appeal_return_path.
-
#audit ⇒ Object
— Hooks (all no-op by default) —————————————-.
-
#ban_handler ⇒ Object
— Hooks (all no-op by default) —————————————-.
-
#default_filter_mode ⇒ Object
— Filtering ————————————————————.
-
#excluded_words ⇒ Object
Returns the value of attribute excluded_words.
-
#filter_adapter ⇒ Object
— Filtering ————————————————————.
-
#filters ⇒ Object
readonly
Returns the value of attribute filters.
-
#locale ⇒ Object
— Misc —————————————————————–.
-
#notice_captcha_verifier ⇒ Object
Returns the value of attribute notice_captcha_verifier.
-
#notice_form_enabled ⇒ Object
Returns the value of attribute notice_form_enabled.
-
#notice_guard ⇒ Object
Returns the value of attribute notice_guard.
-
#notice_human_verification_skip_if ⇒ Object
Returns the value of attribute notice_human_verification_skip_if.
-
#notice_rate_limit ⇒ Object
Returns the value of attribute notice_rate_limit.
-
#notice_turnstile_secret_key ⇒ Object
Returns the value of attribute notice_turnstile_secret_key.
-
#notice_turnstile_site_key ⇒ Object
Returns the value of attribute notice_turnstile_site_key.
-
#notify ⇒ Object
— Hooks (all no-op by default) —————————————-.
-
#on_block ⇒ Object
— Hooks (all no-op by default) —————————————-.
-
#parent_controller ⇒ Object
— DSA notice form —————————————————— Documented in docs/dsa-notice-form.md.
-
#report_categories ⇒ Object
— Taxonomy (host-customizable) —————————————– Override the in-app COMMUNITY report category list.
-
#signed_gid_purposes ⇒ Object
Returns the value of attribute signed_gid_purposes.
-
#transparency_report_enabled ⇒ Object
Returns the value of attribute transparency_report_enabled.
-
#user_class ⇒ Object
— Identity ————————————————————-.
Instance Method Summary collapse
-
#adapter_for(name) ⇒ Object
Look up a registered adapter object by name, constantizing a String/Class reference (the built-ins) on first use.
- #adapter_registered?(name) ⇒ Boolean
-
#filter(class_name, field, with: nil, mode: nil) ⇒ Object
Declare a per-field filter policy in the initializer — the twin of ‘moderates :field, with:, mode:` on the model.
-
#initialize ⇒ Configuration
constructor
A new instance of Configuration.
-
#register_adapter(name, adapter) ⇒ Object
Register a host adapter under a name of the host’s choosing.
-
#validate! ⇒ Object
Cross-field validation run at the end of ‘Moderate.configure`.
Constructor Details
#initialize ⇒ Configuration
Returns a new instance of Configuration.
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/moderate/configuration.rb', line 82 def initialize # Identity. "User" is the overwhelmingly common case; the host overrides it # if their actor model is "Account", "Member", etc. @user_class = "User" # Filtering defaults. :block is the safe default per docs (reject objectionable # writes); :wordlist is the offline, zero-dependency default text adapter. @default_filter_mode = :block @filter_adapter = :wordlist @additional_words = [] @excluded_words = [] # Community report category override. nil ⇒ Moderate::Report::DEFAULT_CATEGORIES. # No migration needed to add a category — `category` is validated in the model. @report_categories = nil # Adapters registry: name (Symbol) => adapter (an object responding to # `classify`, OR a String class name to constantize lazily at use time, so we # don't force the built-in adapter files to be loaded before the initializer # runs). Seeded with the ONE built-in (the offline :wordlist) the README # documents; OpenAI/Rekognition/etc. are reference adapters in examples/ a host # copies in and registers — they are not shipped, loaded, or a dependency. # # The class behind this name is the gem's own adapter; we reference it by string # to keep this file decoupled from its load order (and there is no # `Moderate::Adapters` alias namespace — point straight at the Filters class). @adapters = { wordlist: "Moderate::Filters::Wordlist" } # Per-field filter policies, keyed by [class_name_string, field_string]. @filters = {} # Hooks — no-ops by default. These exact signatures are documented: # audit/notify take a single Moderate::Event # on_block/ban_handler take keyword args @audit = ->(_event) {} @notify = ->(_event) {} @on_block = ->(blocker:, blocked:, at:) {} @ban_handler = ->(user:, by:, reason:) {} # Misc. nil locale ⇒ follow I18n.default_locale at use time. @locale = nil # Engine controller defaults. The parent controller defaults to a stock base so # the engine works even on API-only apps — the same `parent_controller` # indirection Devise and api_keys use. @parent_controller = "::ActionController::Base" # DSA notice-form defaults (see docs/dsa-notice-form.md). The form is on by # default; both bot-gates no-op when their keys are blank; the rate limit is a # sane per-IP throttle. @notice_form_enabled = true @notice_rate_limit = { max: 5, within: 3600 } # 1.hour, expressed in seconds to avoid an ActiveSupport dependency here @notice_turnstile_site_key = nil @notice_turnstile_secret_key = nil @notice_captcha_verifier = nil # Gem-absent fallback bot gate. nil ⇒ no extra gate (the form just works); the # controller only consults it when rails_cloudflare_turnstile is NOT installed. @notice_guard = nil @notice_human_verification_skip_if = nil # DSA internal complaint / appeal form defaults. Same shape as the notice # form: public route, optional bot gate, runtime rate limit, and a redirect # target the host can choose. @appeal_form_enabled = true @appeal_rate_limit = { max: 10, within: 60 } @appeal_guard = nil @appeal_human_verification_skip_if = nil @appeal_return_path = "/" # The public Art. 24 transparency report. OFF by default — opt in with # `config.transparency_report_enabled = true`. A *live* transparency portal is # not itself a legal requirement: the DSA obligation is to *publish* a report at # least annually (a static page/file is fine), and micro/small enterprises are # exempt from the transparency tier entirely (Art. 15(2) / Art. 19). So we don't # publicly expose moderation counts unless the host explicitly turns it on. When # off, the mounted `/transparency` route 404s; the aggregation stays queryable in # code so a host can still build/publish its own report. @transparency_report_enabled = false @signed_gid_purposes = %i[appeal confirm_notice unsubscribe] end |
Instance Attribute Details
#adapters ⇒ Object (readonly)
Returns the value of attribute adapters.
48 49 50 |
# File 'lib/moderate/configuration.rb', line 48 def adapters @adapters end |
#additional_words ⇒ Object
Returns the value of attribute additional_words.
47 48 49 |
# File 'lib/moderate/configuration.rb', line 47 def additional_words @additional_words end |
#appeal_form_enabled ⇒ Object
Returns the value of attribute appeal_form_enabled.
74 75 76 |
# File 'lib/moderate/configuration.rb', line 74 def appeal_form_enabled @appeal_form_enabled end |
#appeal_guard ⇒ Object
Returns the value of attribute appeal_guard.
74 75 76 |
# File 'lib/moderate/configuration.rb', line 74 def appeal_guard @appeal_guard end |
#appeal_human_verification_skip_if ⇒ Object
Returns the value of attribute appeal_human_verification_skip_if.
74 75 76 |
# File 'lib/moderate/configuration.rb', line 74 def appeal_human_verification_skip_if @appeal_human_verification_skip_if end |
#appeal_rate_limit ⇒ Object
Returns the value of attribute appeal_rate_limit.
74 75 76 |
# File 'lib/moderate/configuration.rb', line 74 def appeal_rate_limit @appeal_rate_limit end |
#appeal_return_path ⇒ Object
Returns the value of attribute appeal_return_path.
74 75 76 |
# File 'lib/moderate/configuration.rb', line 74 def appeal_return_path @appeal_return_path end |
#audit ⇒ Object
— Hooks (all no-op by default) —————————————-
60 61 62 |
# File 'lib/moderate/configuration.rb', line 60 def audit @audit end |
#ban_handler ⇒ Object
— Hooks (all no-op by default) —————————————-
60 61 62 |
# File 'lib/moderate/configuration.rb', line 60 def ban_handler @ban_handler end |
#default_filter_mode ⇒ Object
— Filtering ————————————————————
46 47 48 |
# File 'lib/moderate/configuration.rb', line 46 def default_filter_mode @default_filter_mode end |
#excluded_words ⇒ Object
Returns the value of attribute excluded_words.
47 48 49 |
# File 'lib/moderate/configuration.rb', line 47 def excluded_words @excluded_words end |
#filter_adapter ⇒ Object
— Filtering ————————————————————
46 47 48 |
# File 'lib/moderate/configuration.rb', line 46 def filter_adapter @filter_adapter end |
#filters ⇒ Object (readonly)
Returns the value of attribute filters.
48 49 50 |
# File 'lib/moderate/configuration.rb', line 48 def filters @filters end |
#locale ⇒ Object
— Misc —————————————————————–
63 64 65 |
# File 'lib/moderate/configuration.rb', line 63 def locale @locale end |
#notice_captcha_verifier ⇒ Object
Returns the value of attribute notice_captcha_verifier.
74 75 76 |
# File 'lib/moderate/configuration.rb', line 74 def notice_captcha_verifier @notice_captcha_verifier end |
#notice_form_enabled ⇒ Object
Returns the value of attribute notice_form_enabled.
74 75 76 |
# File 'lib/moderate/configuration.rb', line 74 def notice_form_enabled @notice_form_enabled end |
#notice_guard ⇒ Object
Returns the value of attribute notice_guard.
74 75 76 |
# File 'lib/moderate/configuration.rb', line 74 def notice_guard @notice_guard end |
#notice_human_verification_skip_if ⇒ Object
Returns the value of attribute notice_human_verification_skip_if.
74 75 76 |
# File 'lib/moderate/configuration.rb', line 74 def notice_human_verification_skip_if @notice_human_verification_skip_if end |
#notice_rate_limit ⇒ Object
Returns the value of attribute notice_rate_limit.
74 75 76 |
# File 'lib/moderate/configuration.rb', line 74 def notice_rate_limit @notice_rate_limit end |
#notice_turnstile_secret_key ⇒ Object
Returns the value of attribute notice_turnstile_secret_key.
74 75 76 |
# File 'lib/moderate/configuration.rb', line 74 def notice_turnstile_secret_key @notice_turnstile_secret_key end |
#notice_turnstile_site_key ⇒ Object
Returns the value of attribute notice_turnstile_site_key.
74 75 76 |
# File 'lib/moderate/configuration.rb', line 74 def notice_turnstile_site_key @notice_turnstile_site_key end |
#notify ⇒ Object
— Hooks (all no-op by default) —————————————-
60 61 62 |
# File 'lib/moderate/configuration.rb', line 60 def notify @notify end |
#on_block ⇒ Object
— Hooks (all no-op by default) —————————————-
60 61 62 |
# File 'lib/moderate/configuration.rb', line 60 def on_block @on_block end |
#parent_controller ⇒ Object
— DSA notice form —————————————————— Documented in docs/dsa-notice-form.md. Held here so the engine/controller (written by other components) read them off the same Configuration object. ‘notice_guard` is the gem-absent fallback bot gate (see app/controllers/moderate/notices_controller.rb#verify_human!): a proc that receives the controller and returns truthy to allow the POST. nil/no-op ⇒ the form just works (the default). When the host installs `rails_cloudflare_turnstile`, the controller auto-uses Turnstile instead and this proc is bypassed.
73 74 75 |
# File 'lib/moderate/configuration.rb', line 73 def parent_controller @parent_controller end |
#report_categories ⇒ Object
— Taxonomy (host-customizable) —————————————– Override the in-app COMMUNITY report category list. nil ⇒ the gem default (Moderate::Report::DEFAULT_CATEGORIES). Adding a category here requires NO migration: ‘category` is validated in the model (Moderate::Report), not by a DB check constraint. (The DSA legal-reason/country taxonomies are regulator-defined and NOT overridable.) A plain accessor — any value here is coerced to strings and compared at validation time by Report.report_categories.
57 58 59 |
# File 'lib/moderate/configuration.rb', line 57 def report_categories @report_categories end |
#signed_gid_purposes ⇒ Object
Returns the value of attribute signed_gid_purposes.
74 75 76 |
# File 'lib/moderate/configuration.rb', line 74 def signed_gid_purposes @signed_gid_purposes end |
#transparency_report_enabled ⇒ Object
Returns the value of attribute transparency_report_enabled.
74 75 76 |
# File 'lib/moderate/configuration.rb', line 74 def transparency_report_enabled @transparency_report_enabled end |
#user_class ⇒ Object
— Identity ————————————————————-
43 44 45 |
# File 'lib/moderate/configuration.rb', line 43 def user_class @user_class end |
Instance Method Details
#adapter_for(name) ⇒ Object
Look up a registered adapter object by name, constantizing a String/Class reference (the built-ins) on first use. Returns nil for an unknown name —callers decide whether that’s an error (the validator/classify path raises a helpful message; see Moderate.classify).
219 220 221 222 223 224 225 226 227 |
# File 'lib/moderate/configuration.rb', line 219 def adapter_for(name) key = normalize_name(name) ref = @adapters[key] return nil if ref.nil? resolve_adapter(ref).tap do |adapter| @adapters[key] = adapter unless adapter.equal?(ref) end end |
#adapter_registered?(name) ⇒ Boolean
229 230 231 |
# File 'lib/moderate/configuration.rb', line 229 def adapter_registered?(name) @adapters.key?(normalize_name(name)) end |
#filter(class_name, field, with: nil, mode: nil) ⇒ Object
Declare a per-field filter policy in the initializer — the twin of ‘moderates :field, with:, mode:` on the model. Stores the policy keyed by
- class_name, field
-
so ‘filter_policy_for` can resolve it (including up the
ancestor chain) at classify time.
239 240 241 242 243 244 245 246 247 248 |
# File 'lib/moderate/configuration.rb', line 239 def filter(class_name, field, with: nil, mode: nil) name = class_name.is_a?(Class) ? class_name.name : class_name.to_s field_s = field.to_s adapter = with.nil? ? @filter_adapter : normalize_name(with) resolved_mode = mode.nil? ? @default_filter_mode : normalize_mode(mode) policy = FilterPolicy.new(class_name: name, field: field_s, adapter: adapter, mode: resolved_mode) @filters[[name, field_s]] = policy policy end |
#register_adapter(name, adapter) ⇒ Object
Register a host adapter under a name of the host’s choosing. The adapter is any object responding to ‘classify(value) → Moderate::Result`. The name is what gets recorded as `Moderate::Flag#source`, so it shows in the queue.
Both arg orders are accepted for ergonomics:
config.register_adapter :openai, OpenAIModerator.new
config.register_adapter(:openai, OpenAIModerator.new)
206 207 208 209 210 211 212 213 |
# File 'lib/moderate/configuration.rb', line 206 def register_adapter(name, adapter) key = normalize_name(name) unless adapter.is_a?(String) || adapter.is_a?(Class) || adapter.respond_to?(:classify) raise ArgumentError, "adapter for #{key.inspect} must respond to #classify" end @adapters[key] = adapter end |
#validate! ⇒ Object
Cross-field validation run at the end of ‘Moderate.configure`. The per-setter checks already caught most typos; this catches the things that need the whole block resolved:
- the default text adapter must actually be registered
- every per-field filter must name a registered adapter
- a :block-mode filter must use a SYNCHRONOUS adapter — you can't reject a
save on a background result, so :block + an async adapter (e.g. a remote
classifier) is a configuration error. Async adapters run in :flag mode.
(README: "`:block` requires a synchronous adapter".)
261 262 263 264 265 266 267 268 269 270 |
# File 'lib/moderate/configuration.rb', line 261 def validate! validate_adapter_name!(@filter_adapter, context: "filter_adapter") @filters.each_value do |policy| validate_adapter_name!(policy.adapter, context: "filter #{policy.class_name}##{policy.field}") validate_block_mode_adapter!(policy) end true end |