At least 50% of the time a client will want us to use a specific technology (.NET or Java), this makes perfect sense as a lot of our clients are corperates who have invested a lot of time and money in a particular technology.
The great news for us, and the other 50% of our clients is that we choose to use Ruby On Rails whenever possible. The reasons are many, but a few of the more profound ones are:
Productivity
Thats right, we are about 2 (maybe 3) times more productive with Rails than .NET or Java, now this isn't because we are primarily Rails guys in fact I have spent many more years working with both .NET and Java than I have with Rails, the simple fact is that Rails gives us a head start as we get so much for Free.
This is profound in two ways, firstly and most obviously it costs the client less, and enables us to create better relationships. But this productivity gain also allows us to get functionality in front of the user quicker and as such we can identify requirement changes and fix bugs sooner, meaning we deliver better quality code first time.
Agile
We work in an Agile manor and while we can do this regardless of the technology we use, Rails lends itself to an Agile process perfectly.
Solid Design Patterns
Rails encourages the developer to employ consistant design patterns, the rails guys call this Convention over Configuration which promotes maintainable well tested code.
Separation of Concerns
Rails goes beyond Separation of Concerns by removing Concerns, Object Relational Mappers take out 1000's of lines of code that would take up 50% to 80% of the developers time, simple Routing patterns eliminate tones of boiler plate code.
The baked in support for Model View Controller means makes its easy to keep things in the right place.
Open
Over a 10 year contacting career I hit so many hurdles using web frameworks from commercial vendors, from the ASP.NET framework to JSF I found that whenever I wanted to bend these frameworks, they snapped! And the closed source meant that I was back to the drawing board having to invent an ingenues hack, Rails however is open source and the code (especially the rails 3 code) is well written and easy to extend (partly due to the Ruby language), I can easily bend it in any way I choose.
Database Agnostic
As I mentioned the Object Relation Mapper reduces a tone of code and save you even more time, but the abstraction from the database goes even further than that, A rails application written against MYSQL will work against any Database in production, in fact we have an app that is running on SQL Server for one client and Oracle for another, while we host it for other clients on MYSQL.
Just a few reasons why we use Rails, I hope you will too.
A couple of weeks ago, our first iPhone app went live. It's called Gphushed, and it notifies you, through the magic of Apple Push Notifications, the instant an email arrives in your Google Mail inbox. The app consists of two parts - an iPhone app that you buy that you use to configure the service, and a server, that watches your inbox and sends notifications to Apple when your email arrives.
We wrote the server in Ruby initially. This allowed us to get up and running really fast, but it came back to bite us a little bit. In this article I'll share our experience, because it might be interesting to people out there who are thinking about going down a similar path.
So we chose Ruby for two reasons on the server side -- we like it, and we needed a restful service that the iPhone could interact with (and I can't think of any way of creating a restful service faster than Rails). We added a stompserver, because messaging solves a few problems that we knew we'd come up against (notifiying any listener of changes to an account's configuration, for example). We then wrote the listener, which was effectively an infinite loop that created a new Thread for each mailbox that needed to be monitored.
As you can imagine, this service has many edge cases that need to be handled in an elegant manner, and Ruby let us sketch out the code paths really well. We were using net/imap to monitor the mailbox (monkey patching it to add support for the IDLE command), and while it worked fine in development, it didn't hold up so well in the real world.
We still can't say exactly why our Ruby implementation wasn't reliable, but before we'd even gone live we knew it needed to be re-written. If we left the service running for 24 hours or so, it would silently die. We'd added exception handling, and were trying in vain to capture socket errors and the such like, but everything appeared fine, it just didn't work -- we weren't getting notifications of new messages.
Luckily, Apple's app approval process takes a couple of weeks, so we wrote a proof-of-concept replacement in Java using JavaMail. When we saw that it was good, and that exceptions were consistently thrown (so we knew we'd be able to handle sockets being closed and other unexpected errors), we simple re-wrote the service in Java, using our Ruby implementation as the spec. This worked really well -- we had a complete re-write in two days. The improved exception handling in Java meant that we could better understand the things that were going wrong, and we made many small, incremental improvements to the service that mean it should now be pretty solid.
So do we regret writing it in Ruby? Was that two wasted days? Absolutely not. If nothing else, Ruby allowed us to create a prototype without having to actually worry about the framework. The Java implementation was drastically quicker thanks to the Ruby template that we'd developed -- refactoring and restructuring a Ruby application is much easier than doing the same thing in Java.
The stomp server is still in there, and is working great. We usually use ActiveMQ for messaging, but the stomp clients are incredibly lightweight and appear to be more than good enough for this particular application.
We have aquired Jruby.com, and we intend to make it a useful resource for people who are interested in enterprise Ruby on Rails development. I'll explain some of our ideas here, so you can have some inkling of what to expect. We are open to any and all feedback.
We have written our latest business package, BrightSpark, in Ruby. This isn't much of a departure for us because we have quite a few Ruby on Rails projects in our portfolio, including CodeSpaces which is by far and away our largest Rails projects. However, it is the first time we have written a complete business package designed for deployment on our customers' servers in Rails.
One of the influencing factors in our decision to go this way was the ever-maturing JRuby. JRuby provides a wonderful bridge between Ruby and Java, and this opens up certain enterprise opportunities.
In our experience, Ruby gets a lot of of attention from forward thinking developers in enterprise environments, but it rarely gets any real traction. This is often down to lack of familiarity, or the perception that it is not a seriously supported language. Java, of course, is at the complete opposite end of the spectrum. Merging the two makes a lot of sense.
You would be amazed at how fast we can prototype new web applications in Rails. What would take several weeks of effort by a small team can often be accomplished in a few days with Rails. Not because it is magic, but because it is pefectly designed to enable rapid development of data driven web applications. This alone makes it a worthwhile tool for enterprise development teams -- even if it is only ever used as a prototype tool, being able to get ideas in to software form rapidly would positively affect the outcome of many projects.
So what's planned for JRuby.com? Well, as part of our work preparing BrightSpark for easy installtion, we're packaging a complete JRuby server stack. This will include an application/web server (Tomcat, Glassfish or TorqueBox), a database (MySQL, Postgresql) or adapters for commercial databases (SQLServer, Oracle, DB2) and an app store style deployment system for distributing applications (we've currently prepared two free applications; WikiWikiNoteBook and WikiWikiTaskList -- these are available as stand-alone Rails apps in our Labs section of the website).
We're also recording training videos and writing guides that help .NET and Java developers get up to speed on Rails fast. Any competent developer could learn Rails in little over a week, and with the right kind of help it could be even quicker.
This is all planned to arrive some time around the end of September. We're a small ISV, and we have a couple of other projects on the go so this date might slip a little, but we're really keen to get this started and make it a genuinely useful resource.
If like me you think that NetBeans is a superb IDE, but quiet often for some unknown reason end up firing up TextMate to fix a bug or add a new feature, only to realise you lose all of the lovely debugging tools that come with the IDE. Well we can take a step in the right direction by allowing you to break out of your controller actions and into a irb console..
First things first we need a java implementation of ruby-debug-base. Fortunately one exits at http://rubyforge.org/frs/?group_id=3085 but it is not installable remotely so download the latest version of this file into a local directory (at time of writing this was ruby-debug-base-0.10.3.1-java.gem)
Fire up terminal and go to the directory that contains the gem file and enter the following
jruby -S gem install ruby-debug-base-0.10.3.1-java.gemruby -S gem install ruby-debugO.K with that all installed successfully we can now go ahead and start up our mongrel instance so go to your rails app and issue the following command, to start mongrel
jruby --debug -S script/server --debugger
Many enterprises have an Active Directory infrastructure setup. Wouldn’t it be nice if your jruby app could exploit this already in place infrastructure to authenticate your users. ruby-net-ldap has been around for a while and allows users to do exactly this with MRi ruby and now thanks to Ola bini it is now possible with jruby.
First things first, we need to install the gem,
jruby -S gem install jruby-ldap
once the gem has been installed we can then fire up a console, execute the following command in your rails app directory
jruby -S script/console
once the console is booted enter
require 'ldap' conn = LDAP::Conn.new(host="IP Address of your server", port="LDAP Port, typically 389")
this should then return you a connection object which we can use to now use to bind to the server and form there on search the directory tree. to bind to the server we simply supply some credentials. NB: I had a problem initially as some of the usernames contained periods(.) i.e. foo.bar and this caused a few problems for these accounts, the solution was to fully qualify the name (foo.bar@componentworkshop.com)
conn.bind("username","password") do |bound_conn|
conn.search("cn=Users,dc=domain,dc=com", LDAP::LDAP_SCOPE_SUBTREE, "(&(objectclass=person))",["name","mail"]) do |entry|
puts "#{entry.inspect}"
end
endwhen ran this code will render each entry that the search has found, firstly it creates a binding which authenticates you on the server. We then pass a block to search the LDAP tree. The search method takes a few parameters, for info about these parameters have a look at Suns documentation
Now we have verified, that everything is setup and working. We can go ahead and use our new found wisdom to authenticate users from our login controller. The following code provides an example on using jruby-ldap to authenticate the users, and then add them to a user model if they do not already exist in the database.
def authenticate
attrs = ["name","samAccountName", "cn", "mail", "department", "manager"]
conn = LDAP::Conn.new(host = "127.0.0.1", port = 389)
conn.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
domain = "foobar.com"
conn.simple_bind(login + "@" + domain, password) do |conn|
conn.search("cn=Users,dc=componentworkshop,dc=local", LDAP::LDAP_SCOPE_SUBTREE, "(samAccountName=#{login.to_s})", attrs) do |entry|
user = User.find(:all, :conditions => ["login = ?", login]).first
if(!user)
user = User.create!(:name => entry.vals('cn').to_s, :login => entry.vals('samAccountName').to_s, :email => entry.vals('mail').to_s)
end
return user;
else
return User.find(:all, :conditions => ["login = ?", entry.vals('cn')]).first
end
end
end
end
This code demonstrates how you can use the Bing XML api to generate Bing search results for your Ruby (and Ruby on Rails) application
Before you can run this code, you need to register as a Bing developer and create an AppId for your application. This is quick and completely free. See http://www.bing.com/developers/ for more information.
require 'net/http' require 'rexml/document' appId = "1234567890123456789012345678901234567890" # Paste your real AppId here! search = "Lewis Hamilton" uri = URI.parse("http://api.search.live.net/xml.aspx?AppId=#{appId}&sources=Web&query=#{URI.escape(search)}") xml_response = Net::HTTP.get_response(uri).body doc = REXML::Document.new(xml_response) doc.elements.each("SearchResponse/web:Web/web:Results/web:WebResult") do |result| p result.get_elements("web:Title")[0].text p result.get_elements("web:Description")[0].text p result.get_elements("web:Url")[0].text p result.get_elements("web:DisplayUrl")[0].text p result.get_elements("web:DateTime")[0].text p "--------------------------------------------------" end
Related: Bing: Performing a basic web search over SOAP with the new Bing API 2.0
Hashes are a vital technique for validating data that has been passed between two systems. Quite often, the simplest implementation of a hash in one language won't necessarily produce the same hash-string as another language. This article gives you code snippets for three languages that produce identical hashes for any given data.
SHA-1 and MD5 are hashing algorithms. They read some content and produce a string that is relatively unique to that content.
The purpose of a hash is to identify a file as being consistent with your expectations. The two reasons I use a hash are: to ensure that a file I transmit (usually over a web service) is exactly the same as the file on the server, and also to create a ‘base-line’ hash of a downloaded file to compare against the same file at a later date (if the user has made changes the hash will change).
Note that a these hashes, while fairly robust, are not bullet proof. Two files can have the same hash despite being completely different content. These are called collisions, and are extremely rare (at least when considering collisions that happen by chance). Be aware that malicious users can purposefully create matching hashes on different files with relative ease, and this is not intended as a security mechanism (if you need to prove that a document has not changed, consider digital signatures of at least 1024 bytes in length).
The primary purpose for this article is to give programmers a quick reference implementation for the SHA-1 hash that will read a binary file on the local disk and return a hexadecimal string. I have provided implementations for Ruby, Java and .NET that all return identical values for a given file.
If you have written your own functions and are having problems matching your hashes, ensure that you are opening your files in binary mode (applies to Ruby).
Here’s the code:
.NET (C#):
public static string GenerateHash(string filePathAndName) { string hashText = ""; string hexValue = ""; byte[] fileData = File.ReadAllBytes(filePathAndName); byte[] hashData = SHA1.Create().ComputeHash(fileData); // SHA1 or MD5 foreach (byte b in hashData) { hexValue = b.ToString("X").ToLower(); // Lowercase for compatibility on case-sensitive systems hashText += (hexValue.Length == 1 ? "0" : "") + hexValue; } return hashText; }
Ruby/Rails:
def generate_hash(file_path_and_name) hash_func = Digest::SHA1.new # SHA1 or MD5 open(file_path_and_name, "rb") do |io| while (!io.eof) readBuf = io.readpartia<script type="text/javascript" src="http://www.componentworkshop.com/javascripts/extensions/tiny_mce/themes/advanced/langs/en.js"></script>l(1024) hash_func.update(readBuf) end end hash_func.hexdigest end
Java:
public static String generateHash(File file) throws NoSuchAlgorithmException, FileNotFoundException, IOException { MessageDigest md = MessageDigest.getInstance("SHA"); // SHA or MD5 String hash = ""; byte[] data = new byte[(int)file.length()]; FileInputStream fis = new FileInputStream(file); fis.read(data); fis.close(); md.update(data); // Reads it all at one go. Might be better to chunk it. byte[] digest = md.digest(); for (int i = 0; i < digest.length; i++) { String hex = Integer.toHexString(digest[i]); if (hex.length() == 1) hex = "0" + hex; hex = hex.substring(hex.length() - 2); hash += hex; } return hash; }
Note that all examples use SHA-1 by default, but can easily be changed to use MD5 by changing one line in each case (see comments). Note also that these implementations take a file, but could very easily be adapted to take a byte array, if that's what you're working with.
This snippet shows you how to detect which network ports are in use on the current machine from a Ruby script
When delpoying web applications that run their own web-servers, it is quite likely that the port you want (normally port 80) will be in use. At the very least, it is only good manners to check first.
Here’s a quick Ruby script that will loop through an array of port numbers and return the first one that is free:
require 'socket'
def find_unused_port
ports = [80, 8000, 8080, 8800, 8088, 8001, 8002, 8003, 8004, 8005, 8006, 8007, 8008]
port = 0
ports.each do |p|
begin
port = p
TCPServer.new('localhost', port) rescue port = 0
if (port > 0) then break; end
end
end
port
end
puts find_unused_port.to_s
It is Ruby because my own applications include a packaged version of the Ruby interpreter. This affords me a great deal of power, being able to execute .rb scripts as part of the install process. You can do similar things in other languages, or, if you don’t mind processing large amounts of text, using the command netstat -a -n, and parsing all that are “LISTENING”.
This brief article explains how you can call executables and scripts from your Ruby program.
Ruby is great for automating many tasks, and often this requires starting other programmes and collecting their output.
This is a very short article that demonstrates one way of doing this. We shall demonstrate calling the ‘dir’ command and storing the output. It’s a bit basic, but it illustrates the point.
example.rb:
p = IO.popen("dir C:\\")
puts p.readlines
You’ll notice that we’ve collected the output as a string, which we then display. In the real world you could, of course, parse the output and take further action or simply log it in a database.
This also works in Rails and allows us to build useful control panel applications. If you are running your application as a Windows service (either through Apache or Mongrel Service) you need to be aware that the account your service runs as will affect the permissions and privileges available to the commands.
I have used this technique to successfully run various commands in an automated environment, including the subversion commands which, rather cleverly, have an xml switch so that they produce their output in an easily parse-able format.
When you try and run rake solr:start or rake solr:stop it uses Kernel.fork to spawn of a child process. However in JRuby this is disabled by default due to concurrency issues. And this prevents your solr rake tasks from running under a JRuby environment...
The Problem
>When you try and run rake solr:start or rake solr:stop it uses Kernel.fork to spawn of a child process. However in JRuby this is disabled by default due to concurrency issues. There is an option to enabling fork (jruby -J-Djruby.fork.enabled=true) within JRuby but it is experimental and as the warning says, "WARNING: fork is highly unlikely to be safe or stable on the JVM. Have fun!" as it can cause all sorts of weird and wonderful side-effects.
The Solution
The solution therefore is to simply alter the solr:start and solr:stop tasks to use Kernel.exec instead. To do this find the solr rake tasks usually in {RAILS_ROOT}/vendor/acts_as_solr/lib/tasks/solr.rake. In the start task all that is required is to comment out the start of the fork block. so you only have the exec method call,
task :start do require "#{File.dirname(__FILE__)}/../../config/solr_environment.rb" begin n = Net::HTTP.new('127.0.0.1', SOLR_PORT) n.request_head('/').value rescue Net::HTTPServerException #responding puts "Port #{SOLR_PORT} in use" and return rescue Errno::ECONNREFUSED #not responding Dir.chdir(SOLR_PATH) do exec "java #{SOLR_JVM_OPTIONS} -Dsolr.data.dir=#{SOLR_DATA_PATH} -Djetty.logs=#{SOLR_LOGS_PATH} -Djetty.port=#{SOLR_PORT} -jar start.jar" sleep(5) File.open("#{SOLR_PIDS_PATH}/#{ENV['RAILS_ENV']}_pid", "w"){ |f| f << pid} puts "#{ENV['RAILS_ENV']} Solr started successfully on #{SOLR_PORT}, pid: #{pid}." end end end
and in the end task just comment out the begining of the fork block so you are left with
task :stop do require "#{File.dirname(__FILE__)}/../../config/solr_environment.rb" file_path = "#{SOLR_PIDS_PATH}/#{ENV['RAILS_ENV']}_pid" if File.exists?(file_path) File.open(file_path, "r") do |f| pid = f.readline Process.kill('TERM', pid.to_i) end File.unlink(file_path) Rake::Task["solr:destroy_index"].invoke if ENV['RAILS_ENV'] == 'test' puts "Solr shutdown successfully." else puts "PID file not found at #{file_path}. Either Solr is not running or no PID file was written." end end
you should now be able to run your solr rake tasks under jruby with out any problems.. Have fun Colin
About
We are a small British company that produces business-oriented software and solutions. These articles are a product of our daily work - information that we think might be useful to share. We hope you find them useful.
Our Software
These are some of our products. Several are open source, some are web-based and others are proprietary:
Categories
- .NET (10)
- Apple (2)
- Business (5)
- CSS (1)
- HTML (2)
- Innovation (4)
- Java (4)
- Javascript (1)
- Leadership (1)
- MySQL (2)
- Oracle (6)
- Postgres (1)
- Programming (5)
- Rails (4)
- Ruby (10)
- SQL Server (9)
- Subversion (1)
- Web (5)
- Windows Server (2)
Archives
- July 2010 (2)
- September 2009 (5)
- August 2009 (1)
- July 2009 (12)
- June 2009 (16)
- May 2009 (3)