Umgang und Mapping einer Legacy Datenbank mit Ruby (ohne Rails) mit abweichenenden Namenskonventionen mit Active Support

13th January 2010 – 614 words

h3. Einleitung/Motivation

Zur Zeit moechte ich verschiedene XML-Dialekte aus einem vorhandenen Datenbankschema gewinnen, und brauchte dazu ein ordentliches Objektrelationales Mapping, wie man es aus Rails ja kennt. (Ausprobieren! keine Zeile SQL mehr notwendig :D). Allerdings ohne Rails sondern in einem einfachen Rubyscript. Was es dort alles gibt, will ich hier mal kurz exemplarisch vorfuehren.

h3. Voraussetzung und Datenbankverbindung

Um ein Legacy relationales Datenmodell mit Ruby schoen zu mappen, ging ich letztens wie folgt vor:

Zu erst Rails (active_record ist aber auch ausreichend) installieren, falls noch nicht gemacht (fuer active_record) und composite primary keys, das uns wie der Name schon sagt, zusammengesetzte Primärschlüssel, welches unser Legacy-Schema mitunter mit sich bringt, bereitstellt. — bash sudo gem install rails composite_primary_keys —

Nun können wir uns im ersten Schritt mit unserer Datenbank verbinden:

— ruby require “rubygems” require “active_record” require “composite_primary_keys”

options = {:adapter => ‘mysql’, :database => ‘databasename’, :username => ‘username’, :password => ‘**’, :host => ‘localhost’ , :encoding => ‘utf8’} ActiveRecord::Base.establish_connection(options) —

h3. Definition der Modelle

Nun kommt dort drunter die Konstruktion der Modell-Klassen, die die Tabellen abbilden.

Angenommen wir haben eine Tags, Posts, Posts_Tags und Users Tabelle (in freier Ruby Namenskonvention) mit den folgenden Beziehungen: Post : Tag = n : m (Jobs_Tags) Post : User = n : 1

Also ein Post hat eine Anzahl Tags (über “jobs_tags”) und genau einen User.

Da das aber zu leicht wäre, gibt es folgende Handicaps :):

  • “Posts” Tabelle heißt “entries_import”, Primaerschluessel (PK) postid, Fremdschlüssel “uid” heißt “nutzer”
  • “Tags” Tabelle heißt “clouds_import”, PK tagid
  • “Posts_Tags” Tabelle heisst “clouds_import_lnk”, PK (postid,tagid) zusammengesetzter Primaerschluessel!
  • “Users”-Tabelle heißt “users”, mit PK uid

(leicht abgeändertes RealWorld Beispiel!)

Tja sieht schon recht messy aus. Aber alles machbar:

h3. Der Code

— Ruby class Post < ActiveRecord::Base set_table_name “entries_import” # posts Tabelle set_primary_key “postid” has_many :tags, :through => :taggings has_many :taggings, :primary_key => “postid”, :foreign_key => “postid” belongs_to :user, :foreign_key => “nutzer”

Extra Points! Unser ‘Post’ hat ein “visible” Attribut, welches

geradezu nach einem named scope schreit! :)

named_scope :visible, :conditions => {:visible => 1}, :order => “pubDate desc”

#Extra Points! Tags direkt als (Komma)getrennte Liste zurueckgeben lassen, und

noch den Tag “Blog” auf jeden Fall ans Ende der Tags haengen

def tag_list(sep = “,”) (tags.map(&:name) « “Blog”).uniq.join(sep) end end

class Tag < ActiveRecord::Base set_table_name “clouds_import” set_primary_key “tagid” has_many :jobs, :through => :taggings has_many :taggings, :primary_key => “tagid”, :foreign_key => “tagid” end

class Tagging < ActiveRecord::Base set_table_name “clouds_import_lnk” set_primary_keys :jobid, :tagid # Zusammengesetzter Primaerschluessel belongs_to :job, :primary_key => “postid”, :foreign_key => “postid” belongs_to :tag, :primary_key => “tagid”, :foreign_key => “tagid” end

class User < ActiveRecord::Base set_primary_key “uid” set_table_name ‘users’ has_many :jobs, :foreign_key => “nutzer”, :primary_key => “uid” end —

Fertig! Nun können wir in althergebrachter Rails Manier extrem bequem auf unsere Modelle wie folgt zugreifen:

— Ruby

posts = Post.all posts.first.tags # gibt uns die tags zurück post = Post.find(:first, :order => “…”) post.user.name

oder auch named scopes (siehe oben)

Post.visible.first.tag_list — usw. Wirklich eine Wohltat ;)

Natürlich kann man in seinen Modellen noch Methoden definieren, die uns den Zugriff noch erleichtern und unser Modell um Funktionalität erweitert und damit unsere Controller und Views nicht zumuellt.

h3. Bonus: XML Builder

Wie man jetzt damit einen XML-Dialekt (z.B. auch RSS) baut, ist recht einfach, dank des XML Builder Gems (Das bei Rails eigentlich auch schon dabei sein sollte). Ich will hier nur ganz kurz teasern, ansonsten.. Google ist dein Freund:

— ruby require “rubygems” require “builder”

xml = Builder::XmlMarkup.new( :target => $stdout , :indent => 1 ) xml.instruct! xml.rss do

oder: xml.tag!(“rss”) do sinnvoll falls unser Tag nicht nur aus Kleinbuchstaben besteht

xml.channel do for item in Post.visible … … item.title …

end   end end ---