Pass multiple objects into action mailer/email contents in rails - ruby-on-rails

Would like to find out how to pass multiple objects into action mailer/email contents in rails. I have no issues passing in #announcement, but not sure how to pass in #post and also #user info.
announcement_controllers.rb
def create
#post = Post.find_by slug: params[:post_id]
#announcement = #post.announcements.build(announcement_params)
#announcement.creator = current_user
if #announcement.save
flash[:notice] = 'Your announcement was created.'
AnnouncementMailer.announcement_alert(#announcement, #post).deliver
redirect_to :back
else
flash[:notice] = 'Unable to create announcement. Make sure you have enter information.'
redirect_to :back
end
end
announcement_mailer.rb
class AnnouncementMailer < ActionMailer::Base
binding.pry
default to: Proc.new { Signup.where(post_id: #post.id, user_id: current_user.id).pluck(:email) },
from: "#{#post.slug}#fundspace.announcement.com"
def announcement_alert(announcement, post)
#announcement = announcement
#post = post
mail(subject: "#{#post.slug}: #{#announcement.title}")
end
end
binding.pry
1: class AnnouncementMailer < ActionMailer::Base
=> 2: binding.pry
3: default to: Proc.new { Signup.where(post_id: #post.id, user_id: current_user.id).pluck(:email) },
4: from: "#{#post.slug}#fundspace.announcement.com"
5:
6: def announcement_alert(announcement, post)
7: #announcement = announcement
[1] pry(AnnouncementMailer)> #post
=> nil
[2] pry(AnnouncementMailer)> #announcement
=> nil
[3] pry(AnnouncementMailer)> #user
=> nil
binding.pry to check on #post in announcement_mailer.rb returns nil. Not sure why. Thanks in advance.

This is a classic case of your debug code giving you the wrong impression. 😃 Your #post is getting passed in fine - but it's happening after the call to binding.pry.
You pass in your post to announcement_alert, which sets it as an instance variable. If you updated that method to look like this, you should see it set fine:
def announcement_alert(announcement, post)
#announcement = announcement
#post = post
binding.pry
mail(subject: "#{#post.slug}: #{#announcement.title}")
end
(You're not checking that post is an object in your mailer, so it's possible for other code to pass nil. That shouldn't be a problem in this case, though - Post.find_by will return nil if there are no matches, but your #post.announcements.build would fail if it had.)
This confusion over where #post gets set is going to cause problems in your default line, too. Statements outside of a method - like your binding.pry and your default to: - get run when the class is evaluated. Statements inside a method - those inside def announcement_alert - don't run until that method is called. And, as we saw above, #post isn't defined until you call announcement_method.
Here's your current default statement:
default to: Proc.new { Signup.where(post_id: #post.id, user_id: current_user.id).pluck(:email) },
from: "#{#post.slug}#fundspace.announcement.com"
Your to: parameter is set to a Proc, which is great - even though it references #post, because it's a Proc it doesn't run until it's needed. You'll have set #post at that point.
Your from: parameter, on the other hand, is just a string. It's outside of a proc. As a result, it tries to evaluate #post.slug immediately - and there's no #post set yet, leading to an error. Changing it to this fixes it:
default to: Proc.new { Signup.where(post_id: #post.id, user_id: current_user.id).pluck(:email) },
from: Proc.new { "#{#post.slug}#fundspace.announcement.com" }

Related

Argument error; wrong number of arguments given

New to Ruby on Rails and been cracking my head on this. I have been following this tutorial here to make this form to save records into my DB - https://human-se.github.io/rails-demos-n-deets-2020/demo-resource-create/
This is my controller:
class ViewaddparamController < ActionController::Base
def view_add_param
newParam = ViewaddparamController.new
respond_to do |format|
format.html{ render :viewaddparam, locals: { newParam: newParam } }
end
end
def add_param
# new object from params
mlParam = ViewaddparamController.new(params.require(:Viewaddparam_Controller).permit(:name, :machine_model, :parameter_1, :parameter_2, :parameter_3, :data_train_set, :start_date, :end_date))
# respond_to block
respond_to do |format|
format.html do
if mlParam.save
# success message
flash[:success] = "ML parameters saved successfully"
# redirect to index
redirect_to model_url
else
# error message
flash.now[:error] = "Error: parameters could not be saved"
# render new
render :viewaddparam, locals: { newParam: newParam }
end
end
end
end
end
My route:
get 'viewparam', to: 'viewaddparam#view_add_param'
post 'add_param', to: 'viewaddparam#add_param', as: 'add_param'
My view:
<%= form_with model: newParam, url: add_param_path, method: :post, local: true, scope: :Viewaddparam_Controller do |f| %>
...
I kept getting this error whenever I try to submit the form
ArgumentError in ViewaddparamController#add_param
Wrong number of arguments (given 1, expected 0)
The error highlighted at my controller class, line 11.
What am I doing wrong here? I looked through the tutorial over and over but I can't see the fault here.
Thanks in advance.
It seems that you’re treating ViewaddparamController as both the controller – which in Rails terms is what responds to user requests - and the data model.
There’s often a one-to-one correlation to controllers and models, especially if you’re following what’s known as the RESTful pattern. So if your model was a Product, you might set it up in routes using a resources directive:
resources :products
That would set the app up to expect to use a ProductsController. And, using a similar coding style to your example, that would look a little like:
class ProductsController < ApplicationController
def new
product = Product.new
render :new, locals: { product: product }
end
def create
product = Product.new(params.require(:product).permit(…))
# etc
end
# etc
end
So you can see from this example that controller and model are related, but are named differently.
By using your controller name where you should be using the model, you’re calling #new on the controller, which is not normally something you need to do – controller instances are managed by the Rails app framework. And their initialiser doesn’t take any arguments, so Ruby complains that an initialiser that takes 0 arguments is being given 1.

Params is nil on entry into controller method

Rails 5.2
In my inventories_controller.rb, I have the following:
before_action :fetch_product, only: [:show]
def show
........
end
def fetch_product
if params.has_key?(:sku)
#product = Product.get_product(params)
end
end
This works fine, when I do: http://0.0.0.0:3000/sku/12345678
I am trying to implement search functionality, so I modified nventories_controller.rb as follows:
def fetch_product
if params.has_key?(:search) && !params[:search].blank?
product = Product.find_by_sku(params[:search])
if !product
params = params.except[:search]
redirect_to product_show_path, alert: 'Product was not found'
end
params = params.merge!(:sku, product.sku)
end
if params.has_key?(:sku)
#product = Product.get_product(params)
end
end
When I do: http://0.0.0.0:3000/sku/12345678
I get an instant error message:
undefined method `has_key?' for nil:NilClass
Using my debugger, I find that on entry into the fetch_product method, params is nil
Any idea what's going on?
params = params.merge!(:sku, product.sku) modifies the hash in place and returns nil, don't do that assignment, just call params.merge! (if you still want to do the assignment, remove the "!").
Personally, I wouldn't modify the params hash unless it's really really needed, I would use another variable.

Getting "private method `require' called for :params:Symbol" when trying to save my object

I’m using Rails 4.2.3. I have this in my controller …
def create
#myobject = MyObject.new(MyObject_params(:params))
#current_user = User.find(session["user_id"])
#myobject.user = #current_user
if #myobject.save
respond_to do |format|
format.html { redirect_to controller: "users", action: "index", notice: 'Saved successfully.' }
end
else
format.html { render action: "index" }
end
end
private
def MyObject_params(params)
# Main Code goes here
params.require(:myobject).permit(:time_in_ms, :name)
params[:myobject][:time_in_ms] = (params[:myobject][:hour].to_i * 60 * 60 + params[:myobject][:minute].to_i * 60 + params[:myobject][:second].to_i) * 1000
end
but when I submit my form, I get the error
private method `require' called for :params:Symbol
on this line, “params.require(:myobject).permit(:time_in_ms, :name, :distance, :distance_units)”. Not sure what I’m missing (or I have accidentally included). Thanks for help, - Dave
Edit:
After trying the suggestion given, I get the error
When assigning attributes, you must pass a hash as an argument.
on the line
#myobject = MyObject.new(MyObject_params)
In addition to making the adjustment in the suggestion, I also changed the method signature, "def MyObject_params(params)" to "def MyObject_params."
As Brad Werth stated you are trying to pass :params into your MyObject_params method. Since you have marked this method as private in your controller, it will refuse to accept an explicit receiver! There is a wealth of information on private vs public vs protected ruby methods but the long short of it is that you can not call any private method directly on an object (such as #user.make_admin) . This protects you from outside sources trying to manipulate the use of certain methods.
Changing #myobject = MyObject.new(MyObject_params(:params)) to
#myobject = MyObject.new(MyObject_params)
is what you want. I would also recommend following Ruby best practices and downcasing your MyObject_params method to my_object_params
The error message you have is telling you that you are trying to call a private method on a symbol. When you are doing this: #myobject = MyObject.new(MyObject_params(:params)), you are passing the symbol :params to the method. It looks like you really want the params hash, which would be #myobject = MyObject.new(MyObject_params(params)) (not a symbol).
As both methods have the same scope, you may consider not passing the params as an argument at all, but rather just refer to it in the MyObject_params method the same as you would in the create method. Also consider adjusting MyObject_params to at least my_object_params, to fit Ruby conventions, if not something more descriptive.
Since others answers are correct. I leave you the code I think must work.
def create
#myobject = MyObject.new(myobject_params)
#current_user = User.find(session["user_id"])
#myobject.user = #current_user
if #myobject.save
respond_to do |format|
format.html { redirect_to controller: "users", action: "index", notice: 'Saved successfully.' }
end
else
format.html { render action: "index" }
end
end
private
def myobject_params
# Main Code goes here
params[:myobject][:time_in_ms] = (params[:myobject][:hour].to_i * 60 * 60 + params[:myobject][:minute].to_i * 60 + params[:myobject][:second].to_i) * 1000
params.require(:myobject).permit(:time_in_ms, :name)
end
Summary of changes:
Remove parameters from the method myobject_params on it's call and definition (also I changed to snake_case as it is expected in Ruby).
Since time_in_ms was converted from hour, minute and second you must permit parameters after they were called for use.
The return from myobject_param, after reorder will be a hash.
And this should remove the 2 errors you reported.

undefined method paypal_url in Rails

I have a piece of code in Rails,
def create
#registration = Registration.new(registration_params)
if #registration.save
redirect_to #registration.paypal_url(registration_path(#registration))
else
render :new
end
end
I took it from tutorial. But I need just in this line:
#registration.paypal_url(registration_path(#registration))
Now, about my own controller, feed_controller, where
def create
#feed = Feed.new(check_params)
end
In the view erb file I put:
#feed.paypal_url(feed_path(#feed))
In my feed.rb (model):
def paypal_url(return_path)
values = {
business: "merchant#gotealeaf.com",
cmd: "_xclick",
upload: 1,
return: "#{Rails.application.secrets.app_host}#{return_path}",
invoice: id,
amount: course.price,
item_name: course.name,
item_number: course.id,
quantity: '1'
}
"#{Rails.application.secrets.paypal_host}/cgi-bin/webscr?" + values.to_query
end
Rake routes:
feed GET /:locale/feed(.:format) feed#index
feed#create POST /:locale/feed/create(.:format)
feed#new feed_new GET /:locale/feed/new(.:format)
feed#destroy feed_destroy GET /:locale/feed/destroy(.:format)
feed#edit feed_edit GET /:locale/feed/edit(.:format)
feed#update feed_update GET /:locale/feed/update(.:format)
But it prints the next error:
undefined method `paypal_url' for <#Feed::ActiveRecord_Relation:0x007fee24f5fc98>
How can I fix it? What is the problem?
UPDATE
def index
#current_user_is = current_user.email
session[:email] = #current_user_is
session[:id] = current_user.id
unless (current_user.member.present?)
#member = Member.new(:user_id => current_user.id)
#member.save()
redirect_to '/feed'
else
#new_feed = Feed.new
#feed = Feed.where(:member_id => current_user.member.id)
#category = Category.all
render 'home/uploads'
end
end
Simply use def self.paypal_url(return_path) instead of def paypal_url(return_path).
Explanation
You ran into your problem by defining a Class Method instead of an Instance Method, there's other posts discussing this.
The basic difference is, when defining:
def self.get_some_url
# code to return url of an instance
end
you can easily get the desired url of any objects, as in a view:
<% #feeds.each do |feed| %>
<%= feeds.get_some_url %>
<% end %>
Now calling Feed.get_some_url on the class would make no sense. Which url of the thousands would it call?
But there is a lot of use for class methods (where you define the method without self as you did)
def get_top_5
# code to return the top 5 most viewed feeds
end
Since this has nothing to do with a single instance, you define it for the entire Class. Leading to this call: Feed.get_top_5, which makes perfectly sense.
The second problem was not understanding the difference between where & find, this post will help you out with that.

How to test strong_parameters in controller specs?

I would like to test my controller after I added strong_parameters gem, how to do that?
I tried:
Controller
class EventsController < ApplicationController
def update
#event = Event.find(params[:id])
respond_to do |format|
if #event.update_attributes(event_params)
format.html { redirect_to(#event, :notice => 'Saved!') }
else
format.html { render :action => "new" }
end
end
end
private
def event_params
params.require(:event).permit!
end
end
Specs
describe EventsController do
describe "PUT update" do
describe "with forbidden params" do
let(:event) { Event.create! title: "old_title", location: "old_location", starts_at: Date.today }
it "does not update the forbidden params" do
put :update,
id: event.to_param,
event: { 'title' => 'new_title', 'location' => 'NY' }
assigns(:event).title.should eq('new_title') # explicitly permitted
assigns(:event).location.should eq("old_location") # implicitly forbidden
response.should redirect_to event
end
end
end
end
Errors
1) EventsController PUT update with forbidden params does not update the forbidden params
Failure/Error: assigns(:event).title.should eq('new_title') # explicitly permitted
NoMethodError:
undefined method `title' for nil:NilClass
# ./spec/controllers/events_controller_spec.rb:13:in
I see a few things going on here.
The fact that it says undefined method on line 13 is because the #event variable is not being assigned, so assigns(:event) is returning nil.
You should check out why that is happening, maybe you have some authentication that is preventing you from updating the record? Maybe you can check out the testing logs to see what is actually going on.
It could be because you are using let() which is lazy and the record is not actually available yet when you try to search for it, but I'm not completely sure. You could try using let!() and see if that helps.
With regards to the actual usage of strong parameters, if you only want title to be assignable you need to do something like the following:
params.require(:event).permit(:title)
If you use permit!, the event parameters hash and every subhash is whitelisted.

Resources