### Install Carbon Dependency Source: https://github.com/luckyframework/carbon/blob/main/README.md Add the Carbon library to your Crystal project by including it in your shard.yml file. ```yaml dependencies: carbon: github: luckyframework/carbon ``` -------------------------------- ### Testing Email Delivery with Carbon Expectations Source: https://context7.com/luckyframework/carbon/llms.txt Details how to use Carbon's testing expectations for verifying email delivery within specs. It includes setup, assertions for specific emails, general email counts, and content checks. ```crystal require "spec" require "carbon" # Include expectations module include Carbon::Expectations # Configure for testing BaseEmail.configure do |settings| settings.adapter = Carbon::DevAdapter.new end # Reset before each test Spec.before_each do Carbon::DevAdapter.reset end describe "User signup" do it "sends a welcome email" do # Trigger the action that sends email user = create_user("alice@example.com") # Assert the specific email was delivered WelcomeEmail.new(user.name, user.email).should be_delivered end it "sends some emails during registration" do # Just check that any emails were sent register_user("bob@example.com") Carbon.should have_delivered_emails end it "does not send email to unverified users" do create_unverified_user("test@example.com") Carbon.should_not have_delivered_emails end end describe "Email content" do it "includes the user name in subject" do email = WelcomeEmail.new("Alice", "alice@example.com") email.subject.should eq "Welcome to our app, Alice!" email.to.should eq [Carbon::Address.new("alice@example.com")] email.text_body.should contain "Alice" email.html_body.should contain "Alice" end end ``` -------------------------------- ### Define a Welcome Email Class and Templates (Crystal) Source: https://context7.com/luckyframework/carbon/llms.txt Defines a `WelcomeEmail` class inheriting from `BaseEmail`. It includes initialization with user name and activation URL, specifies recipients, subject, and uses text and HTML templates. Helper methods like `formatted_date` are available within templates. ```crystal class WelcomeEmail < BaseEmail def initialize(@user_name : String, @activation_url : String) end to "user@example.com" subject "Welcome, #{@user_name}!" templates text, html # Helper methods available in templates def formatted_date Time.local.to_s("%B %d, %Y") end end ``` ```ecr Welcome, <%= @user_name %>! Thank you for joining us on <%= formatted_date %>. Activate your account: <%= @activation_url %> ``` ```ecr

Welcome, <%= @user_name %>!

Thank you for joining us on <%= formatted_date %>.

Activate your account

``` -------------------------------- ### Configure Testing Adapters and Expectations Source: https://github.com/luckyframework/carbon/blob/main/README.md Sets up the development adapter for capturing emails in memory during tests and includes Carbon expectations for verification. This is typically configured in the spec_helper. ```crystal BaseEmail.configure do settings.adapter = Carbon::DevAdapter.new end include Carbon::Expectations Spec.before_each do Carbon::DevAdapter.reset end ``` -------------------------------- ### Before and After Send Callbacks (Crystal) Source: https://context7.com/luckyframework/carbon/llms.txt Illustrates using `before_send` and `after_send` callbacks to execute code during the email sending process. `before_send` can halt delivery by setting `@deliverable = false`. Callbacks can be defined as blocks or methods. ```crystal class TrackedEmail < BaseEmail def initialize(@recipient : String, @tracking_id : String) end to @recipient subject "Important Update" templates text, html # Block-based callbacks before_send do log_email_attempt(@tracking_id) end after_send do |response| record_delivery_result(@tracking_id, response) end private def log_email_attempt(id) puts "Attempting to send email: #{id}" end private def record_delivery_result(id, response) puts "Email #{id} delivered: #{response}" end end class ConditionalEmail < BaseEmail def initialize(@recipient : String, @can_receive_email : Bool) end to @recipient subject "Newsletter" templates text # Method-based callback before_send :check_subscription after_send :log_result private def check_subscription # Prevent delivery if user unsubscribed @deliverable = false unless @can_receive_email end private def log_result(response) puts "Delivery complete" end end # Usage ConditionalEmail.new("user@example.com", can_receive_email: false).deliver # => Email is NOT sent because deliverable was set to false ``` -------------------------------- ### Apply HTML Layout to Email Templates (Crystal) Source: https://context7.com/luckyframework/carbon/llms.txt Demonstrates how to use a layout template (`app_layout.ecr`) to wrap an email's content. The `NotificationEmail` class specifies `layout app_layout`, which injects its HTML structure around the content from `notification_email/html.ecr`. ```crystal class NotificationEmail < BaseEmail def initialize(@message : String) end to "user@example.com" subject "Notification" templates html layout app_layout # References templates/app_layout/layout.ecr end ``` ```ecr

MyApp

<%= content %>
``` ```ecr

<%= @message %>

``` -------------------------------- ### Implement Email Layouts Source: https://github.com/luckyframework/carbon/blob/main/README.md Define a global layout for your emails using ECR templates and specify the layout name in your BaseEmail class. ```crystal abstract class BaseEmail < Carbon::Email macro inherited from default_from layout :application_layout end end ``` -------------------------------- ### Email Delivery Methods (Crystal) Source: https://context7.com/luckyframework/carbon/llms.txt Shows how to send emails using Carbon. `deliver` sends synchronously, blocking until completion. `deliver_later` sends asynchronously in a background process. The `deliverable?` method and `deliverable` attribute control whether an email is sent. ```crystal # Synchronous delivery - blocks until email is sent email = WelcomeEmail.new("Alice", "alice@example.com") email.deliver # Asynchronous delivery - uses configured deliver_later_strategy email = WelcomeEmail.new("Bob", "bob@example.com") email.deliver_later # Returns immediately, sends in background via spawn # Check if email is deliverable (can be set to false to skip delivery) email = WelcomeEmail.new("Test", "test@example.com") email.deliverable? # => true email.deliverable = false email.deliver # Does nothing - email is not deliverable ``` -------------------------------- ### Perform Unit Testing for Email Fields Source: https://github.com/luckyframework/carbon/blob/main/README.md Shows how to unit test email objects by instantiating them directly and asserting against their properties like recipients and body content. ```crystal it "builds a nice welcome email" do email = WelcomeEmail.new(name: "David", email_address: "david@gmail.com") email.to.should eq [Carbon::Address.new("david@gmail.com")] email.text_body.should contain "Welcome" email.html_body.should contain "Welcome" end ``` -------------------------------- ### Attach IO Memory to Email Source: https://context7.com/luckyframework/carbon/llms.txt Demonstrates how to attach an email directly from an IO::Memory object instead of a file path. This is useful for dynamically generated content. ```crystal class EmailWithIOAttachment < BaseEmail REPORT_DATA = IO::Memory.new("Report content here") def initialize(@recipient : String) end to @recipient subject "Generated Report" templates text # Attach from IO instead of file path attachment({ io: REPORT_DATA, file_name: "report.txt", mime_type: "text/plain" }) end ``` -------------------------------- ### Custom Delayed Email Delivery Strategy Source: https://context7.com/luckyframework/carbon/llms.txt Shows how to implement a custom strategy for delayed email delivery by integrating with a job queue. This allows emails to be processed asynchronously. ```crystal # Custom strategy using a job processor class JobQueueStrategy < Carbon::DeliverLaterStrategy def run(email : Carbon::Email, &block) # Queue the email for later delivery EmailJob.perform_later(email) end end class EmailJob def self.perform_later(email : Carbon::Email) # Add to job queue - implementation depends on your job processor puts "Queued email: #{email.subject}" end def perform(email : Carbon::Email) email.deliver end end # Configure to use custom strategy BaseEmail.configure do |settings| settings.adapter = Carbon::SendGridAdapter.new(api_key: ENV["SENDGRID_API_KEY"]) settings.deliver_later_strategy = JobQueueStrategy.new end # Usage WelcomeEmail.new("user", "user@example.com").deliver_later # => Email is queued via JobQueueStrategy instead of spawned ``` -------------------------------- ### Carbon DevAdapter for Development and Testing Source: https://context7.com/luckyframework/carbon/llms.txt Explains the use of `Carbon::DevAdapter` for capturing sent emails in memory during development and testing. It allows for inspection and verification of email content and delivery. ```crystal # Configure for development BaseEmail.configure do |settings| # print_emails: true will output email details to console settings.adapter = Carbon::DevAdapter.new(print_emails: true) end # Send an email WelcomeEmail.new("Test", "test@example.com").deliver # Access delivered emails Carbon::DevAdapter.delivered_emails # => Array of Carbon::Email # Check if a specific email was delivered email = WelcomeEmail.new("Test", "test@example.com") Carbon::DevAdapter.delivered?(email) # => true or false # Clear all delivered emails (useful between tests) Carbon::DevAdapter.reset Carbon::DevAdapter.delivered_emails # => [] ``` -------------------------------- ### Carbon::Address Class Usage Source: https://context7.com/luckyframework/carbon/llms.txt Illustrates the usage of the `Carbon::Address` class in Crystal for representing email addresses. It shows how to create addresses with and without display names and how to compare them. ```crystal # Create an address with just an email simple_address = Carbon::Address.new("user@example.com") simple_address.address # => "user@example.com" simple_address.name # => nil simple_address.to_s # => "user@example.com" # Create an address with a display name named_address = Carbon::Address.new("John Doe", "john@example.com") named_address.address # => "john@example.com" named_address.name # => "John Doe" named_address.to_s # => "\"John Doe\" " # Compare addresses Carbon::Address.new("test@example.com") == Carbon::Address.new("test@example.com") # => true Carbon::Address.new("Name", "test@example.com") == Carbon::Address.new("test@example.com") # => false ``` -------------------------------- ### Configure Email Adapter Source: https://github.com/luckyframework/carbon/blob/main/README.md Set the delivery adapter for your mailer class. The DevAdapter is useful for local development to print emails to the console. ```crystal BaseEmail.configure do |settings| settings.adapter = Carbon::DevAdapter.new(print_emails: true) end ``` -------------------------------- ### Define Base Email Class Source: https://github.com/luckyframework/carbon/blob/main/README.md Create an abstract base class inheriting from Carbon::Email to define shared settings like default 'from' addresses. ```crystal require "carbon" abstract class BaseEmail < Carbon::Email from Carbon::Address.new("My App Name", "support@myapp.com") from "support@myapp.com" end ``` -------------------------------- ### Email Attachments and Inline Resources (Crystal) Source: https://context7.com/luckyframework/carbon/llms.txt Shows how to add file attachments and inline resources to emails. `attachment` method is used with `file_path`, `file_name`, and `mime_type` for regular attachments. For inline resources like images, `cid` is used along with other parameters, allowing embedding in HTML using the Content-ID. ```crystal class InvoiceEmail < BaseEmail def initialize(@invoice_path : String, @recipient : String) end to @recipient subject "Your Invoice" templates text, html # Attach a file from disk attachment({ file_path: @invoice_path, file_name: "invoice.pdf", mime_type: "application/pdf" }) end class EmailWithInlineImage < BaseEmail def initialize(@recipient : String) end to @recipient subject "Report with Charts" templates html # Inline resource with Content-ID for HTML embedding attachment({ file_path: "./charts/sales.png", cid: "sales-chart", file_name: "sales.png", mime_type: "image/png" }) end # HTML template can reference inline image: ``` -------------------------------- ### Generating New Email Classes with Lucky Task Source: https://context7.com/luckyframework/carbon/llms.txt Illustrates how to use the `lucky gen.email` task to generate new email classes and their associated ECR templates. This streamlines the process of creating new email types. ```crystal # Generate a new email from command line # $ lucky gen.email WelcomeUser # This creates: # - src/emails/welcome_user_email.cr # - src/emails/templates/welcome_user_email/html.ecr # - src/emails/templates/welcome_user_email/text.ecr # Generated email class (src/emails/welcome_user_email.cr): class WelcomeUserEmail < BaseEmail # TODO: Set the 'from', 'to', and 'subject' # from "myapp@example.com" # to recipient # subject "Your subject here" templates text, html end # More examples: # $ lucky gen.email PasswordReset # $ lucky gen.email OrderConfirmation # $ lucky gen.email SubscriptionRenewal ``` -------------------------------- ### Perform Integration Testing for Emails Source: https://github.com/luckyframework/carbon/blob/main/README.md Demonstrates how to verify that emails are sent as a side effect of application logic using integration tests. It utilizes the be_delivered expectation and the global email delivery tracking. ```crystal it "sends an email after the user signs up" do SignUpUser.new(name: "Emily", email_address: "em@gmail.com").run WelcomeEmail.new(name: "Emily", email_address: "em@gmail.com").should be_delivered end it "sends some emails" do SignUpUser.new(name: "Emily", email_address: "em@gmail.com").run Carbon.should have_delivered_emails end ``` -------------------------------- ### Implement Custom Delayed Delivery Strategy Source: https://github.com/luckyframework/carbon/blob/main/README.md Defines a custom strategy for delivering emails asynchronously using a background job processor. It involves creating a strategy class that inherits from Carbon::DeliverLaterStrategy and configuring the BaseEmail settings. ```crystal class SendEmailInJobStrategy < Carbon::DeliverLaterStrategy def run(email : Carbon::Email, &block) EmailJob.perform_later(email) end end class EmailJob < JobProcessor def perform(email : Carbon::Email) email.deliver end end BaseEmail.configure do |settings| settings.deliver_later_strategy = SendEmailInJobStrategy.new end ``` -------------------------------- ### Define Individual Email Classes with Carbon Source: https://context7.com/luckyframework/carbon/llms.txt Demonstrates creating specific email classes in Crystal by inheriting from a base class. It shows how to set subjects, recipients (to, cc, bcc), custom headers, and use ECR templates for different email types. ```crystal class WelcomeEmail < BaseEmail def initialize(@user_name : String, @email_address : String) end # Required email fields to @email_address subject "Welcome to our app, #{@user_name}!" # Use ECR templates for email body templates text, html end class OrderConfirmationEmail < BaseEmail def initialize(@order_id : Int32, @customer_email : String, @total : Float64) end to @customer_email subject "Order ##{@order_id} Confirmed" # Add CC and BCC recipients cc "orders@myapp.com" bcc ["archive@myapp.com", "audit@myapp.com"] templates text, html end class NewsletterEmail < BaseEmail def initialize(@recipient : String, @edition : String) end to @recipient subject "Newsletter: #{@edition}" # Custom headers header "X-Campaign-ID", "newsletter-2024" header "List-Unsubscribe", "" # Reply-to shortcut reply_to "newsletter-reply@myapp.com" templates html end ``` -------------------------------- ### Dynamic Email Configuration with Symbols in Crystal Source: https://context7.com/luckyframework/carbon/llms.txt This snippet shows how to define dynamic email fields (from, to, cc, subject) by referencing private methods using symbols. It allows for runtime computation of these fields based on instance variables like user and order objects. Dependencies include the User and Order models, and the BaseEmail class from the Carbon library. ```crystal class DynamicEmail < BaseEmail def initialize(@user : User, @order : Order) end # Use symbols to call methods for field values from :sender_address to :recipient_list cc :cc_recipients subject :dynamic_subject templates text, html private def sender_address "orders@myapp.com" end private def recipient_list [@user.email, @user.secondary_email].compact end private def cc_recipients @order.high_value? ? "vip-support@myapp.com" : [] of String end private def dynamic_subject "Order ##{@order.id} - #{@order.status.capitalize}" end end ``` -------------------------------- ### Deliver Emails Source: https://github.com/luckyframework/carbon/blob/main/README.md Send emails immediately using the deliver method or asynchronously in the background using deliver_later. ```crystal # Send the email right away! WelcomeEmail.new("Kate", "kate@example.com").deliver # Send the email in the background using `spawn` WelcomeEmail.new("Kate", "kate@example.com").deliver_later ``` -------------------------------- ### Carbon::Emailable Module for Recipients Source: https://context7.com/luckyframework/carbon/llms.txt Explains how to use the `Carbon::Emailable` module in Crystal to make any class usable as an email recipient. It requires implementing `emailable` and optionally `emailable_for_from` methods. ```crystal class User include Carbon::Emailable property name : String property email : String def initialize(@name, @email) end # Required: return the address for recipient fields (to, cc, bcc) private def emailable : Carbon::Address Carbon::Address.new(@email) end # Optional: return a different address when used as 'from' private def emailable_for_from : Carbon::Address Carbon::Address.new(@name, @email) end end # Now use the User directly in email fields class UserWelcomeEmail < BaseEmail def initialize(@user : User) end to @user # Uses user.carbon_address automatically subject "Welcome!" templates text, html end # Usage user = User.new("Alice", "alice@example.com") UserWelcomeEmail.new(user).deliver ``` -------------------------------- ### Create Email Class Source: https://github.com/luckyframework/carbon/blob/main/README.md Define a specific email class by inheriting from your base class, specifying recipients, subjects, and template types. ```crystal class WelcomeEmail < BaseEmail def initialize(@name : String, @email_address : String) end to @email_address subject "Welcome, #{@name}!" header "My-Custom-Header", "header-value" reply_to "no-reply@noreply.com" templates text, html end ``` -------------------------------- ### Create Base Email Class with Carbon Source: https://context7.com/luckyframework/carbon/llms.txt Defines an abstract base email class in Crystal using the Carbon library. It sets a default 'from' address and configures the email adapter, such as the DevAdapter for testing. ```crystal require "carbon" # Create an abstract base class for all your application emails abstract class BaseEmail < Carbon::Email # Set a default 'from' address for all emails from Carbon::Address.new("My App", "support@myapp.com") # Or use just the email string # from "support@myapp.com" end # Configure the adapter - must be done for abstract email classes BaseEmail.configure do |settings| # DevAdapter captures emails in memory and optionally prints them settings.adapter = Carbon::DevAdapter.new(print_emails: true) # Optional: configure delivery strategy for deliver_later settings.deliver_later_strategy = Carbon::SpawnStrategy.new end ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.