Rails / Problems

From WhyNotWiki

Jump to: navigation, search

This is for problems and solutions. And for open problems, for which no solution has been found yet...


Rails / Problems  edit   (Category  edit)


Contents

[edit] [Caveat (category)]: The view gets processed before the layout!

[View-level (category)]

This has caught me off guard a couple times, because I would have expected the layout to get processed first.

Why?

  • Because it's the outermost "view" level, and it has a yield which (I thought) calls the view only when it gets to that point. Wrong. The yield apparently just returns the results that have already been produced by the view.
    • That's the way partials, etc. work ... it doesn't process them until it gets to that point in the "outer" template (the one that called the partial)...
  • Because in the final output (the HTML page), the stuff from the top of the layout (the header) appears first, so you'd think it would be processed first...

I guess the fact that you can use a (now deprecated) @content_for_layout instance variable in the layout to insert the result of rendering the view should have alerted me to the fact that the view has already been processed and stored in an instance variable by this point...

Anyway, this makes it harder to do some things...

Like in the resource_on_demand plugin...

Or, what if you want to have some template "assigns" (like @foo = 'bar') happen for every controller action / every view...?

"Ah," you say, "just put the assigns in a before_filter."

Problem: What if I want the assign to be the result of a helper? Helpers aren't available from the controller.

Arg.

I guess we could just copy and paste the common code to the top of each view, to make sure it always happens, but that's duplication and is bad.

That's what layouts were supposed to solve: the common stuff shared by multiple views. But it kind of defeats half of the purpose, if the layouts are always evaluated after the view...



[edit] How do you make a default value for a request param named params[:questions][:question1] and avoid getting a NoMethodError?

Typically, you access your params and set defaults like this:

@answer = params[:questions] || 'default'    # @answer gets set to 'default' if params[:questions] is nil

But what happens when your param is a "nested name" like "questions[question1]? That's the kind of name that <%= radio_button :questions, :question1, "yes" %> will produce.

But when you do this:

    @answer = params[:questions][:question1] || ''

and params[:questions] is not set, you will get

  NoMethodError
  
    in SurveyController#submit

You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occured while evaluating nil.[]

Best I've come up with so far:

    @answer = (params[:questions] || {:question1 => ''})[:question1] || ''

But that's pretty obfuscated. Slightly more readable alternative:

    if params[:questions]
      @answer = params[:questions][:question1] || ''
    else
      @answer = ''
    end

[edit] [Question (category)]: How do I quickly comment out a line in a template file

This:

<%= #something %>

doesn't work. It causes this error:

SyntaxError in Survey#index

compile error
script/../config/../app/views/survey/_form.rhtml:21: syntax error, unexpected kEND, expecting ')'

One workaround is to split it into multiple lines ,like this:

<%= #something 
%>

But that's really annoying to have to do. Isn't there a better solution to this?

Yes, there is!

<%# something %>

[edit] It seems that the generators violate the DRY principle

rails (project generation), and script/generate

They create duplication.

Fortunately, I see that many of the scripts generated by rails command are just wrappers! In that case, I say the duplication is justified.

I may not have a case. I just need to look more closely at it...

[edit] Obscure namespace behavior/caveats

Something about if you forget that a certain method/property name is built-in and you try to use it as your own, then strange things might happen...

Could be hard to track.

Need a good example...

Forgetting @id is built-in?

Forgetting params is built-in?

[edit] You can't use 'quote' as a field name in your model

Otherwise it will complain about the wrong number of arguments to the quote method.

Name it 'quote_text' or something instead. Sorry.

[edit] What's the best approach for splitting the forms for editing your model across several pages?

Useful when you have a lot of fields in the model and you don't want to overwhelm them with a huge form.

Challenges:

  • If your validation has required fields that are spread across multiple pages, then you will have to store the incomplete object in session variables until you have enough data to pass the validation.
  • You can't just rely on model.valid?/model.save and model.errors because they may be complaining about fields on pages we haven't gotten to yet.

Possible solutions:

  • To the errors problem: call model.valid?, and then call delete() on model.errors for whatever keys you don't care about yet


[edit] I have some methods that I want to be visible to both my view and my controller. How do I do that?

helper methods => only available from view
ApplicationController methods => only available from controllers

Example of when I'd want this: I have a logged_in? or current_logged_in_user method. Obviously need it in controller. In view, I might want to conditionally show 'log out' link only if logged_in? .

One solution (the one I've done so far): duplicate in both places

Another solution: make a module, like SharedBetweenApplicationHelperAndController, that gets mixed into both classes

A better solution: figure out what design choices make this impossible currently. Challenge them (helpers should be available in controller?) or think of a better ideal system (perhaps a third abstraction other than helper or controller).


[edit] text_field(object_name, method_name) expects object.method to return a string. What if it returns another model?

Say you have Order has_one Zip.

You couldn't just do text_field 'order', 'zip' then, could you?

[edit] When should you use flash[:errors] and when should you use error_messages_for (and supply a model with errors)?

flash[:errors] should never be used to display/listic error messages for specific fields on a form. Form validation is usually on the same page as the errors resulting from it anyway, so you wouldn't need to store it in session storage (which is what flash is, I believe--session storage that only lives long enough to make it to the "next page").

One good time for flash[:errors] is if you ever have to redirect someone to another page and it wouldn't be immediately obvious to the user why they are being redirected (insufficient permissions or some other random error), you should use this to explain to them what's going on.

[edit] Not satisfied with layouts yet

What if I want a layout that contains common HTML for all my pages except for 2 sections where it is custom for the page they are on?

Calling yield from my layout will render the page's view at the location in the layout that the yield was called.

But what I'd like to be able to do is to "yield" to two (or more) different views.

Probably that would mean not calling yield but calling "call" on each of two different procs, since I think a method can only have one block associated with its yield calls.

[edit] Validation code becomes fragmented

Some of it is written as methods to class methods, like this:

validates_positiveness_of ...
validates_numericality_of ...

while other parts of it are inside of

def validate
  ...
end

I guess the best solution I can come up with is just to use the convention that those should be kept as close to each other as possible.

[edit] Article metadata

Aliases: Rails / Complaints

Personal tools