<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>ruby on rails, mysql, php on random thoughts</title><link>https://awesomeprogrammer.com/categories/ruby-on-rails-mysql-php/</link><description>Recent content in ruby on rails, mysql, php on random thoughts</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Sat, 28 Jul 2012 00:00:00 +0000</lastBuildDate><atom:link href="https://awesomeprogrammer.com/categories/ruby-on-rails-mysql-php/index.xml" rel="self" type="application/rss+xml"/><item><title>Rails legacy database migration</title><link>https://awesomeprogrammer.com/blog/2012/07/28/rails-legacy-database-migration/</link><pubDate>Sat, 28 Jul 2012 00:00:00 +0000</pubDate><guid>https://awesomeprogrammer.com/blog/2012/07/28/rails-legacy-database-migration/</guid><description><![CDATA[<p>I will give you some of my thoughts about <strong>migrating legacy database</strong> to Ruby On Rails framework. Welcome to part one.</p>
<p>It&rsquo;s 32°C outside (and apparently inside), I&rsquo;m trying to focus and finish my old project that I started after-hours. Being a programmer ain&rsquo;t easy :P. But let&rsquo;s get back on track. So to happens I have this legacy, really old, not maintained for years database. It comes from some old custom php cms, that is you might say - far from perfect. It doesn&rsquo;t have any logical constraints (no data integrity) and validation is a mess.</p>
<p>I have started from rough PHP script that tries to fix what can be fixed in a one way or another, purges database from spam (hello there web bots), applies some &lsquo;static&rsquo; fixes (like rewriting some custom prepared data) and leaves the rest as-it. I won&rsquo;t post the code here, because it&rsquo;s just written Rambo style - I didn&rsquo;t payed attention to the optimization or anything as I had to run it basically once.</p>
<p>After that it was time to cook <strong>rake tasks</strong> that will migrate table by table this data into my new app. I have setup a legacy database in <code>database.yml</code> and created bunch of legacy classes in external file like that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">LegacyPost</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Base</span>
</span></span><span style="display:flex;"><span>  establish_connection <span style="color:#e6db74">:legacy</span>
</span></span><span style="display:flex;"><span>  self<span style="color:#f92672">.</span>table_name  <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;board_posty&#39;</span>
</span></span><span style="display:flex;"><span>  self<span style="color:#f92672">.</span>primary_key <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;odpid&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">end</span>
</span></span></code></pre></div><p>Then was the time to start working or rake task, by trial and error I ended up with a function that could be applied for most of my cases.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">run_migrate_task</span>(new_model, legacy_model, rewrite_hash, broken <span style="color:#f92672">=</span> <span style="color:#f92672">[]</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  error_count <span style="color:#f92672">=</span> migrate_count <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>  p_key <span style="color:#f92672">=</span> legacy_model<span style="color:#f92672">.</span>primary_key<span style="color:#f92672">.</span>to_sym <span style="color:#66d9ef">unless</span> legacy_model<span style="color:#f92672">.</span>primary_key<span style="color:#f92672">.</span>nil? <span style="color:#75715e"># get primary key of legacy table</span>
</span></span><span style="display:flex;"><span>  id_important <span style="color:#f92672">=</span> rewrite_hash<span style="color:#f92672">.</span>keys<span style="color:#f92672">.</span>include?(<span style="color:#e6db74">:id</span>) <span style="color:#75715e"># we are rewriting old ids</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  legacy_model<span style="color:#f92672">.</span>find_in_batches(<span style="color:#e6db74">:batch_size</span> <span style="color:#f92672">=&gt;</span> <span style="color:#ae81ff">2000</span>) <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>old_objects<span style="color:#f92672">|</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    existing_ids <span style="color:#f92672">=</span> id_important ? new_model<span style="color:#f92672">.</span>select(<span style="color:#e6db74">:id</span>)<span style="color:#f92672">.</span>where(<span style="color:#e6db74">:id</span> <span style="color:#f92672">=&gt;</span> old_objects<span style="color:#f92672">.</span>map(<span style="color:#f92672">&amp;</span>p_key))<span style="color:#f92672">.</span>map(<span style="color:#f92672">&amp;</span><span style="color:#e6db74">:id</span>)<span style="color:#f92672">.</span>to_set : {}
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Base</span><span style="color:#f92672">.</span>transaction <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span>      old_objects<span style="color:#f92672">.</span>each <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>old_object<span style="color:#f92672">|</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">begin</span>
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>id_important <span style="color:#f92672">||</span> <span style="color:#f92672">!</span>existing_ids<span style="color:#f92672">.</span>include?(old_object<span style="color:#f92672">[</span>p_key<span style="color:#f92672">]</span>)) <span style="color:#f92672">&amp;&amp;</span> <span style="color:#f92672">!</span>broken<span style="color:#f92672">.</span>include?(old_object<span style="color:#f92672">[</span>p_key<span style="color:#f92672">]</span>)
</span></span><span style="display:flex;"><span>            new_object <span style="color:#f92672">=</span> new_model<span style="color:#f92672">.</span>new
</span></span><span style="display:flex;"><span>            rewrite_hash<span style="color:#f92672">.</span>each { <span style="color:#f92672">|</span>key, value<span style="color:#f92672">|</span> eval(<span style="color:#e6db74">&#34;new_object.</span><span style="color:#e6db74">#{</span>key<span style="color:#e6db74">}</span><span style="color:#e6db74"> = </span><span style="color:#e6db74">#{</span>value<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>) } <span style="color:#75715e"># setup new values by eval&#39;ing passed hash</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> new_object<span style="color:#f92672">.</span>save
</span></span><span style="display:flex;"><span>              migrate_count<span style="color:#f92672">+=</span><span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">else</span>
</span></span><span style="display:flex;"><span>              puts <span style="color:#e6db74">&#34;ERROR | ID: </span><span style="color:#e6db74">#{</span>old_object<span style="color:#f92672">[</span>p_key<span style="color:#f92672">]</span><span style="color:#e6db74">}</span><span style="color:#e6db74"> | MSG: </span><span style="color:#e6db74">#{</span>new_object<span style="color:#f92672">.</span>errors<span style="color:#f92672">.</span>full_messages<span style="color:#f92672">.</span>to_sentence<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>              error_count<span style="color:#f92672">+=</span><span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">end</span> <span style="color:#75715e"># if save</span>
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">end</span> <span style="color:#75715e"># if important</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">rescue</span> <span style="color:#66d9ef">Exception</span> <span style="color:#f92672">=&gt;</span> exception
</span></span><span style="display:flex;"><span>          puts <span style="color:#e6db74">&#34;EXCEPTION | ID: </span><span style="color:#e6db74">#{</span>old_object<span style="color:#f92672">[</span>p_key<span style="color:#f92672">]</span><span style="color:#e6db74">}</span><span style="color:#e6db74"> | </span><span style="color:#e6db74">#{</span>exception<span style="color:#f92672">.</span>message<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>          error_count<span style="color:#f92672">+=</span><span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">end</span> <span style="color:#75715e"># begin</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">end</span> <span style="color:#75715e"># transaction commit</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">end</span> <span style="color:#75715e"># each old objects</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span> <span style="color:#75715e"># each batch</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  puts <span style="color:#e6db74">&#34;DONE | Migrated: </span><span style="color:#e6db74">#{</span>migrate_count<span style="color:#e6db74">}</span><span style="color:#e6db74"> | Error: </span><span style="color:#e6db74">#{</span>error_count<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">end</span>
</span></span></code></pre></div><p>What this function does is takes new and old model and hash that consists of keys corresponding to the new model and raw code to apply for that value, optional I&rsquo;m passing array if IDs of totally broken objects or object that I decided that I don&rsquo;t want to import for some reason. I&rsquo;m checking if object was properly saved (validation message) and rescuing any exceptions as I just let it fail if I couldn&rsquo;t fix it in php script. Few notices:</p>
<ul>
<li>
<p>if I&rsquo;m rewriting old IDs (and most of the time I am) first I&rsquo;m fetching stuff I have already imported (maybe in previous run before some additional tweaks) - I don&rsquo;t want to import 200k records just because two were broken</p>
</li>
<li>
<p>using <code>find_in_batches</code> is quite useful here, apparently <code>all</code> worked fine until I got to table that had half a million rows and then ran out of memory, my bad :P</p>
</li>
<li>
<p>wrap it inside a transaction for some speed improvement, you may want to disable validation or go with raw SQL generation instead - it all depends of your needs</p>
</li>
</ul>
<p>So I can call it like that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span>  task <span style="color:#e6db74">:posts</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">:environment</span> <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span>    setup_env <span style="color:#75715e"># setup configuration, load classes and stuff</span>
</span></span><span style="display:flex;"><span>    rewrite <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">:id</span>         <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;old_object.odpid&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">:topic_id</span>   <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;old_object.tematid&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">:user_id</span>    <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;old_object.autor&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">:content</span>    <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;html_to_bbc(old_object.tresc, true)&#34;</span>, <span style="color:#75715e"># call custom function here</span>
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">:updated_at</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;old_object.edytowano &gt; 0 ? Time.at(old_object.edytowano) : Time.at(old_object.dopisano)&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">:updated_by</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;old_object.edytowal &gt; 0 ? Time.at(old_object.edytowal) : nil&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">:created_at</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;Time.at(old_object.dopisano)&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    run_migrate_task(<span style="color:#66d9ef">Post</span>, <span style="color:#66d9ef">LegacyPost</span>, rewrite)  
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span>
</span></span></code></pre></div><p>Maybe it&rsquo;s not the perfect solution, not the smartest and quickest - but in my opinion migrations are supposed to be dirty, doable in reasonable time and just should work. You won&rsquo;t maintain this code in the future anyway, so it&rsquo;s up to you how much effort you are willing to put it. As it&rsquo;s my side project I decided to be cheap ;).</p>
<p>In next chapter I will give you some hints how to convert pretty messed up HTML to BBCode (don&rsquo;t give me that look!) without using regular expressions (that don&rsquo;t cut it anyway in the end). Cheers!</p>
<h3 id="updated-14082012">Updated 14.08.2012</h3>
<p>After some investigation of my migration I realized that using <code>include?</code> on huge <code>Array</code> in this case is not the way to go. Instead convert array to <code>Set</code>. Why you ask? <a href="http://www.ruby-doc.org/stdlib-1.9.3/libdoc/set/rdoc/Set.html">Here you can find</a> some useful information. Simple benchmark below.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span>array <span style="color:#f92672">=</span> (<span style="color:#ae81ff">1</span><span style="color:#f92672">..</span><span style="color:#ae81ff">1_000_000</span>)<span style="color:#f92672">.</span>to_a
</span></span><span style="display:flex;"><span>set <span style="color:#f92672">=</span> array<span style="color:#f92672">.</span>to_set
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">Benchmark</span><span style="color:#f92672">.</span>bm(<span style="color:#ae81ff">15</span>) <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>x<span style="color:#f92672">|</span>
</span></span><span style="display:flex;"><span>  x<span style="color:#f92672">.</span>report(<span style="color:#e6db74">&#34;Array.include?&#34;</span>) { <span style="color:#ae81ff">1000</span><span style="color:#f92672">.</span>times { array<span style="color:#f92672">.</span>include?(<span style="color:#ae81ff">500_000</span>) } }
</span></span><span style="display:flex;"><span>  x<span style="color:#f92672">.</span>report(<span style="color:#e6db74">&#34;Set.include?&#34;</span>)   { <span style="color:#ae81ff">1000</span><span style="color:#f92672">.</span>times { set<span style="color:#f92672">.</span>include?(<span style="color:#ae81ff">500_000</span>) } }
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                      user     system      total        real
</span></span><span style="display:flex;"><span>Array<span style="color:#f92672">.</span>include?   <span style="color:#ae81ff">20</span><span style="color:#f92672">.</span><span style="color:#ae81ff">940000</span>   <span style="color:#ae81ff">0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">000000</span>  <span style="color:#ae81ff">20</span><span style="color:#f92672">.</span><span style="color:#ae81ff">940000</span> ( <span style="color:#ae81ff">20</span><span style="color:#f92672">.</span><span style="color:#ae81ff">991647</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">Set</span><span style="color:#f92672">.</span>include?      <span style="color:#ae81ff">0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">000000</span>   <span style="color:#ae81ff">0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">000000</span>   <span style="color:#ae81ff">0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">000000</span> (  <span style="color:#ae81ff">0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0001</span><span style="color:#ae81ff">99</span>)
</span></span></code></pre></div>]]></description><content:encoded><![CDATA[<p>I will give you some of my thoughts about <strong>migrating legacy database</strong> to Ruby On Rails framework. Welcome to part one.</p>
<p>It&rsquo;s 32°C outside (and apparently inside), I&rsquo;m trying to focus and finish my old project that I started after-hours. Being a programmer ain&rsquo;t easy :P. But let&rsquo;s get back on track. So to happens I have this legacy, really old, not maintained for years database. It comes from some old custom php cms, that is you might say - far from perfect. It doesn&rsquo;t have any logical constraints (no data integrity) and validation is a mess.</p>
<p>I have started from rough PHP script that tries to fix what can be fixed in a one way or another, purges database from spam (hello there web bots), applies some &lsquo;static&rsquo; fixes (like rewriting some custom prepared data) and leaves the rest as-it. I won&rsquo;t post the code here, because it&rsquo;s just written Rambo style - I didn&rsquo;t payed attention to the optimization or anything as I had to run it basically once.</p>
<p>After that it was time to cook <strong>rake tasks</strong> that will migrate table by table this data into my new app. I have setup a legacy database in <code>database.yml</code> and created bunch of legacy classes in external file like that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">LegacyPost</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Base</span>
</span></span><span style="display:flex;"><span>  establish_connection <span style="color:#e6db74">:legacy</span>
</span></span><span style="display:flex;"><span>  self<span style="color:#f92672">.</span>table_name  <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;board_posty&#39;</span>
</span></span><span style="display:flex;"><span>  self<span style="color:#f92672">.</span>primary_key <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;odpid&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">end</span>
</span></span></code></pre></div><p>Then was the time to start working or rake task, by trial and error I ended up with a function that could be applied for most of my cases.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">run_migrate_task</span>(new_model, legacy_model, rewrite_hash, broken <span style="color:#f92672">=</span> <span style="color:#f92672">[]</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  error_count <span style="color:#f92672">=</span> migrate_count <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>  p_key <span style="color:#f92672">=</span> legacy_model<span style="color:#f92672">.</span>primary_key<span style="color:#f92672">.</span>to_sym <span style="color:#66d9ef">unless</span> legacy_model<span style="color:#f92672">.</span>primary_key<span style="color:#f92672">.</span>nil? <span style="color:#75715e"># get primary key of legacy table</span>
</span></span><span style="display:flex;"><span>  id_important <span style="color:#f92672">=</span> rewrite_hash<span style="color:#f92672">.</span>keys<span style="color:#f92672">.</span>include?(<span style="color:#e6db74">:id</span>) <span style="color:#75715e"># we are rewriting old ids</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  legacy_model<span style="color:#f92672">.</span>find_in_batches(<span style="color:#e6db74">:batch_size</span> <span style="color:#f92672">=&gt;</span> <span style="color:#ae81ff">2000</span>) <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>old_objects<span style="color:#f92672">|</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    existing_ids <span style="color:#f92672">=</span> id_important ? new_model<span style="color:#f92672">.</span>select(<span style="color:#e6db74">:id</span>)<span style="color:#f92672">.</span>where(<span style="color:#e6db74">:id</span> <span style="color:#f92672">=&gt;</span> old_objects<span style="color:#f92672">.</span>map(<span style="color:#f92672">&amp;</span>p_key))<span style="color:#f92672">.</span>map(<span style="color:#f92672">&amp;</span><span style="color:#e6db74">:id</span>)<span style="color:#f92672">.</span>to_set : {}
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Base</span><span style="color:#f92672">.</span>transaction <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span>      old_objects<span style="color:#f92672">.</span>each <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>old_object<span style="color:#f92672">|</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">begin</span>
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>id_important <span style="color:#f92672">||</span> <span style="color:#f92672">!</span>existing_ids<span style="color:#f92672">.</span>include?(old_object<span style="color:#f92672">[</span>p_key<span style="color:#f92672">]</span>)) <span style="color:#f92672">&amp;&amp;</span> <span style="color:#f92672">!</span>broken<span style="color:#f92672">.</span>include?(old_object<span style="color:#f92672">[</span>p_key<span style="color:#f92672">]</span>)
</span></span><span style="display:flex;"><span>            new_object <span style="color:#f92672">=</span> new_model<span style="color:#f92672">.</span>new
</span></span><span style="display:flex;"><span>            rewrite_hash<span style="color:#f92672">.</span>each { <span style="color:#f92672">|</span>key, value<span style="color:#f92672">|</span> eval(<span style="color:#e6db74">&#34;new_object.</span><span style="color:#e6db74">#{</span>key<span style="color:#e6db74">}</span><span style="color:#e6db74"> = </span><span style="color:#e6db74">#{</span>value<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>) } <span style="color:#75715e"># setup new values by eval&#39;ing passed hash</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> new_object<span style="color:#f92672">.</span>save
</span></span><span style="display:flex;"><span>              migrate_count<span style="color:#f92672">+=</span><span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">else</span>
</span></span><span style="display:flex;"><span>              puts <span style="color:#e6db74">&#34;ERROR | ID: </span><span style="color:#e6db74">#{</span>old_object<span style="color:#f92672">[</span>p_key<span style="color:#f92672">]</span><span style="color:#e6db74">}</span><span style="color:#e6db74"> | MSG: </span><span style="color:#e6db74">#{</span>new_object<span style="color:#f92672">.</span>errors<span style="color:#f92672">.</span>full_messages<span style="color:#f92672">.</span>to_sentence<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>              error_count<span style="color:#f92672">+=</span><span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">end</span> <span style="color:#75715e"># if save</span>
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">end</span> <span style="color:#75715e"># if important</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">rescue</span> <span style="color:#66d9ef">Exception</span> <span style="color:#f92672">=&gt;</span> exception
</span></span><span style="display:flex;"><span>          puts <span style="color:#e6db74">&#34;EXCEPTION | ID: </span><span style="color:#e6db74">#{</span>old_object<span style="color:#f92672">[</span>p_key<span style="color:#f92672">]</span><span style="color:#e6db74">}</span><span style="color:#e6db74"> | </span><span style="color:#e6db74">#{</span>exception<span style="color:#f92672">.</span>message<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>          error_count<span style="color:#f92672">+=</span><span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">end</span> <span style="color:#75715e"># begin</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">end</span> <span style="color:#75715e"># transaction commit</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">end</span> <span style="color:#75715e"># each old objects</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span> <span style="color:#75715e"># each batch</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  puts <span style="color:#e6db74">&#34;DONE | Migrated: </span><span style="color:#e6db74">#{</span>migrate_count<span style="color:#e6db74">}</span><span style="color:#e6db74"> | Error: </span><span style="color:#e6db74">#{</span>error_count<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">end</span>
</span></span></code></pre></div><p>What this function does is takes new and old model and hash that consists of keys corresponding to the new model and raw code to apply for that value, optional I&rsquo;m passing array if IDs of totally broken objects or object that I decided that I don&rsquo;t want to import for some reason. I&rsquo;m checking if object was properly saved (validation message) and rescuing any exceptions as I just let it fail if I couldn&rsquo;t fix it in php script. Few notices:</p>
<ul>
<li>
<p>if I&rsquo;m rewriting old IDs (and most of the time I am) first I&rsquo;m fetching stuff I have already imported (maybe in previous run before some additional tweaks) - I don&rsquo;t want to import 200k records just because two were broken</p>
</li>
<li>
<p>using <code>find_in_batches</code> is quite useful here, apparently <code>all</code> worked fine until I got to table that had half a million rows and then ran out of memory, my bad :P</p>
</li>
<li>
<p>wrap it inside a transaction for some speed improvement, you may want to disable validation or go with raw SQL generation instead - it all depends of your needs</p>
</li>
</ul>
<p>So I can call it like that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span>  task <span style="color:#e6db74">:posts</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">:environment</span> <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span>    setup_env <span style="color:#75715e"># setup configuration, load classes and stuff</span>
</span></span><span style="display:flex;"><span>    rewrite <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">:id</span>         <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;old_object.odpid&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">:topic_id</span>   <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;old_object.tematid&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">:user_id</span>    <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;old_object.autor&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">:content</span>    <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;html_to_bbc(old_object.tresc, true)&#34;</span>, <span style="color:#75715e"># call custom function here</span>
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">:updated_at</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;old_object.edytowano &gt; 0 ? Time.at(old_object.edytowano) : Time.at(old_object.dopisano)&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">:updated_by</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;old_object.edytowal &gt; 0 ? Time.at(old_object.edytowal) : nil&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">:created_at</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;Time.at(old_object.dopisano)&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    run_migrate_task(<span style="color:#66d9ef">Post</span>, <span style="color:#66d9ef">LegacyPost</span>, rewrite)  
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span>
</span></span></code></pre></div><p>Maybe it&rsquo;s not the perfect solution, not the smartest and quickest - but in my opinion migrations are supposed to be dirty, doable in reasonable time and just should work. You won&rsquo;t maintain this code in the future anyway, so it&rsquo;s up to you how much effort you are willing to put it. As it&rsquo;s my side project I decided to be cheap ;).</p>
<p>In next chapter I will give you some hints how to convert pretty messed up HTML to BBCode (don&rsquo;t give me that look!) without using regular expressions (that don&rsquo;t cut it anyway in the end). Cheers!</p>
<h3 id="updated-14082012">Updated 14.08.2012</h3>
<p>After some investigation of my migration I realized that using <code>include?</code> on huge <code>Array</code> in this case is not the way to go. Instead convert array to <code>Set</code>. Why you ask? <a href="http://www.ruby-doc.org/stdlib-1.9.3/libdoc/set/rdoc/Set.html">Here you can find</a> some useful information. Simple benchmark below.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span>array <span style="color:#f92672">=</span> (<span style="color:#ae81ff">1</span><span style="color:#f92672">..</span><span style="color:#ae81ff">1_000_000</span>)<span style="color:#f92672">.</span>to_a
</span></span><span style="display:flex;"><span>set <span style="color:#f92672">=</span> array<span style="color:#f92672">.</span>to_set
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">Benchmark</span><span style="color:#f92672">.</span>bm(<span style="color:#ae81ff">15</span>) <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>x<span style="color:#f92672">|</span>
</span></span><span style="display:flex;"><span>  x<span style="color:#f92672">.</span>report(<span style="color:#e6db74">&#34;Array.include?&#34;</span>) { <span style="color:#ae81ff">1000</span><span style="color:#f92672">.</span>times { array<span style="color:#f92672">.</span>include?(<span style="color:#ae81ff">500_000</span>) } }
</span></span><span style="display:flex;"><span>  x<span style="color:#f92672">.</span>report(<span style="color:#e6db74">&#34;Set.include?&#34;</span>)   { <span style="color:#ae81ff">1000</span><span style="color:#f92672">.</span>times { set<span style="color:#f92672">.</span>include?(<span style="color:#ae81ff">500_000</span>) } }
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                      user     system      total        real
</span></span><span style="display:flex;"><span>Array<span style="color:#f92672">.</span>include?   <span style="color:#ae81ff">20</span><span style="color:#f92672">.</span><span style="color:#ae81ff">940000</span>   <span style="color:#ae81ff">0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">000000</span>  <span style="color:#ae81ff">20</span><span style="color:#f92672">.</span><span style="color:#ae81ff">940000</span> ( <span style="color:#ae81ff">20</span><span style="color:#f92672">.</span><span style="color:#ae81ff">991647</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">Set</span><span style="color:#f92672">.</span>include?      <span style="color:#ae81ff">0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">000000</span>   <span style="color:#ae81ff">0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">000000</span>   <span style="color:#ae81ff">0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">000000</span> (  <span style="color:#ae81ff">0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0001</span><span style="color:#ae81ff">99</span>)
</span></span></code></pre></div>]]></content:encoded></item></channel></rss>