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.rc1"
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.
Methods included from ViewIntegration
Constructor Details
#initialize(view: nil, delivery_method: nil) ⇒ Mailer
Initialize a new mailer instance
277 278 279 280 |
# File 'lib/hanami/mailer.rb', line 277 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.
269 270 271 |
# File 'lib/hanami/mailer.rb', line 269 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.
269 270 271 |
# File 'lib/hanami/mailer.rb', line 269 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.
212 213 214 |
# File 'lib/hanami/mailer.rb', line 212 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.
217 218 219 |
# File 'lib/hanami/mailer.rb', line 217 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.
248 249 250 |
# File 'lib/hanami/mailer.rb', line 248 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.
253 254 255 |
# File 'lib/hanami/mailer.rb', line 253 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 ‘:default` option 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.
176 177 178 179 180 181 182 |
# File 'lib/hanami/mailer.rb', line 176 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.
198 199 200 |
# File 'lib/hanami/mailer.rb', line 198 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.
81 82 83 |
# File 'lib/hanami/mailer.rb', line 81 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.
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/hanami/mailer.rb', line 14 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 }`
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 ‘deliver` input.
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
109 110 111 |
# File 'lib/hanami/mailer.rb', line 109 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.
130 131 132 |
# File 'lib/hanami/mailer.rb', line 130 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.
258 259 260 261 262 263 264 265 |
# File 'lib/hanami/mailer.rb', line 258 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)`.
193 194 195 |
# File 'lib/hanami/mailer.rb', line 193 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
292 293 294 295 |
# File 'lib/hanami/mailer.rb', line 292 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.
377 378 379 |
# File 'lib/hanami/mailer.rb', line 377 def file(...) self.class.file(...) end |
#prepare(headers: {}, attachments: nil, format: nil, **input) ⇒ Message
Build the message without delivering it
309 310 311 312 313 314 315 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 |
# File 'lib/hanami/mailer.rb', line 309 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 |