Posts Tagged ‘Rails’

Why Rails Will Not Reign Supreme (and why you shouldn’t want it to)

Saturday, March 22nd, 2008

A while ago, Christian Sepulveda wrote an interesting post Why Rails will Reign Supreme. This is the latest variation on a theme that’s beginning to resound in the Rails community, and I can’t help but to disagree.

EDIT : Christian also has a follow-up post, Why Rails will Reign Supreme, revisited .

For the record, I am a big fan of both Ruby and Rails, and use them both in my daily work and personal projects. However, I cannot see them going mainstream anytime soon, or in Christian’s words, “Ruby/Rails can and will replace Java as the language and platform of choice for software development in the enterprise”.

Other worthy contenders have tried and failed1

Python has had a reasonable foray into the enterprise with Zope/Plone2, Twisted, and now Django and Turbogears. Heck, it’s one of Google’s “big four” languages for crying out loud. What better endorsement than that? But Python job numbers aren’t even close . Hmm what’s this? Rubyists haven’t even overtaken Pythonistas yet? What’s with all the hyperbole then? I know the growth chart shows Ruby growing at a phenomenal rate, but even then how many years before it can Eclipse Java?

Enterprise. Powerful language. Does not want.

So now Ruby, with funky belligerent offspring Rails in tow, is the next challenger. But it is too similar to Python. Simply put, Ruby the language is much too powerful for the mainstream — the same way Python is too powerful, the same way Lisp is too powerful. (This is using Paul Graham’s definition of power of course.)

The reason powerful languages fail is simple. Big enterprise does not want them. Remember, big enterprisey non-tech companies don’t like to hire superstar programmers (who typically prefer powerful languages). They prefer to have developers as low-cost interchangeable parts. It doesn’t matter if they have to hire 50 of them. Google around for some combination of the words ‘average java team size’ and see for yourself.

Java with design patterns3 scales a lot better for average programmers. Bear in mind I’m talking scalability with regards to team size, not with regards to deployment.

With Java, you lack a convenient way to use functions as first-class data . You can’t just open up a class and change default behavior like you can in Ruby. There is limited, if any, meta-programming. Roadblocks and safety nets abound. All these are reasons why many coders spurn Java, but they are the exact same reasons why it is so useful in big teams of mediocre coders. (You’re going to see me using terms like ‘mediocre coders’, ‘average programmers’, etc a lot. No slight against the top Java developers, but there are millions of Java developers and by definition most of them are average.)

Anyway, all this leads to senior developers being able to dump each programmer into a nice little cubbyhole coding a Facade/Adaptor/Observer where he isn’t likely to break anything. Half the naughty things generic programmer ID81732 can do should be caught during compile, and the rest that might get through, well there’s static analysis tools . With dynamic languages, there aren’t those safety nets4.

You can mention the wonderful Ruby/Rails ecosystem. Yes I know we have plug-ins and gems out the wazoo. The huge missing piece here is schools. Java has a veritable JavaCoderFactory of vocational schools and colleges churning out graduates year after year with students who know nothing but Java. In most schools, Ruby has to be learned outside of class, ie not mainstream. The battle has already been lost there, let alone 4 years later when those students are looking for jobs.

Christian concludes with “economics will dictate the needs of software and will establish the supremacy of Ruby and Rails”. I agree with the former. As for the latter, one of the chief concepts of economics is scarcity. And right now, good Ruby coders are scarce compared to their Java brethren. The scarcity of Rails coders compared to Java coders will raise the costs of developing with Rails (since with a limited supply Rails developers can charge more) to an equilibrium point with the cost of a Java team powered by a horde of mediocre code churners.

Another way of looking at this. Suppose one night DHH appeared to every CTO in the world in a vision and said “Tomorrow thou shalt start using Ruby and Rails”, and they all wake up and start looking for Ruby coders. What then? The sheer lack of programmers who know Ruby to fill that demand will mean other languages will have to be used. Mainstream? Pshhh.

I understand from Obie that a lot of big companies are starting to use Rails. That’s great news. But to truly go mainstream goes far beyond that. The companies listed all have (and need to have) a huge online presence. But going mainstream also means winning the hearts of non-tech companies who need bespoke applications. The kind of applications you never see or hear about. They don’t come with fancy gradients, social networking, or RESTful APIs. Yet million-dollar contracts are flying around to get them coded. In Java.

So what’s the big deal?

Why would Ruby/Rails developers want it to be mainstream anyway? There’s another term for becoming commonplace, and it’s called commoditization. Sure if Ruby becomes the new Java, then the early adopters suddenly have 5 years experience when everyone else has 2. That’s why this scenario is so appealing. However, mainstream also means hiring decisions will to be influenced more by non-tech people who don’t understand that 1 senior developer at 120k a year is more cost-productive than 3 junior developers at 30k per annum.

Paul Graham’s RailsConf 2006 Keynote Address, Power of the Marginal, was only two years ago. What’s happened since then? Why the obsession with being mainstream, when we’re having so much fun being on the outside?

Part of the appeal Ruby and Rails have is that they’re on the cutting edge. The moment there’s an “R2EE” out there will probably mark the beginning of the end for many. Time to look for a new language and framework. Scala on Sails. Javascript in Jail. Erlang with Extra-terrestrials. Whatever floats your boat.

PS: Once again, nothing personal against Christian Sepulveda - this essay is directed at anyone who thinks Ruby/Rails is taking over programming as we know it anytime soon - his just happens to be the most recent such post I have come across.


1 By ‘failed’ I mean that it still plays second fiddle to Java and C# in market adoption.

2 Yes I don’t think Zope/Plone then comes close to Rails now, but you have to consider the state of software frameworks available across all languages at that time.

3 For a hilarious look at Java, check out Steve Yegge’s Execution in the Kingdom of Nouns

4 Yes I know dynamic languages espouse testing, but good Java shops use tests too. Static analysis is something Ruby/Python et al cannot do.


CitrusByte developer Tim Goh aka ProgProg was previously a Python/Django hacker but is now a Ruby/Rails convert. He has been charged multiple times with method_missing abuse.

Sass production woes in Rails

Thursday, March 20th, 2008

Rails 2.0+ introduced our team at CitrusByte to a Sass woe: our sass file would not regenerate. We would be able to regenerate it when ssh’ing into the site and starting the server in an environment other than production. Obviously not an ideal solution. So, what’s the fix?

Place the following in any ruby file in your rails_root/config/initializers/some_file.rb:

p "** Ensuring the Sass::RAILS_LOADED is undefined for regeneration"
if defined?(Sass::RAILS_LOADED)
  module Sass
    remove_const("RAILS_LOADED")
  end
end

Here’s a technical description of the problem:

In Rails, Sass runs through the ActionController::Base.process command with an alias_method. The update_stylesheets method is never surfaced outside of there in the Rails environment; this makes it difficult to run it in a Capistrano task.

Also, the Sass file needs to be regenerated after the plugins load. It’s not ideal to place this in the environment.rb, because most of this runs before other configuration parameters. The initializers load after the plugins load and is the final block of code that is run on the init block. That makes an initializer a perfect place to put our fix.

Effortlessly farm work to an EC2 instance without batting an eye

Wednesday, February 20th, 2008

Inline media uploading and processing in ruby

Today, we released a new gem to make integrating off-site processing easy. With this gem and utilizing Amazon’s EC2 cloud and S3 storage back-end, you can easily process media files in a non-blocking, threaded way and do it all inline with the rest of your application.

First download the gem and all it’s dependencies by typing:

sudo gem install processor_pool -y

This gem is built on top of Sinatra. So, if you are familiar with the Sinatra syntax, then you’ll be right at home. If not, I highly suggest you check it out.

First,

# In a file called media_processor.rb
require "rubygems"
require "processor_pool"
 
# Set the access_key_id and the secret_access_key provided by amazon
access_key_id, secret_access_key = 'accesskey', 'somethinglongandfuzzyhere'
 
# And start the processors!
ProcessorPool.start(access_key_id, secret_access_key)

Add to the end of the file

get '/' do
  "Show me the money"
end

That’s it. Start your server up with

ruby media_processor.rb

And now you are ready to go to allow a user to hit the EC2 cloud (more on this later) and be responded with the “Show me the money” text.

Replace the above content with the following

class PFile
  attr_accessor :filename, :temp_file
  @@temp_directory_base = "tmp"
  def initialize(f)
    self.filename = f[:filename]
    self.temp_file = f[:tempfile]
    @media_file = nil
    @temp_dir
  end
  def delete!
    #File.unlink(self.temp_file)
    self.temp_file.delete
  end
  def store_in_temp_directory
    @temp_dir = File.join(@@temp_directory_base, @upload.member_id, @upload.media_file_id)
    FileUtils.mkdir_p(@temp_dir)
    filename = File.basename(self.filename)
    FileUtils.mv(self.temp_file.path, File.join(@temp_dir, filename))
    @media_file = MediaFile.new(File.join(@temp_dir, filename))
  end
  def process!
    ImageConverter.new(@media_file).convert
    @media_file.delete!
    "Processed images for #{@media_file.original_file_name}"
  end
  def save_to_storage
    # DO THE S3 saving here
    MediaDirectory.new(@temp_dir).copy_to_s3(STORAGE_BUCKET, upload_prefix, true)
  end
  def upload_prefix
    "#{@upload.member_id}/#{@upload.media_file_id}"
  end
end
class ImageConverter
  def resize_to(name, image = nil)
    img = image || Magick::Image.read(self.file.file_name).first
    named_size = self.class.named_sizes[name]
    if named_size =~ /c$/
      dimensions = named_size.gsub(/c$/, '').split('x')
      img.crop_resized!(dimensions[0].to_i, dimensions[1].to_i)
    else
      img.change_geometry!(named_size) do |cols, rows, img|
        img = img.resize(cols, rows)
      end
    end
    img.write(path_to(name))
  end
end
post '/new' do
  @file = PFile.new(params[:file])
  @file.store_in_temp_directory
  @file.save_to_storage
  redirect params[:success_url] if params[:success_url]
end

If you look closely, we are just using some simple classes to handle our conversion. But ignoring that, we are sending a post to the server with file parameters. We can send the process a file and the rest is history. Notice that this all happens on a remote server. None of this is handled locally.

That’s it.

Of course, you can imagine how you could extend this to include movie types and other types of processing, but for the time being, let’s focus the images. (Although I will be showing how to do this in rails, you can use this with any back-end, PHP, Java, etc.)

Let’s add a convenience method in the application.rb

def url_for_upload_server
  if RAILS_ENV == 'production'
    # determine the upload server to try to use
    p = Processors.get_random_processor(::SERVER_POOL_BUCKET)
    "http://#{p.hostname}:4567/new"
  else
    p = Processors.get_random_processor(::SERVER_POOL_BUCKET)
    "http://localhost:4567/new"
  end
end

In your upload controller add

@upload_url = url_for_upload_server

Now, in your upload view, add

<% form_for @media, :id => "fileform", :url => upload_url, :multipart => true do %>
...
<% end %>

Lets handle this inline and non-blocking

Add a button field to the end of your form like so:

<button class="button" id="buttonUpload"  onclick="return beginFileUpload();">Upload</button>

and this to your javascript file (note, this is jQuery)

function beginFileUpload()
{
    // todo: some kind of feedback that upload has started...
    $("#uploading").show();
    $("#buttonUpload").hide();
 
    $("#validation_code").load('<%= url_for(:controller=>"media_file", :action=>"upload") %>',
          {    title: $("#title").val(),
               description: $("#description").val()
          },
          function(responseText, textStatus, XMLHttpRequest) {
              $("#validation_code").val(responseText)
              if (textStatus == 'success')
                  completeFileUpload();
              else
                  alert('Error uploading file.');
              });
    return false;
}
 
function completeFileUpload()
{
    $("#fileform").submit();
    return false;
}

With that, you have inline file uploading with inline image processing off-site.

For more information, check the docs at http://rubyforge.org/projects/processorpool/