Friday, June 01, 2007

Ruby on Rails Domino Active Record

I have been using this class for a while that allows some ActiveRecord like features for IBM Lotus Domino databases.

An example use would be:


class Contact < DominoRecord
attr_accessor :email_address, :first_name, :last_name

validates_presence_of :first_name, :last_name, :email_address

validates_format_of :email_address, :with =>

/^\s*(?:(?:[^,@\s]+)@(?:(?:[-a-z0-9]+\.)+[a-z]{2,}\s*(,\s*|\z)))+$/i,

:message => "must be a valid email address",

:on => :create

@@database_name = "contacts.nsf" #your database here

@@server_name = 'www/projectlounge' #your server here

end


It currently uses the OLE/COM bridge to get to domino so only will work on windows at this stage. The next step would be to compile it with the NotesAPI to allow it to work on all platforms.

The model source code is here (I do not maintain this anymore but happy for others to take it on under LGPL):


require 'win32ole'

class DominoRecord



attr_accessor :form



def save

@document = @@database.CreateDocument

form = self.class.to_s.downcase

@document.ReplaceItemValue "form", form

self.instance_variables.each do |inst|

field_name = inst.sub(/[@]/, '')

field_value = self.instance_variable_get(inst)

@document.ReplaceItemValue field_name, field_value.to_s

end

@document.Save(true, false)

end



def save!;

save

end

def update_attribute; end

def new_record?; end

include ActiveRecord::Validations



@@session = nil

@@database = nil

@@database_name = nil

@@server_name = nil



def [](key)

instance_variable_get(key)

end



def self.find(*args)

options = extract_options_from_args!(args)

validate_find_options(options)



case args.first

when :first then

@document = find_initial(options)

when :all then find_every(options)

else

@document = find_from_ids(args, options)

end



record = self.new

@document.Items.each do |item|

record.send(item.Name.downcase + "=", item.Text) unless ["document", "errors"].include?(item.Name)

end

record

end



def DominoRecord.human_attribute_name(attribute_key_name)

ActiveRecord::Base.human_attribute_name(attribute_key_name)

end



def initialize(attributes=nil)

unless @@session

@@session = WIN32OLE.new('Lotus.NotesSession')

@@session.Initialize

end

@@database = @@session.GetDatabase(@@server_name,@@database_name) unless @@database



unless form == nil

fields = @@database.GetForm(form).Fields

else

fields = @@database.GetForm(self.class.to_s).Fields

end



fields.each { |field| self.class.__send__(:attr_accessor, field) }



return if attributes.nil?

attributes.each do |k, v|

send(k + "=", v)

end

end



def finalize

@@session.Finalize

end



def session

@@session

end



def database

@@database

end



def document

@document

end



def attributes=(attributes)

return if attributes.nil?

attributes.stringify_keys!

multi_parameter_attributes = []

attributes.each do |k, v|

send(k + "=", v)

end

end



def self.method_missing(method_id, *arguments)



unless @@session

@@session = WIN32OLE.new('Lotus.NotesSession')

@@session.Initialize

end

@@database = @@session.GetDatabase(@@server_name,@@database_name) unless @@database



if match = /find_by_([_a-zA-Z]\w*)/.match(method_id.to_s)



@view = @@database.GetView($1)

puts arguments

@document = @view.GetDocumentByKey(arguments[0])



record = self.new

@document.Items.each do |item|

record.send(item.Name.downcase + "=", item.Text) unless ["document", "errors"].include?(item.Name)

end

record

else

super

end

end



private

def self.find_initial(options)

#not working

end



def self.find_every(options)

#not working

end



def self.find_from_ids(ids, options)

#not working

end



def self.find_one(id, options)



if result = @@database.GetDocumentByUNID( id )

result

else

raise RecordNotFound, "Couldn't find #{name} with ID=#{id}#{conditions}"

end

end



def self.find_some(ids, options)



result = find_every(options)



if result.size == ids.size

result

else

raise RecordNotFound, "Couldn't find all #{name.pluralize} with IDs (#{ids_list})#{conditions}"

end

end





def self.extract_options_from_args!(args) #:nodoc:

args.last.is_a?(Hash) ? args.pop : {}

end



VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset,

:order, :select, :readonly, :group, :from ]



def self.validate_find_options(options) #:nodoc:

options.assert_valid_keys(VALID_FIND_OPTIONS)

end

end

6 comments:

Thiru said...

Hi Ian Connor,
I am not able to get the source from svn://svn.projectlounge.com/open/DominoRecord please provide the correct link. I am intrested to test Rails and Domino.

with thanks & regards
Thiru

Rich Waters said...

nice to see some others working on ror+domino integration. I've got an authentication and login system using Domino LDAP working great. For Data I've been writing some one-off agents that spit back json since it's easy to parse in ruby.

I'm interested to see where you head with this project.

brandon said...

Has there been any progress on this, or is it abandoned?

I'd be very interested if there's progress, although I have some misgivings about the approach.

ian connor said...

You are better off using XML over http on an as needed basis.

Виктор said...

Hello, I've tried to access the svn://svn.projectlounge.com/open/DominoRecord but it seems that the url is no longer valid.

Can you give a new url, or share the code somewhere else? e.g. http://github.com

ian connor said...

The code has not been maintained by me but I am happy for others to run with it here... (updated post)