#125
Sep 01, 2008

Dynamic Layouts

Discover how to dynamically change the layout using content in the database. Check out this episode for details.
Download (22.8 MB, 11:56)
alternative download for iPod & Apple TV (15.8 MB, 11:56)

Resources

script/generate migration add_layout_to_blogs layout_name:string custom_layout_content:text
rake db:migrate
gem install liquid
# controllers/application.rb
def load_blog
  @current_blog = Blog.find_by_subdomain(current_subdomain)
  if @current_blog.nil?
    flash[:error] = "Blog invalid"
    redirect_to root_url
  else
    self.class.layout(@current_blog.layout_name || 'application')
  end
end

# config/environment.rb
config.gem 'liquid'
<!-- layouts/custom.html.erb -->
<%= Liquid::Template.parse(@current_blog.custom_layout_content).
      render('page_content' => yield, 'page_title' => yield(:title)) %>

<!-- blogs/_form.html.erb -->
<p>
  <%= f.label :layout_name %><br />
  <%= f.select :layout_name, [["Standard", 'application'], ["Plain", 'plain'], ["Custom", 'custom']] %>
</p>
<p>
  <%= f.label :custom_layout_content %><br />
  <%= f.text_area :custom_layout_content, :rows => 12 %>
</p>

Full Source Code

UPDATE: As Pratik mentioned in the comments, the above code is not thread safe. Here is a better way to handle the layout which is thread safe.

# application.rb
layout :set_layout

def load_blog
  @current_blog = Blog.find_by_subdomain(current_subdomain)
  if @current_blog.nil?
    flash[:error] = "Blog invalid"
    redirect_to root_url
  end
end

def set_layout
  (@current_blog && @current_blog.layout_name) || 'application'
end

RSS Feed for Episode Comments 24 comments

1. Gordy Sep 01, 2008 at 04:28

Great cast again, so helpful, i dont think i would have leant so much in the past year without your help.

Much appreciated!


2. Morten Sep 01, 2008 at 05:26

Brilliant! Your timing couldn't have been better..


3. Jeremy Frens Sep 01, 2008 at 11:29

It is so cool to see how easy it is to get a significant feature into a Rails app. Thanks!

One question: what are the security issues involved with letting the user pick her own layout? It would be unfortunate if something like "../../../etc/" were allowed. Is it up to us to prevent this? I can't tell from the "layout" method itself.


4. Ryan Bates Sep 01, 2008 at 12:57

@Jeremy, that's a good question, and I'm not sure if the layout method prevents this either. Even if it does I think it's best to ensure this in our application with a validation.

# blog.rb
validates_format_of :layout_name, :with => /^[a-z]+$/i


5. Adam Hill Sep 01, 2008 at 18:03

Wow, very handy. Thanks again Ryan!


6. AC Sep 01, 2008 at 18:09

Hi Ryan.

 I really love your screencasts - they've been very helpful to me, so thanks a lot for your effort!

 I must say though, I was disappointed to see how basic this episode was. I know, that not all your viewers are advanced, but this really wasn't very creative :/ No offense, but a beginner would've done this without much effort, or research..

 I'll be waiting impatiently for the next episode hoping it will be as exciting as some of your previous ones (the advanced forms come to mind :)

Cheers!

PS. Perhaps some of your viewers would be interested in hearing about the redhillonrails plugins (http://redhillonrails.org/) - I've been using Foreign Key Associations, Foreign Key Migrations, Redhillonrails Core, Schema Validations and Transactional Migrations ever since and they've saved me a LOT of time and effort, DRYing my code and ensuring that it works properly. The guys at Redhill have done a tremendous job and released the code for all of us to benefit from - big kudos to them!


7. Nilesh Sep 01, 2008 at 19:26

Thanks, Ryan. For me, all your screencasts have been perfectly timed. And your casts always use the 'best practices' for smaller things (which others ignore sometimes) like the proxy.pac for the subdomain cast; using liquid (for security) for the templates; the active resource screencasts. I love each and every one of them and find them to be the best way to show my team how to do things the right way.


8. Ryan Bates Sep 01, 2008 at 22:16

@AC, thanks for the feedback. I try to get variety in the episodes. Some will be basic, others advanced. While this episode doesn't show any new techniques, it combines several techniques shown in previous episodes which I think will be valuable to some. I have a few ideas for more advanced episodes in the near future, stay tuned. :)


9. Pratik Sep 02, 2008 at 04:31

This is exactly the kind of programming we'd want to avoid for thread safety reasons. Better way would be http://pastie.org/264443


10. Ryan Bates Sep 02, 2008 at 07:34

@Pratik, thanks for the correction. I'll mention it in the show notes. Since Rails is becoming thread safe I have to pay more attention to this kind of thing!


11. Jonathan Sep 02, 2008 at 09:48

The thread safe code now loads the layout for every action, not just show. Why not just leave the dynamic layout code in the blog_controller now? Anyway, with 2.2 approaching fast, there will be a lot of code needing modification to make it thread safe, maybe a screen cast about it?


12. Ryan Bates Sep 02, 2008 at 14:26

@Jonathan, it will call set_layout for every action, but it checks for the existence of @current_blog which is only set for the show action. In a sense it's doing the same thing.

I haven't had a chance to fully test this yet, so I'll play around with it to make sure it works properly. But in theory it should.

I'm not sure what happens if you now call "layout" in another controller - does it override this :set_layout? Gotta try that.


13. Paul Smith Sep 02, 2008 at 21:24

Hi Ryan,

Great screencast. I'm just wondering what makes the original code NOT thread safe, and what makes the new code thread safe. That would be very helpful in writing better thread safe code.

Thanks,
Paul


14. Ryan Bates Sep 02, 2008 at 22:35

@Paul, from my understanding, the layout is global state, and setting global state directly in a request is a bad thing to do since this is shared between threads. Setting it in one thread will effect another.

Someone please correct me if I'm wrong.


15. iGEL Sep 03, 2008 at 11:34

Nice screencast. But I was waiting all the time for one small note: Before rendering a different HTML layout, everyone should check, if they can modify the layout just by css. If your layout is from this century, that should be enough in 99% of the cases.


16. softprops Sep 03, 2008 at 15:44

wow I was just learning about liquid last night. what an awesome coincidence you posted this episode this week. I love these screen casts. I always learn little tricks that I take with me everywhere.


17. Gerbert Sep 05, 2008 at 13:03

About the custom layout rendering, an easier solution in this case is to use the render method setting the layout param. See it: http://pastie.org/266849


18. Luke Sep 11, 2008 at 14:44

Ryan,
The comments above regarding thread safety remind me of an issue we've seen lately in various Rails projects. It might make a good screencast: the danger of cached classes in production environment (the default setting in new Rails projects) and application state work-arounds with code like 'User.current_user' or 'Project.current'. Imagine what happens when you forget to nullify that value after each controller action :)


19. Tor Sep 14, 2008 at 06:25

Thanks, Ryan. For me, all your screencasts have been perfectly timed. And your casts always use the 'best practices' for smaller things (which others ignore sometimes) like the proxy.pac for the subdomain cast; using liquid (for security) for the templates; the active resource screencasts. I love each and every one of them and find them to be the best way to show my team how to do things the right way.


20. Dipesh Batheja Sep 20, 2008 at 01:05

Can we specify a path to a layout? Instead of just the name of layout which is picked from ./views/layouts directory?

I am really interested in some ideas of creating themeing system in Rails. Any good pointers?


21. Eddie Oct 03, 2008 at 14:22

Can someone please explain why the original code is not thread save vs. pratik's code?


22. Achim Oct 17, 2008 at 16:48

Big thanks @ Ryan for this nice episode again.

Its so nice and kindly learning rails with your screencasts :-)

Add your comment:

(SKIP THIS ONE)

(required)

(not shown)


(use pastie or gist for code)

sponsored by:
if you want to help:
required:
Get Quicktime Player