Email compose view in rails - ruby-on-rails

I have created and (hopefully set up) a mailer.
Instead of sending templates, I would like to email the content of a form textarea in a view.
I need a view to edit the message, which should be sent to the controller which in turn calls the send_mail method in my mailer.
Class Notifier < ActionMailer::Base
default from: "from#example.com"
def send_email( email, subject, body )
mail(
:to => email,
:subject => subject
) do |format|
format.text { render :text => body }
end
end
end
This is my view:
<%= form_for(:post, :url => {:action => 'send'}) do |f| %>
<%= f.text_field(:title, class: 'form-control')%>
<%= f.text_area(:content, rows: 15)%>
<%= f.button "Submit", type: 'submit' %>
<% end %>
The problem is that when generating a mailer with rails g mailer Notifier
I get a notifier.rb in mailers and a notifier folder in views. However I have no view controller for the notifier views.
Question: How do I make the link between my view and sending the input text as email?

You need to create a controller which handles your view, and in that controller you need to call the mailer somewhat like this: (you'll need to change the names of your form fields to match the params in the call or vice versa)
Notifier::send_email( params[:email], params[:subject], params[:body]).deliver
I'd recommend to check out these RailsCasts:
http://railscasts.com/episodes/61-sending-email-revised
http://railscasts.com/episodes/61-sending-email
http://railscasts.com/episodes/206-action-mailer-in-rails-3

This might be a good place to make a non-ActiveRecord model. I understand that right now a problem is solved and this is a bit beyond the scope, but it's useful, so why not?
I suggest you look at pattern 3 in this article and build a form model (Notification?) that encapsulates the process of storing form contents, validating them and sending the actual email. Note that the implementations in the article are pretty much out of date, Rails 4 introduced ActiveModel::Model that facilitates the process.
Pros:
Another class defined mostly in declarative style, easy to read and find
Can be easily and cleanly laid out by SimpleForm or Rails' form helpers
Gets all the benefits of a traditional Rails model, like validations (and errors if they fail)
Semantic, code looks consistent with the rest of the app working with DB or whatever
Cons:
Another class, can be considered overengineering
More code, some more work, ease of maintenance is arguable
Another object to create in controllers that render this form
Once it's done, the process of making it work is pretty much the same as making any other resource to work. And I assume, that this mailer sits on its separate page.
Routes:
resource :notification, only: [:create] do
get :new, path: "" # A matter of taste, really
# You may use the default `new` route
# with a path `notifications/new`
end
Controller:
class NotificationsController
def new
#notification = Notification.new
end
def create
#notification = Notification.new(notification_params)
if #notification.send
# success! redirect somewhere?
else
render :new # render the form again with the errors
end
end
private
def notification_params
params.require(:notification).permit(:email, :subject, :body)
end
end
You will also need a view for the new action that renders the #notification into a form. Only new, create doesn't need its own. And now for the fun part, model:
class Notification # Yep, it inherits nothing!
include ActiveModel::Model
attr_reader :email, :subject, :body
validates :email,
presence: true # You might want to validate its format?
validates :subject,
presence: true, length: {in: 0..100} # Too long subjects are annoying
validates :body,
presence: true
def persisted?
false # I have no idea why, but it's defined in the article, no harm done
# I'd love to hear the explaination about this though
end
def send
if valid? # no objections from validations?
# Alright, send it already!
Notifier.send_mail(email, subject, body).deliver
# thanks for this line go to #Daniel and his answer
true
else
false
end
end
end
And, finally, a pro tip: Rails 4.2 (bleeding edge right now!) introduced ActiveJob, that's integrated with mailers. By replacing a call to deliver method with a call to deliver_later you will enqueue the email for sending by the background task processor as described here (edge guides, subject to change quite soon). I don't really think it's about time to use it everywhere (too new), but consider this for future projects.
Do I really think it's good? Yeah, I really do, I've refactored a user password changer to look this way, the code has become easier to navigate and look at.

So think of your notifier.rb as a controller. In which you defined the #send_mail. This means that in views/notifier you should add a send_mail.html.haml (erb/slim... matter of taste) which will be the body of the mail.
Now from the controller that receives your form you just need to call
Notifier.send_mail(email, subject, body).deliver

Related

Post controller require post

In an article, a Post controller is created.
But in post_params, params.require requires :post. How this possible or What does it mean that Post controller requires :post
def create
Post.create(post_params)
redirect_to root_path
end
private
def post_params
params.require(:post).permit(:description, :image, :user_id)
end
it's a way Rails Controlling mass-assigment attributes with strong_parameters. this approach forbids mass-assignment of a model attribute from a controller unless that attribute was white-listed
whitelisting is configured using two methos
require, calling require will validate that the parameter is actually present, if not present it will throw an error
permit, calling permit with list of attributes will enable those attributes to "pass through" to model during mass-assignment (this restriction disable code injection by passing unpermitted objects, hashes etc)
from your sample above
params.require(:post).permit(:description, :image)
meaning an instance of post must exist in params and keys/column that allowed to pass through only description and image, if your form passing other field for example :other_description and it's not listed in permit, it will not be saved
The class name :post is required because the controller expects that the parameters are arranged hierarchically under their class names, as one single POST request can yield parameters pertaining to many classes. For example your post description will be available as
params[:post][:description]
In development, it's sometimes useful to inspect the parameters in the browser window. You could use this simple code:
<% if !Rails.env.production? %>
<%= debug(params) %>
<% end %>

Instance variables are purged when user fails his authentication

I have trouble getting instances variables while overriding Devise::SessionsController.
When an unauthenticated user add an item to its cart, I want to redirect him to the sign in page and apply the addition after he's authenticated.
1 . I pass the item_id as a URL parameters:
localhost:3000/user_accounts/sign_in?item_id=42
I get back in a hidden field in the form, I can have it through the session creation form:
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<%= f.hidden_field :item_id, value: params[:item_id] || #item_id %>
...
<% end %>
When Rails starts his form flow, the URL parameters are removed:
Before:
localhost:3000/user_accounts/sign_in?item_id=42
After:
localhost:3000/user_accounts/sign_in
I don't want to loose the item_id when the user fails his authentication, so I ensure #item_id is exposed as an instance variable, so I can inject it back to the form.
However, on failure, Rails(?) / Devise(?) / Warden(?) seems to purge the instance variables (for security reasons?). As a result, #item_id is nil.
Note that works well when the user successfully authenticate (through URL parameters). Also, I use this very same technique in the registration process and it works in both success (URL parameters) and failure (instances variables) case.
Here is my implementation of SessionsController:
class MySessionsController < Devise::SessionsController
def create
#item_id = parse_id(params[:user_account][:item])
super do |account|
add_to_cart(account, #item_id) if #item_id
end
flash.delete(:notice)
end
end
Do you know how I can fix this ?
I am open to alternatives as well.
I was wrong. Rails, Devise and Warden aren't doing shady things to my instance variables. I found out that a redirection was made, explaining why the context was lost.
In the end, I used cookies to fix the problem as #nattfodd suggested it. This was trivial I don't know why I didn't think of it.
class MySessionsController < Devise::SessionsController
def create
item_id = parse_id(cookies.encrypted[:item])
super do |account|
add_to_cart(account, item_id) if item_id
cookies.encrypted[:item] = ""
end
flash.delete(:notice)
end
end
Cleaner, no hack. Good.

Rails3 Admin UI frameworks

We will soon be rewriting a 5 year old rails app, with a very unsound code foundation, from scratch in a brand new Rails 3 app with all the new hotness. The current app has a substantial custom admin UI backend which depends on now admin frameworks at all. Just some base controller classes and some somewhat useful CSS conventions. But maintaining that UI is a lot of work, especially if we want it to look half way nice.
So I'm in the market for an Admin UI framework that will make the simple stuff trivial, but without getting the way of more complex customization in both form and function.
The top contender, ActiveAdmin, seems to be very popular, and after playing with it a bit I have some concerns. It seems to declare a whole unique DSL that exists in a single ruby file. This is kind of neat, but it's also a completely different than how most other Rails apps are architected. It abstracts away the view, the helper the controller, and gives you a pure ruby DSL. It seems to me that this would get in the way of doing tricky things, more advanced custom things in our admin views. DSL's are great, until you want to do something they don't explicitly support.
Example "Resource" from my experimentation, with no controller and no view.
ActiveAdmin.register Region do
menu parent: "Wines"
show title: :name
index do
column(:zone) { |o| link_to o.zone, admin_region_path(o) }
column(:name) { |o| link_to o.name, admin_region_path(o) }
default_actions
end
end
So, the questions:
Is not being based on the standard Rails MVC architecture in separate files, and the typical controller inheritance based implementation of an admin area, actually something I should be concerned about? Will it hamper extensibility in the long term?
Is the DSL in ActiveAdmin better and more flexible than I'm giving it credit for?
Should I be looking at some other framework that lends itself better to my goals of high customization?
Should I stop being lazy and roll my own?
Does the choice of Mongoid instead of MySQL as a database affect any of the above questions?
It is worth mentioning that in ActiveAdmin you can also create your own complex forms by using partials
form partial: 'my_form'
and expand controller functions with a controller block.
controller do
def edit
end
def index
end
end
+1 for active admin, I use it in many projects, including a cms I'm building. It indeed is more flexible than many people who are newer with it give it credit for, at the end of the day you can always do:
controller do
def custom_action
Puts "hi"
end
end
(Think that's the right syntax writing from phone so all this is off top of head)
Also, I swear by inherited resources, which active admin controllers extend, as they really force you (in a good way) into writing restful, reuse able code. Bottom line is I believe active admin is leaps and bounds ahead of the others I've tried (railsadmin and at least one other)
Update:
Sure, here is the inherited_resources documentation
https://github.com/josevalim/inherited_resources
And here is an example of modifying the controller directly, from my little CMS project.
ActiveAdmin.register Channel do
index do
column :title
column :slug
column "Fields" do |channel|
"#{link_to('View Fields', admin_channel_entry_fields_path(channel))} #{link_to 'Add Field', new_admin_channel_entry_field_path(channel)}".html_safe
end
column "Actions" do |channel|
"#{link_to('View Entries', admin_channel_entries_path(channel))} #{link_to 'Create Entry', new_admin_channel_entry_path(channel)}".html_safe
end
default_actions
end
form :html => { :enctype => "multipart/form-data" } do |f|
f.inputs "Channel" do
f.input :title
f.input :display_name
f.input :image
end
f.buttons
end
controller do
def begin_of_association_chain
current_site
end
def tags
query = params[:q]
if query[-1,1] == " "
query = query.gsub(" ", "")
ActsAsTaggableOn::Tag.find_or_create_by_name(query)
end
#tags = ActsAsTaggableOn::Tag.all
#tags = #tags.select { |v| v.name =~ /#{query}/i }
respond_to do |format|
format.json { render :json => #tags.collect{|t| {:id => t.name, :name => t.name }}}
end
end
end
end
Basically, I am using the inherited resources, begin_of_association_chain method (one of my favorite things about IR), to scope all the data within channels, or any of the admin resources that inherit from my channels resource, to the current site, without having a url like /admin/sites/1/channels -- Because I am already setting current_site based on the url the visitor enters with. -- Anyways, basically once you are inside:
controller do
puts self.inspect
end
Returns the actual controller itself, e.g. Admin::ChannelsController (which < InheritedResources::Base, maybe not directly but all the IH controller methods should be available at this point).

What is the right way to embed image into email using Rails?

What is the right way to embed an image into email using Rails?
I combined the answer from Oksana with a custom helper approach and got the following to work quite nicely.
app/helpers/email_helper.rb
module EmailHelper
def email_image_tag(image, **options)
attachments[image] = File.read(Rails.root.join("app/assets/images/#{image}"))
image_tag attachments[image].url, **options
end
end
app/mailers/base_mailer.rb
class BaseMailer < ActionMailer::Base
helper(EmailHelper)
end
app/mailers/my_mailer.rb
class MyMailer < BaseMailer
def send_my_mail(email)
mail to: email, subject: "My Subject"
end
end
Then for example where I want to attach the company logo in my email layout file I would use
app/views/layouts/email.html.erb
<%= email_image_tag("company_logo.png") %>
Note the **options makes the tag more extensible but it will only work in ruby >=2. To make this work in ruby < 2 you will have to use the older way of handling key word options.
Update 03/25/2022: Rails 6 no longer supports add_template_helper, and now replaces it with helper, as explained by this answer that links to the commit that did so.
RAILS 5
In your mail method add your inline attachment pointing to your image:
class ConfirmationMailer < ActionMailer::Base
def confirmation_email
attachments.inline["logo.png"] = File.read("#{Rails.root}/app/assets/images/logo.png")
mail(to: email, subject: 'test subject')
end
end
Then in your mail html view an image_tag with the attachment url:
<%= image_tag(attachments['logo.png'].url) %>
Adding onto Oksana and tdubs' answers
The module tdubs wrote works on desktop, but for the mobile gmail client, the images appeared as attachments. To fix this, do this for the
app/helpers/email_helper.rb
module EmailHelper
def email_image_tag(image, **options)
attachments[image] = {
:data => File.read(Rails.root.join("app/assets/images/emails/#{image}")),
:mime_type => "image/png",
:encoding => "base64"
}
image_tag attachments[image].url, **options
end
end
For the rest, follow tdubs's answer.
After a lot of research i have found very cleaner way to embed image in email.
Just add following line in production.rb and development.rb
config.action_mailer.asset_host = 'YOUR HOST URL'
In your view embed image by using following code.
<%= image_tag('My Web Site Logo.png') %>
Note: Make sure to update YOUR HOST URL and My Web Site Logo.png in
above code.
For basic details of usage of Action Mailer, please refer to ActionMailer::Base.
Copy pasted from here
http://api.rubyonrails.org/classes/ActionMailer/Base.html#class-ActionMailer::Base-label-Inline+Attachments
Inline Attachments
You can also specify that a file should be displayed inline with other HTML. This is useful if you want to display a corporate logo or a photo.
class Notifier < ApplicationMailer
def welcome(recipient)
attachments.inline['photo.png'] = File.read('path/to/photo.png')
mail(to: recipient, subject: "Here is what we look like")
end
end
And then to reference the image in the view, you create a welcome.html.erb file and make a call to image_tag passing in the attachment you want to display and then call url on the attachment to get the relative content id path for the image source:
<h1>Please Don't Cringe</h1>
<%= image_tag attachments['photo.png'].url -%>
As we are using Action View's image_tag method, you can pass in any other options you want:
<h1>Please Don't Cringe</h1>
<%= image_tag attachments['photo.png'].url, alt: 'Our Photo', class: 'photo' -%>
In rails 6, if you use the solution of #Tyron, you'll need to replace add_template_helper with helper in the BaseMailer. So, it becomes:
class BaseMailer < ActionMailer::Base
helper(EmailHelper)
end
I don't know much about rails, but I've worked on projects in C# that create emails and then insert them in a users inbox via the Google APIs. To create the email, I had to generate the email string from scratch. If you enable multipart for the email, then the image bytes will be included in a multipart section using base64 encoding.
You may want to check out the TMail and RubyMail packages to see if they support these actions for you.

Ruby on Rails: Confirmation Page for ActiveRecord Object Creation

Using Ruby on Rails I want a confirmation page before creating an ActiveRecord object. The user will see a preview of the item they are creating before submitting and the object being saved in the database
A common pattern;
User visits /entry/new
User enters details and clicks submit
User is redirected to /entry/confirm which displays the entry and clicks submit or edit to correct mistakes
Object is saved
How would you implement it?
Another option to solve this issue adding by a virtual confirmation attribute to your model. This way, there is no need to create a separate action for this:
class MyRecord < ActiveRecord::Base
attr_accessor :confirmation
validates_acceptance_of :confirmation, :on => :create
end
Now, your new object will not save correctly because the validation will fail on the confirmation field. You can detect this situation and present something like this:
<% form_for(#my_record) do |form| %>
...
<%= form.check_box :confirmation %> Really create this record.
<%= submit_tag('Confirm') %>
<% end %>
I would probably add a "preview" action to the routes.rb file for that model:
map.resource :objects, :new => { :preview => :post }
You would get to this preview action by POSTing the preview_object_url named route. You would need to essentially create the Object in the same way you would in your create action, like this:
def preview
#object = Object.new(params[:object])
end
This page would then POST to the create action, which would then create the Object. It's pretty straight forward.
http://api.rubyonrails.org/classes/ActionController/Resources.html
A few options
1- store the object you want to create in the session until you hit the confirm page, then just save it
2- pass around the object w/ each Post/submit from new -> details -> confirm
I would probably go with 2, since I am not prone to saving state with the session.
I'm not sure how to do this (RoR is new to me) but you could just specify the action for /new as /confirm, and then it calls create.
Right?

Resources