# == Basic overview # # A Cell is the central notion of the cells plugin. A cell acts as a # lightweight controller in the sense that it will assign variables and # render a view. Cells can be rendered from other cells as well as from # regular controllers and views (see ActionView::Base#render_cell and # ControllerMethods#render_cell) # # == A render_cell() cycle # # A typical render_cell state rendering cycle looks like this: # render_cell :blog, :newest_article, {...} # - an instance of the class BlogCell is created, and a hash containing # arbitrary parameters is passed # - the state method newest_article is executed and assigns instance # variables to be used in the view # - Usually the state method will call #render and return # - #render will retrieve the corresponding view # (e.g. app/cells/blog/newest_article.html. [erb|haml|...]), # renders this template and returns the markup. # # == Design Principles # A cell is a completely autonomous object and it should not know or have to know # from what controller it is being rendered. For this reason, the controller's # instance variables and params hash are not directly available from the cell or # its views. This is not a bug, this is a feature! It means cells are truly # reusable components which can be plugged in at any point in your application # without having to think about what information is available at that point. # When rendering a cell, you can explicitly pass variables to the cell in the # extra opts argument hash, just like you would pass locals in partials. # This hash is then available inside the cell as the @opts instance variable. # # == Directory hierarchy # # To get started creating your own cells, you can simply create a new directory # structure under your app directory called cells. Cells are # ruby classes which end in the name Cell. So for example, if you have a # cell which manages all user information, it would be called UserCell. # A cell which manages a shopping cart could be called ShoppingCartCell. # # The directory structure of this example would look like this: # app/ # models/ # .. # views/ # .. # helpers/ # application_helper.rb # product_helper.rb # .. # controllers/ # .. # cells/ # shopping_cart_cell.rb # shopping_cart/ # status.html.erb # product_list.html.erb # empty_prompt.html.erb # user_cell.rb # user/ # login.html.erb # layouts/ # box.html.erb # .. # # The directory with the same name as the cell contains views for the # cell's states. A state is an executed method along with a # rendered view, resulting in content. This means that states are to # cells as actions are to controllers, so each state has its own view. # The use of partials is deprecated with cells, it is better to just # render a different state on the same cell (which also works recursively). # # Anyway, render :partial in a cell view will work, if the # partial is contained in the cell's view directory. # # As can be seen above, Cells also can make use of helpers. All Cells # include ApplicationHelper by default, but you can add additional helpers # as well with the ::Cell::Base.helper class method: # class ShoppingCartCell < ::Cell::Base # helper :product # ... # end # # This will make the ProductHelper from app/helpers/product_helper.rb # available from all state views from our ShoppingCartCell. # # == Cell inheritance # # Unlike controllers, Cells can form a class hierarchy. When a cell class # is inherited by another cell class, its states are inherited as regular # methods are, but also its views are inherited. Whenever a view is looked up, # the view finder first looks for a file in the directory belonging to the # current cell class, but if this is not found in the application or any # engine, the superclass' directory is checked. This continues all the # way up until it stops at ::Cell::Base. # # For instance, when you have two cells: # class MenuCell < ::Cell::Base # def show # end # # def edit # end # end # # class MainMenuCell < MenuCell # .. # no need to redefine show/edit if they do the same! # end # and the following directory structure in app/cells: # app/cells/ # menu/ # show.html.erb # edit.html.erb # main_menu/ # show.html.erb # then when you call # render_cell :main_menu, :show # the main menu specific show.html.erb (app/cells/main_menu/show.html.erb) # is rendered, but when you call # render_cell :main_menu, :edit # cells notices that the main menu does not have a specific view for the # edit state, so it will render the view for the parent class, # app/cells/menu/edit.html.erb # # # == Gettext support # # Cells support gettext, just name your views accordingly. It works exactly equivalent # to controller views. # # cells/user/user_form.html.erb # cells/user/user_form_de.html.erb # # If gettext is set to DE_de, the latter view will be chosen.