Many applications send emails out to users when certain actions happen, such as placing an order. Sometimes the contents of those emails can change frequently, so it is a nice feature is to be able to give the administrative users of your system a way to change the contents of those emails.
One way to do it is to use the Liquid templating language. The reason Liquid is a good choice is that Liquid is "non-evaling", which means you can just execute arbitrary Ruby code from within a Liquid template. This isn't true of ERB, for example, where you could just put <% something_evil %> in the middle of a template and it would get executed. So if you were to give your users a way to edit the the template and it was using ERB, you would have a potential security problem.
Now you can create a new mail template through the browser or just using IRB:
$ script/console
>> MailTemplate.create!(:name => "order_confirmation",
:body => "Dear {{name}},\n\nThank you for your order!")
There you can see the very basic syntax of a Liquid template. The {{name}} is a variable that will get replace with content when we render the template. Now we'll generate a mailer:
Now we have to modify the OrderMailer to use our Liquid template stored in the database. Edit app/models/order_mailer.rb so that it has the following contents:
class OrderMailer < ActionMailer::Base
def order_confirmation(sent_at = Time.now)
@from = 'orders@yoursite.com'
@recipients = 'some_customer@whatever.com'
@subject = 'Order Confirmation'
@body = {"name" => "Paul"}
@sent_on = sent_at
@headers = {}
end
def render_message(method_name, body)
mail_template = MailTemplate.find_by_name(method_name)
template = Liquid::Template.parse(mail_template.body)
template.render body
end
end
The order_confirmation method should look familiar, it is the almost the same as it would be for any regular mailer. One caveat is that Liquid expects the keys in the context hash that you pass to it to be strings, not symbols. The @body instance variable is the context hash that will be passed to Liquid. Those are the variables that you will have access to when the template renders.
The real work is happening in the render_message method. This is the method that the mailer calls when delivering your message. Normally you don't need to override this method, it takes care of finding the ERB template and rendering it for you. We can override it here to look for our Liquid template from the database. The first argument to the method will be set to the method name of the mailer, which in this case is order_confirmation. The second argument will be the value that we set @body to in the order_confirmation method. This makes for a pretty clean way of doing this, hooking in our Liquid functionality using the standard object-oriented technique of overriding a method, no monkey patching required, which should make Avdi proud!
Make sure you've started the app with script/server and then go into script/console and enter this:
>> OrderMailer.deliver_order_confirmation
Now in the output from script/server, you should see something like this:
Sent mail:
Date: Sat, 19 Apr 2008 06:41:53 -0400
From: orders@yoursite.com
To: some_customer@whatever.com
Subject: Order Confirmation
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8
Dear Paul,
Thank you for your order!
Editorial: 4 years ago, Matt Raible posted an article to the The Server Side titled Sending Velocity-based Email with Spring. Consider this the Ruby equivalent to that article. Compare the amount of code/configuration required to accompish this task in Java/J2EE vs. Ruby on Rails. Also, the Ruby version is more complicated because it involves storing/retrieving the templates from the database rather than the file system. If we were just retrieving the templates from the file system, this probably would have been a 2 line article. This is just another example of how much simpler Rails makes things when compared to Java/J2EE.
I've given a few presentations on Merb now and had the chance to discuss Merb with several different people. One point people have mentioned is that Rails is single threaded by design, not as a limitation as I've sort of implied in the talks I've given. Rails being single threaded relieves the developer from thinking about threading issues, which can cause bugs that hard to track down and test for. Sure, this makes it theoretically less efficient than a multi-threaded app, but that may be a small price to pay.
On the other hand, before doing Rails I was doing multi-threaded web applications in Java/J2EE for years and I never ran into threading issues. Most MVC frameworks made it pretty easy to avoid those types of problems and it was easy enough to just make sure that any servlets that I would write wouldn't use any shared data. Threading issues were something you had to kind in the back of your mind, but I never really had any problems.
So do we need multi-threaded web applications or do the potential complexities introduced by threading outweigh that benefit? And how much resource utilization/performance benefit is there to be gained by making our Ruby web apps multi-threaded?
If you are a programmer that deals with web applications and you keep up with the latest trends, then there is no doubt you will at least have heard of Ruby on Rails. You might be at the level where you have read about Ruby on Rails and played with it a bit, but you really haven't immersed yourself the Ruby/Rails way. Maybe you know how to build web applications with Java, .Net or PHP, and you think "Rails is nice, but there's nothing Rails can do that you can't do in X".
If you are in this camp, it may be because you aren't willing to adapt your ways to Rails. To really understand the benefits of Rails, you have to not only learn Rails, but learn the best practices followed by good Rails programmers, like Skinny Controller, Fat Model, REST and Behavior Driven Development. You don't see the true benefit of Ruby until you start to fully embrace concepts like these. The only way to learn concepts like these is to read blogs, listen to podcasts, talk to other Ruby programmers at work, user groups and conferences and most importantly, you have to actually write code that reflects what you have learned. You must put aside your preconceived notions about what is right and what is wrong and surrender to the flow. You must unlearn what you have learned.
So if you haven't experienced this transformation first hand yet, and someone asks you "What is the biggest advantage to Ruby on Rails", I would be willing to bet your answer would be productivity. This is the way I think many people involved with technology, who don't fully grasp the Rails Way, perceive Rails. They believe, with some cynicism, that because of the dynamic nature of Rails, you can develop applications faster. They'll also probably say the downside is that Rails can't scale.
But anyway, I'm hear to say that productivity is the most over-rated benefit of Rails. The real advantage to Rails is that it is written in Ruby, which is a very powerful language that will change the way you think about programming. It's funny, I've thought to myself a number of times about how interesting it is that Ruby fundamentally changes the way you think about programming and that "Thinking in Ruby" would probably be a great book. But ironically, Bruce Eckel, the author of the "Thinking in..." line of programming books, seems to be happy with Python and not willing to give Ruby a chance. Who knows, that article is a few years old now, so maybe he's changed his attitude towards Ruby since then. I know mine has.
It's hard to quantify the advantage that "Thinking in Ruby" brings. The simplest way I can state it is that you will look at problems differently and come up with better solutions, solutions you may not have thought of if you were programming in other languages. The way I support this claim is by looking at some of the web application development innovations that have come out of the Ruby community.
The first is Rails itself. Rails has been copied, ported, attempted to be ported or talked about being ported to almost every other mainstream language you could think of, including Groovy, PHP, JavaScript, Perl, Java and .Net. This phenomenon is unique to Rails, I can't think of any other web application framework that can say that. If not the whole framework itself, parts of it such as convention over configuration, migrations and embracing REST have influenced the way web application development is done in almost every language.
Another example is HAML. HAML is a truly new and different take of the problem of generating HTML from a combination of dynamic code and HTML. It is a new idea and it has been ported to PHP, Python, and .Net. Whenever you have a framework or library that is being ported to other languages, it shows that the framework being ported contains new and good ideas about programming. In other words, it is a contribution to the paradigm of web development and a clear sign that the original language that the framework was implemented is at the edge of innovation.
Another example is Behavior Driven Development. This example is even more interesting because the idea originally started in Java with the JBehave framework. Even though the idea for behavior driven development started with Java, the idea didn't really take off until it was implemented in RSpec. They are fairly similar in terms of syntax. Here's an example from the JBehave website:
public class CheeseBehaviour extends UsingJMock {
public void shouldRequireTheUseOfMocks() {
// given
Mock cheese = new Mock(Cheese.class);
Sheep sheep = new Sheep((Cheese)cheese.proxy());
//expect
cheese.expects(once()).method("squelch").withAnyArguments();
// when
sheep.eatCheese();
// then
verify();
}
}
and here it is converted to RSpec:
describe Sheep do
before do
@cheese = mock(Cheese)
@sheep = mock(Sheep, :cheese => @cheese)
end
it "should squelch when it eats cheese" do
@cheese.should_receive(:squelch)
@sheep.eat_cheese
end
end
For whatever reason, JBehave really never took hold in the Java community, but RSpec has in the Ruby community. RSpec has been ported to .Net, PHP and Groovy. All of those projects describe their code as a port of RSpec, not JBehave. Again it is Ruby influencing the wider web application development community.
Post World War II, the center of the art world was New York City and it was there that the modern art movemement was born. New York was where innovation in the art world was happening. In that time period if you wanted to be a serious artist, you had to go to New York to experience the movement first hand. Today, I believe the Ruby community is leading the way in innovative techniques for web application development. There is certainly innovation happening in other languages like Python, Smalltalk and Erlang as well, but I don't think any one other language/community is doing as much as Ruby. As far as I can tell, languages like Java, .Net and PHP are doing nothing to innovate web application development. They are simply lagging behind, playing catch up and trying to figure out how implement new features pioneered in the Ruby community and others as closely as possible, given the limitations of the language. So if you are a web developer, I suggest you ask yourself this question. Are the languages and frameworks you are working with leading others to come up with new ideas? Are the languages and frameworks that you are working helping you come up with new ideas? If not, embrace Ruby and someday you will discover an elegant solution to a problem, one that you may not have without Ruby.
The sun has set on Acts as Conference 2008. In case you missed it, here are my notes from Day 1. I just have a few quick notes from Day 2.
The morning started off with someone (Taylor?) from Engine Yard with a hosting support Q&A session. One thing he said is you shouldn't be using ferret, it is not production ready and they get a lot of support issues related to ferret. He suggests using sphinx and ultrasphinx instead. I can comment on that, I haven't used either myself.
Next was Charles Nutter talking about JRuby. He said that JRuby 1.0 is 2x slower that Ruby 1.8, but the upcoming JRuby 1.1 is 2x faster than Ruby 1.8, and the trunk is 2-5x faster than Ruby 1.8 and often faster than Ruby 1.9. He said that they (the JRuby team) have reached a point where they are going to stop worrying about performance issues because they consider it to be fast enough.
After that was Anthony Eden who gave a talk about the internals of ActiveRecord. One thing that came up in the discussion was the Ruby send method in Ruby 1.9. They have changed it so that if you try to use send to call a private method to an object, that raises a NoMethodError. You can use send! if you really want to call private method. Obviously the Rails core team doesn't agree with this change, and since Ruby is a dynamic language with open classes, you can override things like that, which they have done in ActiveSupport already.
Next was Bryan Liles' talk on Behavior Driven Development (BDD). It was a narrative covering Bryan's journey to BDD. His "air keyboard" livecast style was a big hit. The synopsis of his talk was that testing is hard, you only get good at it through experience, BDD is really the same thing as Test Driven Development (TDD) and RSpec is good but not the be all, end all of testing.
Next up was Dave Naffis and Josh Owens. I have to say they weren't the most engaging speakers, but the certain did have some really good content. One of the thing that stuck out for me was Juggernaut. Juggernaut is a rails plugin that uses flash to allow you to push data from the server to the client. It sounds like a great tool for certain applications like a chat room or a screen that shows real-time, updating data.
After that was Charles Brian Quinn, or CBQ for short. He is a great speaker, confident, clear, and engaging. His talk was on teaching Rails, which was really good. He does Rails training professionally and I bet he does a great job. One thing I remember from his talk was to avoid using foo and bar in examples, instead use real examples that your audience will understand. I agree with him on this for most cases, but in some situations I think foo and bar can actually useful to represent "you can use anything here". For example, as you know, ActiveRecord has a class method set_table_name, which allows you to specify something other than the table name that gets used by convention, which is the pluralized version of the class name. In that case, I think it's perfectly ok to say:
class Person < ActiveRecord::Base
set_table_name :foo
end
I'll give Patton's explanation, because I don't have exactly what Obie said:
By incremental development I mean to incrementally add software a time. Each increment adds more software - sorta like adding bricks to a wall. After lots of increments, you've got a big wall.
By iterative development I mean that we build something, then evaluate whether it'll work for us, then we make changes to it. We build expecting to change it. We never expected it to be right. If it was, it's a happy accident. Because we don't expect it to be right, we often build the least we have to to then validate whether it was the right thing to build.
That's the gist of it. I suggest reading the full article on it, it is very good. I certainly can't criticize Obie for referencing this great article along with it's fantastic images, but the problem was that Luke Francl already referenced the same thing the previous day in his speech. Maybe it was because Luke was sitting right next to me, but it seemed like everyone in the room kind of snickered when Obie brought up that slide. It was really bad because he obviously didn't attend Luke's talk and had no idea that everyone had already seen that, so he made no comment to that effect.
That really rubbed me the wrong way. This was a conference where there were no concurrent sessions, so you could attend every talk, so there was no excuse for not attending. Maybe Obie had a reason that he couldn't attend the first day of the conference, but I feel like if you are speaking at the event, you should make an effort to attend the other talks.
Anyway, that aside, I loved Obie's talk. There were two ideas that stuck out in my mind. First was "Practice makes permanent", which means if you practice doing the wrong things, like writing code without tests, you are just ingraining bad habits. Second was the idea that no one cares what tools you use to build your application, but we as developers have a tendency to obsess about our tools. I think he was trying to say we should spend less time arguing about Rails vs. Django vs. Merb and more time working on building great applications. I really hope the video of his talk gets put online at some point because I'd like to watch it again. He covered a lot of good ideas about application development that I'd like to go over again. If you have a chance to see him speak, or any of the speakers from this conference for that matter, do yourself a favor and go check it out.