ActionMailer- access Mail::Message content, or manually initialize view in controller? - ruby-on-rails

I've got a form where an internal user can request that informational materials be sent to a client. When the form is submitted, it sends an email to the person in charge of physically mailing the materials to the client.
Now, I want to capture the content of the email so I can add a note to the client's show page, and I'm unsure how to go about it.
One option I've looked at is to use an after_filter in the MaterialsRequestMailer, but calling message.body returns a large string with way more text than I need and I want to avoid adding a bunch of parsing logic to get the message content.
Basically, I want what is generated by the views/materials_request_mailer/send_request_notification.text.erb template. I've looked through http://www.rubydoc.info/github/mikel/mail/Mail/Message and can't find a method to return just the rendered template content. Is there a way to do that?
If not, is there a way to manually initialize a View in the controller, where I already have the instance variables I'm passing to the mailer? That doesn't seem to be an ideal solution, because I'm using DelayedJob, and the code for adding the note would be run before the email is actually sent. Also, due to DelayedJob, it appears that I can't directly access the mail object from within the controller (if I do mail = MaterialsRequestMailer.delay.send_request_notification(...) it assigns an instance of Delayed::Backend::ActiveRecord::Job to mail).

Found a solution that works- message.text_part.body.raw_source is what I was looking for. (credit to this answer: https://stackoverflow.com/a/15257098/2599738)
class MaterialsRequestMailer < ActionMailer::Base
include AbstractController::Callbacks
after_filter :add_note_to_client
def send_request_notification(client, ...)
#client = client
...
end
def add_note_to_client
mail_text = message.text_part.body.raw_source
#client.add_account_note(mail_text)
end
end

Related

How to create a form to trigger model method

I would like to know how to create a HTML form to send data to rails application so that specific model method guess gets triggered.
I managed to get this work in rails console, however even using HTML form guides from https://guides.rubyonrails.org/form_helpers.html I can't seem to get my head around this concept.
to generate the basic setup I used:
rails g scaffold Riddle content image_url
my model file:
models/riddle.rb
class Riddle < ApplicationRecord
def guess(guess)
content == guess
end
end
If the guess is correct(equal to content of the current object) user should see "correct guess" on the HTML page.
A form upon submit can hit a controller action. So you could do something like this:
Have a view which has the form has a form which makes a GET/POST request to an action in RiddlesController; lets call it validate_guess passing the required parameters id of the riddle and guess which is the guess that is a user input.
app/controllers/riddles_controller.rb
class RiddlesController < ApplicationController
def validate_guess
riddle = Riddle.find param[:id]
guessed_correctly = riddle.guess(params[:guess])
if guessed_correctly
render plain: "correct guess"
else
render plain: "incorrect guess"
end
end
You would also have to declare a route for this in config/routes.rb. As this works on a single Riddle object; it would be a member_action for the Riddle resource.
resources :riddles do
member do
post :validate_guess
end
end
Controller - Its the controller that renders the view and a view can only interact with the controller. Views dont directly interact with a model, and the additional controller layer does this, which provides us with a lot of structure, i.e. we can keep all data related logic in the model(though it tends to creep out of these sometimes :)). Deal with requests generation/handling at the View/Controller level and contact model to get/set info related to the business objects.
For example - your guess method is a good example. It checks whether a given value is a correct guess for a given Riddle object and returns a boolean as result(by comparing it to some attribute in the Riddle object). Going forward one could also add authentication in the controller layer, add caching or add more presentation logic. Let the model do its job of just telling the controller if the guess is correct or not for a riddle. The controller and view then deal with how to show the user that result. (e.g. Internationalize the text, style the text shown in the view or return the response in different formats like JSON/XML/HTML or maybe on a correct guess inform some other 3rd party service that the guess is correct)
Read more about the MVC pattern here and some things it enables us to achieve here

In rails 4.2, how to display a form for preview but ensure it cannot be submitted

I'd like to have a a form view that can, depending on circumstances, have submit functionality disabled in a bullet-proof way so that even a clever user could not edit the HTML source (via a browser extension) to re-add the submit button.
It seems one way to do that might be to somehow inject an invalid authenticity token that replaces the (valid) rails-generated one, so that even if a user somehow re-adds the submit button (by editing the HTML via a browser extension) it would still be an invalid submission.
My thought is to have some logic in the view:
- if #form_disabled # set by controller
- somehow_invalidate_the_authenticity_token?
How might one 'break' Rails form submission?
The purpose of doing this, instead of rendering the preview in a :show action, is to have the exact same view displaying both the live-form and the dead-form.
If I were you, I would use pundit.
It's pretty simple, and has few lines of code if you need to know how it works.
I'd start to write the code here, but I realize that the example at the readme fit your needs.
At the application controller add this
At the folder app/policies put the class PostPolicy, of course, you must replace "Post" with the name of your controller in singular (even if you have not a model with that name). The update? (and create?) actions should return true/false to indicate if user is allowed or not.
A few lines down on the readme, you will find the PostsController#update action, which call to authorize with the record before the update. I think you want do the same with create (then you need a create? method at the policy class).
Pundit needs current_user controller method, if you don't have it. Just follow the user customization instructions.
Of course, new and edit actions don't call authorize because they are allowed to everybody. Only the POST & the PUT/PATCH actions are forbidden.
Yes, it's more than a surgery of one line of code. But it's simple and the right way of give access to users.
After reading my other answer, I start thinking that you can do the same that Pundit does at the controller:
def update
if <unauthorized user>
flash[:alert] = "You are not authorized to perform this action."
redirect_to(request.referrer || root_path)
else
# all the update stuff
# ...
end
end

What's the absolute path to current request object in Rails environment?

I'm searching for the current request of class Rack::Request to find the params. Suppose I've spawned a debugger in my model, I don't want to send a new request, but still find my params.
I couldn't find any class attributes, that would store current request, which is reasonable.
I don't know how to find any instances of ApplicationController or Rack::Server, which might contain the info.
Also, peaking into the log is considered too much effort, so I'd like the effort to be concentrated on finding the request object, not telling me to grep/search through log.
In hopes of being able to be lazy,
Love Dzhon.
It's possible I'm misunderstanding your question, but from within a controller you can simply access a request object to get its details, and params to get the params.
ItemController
def show
#page_variable = request.inspect + params.inspect
end
end
If you want to make the request object available to your models you can create a class accessor and store it at the beginning of any action (via a before_filter in the application controller) for example. More details why here.

where to put utility code used by a controller and a mailer?

Our app has "notifications" which you view through your inbox on the site, and can also get email to tell you about them. When you receive a notification, it contains a link to reply to the message in question. That might mean sending a PM back to the sender of the original message, or might mean leaving a comment on a post.
This is the code to figure out what reply link to use:
if #notification.post
# comment on the post in question
#reply_link = new_comment_path(:post_id => #notification.post.id)
else
# by default, reply link sends a PM in return
#reply_link = new_notification_path(
:recipient_id => #notification.sender.id,
:subject => #notification.subject =~ /^Re: / ?
#notification.subject :
"Re: " + #notification.subject
)
end
I took that from our controller code, btw: app/controllers/notifications_controller.rb
Now we want to include the same reply link in our email notifications, which means we need to do the same sort of reply link generation in app/mailers/notifier.rb
I don't want to repeat myself, so I would rather create a reply_link method and put it somewhere where both the controller and the mailer can access it.
My first thought was to put it in the model, so that we could have Notification.reply_link. That'd be nice, but it doesn't work because we need new_comment_path and new_notification_path which aren't available in the model.
My second thought was to use a helper, but a) everyone seems to think that helpers suck, and b) we couldn't get it to work anyway.
So, where should I be putting this handy reply_link method, so that it will be accessible to both the controller and the mailer, and in keeping with good coding practices?
Extract it to a module and 'mix it in' to access it in both places you need it.
You can put the module anywhere you like: the lib folder has been a historically popular place, or create a 'modules' folder in your app directory.
Put it in /app/helpers or /lib
I generally tend to put methods accessed from controllers in helpers

rails 3.1: Where to filter out email addresses that have opted out

Trying to figure out the cleanest way prevent sending email to users who have opted out from receiving them in rails 3.1.
I was thinking about overriding Mail.deliver to check the db and determine if the recipients are unsubscribed or not, then conditionally delivering the email.
That seems like the least intrusive way to go about it, but requires creating the Mail objects that are never going to be sent.
Seems like the most resource conscious way would be to do the check in the controller, thus preventing the Mail objects that are never going to be sent from the burden of existence.
This though seems more intrusive and prone to developers forgetting to make the check when creating new mailers.
Is there a standard practice for this situation?
** edit **
This is for managing a collection of users who have opted out of receiving notifications, rather than something like managing subscriptions to a news letter.
If the attribute that determines whether or not to get email notifications is just a field on a model in the DB, you could create a named scope called something like 'want_email_notifications' to get all the users that have subscribed.
So, if you have a User class, and that class has an attribute called opt_out, then you could do something like:
class User < ActiveRecord::Base
named_scope :want_email_notifications, :conditions => ['opt_out = ?', false]
...
end
Then, to call it, you do User.want_email_notifications, which gives you an array of all User objects that want email notifications.
Then, when you're checking whether or not a given user should receive an email notification, write a condition similar to:
send_email_notification(user_in_question) if User.want_email_notifications.include?(user_in_question)
In this example, send_email_notification is the method where you would call the associated delivery method, which actually sends the email.

Resources