Merbcamp - Day 2
Oct 12, 2008
In case you missed the first installment, click to read day one at merbcamp. This is what happened on day 2:
Deploying a Merb app
"Your number one priority when building a merb app should be deployment". It's going to make scaling much easier later. As a result, there are some decisions you need to consider very early on in the process.
You've got two ways to freeze merb and dependencies into your application folder:
thor merb:stable thor merb:edge
But should you freeze or not? And if so, which option should you take? The 3 options you've got are:
- Stable release
- Everything is contained within your app (in merb_root/gems)
- Start app with merb_root/bin/merb
- Maximum deployability
- Edge release
- Everything is contained within your app (in merb_root/src)
- Start app with merb_root/bin/merb
- Maximum deployability
- Stable or Edge
- Everything as gems
- Start app with wherever merb has been installed
- Least deployability, least recommended
Managing your app dependencies with thor
Once you've frozen everything within your app directory, you have two ways to upgrade your dependencies. Updating all of your frozen gems within your application from rubyforge or wherever:
Or rebuilding all of your native extensions:
The main options on the front end are apache or nginx, pushing requests through to mongrel, thin, or ebb. Nothing really that different to rails here.
The other option is passenger, again nothing different to rails here.
Starting and stopping merb
You've got 2 options here:
merb -k merb -K
the latter starts merb gracefully, the latter forcefully. Graceful will clean up pid files. Forceful will allow the service to start regardless. New in the latest code base is the following enhancement:
kill -USR1 pid_here
Which will output the current config of the server (running on the PID you specify) as YAML.
You've also got merb-manage, which you pass in start/stop/restart/reload. Reload does a fast restart which means very very little downtime between starts because it keeps the config and gem dependencies but just reloads the app files.
merb –help is your friend to find out what other options you've got
collectd is a lightweight statistics collection daemon. Plugin based and network aware. A configuration definition looks like a fairly lightweight XML-style file. Includes a basic PERL CGI script which extracts data out and allows it to be viewed on a graph. Allows you to map things like the number of TCP connections over time, ruby process CPU time, memory usage, etc.
collectd-nagios is a plugin that allows nagios to interrogate any statistic collected by collectd. Point it at a statistic and flag up the appropriate alert should it not be within acceptable bounds.
An intro to puppet for those that haven't used/seen it before. Allows you to describe how you want a various machine, machine type, etc. to be configured. What services need to be installed, running, and the required configurations files. Your manifests/configurations are managed via a version control system (svn, git, whatever you want). You check in your config changes, pull it out onto your puppet master and all your nodes will be updated with the required
Use the merb_exceptions plugin and configure with the appropriate settings like so:
:exceptions: :app_name: Skippy :email_from: email@example.com :email_addresses: - firstname.lastname@example.org :environments: - production
Make sure you've got merb-mailer setup, and set your delivery method to sendmail to reduce the potential problems:
Merb::Mailer.delivery_method = :sendmail
Within your configuration file you can give your instance a name, and it will mean you can easily identify processes when doing a process listing. The syntax is:
Merb::Config.use do |c| c[:name] = 'skippy' end
Real time web apps with Orbited and Merb
What is Comet? It's real-time web apps and it's easy. Facebook should have known better, they didn't need to write a custom stack on top of erlang. The wheel doesn't need to be re-invented, we have the socket and protocol definitions that we need.
In HTML5 we've access to WebSocket, and you can create a new one like this:
And we get 3 callbacks:
Orbited is a python server that provides a WebSocket API within all browsers. The Orbited daemon acts as a security control between the browser and TCP, tunneling over HTTP. This means that you can implement push based services using protocols like XMPP to provide data to clients rather than the current AJAX polling approach. The list of protocols being implemented is much longer than I'll list here, but it's unlikely you'll be left wanting.
Ultimately it means you can use HTTP where it's appropriate, and then more appropriate protocols when they are better suited to the task (i.e., messaging).
It sounds like Orbited is a bit of an interim technology to back-port the WebSocket definition from HTML5 into today's browsers. Once HTML5 is implemented you'll be able to have native WebSocket support.
Writing a Merb plugin 101
merb-slices are reusable and over-writable mini-apps. They are much better than generated code because every time you want to change your code, you don't need to re-generate the code you just pull in the update. If you've got your own overrides there is no problem. Some of the most popular slices are:
- Login form
- Password reminder
- Event Registration
- Admin Interface
- Tune Up
When should you use them? When you want to target application content.
Firstly, they are not DataMapper plugins (like the actsas_* plugins). They are used when you want to extend the framework, but don't use aliasmethod_chain. This was touched on briefly, and it seems that in the merb world it's about as evil as evil gets. Basically it's a hack that lets people write their own methods as a decorator around the underlying framework API to get their own implementation in where they need it. Merb has taken the approach of providing hooks at the relevant places you might need them.
Is called when merb first starts up. You have access to at least two events (at the moment) within the boot up process:
Using the Merb API
There are essentially 3 types of methods within the API. Private, public, and plugin methods. Don't ever touch the private ones, there is no guarantee they won't be changed in core without warning. If you don't have the hook you need, ask them for it. You can specify a version dependency within your plugin, and so long as you stick to the rules you're right to say that your plugin will work <= 2.0.
Make sure you test your plugins through the full stack, you want to make sure that the plugin you're using will work with the version of merb you are using. If you're mocking everything out, you've got no guarantee of that.
Learning from Django
The django equivalent of merb-slices has proven to be awesome, so embrace them. On a project you'll likely end up with a heap of them but don't worry. The benefits from reusability are worth it, and they embrace the unix philosophy of do one thing, do it well, and work well together. Even simple django apps typically end up with a dozen or so slices.
So move towards slice-centric development where the slice becomes the core way to develop. The end result is something like the project pinax in django. The merb project gluttonberg is heading in that direction.
The merb routing kicks ass, hopefully django looks like that soon.
Something that is missing in merb that django does have is automatic admin interface generation. It's not like rails scaffolding so it won't break when you implement model changes. It's tightly bound to the ORM though, and given merb's ORM-agnostic approach it could be difficult to implement on this side of the fence.
Django also has generic controller generation, which sounds a lot like the make_resourceful type implementations in rails. You also get feed syndication almost for free.
It also looks like django.contrib has some awesome re-usable components. An example given in the presentation was importing logic to handle capturing US social security numbers. You import the library, and you get for free the appropriate form and all the validation.
The django docs have historically also kicked ass, and getting good docs makes adoption so much easier. Rdocs really don't cut it.
Driven Merb: A user story
A pretty quick presentation on the benefits of using user stories in development, and BDD. The high-level full stack tests fits nicely with Yehuda's "test what you care about" points from yesterday. We got some quick examples of running stories in both webrat and selenium. If you're new to stories, check out David Chemlimsky's stuff and rspec and the new story runner (called cucumber), they'll do it much more justice than my paraphrasing here.
From Rails to Merb (Merb team panel Q&A)
Q: What is thor, and how do we use it?
A: Rake was sort of a hack that became the lingua franca for making scripts in ruby, but it didn't support command line arguments so people provided a multitude of ways to get options out. Thor allows you to specify a command and provide a series of options, that was originally intended to run your own binaries. It's as easy to use as rake, but provides more options, and allows tasks to be system wide and not just application specific.
Q: What is the core team doing to make merb easy to install and get up and running?
A: You don't need git to run merb, you only need that if you want to run edge. You can install everything you need via gem. You will be able to do "gem install merb" and then "merb-gen app" to get up and running.
Q: What do you see as the biggest barrier to growth of merb usage?
A: The biggest barrier has been how fast it's been developing and how quickly things have been changing. Going 1.0 will be the biggest barrier knocker-downer on that front, as people can be happy that the API is solid and stable and won't be changing every week.
Q: How do you guys find time to organise yourself on various open source projects?
A: No sleep, and general addictions to OSS.
Q: What is the Engine Yard/Core team view on alternative ruby implementations such as Rubinius and JRuby, and does merb run on JRuby?
A: Yes it runs on JRuby. And merb will run on anything that rails runs on, Engine Yard's position is ruby is good and make ruby win. So support them all.
Q: Short of improving stability and small evolutionary changes is there anything cool/big/different you'd like to to introduce to merb in plugins/slice/etc?
A: Wait for the keynote
Q: I have an app deployed with users, authentication, etc. how do I chunk it down to migrating with merb?
A: merb-auth isn't going to dictate how your authentication is going to work, so you should just be able to port over your user model and the logic within. If you're using restfulauthentication by Rick Olson then there is a mixin you can use._
Q: How does core see Rubinius support within merb?
A: As soon as it supports rails, we'll support it. There's lots of cool things in Rubinius that you can't do in MRI, so as soon as it's closer to production ready they'll be onto it. We won't limit merb to Rubinius once it's out though, we'd look to support MagLev and co.
A: Absolutely not, if it does then it's a bug. Merb stack is designed to generate the code you'd have to do yourself, not hide you from merb core. You'll also be able to make your own stacks if you need to have your own standard setup.
Q: One of the things I dislike about rails is how the mindset of the rails core developers has changed as Rails has gained popularity. Do we have to worry about that here?
A: Absolutely not. If any of the core tenants of merb are violated Ezra will kill people :) Because the merb project isn't tied into specific applications that they're trying to build (unlike the 37signals apps that rails was extracted from), they're always thinking about the benefit to the community. That's not to say rails isn't thinking about the community, but the merb approach is less opinionated because they are dealing with a diversity of opinions and not a single approach.
Q: How do we support internationalization in merb? Did you abandon the rails internationalization solution?
A: No we didn't abandon internationalization and we still want to do it, but we want to do it in the merb way. Matt A personally worked on the rails API, and the problem with the way it was done in rails wont work with merb. Merb's approach is all about performance and speed, and the original approach was slowing merb down. We will be working on it, but the only real solution is to do it in C. It's about half way done.
Q: With ruby 1.9 coming around in December, has merb come to support that?
A: merb should run fine on it, but it hasn't been tested for quite a while. That being said, we still wonder if the API really is locked down for 1.9 and that there won't be any changes before December. The main problem is with support in other gems your app is dependent on. At this point, there is no clear guidance from the ruby core team so we have to wait until people are using 1.9
Q: You guys on the panel are all ruby and merb hackers, but you need to help us get there. What help can you provide?
A: Go read interesting code and try and grok it and understand it, then hack and make sure you understand it. Try and fix bugs in frameworks. There are a bunch of good books out there, read blog, read code on github, check out your local user groups and IRC. Make sure things you're doing are fun and cool to keep your motivation levels high. Code things you're interested in. There are 2 merb books coming out, "The Merb Way" and "Merb in Action".
Q: Can you each talk about your workflow on a typical day with regard to coding?
A: The general consensus seems to be to spike when working on something experimental. Then detailing a list of things to implement, that can be either some specs, an actual implementation against the still-to-be-developed API, a list of work, etc. then go make it work. Go speak to someone else about the problem to get another perspective, especially from someone who doesn't understand the problem you're working on.
Q: If we see something that interests in merb and we want to contribute, what process do we follow?
A: Get in touch (mailing list, irc, etc.), check out lighthouse, or submit a patch. Come talk to them before you submit a patch though, you don't want to spend hours working on a patch and having it rejected because it doesn't fit the general approach guidelines. The process is usually fork the the repo, create a branch for each bug you fix so it can be cherry picked, fix it and don't send a pull request but create a patch and attach it to a lighthouse ticket or provide the branch to pull from (and keep it up to date with master so it's easier to apply when they get around to it).
Q: What's your favourite deployment strategy?
A: Capistrano or Vlad, deploy to staging and if it works deploy to live. But always do it from a tag. And use ruby enterprise edition, it fixes garbage collection and allows you to efficiently share memory across child processes. When you deploy your app, you need to make sure that you use the thor task to rebuild the native gems so they're recompiled for the deployed platform
Expanding our idea of the background web with Merb
HTML makes things look pretty, but all the interesting stuff happens in the background behind what you see in your web browser.
Running Background Tasks in Merb
You have a few ways to run tasks in the background. If you want to use cron, you can just setup some rake tasks. Within merb itself you have the renderthencall and run_later methods. You can call them like:
render_then_call do Node.inactive.purge end run_later do Node.scan(Node.first) end render
From the presentation I couldn't glean what the effective difference is between the two calls.
WebServices in Merb
Merb handles RESTful web services with the provides, display, and render methods. An example definition that makes it possible is:
It will automatically assume you want to provide HTML as well, if you want only JSON then you use onlyprovides_ instead of provides. Merb's equivalent of the rails respondto_ statement is a case statement:
case content_type when :html render when :json display @post.to_json end
The rails respond_to block means a proc needs to be created and evaluated when you won't need to evaluate at least half of it (i.e., you only return one format). It's about 10x slower than the merb approach. You actually don't need the case statement normally though you should just call display and it will work out the format switching.
Yehuda is talking about some of the bad press about "ruby is slow" and how the community response is usually "Ok!", and that's just embarrassing. He want looking for some benchmarks and found that rails generally outperforms CakePHP, CodeIgniter and similar frameworks the people traditionally think are much faster than ruby. They got in the region of 3-4 requests/sec. So how does merb stack up?
Merb Performance Benchmarks
A simple "hello world" scenario was used to show that ruby isn't super slow, that if you go through the stack you still get reasonable performance. Returning a rack response directly from the router handled 3,500 req/sec. Doing a string render returned 2,000 req/sec. Rendering a template comes in at 1,500 req/sec.
Compare that to Apache serving static HTML which got 4,000 req/sec. Comparing it to the PHP equivalent of "hello world" showed almost identical performance to the merb controller rendering string "hello world". Things really get interesting when comparing it to rendering a full template vs. using PHP CodeIgniter which is meant to be the fastest PHP engine. CodeIgniter gets 300 req/sec, that's against the 1,500 req/sec you get from the standard merb setup. CakePHP was even worse with under 100 req/sec.
The ruby leaders have incorrectly conceded the performance ground just because ruby is slow at fibonacci, but PHP is slow in other areas. It has to load entire frameworks up at runtime. Our web servers (rack/thin) are far more optimized and based in C.
Merb vs Rails vs Sinatra
Next was comparing merb against other ruby options. Returning directly from the merb router kills everything, but there aren't many scenarios you could use that. Sinatra is just slightly faster than returning from the merb controller, which both outperform Rails by a 3-4x. Rails itself still kills CakePHP.
The Future - Merb 2.0
It's going to stay modular, but it's also going to be integrated (back to that unix philosophy). Looking at django is an important part of moving forward because they've done lots of things well, but it won't be so monolithic and a tangled web of dependencies. But the out of the box user experience should be just as pleasant. A consistent merb ORM so that you interface in a single way, and the ORM plugins will work out what underlying methods would be called in DataMapper, Sequel, or whatever you are using. These means the admin type interfaces django has would be possible.
Mailer will move towards a messenger approach, mail isn't the only way you want to send notifications these days.
Moving towards Merb 2.0
The 1.0 release is a stake in the ground, the 1.x releases will be entirely backward compatible but they're just stable experiments and an incremental step towards 2.0. Don't consider any new changes between 1.0 and any 1.x to always be there, the 1.x releases are not the same stake in the ground. Bug fixes and additions to the stable release will be applied as 1.0.x.
How can you help?
Contribute to the central slice repository, but make sure they rigidly adhere to the public API and don't use any plugins or slices that aren't. Report missing functionality. Work on the wiki, you can't beat community driven documentation. Help test RC1. Report any missing documentation. List your site on Merbunity.com.