I am creating a rails app whereby from a record's "show" page, the user is able to cycle through the record's nested resources (similar to a slideshow). For example, from the "show" page, the user will be able to link to the "show" for the first nested resource. From there, the user will be able to link to the "show" page of the next nested resource and so on. As the ids of each nested resource should be ordered smallest to largest, how can I create a link_to that looks for the next highest id (assuming nested resources are being created for multiple records simultaneously they may not necessarily be sequential) of a nested resource within a given record.
Because of Rails magic, you can pass the resource directly to the route helper, and it will use the correct id for that resource. For example:
<% #foo.bars.each do |bar| %>
<%= link_to bar.name, foo_bar_path(#foo, bar) %>
<% end %>
The above assumes that your route file looks something like:
resources :foos do
resources :bars
end
I highly recommend Rails Routing from the Outside In; it's been a very helpful resource for me!
To set the order of the child resource, you could use a scope, like this:
class Bar < ActiveRecord::Base
scope :ordered, -> { order(id: :asc) }
end
And then in your view, call foo.bars.ordered.each do |bar| etc.... Your nested resource will be returned from lowest to highest ID, skipping over any that have been deleted.
I hope this helps with what you were asking.
EDIT
I misread the question. To dynamically generate the "next" id, you could create method, next, on your child class. This answer seems to be something like what you want. Then in your view, you can just call:
<%= link_to "Next", bar_path(current_bar.next) %>
Related
i'v been trying to resolve this error for the past 5 hours and I'm gonna burn my computer if I can't solve this.
undefined method `pushes_path' for #<#:0x007f859d605250> this is the error code I'm getting but i don't understand why.
this is my index.html.erb file inside of the interaction
<%= simple_form_for #push do |f| %>
<%= f.input :payload, as: :text %>
<%= f.input :segment, as: :radio_buttons %>
<%= submit_tag "start the campaign" %>
<% end %>
and this is my interaction controller
class InteractionController < ApplicationController
def index
#push =Push.new
end
end
Push is my table in the database and i'll get the inputs and write them in the database to use them for later one.
and this is my routes file
devise_for :partners
get 'home/index'
get 'segmentation/index'
get 'interaction/index'
root to: "home#index"
i really don't know why its looking for pushes_path, what am i doing wrong?
form_for
The problem you have is that your form_for method is going to try and generate a route based off your #path object. And as such, if you don't have a path created for it, you'll receive the error you're getting:
:url- The URL the form is to be submitted to. This may be represented
in the same way as values passed to url_for or link_to. So for example
you may use a named route directly. When the model is represented by a
string or symbol, as in the example above, if the :url option is not
specified, by default the form will be sent back to the current url
(We will describe below an alternative resource-oriented usage of
form_for in which the URL does not need to be specified explicitly).
The bottom line is that as Rails is object orientated, its built around the assumption that you'll have routes set up to handle the creation of individual objects.
Every time you use form_for, Rails will attempt to construct your routes from your object -- so if you're trying to do the following, it will treat the routes as photo_path etc:
#app/views/pushes/new.html.erb
<%= form_for #push do |f| %>
...
<% end %>
--
Fixes
As #mandeep suggested, there are several fixes you can employ to get this to work:
Firstly, you can just create a route for your push objects:
#config/routes.rb
resources :pushes
Secondly, as you're using a different controller, you'll want to do the following:
#config/routes.rb
resources :interactions
#app/views/pushes/new.html.erb
<%= form_for #push, url: interaction_path do |f| %>
...
<% end %>
This will route your form submission to the interactions controller, rather than the pushes controller that you'll get by default!
Objects
Something to consider when creating Rails-based backends is the object-orientated nature of the framework.
By virtue of being built on Ruby, Rails is centered on objects - a term for a variable, which basically encompasses much more than just a piece of data. Objects, in the case of Rails, are designed to give the application:
Once you understand this, the entire spectrum of Rails functionality becomes apparent. The trick is to realize that everything you do in Rails should be tied to an object. This goes for the controllers too:
--
Ever wondered why you call resources directive in your routes, for a controller? It's because you're creating a set of resourceful routes based for it:
Do you see how it's all object orientated?
This gives you the ability to define the routes for specific controllers etc. The most important thing to note is how this will give you the ability to determine which routes / controller actions your requests should go
--
There's nothing wrong in using the controller setup as you have - the most important thing is to ensure you're able to define the custom URL argument, as to accommodate the non-object based structure
In your index action you have
def index
#push =Push.new
end
and your form has
<%= simple_form_for #push do |f| %>
so your form is looking for /pushes with post verb or pushes_path and you don't have that route in your routes.rb file so to fix this you need to add this in routes.rb:
resources :pushes
Update:
when you add resources :push rails basically creates seven different routes for you. One of which is
POST /pushes pushes#create create a new push
and if you look at the html generated by your form it would be something like:
<form action="/pushes" class="new_push" id="new_push" method="post">
// your fields
</form>
notice the action and verb so when you submit your form your routes are checked for them and since you didn't define them in your routes you were getting this error
And how will i be able to use the params i m getting from this form with this new resource addition?
Your form will take you to pushes_controller create action so first of all you'll have to define them. You can access them simply by params[:pushes] in your controller action but since you want to create a new record so you'll have to permit those attributes, checkout strong parameters
If you are using rails >= 4 then you can do
class PushesController < ApplicationController
def create
#push =Push.new(push_params)
if #push.save
redirect_to #push
else
render 'interaction/index'
end
end
private
def push_params
params.require(:push).permit(:attributes)
end
end
If you are using rails < 4 then instead of permitting these attributes(because strong parameters feature came from rails 4) you'll have to tell rails that these attributes are accessible by writing this in your pushes.rb
attr_accessible :attribute_name
Why it is assuming that its pushes controller?Because of the Push.new creation?
That's because if you look at your index action #push = Push.new so #push contains a push object with nil values(as you have just initialized it) so this is where rails magic comes, rails automatically tries to figure out url of your form and since your #push is only an initialized variable so rails takes you to create action for it. For details you should checkout rails polymorphic urls If you want your form to go to interaction_controller or some other url then you'll have to specify the url option for it
<%= form_for #push, url: "your_url_for_custom_method" %>
// other fields
<% end %>
And in the end you should really read docs
Right now I have this:
resources :sources do
resources :pages
end
And when I want to create a page for a particular resource, I use the following route helper:
new_source_page_path(source_id)
So in the controller I know for which source I need to create the page for.
My routes are getting really messy with deep levels of nesting, and I have decided to avoid doing the nesting. However my doubt is, if I have this:
resources :sources
resources :pages
In the new action of my PagesController, how do I pass or tell that this new object needs to be associated with that particular Source?
You can pass the source_id as a GET param. Create your page#new links like this:
<%= link_to "new page", "/pages/new?source_id=#{source_id}" %>
Then in the page#new action assign that source to the page when you create the new page:
#page = Page.new
#page.source = Source.find params[:source_id]
My rails app has a single CustomerSelectionController, with two actions:
index: which shows a form where the user can enter customer information and
select: which just displays a static page.
class CustomerSelectionController < ApplicationController
def index
end
def select
end
end
I've created an entry in my routes.rb file:
resources :customer_selection
and the form in the index view looks like:
<h1>Customer Selection</h1>
<%= form_tag("customer_selection/select", :method => "get") do %>
<%= submit_tag("Select") %>
<% end %>
however when I click on the Select button in the browser, all I get is:
Unknown action
The action 'show' could not be found for CustomerSelectionController
I'm not sure why it is trying to perform an action called show? I haven't defined or referenced one anywhere.
I'm not sure why it is trying to perform an action called show? I haven't defined or referenced one anywhere.
Yes you have. That's what resources does. It defines the seven default RESTful routes: index, show, new, create, edit, update and destroy. When you route to /customer_selection/select, the route that matches is "/customer_action/:id", or the "show" route. Rails instantiates your controller and attempts to invoke the "show" action on it, passing in an ID of "select".
If you want to add a route in addition to those, you need to explicitly define it, and you should also explicitly state which routes you want if you don't want all seven:
resources :customer_selection, only: %w(index) do
collection { get :select }
# or
# get :select, on: :collection
end
Since you have so few routes, you can also just define them without using resources:
get "/customer_selection" => "customer_selection#index"
get "/customer_select/select"
Note that, in the second route, the "customer_select#select" is implied. In a route with only two segments, Rails will default to "/:controller/:action" if you don't specify a controller/action.
I have a double nested resource:
resources :companies do
resources :orders do
resources :comments
end
end
Am having issues when trying to include a form to create a comment in my orders show view. This is what I've tried:
<%= form_for([#order, #order.comments.build]) do |f| %>
However this gives me a no method error.
Any chance you can recommend the best way to deal with this.
You have to define the company as well. If you write rake routes you can see that you dont have order_comments_path because its double nested, so you will se something like company_order_commments_path which takes minimum two parameters, a company_id and an order_id. So if you really want to use this 3 level nester resource you have to add a #company variable to the form path. Like:
<%= form_for([#company, #order, #order.comments.build] do |f| %>
But in the most cases it's useless to define both company and order to identify an order, so the other option which could be better to add separately another route for the orders and comments, and this makes sense.
In your routes.rb
...
resources :orders do
resources :comments
end
...
So you can manipulate orders, without specifying the company. Also in the most general cases you don't get any important advantage by defining 3 level nested routes.
What is your solution to the problem if you have a model that is both not-nested and nested, such as products:
a "Product" can belong_to say an "Event", and a Product can also just be independent.
This means I can have routes like this:
map.resources :products # /products
map.resources :events do |event|
event.resources :products # /events/1/products
end
How do you handle that in your views properly?
Note: this is for an admin panel. I want to be able to have a "Create Event" page, with a side panel for creating tickets (Product), forms, and checking who's rsvp'd. So you'd click on the "Event Tickets" side panel button, and it'd take you to /events/my-new-event/tickets. But there's also a root "Products" tab for the admin panel, which could list tickets and other random products. The 'tickets' and 'products' views look 90% the same, but the tickets will have some info about the event it belongs to.
It seems like I'd have to have views like this:
products/index.haml
products/show.haml
events/products/index.haml
events/products/show.haml
But that doesn't seem DRY. Or I could have conditionals checking to see if the product had an Event (#product.event.nil?), but then the views would be hard to understand.
How do you deal with these situations?
Thanks so much.
I recommend you to make separate admin controller with it's own views to administrate everything you want. And your customer's logic stayed in products contoller.
I don't have good and clean solution for this problem. Usualy if views doesn't differ to much, I use single view and add some code like #product.event.nil?. You can always add some variable, or helper that will make this method shorter, on example has_event? - then your view will look cleaner. And use it in code like this:
<% if has_event? %>
some html
<% end %>
or for single line:
<%= link_to 'Something special', foo_path if has_event? %>
On the other side, you can create few partials that are the same for both views, put them in some folder, on example /shared/products/... and render them from your views like this:
<%= render :partial => '/shared/products/info' %>
and so on.
But if they don't differ too much, I really would use if version.
The views will be handled by the ProductsController. You can alter the logic in your controller depending on the nesting of the resource.
# app/controller/products_controller.rb
# ...some code...
def index
#event = Event.find_by_id(params[:event_id]) if params[:event_id]
#products = #event ? #event.products : Product.all
end
The view will be handled by the usual product view
# app/views/products/index.html.haml
- unless #products.blank?
- #products.each do |product|
%p= product.some_attribute