pass query parameter to render in rails - ruby-on-rails

In an application a developer written code as
show.html.slim
= render #driver.trips.order("id DESC"), show_driver: false
The application dont have any partials like _driver.html.erb in the same resource.
In that page it shows all trips of a driver except driver name attribute.
another code snippet
= render Trip.order("id DESC"), show_driver: true
Model association is
driver has_many :trips
trip belongs_to :driver
Is it possible to pass query as a param to render? and which route it states?

You can pass whatever you want to render, as long as it defines to_partial_path or is a collection of such objects. ActiveRecord defines it (this is primarily how it is used).
You mentioned that there is no _driver partial, however, what you should be looking for is trips/_trip.

Related

Set Position Value from Index for nested model attributes

How can I set a position field in the attributes for a nested model so that my has_many relationship remembers it's sort order?
I want to set the position from the index so that it reflects the order the relations have been dragged into.
I have a form with Nested fields, using the cocoon gem with JQuery Sortable allowing each field-set to be drag-sortable.
I want to update the order of all the fields on saving the form.
Try to use "act_as_list" gem.
class TodoList < ActiveRecord::Base
has_many :todo_items, -> { order(position: :asc) }
end
class TodoItem < ActiveRecord::Base
belongs_to :todo_list
acts_as_list scope: :todo_list
end
todo_list = TodoList.find(...)
todo_list.todo_items.first.move_to_bottom
todo_list.todo_items.last.move_higher
Refer: https://github.com/swanandp/acts_as_list
If you have the standard CRUD/restful routes and controller actions set up, then all you are wanting to do is to call "update_attributes(:position => 3)" on an instance of the nested class (education in this case).
The usual route to update an education which is nested under "resume" would be
UPDATE /resumes/123/educations/456
so, you'll be making an ajax call to this url. The "UPDATE" method isn't really an update method, it's sort of spoofed by passing through a parameter "_method" = "UPDATE", so your ajax call will need to include this parameter too.
So, basically, on the "finished dragging" event, you're going to be making an ajax call to this url
"/resumes/<%= #resume.id %>/educations/<%= education.id %>"
and passing data like
{"_method": "UPDATE", "education[position]": <new position>}
This should update the position of the education object.
The other remaining issue is that, with acts_as_list, when we change the position of one item, we want the others to adjust their position automatically, too. I'm not sure if acts_as_list does this automatically. Try it.
Ok, non ajaxy version.
Let's assume that your form is a form_for #resume, which means that it will call the resumes#create or resumes#update action.
If all the education rows in your list have a hidden field like this
<%= hidden_field_tag "resume[education_ids][]", education.id %>
then when the form is submitted, they will go through into an array in params[:resume][:education_ids] in the order in which they appear in the page when the form was submitted, which is what you want.
The association gives you the setter method Resume#education_ids, allowing you to set the associations, in order, this way.
Ie, your update action will (if it's a normal update action) be saying something like
#resume = Resume.find_by_id(params[:id])
if #resume.update_attributes(params[:resume])
...
in this case, this will be saying
#resume.update_attributes(:education_ids => [5,6,2,1])
which is like saying "set my educations to be those with ids 5,6,2,1, in that order.
CAVEAT: in my version of rails (this might be fixed in subsequent version), if you use this _ids method, and it already has associations, but in a different order, it WILL NOT reorder them. Give it a go and see what happens.
This can't be the best way to do it, but I have got this working in my controller.
p = 1
experiences = []
params[:user][:resume_attributes][:experiences_attributes].each do |e|
e = e.last.merge(:position=>p)
experiences << e
p = p + 1
end
params[:user][:resume_attributes][:experiences_attributes] = experiences
which at least illustrates what i want to achieve.

Ruby on Rails 4 find object by id

I have the following show-view, where i display basic information about Product and display other User's Products.
<h1>Book <%= #product.name %></h1>
<% #products.each do |product| %>
<ul>
<%= product.name %>
<%= link_to "Make offer", {controller: "offers", :action => 'create', id: product.id } %>
</ul>
Controller
def show
#product = current_user.products.find(params[:id])
#products = Product.all
end
My goal is to make Offer between two Products.
I created Offer model and methods for making Offers:
class Offer < ActiveRecord::Base
belongs_to :product
belongs_to :exchanger, class_name: "Product", foreign_key: "exchanger_id"
validates :product_id, :exchanger_id, presence: true
def self.request(product, exchanger)
unless product == exchanger or Offer.exists?(product, exchanger)
transaction do
create(product: product, exchanger: exchanger, status: "oczekujace")
create(product: exchanger, exchanger: product, status: "oferta")
end
end
#other methods
end
Making offers is working, because I checked it in Console.
My problem is in OffersController:
class OffersController < ApplicationController
before_filter :setup_products
def create
Offer.request(#prod, #exchanger)
redirect_to root_path
end
private
def setup_products
#prod = current_user.products.find(1)
#exchanger = Product.find_by_id(params[:id])
end
end
Problem is with a following line (using link in show-page for products with different id's than 1 works):
#prod = current_user.products.find(1)
But I don't know how to find object in Db for actual product which my show-page shows. Not only for id = 1.
I don't know how to find this object in database.
I don't know the specific answer to your question, but perhaps if I explain what you need to look at, your solution will arise:
Find
Rails isn't magic - it uses ActiveRecord (which is an ORM - Object-Relation Mapper), which means every time you fire a query (through find), your ORM (ActiveRecord) will search the relevant database data for you
The problem you have is that although you're using the correct syntax for your lookup, you may not have a record with an id of 1 in your db.
current_user.products.find(1) tells ActiveRecord to scope the query around the current user, and their products. So you'll get something like like this:
SELECT * FROM 'products' WHERE user_id = '15' AND id = '1'
Objects
Further, you have to remember that Ruby (and Rails by virtue of being built on Ruby) is an object orientated language. This means that everything you load / interact with in the language should be based on an object
The problem you have is you're not associating your object to your Rails framework correctly. What I mean here is described below, but essentially, if you build your Rails framework correctly, it will give you the ability to associate your objects with each other, allowing you to call the various products you need from your offer
This is a simplistic way of looking at it, of course. You'll want to look at this diagram to see how it works:
Bottom line - try treating your application like a series of objects, rather than a logical flow. This will help you appreciate the various associations etc that you need to get it moving forward
Resources
You mention you can't show the product on your show page for an id other than one. I think the problem is really about how to get your show action to work.
If this is the case, let me explain...
Rails is resource-based, meaning that everything you do / create needs to be centred around a resource (object) of some sort. The problem is many people don't know this, and consequently complicate their controller structure for no reason:
Above is the typical "CRUD" routing structure for Rails-based resources. This should demonstrate the way that Rails will typically be constructed -- around resources
--
Further, Rails is built on the MVC programming pattern - meaning you need to use your controller to populate a series of data objects for use in your application.
To this end, if you load a resource, and want to populate it with resourceful information of another object - you need to make sure you have set up the data objects in a way to ensure you can look them up correctly, which either means passing the data through your routes or using a persistent data-type, such as cookies or sessions
The problem you have is you need to pass the product id to your controller somehow. How I'd do that is as follows (using nested resources):
#config/routes.rb
resources :offers do
resources :products #-> domain.com/offers/2/products
end
This will give you the ability to load the products controller with the variables params[:id] for the product, and params[:offer_id] for your Offer made available:
#app/controllers/products_controller.rb
Class ProductsController < ApplicationController
def show
#offer = Offer.find params[:offer_id]
#product = Product.find params[:id]
end
end

How to reduce number of calls to a helper method

In my view, I need a User object to display a few different properties. There is an instance variable #comments that's being sent from the controller. I loop through the comments and get the User information through a helper method in order to reduce db calls.
Here is the helper method:
def user(id)
if #user.blank? == false && id == #user.id
return #user
else
return #user = User.find(id)
end
end
And in the view, I display the details as follows:
<h4> <%=user(comment.user_id).name%> </h4>
<p><%=user(comment.user_id).bio%></p>
<p><%=user(comment.user_id).long_bio%></p>
<p><%=user(comment.user_id).email%></p>
<hr>
<p><%=user(comment.admin_id).bio%></p>
<p><%=user(comment.admin_id).long_bio%></p>
<p><%=user(comment.admin_id).email%></p>
I was told that assigning a variable in the view is bad practice and hence I am calling the helper method multiple times instead of assigning the returned User object.
Is there a better way to do this?
I think you are overcomplicating things here.
Let's say you have a user model
class User < ActiveRecord::Base
has_many :comments
end
an admin model
class Admin < User
end
a comment model
class Comment < ActiveRecord::Base
belongs_to :user
end
Now you only need a type column in your users table and you can do things like this:
Admin.all (All users with type "Admin")
User.all (Really all users including type "Admin" and all other types)
and for every comment you can just use
comment.user.bio
and it doesn't matter if it's an admin or not.
See http://www.therailworld.com/posts/18-Single-Table-Inheritance-with-Rails for example
Additional info: To reduce db calls in general(N+1 queries) watch http://railscasts.com/episodes/372-bullet
It's perfectly fine to pass models to your view and build the data on the view off of the data contained in the model. Keep in mind that I'm not entirely certain how you want your page to work, but one option you may have is to use a partial view and pass it the user object. This allows you to still only have the one model in your partial view without setting additional variables.
Also, without knowing what kind of database you're using or if your models have any associations, and assuming that you're doing some input validation, you may not need this helper method and may be able to lean on your ORM to get the user object.
For Example:
<%= comment.user.age %>
This isn't any more efficient than what you've currently got, but it certainly makes the code look cleaner.
Another alternative: set a user variable in the view. You're not performing logic in your view at this point, you're simply storing some data to the heap for later use.

Best Practice for Helper/Partial that render a Model driven select

Let's assume I have a model Product, and I want a drop-down select box that contains all products. This drop-down is used in several views, so it is going to be created by a helper method. Where is the 'best practice' location to get the select options from Product? Do I set #products = Product.all in every controller action that needs to show the drop-down, or do I make the helper method self contained by having it call Product.all? Does the answer change if I am dealing with a partial, or if I am filtering the products (i.e. Product.in_category(#category))? MVC says use the controller, but DRY says use the helper.
Look at the collection_select form helper that's built in. You can pass in different collections (Product.all, Product.) as and where needed in different views.
collection_select
From the link:
collection_select(object, method, collection, value_method,
text_method, options = {}, html_options = {})
Returns and tags for the collection of existing
return values of method for object‘s class. The value returned from
calling method on the instance object will be selected. If calling
method returns nil, no selection is made without including :prompt or
:include_blank in the options hash.
The :value_method and :text_method parameters are methods to be called
on each member of collection. The return values are used as the value
attribute and contents of each tag, respectively. They can
also be any object that responds to call, such as a proc, that will be
called for each member of the collection to retrieve the value/text.
Example object structure for use with this method:
class Post < ActiveRecord::Base belongs_to :author end
class Author < ActiveRecord::Base has_many :posts def
name_with_initial
"#{first_name.first}. #{last_name}" end end
Sample usage (selecting the associated Author for an instance of Post,
#post):
collection_select(:post, :author_id, Author.all, :id,
:name_with_initial, prompt: true)
If #post.author_id is already 1, this would return:
Please
select D. Heinemeier
Hansson D. Thomas M. Clark
In my opinion, Controller should decide what data the user sees. How the user sees it can be decided by the view or by the helper.
So i would advise you to put
#products = Product.all
or
Product.in_category(#category)
in your controller
Any kind of filter you apply should be done in the controller as well
With rails being a model-view-controller (MVC) framework, you're going to want that logic to be on the model. Having some method that returns the options for your select would probably be best (though, take that with a grain of salt because these things change a lot with application). Something I might try would be along the lines of:
class Product < ActiveRecord::Base
def self.get_select_options(category=nil)
if category.nil?
Product.all
else
Product.in_category(category)
end
end
end
... which you could then call with Product.get_select_options or Product.get_select_options(#category)

Create join table record automatically, rails 3

Right now my Posts model has_many :tags, :through => :tag_joins
When I add tags, while creating a post, the tag_join records are automatically created.
Now here is what I'm trying to accomplish: While viewing the show view of posts I want to be able to add a new tag.
I tried #post.tag = Tag.new didn't work (returns a "nomethoderror" for tag=)
So I'm trying to figure out how I can add tags and still create those joins automatically.
I am using accepts_nested_attributes etc.
UPDATE: I originally asked how to do this on the index view, but I have changed it to the show view - because I expect it to be a little easier.
You're not too far off with #posts.tags = Tag.new. Here's a couple of ways to do it;
#post.tags << Tag.create(params[:tag])
#post.tags.create params[:tag]
I see a couple of approaches to this problem.. One is to pass through the id of the post with the tag form using either a hidden_field or by using nested routes for tags. Then you can use that in the controller to retrieve the post and use a syntax similar to above.
While that would work, the problem is that it's a bit ugly.. It means your tag controller would be dealing with finding a post (which isn't necessarily wrong, but it shouldn't need to worry about posts. Unless tags can only be associated with posts, that is).
The more graceful way of dealing with it would be to make the form you're showing be a form for the post instance, not a tag. Then you could use nested attributes to create the tag as part of a post.
Take a look at the build_xxx or create_xxx methods that the association (belongs_to, has_many etc) add to the models. You need to create your tag through the post for rails to 'connect' it automatically.
The key observation here is the difference between .new and .create. For my Devour.space application, I was running into the same issue. If you create the object in memory using:
tag = #post.tags.new(tag_params)
tag.save
There will be no tag_joins entry saved to the database. #post.tags will not return your new tag. You must use .create at the moment of instantiation or the association will not be recorded in the JOIN table:
tag = #post.tags.create(tag_params)
#post.tags.last # tag
In my situation, this required a change in how my create action handled requests and errors:
has_many :deck_shares
has_many :decks, through: :deck_shares
....
deck = current_user.decks.new(deck_params)
if deck.save # Does not create entry in DeckShares table
render json: deck
else
render json: deck.errors, as: :unprocessable_entity
end
This became:
begin
deck = current_user.decks.create(deck_params) # creates DeckShare
rescue Exception => e
render json: e, as: :unprocessable_entity
end
render json: deck unless e

Resources