ActionMailer and Ramaze - ruby-on-rails

Is it possible to use ActionMailer in a web framework like Ramaze, or do I need to use Rails?

You can use ActionMailer without Rails quite easily. I'm not familiar with Ramaze, but here's plain ruby, which should be easy to integrate into whatever framework you wish:
PATH/mailer.rb
require 'rubygems'
require 'action_mailer'
class Mailer < ActionMailer::Base
def my_email
recipients "recipient#their_domain.com"
from "me#my_domain.com"
subject "my subject"
body :variable1 => 'a', :variable2 => 'b'
end
end
Mailer.template_root = File.dirname(__FILE__)
Mailer.delivery_method = :sendmail
Mailer.logger = Logger.new(STDOUT)
# this sends the email
Mailer.deliver_my_email
Then put the email templates in a directory named after the your ActionMailer class
PATH/mailer/my_email.html.erb
variable 1: <%= #variable1 %>
variable 2: <%= #variable2 %>
Check out the API Docs for more configuration options, but those are the basics

Related

How to avoid sending emails in Rails test-env with sendgrid-ruby?

Problem
I seem to be facing a stubborn issue with my RSpec tests trying to constantly send emails in test-env despite my configuration should avoid it. Whatever I try it seems to totally ignore it.
My environment
Rails 6.1.1
Ruby 3.0.0
sendgrid-ruby gem 6.3.9
I have a mailer class inheritance-chain as follows: OrganizationMailer<-ApplicationMailer<-ActionMailer::Base
In my config/environments/test.rb I have the following mail-related configuration
config.action_mailer.delivery_method = :test
config.action_mailer.perform_deliveries = false
config.active_job.queue_adapter = :test
My config/application.rb and config/environment.rb don't contain any extra configuration.
Bit offtopic maybe, but just in case adding it as well:
I have ensured that the line ENV['RAILS_ENV'] ||= 'test' is present in both- spec/spec_helper.rb and spec/rails_helper.rb since for some reason Rails randomly triggered my tests in staging environment. I also had to change the .rspec file contents in my project from --require rspec_helper to --require rails_helper. I got the solution from here1, here2 and here3. But this I don't think plays any role in current problem.
Bad solution
I hacked my way through this issue atm by just adding the unless Rails.env.test? on top of each email-sending method I'm having to ensure none of them reach Sendgrid in tests, but this sucks big time I know. That's why I'm posting this question to get this fixed properly without such if/unless-clauses.
My theory
Can it be that sendgrid-ruby is here to blame? I inherit things from ApplicationMailer and ActionMailer but eventually what is sending the emails is Sendgrid ruby gem. If so, how to avoid it best? I didn't find any hints from the sendgrid-ruby documentation about that. I will post one of my simple mailer-methods below so you could see the situation atm:
# frozen_string_literal: true
# using SendGrid's Ruby Library
# https://github.com/sendgrid/sendgrid-ruby
require 'sendgrid-ruby'
class MyMailer < ApplicationMailer
include SendGrid
def my_mailer_method(my_object:)
unless Rails.env.test? # <---- Hack I'd like to get rid of
from = Email.new(email: 'no-reply#my.domain', name: t('general.title'))
to = Email.new(email: my_object.contact_email)
subject = "#{t('my_mailer.my_mailer_method.subject')}: #{my_object.my_object_title}"
content = Content.new(
type: 'text/html',
value: ApplicationController.render(
template: 'my_mailer/my_mailer_method',
locals: {
my_object: my_object
},
layout: nil
)
)
mail = SendGrid::Mail.new(from, subject, to, content)
sg = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY'])
# Send out mail
response = sg.client.mail._('send').post(request_body: mail.to_json)
end
end
end
The issue here is that you may have set config.action_mailer.delivery_method = :test but you are not actually using ActionMailer to deliver your emails. Instead, within the MyMailer class you are directly using the SendGrid API, and sidestepping ActionMailer entirely.
If you want to use SendGrid API to send your emails, then I actually recommend using the sendgrid-actionmailer gem. It allows you to use ActionMailer to build your emails and uses the SendGrid API under the hood to send them. This allows you to send other parameters that the API supports and would be more difficult or impossible with SMTP, while still using the Rails standard ActionMailer to send the emails.
To ensure that your mails are sent by SendGrid in production, but not sent in test, you would set:
config.action_mailer.delivery_method = :sendgrid_actionmailer
in your production.rb environment. And set:
config.action_mailer.delivery_method = :test
config.action_mailer.perform_deliveries = false
as you already have in your test.rb environment.

why am i getting "undefined method `strip' for nil:NilClass" when i'm not calling the strip method?

I'm building a rudimentary app using the Twilio API, and have reached a block when using figaro to store account_sid and auth_token as environment variables in application.yml.
In my controller I have:
require 'twilio-ruby'
require 'figaro'
class TwilioController < ApplicationController
def voice
account_sid = ENV["TWILIO_ACCOUNT_SID"]
auth_token = ENV["TWILIO_AUTH_TOKEN"]
#client = Twilio::REST::Client.new account_sid, auth_token
message = #client.account.sms.messages.create(:body => "Hello",
:to => "+12345678",
:from => "+12345678")
puts message.sid
end
end
And in config/application.yml I have:
# TWILIO_ACCOUNT_SID: 1234567890
# TWILIO_AUTH_TOKEN: 1234567890
The program works as intended when I replace the env variables with actual values, so the most I'm able to grasp at the moment is that something is preventing those variables from being set.
I just upgraded from Rails 4.2 to Rails 5 and started getting this error.
For me, it was because a newer HAML version was no longer tolerant of a pre-existing empty javascript block.
:javascript
I deleted the ':javascript' line and everything was fine afterwards.
move constants to development.rb
and then just:
account_sid = TWILIO_ACCOUNT_SID

Cucumber/Capybara/Email Spec not working correctly with Selenium Driver

I've running some Cucumber/Capybara tests. I've been using the email_spec gem to check email stuff. Some steps where of the kind 'And "someone#email.com" should receive an email'. They give no problem when I run the test using the rack_test driver. However, they fail when using the selenium driver:
And "someone#email.com" should receive an email
expected: 1
got: 0 (using ==) (RSpec::Expectations::ExpectationNotMetError)
Can you help me? Thanks
You have to put your emails into a file because by default rails in test environemnt saves them in static variable that cannot be accessed from test thread. If you're using rails3 set delivery method to :file in cucumber environment. If you're on rails 2.x put this into your cucumber initializer:
ActionMailer::Base.class_eval do
DELIVERIES_CACHE_PATH =
File.join(RAILS_ROOT,'tmp','cache',"action_mailer_cache_deliveries {ENV['TEST_ENV_NUMBER']}.cache")
def perform_delivery_cache(mail)
deliveries = File.open(DELIVERIES_CACHE_PATH, 'r') do |f|
Marshal.load(f)
end
deliveries << mail
File.open(DELIVERIES_CACHE_PATH,'w') { |f| Marshal.dump(deliveries, f) }
end
def self.cached_deliveries
File.open(DELIVERIES_CACHE_PATH,'r') { |f| Marshal.load(f) }
end
end
config.action_mailer.delivery_method = :cache

Email spec doesn't match body content in Rails

I'm using email_spec gem to test a simple email, but for some reason the body content appears to be empty:
1) ContactMailer welcome email to new user renders the body
Failure/Error: mail.should have_body_text("Hi")
expected the body to contain "Hi" but was ""
# ./spec/mailers/contact_mailer_spec.rb:17:in `block (3 levels) in <top (required)>'
Every other example passes. The template file is called welcome_email.text.erb. Not sure why body is not matched, but the email does have a body when it gets sent.
Edit: the Rspec code is:
let(:mail) { ContactMailer.welcome_email(email) }
it "renders the body" do
mail.should have_body_text("Hi")
end
The best way I've found to do this is:
it "contains a greeting" do
mail.html_part.body.should match /Hi/
end
You can also use text_part in place of html_part if you want to check the plain text part of a multipart message.
Also note that others may recommend using #encoded, but I had trouble using that with long URLs, as they may get line-wrapped during the encoding process.
So, I was experiencing the same thing. I was trying to test my mailers without loading all of Rails.
What finally solved my problem was adding this to my test:
(note that my test is in test/unit/mailers/my_mailer_test.rb - you may have to adjust paths)
ActionMailer::Base.delivery_method = :test
ActionMailer::Base.view_paths = File.expand_path('../../../../app/views', __FILE__)
Basically, without the view paths pointing to your views directory, the template is not found and all the parts (html, text, etc) are blank.
NOTE: The directory specified is NOT the one the actual templates are in. The mailer knows to look for a directory in the template root named after the class itself.
Here's a sample in minitest/spec
require 'minitest/spec'
require 'minitest/autorun'
require "minitest-matchers"
require 'action_mailer'
require "email_spec"
# NECESSARY TO RECOGNIZE HAML TEMPLATES
unless Object.const_defined? 'Rails'
require 'active_support/string_inquirer'
class Rails
def self.env
ActiveSupport::StringInquirer.new(ENV['RAILS_ENV'] || 'test')
end
end
require 'haml/util'
require "haml/template"
end
# END HAML SUPPORT STUFF
require File.expand_path('../../../../app/mailers/my_mailer', __FILE__)
ActionMailer::Base.delivery_method = :test
ActionMailer::Base.view_paths = File.expand_path('../../../../app/views', __FILE__)
describe MyMailer do
include EmailSpec::Helpers
include EmailSpec::Matchers
let(:the_email){ MyMailer.some_mail() }
it "has the right bit of text" do
the_email.must have_body_text("some bit of text")
end
end

ActiveSupport overrides #to_json behavior

How to teach ActiveSupport to not override standard "json" gem behavior?
require "rubygems"
gem "json"
require "json"
class Time
def to_json(options = nil)
"custom string"
end
end
hash = { :x => Time.now }
puts hash.to_json # => {"x":custom string}
gem "activesupport"
require "active_support/core_ext/object" # Somewhere into Rails internals
puts Time.now.to_json # => custom string
puts hash.to_json # => {"x":"2011-02-14T16:30:10+05:00"}
Expected: after require "active_support/core_ext/object" I wanna get {"x":custom string} result.
Rails since v2.3.3 switched to #as_json due to some significant reasons. So dance with it.
http://weblog.rubyonrails.org/2009/7/20/rails-2-3-3-touching-faster-json-bug-fixes
You have to define
class Time
def to_json(options = nil)
"custom string"
end
end
after
gem "activesupport"
require "active_support/core_ext/object"
code.
How about formatting your Time.now value with strftime like Time.now.strftime("format") for the formatting string please see the Ruby Docs.
Or if you don't really want to format it, just use it as a string call Time.now.to_s

Resources