Thursday, July 22, 2010

The power of Rails isn't Rails

My team is primarily a Java team (we write servers and services) so when we have to do some web work it always ends up being with a Java web framework, some reasonable, some not so good.

There are a lot of reasons to choose Java, it's stable, it's very well understood (by the people who care to dig into it's internals) and it's brilliant for long running processes like servers. What it's not good at, it seems, is web. Of all of the mainstream languages, Java seems to have the worst web frameworks, the worst view engines and the most over-thought data access conventions. If there's one saving grace in the enterprise Java landscape it's Spring, but even their best (Spring @MVC), while better or even the best in the Java landscape, it never quite feels like the right tool for the job.

Given this, the Spring team created ROO, a project to add code generation to Spring MVC. While it may not be the case here, many other web frameworks looked at the Power of Rails project and asset generation and thought "YES! That's the secret sauce!", and it turns out they're wrong. Spring ROO generates a whole load of code, it manages your DAO layer, but even with all of that power, it's real world use case has by many been deemed prototyping, not enterprise project ready. I felt the same way when I tried to use it, easy to get running, hard to keep progressing. Turns out all of the code generation in the world isn't the secret sauce.

There are two factors that I believe make Rails a killer web framework.

The first is The Community (yes, proper noun). The Rails lighthouse shows over 800 open bug tickets. I've seen people mock this fact (ROO in contrast appears to have 240, and I couldn't find the ASP.net one...) but the nature of The Community is that bugs, when found, are reported, usually with a failing test and possibly a patch for good measure. The number of found bugs (along with the rate of commits on the Rails Github account) is actually a positive sign, it means that The Community as a whole is invested in the success of Rails, and is working together to improve it rather than letting it atrophy or ignoring the issues with it and working around them. This leads to a vibrant piece of software that is a joy to use, that while not perfect, won't settle for average.

The second reason is that Rails, for multiple reasons, naturally lends itself towards a style of application that allows you to maintain the original "Wow I got that project running fast!" feeling (PM types: read "Project Velocity") for the entire duration of the Project. This seems to be why people who love agile seem to gravitate towards Rails, your burndown chart isn't logarithmic, it's flatter, more predictable, and more accurate (until the client changes their mind) than that old Gannt chart.

For the same reason that excites customers and project managers is why developers love it, because we love adding features, getting a product past just enough to the wow stage. Working a lot with legacy code teaches you the value of testability, simplicity, readability, and other agile-ilities, so anything that promotes these things to the extent that Rails does or that lets us add value to a product at the same rate regardless of the age or size of a codebase is a good thing, for everyone.

I believe that any framework garners all of it's power from the language that it sits on. A Java based web framework will be constrained by Java's inherit strengths and limitations. Likewise, a Ruby based web framework will live and die by Ruby's strengths and limitations.

It seems for the web, that Ruby's strengths are paramount to it's success, and it's limitations can be lived with.

Thursday, January 14, 2010

Scaffolds from existing tables

I find it hard to believe how often I see experienced, skilled and intelligent programmers who when faced with a tiny feature they would like to see in an OSS product resort to whining on forums or to colleagues instead of checking if there's already an existing solution

Granted this doesn't seem to come up in the Ruby world as much as it does a few other language circles I'm involved in (I suspect it's because it's usually faster to write the Ruby code to do what you want than to log into a forum...), but there are a few of these "requests" that you see over and over again, even when a good solution already exists and is freely and openly available. In rails it tends to be the "I have an existing schema but I want scaffolding!" one.

I had that thought trigger while dealing with a CRUD app I've been working on that deals with a large, legacy schema. It's not because I like scaffolding all that much (I don't) but because I wanted to get a working prototype up and running really fast and Ruby is much better at writing boilerplate code than I am.

In an attempt to become more like the programmers I look up to I didn't whine, I didn't log into a tech forum or post to a google group (where they are undoubtedly already 500 emails requesting the same thing already) I just started coding. Here's the "simplest thing that might work", it uses eval, it's not altogether elegant, but it fits all of the requirements.



We don't need to Google everything, there's nothing wrong with programmers that actually write code to solve problems...

Monday, November 23, 2009

Recursion in Ruby

I recently took part in the RPCFN. Being that this month's challenge was a Shortest Path type challenge and I've done enough discrete maths papers to know that the best graph traversal Algorithm for this is Dijkstra's least cost path one I went away and implemented it in a couple of hours. Many others did exactly the same thing, often much more elegantly that me. What struck me was that the markers were quite obviously looking for people to imagine up their own method of finding the shortest path, and had a massive bias towards recursive algorithms.

Now the winning submission is a very nice piece of code, it's concise, easy to read, simple, well tested, and it clearly lays out it's goals, to find a recursive solution to the problem with disregard of performance. It performed ok, I did wonder however how the recursion would impact the performance on larger problem sets.

So I took Todd's code and refactored mine to work with his data structures. I also changed up my code to match his module's interface and packaged both implementations into their own classes so that there wouldn't be clashes while trying to test them. (modules are for adding abilities to classes, not for providing whole feature sets, if you see yourself including a module into main, you're doing it wrong...)

And here it is.

I ran benchmarks on a low end computer (Pentium 2.8) against 3 graphs, one with 2 nodes and 2 edges, the RPCFN example with 7 nodes and 10 edges, and a larger, more complicated one of 7 nodes and 21 edges, all of this is in the code linked above. Here's the results over 10000 iterations:
Simple Graph:
Iterative Approach took 1.54700second(s).
Recursive Approach took 0.95300second(s).
RPCFN Graph:
Iterative Approach took 6.37500second(s).
Recursive Approach took 20.62500second(s).
Large Graph:
Iterative Approach took 10.70300second(s).
Recursive Approach took 472.96200second(s).

The numbers speak for themselves, this approach doesn't scale at all well. In fact at first I thought I'd broken some of Todd's code and created an infinite loop of some sort. It's more than likely that the recursive function could be tweaked to get better performance, but short version is that if I were ever to need to implement graph traversal in Ruby for any production system, recursion would be the last approach I'd look at.

Final word
Todd's decision to use a recursive solution in this case was totally justified, readability and simplicity were massive factors in this event. That said I believe that outside of the smaller, more contrived problem scopes, recursion brings a lot of headaches and scaling issues than the iterative approaches.

Exception Errno::ENOENT in PhusionPassenger

As Passenger fails so rarely there's not much documentation on the error messages, so I'm posting this for the benefit of googlers everwhere... Scroll to the bottom for the Cliffs notes.

Passenger is a fantastic piece of software, I can't praise it highly enough. It's brought Ruby deployments from the complexity of reverse proxying to a mongrel cluster up to being on par of ease with PHP, a difficult and noteworthy achievement that can only increase the adoption of Rack based technologies in the future.

That said, in very rare cases the Passenger spawner blows up in spectacular ways that the old Mongrel deployments never even dreamed of. When this happens all of your current and future Rack instances running on Passenger will respond to every request with a 500 error, your only course of action being a full Apache restart, and since it's so rare there's usually nothing that can be found on the net to fix it.

Here's an example from one of my company's Sinatra services:

*** Exception Errno::ENOENT in PhusionPassenger::Rack::ApplicationSpawner (No such file or directory - /tmp/passenger.19250/backends/backend.Hoa3NPf9xWIsBy3D4sBZtTKE1DgjhTKjbSpdwSPWW6FakeTwddzh2hwteMo6qYecQC
zgV) (process 8942):
from /opt/ruby-enterprise-1.8.6-20090610/lib/ruby/gems/1.8/gems/passenger-2.2.4/lib/phusion_passenger/abstract_request_handler.rb:279:in `initialize'
from /opt/ruby-enterprise-1.8.6-20090610/lib/ruby/gems/1.8/gems/passenger-
...snip...
[ pid=725 file=ext/apache2/Hooks.cpp:688 time=2009-11-24 06:31:02.381 ]:
Unexpected error in mod_passenger: Cannot spawn application '/opt/number_service': The spawn server has exited unexpectedly.
Backtrace:
in 'virtual boost::shared_ptr Passenger::ApplicationPoolServer::Client::get(const Passenger::PoolOptions&)' (ApplicationPoolServer.h:471)
in 'int Hooks::handleRequest(request_rec*)' (Hooks.cpp:485)

The root cause here is actually this ticket - the /tmp/passenger.****/ folder will be almost empty because some temp watcher cleaned it out in a quiet period. The ticket has been closed but unless you feel like building from source you'll have to wait until the next release to fix the issue.

Our fix was to move the Passenger temp directory out of /tmp in the Apache httpd.conf to stop temp watchers from nuking our deployments. Instructions on setting this up are found in the fantastic Passenger documentation.

Wednesday, September 16, 2009

Serving Rails sites using Passenger with SSL

This comes up a lot in the rails community, and it's not too difficult to do.

This brief guide assumes that you have a working site hosted on a Linux server using Passenger (and also having installed mod_ssl for Apache) and that you want to secure the whole thing using SSL. If not, you'll need to add extra steps like deploying the project, installing the gems, running your migrations, installing mod_ssl and Passenger for Apache etc.

We will generate our own (horrible...) temporary SSL cert assuming that later we will get a real one from a company like VeriSign and can swap out the temp one. Also, because the staging environment I've been deploying to runs on XAMPP and we've still needed phpMyAdmin while developing the site, I'll throw in the "Serving a PHP folder from passenger" whirlwind tour (for a much better explanation, see AbleTech's description).

Firstly Passenger will want a log file it can write to.
You'll want your Passenger logs to come into a log file in your rails site's log folder (near the end of the post you'll see I've configured "ErrorLog /opt/railssite/log/apache.log" in my httpd.conf). Create one in your rails apps log directory and run "chmod 0666" against it so that Apache will have read/write rights. If you don't, Apache will write all of the Passenger logging to the standard error_log and you'll waste time trying to see why your site isn't working like you thought it should.

Second create your SSL key/crt files.
Create a folder to hold your ssl key/crt files. We're going to use openssl to generate our key and certs so if your flavor of Linux doesn't have it installed you'll need to use apt or yum or something to get it. Here's the brief rundown on how to generate a valid key/cert with openssl, with the password "password". You may need to do this as root.

Run "openssl genrsa -des3 -out your.domain.name.key 1024"
When prompted enter the passphrase "password"
This generates the file your.domain.name.key. Because you want to get into good security habits early run "chmod 400" against this file, this makes sure that your key file is only editable by the root user.

To generate the self signed cert from your key run "openssl req -new -key your.domain.name.key -x509 -out your.domain.name.crt"
This generates the file your.domain.name.crt which is the certificate that we're going to use to secure this site.

Thirdly configure Apaches httpd.conf.

We're going to forward all traffic that comes in on port 80 to port 443 by configuring a RewriteRule in a virtual host listening on port 80 to forward all requests to the virtual host listening on port 443. You don't have to do this but users are easy to frustrate. Since HTTP is roughly equivalent to HTTPS in the mind of the average end user while they're typing a URL, making both "just work" is easy and highly recommended.

For the purposes of the folowing httpd.conf snippet, my SSL key/crt files were located in /opt/lampp/ssl and my rails site was in /opt/railssite

<VirtualHost *:80>
RewriteEngine On
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=permanent]
</VirtualHost>

<IfDefine SSL>
<VirtualHost *:443>
ServerName your.domain.name
RailsEnv staging
DocumentRoot /opt/railssite/public
ErrorLog /opt/railssite/log/apache.log
SSLEngine on
SSLCertificateFile /opt/lampp/ssl/your.domain.name.crt
SSLCertificateKeyFile /opt/lampp/ssl/your.domain.name.key
SSLProtocol all -SSLv2
SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown

<Location /phpmyadmin>
PassengerEnabled off
</Location>
</VirtualHost>
</IfDefine>

A few notes on the above.

"PassengerEnabled off" in the location phpmyadmin ensures that when someone requests your.domain.name/phpmyadmin Passenger doesn't try to serve it up and PHP takes over. If Apache can't make the jump and find the directory you need it to serve up on it's own, add "Alias /phpmyadmin /dir/to/phpmyadmin" above the directory node to force it to look in the right physical location. This is particularly useful if you need to serve up something like a PHP blog alongside your rails application, and want to keep the source code for this in the rails project.

Often you see people configure the rails document root in both virtualhosts, it's not necessary and depending on how your copy of Apache is running, it can cause Passenger to spin up a new rails instance just to have the user forwarded to the secured site. On a heavy usage site this could cause a lot of memory to be consumed for no reason.

It goes without saying that the secured virtualhost (443) should be configured within an <IfDefine SSL> node. This ensures that the site isn't served up unsecured if SSL fails for some reason.

Monday, August 24, 2009

Drop downs selections for belongs_to in rails

Rails does a lot of work for you out of the box, and with it's form builders you can get it to do some things that make you never want to go back to .NET and databind a control ever again...

Example: Recently I needed to bash together a quick admin screen with a couple of models. We have an organisation model that belongs_to :region and a Region model that has_many :organisations. This dictates a foreign key in organisation that points at a single region. In our generated Rails forms we enter that as a number, but it'd really be a nicer user experience to select the region from a drop down list right?

Enter collection_select. This little helper generates a selection dropdown from a collection (not surprisingly). So if in my organisation_controller's edit method I return @regions = Region.all we can get instant rails gratification.

Finally, since Edit and New use EXACTLY the same form, we're going to try be DRY and pull all the duplicated code out into a partial. I usually use the default form builder to base it on (you know, "form_for(@organisation) do |f|" ) and call "render :partial => f"

When this renders it looks for the partial pointed at by f, which is a form. so _form.html.erb will be rendered. A final note, even though the partial here will be able to see @regions, I like to explicitly pass it in the locals hash, so I know where the heck it came from in 6 months time. If that's confusing you can just strip out that code and use the global set in the organisation controller. Here's the code:

models/organisation.rb

class Organisation < ActiveRecord::Base

  belongs_to :region

end


models/region.rb

class Region< ActiveRecord::Base

  has_many :organisations

end


controllers/organisations_controller.rb

class OrganisationsController < ApplicationController

  # GET /organisations/new

  # GET /organisations/new.xml

  def new

    @organisation = Organisation.new

    @regions = Region.all

    respond_to do |format|

      format.html # new.html.erb

      format.xml  { render :xml => @organisation }

    end

  end

  # GET /organisations/1/edit

  def edit

    @organisation = Organisation.find(params[:id])

    @regions = Region.all

  end
end


views/organisations/_form.html.erb

<%= form.error_messages %>

<p>

  <%= form.label :org_name %><br />

  <%= form.text_field :org_name %>

</p>

<p>

  <%= form.label :org_code %><br />

  <%= form.text_field :org_code %>

</p>

<p>

  <%= form.label :provider %><br />

  <%= form.text_field :provider %>

</p>

<p>

  <%= form.label :region_id %><br />

  <%= collection_select(:organisation, :region_id, regions, :id, :full_name, {:prompt => false}) %>

</p>


views/organisations/edit.html.erb

<% form_for(@organisation) do |f| %>

  #:region => regions exposes the regions array as a local variable in the partial

  <%= render :partial => f, :locals => { :regions => @regions } %>

<p>

  <%= f.submit 'Update' %>

</p>

<% end %>

Monday, August 17, 2009

SOAP4R clients with proxy and basic authentication

If you go and google "soap4r proxy" you'll get a lot of helpful hits that show you how to set your http_proxy environment variable. Unfortunately there's not many (read none) that are devoted to configuring it programatically. So here it is.

If you run the wsdl2r tool to generate code you'll end up with a defaultDriver.rb file. This defines the RPC driver that you can call the webservices against. You'll want to ensure that it's going to use the HTTPClient libraries. If you installed it manually you should be fine, but if you installed it with rubygems you'll need to add requires rubygems to the top of your file.

Next you'll need to add the basic authentication configuration and the proxy configuration to the SOAP driver (using some horrendously poorly documented features of the SOAP4R library). The best place to do this is in your initialize method. You'll end up with a class that resembles the following (I generated mine from a WSDL pertaining to sending SMS messages):


require 'rubygems'
require 'xsd/qname'
require 'httpclient'
require 'soap/rpc/driver'

class MessagingService < ::SOAP::RPC::Driver

DefaultEndpointUrl = "https://a.secure.soap/endpoint"
MappingRegistry = ::SOAP::Mapping::Registry.new

Methods = [
[ XSD::QName.new("https://somesoapgeneratedmappingname", "sendSMS"),
"",
"sendSMS",
[ ["in", "to", ["::SOAP::SOAPString"]],
["in", "application", ["::SOAP::SOAPString"]],
["in", "message", ["::SOAP::SOAPString"]],
["in", "options", ["::SOAP::SOAPInt"]],
["retval", "sendSMSReturn", ["::SOAP::SOAPString"]] ],
{ :request_style => :rpc, :request_use => :encoded,
:response_style => :rpc, :response_use => :encoded }
]
]

def initialize(endpoint_url = nil)
endpoint_url ||= DefaultEndpointUrl
super(endpoint_url, nil)
self.mapping_registry = MappingRegistry
init_methods
self.options["protocol.http.basic_auth"] << [endpoint_url,'username','password']
self.options["protocol.http.proxy"] = "http://yourproxyserver:8080/"
end

private

def init_methods
Methods.each do |definitions|
opt = definitions.last
if opt[:request_style] == :document
add_document_operation(*definitions)
else
add_rpc_operation(*definitions)
qname = definitions[0]
name = definitions[2]
if qname.name != name and qname.name.capitalize == name.capitalize
::SOAP::Mapping.define_singleton_method(self, qname.name) do |*arg|
__send__(name, *arg)
end
end
end
end
end
end