Pass an object from one controller action to another - ruby-on-rails

I have a request object that I want to pass to my charges_controller. I can pass it in to the new action but after that new action is called I need that same request object passed to the create object in the same charges_controller. I can not get it. Here is the initial request_controller action passing the request_id to the charges controller.
class Users::RequestsController < ApplicationController
before_filter :authenticate_user!
def create
#user = current_user
if #request = #user.request.create!(authorization_params)
if #request.user_paying == true
redirect_to new_users_charge_path(id: #request)
else
redirect_to users_dashboard_path
end
else
redirect_to users_dashboard_path, :error => "There is something wrong and your request has not been submitted."
end
end````
It then passes it to the new method in the charges controller. Here is my charges controller.
class Managers::ChargesController < ApplicationController
before_filter :authenticate_user!
def new
#request = Request.find(params[:id])
#user = current_user
#amount = #request.user_amount
end
def create
# Amount in cents
#request = Request.find params[:request_id]
#user = current_user
#amount = #request.user_amount
customer = Stripe::Customer.create(
:email => #user.email,
:card => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => #amount,
:description => 'Manager Paid Report',
:currency => 'usd'
)
#request.report_paid = true
#request.save!
redirect_to managers_dashboard_path, :notice => "You have succesfully requested a pre-paid report from #{#reportapproval.tenant_last_name}."
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to managers_charges_path
end
end
I can get the request info into the charges/new.html view. But when I create the charge the request is not found. How can I pass the request to the create action?
Here is the new view...
<h4>Total Payment Due: <%= number_to_currency(#amount.to_i/100.0) %>
<%= form_tag users_charges_path do %>
<%= hidden_field_tag :request_id, #request.id %>
<br />
<script src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="<%= Rails.configuration.stripe[:publishable_key] %>"
data-description="Payment"
data-amount="<%= #amount %>"
data-locale="auto" >
</script>
<% end %>

Controllers simply respond to HTTP requests - there is no way to forward a request internally in Rails. This is a very conscious design decision.
If you need to stash data in between requests you would either save it in the database, the session or some sort of caching mechanism like Memcached/Redis.
Most commonly in Rails you stash data in the database and pass ID's or other unique identifiers in the request parameters.
If you need to pass the id to the "Request" object you would either pass it in the request url or in the request body in the case of PUT/PATCH/POST.
So in your case you need to ensure that the #new action either posts to:
post "/managers/charges/:request_id"
Or that the form includes
<%= hidden_field_tag 'request_id', #request.id %>
PS
Request although not technically a reserved word is a really bad name for a model since it will eventually cause conflicts and confusion with ActionDispatch::Request especially if you use the controller instance variable #request.
As you can see from the my answer above it gets confusing as h*ll since the word request is allready has a very specific connotation in web development.
The same applies to Response.
Use PaymentRequest or any other synonym.

Assuming you have a form in the charges/newview, you can set the request_id using a hidden form field:
<%= hidden_field_tag :request_id, #request.id %>
You should add the line above between <%= form_for ... do |f| %> and <% end %>.
In that example, you were assuming the create action would somehow have a request_id passed in automatically. Rails doesn't do that, in every request you need to explicitly provide all the information for the controller to process that request.
I hope that helps! ;)

Related

Rails: External API Integration using RestClient (undefined local variable or method `username')

I am building a digital library, and I have completed a lot of the functionalities needed. I am currently having an issue with integrating the digital library with a Learning Management System (LMS).
I already have an admin authentication system for the digital library using the Devise gem. My goal is to allow users who want to access the digital library to login to the digital library using their Learning Management System (LMS) credentials (username and password).
I have been provided with the Login API endpoint and other needed parameters of the Learning Management System (LMS), and I have created the User Model, the Sessions Controller and the Sessions View Templates.
I am currently using the RestClient Gem for the API call, but I having an error undefined local variable or method `username' for # Did you mean? user_path. I can't figure out where things went wrong.
Sessions Controller
require 'rest-client'
class SessionsController < ApplicationController
def new
end
def create
response = RestClient::Request.execute(
method: :post,
url: 'https://newapi.example.com/token',
payload: { 'username': "#{username}",
'password': "#{password}",
'grant_type':'password' },
headers: { apiCode: '93de0db8-333b-4f478-aa92-2b43cdb7aa9f' }
)
case response.code
when 400
flash.now[:alert] = 'Email or password is invalid'
render 'new'
when 200
session[:user_id] = user.id
redirect_to root_url, notice: 'Logged in!'
else
raise "Invalid response #{response.to_str} received."
end
end
def destroy
session[:user_id] = nil
redirect_to root_url, notice: 'Logged out!'
end
end
Sessions New View
<p id=”alert”><%= alert %></p>
<h1>Login</h1>
<%= form_tag sessions_path do %>
<div class="field">
<%= label_tag :username %>
<%= text_field_tag :username %>
</div>
<div class="field">
<%= label_tag :password %>
<%= password_field_tag :password %>
</div>
<div class="actions">
<%= submit_tag 'Login' %>
</div>
<% end %>
User Model
class User < ApplicationRecord
has_secure_password
validates :username, presence: true, uniqueness: true
end
Any form of help with code samples will be greatly appreciated. I am also open to providing more information about this integration if required. Thank you in advance.
I think that the problem is in fact that inside your SessionsController in create action, you are interpolating username and password. There's no definition for these methods in your code so you get undefined local variable or method.
You could probably pick those from params like this:
def username
params[:username]
end
def password
params[:password]
end
Or interpolate them directly in payload replacing current method calls with params[:username] and params[:password].
In such situations, it is good to use byebug or pry to debug your code and see what's happening inside your controller.
You could also think of closing some parts of your logic in Service objects - you shouldn't have more 10-15 lines in your controller action (unless the situation requires it)
Maybe you should use params[:username] rather than only username ?
username and password in payload are undefined variables. Please set their values. Possible values could be params[:username] and params[:password]

Pass a text_field custom parameter to a link_to paypal IPN

I'm trying to send an email to the client when he successfully makes a transaction using paypal.
I've already manage to send the custom email parameter to paypal in a custom parameter they provide.
What I have right now
My product model:
class Product < ActiveRecord::Base
# This defines the paypal url for a given product sale
def paypal_url(return_url, cancel_return, useremail)
values = {
:business => 'your_business_email#example.com',
:cmd => '_xclick',
:upload => 1,
:return => return_url,
:rm => 2,
:cancel_return => cancel_return,
:custom => useremail
}
values.merge!({
"amount" => unit_price,
"item_name" => name,
"item_number" => id,
"quantity" => '1'
})
# For test transactions use this URL
"https://www.sandbox.paypal.com/cgi-bin/webscr?" + values.to_query
end
has_many :payment_notifications
end
here, I'm passing a parameter for the :custom object which I have it hardcoded in the button link_to helper here:
<%= link_to 'checkout', #product.paypal_url(payment_notification_index_url, root_url, 'testing#testing.com') %>
This works perfectly, and I am able to store the custom email in the database:
class PaymentNotificationController < ApplicationController
protect_from_forgery except: [:create]
def create
# #payment = PaymentNotification.create!(params: params, product_id: params[:invoice], status: params[:payment_status], transaction_id: params[:txn_id] )
#payment = PaymentNotification.create!(params: params, product_id: 1, status: params[:payment_status], transaction_id: params[:txn_id], email: params[:custom] )
# render nothing: true
if #payment.status == 'Completed'
PaymentTransactions.success(#payment).deliver_now
redirect_to root_url, notice: 'Success!'
else
redirect_to root_url, notice: 'Error'
end
end
end
Question
How do I get the client to input their email in a field and pass that value into the parameters of the link_to so that paypal return the email so I can store it in the database and send an email to the client?
Thanks
You should not use link_to, but form_tag with method: :get
<%= form_tag (#product.paypal_url(payment_notification_index_url, root_url, :custom)),method: :post do %>
<%= text_field_tag :custom %>
<%= submit_tag 'checkout' %>
<% end %>
This might be more than what you're expecting...but read on.
Before you dive further into the implementation, keep in mind that, you're using the sandbox version of Paypal for testing, and in production, you'd want the paypal_url to return an encrypted url for the user as to avoid tampering of the transaction, such as changing the price (more details at Railscast #143).
Now, realize that any approaches on the client-side via javascript to get the user email field and modify the link will not be secure as the link should be generated from your server after encryption (and you'd need to pass in the user email as part of the call).
So, what can you do? Use ajax to send the request to the server containing the parameters (e.g. return_url, user_email, etc..), and respond in the server with an encrypted link. Then, you can use javascript to replace the link and allow user to click that instead.
As you realize, the implementation above is very general and any answer would not suit your specific case. You should keep the above in mind as you'd be required to do that anyway down the road.

Manually Send Email Rails

I've been trying to implement the code from this question: Send an email manually of a specific page in rails app
The only difference is that I need to fetch the email address from my model. This is usually no problem when sending emails from models.
UserMailer.report(self).deliver
But I want to click on a button in the show view of my record.
I need to manually send out emails using the details of the record in the email.
Maybe there is a better approach than using an extra controller for this?
# app/mailers/user_mailer.rb
class UserMailer < ActionMailer
def report(thing)
#thing = thing
mail :to => thing.email, :from => 'you#example.com',
:subject => 'that report you want'
end
end
# app/views/user_mailer/report.html.erb
<h1>Report</h1>
<p>Here is your <% #thing.customer_id %></p>
# app/controllers/reports_controller.rb
def create
UserMailer.report(#thing).deliver
flash[:notice] = 'report sent!'
redirect_to root_path # or wherever
end
# in a view
<% form_tag(reports_path(#thing), :method => :post) do %>
<% submit_tag 'send report email' %>
<% end %>
I'm returning null with the code above:
ArgumentError (wrong number of arguments (0 for 1)):
app/controllers/reports_controller.rb:3:in `create'
Create is a post request in rails, you cannot pass parameter like this you need to fetch from params. I'm seeing you are giving it a parameter which is wrong.
Secondly you are doing #thing = thing and then you are sending thing (without #) to report method of UserMailer which is also wrong, it would be be nil in report method. you should do UserMailer.report(#thing).deliver after #thing is an object which has email

rails 4 strong params session

I am using a custom passwordless login for my app. A user simply needs to enter their username or email, and then a unique login link with a token is sent to them. They enter their username or email into a simple form :
<%= form_tag request_token_path, id:'login-form' do %>
<%= text_field_tag :user_id %>
<% end %>
This posts to a sessions#request_token method which verifies whether that user exists and then sends along the login link.
def request_token
lookup = session_params[:user_id]
if lookup.include? '#'
#user = User.find_by(email: lookup)
else
#user = User.cached_find(lookup)
end
if #user
#user.send_login_link
redirect_to login_path, notice: "#{#user.username.capitalize} your email was sent!"
else
redirect_to login_path, notice: "Whoops! Looks like #{lookup} is not registered on this site. Please check spelling or signup!"
end
end
My question is that in my SessionsController file I defined the sessions_params
private
def session_params
params.require(:session).permit(:user_id,:auth_token)
end
I know that means that I have to use a session object or in order to pass along the :user_id from the form since I defined :user_id as a param that is on valid as an attribute of a session. I am wondering the correct way to do this. Making a new session object doesn't make sense since that isn't even a model I have but is it safe to just take it from the params?
and instead make lookup = params[:user_id] ?
If you have a session object that responds to user_id attribute, you need to create the form for that object specifically:
<%= form_for #session do |f| %>
<%= f.text_field :user_id %>
<% end %>
If that's not the case, and you need to stick to form_tag, try making the attribute name something that would come up in the controller as a session hash:
<%= text_field_tag "session[user_id]" %>
When you do
params.require(:session)
it means you're requiring your params hash to have a session key, which in turn should have the permitted user_id attribute:
{params: {session: {user_id: "something"}}
And thats why you'd need form_for #session OR the textfield with the suggested "session[user_id]" name

How to make a POST to a third party and get the result back with Ruby on Rails?

I'm having a form in a .erb file, where the user can enter some info. Then I want to make a POST to a different URL (let's say www.3rdparty.com/api.php?f=add_entry), that will reply with 0 or 1, for success or failure. Which is the right way to go, especially if I want to stay on the page with the form, and then show a dialog according to the response? I have no experience in Ruby on Rails, so I'm not even sure if this is possible, or if I should add functionality to the controller. Thus, I would really appreciate it if you could provide beginner details :)
Thanks,
Irene
require 'net/http'
url = URI.parse('http://www.3rdparty.com/api.php?f=add_entry')
args = {'arg1' => 'data' }
response = Net::HTTP.post_form(url, args)
See:
http://www.rubyinside.com/nethttp-cheat-sheet-2940.html
# routes.rb
map.resources :books, :member => {:status => :get}#status route member optional - see below
# book_controller.rb
def new
#book = Book.new
# new.html.erb
form_for #book, :url => books_path do |f|
<%= f.fields_for :title -%>
<%= f.fields_for :isbn -%>
#book_controller.rb
def create
#book = Book.new params[:book]
if #book.save
redirect_to status_book_path #book #optional
# no need to redirect. Create a create.html.erb and it will be rendered by default
#book.rb #model
before_create :verify_isbn
private
def verify_isbn
require 'net/http'
url = URI.parse('http://www.3rdparty.com/api.php?f=add_entry')
args = {:isbn => isbn }#here the latter isbn is a field of your model
response = Net::HTTP.post_form(url, args)
errors.add(:isbn, "That isbn is not valid") unless response.to_i == 1
end

Resources