Whiny Nils in Rails - ruby-on-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') %>

Related

How can I create a route and use a path to enter the first step in a mulistep form with the Wicked gem?

I have tried using the Wicked gem 3 different times over the past 8 years. Each time, I have given up for the same reason. I'm trying again, because if I understand it, I think it will be perfect for my use case.
My main problem is that I don't understand how to actually begin the wizard. With the example used in the gem, it is an after_registration event that already has an associated user object. That is not helpful, nor do I think that example would be helpful in the majority of use cases.
There is another example about building a Product in multiple steps. However, the author fails to adequately explain the routing. From https://github.com/zombocom/wicked/wiki/Building-Partial-Objects-Step-by-Step:
Since Wicked uses our :id parameter we will need to have a route that also includes :product_id for instance /products/:product_id/build/:id. This is one way to generate that route:
resources :products do
resources :build, controller: 'products/build'
end
This also means to get to the create action we don't have a product_id yet so we can either create this object in another controller and redirect to the wizard, or we can use a route with a placeholder product_id such as [POST] /products/building/build in order to hit this create action.
OK, I have no idea what the second part of the sentence means as far as placeholder product_id and that route name of /products/building/build. I spent 2 hours trying that and just moved on to a blank create form.
...we can either create this object in another controller and redirect to the wizard
That's what I'm trying to do upon successful save of the #product object.
redirect_to product_by_interchange_path(#product, :step1)
That doesn't work. raise InvalidStepError if the_step.nil? Says my step is nil. It's not.
redirect_to product_by_interchange_path(#product, step: :step1)
Same thing.
redirect_to product_by_interchange_path(:step1)
That's an exact mirror of the 8 year old example app. But of course #product isn't in a session variable like current_user is, so in this case the error is that there's no Product with an id of :step1.
Please help! I am missing something very, very basic here but I very much need to persist.
OK I have finally figured this out. Here's what I did:
First of all, I changed my controller back to a plain old ApplicationController and used the include include Wicked::Wizard. I don't know if that did anything, but the newer example was laid out like the old.
I was really screwed up by :id. I'm thinking :id is generally my object ID. I had a set_product private method in my controller, and it was failing. When I finally figured out that :id was the actual step itself, that led me to change my path in the redirect.
I changed the redirect from product_by_interchange_path(#product, :select_vehicle) to product_by_interchange_path(:select_vehicle, product_id: #product.id)
I got rid of my set_product. Just while I was trying to eliminate confusion.
I changed my finder calls in the wizard to use :product_id instead of :id.
It works now. I still don't understand how I could have stubbed out a route with a placeholder product_id, that's still a mystery. But this is fine and it works.

generated Set_model path in rails controller

I'm used scaffold_controller to generate a controller for a model I have. In my view, I have the following link to
link_to, 'Like', like_path(param: 'param')
In my controller that was generated, I have a private method set_like which is called and I can figure out why. I just want the link to go to the likes#new path, but it's going to the set_like method first. I feel like this is a new rails thing and I'm not sure why. Any ideas?
Looks like you want to use "new_like_path" rather than "like_path".
You probably would benefit from reading this closely: http://guides.rubyonrails.org/routing.html
When you use "like_path" you are linking to an existing record, whose id you should pass to the "like_path" route generator. Like:
like_path(2) # link to like with id==2

How to use Rails named route helpers with parameters?

given this route
match 'posts/hello/:name/:title' => 'post#show', :as => :hello
what are the ways that I can call hello_path ?
if i call hello_path(#post), what does it try to do?
I was hoping that the :name and :title files will bind to the path automatically but it seems that rails only know how to get the :id out of the model object.
instead, it only works if I call it like
<%= link_to "link2", hello_url(:name=> #post.name, :title=>#post.title) %>
(lack of proper documentation is really killing me)
To answer your two questions:
At the command line, runrake routes to see what routes there are in
your app. It will show you all the ways you can use the named routes,
just add "_path" or "_url" to the name of the route which is shown on
the left.
Calling hello_path(#post) will generate a URL to the
show page for that hello instance.
The way you are calling it is the norm:
<%= link_to "link2", hello_url(:name=> #post.name, :title=>#post.title) %>
However, this may work too:
<%= link_to "link2", hello_url(#post.name, #post.title) %>
Here is some documentation (other than the Rails API) which should help.
http://guides.rubyonrails.org/routing.html
To answer your question of "what does hello_path try to do?"
hello_path knows how many parameters it's supposed to get. This is from counting the named parameters in config/routes. It will accept either a hash or a list of arguments. If you give it a hash, the keys must match the names of the URL parameters. If you give it a list of arguments, it'll just match them up by position - the first argument with the first named parameter.
Then, it will call to_param on each parameter individually before joining them all together (see code here, 4.0 branch).
If you pass in an object when it's expecting 2 or more params, it won't even get around to calling to_param on the object. That's when you get errors with no stack trace that say something like
No route matches {:controller=>"posts", :action=>"show", :id=>#<Post ...>}
Working with 1 named parameter
If you've only got one named parameter, things are pretty straightforward. If you need to look up your posts by name instead of id, you can just redefine to_param
class Post < ActiveRecord::Base
...
def to_param
name
end
end
Working with multiple named parameters
But if the URL has more than one named parameter in it, then redefining to_param isn't enough. Let's say you tried this:
# app/models/post.rb
class Post < ActiveRecord::Base
...
def to_param
{name: name, title: title}
end
end
# app/views/posts/index.html.erb
<%= post_path(post) %>
In this case, you'll get a routing error because you're not passing in enough arguments to post_path (see above). To get around this, I just call to_param explicitly:
# app/views/posts/index.html.erb
<%= post_path(post.to_param) %>
This is a little less slick than most Rails routing magic, but works perfectly well. If you later change the way you're looking up Posts, all you have to do is redefine to_param. No need to worry about all the places you've called post_path
Under the hood
The relevant code to look at is actionpack/lib/action_dispatch/routing
The other answers (at the time of this writing) are fine, but your reply to GregT's answer shows a lack of understand about Rails, which is fine -- we've all been there.
Specifically, three of the key principles behind Rails: convention over configuration, the model-view-controller architecture (MVC), and REST. It's stuff at the beginning of every beginning Rails book. Beginners often think they can just jump to the first chapter with code, but Rails differs from many other topics in that the first chapters explain important concepts and aren't just intro chapter filler. Because Rails isn't "code", it's a "framework of code".
"Convention over configuration" means if you follow certain conventions then you benefit from behaviors baked into Rails. Routing is one of those areas, if not the biggest, where convention benefits the developer although it is entirely configurable.
Paths following a specific routing format, are parsed into the controller, action and possibly an id, format, and additional parameters. By default and at minimum, a Rails (and also a Sinatra) path takes the following format and order:
/controller_name/action_name
It's a little more complicated than that, with more options, and it looks more like this in actuality:
/controller_name/action_name/(id)(.format)(?param=value)(&...)
...but it's more detail than is necessary for this answer.
The controller is the C in MVC, or the class that handles the request. The action is one of the seven RESTful actions (index, show, new, create, edit, update, and destroy) within that controller. Not all actions require an id (index, new and create) and not all of them are get requests (requests that generate a view, for instance destroy, create and update don't have views).
Putting it all together we see this example:
/articles/edit/1
...will route the request to the 'edit' action in the ArticlesController controller passing along id 1. It's expected that the controller will do some housekeeping, like authentication and authorization, then retrieve Article model (MCV) with ID 1 and pass it along to the "edit" view (MCV).
It's best, especially for new Rails developers, to stick to these conventions and perhaps add a few additional actions beyond those provided by REST.
You can step outside this convention by configuring in your routes.rb file an alternative routing scheme, not following REST, etc., but you'll be like a salmon swimming upstream -- it's a lot harder than going with the flow. If you go down that path (pun) you'll make a lot of additional work for yourself, probably reinvent the wheel somewhat, and lose advantages provided by the Rails framework. If you find yourself in that situation for whatever reason, perhaps Rails isn't the right tool for your job.
You can also call it like this
hello_path(#post.name, #post.title)
Hope it helps.

New controler method requires ID in the parameters hash

I have created a new method in one of the project's controllers that it will allow the users to search using SearchLogic's gem.
The method is called search_entries and it is of course accompanied by a corresponding view. But when I hit the "Submit" button Rails complains that "Couldn't find Entry with ID=search_entries" (where Entry is the model.) In the params hash there is an ID with value "search_entries".
When I place the code from the search_enrties view inside the index template everything works without a problem (and no, the params hash does not have an ID...)
I am sure the problem is caused from a lack of understanding of how RoR works.
Thank you in advance for your time,
Angelos Arampatzis
i believe this is caused by the route entries in your config/routes.rb file as it's using RESTful actions. try to add a :search_entries => :get into the :collection of your resource.
more info can be found here: http://guides.rubyonrails.org/routing.html#adding-more-restful-actions
hope it helps =)

on Rails, how does form_for and validate_presence_of work hand in hand?

when we have validate_presence_of :name in the model and then when we put in the create action that we re-render 'new', then the form_for will populate the fields, and error_messages_for 'story' will have the correct error message.
this is really great, and and the same time, this looks like magic... i found that many books don't explain how the magic occur. is it by some global variable?
when the form_for is called... is it using the #story that came back from the #story.save, instead of the #story = Story.new from the new action? so if i use :story for the form_for, the fields won't be populated on error?
sometimes i feel that i am playing magic when using Ruby on Rails, except I don't know how the magic happens... kind of like if I make the rabbit appear, but I don't know how I did it. So I really want to know the inner workings of Rails.
Yes, Rails is very magical. Unfortunately these are just things that you have to learn to live with, and once you get used to the conventions you get to use the magic to do some very complicated things with great ease.
There are three separate issues here that are relatively simple individually but look very magical when you take it all in at once. Let's break them down one by one:
When validations fail, they disallow the model object from being saved and add errors to the object.errors hash.
When you run #story.save, it kicks off all the validations. Since #story.name is blank, validates_presence_of :name adds an error to the object.
Instance variables in the controller are available to the views they render.
So, yes, it is the same #story that the view has access to - the one that is invalid and has error information attached to it.
form_for takes many forms, and the one you're using is very smart
The form_for tag in your view probably looks like this:
<%= form_for #story do |story| =>
This is a special version of form_for that infers all kinds of information from the object passed in and renders the form appropriately. #story has some of its fields populated because of the line
#story = params[:story]
in your controller, so it goes ahead and fills in those fields for you. It does some other things, too - for example, it checks #story.new_record? to see if it should use the POST HTTP method (RESTful create) or the PUT method (RESTful update).
In summary, there are lots of little bits of magic to learn, but once you do the big magic is much easier to understand. Good luck!

Resources