Ruby on Rails: Observers and flash[:notice] messages? - ruby-on-rails

I'm trying send flash messages and welcome notices to users if it's their first time commenting; basically, something like this:
class CommentObserver < ActiveRecord::Observer
def after_save(comment)
if comment.user.new?
Mailer.deliver_welcome_package(comment)
flash[:notice] = "Welcome! We just delivered a welcome package to your email"
end
end
end
I'm not sure how I should display that flash message for users after they create their first comment. Should I put that flash message in the controller (with an additional "if comment.user.new?") or is there a way to display the flash message more efficiently?

Putting the flash message in the method seems fine to me.
I usually have a helper method in my application_helper file that checks flash and diplays.
def show_flash
[:notice, :error, :warning].collect do |key|
content_tag(:div, flash[key], :id => key, :class => "flash flash_#{key}") unless flash[key].blank?
end.join
end
I have three types of messages, notice, warning, and error, this checks to see if any of them are set, if so it prints them out, if not then nothing is printed.
In my layout i then have..
<% show_flash %>

Firstly, why are you observing comments? If you want to react to a new user, why don't you observe users?
To answer your question, I would definitely put the flash assignment in your controller, the reason being that the flash is a view level concern.
I used to use observers for sending mails, but have more recently just sent them in the controller. In this case, doing that would make your life a little simpler.

Related

Rails Flash doesn't behave as what the Rails Guide says

According to the official flash section on Rails Guide:
The Flash
The flash is a special part of the session which is cleared with each request. This means that values stored there will only be available in the next request, which is useful for passing error messages etc.
...
flash.now
By default, adding values to the flash will make them available to the next request, but sometimes you may want to access those values in the same request.
However, after doing some test, I found that flash is available in the current & next request. And flash.now is only available in the current request.
Is the Rails Guide incorrect? Or maybe I miss something?
# Controller action
def test_flash
flash[:notice] = "This is a flash msg."
end
# test_flash.html.erb
<% flash.each do |name, msg| -%>
<%= content_tag :div, msg, class: name %>
<% end -%>
I won't say the document is incorrect, but I agree it could be more clear.
It should be phrased something like:
Flash is clear after next request, therefore it's available in the current
request and next request. If you only want it be available in the current
request, not along with the next request, use flash.now.
On the other hand, don't you think something is not available in the current
request, given current flash API, as a hash like object, it would be quite
weird? For example:
flash[:notice] = 'nnf'
puts flash[:notice] # => nil
It would make more sense with write and read if that's the case.
You have read it incorrectly. flash.now is only available to the current request. flash (without now) is the only one that will be available to the next request.
The sentence you read above - is actually talking about plain flash (to begin with), and only then introduces flash.now as a contrast at the end of the paragraph.

Email compose view in 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

Flash messages in Rails getting carried over to the next page

I am displaying error and notice messages in my application with a helper method as shown below.
def display_flash_content
[:error, :notice].map do |key|
collection = flash[key].blank? ? [] : (flash[key].respond_to?(:map) ? flash[key] : [flash[key]])
collection.map {|item| content_tag(:div, item, :class => key.to_s) }
end
end
and my layout has this
<%= display_flash_content %>
I need to display these messages when I do some operation and then redirect to a specific page (this is working fine). But my problem is this flash message persists between pages. It's coming twice between pages where it needs to be cleared immediately once it's displayed to the user.
How to handle this scenario. Please help me!
The way you are displaying the flash messages is fine. I think the problem is how you are setting them. If you are setting flash messages and not redirecting you can assign to flash.now[:notice] instead of flash[:notice], for example, and your message will not hang around after the redirect.

What approach do you take for embedding links in flash messages?

The ability to have flash messages (notice, error, warning, etc) with embedded links is nice from a user interaction standpoint. However, embedding an anchor tag inside a flash message from the controller is dirty.
Let's assume that a flash message like this is good for usability*:
Example Flash Message Notice with an Embedded Link http://img.skitch.com/20090826-xbsa4tb3sjq4fig9nmatakthx3.png
(borrowed from DailyMile.com)
What tactic would you take between the controller and view to employ something like this cleanly?
Just thought I would share this, since found answer I was looking for elsewhere:
Works on rails 3.1
flash[:notice] = "Proceed to #{view_context.link_to('login page', login_path)}".html_safe
Glenn Gillen has an approach that he calls Useful Flash Messages in Rails.
I modified his code snippets to be a little more idiomatic (for me at least).
The controller fills the flash like this:
flash[:notice] = "Your profile was updated. %s"
flash[:notice_item] = ["Edit again?", edit_profile_path(#profile)]
You then might have helpers that look something like this:
def render_flash_messages(*keys)
messages = keys.collect do |key|
content_tag(:p, flash_message_with_item(key), :class => "flash #{key}") if flash[key]
end.join
content_tag(:div, messages, :id => "flash_messages") unless messages.blank?
end
def flash_message_with_item(key)
item = flash["#{key}_item".to_sym]
substitution = item.is_a?(Array) ? link_to(*item) : item
flash[key] % substitution
end
The view looks simply like this:
<%= render_flash_messages(:error, :notice, :warning) %>
The view (via the flash_message_with_item helper) is responsible for creating the anchor tag, but the controller manages what goes into the flash message including an optional resource for further action.
You could create a helper method to render out partials based on the value passed back in the flash message.

Sending messages between users

I am building an application and it needs to have a feature whereby one user can send another user a message. The system can be the most basic type available in rails and that would suit me fine.
Any ideas on how to go about this?
Thanks.
Table structure like this:
Users
name,pwd
Messages
title,body
UserMessages
user_id,message_id
Why don't you use acts_as_messageable plugin:http://www.philsergi.com/2007/10/actsasmessageable-plugin-released_04.html ?
Similarly, there are other plug-ins for authentication (restful authentication).
So i have implemented the DB tables and now need to pass the data around my system which im finding quite troubling. When the user clicks "send message to" on my form i need it to carry the id of the profile which the user is viewing. I thought this would do that:
<%= link_to "Message", :action => 'message', :id => #user.id %>
Now this pass the persons ID who i was looking to the message action (i know #user.id should work because i use #user.detail to view other details about the user on that page)
My controller should then receive that #user.id, heres my controller:
def message
#reciever = User.find_by_id(params[:id])
end
and in my view for i want to show the recievers id so i thought that
<label>Send Message To: <%= render :text => #reciever.id %></label>
would be suffiencent.
Any ideas?

Resources