tags:

views:

94

answers:

4

Depending on if a user is signed in or not, I'd like to print a different kind of %body-tag.

This is how I currently do it:

- if defined? @user
  %body(data-account="#{@user.account}")
    %h1 Welcome
    -# all my content
- else
  %body
    %h1 Welcome
    -# all my content

As you can see there's a lot of duplicated code in there. How can I eliminate this? I already tried the following:

- if defined? @user
  %body(data-account="#{@user.account}")
- else
  %body
  %h1 Welcome
  -# all my content

Unfortunately, this doesn't work since HAML interprets it as if the %h1 and the content is part of the else-statement, which of course they aren't.

Any ideas on how to solve this? I run in this problem all the time, so I can't imagine there isn't a simple solution for it.

+6  A: 

I don't think that you can avoid the indentation issue, because of the way HAML autoassigns the "end" statement, but you can instead push the if statement into the body tag itself -

%body{:data_account => (defined? @user ? @user.account : nil)}

as opposed to

%body(data-account="#{@user.account}")

Not super-pretty, but less ugly than repeating the entire block!

bhaibel
I can't imagine HAML having an elegant solution for such a common use-case. If it's really the case I might have to consider moving to regular HTML again.Anyway, thanks for your suggestion. Is it possible to omit the 'data-account'-attribute altogether if @user isn't defined?
Marc
This code'll do that - setting :data_account => nil (which this does, indirectly) will lead HAML to ignore the attribute and generate a plain body tag.
bhaibel
The code returns the following:<body data-account='expression'>So instead of executing the expression and return its value, it just displays the word 'expression'. (And no, the @user.account != 'expression')
Marc
Turns out the solution was to use parentheses for the defined? method.
Marc
+1  A: 
!!!
 - @user = "jed" #=> stubbing out an instance
%html
  %head
    - string = defined?(@user) ? "#{@user}" : nil #=> for demo only, wrap this in a helper method
    %title{'data-account' => string}
  %body
    =yield
Jed Schneider
Marc - without seeing the other answer by @bahabil before i posted, his will work too, or as you asked about not setting the data-account attribute when @user.nil?, this will do that, it just wont put the key in if string is nil. string could be a helper method call in your view helper.
Jed Schneider
Thanks, works great! The parentheses for the defined?-method actually solved the problem I encountered with bhaibel's answer.
Marc
+2  A: 

HAML's elegant solution is helpers

class ApplicationHelper...

  def body_for_user(user, &blk)
    return content_tag(:body, :'data-account' => user.account, &blk) if user
    content_tag(:body, &blk)
  end

end

The ternary operators described above are more than sufficient for this particular situation, but for more complex things, break out the helpers.

Oh, to use this, in your view change %body(...) to = body_for_user @user do

chrisrhoden
I forgot to mention it explicitly in my opening post, but I'm not using Rails but Sinatra, which doesn't have the content_tag method. I can probably mimic it, but I rather keep it as simple as possible.Thanks for the suggestion though. It will be useful in my Rails projects.
Marc
+1  A: 

Write a helper like this:

def body_attributes
  {}.tap do |hash|
    hash[:data] = {}
    hash[:data][:account] = @user.account if @user
    # add any other attributes for the body tag here
  end
end

Then call the helper from the body element:

%body{ body_attributes }
  %h1 Welcome
  -# all my content
Jeremy Weiskotten
See http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#attribute_methods for more information about attribute methods.
Jeremy Weiskotten
I'm probably doing something wrong, but this just returns:<body body_attributes>
Marc
Updated example. You need curly braces around the method name (I used parens).
Jeremy Weiskotten