Categories
About

Nodeta is a software development company that focuses on web software. We employ a highly agile and effective process. We have worked both on light independent projects and in the environment of large global enterprises.

Archives

Ruby 1.8.6 is not compatible with Ruby 1.8.6

Otto Hilska May 29th, 2008

In Euruko 2008 Matz was participating in a panel discussion about Ruby/Win32 support. That’s where we heard one of the best Matz quotes: “I don’t use Windows, so I don’t care”

We already had this picture on our scrum board to remind us about the driving forces of Ruby programming language, but today it hit my face again: it’s not guaranteed that all Ruby 1.8.6 installations are compatible with other Ruby 1.8.6 installations.

The first release of 1.8.6 is quite old now, but there’re already 114 patches silently added to the release. None of these made it to the news section of ruby-lang.org.

This was concretized today when I was deploying rails-doc.org to a new server: the REXML library (which is a part of standard Ruby library) didn’t have REXML::Formatters available. The whole REXML library had gone through some major refactoring between different 1.8.6 patch releases. Well, “I don’t use XML, so I don’t care”.

By the way, JRuby 1.1.2 was released a couple of days ago. Go grab it. They even seem to have some kind of a release process. :)

Baby’s got a brand new design

Mikael Roos May 28th, 2008

Does a good design make a blog better? If the design has LAB TUBES IN SPACE, then yes it definitely does. Things are so busy right now that I only had time to test the layout in Firefox 2/3, Safari and in IE7 (just to see it doesn’t work). I’ll do IE-fixing and some finetuning when I get the time.

Living on the edge: JRuby + Merb + Glassfish

Otto Hilska May 22nd, 2008

I spent practically my whole evening setting up JRuby + Merb + Glassfish, so I thought I’d share my experience with those of you who are wondering if JRuby (with real threading) + Merb could be what you’re looking for in terms of scalability and ease of deployment.

Btw, everything was done on a pretty standard Ubuntu server. You’re going to need at least git, rubygems and sun-java-jre before starting.

Setting up JRuby

This is the easiest part. Just download it from JRuby.org and extract it somewhere:

vodka:~% tar zxvf jruby-bin-1.1.1.tar.gz
vodka:~% mv jruby-1.1.1 jruby
vodka:~% export PATH="/home/mutru/jruby/bin:$PATH"
vodka:~% jirb
irb(main):001:0> puts "This must be JRuby"
This must be JRuby

Setting up Merb

This was not as straight-forward as it could’ve been. I had no luck with Merb 0.9.3 (gem), so I had to get it manually. But first, some dependencies:

vodka:~% jruby -S gem install erubis rake json_pure rspec rack hpricot mime-types rubigen
JRuby limited openssl loaded. gem install jruby-openssl for full support.
http://wiki.jruby.org/wiki/JRuby_Builtin_OpenSSL
Updating metadata for 380 gems from http://gems.rubyforge.org/
complete
Successfully installed abstract-1.0.0
Successfully installed erubis-2.6.0
Successfully installed rake-0.8.1
Successfully installed json_pure-1.1.2
Successfully installed rspec-1.1.3
Successfully installed rack-0.3.0
Successfully installed hpricot-0.6-java
Successfully installed mime-types-1.15
Successfully installed activesupport-2.0.2
Successfully installed rubigen-1.3.2
10 gems installed

Now let’s get to the beef:

vodka:~% git clone git://github.com/wycats/merb-core.git
vodka:~% cd merb-core
vodka:merb-core% jruby -S rake package
vodka:merb-core% jruby -S gem install --local pkg/merb-core-0.9.4.gem

That was core. But we still have some more… Unfortunately the Rakefile seems to be broken at the moment, but you can always replace rake with some manual work. :)

vodka:~% git clone git://github.com/wycats/merb-more.git
vodka:~% cd merb-more
vodka:merb-more% for i in merb-*; do cd $i; jruby -S rake package; cd ..; done
vodka:merb-more% jruby -S rake package
vodka:merb-more% jruby -S gem install --local **/*.gem

Some of the gems depend on other libraries. Consult the error message if you want to install everything.

Anyways, now you should have merb installed:

vodka:merb-core% jruby -S merb help
Usage: merb [uGdcIpPhmailLerkKX] [argument]
Merb. Pocket rocket web framework

Creating a Merb project

Now let’s create our very own Hello world wannabe.

vodka:tmp% jruby -S merb-gen app hello_nodeta
RubiGen::Scripts::Generate
      create  log
      create  gems
      create  autotest
      create  config
      create  app
      create  spec
      create  public
      create  config/environments
      create  app/helpers
      create  app/views
      create  app/controllers
      create  app/views/layout
      create  app/views/exceptions
      create  public/images
      create  public/stylesheets
      create  autotest/merb.rb
      create  autotest/discover.rb
      create  autotest/merb_rspec.rb
      create  config/rack.rb
      create  config/router.rb
      create  config/init.rb
      create  spec/spec_helper.rb
      create  spec/spec.opts
      create  public/merb.fcgi
      create  config/environments/production.rb
      create  config/environments/test.rb
      create  config/environments/rake.rb
      create  config/environments/development.rb
      create  app/helpers/global_helpers.rb
      create  app/controllers/application.rb
      create  app/controllers/exceptions.rb
      create  app/views/layout/application.html.erb
      create  app/views/exceptions/not_found.html.erb
      create  app/views/exceptions/internal_server_error.html.erb
      create  app/views/exceptions/not_acceptable.html.erb
      create  public/images/merb.jpg
      create  public/stylesheets/master.css
      create  /Rakefile

Generating controllers works the same way as in Rails:

vodka:hello_nodeta% jruby -S merb-gen controller testing

After that we can add our greetings to app/views/testing/index.html.erb.

That’s it, now let’s get it running.

Installing Glassfish

You can use whatever application server you want. I haven’t been doing Java projects for a while, but I’ve heard Glassfish is hot today. So let’s use it.

I downloaded the Linux installer:

vodka:tmp% java -Xmx256m -jar glassfish-installer-v2ur2-b04-linux.jar

Somehow Ruby software is never this easy to install… Anyways, we’re going to need ant before going to the next step. You can usually find it using your package manager. Let’s continue:

vodka:tmp% cd glassfish
vodka:glassfish% ant -f setup.xml
...
create.domain:
     [exec] Using port 4848 for Admin.
     [exec] Using port 8080 for HTTP Instance.
     [exec] Using port 7676 for JMS.
     [exec] Using port 3700 for IIOP.
     [exec] Using port 8181 for HTTP_SSL.
     [exec] Using default port 3820 for IIOP_SSL.
     [exec] Using default port 3920 for IIOP_MUTUALAUTH.
     [exec] Using default port 8686 for JMX_ADMIN.
     [exec] Domain being created with profile:developer, as specified by variable
AS_ADMIN_PROFILE in configuration file.
     [exec] Security Store uses: JKS
     [exec] Domain domain1 created.
     [exec] Login information relevant to admin user name [admin] for this domain [domain1]
stored at [/home/mutru/.asadminpass] successfully.
     [exec] Make sure that this file remains protected. Information stored in this file will be
used by asadmin commands to manage this domain.
   [delete] Deleting: /home/mutru/tmp/glassfish/passfile

BUILD SUCCESSFUL
Total time: 31 seconds

There it is. We can already start the server:

vodka:glassfish% ./bin/asadmin start-domain domain1

We can leave it running when preparing the next step.

Deploying your Merb application to Glassfish

The key here is a gem called Warbler. It comes bundled with JRuby-Rack. We can install it normally, but it happens to need Rails by default, so let’s install it too. And ActiveRecord with JRuby of course needs a JDBC adapter.

vodka:~% jruby -S gem install rails warbler activerecord-jdbc-adapter

Now we can start setting up our project. First we can create a Warble configuration file and try to build our first WAR archive:

vodka:hello_nodeta% jruby -S warble config
vodka:hello_nodeta% jruby -S warble war
jar cf hello_nodeta.war -C tmp/war .

Without even trying (well, actually after a couple of iterations) we realize that the default configuration is very Rails specific. So let’s edit the configuration file:

vodka:hello_nodeta% cp tmp/war/WEB-INF/web.xml config/web.xml

At the bottom of the file there’s something that seems to be tied to Rails. Use this instead:

  <listener>
    <listener-class>org.jruby.rack.merb.MerbServletContextListener</listener-class>
  </listener>

config/warble.rb also needs to know about our gems.

config.gems = ["activerecord-jdbc-adapter", "merb-core"]

Before deploying, let’s add one more route to your config/router.rb:

  r.match("/hello_nodeta/testing").to(:controller => "testing", :action => "index")

Now we’re ready to deploy:

vodka:hello_nodeta% cp hello_nodeta.war ~/tmp/glassfish/domains/domain1/autodeploy/

You can see that Java is starting to eat all of your CPU time. It means that Glassfish has noticed your WAR file and is deploying it.

You can take your browser to http://127.0.0.1:8080/hello_nodeta/testing and see the fancy index.html.erb you wrote earlier.

I’ll do some benchmarking later when I can set up a real benchmarking environment, but at least Glassfish seems to handle concurrent connections nicely out-of-the-box:

vodka:~% ab -c 400 -n 5000 http://127.0.0.1:8080/hello_nodeta/testing
This is ApacheBench, Version 2.0.40-dev <$Revision: 1.146 $> apache-2.0
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Copyright 2006 The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 500 requests
Completed 1000 requests
Completed 1500 requests
Completed 2000 requests
Completed 2500 requests
Completed 3000 requests
Completed 3500 requests
Completed 4000 requests
Completed 4500 requests
Finished 5000 requests

Server Software:
Server Hostname:        127.0.0.1
Server Port:            8080

Document Path:          /hello_nodeta/testing
Document Length:        495 bytes

Concurrency Level:      400
Time taken for tests:   13.471622 seconds
Complete requests:      5000
Failed requests:        0
Write errors:           0
Total transferred:      3150000 bytes
HTML transferred:       2475000 bytes
Requests per second:    371.15 [#/sec] (mean)
Time per request:       1077.730 [ms] (mean)
Time per request:       2.694 [ms] (mean, across all concurrent requests)
Transfer rate:          228.33 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    2  10.5      0      64
Processing:    32 1043 302.5    982    1893
Waiting:       31 1042 302.6    982    1893
Total:         36 1046 299.3    984    1893

Percentage of the requests served within a certain time (ms)
  50%    984
  66%   1067
  75%   1111
  80%   1151
  90%   1551
  95%   1685
  98%   1743
  99%   1759
 100%   1893 (longest request)

Stay tuned. :)

Why you really need to let your tests fail first

Otto Hilska May 13th, 2008

Test-driven development (or even Behaviour-driven development) is increasingly popular, but sometimes old habits make you write tests afterwards – just to keep your test coverage up.

This is just plain wrong.

If you’ve never seen your test fail, how can you know that you’re testing the right thing? Let’s imagine we were trying out XMLBuilder for the first time:

module Demo
  def self.build_xml(xml)
    xml.parent do
      xml.child :id => 1
    end
  end
end

Could it get any easier. We already have a parent tag. Now let’s write the test afterwards:

require 'demo'

require 'rubygems'
gem 'builder'
require 'builder'

describe Demo do
  before(:each) do
    @xml = Builder::XmlMarkup.new(:indent => 2, :encoding => 'UTF-8')

    Demo::build_xml(@xml)
  end

  it 'should have a parent' do
    @xml.should match(/

Indeed, it seems that we have a parent. And our test coverage is blasting 100%.

However, just out of curiosity, let's try writing an invalid test:

it 'should not behave like this' do
  @xml.should match(/THIS WAS NOT IN MY XML/)
end

It still passes. Why? Well, XMLBuilder's method_missing happens to catch RSpec's should method. All possible matchers just pass. When outputting XML everything could seem to be in order.

And this is not the only problem. I've even seen cases, where someone is using a clever do_as(:user_name) helper to run tests under a certain user. Too bad that the helper method itself happened to be broken and even the craziest tests passed.

I pretty much assume that if a certain code block is not unit tested, it's broken. The same goes with tests: if you haven't seen a spec failing before implementing the feature, there's usually something wrong with the spec.

Project management software written in Rails, part N

Otto Hilska May 9th, 2008

We’ve been using trac for a long time. It’s great, but it’s written in Python. That actually shouldn’t matter, but patching and running a Rails application would be much easier for us.

Existing solutions have mostly been direct trac rip-offs, except that they’re usually slower, missing some features and visually less appealing. Probably that’s also why their development stalls at some point (see Collaboa for example).

Now that Redmine has reached 0.7.0, let’s have a quick look at its features.

CruiseControl.rb integration

This is only a plugin, but haven’t you always wanted your CruiseControl log and issue tracking in one place? Here it is, the SimpleCI plugin.

Truly multiuser and multiproject

You don’t have to set up multiple Redmine instances to serve multiple projects. Everything is done in the web configuration UI.

Configuration

Everything else is also configured in the web UI. Sometimes even too much: Redmine is not very usable without any configuration.

Workflow can be defined precisely to suit your needs. For example, if you want that developers are not allowed to close tickets before they’ve gone through testing, you can configure different states for the tickets and who’s allowed to do what. This is nothing new in “enterprise” issue trackers, but trac didn’t have this feature before 0.4.

Remote SCM support

trac is pretty much limited to local Subversion repositories. Redmine works well with remote SVN repositories, and it even supports some other SCMs. Git support should be almost there…

Conclusion

Redmine has tons of features you’d never expect to see in a software that’s mostly developed by one guy. Time tracking, Gant charts, localization… You name it.

Even though the user interface may not please everyone (it could still learn some simplicity from trac), it’s definitely worth checking out.