Wednesday, August 05, 2009

Differences JRuby JDBC-Adapter for mysql and Ruby MysqlAdapter in method: pk_and_sequence_for


This will result in a bad schema.rb when using non default primary-keys and thus test databases are not created correct.

JRuby

>> connection = ActiveRecord::Base.connection
=> #<ActiveRecord::ConnectionAdapters::JdbcAdapter:0x321bc965>
>> connection.respond_to?(:pk_and_sequence_for)
=> false
>> connection.respond_to?(:primary_key)
=> false


Ruby

>> connection = ActiveRecord::Base.connection
=> #<ActiveRecord::ConnectionAdapters::MysqlAdapter:0x19ad73c @co...
>> connection.respond_to?(:pk_and_sequence_for)
=> true
>> connection.respond_to?(:primary_key)
=> false

Friday, July 24, 2009

Running Rails 2.3.3 on JRuby 1.3.1 with MS-SQL 2008


This document is derived from codersifu - Sudirman

We need to run Rails 2.3.3 on JRuby 1.3.1 (Java 6) in combination with MS-SQL Server 2008.
There is no activerecord-jdbc-adapter for MS SQL Server so you need to do something else.

This should work on Mac OS-X Leopard and Debian Lenny.

jruby -S gem install activerecord-jdbc-adapter

Download MS JDBC driver version 2.0 from Microsoft MSDN

After your download complete, extract sqljdbc4.jar into your jruby\lib directory.

You need to modify your database.yml. Here is the example:


development:
adapter: jdbc
username: sa
password:
driver: com.microsoft.sqlserver.jdbc.SQLServerDriver
url: jdbc:sqlserver://database-server;databaseName=trogdor_development

Monday, April 13, 2009

Be careful with callbacks and log messages in Rails



A lot of times I see a very annoying 'feature' in rails projects.

People using log messages in some callbacks of ActiveRecord to display some values.

before_save :add_generated_order_number

def add_generated_order_number
self.order_number = SequenceNumber.next_order_number
log.debug "Added #{order_number}"
end

This is potentially dangerous, since log has a very nice feature. It returns a value depending on the log level. If log level is on DEBUG (typically development and test mode setting) it will return TRUE, if log level is on INFO (typically production mode setting) it will return FALSE, breaking the chain of callbacks, thus the record is NOT being saved. Make sure you're testing your code against production mode. Put the loglevel in your test environment setting to the same value as your production setting.

See add function return values:
http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/classes/Logger.html#M000901

Sunday, June 03, 2007

ActiveProperty



I'm a Ruby on Rails developer and I was missing some functionalities in the area of property and configuration of applications. In my previous job as a Java developer, I used property files to define environment specific values. When moving from development to test or production changing the property files would configure file paths and so on.

RoR has a very powerfull testframework with fixtures so most of the property functionalities are not necessary anymore, because you can use the fixtures to do that. One area I couldn't get configured without the use of some kind of properties, paths for file modifications.
RoR has several environment.rb files to set some of the differences between production, test and development, but that is not very intuitive to me. That's why I wanted to use an ActiveRecord to read and store my properties.

The ActiveProperty was born..

The main reason why I wanted something like an ActiveProperty is because:
  1. I wanted a clear distinction between production properties and test properties.

  2. By storing these properties as ActiveRecord, we can use fixtures to make sure we can test everything without have to write testspecific stuff like "path = '/../mocks/test/' if RAILS_ENV == 'test' " in controllers and models.

  3. Using the a scaffold controller on the ActiveProperty gives you an easy interface to maintain the properties.


For test purposes these properties can be simply overridden by the active_properties.yml fixture
This is especially handy when working with files. Production paths can be loaded with migrations and functional tests use values from the fixture.

By superclassing ActiveProperty, you can create multiple scopes for your properties.

Usage:
Superclass this class and set your scope fe ApplicationProperty
  • ApplicationProperty.spoon -> "no key found"
  • ApplicationProperty.spoon = "There is no spoon" -> "There is no spoon"
  • ApplicationProperty.spoon -> "There is no spoon"
Author: Jeroen Knoops
Company: ViCi BV - Eindhoven

ActiveProperty application: ActiveProperty.zip

active_property.rb:


# ActiveProperty.
#
# This class can be used to store properties. The main reason why I made this is because
# I wanted a clear distinction between production properties and test properties.
# By storing these properties as ActiveRecord, we can use fixtures.
#
# For test purposes these properties can be simply overridden by the active_properties.yml fixture
# This is especially handy when working with files. Production paths can be loaded with migrations and functional tests use values from the fixture.
#
# By superclassing ActiveProperty, you can create multiple scopes for your properties.

# Usage:
# Superclass this class and set your scope fe ApplicationProperty
# * ApplicationProperty.spoon -> "no key found"
# * ApplicationProperty.spoon = "There is no spoon" -> "There is no spoon"
# * ApplicationProperty.spoon -> "There is no spoon"
#
# Maintaining the properties can be done by scaffolding the ActiveProperty model.
#
# Author: Jeroen Knoops jeroen@vici-nl.com
# Company: ViCi BV - Eindhoven

class ActiveProperty < ActiveRecord::Base

@scope = self.name

class << self

def method_missing(method_id, *args)
begin
super(method_id, args)
rescue
method_id_string = method_id.id2name
if method_id_string[method_id_string.length - 1] == 61
result = set_value(@scope, method_id_string[0...-1], args[0])
else
result = get_value(@scope, method_id.id2name)
end
result || "No active property found in scope #{@scope} for: #{method_id}"
end
end

private

def get_value(scope, key)
ap = find(:first, :conditions => ["`key` = ? and `scope` = ?", key, scope])
if ap
return ap.value
end
return nil
end

def set_value(scope, key, value)
ap = find(:first, :conditions => ["`key` = ? and `scope` = ?", key, scope])
if ap
ap.value = value
ap.save!
else
self.create!(:scope => scope,
:key => key,
:value => value)
end
end
end
end