Ruby on Rails – Hacker Notes https://hackernotes.io Sat, 30 Mar 2024 16:42:02 +0000 en-US hourly 1 https://wordpress.org/?v=4.6.28 A Production-Ready Rails 5 Email Workflow with Mailer Previews, Premailer and ActiveJob https://hackernotes.io/a-production-ready-rails-5-email-workflow-with-mailer-previews-premailer-and-activejob/ https://hackernotes.io/a-production-ready-rails-5-email-workflow-with-mailer-previews-premailer-and-activejob/#comments Sat, 24 Sep 2016 05:52:10 +0000 https://hackernotes.io/?p=96 Since the introduction of Mailer Previews and ActiveJob in Rails 4.1, the framework has truly set itself apart from others in the comprehensiveness and comfort of its workflow around emails. The framework’s creator, DHH, has espoused a perspective that mailers are views, and accordingly Rails now gives us an email workflow that is nearly identical to that […]

The post A Production-Ready Rails 5 Email Workflow with Mailer Previews, Premailer and ActiveJob appeared first on Hacker Notes.

]]>
Since the introduction of Mailer Previews and ActiveJob in Rails 4.1, the framework has truly set itself apart from others in the comprehensiveness and comfort of its workflow around emails. The framework’s creator, DHH, has espoused a perspective that mailers are views, and accordingly Rails now gives us an email workflow that is nearly identical to that around ordinary controllers and views.

I will walk through my complete email stack and workflow, which I have used very effectively across several production apps.

As of Rails 5, the project generator will drop the following ApplicationMailer implementation into your project tree:


# in app/mailers/application_mailer.rb

class ApplicationMailer < ActionMailer::Base
  default from: '[email protected]'
  layout 'mailer'
end

Perhaps the only notable omission from Rails’ stock email workflow is CSS inlining, but that is easily addressed with the premailer-rails gem, which we will take care of before we go any further:


# in your Gemfile, add premailer-rails with...

gem 'premailer-rails'

We should then run bundle install and restart our server with bin/rails server. We can now add a mailer.sass stylesheet to the base of our app/assets/stylesheets directory:

// in app/assets/stylesheets/mailer.sass

body
  padding: 0
  background: #f7f7f7
  text-align: center

.email-table
  width: 538px
  padding: 20px
  background: #fff
  margin: 40px auto
  text-align: left
  font-family: 'Helvetica Neue'

h1
  font-size: 20px

p
  margin-top: 0
  font-size: 15px

In order for premailer to pull and inline these styles in production, we will need to add this file to our precompile list:

# add in config/initializers/assets.rb...

Rails.application.config.assets.precompile += %w( mailer.css )

Rails’ project generator will drop a mailer layout into app/views/mailers/mailer.html.erb. Now all we have to do to add inline styles to our mailers is to reference this stylesheet from the layout with an ordinary stylesheet_link_tag. With this, premailer will implicitly handle inlining when the message is rendered and delivered.

<!-- in app/views/layouts/mailer.html.erb -->
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

    <!-- * simply referencing the stylesheet with this stylesheet_link_tag * -->
    <!-- * is enough for premailer to pull-in and inline your styles * -->
    <%= stylesheet_link_tag "mailer" %>

  </head>

  <body>
    <%= yield %>
  </body>
</html>

Let’s go ahead and subclass ApplicationMailer now with something concrete. For our purposes here, let’s create a UserMailer with a welcome action to deliver a welcome email to new registrants.

# in app/mailers/user_mailer.rb

class UserMailer < ApplicationMailer
  def welcome(user)
    @user = user
    mail(to: @user.email, subject: "Welcome to our site!")
  end
end

…and a corresponding template:

<!-- in app/views/user_mailer/welcome.html.erb -->

<table class="email-table">
  <tbody>
    <tr class="header-row">
      <td>
        <h1>Welcome, <%= @user.name %>!</h1>
      </td>
    </tr>
    <tr class="body-row">
      <td>
        <p>Thanks for your registering! We appreciate your interest.</p>
      </td>
    </tr>
  </tbody>
</table>

Now in order to work with this mailer as we would an ordinary view all we have to do is create a mailer preview for it. We can do this by dropping a file into test/mailers/previews:

# in test/mailers/previews/user_mailer_preview.rb

class UserMailerPreview < ActionMailer::Preview
  def welcome
    user = OpenStruct.new(email: "[email protected]", name: "John Doe")
    UserMailer.welcome(user)
  end
end

Note that we are passing an OpenStruct with a few carefully defined attributes to the mailer here. In reality you will probably want to pull an actual record from your database via ActiveRecord or use a factory or fixture.

You can now navigate to http://localhost:3000/rails/mailers, where you will find an index of defined mailer previews, which at the moment will just show the above-defined UserMailer#welcome preview.

screen-shot-2016-09-23-at-10-21-46-pm

…and if you click through the link to “welcome” you will find a preview of the mailer, complete with inlined styles:

screen-shot-2016-09-23-at-10-27-20-pm

You can now work with your mailer and template in-browser as if they were an ordinary controller action and view.

The final piece to Rails’ email workflow worth noting is ActiveJob. As of Rails 4.2 ActionMailer has included an elegant ActiveJob integration in deliver_later. When you wish to deliver an email in your application and would have previously invoked deliver, instead invoke deliver_later and the delivery will be queued up in ActiveJob for asynchronous execution. For example…

UserMailer.welcome(@user).deliver_later

That is all there is to it.

The post A Production-Ready Rails 5 Email Workflow with Mailer Previews, Premailer and ActiveJob appeared first on Hacker Notes.

]]>
https://hackernotes.io/a-production-ready-rails-5-email-workflow-with-mailer-previews-premailer-and-activejob/feed/ 4
Validation Contexts in ActiveRecord https://hackernotes.io/contextual-validation-activerecord/ Wed, 10 Aug 2016 16:17:53 +0000 https://hackernotes.io/?p=357 Validation contexts are a little-appreciated but immensely practical feature of Ruby on Rails’ object-relational mapper, ActiveRecord. I cannot count the number of times I have seen hacks around a problem for which a validation context would have been a perfect fit simply because this feature lives a bit under the radar and isn’t in every Rails […]

The post Validation Contexts in ActiveRecord appeared first on Hacker Notes.

]]>
Validation contexts are a little-appreciated but immensely practical feature of Ruby on Rails’ object-relational mapper, ActiveRecord. I cannot count the number of times I have seen hacks around a problem for which a validation context would have been a perfect fit simply because this feature lives a bit under the radar and isn’t in every Rails developer’s toolbox.

What is a validation context, precisely? It is a way to constrain a model validation to a particular usage context for a record. This is similar to what you might achieve with something like state_machine, but far more lightweight.

Let’s say we have an application where we want to dispense gift cards to select users. Administrators can manage an inventory of gift cards and then invite users to claim them by filling out a form at a tokenized link.

A schema for such a feature might look as follows:

class DefineSchema < ActiveRecord::Migration[5.0]
  def change
    create_table :gift_cards do |t|
      t.string :code
      t.string :token
      t.string :name
      t.string :email
      t.datetime :claimed_at
      t.timestamps
    end
  end
end

Different validation rules apply in different contexts. In the admin-editing context (which we’ll consider the default context) the record is valid so long as it has a code. In the user-claiming scenario, the gift card is only valid if it has been assigned a name, email and the user has supplied a valid confirmation of its token.

We can tie these contexts to model validations by supplying the :on option to our validates and validate calls. Our gift card model might therefore look as follows:

class GiftCard < ApplicationRecord 
  attr_accessor :token_confirmation 

  validates :code, presence: true 

  # Contextual validations:
  validates :name, presence: true, on: :claim 
  validates :email, presence: true, format: /\A\S+@.+\.\S+\z/, on: :claim 
  validate :token_match?, on: :claim 

  before_create :generate_token 

  scope :unclaimed, ->{ where(claimed_at: nil) }

  def claim(attrs={})
    self.attributes = attrs.merge(claimed_at: Time.now)
    save(context: :claim)
  end  

  private

  def token_match?
    unless token_confirmation == token
      errors[:base] << "You are not authorized to claim this gift card"
    end
  end

  # generate a random token for this Gift Card (i.e. for token link authorization)
  def generate_token
    self.token = SecureRandom.hex(10)
  end
end

The beauty of validation contexts for use cases like we have here is how declarative and readable they are and how foolproof they become once we’re further up in the stack. To drive this point home, let’s have a look at how skinny the controller and UI layers we build around this model to handle the full user flow are.

class GiftCards::ClaimsController < ApplicationController
  before_action :find_gift_card

  def new
    @gift_card.token_confirmation = params[:token_confirmation]
  end

  def create
    if @gift_card.claim(gift_card_params)
      GiftCardMailer.claim_notification(@gift_card).deliver_later
      redirect_to gift_card_claim_path(@gift_card)
    else
      render :new
    end
  end

  def show
    # default render
  end

  private

  def gift_card_params
    params.fetch(:gift_card, {}).permit(:name, :email, :token_confirmation)
  end

  def find_gift_card
    @gift_card = GiftCard.find(params[:gift_card_id])
  end
end

This minimal controller implementation can handle our entire flow of presenting a claim form, processing and validating user input, delivering an email and presenting the user a success page when they are done. The minimal form implementation below is enough to take all the requisite input, as well as keep the token that the user came into the flow with in scope (note: this is using simple_form):


<div class="row">

<div class="col-md-4 col-md-offset-4">
    <%= simple_form_for @gift_card, url: gift_card_claims_path(@gift_card), method: :post do |f| %>
      <%= f.error :base %>
      <%= f.input :name %>
      <%= f.input :email %>
      <%= f.input :token_confirmation, as: :hidden %>
      <%= f.button :submit, "Claim Gift Card" %>
    <% end %>
  </div>

</div>

It is worth noting that as of Rails 4.1, the on option to validate/validates can now take multiple contexts. This is welcome flexibility and in my opinion even further reduces the number of real-world use cases for heavyweight solutions like state_machine.

The post Validation Contexts in ActiveRecord appeared first on Hacker Notes.

]]>
Less Code https://hackernotes.io/less-code/ https://hackernotes.io/less-code/#comments Sat, 26 Mar 2016 04:42:42 +0000 https://hackernotes.io/?p=76 These days I increasingly feel that I am in a minority in my deep appreciation – love, even – of the heaviest weight of heavyweight dynamic MVC web frameworks: Ruby on Rails. I have worked, now, with many of the more popular frameworks on both the server and client side. Flask, Django, Web.py, React, Angular, […]

The post Less Code appeared first on Hacker Notes.

]]>
These days I increasingly feel that I am in a minority in my deep appreciation – love, even – of the heaviest weight of heavyweight dynamic MVC web frameworks: Ruby on Rails. I have worked, now, with many of the more popular frameworks on both the server and client side. Flask, Django, Web.py, React, Angular, Express JS, Sinatra and others. I still come back to Rails.

I belive that, for the overwhelming majority of domains, it is the tool – if weilded with healthy reverence for its conventions and idioms – which leads to the most manageable, consistent, non-redundant and all-around sane codebases.

Nearly every “scaling” or “complexity” gripe that I have heard leveled at Rails is in fact a complaint about non-idiomatic use of the framework. I still routinely stumble on codebases that make zero use of key-based fragment caching (an innovation that elevated Rails far above many other frameworks in performance when it came to real-life application – not just framework benchmarking), poor or incorrect use of the ActiveRecord query interface, poor domain modeling (failing to see when validations or logic belong on relations, for instance, and not in the related objects), failure to design properly DRY controllers (failure to use exception-based control flow and filters where appropriate), heavy integration of and reliance on obviously poorly maintained dependencies, and much, much more.

So accepting now that one can – and, in fact, people often do – make horrible messes of applications they implement with Rails, I will continue to devote myself to its idiomatic application until something comes along that does it all better in the real world. And my real world metrics of success are modest: “is there less code?”, “is it easier to read?”, “does it break less?”. Academic cases for purity should always take a back seat if they result in an outcome that is many times worse by these practical metrics, which is often the reality.

In this time when choices at all levels of your stack abound like never before, it pays to be mindful of what your real-world metrics of success are, and to honestly weigh how closely your stack is aligned with them.

The post Less Code appeared first on Hacker Notes.

]]>
https://hackernotes.io/less-code/feed/ 1
Pattern Vision Redux https://hackernotes.io/pattern-vision-redux/ Thu, 11 Sep 2014 04:37:46 +0000 https://hackernotes.io/?p=68 A couple years ago I read an article DHH had posted to 37Signals’ “Signal vs. Noise” blog called Pattern Vision. I love that article not because it is at all profound. It isn’t. Kind of the opposite. I love it because I could have written it. The ways of thinking described in it are overwhelmingly […]

The post Pattern Vision Redux appeared first on Hacker Notes.

]]>
A couple years ago I read an article DHH had posted to 37Signals’ “Signal vs. Noise” blog called Pattern Vision. I love that article not because it is at all profound. It isn’t. Kind of the opposite. I love it because I could have written it. The ways of thinking described in it are overwhelmingly common in the work I come across and live with day to day. Every new codebase I encounter seems to carry some new dubiously-applied patterns, some new collection of less-than-idiomatic directories, modules and classes for me to discover nested within “/app”.

  • contexts
  • habits
  • repos
  • notification_subscribers
  • policies
  • services
  • authorizers
  • interactors

Every item in the above list is a real example from the wild.

Universally the consequence of introducing less-than-idiomatic patterns to an otherwise idiomatic Rails application has been degraded productivity, more work, more bugs. Ususally some justification is made along the lines of “Rails was built for a certain limited type of application, and our application is obviously not one of them. It’s far more complicated.” Ususally this is an excuse for not fully learning or attempting to apply Rails’ idioms, or even for flat-out misunderstanding them. Usually the app has become so complicated because of a rolling snowball of decisions to make it so, not because it innately is. Usually the root of the problem is some type of Pattern Vision.

Some people fear ActiveRecord. I have sat through hours-long engineering meetings where a couple of developers have pontificated at length about how unhygenic it is to embed logic in your persistence layer. In fact, this isn’t at all what ActiveRecord does — your persistence layer is the database, ActiveRecord sits atop it. It’s ActiveRecord’s job is to enforce your application’s domain constraints in a single place.

I will gladly sacrifice some amount of theoretical “correctness” in architecture for a 10x reduction in quantity of code and complexity. Reductions of that scale are the wonderful reality that Rails brought to light by embracing the practical and not giving a fuck about architectural “wankery”, as DHH calls it. It always makes me sad to see the wonderful gifts that Rails’ brought us with its idioms ignored or thrown to the wayside in service of imagined problems.

The post Pattern Vision Redux appeared first on Hacker Notes.

]]>
Russian Doll Caching in Rails 4 https://hackernotes.io/russian-doll-caching-in-rails-4/ Sat, 07 Sep 2013 00:58:11 +0000 https://hackernotes.io/?p=37 Rails 4 nudges developers toward more pervasive use of fragment caching (and the so-called “russian doll caching” strategy of deeply nesting fragment caches) with the introduction of ActionView::Digestor and on-by-default appending of template digests to fragment cache keys. Though actually pretty simple in its implementation, this feature (paired with correct use of ActiveRecord’s “touch” relation […]

The post Russian Doll Caching in Rails 4 appeared first on Hacker Notes.

]]>
Rails 4 nudges developers toward more pervasive use of fragment caching (and the so-called “russian doll caching” strategy of deeply nesting fragment caches) with the introduction of ActionView::Digestor and on-by-default appending of template digests to fragment cache keys. Though actually pretty simple in its implementation, this feature (paired with correct use of ActiveRecord’s “touch” relation option) solves most of the formerly enormous headaches you’d encounter (and still do today with pretty much every other framework) trying to implement an advanced caching strategy with largely dynamic content. I’ve been working with this technique for more than half a year now (initially using the Rails 3 “cache_digests” gem , which implemented the same functionality now available through ActionView::Digestor in Rails 4).

At Chloe + Isabel we have weekly developer talks. Below are the slides from one I gave detailing the “russian doll” caching strategy in enough depth, I feel, to bring most Rails developers to a strong enough understanding to work with the techinique.

 

The post Russian Doll Caching in Rails 4 appeared first on Hacker Notes.

]]>