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 %>
Related
Hi all very noob question.
I'm trying to store data in a react calendar but it needs to store it using JSON.
I've noticed that when you scaffold, rails automatically also gives you a JSON version.
In my case - http://localhost:3000/users/1/bookings.json
Which returns [{"first_name":"Fake Name","booking_time":"2019-04-22T02:03:00.000Z","pick_up_time":"2019-04-22T02:03:00.000Z"}] in JSON.
I know how to fetch JSON data from a external URL and parse it through however all these external URL's are public whereas in my case the bookings are private.
Is there a way for me to fetch from bookings.json and store it in a variable and also by making it private where I wouldn't need to publicise it?
class HomeController < ApplicationController
def dashboard
#lookup_booking = ???("/users/1/bookings.json")???
end
end
React dashboard
<%= react_component("Booking", { booking: #lookup_booking})%>
You could make a local request to the Bookings JSON endpoint the same way you'd make any external request - using something like HTTParty or Faraday might work:
#lookup_booking = HTTParty.get(user_bookings_url(1))
But this won't be authenticated, so it'll need the same authentication as any other request.
It's a little weird to do it this way unless whatever is generating the bookings is a separate service, or if you want it to be one. If you're going to be using one codebase, you might want to do something similar to what arieljuod suggested in the comments, and simply share the code.
You could break the BookingsController code into an ActiveSupport::Concern or a module, or a Service Object (or, more simply, a method on the User class) and that would then allow you to cleanly share the code between the BookingsController and HomeController. It might look something like this:
# app/services/lookup_user_bookings.rb
class LookupUserBookings
def self.bookings_as_json(user_id)
# complicated logic to find user bookings goes here...
bookings.as_json
end
end
# bookings_controller.rb
class BookingsController
def index
#bookings = LookupUserBookings.bookings_as_json(current_user)
render json: #bookings
end
end
# home_controller
class HomeController
def dashboard
#bookings = LookupUserBookings.bookings_as_json(current_user)
end
end
# dashboard.html.erb
<%= react_component("Booking", { booking: #bookings.to_json })%>
I have a controller action which renders some JSON with the help of a service class, something like:
def index
render json: ServiceClass.search(params)
end
The ServiceClass's .search method is of the form:
def self.search
new(params).search
end
def search
{
key0: value0,
...,
key1: value1,
}
end
where each value is a separate JSON object obtained through different serializers and helper classes.
I wish to profile the action to get a breakdown of the stack trace to see which methods/queries are taking the longest so I can see where the performance needs improving.
Does anyone know of a good way to do this in Rails 5? The few other solutions I've found on SO are very dated.
My rails app has a database set.
def index
#clubs = Club.all
end
This is my controller.
If i type in my Index.html.erb
<% #clubs.each do |club| %>
<%= club.name %>
<% end %>
I get all the names of my database show in my index view.
What if I just want to pick one or just a couple?
Thru the rails console i can by typing c=Club.find(1) 1 by default takes id=1.
So how can i just display several ID's and not all one the database in the same index.html.erb.
thanks anyway!
Try this:
Let us consider that params[:ids] contains all the ids that belong to the records you want to get.
def index
#clubs = Club.where(id: params[:ids])
end
Fix
The straightforward answer here is to recommend you look at the ActiveRecord methods you can call in your controller; specifically .where:
#app/controllers/clubs_controller.rb
Class ClubsController < ApplicationController
def index
#clubs = Club.where column: "value"
end
end
This will populate the #clubs instance variable with only the records which match that particular condition. Remember, it's your Rails app, so you can do what you want with it.
Of course, it's recommended you stick with convention, but there's nothing stopping you populating specific data into your #clubs variable
--
RESTful
As someone mentioned, you shouldn't be including "filtered" records in an index action. Although I don't agree with this idea personally, the fact remains that Rails is designed to favour convention over configuration - meaning you should really leave the index action as showing all the records
You may wish to create a collection-specific action:
#config/routes.rb
resources :clubs do
collection do
get :best #-> domain.com/clubs/best
end
end
#app/controllers/clubs_controller.rb
Class ClubsController < ApplicationController
def best
#clubs = Club.where attribute: "value"
render "index"
end
end
There are several ways to select a specific record or group of records from the database. For example, you can get a single club with:
#club = Club.find(x)
where x is the id of the club. Then in your view (the .html.erb file), you can simply access the #club object's attributes.
You can also cast a wider net:
#disco_clubs = Club.where(type: "disco") # returns an ActiveRecord Relation
#disco_clubs = Club.where(type: "disco").to_a # returns an array
And then you can iterate over them in the same manner you do in your index.html.erb. Rails has a rich interface for querying the database. Check it out here.
Also note that individual records - such as those selected with the find method - are more commonly used with the show action, which is for displaying a single record. Of course, that's for generic CRUD applications. It't not a hard rule.
change
def index
#clubs = Club.all
end
to this
def index
#clubs = Club.find(insert_a_number_that_is_the_id_of_the_club_you_want)
end
Querying your database is a complex thing and gives you a ton of options so that you can get EXACTLY what you want and put it into your #clubs variable. I suggest reading this part of the rails guide
It should also be noted that if you're only going to query your database for one record then change #clubs to #club so you know what to expect.
What's the best way to construct a where clause using Rails ActiveRecord? For instance, let's say I have a controller action that returns a list of blog posts:
def index
#posts = Post.all
end
Now, let's say I want to be able to pass in a url parameter so that this controller action only returns posts by a specific author:
def index
author_id = params[:author_id]
if author_id.nil?
#posts = Post.all
else
#posts = Post.where("author = ?", author_id)
end
end
This doesn't feel very DRY to me. If I were to add ordering or pagination or worse yet, more optional URL query string params to filter by, this controller action would get very complicated.
How about:
def index
author_id = params[:author_id]
#posts = Post.scoped
#post = #post.where(:author_id => author_id) if author_id.present?
#post = #post.where(:some_other_condition => some_other_value) if some_other_value.present?
end
Post.scoped is essentially a lazy loaded equivalent to Post.all (since Post.all returns an array
immediately, while Post.scoped just returns a relation object). This query won't be executed until
you actually try to iterate over it in the view (by calling .each).
Mmmh, the best approach you want to use can be to spread this in 2 actions
def index
#post = Post.all
end
def get
#post = Post.where("author=?", params[:author_id])
end
IMHO it has more sense if you think about a RESTful API, index means to list all and get (or show) to fetch the requested one and show it!
This question is pretty old but it still comes up high in google in 2019, and also some earlier answers have been deprecated, so I thought I would share a possible solution.
In the model introduce some scopes with a test for the existence of the parameter passed:
class Post
scope :where_author_ids, ->(ids){ where(author_id: ids.split(‘,’)) if ids }
scope :where_topic_ids, ->(ids){ where(topic_id: ids.split(‘,’)) if ids }
Then in the controller you can just put as many filters in as you wish e.g:
def list
#posts = Post.where_author_ids(params[:author_ids])
.where_topic_ids(params[:topic_ids])
.where_other_condition_ids(params[:other_condition_ids])
.order(:created_at)
The parameter can then be a single value or a comma separated list of values, both work fine.
If a param doesn’t exist it simply skips that where clause and doesn’t filter for that particular criteria. If the param exists but its value is an empty string then it will ‘filter out’ everything.
This solution won’t suit every circumstance of course. If you have a view page with several filters on, but upon first opening you want to show all your data instead of no data until you press a ‘submit’ button or similar (as this controller would) then you will have to tweak it slightly.
I’ve had a go at SQL injecting this and rails seems to do a good job of keeping everything secure as far as I can see.
You should model url using nested resources. The expected url would be /authors/1/posts. Think of authors as resources. Read about nested resources in this guide: http://guides.rubyonrails.org/routing.html (scroll to 2.7 - Nested Resources).
Would something like this work?
def get
raise "Bad parameters...why are you doing this?" unless params[:filter].is_a?(Hash)
#post = Post.where(params[:filter])
end
Then you can do something like:
?filter[author_id]=1&filter[post_date]=... etc.
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.