You probably heard that rendering views outside controllers is pure evil. That’s true, but there are cases where it is very handy to render partials or views defined in your Rails application in different places. Think about generating newsletter templates and saving them to database, or generating bits of static HTML that need to be saved to filesystem. You will learn how to do it from this article.
In Rails prior to 3.0, there was no clean way to do so. There were a few hacks and libraries that made it possible but it always felt a bit ugly for me, while developing Ruby on Rails applications to use that.
Rails 3.0 gave us a way to use cleaner approach by introducing AbstractController::Base that we can subclass and get the ActionController’s ability to render views in our own classes.
First, we need to create our controller class (app/controllers/hello_world_controller.rb):
class HelloWorldController < AbstractController::Base include AbstractController::Rendering include AbstractController::Layouts include AbstractController::Helpers include AbstractController::Translation include AbstractController::AssetPaths include ActionController::UrlWriter # Uncomment if you want to use helpers # defined in ApplicationHelper in your views # helper ApplicationHelper # Make sure your controller can find views self.view_paths = "app/views" # You can define custom helper methods to be used in views here # helper_method :current_admin # def current_admin; nil; end def show render template: "hello_world/show" end end
First, we include bunch of modules from AbstractController (lines 2-7), but we can also borrow some functionality from ActionController (like ActionController::UrlWriter that will allow us to use Rails routes in views and helpers). You can slim your controller down by removing unnecessary modules from this list. If you don’t use Rails routes, remove ActionController::UrlWriter, if you don’t want layouts – AbstractController::Layouts and so on.
Important thing is to define a view path, that is relative to Rails.root by default. When using AbstractController, you can store your views anywhere you like (think: “app/cells/views” for example). On line 13 of above example, I decided to use standard Rails view path to save my views.
You’ll also need a view file, put it in “app/views/hello_world/show.html.erb”:
To execute your controller, and get rendered view as a string, execute:
# From your model, observer or library, call: HelloWorldController.new.show # => "<h1>Hello, World!</h1>\n"
But it’s not all. You can define helper methods, and specify which helpers are available to views, just like you would do with ActionControllers. I found reading ActionMailer::Base implementation very helpful in understanding how to use AbstractController::Base. You might also be interested in watching this screencast.
Find a gist for sources here.
Post by Hubert Łępicki
Hubert is partner at AmberBit. Rails, Elixir and functional programming are his areas of expertise.