Conditional default from ActionMailer - ruby

I'm trying to figure out the logic so when a user selects a dropdown selection, it uses that email address as the default from. For example, if the user selects store1, it should send from test1#example.com, store2 should be test2#example.com, etc.
class ApplicationMailer < ActionMailer::Base
default from: "test1#example.com"
layout 'mailer'
end
This is the form the user uses to select a store:
<%= form_for #entry do |f| %>
<div class="form-group">
<%= f.select :store, Store.all.map { |store| [store.name] }, :selected => #entry.store %>
</div>
<% end %>
In the stores table, I have columns name and email_address.

You won't be able to use default to do this - default is for the case when you don't want to customize the email.
When you write the delivery method, accept entry or store as an argument and use it to pick the from address at runtime. It would look something like
class ApplicationMailer < ActionMailer::Base
layout 'mailer'
def entry_email(entry)
mail(from: entry.store.email_address)
end
end
And you would call it with
ApplicationMailer.entry_email(entry).deliver

Related

How to create nested model from partial Rails 6

Newbie Here!
I'm unsuccessfully trying to submit a form of a nested model.
Aim:
Verify a translation by submitting a boolean to a reviews model to associate whether a translation is verified or not, with translation entry and user model associated references.
# routes.rb
resources :entries do
resources :reviews
end
# entry.rb
belongs_to :user
has_one :review
accepts_nested_attributes_for :review
# user.rb
has_many :entries
has_many :reviews
# review.rb
belongs_to :user
belongs_to :entry
From entry index, pass the entry instance to partial, works perfect
# /entries/index.html.erb
<% #entries.each do |entry| %>
...
<%= render 'reviews/new', entry: entry %>
...
<% end %>
Unsuccessfully setting up new/create form.
What happens is that the entry instance is well received, but I am failing to create a new model instance for review belonging to entry.
entry.review raises an nil error for review, while entry is fine when testing through browser console
First argument in form cannot contain nil or be empty
# reviews/_new.html.erb
<span>
<%= form_for entry.review do |f| %>
<div class="form-check form-switch">
<%= f.check_box :verified, class: "form-check-input" %>
</div>
<%= f.submit class: "btn btn-primary"%>
<% end %>
</span>
Another attempt was also to use just #review from the controller but that doesn't obey nested routes.
My controller looks like this
# reviews_controller.rb
def create
#entry = Entry.find(params[:entry_id])
#review = #entry.review.build(review_params)
#review.user_id = current_user.id
#review.save
end
private
def review_params
params.require(:review).permit(:verified, user: current_user, entry: #entry)
end
Am I suppose to implement my actions in the entries_controller?
I have also found the tutorial here useful but replication was unsuccessful.
Another StackOverflow reference here
I still get the error entry.review.build that review is nil.
First argument in form cannot contain nil or be empty
When building an associated record over a has_one relation, instead of
#review = entry.review.build(review_params)
you need to use the following:
#review = entry.build_review(review_params)
See the documentation for more details.
Am I suppose to implement my actions in the entries_controller?
It depends on what you're after. If you have a dedicated form for adding a new review and it is not embedded in another form for creating or updating an entry then implementing a create action in ReviewsController is the straightforward solution – in this case you should also not need accepts_nested_attributes_for in Entry.
If, however, you want to be able to create or update an entry as well as its review using the same form, then you should nest the review form in the form of the entry, keep accepts_nested_attributes_for, and use actions in EntriesController. The documentation should get you started there.

Rails 4. Moving form information from one controller (with no model), to another (no model) in order to pass arguments to a method

I have a simple app where users will be able to purchase items. On item pages there will be an add to cart button. The carts-controller displays the items using REDIS relationship, it has no model. Once the user has reviewed the items and wishes to buy, they will be directed to purchases view where once they pay with stripe, an order will be created in purchases-controller. The purchases controller also has no model.
The user.rb model:
class User < ActiveRecord::Base
has_many :orders
def get_cart_items
cart_ids = REDIS.smembers "cart#{id}"
Item.find(cart_ids)
end
def purchase_cart_items!(recipient_name:, recipient_address:)
get_cart_items.each { |item| purchase!(item, recipient_name, recipient_address) }
REDIS.del "cart#{id}"
end
def purchase!(item, recipient_name, recipient_address)
self.orders.create!(user_email: self.email,
item_id: item.id,
recipient_name: recipient_name)
recipient_address: recipient_address)
end
end
The purchases-controller:
class PurchasesController < ApplicationController
def create
Stripe::Charge.create(
...
)
##########
current_user.purchase_cart_items!(recipient_name:, recipient_address:)
##########
end
end
The carts-controller:
class CartsController < ApplicationController
def create
REDIS.sadd current_user_cart, params[:item_id]
end
def destroy
REDIS.srem current_user_cart, params[:item_id]
end
end
Creating the Order attributes of user_email: and item_id: is easy by calling self.email and item.id since they are attached to a model, but I am unsure of how to use a form in the carts view to have the user input the recipient_name: and recipient_address: arguments for purchase_cart_items!(recipient_name:, recipient_address:) method in the purchases-controller.
I need to:
Create a form in the carts view where the user can input arguments for a method in another controller
Upon submission of the form details, I need the user to be redirected to purchases view
The form information has to be then passed as an argument to a method in the purchases-controller
Thanks!
A form_tag with a GET method needs to be used in order to submit the form info to the right controller.
In the carts view:
<%= form_tag({controller: "purchases", action: "new"}, method: "get") do %>
<%= label_tag 'recipient name: ' %>
<%= text_field_tag 'recipient_name' %>
<%= label_tag 'recipient address: ' %>
<%= text_field_tag 'recipient_address' %>
<%= submit_tag "PAY" %>
<% end %>
Once in the new action of the purchases-controller I used params to get the form info and session to make the form information available to the create action in the same controller:
class PurchasesController < ApplicationController
def new
#name = params[:name]
#address = params[:address]
session[:name] = #name
session[:address] = #address
end
def create
Stripe::Charge.create(
...
)
##########
#name = session[:name]
#address = session[:address]
current_user.purchase_cart_destinations!(name: #name, address: #address)
##########
end
end
Once the form information was accessible in the create action of purchases-controller, the keyword arguments for current_user.purchase_cart_items! were easy to set with instance variables of #name and #address.

How to create multiple 'new' pages within a controller that behave the same with different text?

Ok - I have a simple application built that allows you to store action items based on business categories: Business Admin, Business Development, Business Financial, Etc.
Each one of the categories, will have the same exact form:
<div align="center"><h1>What are your important Business Admin Action Items?</h1>
<%= form_for #category do |f| %>
<p>
<p>Store Answer Below:</p>
<%= f.text_field :name, :size => 40, :style => 'height: 40px' %>
</p>
<p>
<%=f.submit 'Save action item' %>
</p>
<% end %> </div>
However, not all of these categories will have the same H1 tag (or other text/attributes within the view). So, how does that work?
Do I create new pages, that are: business_admin.html.erb, business_development.html.erb etc - and if so, I can't see how I'd use the same new action for all of them, especially since the answers to the user input are going to be stored on separate pages (the categories' respective pages: business_admin_storage.html.erb, etc.
I understand that naming the urls in that fashion may not be the best way, nor a conventional way. That's part of my question as well. How should I approach this situation. Will be trying a whole host of stuff in the meantime :) If I need to be more clear, please tell me.
New to rails. Loving every second.
Categories controller
class CategoriesController < ApplicationController
def index
#categories = Category.all
end
def new
#category = Category.new
end
def create
#category = Category.new(category_params)
#category.save
end
def show
end
def destroy
end
private
def category_params
params.require(:category).permit(:answer)
end
end
Index View
<h1>Select A Business Category To Begin Identifying Action Items</h1>
<ol><li><%= link_to 'Business Admin', new_category_path %></li><br><br>
<li><%= link_to 'Business Development/Marketing', new_category_path %></li><br><br>
<li><%= link_to 'Financial', new_category_path %></li>
</ol>
<%= link_to 'Store random action items', new_facilitate_path %>
Routes.rb
Rails.application.routes.draw do
resources :facilitates
resources :categories
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".
# You can have the root of your site routed with "root"
root 'categories#index'
get 'show' => 'facilitates#show'
get 'index' => 'categories#index'
get 'business_admin' => 'categories#new'
SHOW
Stored !!
<h1>Listing stored action items for this category... </h1>
I can't see your model for Categories but it seems like you're varying the page served based on the name of the category?
Wouldn't something like this cover you?
<div align="center">
<h1>What are your important <%= #category.name %> Action Items?</h1>
</div>
You would need to pass the relevant #category to the relevant view via the controller, here is the Rails 4 way:
before_action :set_category, only: [:show, :edit, :update, :destroy]
...
def show
end
...
private
def set_category
#category = Category.find(params[:id])
end
And your Categories model would need to have a name property defined, which if you don't have it already could be accomplished by a migration.

Not showing created blog entries

I'm fairly new to Rails and learning to create a blog using this tutorial. On step 10, once I define create and show, after creating a new post in browser I don't see any entries on show with id page. All I see is heading and and blank title and post header.
Following is my controller -
class PostController < ApplicationController
def index
end
def new
end
def create
#post = Post.new(params[:posts])
#post.save
redirect_to #post
end
def show
#post = Post.find(params[:id])
end
end
Show view ---
<h1>Show a post</h1>
<p>
<strong>Title:</strong>
<%= #post.title %>
</p>
<p>
<strong>Text:</strong>
<%= #post.text %>
</p>
Route ---
RailsBlog::Application.routes.draw do
resources :post
root :to => "post#index"
end
Form ---
<%= form_for :post, url: {action: 'create'} do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit 'Submit' %>
</p>
<% end %>
May be this is just a spelling mistake, but since I've recently started learning Rails, I'm unable to resolve this.
Update: I can go to particular id using
http://localhost:3000/post/1
but am only seeing blank page with view headers
The problem is here:
#post = Post.new(params[:posts])
It should be params[:post] - singular, not plural.
Also note that the best practice with form_for is to pass an object instead of symbol:
form_for #post do |f|
Then:
You don't need to specify url
You can reuse the same form for an edit action or create action (if object creation failed due to failing validation)
This however requires to initialize new object in your new action:
def new
#post = Post.new
end
UPDATE:
Also your routes are incorrect. When defining plural resources, you need to use plural form (it's more the convention than requirement, but since you're learning stick with it). So change your routes to:
resources :posts
And rename your controller to PostsController (remember to rename file name as well). restart the server and all should work.
ANOTHER UPDATE:
You also need to rename folder app/views/post to app/view/posts.
AND YET ANOTHER UPDATE:
In rails 4, you are not allowed to mass assign any params which has not been whitelisted using strong parameters. You need to tell rails which fields you allow to be assigned first - this is a security thing. You need to make some changes to your controller:
class PostsController < ApplicationController
...
def create
#post = Post.new(post_params)
...
end
...
private
def post_params
params.require(:post).permit(:title, :text)
end
end
This is the way to tell your controller that you are expecting those attributes from your form and they can be safely assigned.
I had just similar problem on the same tutorial.
The code spelling was correct and clearly accorded to examples in tutorial and BroiSatse's answer above.
The mistake was in order of private method definition.
How it was:
...
private
def post_params
params.require(:post).permit(:title, :text)
end
def show
#post = Post.find(params[:id])
end
...
The working order:
def show
#post = Post.find(params[:id])
end
private
...
Anyway, this topic was rather helpful. Thak you for your answers!

Multiple Contact Forms - Rails 3

I am relative newbie to all this, so sorry if this sounds mad!
I have used this tutorial: http://www.railsmine.net/2010/03/rails-3-action-mailer-example.html
And I have a new contact form working great.
The controller is at app/controllers/support_controller.rb
class SupportsController < ApplicationController
def new
# id is required to deal with form
#support = Support.new(:id => 1)
end
def create
#support = Support.new(params[:support])
if #support.save
redirect_to('/', :notice => "Support was successfully sent.")
else
flash[:alert] = "You must fill all fields."
render 'new'
end
end
end
And the model at /app/models/support.rb
class Support
include ActiveModel::Validations
validates_presence_of :email, :sender_name, :support_type, :content
# to deal with form, you must have an id attribute
attr_accessor :id, :email, :sender_name, :support_type, :content
def initialize(attributes = {})
attributes.each do |key, value|
self.send("#{key}=", value)
end
#attributes = attributes
end
def read_attribute_for_validation(key)
#attributes[key]
end
def to_key
end
def save
if self.valid?
Notifier.support_notification(self).deliver!
return true
end
return false
end
end
The views however only work in views/supports/new.html.rb (rendered - views/supports/_form.html.erb)
So I can call the Model / Controller from localhost:3000/support/new but if I try and render the same form in another view from the root directory e.g. app/view/contact.html.erb I get:
undefined method `model_name' for NilClass:Class
I think this is because it is calling the support model away from the supports directory.
Do I have to create an instance on #support so it can be called? If so what is the best way of doing that? I think I am nearly there. I just want the contact form on multiple pages not just in suppport/new
Thanks
Charlie
Yes, you would need to create a #support variable in each action you wish to render your form.
Another option would be to refactor the form to take a parameter, that way you're a bit more flexible. For example, from your view:
<%= render :partial => "supports/form", :locals => {:support => #support} %>
Now, instead of referring to #support in your _form.html.erb, you'd refer to simply support as it's a local_assign.
Yet another option would be to refactor the form a little further, and worry about creating the actual form tag outside of the partial.
Such as:
app/views/supports/new.html.erb
<%= form_for #support do |form| %>
<%= render :partial => "suppports/form", :object => form %>
<% end %>
app/views/supports/_form.html.erb
<%= form.text_field :foo %>
<%= form.text_field :bar %>
...
In this case, when you render a partial with the object option, you will get a local variable in your partial with the same name as the partial. You maintain a little bit more flexibility in the path of your form, but can still render the meat of what a Support object is inside of the form while remaining consistent across your app.
To clarify, you could use this somewhere else by doing something like:
app/views/foos/_create_foo_support.html.erb
<%= form_for #foo.support do |form| %>
<%= render :partial => "supports/form", :object => form %>
<% end %>
You have to pass #support object wherever you use your contact form. It's working in SupportsController#new because you initialize the variable there. In all other places where you want to use the form, you'll have to do the same.

Resources