Parts in merb are just like small controllers that you can use to perform logic. One very good usage for them is when you want add a “widget” to a view, that has its own logic.
We'll look at an example on how to make a part that, displays the news titles.
Merb parts don't come with merb-stack, so you need to get them separately
sudo gem install merb-parts
Generate a part
$ merb-gen part news [ADDED] app/parts/news_part.rb [ADDED] app/parts/views/news_part/index.html.erb [ADDED] app/helpers/news_part_helper.rb
If getting an error
FATAL: The gem merb-action-args (= 0.9.10, runtime), [] was not foundedit your
dependencies.rbfile an change the version on the top to the (latest) version of merb you have installed.
You will need to add merb_parts as a dependency:
In your dependencies.rb file add
dependency "merb-parts", merb_gems_version
Note that currently merb-parts is not tracking merb_gems_version, so may need a separate version
Lets assume we have some News model (merb-gen model news to get one)
We want to make our part display the news titles in a pane, that is reusable on other pages.
First, lets get the news form the db in the PartController.
Edit app/parts/news_part.rb to look something like this:
class NewsPart < Merb::PartController def index @news = News.all render end end
Nice, we have the data now.
Lets touch the view to show it. Edit app/parts/views/news_part/index.html.erb and make it look similar to this (for example):
<h2>News</h2> <ul> <% @news.each do |news| %> <li><%= news.title %></li> <% end %> </ul>
Nice! 1) Now we have a view for our part action. Lets use it
Using the part is very simple - in any of your views call
<%= part NewsPart => :index %>
If you have used the part on the controller, responding to '/', go to http://localhost:4000/ and see the titles of the news.
Remember to actually add some news to your database so there is something you can see. :)
Now, let's write a spec
In a BDD/TDD manner - this should have been the first section, but for this tutorial it's fine.
Following the idea from Yehuda to test the whole request instead of various components let's test our '/' route (the one that is displaying the news titles).
Create a file index_spec.rb under spec/request and put this code in it.
require File.join( File.dirname(__FILE__), '..', "spec_helper" ) describe '/' do before :each do # Generate 3 news to help us test 1.upto(3) do |i| News.new(:title => "news#{i}", :content => "this is news #{i}").save end end it "should have news titles listed" do @response = request('/') # Make a request to '/'. @response.should be_successful # Request is ok @response.should have_xpath('//ul/li[3]') # We have at least 3 news titles end end
On a side - to make your testauto_migrate!our database when our tests are run add the following line tospec/spec_helper.rb
Spec::Runner.configure do |config| config.include(Merb::Test::ViewHelper|>) config.include(Merb::Test::RouteHelper|>) config.include(Merb::Test::ControllerHelper|>) DataMapper.auto_migrate! # (Re)Create our database end
When calling the part controller form the view you can pass additional parameters, that get mixed in the params hash in the controller.
<%= part NewsPart => :index, :max => 3 %>
The :index action of the NewsPart should be updated like
def index limit = params[:max] || 2 @news = News.all(:limit => limit) display @news end
If you are using merb-action-args the former can be rewritten as
def index(max = 2) @news = News.all(:limit => max) display @news end
propery :content, Text datamapper will not even fetch it here. Checkout Datamapper docs for lazy-loading