<?xml version="1.0" encoding="UTF-8"?>
<posts type="array">
  <post>
    <body>ActiveRecord er et fint v&#230;rkt&#248;j til database administration. Simpelt vedligehold eller hele migrationer af data p&#229; en enkelt eller flere databaser fra et Ruby script med ActiveRecord model objekter. Supercool!  

I dette tilf&#230;lde vil jeg hente domain objekter ud fra en database og fodre dem til en anden database instans og kunne bruge alle de fede features fra active record. 

Konfiguration

F&#248;rste punkt er en yaml fil med database konfiguration p&#229; de forskellige databaser. I dette tilf&#230;lde operere jeg p&#229; active record objecter fra to forskellige typer af database.   
&lt;pre&gt;
database_from:
  adapter: mysql
  # encoding: utf8
  database: intra_production
  username: ****
  password: ****
  host: thor

database_to:
  adapter: sqlite3
  database: db/production.sqlite3
  timeout: 5000
&lt;/pre&gt;

$config = YAML.load_file(File.join(File.dirname(__FILE__), 'database.yml'))
&lt;/pre&gt;
Den nederste linje l&#230;ser yaml konfigurationsfilen ind i ruby. Herfra kan direkte sl&#229; op i yaml strukturen.  


Database forbindelse 

&lt;pre&gt;
class DBFrom &lt; ActiveRecord::Base
  establish_connection $config['database_From']
end

class DBTo &lt; ActiveRecord::Base
  establish_connection $config['database_To']
end
&lt;/pre&gt;


Ruby's Mixin skal hj&#230;lpe med med at bruge de korrekte modelnavne. Jeg indkapsler modellerne i hver deres mixin modul. Det er min Separation of concern, nu kan jeg notere mine object p&#229; namespace eller prefix. 

Nu mangler blot klassernedefinitionerne for de modeller der skal arbejdes med men det er nemt med AC. P&#229; grund af Ruby's metaprogrammering egenskaber er der ingen metoder. Kun relationerne skal specificeres. 

&lt;pre&gt;
module From
  class Post &lt; DBFrom
    has_many :comments
  end
  class Comment &lt; DBFrom
    belongs_to :posts
  end
end

module To
  ...
end
&lt;/pre&gt;

I dette skal jeg flytte data og har derfor n&#230;sten samme skema i begge databaser men samme metoder kunne ogs&#229; bruges mellem development og produktion p&#229; samme skema. 

&lt;pre&gt;
posts = From::Post.find(:all, :order =&gt; &quot;created_at&quot;)
posts.each do |post| 
    new_post = To::Post.new
    new_post.title = post.title
    new_post.save 
end
&lt;/pre&gt;

Nu har man jo naturligvis relationer til andre tabeller i databasen og de skal m&#229;ske ogs&#229; kopieres over i den nye instans. N&#229;r jeg henter en post op er der m&#229;ske nogle comments med og alle associationer skal loades samtidig. 

Derp&#229; er det blot at mixin det nye objekt og bygge de associationer som h&#248;rer til. N&#229;r man kalder objektes save metode gemmes den nye record i den anden database. 
</body>
    <category-id type="integer">11</category-id>
    <created-at type="datetime">2008-01-09T22:17:12Z</created-at>
    <id type="integer">89</id>
    <post-id type="NilClass">89</post-id>
    <published type="boolean">false</published>
    <tag-id type="NilClass">8</tag-id>
    <title>Mutible ActiveRecord</title>
    <updated-at type="datetime">2008-01-10T04:31:02Z</updated-at>
  </post>
  <post>
    <body>Midt i et foredrag kom jeg i tvivl om hvilken form for join model der er den bedste i Rails. Svaret er jo nok afh&#230;ngig af l&#248;sningen og den enkelts krav til information p&#229; selve relationen. Hidtil har det v&#230;ret god metodik at man skal holde sine join tabeller rene. Det betyder alts&#229; at der ikke b&#248;r v&#230;re anden information end  prim&#230;r n&#248;gler der relatere de to mange til mange tableller.

I Rails findes der to l&#248;sninger p&#229; mange til mange relationen. Den ene er en simple (habtm) implementation som kun kr&#230;ver en ekstra database join tabel. Den anden l&#248;sning(through association) kr&#230;ver ogs&#229; en ekstra tabel men har et model objekt og ekstra attributter p&#229; relationen. 

Simple Associations(HABTM)

Tabel Join 
&lt;pre&gt;
create_table :posts_tags, :id =&gt; false do |t|
     t.column :post_id, :integer
     t.column :tag_id, :integer
 end
&lt;/pre&gt;

Model
&lt;pre&gt;
class Post &lt; ActiveRecord::Base
   has_and_belongs_to_many :tags
end
class Tag &lt; ActiveRecord::Base
  has_and_belongs_to_many :posts
end
&lt;/pre&gt;

Her har vi alts&#229; to model objekter som har en many-to-many relation mellem posts og tags. Det er en konvention i Rails at join tabellen bliver kaldt posts_tags. Man kan ikke l&#230;gge ekstra information p&#229; relationen og tabellen kan ikke indenholde prim&#230;r n&#248;gle. Denne model er at fortr&#230;kke hvis l&#248;sningen ikke kr&#230;ver mere information omkring selve relationen. 

&lt;pre&gt;
@post.tags &lt;&lt; Tag.find &#8221;Ruby&#8221; &lt;&lt; Tag.find &#8221;Java&#8221; 
&lt;/pre&gt;

Denne l&#248;sning er enkelt at bruge og s&#230;tte op. Bem&#230;rk at der ikke er en model for relationen. 

Rich Associations(through association)

I denne variation kr&#230;ver AC en prim&#230;r n&#248;gle p&#229; relationen samt et model objekt. I dette tilf&#230;lde kan det betale sig at finde p&#229; et godt navn til relationen. I den simple model skulle vi blot navngive tabellen udfra de to tabeller som skal relateres men nu skal vi bruge et godt sigene navn til den model som indkapsler relationen.   

Tabel Join 

&lt;pre&gt;
create_table :tagszations do |t|
     t.column :post_id, :integer
     t.column :tag_id, :integer
     t.column :created_at, :datetime
end
&lt;/pre&gt;

Model 

&lt;pre&gt;
class Tagszation &lt; ActiveRecord::Base
   belongs_to :post
   belongs_to :tag
end
class Tag &lt; ActiveRecord::Base
   has_many :tagszation
   has_many :posts, :through =&gt; :tagszation
end
class Post &lt; ActiveRecord::Base
   has_many :tagszation
   has_many :tags, :through =&gt; :tagszation, :select =&gt; &quot;DISTINCT tags.*&quot;
end
&lt;/pre&gt;

Denne metode giver mere fleksibilitet p&#229; selve relationen og er nemt at s&#230;tte op for den simple l&#248;sning men kan snyde lidt n&#229;r man g&#229;r fra HABTM til through association. 

&lt;pre&gt;
post = Post.find(1)
post.tags.size             # =&gt; 4
ruby = Tag.find(2)

tagz = Tagszation.new(:post =&gt; post, :tag =&gt; ruby)
post.save
post.tags.size             # =&gt; 4
post.tags(true).size       # =&gt; 5
&lt;/pre&gt;


Ups. Push metoden virker ikke for has_many :through relation. Hvorfor? Rails API siger at metoden tilf&#248;jer en samling attributter til objektet i relationen. Denne relation virker som et array af relateret objekter men indeholder ekstra hj&#230;lpe metoder som g&#248;r livet v&#230;rd at leve. 
 
Faktisk er dette ikke et array men en instans af AssociationCollection som igen er en supklasse af AssociationProxy. Dette er klasser som f&#248;lger Proxy pattern. Klassen fungerer som front objekt for at give ekstra adf&#230;rd til det bagvedliggende objekt. AssociationProxy uddelegere til et enkelt model objekt og for has_one eller  belongs_to. AssociationCollection delegere et Array af model objekter. 

De delegerede metoder er med i class s&#229; hvis du spore objektet hvilken type vil det sige array selvom det ikke er tilf&#230;ldet. Funny



Her er en sammenlinning af de to typer i tabelform. Tak til has_many :through

|| Association| has_and_belongs_to_many | has_many :through ||
|| AKA | habtm | through association ||
|| Structure | Join Table | Join Model ||
|| Primary Key | no | yes ||
|| Rich Association | no | yes ||
|| Proxy Collection | yes | no ||
|| Distinct Selection | yes | no yes ||
|| Self-Referential | yes | yes ||
|| Eager Loading | yes | yes ||
|| Polymorphism | no | yes ||
|| N-way Joins | no | yes ||


Structure

has_and_belongs_to_many benytter en simple join table hvor hver r&#230;kke blot er to fremme n&#248;gler. Der findes ikke et model objekt i rails og man tilg&#229;r ikke denne tabel direkte

Primary Key

Join tables har ikke en prim&#230;rn&#248;gle. Nogle fors&#248;ger sig med at generer en n&#248;gle ud fra et par fremmed n&#248;gler men det er noget crap. 

Join models derimod har en prim&#230;rn&#248;gle ligesom alle modeller har et telefonnummer. Det betyder at du kan manipolere tabellen direkte. 

Rich Association

Hvis du beh&#248;ver information p&#229; relations tabellen m&#229; du bruge en join model.

Proxy Collection

En fordel ved habtm er at den skabte relation er en s&#229;kaldt proxy collection. Det betyder mulighed for oprette elementer i join tabellen med relationens &lt;&lt; (push) metode p&#229; samme m&#229;de som med has_many relationer.  Fordi join model har ekstra attributter p&#229; relationen bliver det mere kompliceret at oprette dem automatisk.  

Distinct Selection

Nogle gange kan en join table, eller model have flere referencer mellem de samme r&#230;kker. Hvis man ikke vil ha alle men blot en kan man bruge :unique. Dette ville svare til at bruge DISTINCT i sql. Fors&#248;rgelsen returner uden duplikater. Forskellen er om duplikater skal fjernes i databasen eller i Ruby. 
 
Self-Referential

Begge modeller kan referere til sig selv. Fx kan man i habtm bruge :foreign_key og :association_foreign_key. 

Eager Loading

Begge modeller supporter eager loading af relaterede objekter med :include.

Polymorphism

Join models og through relationer kan arbejde med polymorfe model typer. 

N-way Joins

En habtm relation kan kun joine to modeller men through modellen kan joine liges&#229; mange du vil. Men pas p&#229;, det bliver let kompliceret.
</body>
    <category-id type="integer">11</category-id>
    <created-at type="datetime">2007-12-13T08:34:00Z</created-at>
    <id type="integer">86</id>
    <post-id type="NilClass">86</post-id>
    <published type="boolean">false</published>
    <tag-id type="NilClass">8</tag-id>
    <title>Association Join Models</title>
    <updated-at type="datetime">2008-01-03T13:32:04Z</updated-at>
  </post>
  <post>
    <body>Hvis din objektmodel er replikeret over flere VM'er, s&#229; er sandsynligheden for at alle crasher samtidigt, lige s&#229; lille som at DB's diske crasher. S&#229; kan man rent faktisk arbejde med crash-tolerant memory og antage at heap'en aldrig slettes. Det fjerner hele behovet for et data-access layer, for applikationens model er jo crash-resilient. 


Det betyder at man kan droppe databasen og bruge memory i stedet. 

Halvdelen af alle framevorks i Java handler om O/R mappingsproblemet og der er skrevet indtil flere design patterns som attakere netop dette problem som fx valuebean(VB) og datatransferobject(DTO) der er strukturelle m&#248;nster. 

Enlig er det en ret enkelt og ligetil proces. Problemet er at det er trivielt og at vi i mange &#229;r skrevet tusind af klasser som g&#248;r pr&#230;sis det samme. En s&#229;kaldt serverside programm&#248;r lever af at mappe tabeller og relationer i databasen til en objektmodel til trods trods for at tabeller og objekter er to vidt forskellige konstruktioner. Firkantede database tabeller passer ikke lige over til et rent objektorienteret paradigme.


Et par gode kolleger fra Javahouse tiden og jeg selv, inviterede Owen Taylor til Danmark for at tale i et par banker og i cybercomgroup headquarter. Owen er specialist i Giga Spaces teknologien. Det helt essentielle ved Spaces er hastighed og skalerbarhed. Men i tilgift f&#229;r man en sjov afledt effekt. Et typisk forl&#248;b er at man preloader en m&#230;ngte data fra databasen om morgenen hvorefter arbejdet begynder. Efter arbejdet kan man evt. gemmer nye data. Resten af tiden benyttes kun memory. Alle objekter er persistente og replikerede. 

Eneste logiske grund til at opdatere databasen er at interaktion med legacy systemer. Selve spaces er aldrig nede.


Den helt store fordel er at man kan fjerne hele databasen og alle de gamle problemer med O/R mapping og hele dataaccess layer i den gammeldags software model. 

Der er fortaget unders&#248;gelser som viser at backend programmering(O/R mapping) tager ca. 60 procent af alt udviklingstid. 

</body>
    <category-id type="integer">13</category-id>
    <created-at type="datetime">2007-08-14T00:12:00Z</created-at>
    <id type="integer">59</id>
    <post-id type="NilClass">59</post-id>
    <published type="boolean">false</published>
    <tag-id type="NilClass">8</tag-id>
    <title>Drop Databasen</title>
    <updated-at type="datetime">2008-04-08T07:29:18Z</updated-at>
  </post>
</posts>
