Wednesday, December 19, 2007

Generate Merb Plugins and Generators

As part of my effort to port over restful_authentication I'm at the point where I want to start making a merb plugin gem out of it. This will of course include some generators for merb and datamapper. Later I'll add the Active Record generators. This is super simple thanx to Rubigen

Just today I went to start and found the merb plugin generator wasn't able to generate generators. Now just to move on from that fairly circular sentence, a quick call to DrNic and he had it sorted pretty quickly. The end result is, that as of today, you'll need to install merb from trunk to follow this. That or wait until merb 0.5.0 is released officially.

Generate a Merb Plugin

Merb comes with an option to build the skeleton of a plugin inside the merb command.
$ merb -P merbful_authentication
create
create lib/merbful_authentication
create spec
create Rakefile
create README
create LICENSE
create TODO
create spec/spec_helper.rb
create spec/merbful_authentication_spec.rb
create lib/merbful_authentication.rb
create lib/merbful_authentication/merbtasks.rb
dependency install_rubigen_scripts
create script
create script/generate
create script/destroy
As you can see it's very simple to generate a merb plugin. You could call rake gem in that directory and it will build the gem for you automatically.

From here it's just like any other gem. When you add new files, be sure to add them to the gemspec in the Rakefile.

For the specific case of merbful_authentication, I want to have a generator that is available for all merb projects, and also generators with the relevant code for datamapper, activerecord, and sequel. This is a pretty important way to help support merb's ORM agnosticness.

Merb makes use of rubigen scopes. All merb projects have the merb scope and in your app when you select which orm to use via use_orm the ORM scope is selected automatically. The available scopes in a merb application at the moment are

  • merb

  • merb_default - (used when no orm is selected)

  • datamapper

  • sequel

  • activerecord

  • rspec

  • test_unit

The merb scope is available in all merb projects, with the others available as selected. By default the rspec library is the testing scope that is available.

How to Use Scopes?

When you make your generators, you should apply a scope to them. What this does is allows your gem to behave itself according to the configuration the user has setup. The way I'll setup the generators structure in merbful_authentication will be:

  • merb_generators
    • authentication_generator


  • datamapper_generators
    • authentication_model_generator


  • activerecord_generators
    • authentication_model_generator


  • rspec_generators
    • authentication_testing_generator


  • test_unit_generators
    • authentication_testing_generator


The final names that I end up with might be different, but this is the general structure I will use.

Why this is useful, is that the merb generators are always available, so we can always call the authentication generator in a merb app. This in turn will have a dependency on the authentication_model generator. This is where the magic happens. So if the app has:

Scope : datamapper
The authentication_model generator defined in datamapper_generators will be active and therefore used.

Scope : activerecord
The authentication_model generator defined in activerecord_generators will be active and used instead

This is the way merb generators support the different syntaxes of models and controllers when you use script/generate resource or similar. The testing frameworks will work the same way.

How to Generate a Generator with Scope

To generate your generator with the required scope, it is really simple. In the root directory of your generated plugin:
$ ruby script/generate component_generator authentication merb
create merb_generators/authentication/templates
create test
create merb_generators/authentication/authentication_generator.rb
create test/test_authentication_generator.rb
create test/test_generator_helper.rb
create merb_generators/authentication/USAGE
There are also a whole heap of instructions on what to do next that are put up on the screen.

This will generate the authentication generator in the merb scope. See the documentation for rubigen to see how to use your shiny new generator. It's pretty much the same that you'd do for rails.

So then to make the datamapper generator
ruby script/generate component_generator authentication_model datamapper
I don't want this to show in the list of available generators when you run ruby script/generate so I will delete the USAGE file to prevent it showing up in that list.

Inside the merbful_authentication.rb (your_plugin.rb) file is the place to put your hook script. Similar to the init.rb file in rails plugins.

A note about plugins in general for merb. To mixin behavior to views and controllers you would include into and extend:

Views:- Merb::ViewContext (class)

Controllers:-

  • All controllers - Merb::AbstractController

  • Just web controllers - Merb::Controller (or Application)

  • Just Part controllers - Merb::PartController

  • Just Mail controllers - Merb::MailController

Ok now get out there and make some cool plugins for Merb! :)

Thursday, December 13, 2007

Using Git with SVN

I've been setting up a git repository to help aid collaboration on the port of restful_authentication to merb as merbful_authentication.

This is such a good library and I use it in all my rails sites that need authentication. It just works and I like that. All credit to Rick Olson for such a great plugin. Hopefully we'll be able to to a good job porting it.

Initially I setup a git repo with this. I'm starting to do most of my work in git so this is a good opportunity to keep going learning it.

You can get the WIP on this plugin via
git clone http://snatcht.com/git/merbful_authentication.git
# OR
git clone git://snatcht.com/git/merbful_authentication.git
This is really just a blank Merb app that has the beginnings of a port in it. There were so many people working on a port of this plugin I figured we might as well work on it together.

To include everyone in the development I needed to have svn as well. Rubyforge have been generous in approving a project for me.

The issue was, how to bring the svn into my local git repo so that I can push changes up to svn. Heres how I did it.
git clone git://snatcht.com/git/merbful_authentication.git

cd merbful_authentication.git

mate .git/config
In this file I put the following
[svn-remote "rubyforge/trunk"]
url = svn+ssh://username@rubyforge.org/var/svn/merbful-auth/trunk
fetch = :refs/remotes/rubyforge/trunk
Then to get the SVN linked into my local git
git-svn fetch rubyforge/trunk

git branch -a
* master
origin/HEAD
origin/master
rubyforge/trunk
You can see that rubyforge/trunk is now present in my git remote branches. Next create a branch from rubyforge/trunk
git checkout -b local-svn/trunk rubyforge/trunk
This will create a local branch that you can use in git like any other branch. Now once you've made changes to your app. Merge them into the local-svn/trunk then update your svn repo with a lazy
git-svn dcommit
from within the local-svn/trunk branch.

Friday, November 30, 2007

Raising Routeable Errors in Merb Router

On the #merb channel today there was an interesting puzzle to solve with routes. Merb routes have some crazy power, and today took my first crack at trying to give it a bit of a flex.

There were two distinct pieces of the puzzle that needed to be addressed. First, requests sent to actions on the application and exceptions controllers. The application controller will feed back with an error that says

"The 'Application' controller has no public actions"

The exception controller can be routed to like any other action. Failing missing actions are handled like any other controller, and actions rendering a known template will render but without the error message.

This behavior was not good for me and I didn't want it to occur. You might want the flexibility of being able to directly call the exceptions controller. But for this I don't.

Second, the error should be short and not give out too much information when either you request application or exceptions controller actions, or non-existent routes.

When Merb catches an exception it gives you a small amount of information about what the problem was. For this the requirement was that just a non-descript message be displayed.

This route setup prevents both issues from occuring and just renders an error screen with a simple message.

class Merb::Router
DEFAULT_NOT_FOUND_HASH = { :exception => Merb::ControllerExceptions::NotFound.new("URL Not Found"),
:controller => "exceptions",
:action => "not_found"}
end

puts "Compiling routes.."
Merb::Router.prepare do |r|

r.match(%r{^\/(application|exceptions)}).defer_to do |request, params|
params.merge!(Merb::Router::DEFAULT_NOT_FOUND_HASH)
end

# All the other routes

r.match(%r{.+}).defer_to do |request, params|
params.merge!(Merb::Router::DEFAULT_NOT_FOUND_HASH)
end
end
The first part of this route will catch any routes requested for application or exceptions controllers and render the error NotFound with the message "URL Not Found". The second part just catches any routes you haven't defined and renders that same error.

This setup will not work with default_routes. If you use default routes in between these to matchers, the bottom matcher will most likely not get called, and if you put the default routes after the last matcher in the example, default_routes will never be called. Personally, I don't like the idea of over riding the default behavior for not found errors, so I wouldn't have the second matcher, but I don't mind restricting routing from application and exceptions. At least that way you can use default_routes if you wanted to.

The defer_to method on a route will wait until the route comes in to assess it. If it matches the regexp then control is yielded to the block with the request and params objects.

Merbs error handling in controllers means that you just raise an error and the error will be caught by the exceptions controller and rendered. If you directly raise an error in the defer_to block though, your app will crash. You need to set the :controller, :action, and :exception options manually in the params hash to get error handling.

It's interesting to note that if the result of your defer_to block is false, it will not be considered as a match and will move onto the next. This does not allow for multiple defer_to matches to influence the result of another defer_to. For example, without default_routes set:
    r.match(%r{.+}).defer_to do |request, params|
params.merge!(:one => "one")
puts params.inspect
false
end

r.match(%r{.+}).defer_to do |request, params|
params.merge!(:two => "two")
puts params.inspect
false
end
will produce
{"action"=>"index", "one"=>"one"}
{"two"=>"two", "action"=>"index"}

# Instead of

{"action"=>"index", "one"=>"one", "two"=>"two"}
I'm not sold yet on what any of this means in terms of usefulness. I think I'll include the route to prevent requests going to application or exceptions directly, but I'll leave the normal behavior in for the rest of them.

Tuesday, November 27, 2007

New Merb Benchmarks

A while ago I started to do some really simple benchmarks on Merb and Rails. Yehuda Katz, aka wycats on IRC went and wrote two simple benchmark apps that live in Merbs SVN. To get these:
svn co http://svn.devjavu.com/merb/apps/benchmark 

svn co http://svn.devjavu.com/merb/apps/rails_benchmark
Both of these will use evented mongrel and swiftiply if they're installed.

To use these benchmark apps change into the root directory.
ruby script/benchmark
That command will run all the available benchmarks.

There are a number of options. Use the -h flag to help you find your way around. The options are there to help you setup the benchmark and also to select which benchmarks to run.
ruby script/benchmark -l
will list all the benchmarks available.
ruby script/benchmark -s erb,haml
will run the template and partials for erb templates and haml templates only.

Be careful though, these can take a while. Especially on Rails.

Organic Inefficiency

Merb really floats my boat at the moment. But I do think of other stuff... Really.

During my day job as a Mechanical Engineer it's part of my role to design and implement manufacturing cells in our factory. This can be pretty cool. Trying to make the cell as efficient and lean as possible can be quite a task sometimes. I've found that the cells not only need to be designed around the latest available (or is that affordable?) technology, people must be considered in the work flow if it's going to be successful.

When you design the cell with people being people in mind, all goes reasonably well. You can even make the cell efficient in a stand alone kind of way. But is that the best that can be done? I don't think so.

When the factory was first put on the floor, hopefully whoever did it did their homework and made an efficient layout so that all the individual work cells could function together reasonably well. As time goes on, you gain new products and you loose older ones. Some cells close and some new ones go in, and some of the bigger machinery which is very expensive to move now has to be planned around when you put in new equipment. What you end up with is an organically changing factory layout.

Picture this organically changing factory going forward for a few projects. Potentially you could end up with an unsatisfactory performance of the factory as a whole but this is hard to pick since all the individual cells seem to be efficient.

A work cell itself can have a very efficient theoretical process flow. In reality, if you can't translate that onto the floor the way you want, inefficiencies will result. The fact that when you lay the cell down, you "must put it in this spot" because that's where there's some free space can impact dramatically. Material in and out, and even flow in nice lines may not be optimum because you need to fit into the current factory layout. This can potentially lead to increased WIP (work in progress), increased handling equipment and all sorts of other nasties that may or may not be easily quantifiable.

It's my opinion that this kind of thing should be avoided. There is at least one software product I know of that's designed to assist with this issue. Tools like this can be very useful both to correct failing factories and also to move forward. Using a tool like FactoryFlow, is time consuming up front, for larger factories though it's much more expensive if you have to move equipment two or three times than if you invest up front with the design and do it right just once.

This is a huge issue that I'm sure could be talked about and theorised about. But this is a blog post so I won't do that to you :P

I think these same kind of organic inefficiencies can affect software development. I've seen plenty of old bloat-ware type products that attest to this. Automated BDD could go a long way towards alleviating this issue since the behavior is spec'ed and ideally you can change the implementation under the covers removing this organic inefficiency. Does it though? Is this kind of inefficiency common in software projects for the web?

I'd love to hear feed back on this issue.

Sunday, November 18, 2007

Native Sass Support for Merb

Nathan Weizenbaum aka nex3, a contributer to the awesome haml template system has just put support for using Sass in merb into the stable branch of Haml.

Haml has been supported in merb for a long time, longer than I've been involved with merb for sure. Sass was lacking though... Until Now :)

Before you can use Sass in Merb, you'll probably need to get the latest Haml.
sudo gem install haml
To use Sass in your Merb App it works the same way as in Rails. Put your Sass templates into public/stylesheets/sass
public/stylesheets/sass/my_style_sheet.sass
and include the css in the head of your layout application.html.*
css_include_tag :my_style_sheet
The only thing left to do now is to declare the dependency. In
config/dependencies.rb
include the line
dependency "haml"
Your app will now happily use your Sass templates, updated when you change them in development, but compiled only once during production. Sweet :)

Thursday, November 8, 2007

The Great Merb Speedup

Well, Merb has been coming along in leaps and bounds, but unfortunately it's also slowed down a bit in it's rendering. With all the changes this method has become heavy and quite slow.

The merb team did some great work on this yesterday. Hopefully there'll be some more in the near future too. Here's some numbers for your delectation. Please take these with a grain of salt. I'm no expert on benchmarking. See below for a full setup of how I ran the tests.

This table is on pastie for your viewing pleasure

Rails: ab -n 5000 -c 5 http://127.0.0.1:3000/tester
Merb: ab -n 5000 -c 5 http://127.0.0.1:4000/tester

Rails Version 8117
rake rails:freeze:edge
rake rails:update

Merb Version

Invocation:
Rails: mongrel_rails start -e production
Merb: merb -e production > a_log_file

======================= RESULTS =========================
0 Partials

Merb 0.4.0: Requests per second: 456.86 [#/sec] (mean)
Merb Edge 913: Requests per second: 723.98 [#/sec] (mean)
Rails Edge 8117: Requests per second: 375.47 [#/sec] (mean)

10 Partials
Merb 0.4.0: Requests per second: 150.57 [#/sec] (mean)
Merb Edge 913: Requests per second: 424.83 [#/sec] (mean)
Rails 1.2.5: Requests per second: 214.91 [#/sec] (mean)
Rails Edge 8117: Requests per second: 213.56 [#/sec] (mean)

100 Partials:
Merb 0.4.0 Requests per second: 22.42 [#/sec] (mean)
Merb Edge 913: Requests per second: 100.83 [#/sec] (mean)
Rails 1.2.5: Requests per second: 45.15 [#/sec] (mean)
Rails Edge 8117: Requests per second: 44.23 [#/sec] (mean)

* Merb edge without view context caching on partials
* Experimental ONLY

0 Partials: Requests per second: 727.22 [#/sec] (mean)
10 partials: Requests per second: 573.50 [#/sec] (mean)
100 partials: Requests per second: 229.13 [#/sec] (mean)

The setup - Rails

rails partial_speed_test
cd partial_speed_test
ruby script/generate controller tester

# setup AR to point to an emtpy db

# setup your controller See Below
# setup routes

mongrel_rails start -e production
# run the ab tests

The Setup - Merb

merb -g partial_speed_test_merb
cd partial_speed_test_merb
ruby script/generate controller tester

#setup your controller See Below
# Setup routes

merb -e production

Controller setup

# app/controllers/tester.rb

def index
render
end

# app/views/tester/index.html.erb (RAILS)
Inside Index
<% 10.times do %>
<%= render :partial => "blah" %>
<% end %>

# app/views/tester/index.html.erb (MERB)
Inside Index
<% 10.times do %>
<%= partial :blah %>
<% end %>

# app/views/tester/_blah.html.erb
BLAH<br/>

Merb Tests

Procedure

  1. Uninstall merb gem

  2. Install merb gem

  3. Start app

  4. Start tests


Released 0.4.0 gem
svn co with revision 913 and rake install
change the partial method to use cached_view_context instead of clean_view_context(engine) in render.rb

Rails Tests


  1. Generate an app using the 1.2.5 gem and start it up

  2. Run ab tests

  3. rake rails:freeze:edge (8117)

  4. rake rails:update

  5. Start app and run ab tests

The number of partials was changed by changing the number in index.html.erb

Phew.. That was a long post.

Just to reiterate, take this with a grain of salt. This was just a test for my curiosity and I encourage you to run these yourself if you are so inclined. Hopefully I've captured all of the setup.

Update: Again with these numbers take them with a grain of salt. These numbers are from the activity monitor on OS X
Rails 8117          MEM(MB)         CPU       
10 Partials 24.9 99.7
100 Partials 25.8 100.0

Merb 0.4.0 MEM(MB) CPU
10 Partials 15.6 98.8
100 Partials 21.4 100.0

Merb 913
10 Partials 14.3 98.8
100 Partials 13.8 100.0

Tuesday, November 6, 2007

Merb Generators Now Read Your Mind

Three days ago I blogged about the new generators in merb. Meh, that's so 3 days ago ;)

Update: If you've installed these gems from trunk since 0.3.7 and before the official release of 0.4.0 you should first uninstall the merb, merb_active_record, merb_data_mapper and merb_sequel gems before you install them again.

Update #2:use_orm :active_record and :data_mapper have been changed to :activerecord and :datamapper respectively.

ivey and wycats, regulars on #merb and all round good guys today rolled up their sleeves and removed the ugly contstant Merb::GENERATOR_SCOPE that you had to modify to get the generators working. They also came up with a way to implement default model and resource_controller generators in merb.

How to use it now?

Now when you setup your brand new merb app, you go into config/dependencies.rb as usual but instead of
dependency "merb_data_mapper"
you use
use_orm :data_mapper
# and
use_test :rspec
This will setup the dependency and also sort out what generators your app will use. You've got a good selection of generators now.

  • migration - a migration file

  • model - a model in the correct format + migration + tests / specs

  • controller - a blank controller + tests / specs

  • resource_controller - a controller setup with basic CRUD actions + tests / specs

  • resource - model and resource_controller and all things associated with them

So the one you'd normall use is resource.
ruby script/generate resource my_resource attr:type attr:type
This will generate a model and resource controller complete with tests / specs and migrations in the syntax of your selected ORM.

You can also use all of these generators even if you don't use an ORM at all. In that case it will just give you plain vanilla ruby classes with attr_accessors

Neat. These generators are very new though and I'm sure the content of the generated files can be improved. If you find something that you think needs to be changed please raise a ticket preferably with a patch.

wycats blog has a great outline of the major new features of the imminent version 0.4.0 of merb. If your interested you should check it out.

Saturday, November 3, 2007

The New Merb Generators

Update: The general information in this article is still correct, but the syntax is now nicer. You can read about the new syntax here.

In preparation for the big 0.4 release of Merb, the generators have had a major overhaul. Merb is ORM agnostic and uses plugins to harness an ORM. There are currently plugins for ActiveRecord, Sequel, and DataMapper.

The ORM agnostic goal of Merb, is a great thing, developers can use what they're comfortable with, or what suits a particular task at hand. This does create it's own issues of how to support it in the framework.

There's been great work done on merb to make this reasonably painless on selecting which ORM to use. Once the gem for the plugin is installed, a quick trip to config/dependencies.rb, and uncomment the line for your plugin and your away. But with generators it's been a bit more difficult up to now.

You'd think that will the option of ORM, and Rspec vs Test::Unit things could get a bit murky. Up to now the generators have been named specifically for the ORM you want to use so you can get the right syntax and parent classes etc. The only controller generator was just a blank stub that you have to fill out each time. Well now there's a new way to manage your generation needs.

Rubigen is awsome

DrNic extracted the generators from rails and added some of his magic to them. The result is a very clever and flexible system for generating files for your app and Merb tries it's hardest to put all this goodness to work for you.

Ok Already. Example Time

After you've installed the gem for your ORM plugin you need to tell Merb to use it for generators.
# config/merb_init.rb

# Put your selection for generators in here. I'll use DataMapper because I like it :)
Merb::GENERATOR_SCOPE = [:data_mapper, :merb, :rspec]
This will tell Merb to generate with Datamapper, and Rspec. This could have been :active_record and :test_unit if you want to go down that road. The order you put these in will matter in future (hopefully ;))

Generate A Model

ruby script/generate model my_model attr:type attr:type
This will generate the correct model, migration if required, and the correct test or spec stub. If you change the GENERATOR_SCOPE to use :active_record and generate another model, you'll see that the correct files are generated.
Note: This currently is not available when you're not using an ORM plugin. There are plans to allow this but at the moment it's not happening.

Generate Controllers

There are two ways to generate a controller.
  1. merb_controller
  2. resource_controller
The first, merb_controller just generates a blank controller stub, helper and tests or specs. This is available all the time, even when you're not using an ORM.

The other is available when you use an ORM plugin.
ruby script/generate resource_controller my_model
This generates a CRUD controller with the correct sytnax for your ORM selection you specified in merb_init.rb along with the helpers and test or spec stubs.

Currently there is no "resource" generator that takes care of a model and controller and all the associated files. This is on the drawing board and hopefully it won't take too long to get it sorted.

Change the ORM, mix and match your test_unit or rspec selections to see it in action. I wouldn't experiment by changing scopes in a real project though since Merb currently only supports using one ORM at a time.

Friday, November 2, 2007

Merb Crud Controller

The other day I decided to try a complete CRUD controller using the new provides format and Active Record. It supports XML, Javascript, YAML formats where appropriate and HTML across all actions. The built in error handling in Merb also helps to clean things up.

Well. Here it is, for better or worse. I really like it though ;)

class Posts < Application
provides :xml, :js, :yaml

def index
@posts = Post.find(:all)
render @posts
end

def show
@post = Post.find(params[:id])
render @post
end

def new
only_provides :html
@post = Post.new(params[:post])
render
end

def create
@post = Post.new(params[:post])
if @post.save
redirect url(:post, @post)
else
render :action => :new
end
end

def edit
only_provides :html
@post = Post.find(params[:id])
render
end

def update
@post = Post.find(params[:id])
if @post.update_attributes(params[:post])
redirect url(:post, @post)
else
raise BadRequest
end
end

def destroy
@post = Post.find(params[:id])
if @post.destroy
redirect url(:posts)
else
raise BadRequest
end
end
end

Wednesday, October 31, 2007

Adding Mime Types to Merb

Adding a new mime type in Merb is really simple. It's just one line.

Lets say we want the following:

  • .pdf extension to be associated with the :pdf content_type

  • Want to call to_pdf on an object we give to render

  • Content-Type: application/pdf

  • Content-Encoding: gzip

For all this we just make a call to Merb.add_mime_type.
The arguments to Merb.add_mime_type are

  1. Symbol for content_type

  2. Symbol of method to call to convert an object to this format

  3. An array of possible content types that the browser might give the server for this format. The first one will be used as the outgoing Content-Type header

  4. A hash of response headers you want to include [optional]

To write add our pdf mime type we would put the following into merb_init.rb down the bottom.
Merb.add_mime_type(:pdf, :to_pdf, %w[application/pdf], "Content-Encoding" => "gzip")

That's it. Now in your controllers you can use
provides :pdf
# and then
render @obj
and to_pdf will be called on @obj.

This looks like a great opportunity to use a presenter object with a to_pdf method on it.

If there's a format that you never want to use a transform method on, since you'll always have templates for them, just use nil for the transform method.

Sunday, October 28, 2007

Goodbye respond_to, Hello provides

With the upcoming release of Merb 0.4.0 lets say goodbye to respond_to and hello to provides.

This isn't as bad as it might look at first glance. I remember when rails first brought out respond_to. I thought it was awesome, and it certainly has been a good, enabling method. Merb borrowed this along with a slew of other good ideas from rails, but today Merb transcends respond_to.

The provides method does a similar job to respond_to. It gives the user the format they want if it's available. It just does it a bit differently.

Lets have an example


class OldWay < Merb::Controller

def index
@items = Item.find(:all)
respond_to do |fmt|
fmt.html { render }
fmt.js { @items.to_json }
fmt.xml { render } # use a template
end
end

def show
@item = Item.find(:first)
respond_to do |fmt|
fmt.js { render } # use a template
fmt.xml{ @item.to_xml }
end
end

#...
end

I'm sure this kind of controller setup is reasonably familiar with anyone who is offering more than plain html to users.

Lets see this same example as it is with the new provides functionality.

class NewWay < Merb::Controller
provides :js, :xml

def index
@items = Item.find(:all)
render @items
end

def show
does_not_provide :html

@item = Item.find(:first)
render @item
end

#...
end

To my eye, that's much more concise.

This will respond to the user with the format they selected and the correct headers. But the good juju is not just that provides will select the format to return to the client. Render now accepts a model or ORM collection object as well. Say you call render with an object it will render a template if it finds one, otherwise it will attempt to call to_format method on the object.

What... No Blocks per Format

This behavior should cover a good proportion of cases without intervention, but if you need something more explicit you can fall back to good ol' ruby and use a case statement. The content_type method will give you the current format so you can be as fine grained as you like.

How to Control all this POWER?

There are a number of methods available.

provides :format1, :format2
only_provides :format1, :format3
does_not_provide :format6

These are available as both class an instance methods so you can combine them for very fine control.

Is it extensible?

What if you want to add your own format that a controller provides? Lets say you wanted to add an MS Word format for your controller. First add the mime type you want to merb specifying the format extension, transforming method, and possible accept headers.

Merb.add_mime_type(:doc, :to_doc, %w[application/vnd.ms-word])

This makes the doc format available for use in your controller

provides :doc

# ...

render @obj

to_doc will be called on @obj and the outgoing content-type header is set to application/vnd.ms-word. Using to_doc on a model is not the best idea, please remember I'm just using it as an example.

Thursday, October 11, 2007

Ruby: Default Argument Gotcha

Tonight I got very frustrated chasing a bug.

I was getting a method missing for a nil object with the + method.

def text_area_field(val="", attrs={})
add_label(attrs) do
open_tag("textarea", attrs) +
val +
"</textarea>"
end
end

I thought that if val was nil it would be set to a blank string. This is only the case when it's nil because it wasn't specified. I was sending nil to it from another method, and so it really did put a nil in there. Nasty.

I changed it to this and it worked as expected.

def text_area_field(val, attrs={})
val ||=""
add_label(attrs) do
open_tag("textarea", attrs) +
val +
"</textarea>"
end
end

Wednesday, October 10, 2007

Enabling Sessions on Merb

I just fired up merb for a project and i realised, I didn't know how to turn on sessions. It turns out its easy.

In merb.yml, to use the data_mapper sessions support include the line

:session_store: data_mapper

Alternatively you can use memory, cookie, memcache or a custom one from an ORM plugin.

Usually when using an ORM plugin a migration is required. To find out what's available use

rake -T

At the moment the datamapper plugins rake tasks are broken and don't generate migrations. You can still use the datamapper sessions, just start merb and if the table is missing, it will be created.

Easy :)

Friday, October 5, 2007

Using Rubigen With Scope for Merb Plugins

I like to help where I can with contributing to merb. I tried to use the merb_sequel plugin the other day and found that there are no generators. It seems I've been spoiled by the generators in rails getting the basic empty model, migration and test stubs are great when I want to write a model. The only generator for merb_sequel was a migration one but I wanted more. That was ok though, I wanted to learn about generators anyway.

Merb has the rubigen gem in it to generate the application structure, so it's only natural to use it. Rubigen was extracted from rails by DrNic and I was lucky enough for him to explain the general workings of it to me. The major idea that I really liked was the ability to use scope in your gem generators.

What does using scope in your gem generators mean? Well, there is a line in a merb applications script/generate file


RubiGen::Base.use_component_sources! [:merb, :rspec]

What this line does is when you call script/generate it goes through all your gems and loads any generators found in the folders merb_generators and rspec_generators. [:merb, :rspec] are the generator scopes that merb has by default. All gems with merb or rspec scoped generators are loaded and available when script/generate is called. Newgem has some great examples of this in use. How do you scope these in your gems? For this example, include them in the following directories in your gem.

merb_genereators
rspec_generators

So if you do

ruby scirpt/generate

A list of all the generators of all the gems in those scopes. OK but what good is that?

In the merb_sequel plugin this allowed me to cater for both rspec and test unit stubs when generating a model. I just create the same named generator in a different scope and then whichever scope the user has selected is available. I used this when generating models to include the stubs. The directories I used were

merb_generators
sequel_migration
sequel_model
rspec_generators
sequel_model_test
test_unit_generators
sequel_model_test

Then in the model generator

record do
...
m.dependency "sequel_model_test", [@name]
end

This is where drnic's magic happens. The scope, set in script/generate, will only pick up the sequel_model_test generator from the rspec_generators directory and not the test_unit one. So when the dependency gets called it's the one defined in rspec_generators that it finds. Of course, if the scope is set to :test_unit

RubiGen::Base.use_component_sources! [:merb, :test_unit]

the sequel_model generator, when the dependency is called will find the seuqel_model_test defined in the test_unit_generators directory instead and use that.

This is great in that it allows you to select the flavor of testing that you like, and the generators will give it to you. That is providing that the gem has both rspec and test_unit generators defined.

By the way, if you call a dependency and there is neither scope is selected then an error will be raised. This is because there is no dependency of that name loaded to find.

In the merb_sequel plugin I've hacked a bit into it to check that an :rspec or :test_unit scope has been set. If a scope has not been set the test stub won't be generated and a message will show up.

I haven't found a way to dynamically set and then unset the scope which I'm guessing would be good for default scopes. That would be better in this case than the hack to not generate the test stubs.

Tuesday, October 2, 2007

What's Happening

I'm really bad at writing blog articles so I've got a plan. I'm going to write a merb app, something I've been meaning to do for a while, and try and document it as I go.

Well, lets see how I go :)

Monday, October 1, 2007

Mysql Gem on OS X 10.4

I'm starting to work with merb and I thought I'd give the sequel plugin a try.

I hoped it would be sweet but I had issues. The mysql gem needed to be installed. Apparently the AR gem has the mysql adapter coded into it and so it doesn't need the mysql gem, but I want to try sequel so I decided to get the gem installed.

By default it didn't work. The following is what I did to get it working. This is based on information I found here but it didn't work correctly for me.

According to the article at http://blog.maxdunn.com the gcc needs to be set to 3.3 I left mine at 4.0.1 and it worked fine.

First try and install the gem. This will setup the directories and get all the files.

sudo gem install mysql -- --with-mysql-dir=/usr/local/mysql
cd /usr/local/ruby/gems/1.8/gems/mysql-2.7

Edit mysql.c.in and add the following line at the top.

#define ulong unsigned long

Then

sudo ruby extconf.rb --with-mysql-dir=/usr/local/mysql
sudo make
sudo make install
sudo make clean

I can't promise this will work for anyone else but it worked for me.

Saturday, July 28, 2007

rspec Just keeps getting better

Well it's not what I had in mind for my first post on this blog.

I've just been working on my rails project where I'm writing specs using rspec in a controller. What I'm trying to do is spec that under a set of circumstances a particular template is rendered.

This was my first attempt


it "should render with my_layout" do
controller.should_receive( :render ).with( :layout => "my_layout" )
do_get
end


This didn't work. It gave me an error that render was called without the arguments.

My hat goes off to David Chelimsky who seems to answer just about everyones questions on the rspec mailing list. Within 10 minutes of posting my issue to the mailing list David responded by recommending a new method in the trunk. expect_render

Since I'm using piston to manage my plugins, a quick

piston update vendor/plugins/rspec
piston update vendor/plugins/rspec_on_rails


brought me up to speed. I changed my spec to his suggestion:


it "should render with my_layout" do
controller.expect_render( :layout => "my_layout" )
do_get
end


Once I tried this it worked great.

I really like rspec. My testing has never been better since I started using it.