query strings are persisting across all controllers in error - ruby-on-rails

I have a rails 3 application where I am passing some string queries like this:
<%= link_to "All", work_orders_path(params.merge({:status_filter => "E", :sort_filter => params[:sort_filter]})) %>
Everything works persistence wise in my views for work_order which is what these parameters are intended for (sorting and filtering existing work orders.)
The problem that I'm having is that when I go to add a new work order with a link_to, or attempt a link_to to another controller, the :status_filter and :sort_filter both are persisting, causing a routing error.
For example when I want to hit the index to view all technicians (of which these are not applicable I get
No route matches {:sort_filter=>nil, :status_filter=>"E",
:controller=>"technicians"}
I've looked everywhere for a solution but as of yet I've been unable to come up with anything. I've tried params.delete, no dice. I know there must be an easy way to clear these from the URL in the link_to but I can't figure it out.

I figured out that these links for all my navigation are in my application.html.erb, so any params I use there are carried across the entire app. I still haven't figured out why. I moved these links to the index.html.erb file in work_orders and now everything else works.

Related

Rails routes. How does the process explicitly work?

I've created a few apps using Ruby on Rails now, but there's a few concepts that I haven't quite been able to get my head around.
One of these is how does the 'routing' process work? By that I mean, where a user enters a URL string and Rails serves the relevant assets in response to the URL.
Here's what I think is happening:
A user browses to the server using their browser:
http://0.0.0.0:3000
They then prepend their URL with a string:
http://0.0.0.0:3000/entries/view_all
Rails' 'routes.rb' file specifies what the string should actually relate to via directives:
match "/entries/view_all" => "entries#view_all"
This above directive says that when the string "/entries/view_all" is prepended to a URL, execute the method view_all, found in the file 'entries_controller.rb'.
The view_all method is executed:
def view_all
#entries = Entry.all(:order => 'created_at DESC')
end
It assigns all the entries from the table 'Entry' to the constant #entries, in descending order.
Rails then magically knows to serve the user the 'view_all.html.erb'.
An each loop inside the 'view_all.html.erb' displays relevant information from the 'Entry' table:
<% #entries.each do |entry| %>
<h1><%= entry.title %></h1>
<p><%= entry.content %></p>
<p><em>Posted at <%= entry.created_at %></em></p>
<% end %>
My questions are as follows:
How wrong is my concept of how things work?
In step #3 above, how does Rails actually know the view_all method is found inside 'entries_controller.rb'? The directive was entries#view_all, not entries#view_all. Does Rails automatically match the start of the controller file names inside the 'controllers' directory, and ignore the '_controller.rb'?
In step #6 above, how does Rails 'magically' know to serve the 'view_all.html.erb' view? Is it similar to how I think it works in question #2? Does Rails match the 'view_all' part of the file name with the name of the method found in 'entries_controller.rb'?
How is the object/constant #entries, and all its methods, "passed on" to 'view_all.html.erb' from 'entries_controller.rb'?
In response to (2) and (3) - Rails emphasizes convention over configuration, which results in seemingly magical coupling between the router, controllers, and view templates. For example, the router knows that entries refers to an EntriesController class because there's a line in ActionDispatch::Routing::RouteSet:
def controller_reference(controller_param)
controller_name = "#{controller_param.camelize}Controller"
...
It's not magic - the "Controller" word is hard coded. It's just what Rails was programmed to expect given your inputs. And it's like this all over the place, which can be a bit daunting to comprehend when you're starting out with it (have a look at Ember.js for even more daunting magic like this).
In response to (4): Rails copies your individual instance variables into an ActionView instance. There is fairly significant contention in the community over whether it should really be doing this, but for now that's how it works, and you should keep that in mind when you are writing your controller actions. You don't want a lot of overhead in copying numerous or bloated objects that you don't need in the view.
You got it, actually. It's all name matched. The Entry model matches up with the entries_controller which matches up with the entries view. The name of the specific view correlates with the controllers action.
#entries is an instance variable (called such as it's an instance of the whole model), as is any variable that starts with an #. Those variables in the controller are the ones that are available to the corresponding view.
A neat trick to use is the _enrty partial.
_entry.html.erb
<h1><%= entry.title %></h1>
<p><%= entry.content %></p>
<p><em>Posted at <%= entry.created_at %></em></p>
Then in places that you want to call the index (your view_all is typically labeled as index), or parts there of, you can <%= render #entries %>
But yeah, a lot of the magic of rails is in the matchy matchy naming conventions. There are ways around that, as everything is customizable, but that sums it up. Cheers!
1) How wrong is my concept of how things work?
Thats a pretty good way of thinking about how the process works
2) In step (3), how does rails actually know the 'view_all' method is found inside 'entries_controller.rb'? The directive was 'entries#view_all', not 'entries#view_all'. Does rails automatically match the start of the controller file names inside the 'controllers' directory, and ignore the '_controller.rb'?
Rails abides by a directive called "Convention over configuration", this means Rails will behave a certain way, as long as you give it instructions in the way it expects. So in your query above, because you specify the "entries" part of your controller it knows to go look in "entries_controller" for the "view_all" action
3) In step (6) how does rails 'magically' know to serve the 'view_all.html.erb' view? Is it similar to how I think it works in question 2? Does rails match the 'view_all' part of the file name with the name of the method found in 'entries_controller.rb'?
Convention over configuration aka "magic". Once Rails executes an action, it will look for the matching template to execute based on your request. if you made a request for an json page (by altering your request headers for example), it would look for view.json.erb. If you left out that template it would throw an error unless at the end of the action you used a render call to tell it to do something else
4) How is the object/constant '#entries', and all its methods, 'passed on' to 'view_all.html.erb' from 'entries_controller.rb'?
It just is :D, or are you asking to see the rails source code that is responsible for that?
ActionView (Views) and ActionController(Controllers) both inherit from Actionpack, so I imagine its not that hard to share variables between the two.

Creating links using link_to for hierarchical model

Okay, so I am new to Rails and am creating a project management system with the framework. For a couple of my models, views, and controllers I used scaffolding and had no problems. For other parts, I coded all of the parts myself.
So as an overview of my project, at the root of it all you can have many projects. Within all of these projects you can create multiple to-do lists. Within each to-do list, you can have multiple tasks. This is what I meant by "hierarchical" in the title.
I just created my lists page, and when I go to the URL directly in my browser (ex: http://localhost:3000/projects/3/lists/20/tasks/1) the task is displayed correctly. However, I do not know how to format my link that is in one of my to-do list views (the tasks are usually shown below the to-do list but now I want them to show on their own view).
Here is the code I currently have:
<%= link_to "#{task.description}", project_list_tasks_url(#list.id,task.id) %>
I know that the link_to "#{task.description}" is correct because I tried using it with a static URL (Google or something), but project_list_tasks_url(#list.id,task.id) is where I'm having trouble.
Can anybody help me out? I can provide as much code as you'd like from my to-do list or task controllers and views.
A couple of tips to help make routing less confusing. It can be a bit unnerving to get used to.
Routing Rule #1
Always check the output of rake routes to be sure how to call your various routing methods. You might think you know how your routes will play out by looking at routes.rb but you won't know until you look at the compiled routes table.
In your case you're expecting a route with the format:
/projects/:project_id/lists/:list_id/tasks/:id
Be sure that's the case. If it is, your call should look like:
project_list_task_path(#project, #list, task)
Note that the arguments here are :project_id, :list_id and :id, so all three are required in this case. Any in brackets in the path specification can be ignored, like :format usually is.
Routing Rule #2
Use the _path methods unless you strictly require the full URL. They're shorter and the output is easier to read and debug. They also don't inadvertently flip the URL in the browser and cause session problems if you don't properly differentiate between www.mysite.com and site.com.
Routing Rule #3
Don't forget there's a huge difference between #project and #project.id when it's supplied to a routing path method.
The router will always call the to_param method if it's available and this can be over-ridden in your model to produce pretty or friendly URLs. id is for your database and your database alone. to_param is for routing but you shouldn't be calling it manually unless you're doing something exceptionally irregular.
You generally should not nest resources more than one level deep, but putting that aside, the link_to format should be:
link_to task.description, project_list_task_path(#project, #list, task)
i.e. project_link_tasks_url should be project_link_task_url, and you have to pass the #project as the first argument (I'm assuming that your project is named #project). I've switched _url to _path so you can just pass the objects themselves as arguments rather than their ids.
See the documentation on creating paths and URLs from objects for details.

Where does Kaminari calculate it's page url links?

I'm working with a custom model that I made that pretends to be a table based ActiveRecord object. It is instead backed by a SQL view. There's some trickery behind the scenes.
The problem is that pagination links with Kaminari aren't working correctly. They are displaying the correct page numbers and number of page links just to the wrong route.
I need to know where in the Kaminari source it figures out the route to the object it has been set to paginate on. Or if someone is familiar with the active record method name that would be used to calculate that.
I've been searching through the source code and I can't figure it out.
UPDATE
My actual problem was caused by my routes file. I had this entry -
match 'dashboard' => 'users#start', :as => 'user_root'
Instead of my links referring to /users/start?page=x they refereed to /users/dashboard?page=x. I was still at the same controller action but I made view of the page that needed the pagination links from /users/start so I would think that it would use that and not this match rule. Removed the match rule and I'm all set!
The comment above Kaminari::ActionViewExtension#paginate says it accepts :params key in options hash, so you can use it to override the URL parameters:
paginate #bloops, params: {controller: "foos", action: "index"}
By default it assumes that we're paginating the "current" page, which is the usual case.
From Kaminari::Helpers::Tag#initialize:
#params = #options[:params] ? template.params.merge(#options.delete :params) : template.params

How do controller actions know which REST operation to perform?

When I'm at /profile/new, for example, and I submit a form to create a profile, Rails knows to perform a POST operation; and when I update that profile from /profile/edit/1, Rails knows to perform a PUT operation... My question is, how does it know to do that?
I can't understand how this works past the controller. What exactly is going on in the background? I've dug around a little bit and I know ActiveRecord and ActiveResource? are involved, but I'd like to know the details. I've only been around since Rails 2.2 and every resource I find seems to teach by example. I'm interested in understanding how things work at a lower level, but there's nothing to guide me through learning by reading apis & source code.
You know how a view page of new page or edit page in a user's scaffold looks right?
form_for(#user) # something like that
So this is a helper method which you can find inside action_view/helper .. file
Basically the form rendering for new and edit will be decided by this form_for method, what this form_for method will do is (I just made some bullet points)
1) It will check what type of input you gave in your form_for
(check api for different ways of using form_for helper)
2) It will decide the the html options based on the below code
if object.respond_to?(:new_record?) && object.new_record?
{ :class => dom_class(object, :new), :id => dom_id(object), :method => :post } # for new
else
{ :class => dom_class(object, :edit), :id => dom_id(object, :edit), :method => :put } # for edit
end
3) It will do one more thing for edit page it will add a hidden field which will have user's id value in it.
Please let me know if you need some more details. I will update my answer accordingly.
The best I can tell, it relies completely on what action you're performing in the controller. For instance, #object.new would be a POST action, where-as #object.find([:params]) would be a GET action, based upon RESTful practice.
I could be off-base, as I'm in the same boat as you but that's my interpretation of it.

Whiny Nils in Rails

So I generate my scaffold in rails, and it creates the usual CRUD files. In my View, I copy over the form found in new.html.erb and paste it over at index.html.erb, so I can create a new record from my index. When I do that, I get the following error consistently, no matter what I do.
Called id for nil, which would mistakenly be 4 -- if you really wanted
the id of nil, use object_id
I got tired of searching all over the web for answers, and just learned it's called a whiny nil (not much help). I tried renaming my instance variables, capitalization, using global variables, etc. but it's frustrating that Rails doesn't have an error documentation library. Can anyone help?
Do you have all the required objects present in your controller? Looks like it's calling something.id, but that something does not exist in your index action. Look at the whole error message - it should be saying what line is causing it, then check that line in source files for the missing variable.
In your controller, you need to add the code found in new into index (I guess it is something like #model = Model.new). Or better: Make a private method which has name expose_new or something like that, move the common code down there and add before_filter :expose_new, :only => [:index, :new].
Just a side note..
If I were you, I'd make a partial out of your form in new, and render that in both index and new (and quite possible edit if them all are equal) so you don't need to copy paste it in.
So, you'll end up with one _form.html.erb which includes the form, and in new and index, you have <%= render('form') %>

Resources