Sunday, June 17, 2007

Code Digest #1

When you program for a living, you write lots of code. There is often some code that you are fond of. We start the Code Digest series to present such code written by the RHG developers. We encourage other teams and individual developers to share similar snippets in their blogs so we all can learn from each other and become better rails developers.

Daniel Silva



<%= link_to(image_tag("/images/icon.gif", :style=>"padding-right:0px;"), {:controller => '/registration', :action => 'login', :dest => request.request_uri}, {:style => ""}) %> 

With this line, I can create an image link using Rails code, to take advantage of the power of routes in Rails while still creating this kind of structure: <a href="#"><img border="0"/></a>



Jack Dempsey



Ok, so here's something useful for using keyboard short cuts to navigate through a small supporting application:
def include_keyboard_shortcuts

hotkey = 'Ctrl+Shift'
code_string = ''
keys = {
'm'=>'/account_info/show',
's'=>'/account_info/index',
'c'=>'/menu/go?menu_action=new_customer',
'p'=>'/products',
'o'=>'/account_info/orders',
't'=>'/order_tracker',
'x'=>'/auth_sessions/destroy',
'r'=>'/current_users/reset_password'
}

current_user_needed = %w{m o r}

keys.each_pair do |k,v|
next if current_user_needed.include?(k) && current_user.nil?
code_string << "shortcut('#{hotkey}+hm#{k}',function() { document.location.href='#{v}' });\n"
end

javascript_tag(code_string)

end



Then in a layout file just:
<%= javascript_include_tag 'shortcuts' %>
<%= include_keyboard_shortcuts %>



Makes use of a nice shortcuts.js lib.


Val Aleksenko


This is a fragment of a conversation on our internal IRC with my solution for refactoring a piece of code:
May 07 14:07:56 <Anthro> There has to be a better way to do this (x and y are non-negative integers): increment = (x!=0) ? ( (y!=0) ? 0 : 1 ) : -1
May 07 14:08:22 <Anthro> I think it can be done with math instead of logic.
May 07 14:16:56 <jack> Anthro: have you thought about using sin and cos? ;-)
May 07 14:18:42 <muzzy> (x <=> 0) <=> (y <=> 0)



Granted, it is not as clear as the original, but it is hard to resist it from the aesthetic point of view.

Val Aleksenko


I love writing dynamically generated methods and classes in Ruby as the next guy. When I was writing acts_as_readonlyable, I needed to manage multiple connections. I found that the easiest solution for managing active connections was generation of an AR class for each read-only definition and borrowing the connection from it. The usage is acts_as_readonlyable :read_only_entry_in_database_config.
def acts_as_readonlyable(readonly_db)
define_readonly_class(readonly_db) unless ActiveRecord.const_defined?(readonly_class_name(readonly_db))
end

def readonly_class_name(db)
"Generated#{ db.camelize }"
end

def define_readonly_class(db)
ActiveRecord.module_eval %Q!
class #{ readonly_class_name(db) } < Base
self.abstract_class = true
establish_connection configurations[RAILS_ENV]['#{ db }']
end
!

end



Warren Konkel


The ConfigFile class is a quick and easy way to load YAML files located in your Rails application's config directory. Simply place a YAML file into your config directory and then access it like a hash. For example this will read your config/database.yml: ConfigFile['database']['production']['adapter'].
class ConfigFile
def self.[](arg)
@@cached_configs ||= {}
@@cached_config_mtimes ||= {}

base_name = "config/#{arg}.yml"
filename = File.join(RAILS_ROOT, base_name)
raise "ERROR: Config not found: #{base_name}" unless File.exists?(base_name)

if @@cached_configs[arg].nil? || @@cached_config_mtimes[arg] < File.stat(filename).mtime.to_i
@@cached_configs[arg] = YAML.load(File.open(filename))
@@cached_config_mtimes[arg] = File.stat(filename).mtime.to_i
end

@@cached_configs[arg]
end
end

Thursday, June 07, 2007

Our Contribution to Advanced Rails Recipes

We submitted a few recipes, answering the call for sharing what is happing in the rails community, and a half a dozen of them were accepted as entries to the upcoming book. If you enjoyed reading the blog, you would soon have a chance to see some of it on paper.

Monday, June 04, 2007

Revolution Health Traffic Doubles in 4 Weeks

Hitwise has recently publish traffic statistics on our growth over the past month.

This Wednesday AOL Founder Steve Case spoke at D5 about his new venture, Revolution Health. Revolution Health is setting out to be far more than just a health information source on the web, but it is already succeeding on that front. For the week ending May 26, 2007, Revolution Health was ranked #11 in the Health & Medical - Information category, and traffic increased by 113% since the week ending April 28, 2007, when it was ranked #23.

Sunday, June 03, 2007

Ruby vs. JRuby - The path to consistent behavior + OpenSSL

In Ola Bini's recent post There can be only one, a tale about Ruby, IronRuby, MS and whatnot, he stresses the importance of documenting Ruby's API so that other implementations can match MRI's behavior. I have to strongly agree. We've recently been bitten by this, which I'll outline using OpenSSL as an example.

Ruby's OpenSSL library does not strictly enforce a ciphers's initialization vector (IV) size specification. It simply truncates to the appropriate size.

Ola's JRuby OpenSSL library (which is awesome btw!) leverages the Bouncy Castle's JCE implementation which strictly enforces IV specifications.

Below is a simple example that works fine on MRI, and fails on JRuby.

require 'openssl'
text = "abc123"
cipher = OpenSSL::Cipher::Cipher.new("des-ecb")

#The DES Cipher specifies a length of 8 bytes, the below IV exceeds that
key, iv = "1234567890", "1234567890"

cipher.encrypt
cipher.key, cipher.iv = key, iv
encrypted = cipher.update(text) << cipher.final

cipher.reset
cipher.decrypt

#I double the length of the cipher here, just to prove that MRI truncates it.
cipher.key, cipher.iv = key*2, iv*2
decrypted = cipher.update(encrypted) << cipher.final
puts "#{text} == #{decrypted}"



> jruby test_openssl_iv.rb
java.security.InvalidKeyException: DES key too long - should be 8 bytes
at org.bouncycastle.jce.provider.JCEBlockCipher.engineInit(Unknown Source)
at org.bouncycastle.jce.provider.JCEBlockCipher.engineInit(Unknown Source)
at javax.crypto.Cipher.init(DashoA12275)
at javax.crypto.Cipher.init(DashoA12275)
at org.jruby.ext.openssl.Cipher.doInitialize(Cipher.java:416)
at org.jruby.ext.openssl.Cipher.update(Cipher.java:432)

> ruby test_openssl_iv.rb 
Result is: abc123 == abc123



Yes, this is obviously an edge case, and one could argue that the Java implementation is more correct than MRI's, but the goal is any piece of code should run the same on either platform.

Charles Nutter (Headius) has started a RubySpec Wiki where I've noted this issue there.

I've submitted a bug against JRuby to track the issue, but arguably its not a JRuby bug. Its a difference between BouncyCastle and SSLeay's implementation. I'd be interested in some community feedback on how to correctly implement this. For now, we've worked around this by simply truncating if the RUBY_PLATFORM =~ /java/, which I know if horrible, but it allows me to move on.

Monday, May 28, 2007

Ruby-esque JMX -- Part 2

Below is the code from the JMX tinkering. I snake-ized the keys to be more Ruby-esque, per Ed's suggestion.

require 'java'
include Java

# Obviously stolen from Rails ActiveSupport for underscoring strings
class String
def underscore
self.to_s.gsub(/::/, '/').
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
gsub(/([a-z\d])([A-Z])/,'\1_\2').
tr("-", "_").
downcase
end
end

module JMX
class MBean
include_class 'javax.management.ObjectName'
include_package "java.lang.management"
include_package "javax.management"
include_package "javax.management.remote"
include_class "java.util.HashMap"
attr_accessor :name
def initialize(object_name)
@object_name = object_name
@name = @object_name.to_s
end

def attributes
attrs = MBean.connection.getMBeanInfo(@object_name).attributes rescue []
attrs.inject({}) do |list, a|
list[a.name.underscore] = MBean.connection.getAttribute(@object_name, "#{a.name}") rescue "Unknown"
list
end
end

def self.find_all_by_name(name)
object_name = ObjectName.new(name)
beans = MBean.connection.queryMBeans(object_name,nil )
beans.collect {|bean| MBean.new(bean.get_object_name)}
end

def self.find_by_name(name)
#obviously inefficient
find_all_by_name(name).first
end

def self.connection
@@mbsc ||= begin
#load from some config file later maybe
url = "service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi"
connector = JMXConnectorFactory::connect(JMXServiceURL.new(url), HashMap.new)
connector.getMBeanServerConnection
end
end
protected

def method_missing(method, *args, &block)
attributes.keys.include?(method.to_s) ? attributes[method.to_s] : super
end
end
end

#Find all the MBeans matching some object name
mbeans = JMX::MBean.find_all_by_name("java.lang:*")
puts "Found #{mbeans.size} beans"

#Find a bean, and get its attribute
bean = JMX::MBean.find_by_name("java.lang:type=ClassLoading")
puts "There are #{bean.loaded_class_count} classes loaded in that VM"