How can I pass objects from one controller to another in rails? - ruby-on-rails

I have been trying to get my head around render_to but I haven't had much success.
Essentially I have controller methods:
def first
#I want to get the value of VAR1 here
end
def second
VAR1 = ["Hello", "Goodbye"]
render_to ??
end
What I can't figure out is how to accomplish that. Originally I just wanted to render the first.html.erb file but that didn't seem to work either.
Thanks
Edit: I appreciate the answers I have received, however all of them tend to avoid using the render method or redirect_to. Is it basically the case then that a you cannot pass variables from controller to controller? I have to think that there is some way but I can't seem to find it.

It is not a good idea to assign the object to a constant. True this is in a global space, but it is global for everyone so any other user going to this request will get this object. There are a few solutions to this.
I am assuming you have a multi-step form you are going through. In that case you can pass the set attributes as hidden fields.
<%= f.hidden_field :name %>
If there are a lot of fields this can be tedious so you may want to loop through the params[...] hash or column_names method to determine which attributes to pass.
Alternatively you can store attributes in the session.
def first
#item = Item.new(params[:item])
session[:item_attributes] = #item.attributes
end
def second
#item = Item.new(session[:item_attributes])
#item.attributes = params[:item]
end
Thirdly, as Paul Keeble mentioned you can save the model to the database but mark it as incomplete. You may want to use a state machine for this.
Finally, you may want to take a look at the Acts As Wizard plugin.

I usually don't have my controllers calling each other's actions. If you have an identifier that starts with a capital letter, in Ruby that is a constant. If you want to an instance level variable, have it start with #.
#var1 = ["Hello", "Goodbye"]
Can you explain what your goal is?

Have you considered using the flash hash? A lot of people use it solely for error messages and the like, it's explicitly for the sort of transient data passing you might be interested in.
Basically, the flash method returns a hash. Any value you assign to a key in the hash will be available to the next action, but then it's gone. So:
def first
flash[:var] = ["hello", "goodbye"]
redirect_to :action => :second
end
def second
#hello = flash[:var].first
end

way 1
Global variable
(fail during concurrent requests)
way 2
class variable
(fail during concurrent requests)
way 3
Stash the object on the server between requests. The typical way is to save it in the session, since it automatically serializes/deserializes the object for you.
Serialize the object and include it in the form somewhere, and
deserialize it from the parameters in the next request. so you can store attributes in the session.
def first
#item = Item.new(params[:item])
session[:item_attributes] = #item.attributes
end
def second
#item = Item.new(session[:item_attributes])
#item.attributes = params[:item]
end
way 4
The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed to the very next action and then cleared out.
def new
#test_suite_run = TestSuiteRun.new
#tests = Test.find(:all, :conditions => { :test_suite_id => params[:number] })
flash[:someval] = params[:number]
end
def create
#test_suite_run = TestSuiteRun.new(params[:test_suite_run])
#tests = Test.find(:all, :conditions => { :test_suite_id => flash[:someval] })
end
way 5
you can use rails cache.
Rails.cache.write("list",[1,2,3])
Rails.cache.read("list")
But what happens when different sessions have different values?
Unless you ensure the uniqueness of the list name across the session this solution will fail during concurrent requests
way 6
In one action store the value in db table based on the session id and other action can retrieve it from db based on session id.
way 7
class BarsController < UsersController
before_filter :init_foo_list
def method1
render :method2
end
def method2
#foo_list.each do | item|
# do something
end
end
def init_foo_list
#foo_list ||= ['Money', 'Animals', 'Ummagumma']
end
end
way 8
From action sent to view and again from view sent to other actions in controller.

Related

flexible system to destroy each records in batch

client_skipped_day_controller.rb
class ClientSkippedDaysController < ApplicationController
before_action :check_client_on_exist, only: [:create]
def index
#client_skipped_days = ClientSkippedDay.order_by(params[:sort_by], params[:direction])
if params[:date].present?
#client_skipped_days = #client_skipped_days.where('skipped_at = ?', Date.parse(params[:date]))
end
render json: #client_skipped_days, status: :ok
end
def create
#client_skipped_days = ClientSkippedDay.create!(client_skipped_days_params)
render json: #client_skipped_days, status: :created
end
def destroy
end
private
def client_skipped_days_params
params.permit(client_skipped_days: %i[client_id skipped_at])[:client_skipped_days]
end
def check_client_on_exist
client_skipped_days_params.each do |day|
ClientSkippedDay.find_by(day)&.destroy
end
end
end
My code works if I try to delete only one record, like a :
Parameters: {"client_skipped_days"=>[{"client_id"=>533, "skipped_at"=>"2019-02-24"}], "client_skipped_day"=>{}}
But if I try to delete each hash in the array, it's didn't work :(
Parameters: {"client_skipped_days"=>[{"client_id"=>533, "skipped_at"=>"2019-02-24"}, {"client_id"=>512, "skipped_at"=>"2019-02-24"}], "client_skipped_day"=>{}}
Only one record will be deleted, but how to add the ability to delete all records? which coincide with the parameters that come from the controller?
And it must be a flexible system to remove if 1 hash in the array and immediately a collection of hashes in the array. Tell me how to do it.
Instead of looping over the params and finding each record one by one you could also consider using multiple #where queries combining them together with the use of #or and loop over the resulting records.
def client_skipped_days_params
params.permit(client_skipped_days: [:client_id, :skipped_at])
# removed `.values` ^
end
def check_client_on_exist
destroyed_records, undestroyed_records =
client_skipped_days_params
.fetch(:client_skipped_days, []) # get the array or use an empty array as default
.map(&ClientSkippedDay.method(:where)) # build individual queries
.reduce(ClientSkippedDay.none, :or) # stitch the queries together using #or
.partition(&:destroy) # call #destroy on each item in the collection, separating destroyed once from undestroyed once
end
In the above example the resulting destroyed records are present in the destroyed_records variable and the records that could not be destroyed are present in the undestroyed_records variable. If you don't care about the result you can leave this out. If you want to raise an exception if a record cannot be destroyed use #destroy! instead (call upon each collection item).
Alternatively you can destroy all records by calling #destroy_all (called upon the collection), but it will simply return an array of records without differentiating the destroyed records from the undestroyed records. This method will still instantiate the records and destroy them one by one with the advantage that callbacks are still triggered.
The faster option is calling #delete_all (called upon the collection). This will destroy all records with one single query. However records are not instantiated when destroyed, meaning that callbacks will not be triggered.
def check_client_on_exist
destroyed_record_count =
# ...
.reduce(ClientSkippedDay.none, :or)
.delete_all # delete all records with a single query (without instantiation)
end
references:
ActionController::Parameters#fetch
Array#map
ActiveRecord::QueryMethods#none
Enumerable#reduce
Enumerable#partition
You need to loop over your array instead of just taking the first value out of it. I don’t understand the params that you have, so I’m assuming that you want to do your find_by using the Hash of client_id and skipped_at.
Also, Ruby 2.3.0 introduced the safe navigation operator, which is what that &. is if you aren’t used to it. http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/
Since find_by either returns an ActiveRecord object or nil, it’s a great time to use the safe navigation operator to shorten things up.
def client_skipped_days_params
params.permit(client_skipped_days: %i[client_id skipped_at])[:client_skipped_days]
end
def check_client_on_exist
client_skipped_days_params.each do |day|
ClientSkippedDay.find_by(day)&.destroy
end
end
Note, I’m not sure what your client_skipped_day Hash is. I assumed you’re making it possible to delete a single day, or delete in bulk. I would warn against having it do two things. Just make the client always send an array for this action and things will be easier for you. If you can do that, then you can make client_skipped_days required.
def client_skipped_days_params
params.require(:client_skipped_days).permit(%i[client_id skipped_at])
end
This will raise a 422 error to the client if they don’t provide the client_skipped_days key.
If this isn’t possible, then you’ll need to add an if to check_on_exist to make sure that client_skipped_days_params is not null (because they’re using client_skipped_day).

Strong Parameters and Find in Rails 4

I am having trouble with strong parameters in my custom controller. I understand how to use strong parameters in a new or update action. However, I can't seem to figure out if this is an unsecure use of the params[] hash or not in my custom action.
My view redirects to a controller with an order id and an operation number:
link_to 'Confirm', confirmpayment_confirm_path(order: order, operacion: order.operacion), :data => { confirm: 'Are you sure?' }
My confirmpayment controller is as follows:
class ConfirmpaymentController < ApplicationController
before_action :authenticate_user!
def lookup
authorize! :lookup, :confirmpayment
#orders=Order.where(:status => 'PENDING')
end
def confirm
authorize! :confirm, :confirmpayment
#order=Order.find(params[:order])
#order.payment_id = params[:operacion]
#order.confirm_payment_date = DateTime.now()
#order.save
end
def order_params
params.require(:order).permit(:order, :operacion)
end
end
The question is:
I am not using order_params anywhere in my confirm action, since this is not a new order. I am using the parameter to find the proper order and confirm it. Is this secure? Or am I missing something?
So to clear a few things up.
Strong Parameters is responsible for the allowed parameters which are passed to your database. It should prevent the users to modify attributes in the database which they aren't allowed to modify.
For example:
You have the following table with columns:
User:
- firstname
- lastname
- email
- password_digest
- role (user, admin)
You probably want to prevent normal users to change their role. But if you pass a parameters hash as it is to the database, he could also add a role key with value to the hash. Your application would accept it. Strong parameters checks the hash and prevent the change.
In your example above, Strong Parameters brings no advantages. You assign the values directly to the appropriate table columns. It isn't possible to modify any other attributes in the database for the user. If you don't have any other methods in your controller, you could remove the entire #order_params. Strong Parameters just raises an exception if you would try to pass a hash directly through.
However I would recommend you to search for the payment in the database before you assign it. If you assign payment_id directly you have no guarantee that payment exists. So first check for the payment and if you found it assign it to the order.
Here is how I would have done it:
class PaymentConfirmationController < ApplicationController
before_action :authenticate_user!
before_action :authorize_user! # To DRY up your code
def lookup
#orders = Order.where(:status => 'PENDING')
end
def confirm
#order = Order.find(params[:order_id])
#payment = Payment.find(params[:operation_id])
# You should catch the exceptions if an order or payment wasn't found
#order.payment = #payment
#order.payment_confirmation_date = Time.now()
#order.save
end
private
def authorize_user!
authorize! :confirm, :confirmpayment
end
end
I haven't tested the code but it should work.
Here are the Docs of Strong Parameters. They describe everything in more detail.
I hope this helps!
Happy coding :)
About the way of using parameter, I think there is nothing wrong with it. But about the security problem, you may want to think about the case that user can change any Order information by just change the order param to something that doesn't belong to him.
In that case, you will want to limit the query to Order, make he can only confirm the order that belongs to him.
Strong Parameters: prevent accidentally exposing that which shouldn't be exposed. They are generally used when you create or update a model, this to avoid entering parameters have not been allowed.
I have some suggestions:
English preferably encoding: operacion to operation
Check style
in your code ConfirmpaymentController to class
ConfirmPaymentController
you can see: best practices and style prescriptions for Ruby on Rails 4 :)

rails 4 pass object between controllers

I have a Queries Controller which handles the results of an API request and I'm trying to pass that api object to another controller without having to persist the information to the database (the point of the app is returning a list of movies available in a certain zipcode and then allowing a user to view those results and create an event with friends around that movie, so there's no need to save the movie information in my database when the api call is made since it returns a lot of movies)
Here is my create method in the Queries Controller:
def create
#query = Query.new
#query.zip = query_params['zip']
#query.date = query_params['date']
#results = data(#query.zip, #query.date)
redirect_to results_path, :results => #results
end
and the results method which it gets passed to
def results
end
and then the corresponding Results view where I am just trying to display the results object:
<h3>Index</h3>
<%= results %>
MVC
My immediate thought is your thoughts are against the MVC programming pattern (on which Rails is based):
Controllers are meant to take a request from HTTP, and use it to manipulate the data on your screen through a model. The model is then able to provide the data you require, which can be passed to the view.
Sending requests inter-controller is against this pattern IMO - you'll be much better manipulating the data in a single controller action, and then pulling that data from the model.
Having said that, I think you are doing things relatively well, although you may wish to consider refactoring at system-level.
--
Fix
You should be able to pass your instance variable to your results action through the params object, as described by mjhlobdell:
#app/controllers/queries_controller.rb
Class QueriesController < ApplicationController
def query
...
redirect_to results_path(results: #results)
end
end
#app/controllers/results_controller.rb
Class ResultsController < ApplicationController
def results
#results = params[:results]
end
end
You should be able to use this to pass the data you need.
An alternative way would be to manipulate / render the response directly in the Queries create method, as here:
#app/controllers/queries_controller.rb
Class QueriesController < ApplicationController
def create
...
render :results
end
end
Try passing the results in the params hash to the results method
def create
#query = Query.new
#query.zip = query_params['zip']
#query.date = query_params['date']
#results = data(#query.zip, #query.date)
redirect_to results_path(:results => #results)
end
Then:
def results
#results = params[:results]
end
In the view:
<h3>Index</h3>
<%= #results %>

Ruby - How to define params for require()?

questions_controller.rb
def index
#questions = Question.all(app_params)
end
private
def app_params
params.require(:questions).permit(:question, :answer)
end
end
question.rb
class Question < ActiveRecord::Base
end
I am completely new to ruby-on-rails. I was following a guide and it said I should take care of some "loopholes" or "security issues" and it used attr_accessible, but on Rails 4, they suggest strong parameters, so now I'm trying to use them. I'm confused on how to define the :questions params, because I'm currently getting an error saying that :questions param is not found.
:questions is pretty much something that I will define myself as the web developer.
So for example, I will define questions = "How are you?", "What is your name?". I'm basically starting very simply. I want questions that I have created to be displayed on my webpage. Ultimately, I plan to make a website what is basically a list of questions and, with answer options. After the user clicks "submit" I want to store the information into my database.
Am I supposed to even be requiring this as a param? I'm completely lost..
Do you have a dump of the params we could look at? They are shown when your app encounters an error, and typically shows you the params array which rails will pass through
Strong Params In Rails 4
Strong Params allow you to allow certain parameters for use in the controller, protecting against any malicious assignment client-side. They replaced attr_accessible in Rails 4.0
Strong Params is only for user-submitted content, as it's designed to protect the params hash. To that end, it's mostly used with the create and find functions:
class PeopleController < ActionController::Base
# Using "Person.create(params[:person])" would raise an
# ActiveModel::ForbiddenAttributes exception because it'd
# be using mass assignment without an explicit permit step.
# This is the recommended form:
def create
Person.create(person_params)
end
# This will pass with flying colors as long as there's a person key in the
# parameters, otherwise it'll raise an ActionController::MissingParameter
# exception, which will get caught by ActionController::Base and turned
# into a 400 Bad Request reply.
def update
redirect_to current_account.people.find(params[:id]).tap { |person|
person.update!(person_params)
}
end
private
# Using a private method to encapsulate the permissible parameters is
# just a good pattern since you'll be able to reuse the same permit
# list between create and update. Also, you can specialize this method
# with per-user checking of permissible attributes.
def person_params
params.require(:person).permit(:name, :age)
end
end
params.require
The params.require function works by taking this params hash:
params{:question => {:question => "1", :answer => "5"}}
That's why people asked what your params hash looks like, because the require function can only work if the :question hash is present.
Possible Solutions For You
Question.all(app_params)
Regardless of what you're trying to achieve, don't use all. The where function is better for receiving an array of data based on certain values. I believe all is depreciated anyway.
def index
#questions = Question.where("value = ?", variable)
end
What data is being passed?
I will define questions = "How are you?", "What is your name?"
This is okay, but typically in rails, you'd call data by using an ID in the database. If you're defining these questions in a form, you'd use the strong params system; but you'd need a form to submit the data to
Further Additions
The rails way is to keep all your data in a database, and use the application to manipulate that data, either by showing it, or allowing people to input more.
The "params" variables are basically there to help the rails controllers & models accept & process data from end users, and consequently allow you to keep the system growing. Instead of having to write custom code to accommodate all sorts of different data, the params give you a rigid structure to work with. Here is a good explaination of how MVC (and params) works for you: How does an MVC system work?
I think you're getting confused with how your app should work
Your "questions" should be stored in a questions table / model, and can be accessed by calling their ID's with the find function. This code would be like this:
#app/controllers/questions_controller.rb
def show
#question = Question.find(params[:id])
end
If you want to add new questions, you'll be best to add them to the questions table, like this:
#app/controllers/questions_controller.rb
def new
#question = Question.new
end
def create
#question = Question.new(question_params)
#question.save
end
private
def question_params
params.require(:question).permit(:question)
end
#app/views/questions/new.html.erb
<%= form_for #question do |f| %>
<%= f.text_field :question %>
<% end %>
This will give you a central store of your questions, which you'll then be able to access when you need them, either with a helper or with your ".all" call :)
Give it a shot with question (singular):
params.require(:question).permit(:text, :answer)
Assuming question is your model and text (which I made up) is the wording of the question.

How to make an object visible in scope of entire controller?

So i need to get instantiate an object which requires parameters. I also need this object to be available in the scope of the entire controller once instantiated. How can this be done?
Edit: some code to help illustrate
def beginLoad(user, category)
#stuff
#gaobj = GraphAssistant.new(#arrays.fetch(0), #arrays.fetch(1))
values = #gaobj.externalize
ret = {"axis_label" => values.fetch(0), "out" => values.fetch(1), "i" => values.fetch(2)}
end
But when I try to call it again from this method:
def resumeLoad(direction)
if direction.eql? "left"
#gaobj.decrementPosition
elsif direction.eql? "right"
#gaobj.incrementPosition
end
#other stuff
end
it doesnt work. I suppose I could do all of this logic in the view, what implications will that have on performance though?
Prefix it with an #sign: #foo = Foo.new.
Update: Sounds like you need this persisted to the session.
At the end of beginLoad, add:
session[:foo] = #foo
At the beginning of resumeLoad, add:
#foo = session[:foo]
If both of those functions are being called from the same controller action, the instance variable (#gaobj) should be accessible to both. You will need to make sure that #beginLoad is called before #resumeLoad for every incoming request. Is that the problem?

Resources