Class: Hanami::Mailer
- Inherits:
-
Object
- Object
- Hanami::Mailer
- Extended by:
- Dry::Configurable
- Includes:
- ViewIntegration
- Defined in:
- lib/hanami/mailer.rb,
lib/hanami/mailer/errors.rb,
lib/hanami/mailer/message.rb,
lib/hanami/mailer/version.rb,
lib/hanami/mailer/attachment.rb,
lib/hanami/mailer/dsl/exposure.rb,
lib/hanami/mailer/delivery/smtp.rb,
lib/hanami/mailer/delivery/test.rb,
lib/hanami/mailer/dsl/exposures.rb,
lib/hanami/mailer/attachment_set.rb,
lib/hanami/mailer/delivery/result.rb,
lib/hanami/mailer/dsl/attachments.rb,
lib/hanami/mailer/dsl/plucky_proc.rb,
lib/hanami/mailer/view_integration.rb
Overview
Base mailer class
Defined Under Namespace
Modules: DSL, Delivery, ViewIntegration Classes: Attachment, AttachmentSet, DuplicateAttachmentError, Error, Message, MissingAttachmentError, MissingDeliveryError, MissingRecipientError, MissingSenderError
Constant Summary collapse
- STANDARD_HEADERS =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Standard email headers that have dedicated convenience methods
%i[from to cc bcc reply_to return_path subject].freeze
- VERSION =
"3.0.0"
Instance Attribute Summary collapse
- #delivery_method ⇒ Object readonly private
- #view ⇒ Object readonly private
Class Method Summary collapse
-
.attachment(name_or_filename = nil, **options, &block) ⇒ Object
Define an attachment.
- .attachments ⇒ Object private
-
.delivery_option(name, value = nil, &block) ⇒ Object
Define a delivery option.
- .delivery_options ⇒ Object private
-
.expose(*names, **options, &block) ⇒ Object
Defines one or more values to expose to the template.
- .exposures ⇒ Object private
-
.file(filename, content, content_type: nil, inline: false) ⇒ Attachment
Helper method for creating Attachment objects.
- .gem_loader ⇒ Object private
-
.header(field_name, value = nil, &block) ⇒ Object
Define a header field.
- .headers ⇒ Object private
- .inherited(subclass) ⇒ Object private
-
.private_expose(*names, **options, &block) ⇒ Object
Defines one or more private exposures.
Instance Method Summary collapse
-
#deliver(headers: {}, attachments: nil, format: nil, **input) ⇒ Delivery::Result
Deliver the email.
-
#file ⇒ Attachment
Helper method for creating attachments in attachment blocks.
-
#initialize(view: nil, delivery_method: nil) ⇒ Mailer
constructor
Initialize a new mailer instance.
-
#prepare(headers: {}, attachments: nil, format: nil, **input) ⇒ Message
Build the message without delivering it.
-
#preview(headers: {}, attachments: nil, format: nil, **input) ⇒ Message
Previews the email without delivering it.
Methods included from ViewIntegration
Constructor Details
#initialize(view: nil, delivery_method: nil) ⇒ Mailer
Initialize a new mailer instance
287 288 289 290 |
# File 'lib/hanami/mailer.rb', line 287 def initialize(view: nil, delivery_method: nil) @view = view @delivery_method = delivery_method || default_delivery_method end |
Instance Attribute Details
#delivery_method ⇒ Object (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
278 279 280 |
# File 'lib/hanami/mailer.rb', line 278 def delivery_method @delivery_method end |
#view ⇒ Object (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
278 279 280 |
# File 'lib/hanami/mailer.rb', line 278 def view @view end |
Class Method Details
.attachment(name_or_filename = nil, **options, &block) ⇒ Object
Define an attachment
An attachment block returns one or more attachment objects (use the #file helper). As with
#header, its positional parameters receive exposure values and its keyword parameters
receive matching keys from the deliver input.
220 221 222 |
# File 'lib/hanami/mailer.rb', line 220 def (name_or_filename = nil, **, &block) .add(name_or_filename, block, **) end |
.attachments ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
225 226 227 |
# File 'lib/hanami/mailer.rb', line 225 def @attachments ||= DSL::Attachments.new end |
.delivery_option(name, value = nil, &block) ⇒ Object
Define a delivery option
Delivery options are delivery-method-specific parameters that can be used to customize how a message is sent. For example, a third-party email service might support scheduled sending, priority levels, or tracking options.
As with #header, a block's positional parameters receive exposure values and its keyword
parameters receive matching keys from the deliver input.
257 258 259 |
# File 'lib/hanami/mailer.rb', line 257 def delivery_option(name, value = nil, &block) .add(name, block, default: value) end |
.delivery_options ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
262 263 264 |
# File 'lib/hanami/mailer.rb', line 262 def @delivery_options ||= DSL::Exposures.new end |
.expose(*names, **options, &block) ⇒ Object
Defines one or more values to expose to the template.
An exposure's value comes from the first of these that applies:
- The given block (single name only).
- An instance method matching the name.
- The matching key in the input given to #call, or the
:defaultoption if the input has no such key.
When a block or method provides the value, its parameters determine what it receives:
- Positional parameters receive other exposures' values, matched by name.
- Keyword parameters receive matching keys from the input. Give them defaults to make those input keys optional.
- A keyword splat (
**input) receives the entire input.
Pass several names to expose multiple values at once; the options then apply to every named exposure. A block may only be given for a single name.
182 183 184 185 186 187 188 |
# File 'lib/hanami/mailer.rb', line 182 def expose(*names, **, &block) if names.length == 1 exposures.add(names.first, block, **) else names.each { |name| exposures.add(name, nil, **) } end end |
.exposures ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
205 206 207 |
# File 'lib/hanami/mailer.rb', line 205 def exposures @exposures ||= DSL::Exposures.new end |
.file(filename, content, content_type: nil, inline: false) ⇒ Attachment
Helper method for creating Attachment objects
This is a convenience method for creating Attachment objects
that can be passed to the attachments: parameter.
84 85 86 |
# File 'lib/hanami/mailer.rb', line 84 def file(filename, content, content_type: nil, inline: false) Attachment.new(filename:, content:, content_type:, inline:) end |
.gem_loader ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/hanami/mailer.rb', line 15 def self.gem_loader @gem_loader ||= Zeitwerk::Loader.new.tap do |loader| root = File.("..", __dir__) loader.tag = "hanami-mailer" loader.push_dir(root) loader.ignore( "#{root}/hanami-mailer.rb", "#{root}/hanami/mailer/version.rb", "#{root}/hanami/mailer/errors.rb" ) loader.inflector = Zeitwerk::GemInflector.new("#{root}/hanami-mailer.rb") loader.inflector.inflect( "dsl" => "DSL", "smtp" => "SMTP" ) end end |
.header(field_name, value = nil, &block) ⇒ Object
Define a header field
Can be called with:
- A static value:
header :from, "noreply@example.com" - A static value with proper casing:
header "X-Priority", "1" - A proc/block:
header(:to) { |recipient| recipient[:email] }
A block's parameters follow the same convention as everywhere in the mailer:
- Positional parameters receive exposure values, matched by name.
- Keyword parameters receive matching keys from the
deliverinput.
Header names:
- Symbols with underscores (e.g., :x_priority) are converted to Title-Case (X-Priority)
- Strings are passed through as-is, preserving casing
- Use strings for full control over casing
113 114 115 |
# File 'lib/hanami/mailer.rb', line 113 def header(field_name, value = nil, &block) headers.add(field_name, block, default: value) end |
.headers ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
135 136 137 |
# File 'lib/hanami/mailer.rb', line 135 def headers @headers ||= DSL::Exposures.new end |
.inherited(subclass) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
267 268 269 270 271 272 273 274 |
# File 'lib/hanami/mailer.rb', line 267 def inherited(subclass) super subclass.instance_variable_set(:@headers, headers.dup) subclass.instance_variable_set(:@exposures, exposures.dup) subclass.instance_variable_set(:@attachments, .dup) subclass.instance_variable_set(:@delivery_options, .dup) end |
.private_expose(*names, **options, &block) ⇒ Object
Defines one or more private exposures.
A private exposure is computed and stays available as a dependency to other exposures, and
to the mailer's headers, attachments, and delivery options, but is never passed to the view
for rendering. This is a shorthand for expose(..., private: true).
200 201 202 |
# File 'lib/hanami/mailer.rb', line 200 def private_expose(*names, **, &block) expose(*names, **, private: true, &block) end |
Instance Method Details
#deliver(headers: {}, attachments: nil, format: nil, **input) ⇒ Delivery::Result
Deliver the email
303 304 305 306 |
# File 'lib/hanami/mailer.rb', line 303 def deliver(headers: {}, attachments: nil, format: nil, **input) = prepare(headers:, attachments:, format:, **input) delivery_method.call() end |
#file ⇒ Attachment
Helper method for creating attachments in attachment blocks
Returns an Attachment object that provides a structured, validated way to define attachment data instead of using raw hashes.
410 411 412 |
# File 'lib/hanami/mailer.rb', line 410 def file(...) self.class.file(...) end |
#prepare(headers: {}, attachments: nil, format: nil, **input) ⇒ Message
Build the message without delivering it
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 |
# File 'lib/hanami/mailer.rb', line 341 def prepare(headers: {}, attachments: nil, format: nil, **input) # Evaluate exposures as our "locals". These will be provided as the _depdenencies_ (available # via positional params) to all our other class-level exposure-like APIs: headers, # attachments, and delivery options. locals = self.class.exposures.bind(self).call(input) # Evaluate class-level headers, giving precdence to headers given as explicit arguments. header_overrides = headers.compact headers = self.class.headers .bind(self) .call(input, dependencies: locals) .merge(header_overrides) # Extract custom headers and normalize their header names to proper casing. custom_headers = headers .reject { |key, _| STANDARD_HEADERS.include?(key) } .transform_keys { |key| normalize_header_name(key) } # Render bodies. Private exposures are available to the methods above as dependencies, but are # withheld from the view. html_body, text_body = render(self.class.exposures.reject_private(locals), format:) # Evaluate class-level attachments and merge with runtime attachments. = = self.class. .bind(self) .call(input, dependencies: locals) .concat() .to_a # Evaluate delivery options. = self.class..bind(self).call(input, dependencies: locals) # Build message Message.new( from: headers[:from], to: headers[:to], cc: headers[:cc], bcc: headers[:bcc], reply_to: headers[:reply_to], return_path: headers[:return_path], subject: headers[:subject], html_body:, text_body:, attachments: , headers: custom_headers, delivery_options: ) end |
#preview(headers: {}, attachments: nil, format: nil, **input) ⇒ Message
Previews the email without delivering it
Builds the message and passes it to the delivery method's preview hook, returning whatever
that returns. The default (and test) delivery method returns the message unchanged; a
third-party delivery method can override preview to apply service-specific logic.
323 324 325 326 |
# File 'lib/hanami/mailer.rb', line 323 def preview(headers: {}, attachments: nil, format: nil, **input) = prepare(headers:, attachments:, format:, **input) delivery_method.preview() end |