<?xml version="1.0" encoding="UTF-8"?> <rss
version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
xmlns:series="http://organizeseries.com/"
> <channel><title>RubySource</title> <atom:link href="http://rubysource.com/feed/" rel="self" type="application/rss+xml" /><link>http://rubysource.com</link> <description>RubySource provides advice, tutorials, commentary, and insight into the Ruby and Rails ecosystem.</description> <lastBuildDate>Thu, 23 May 2013 13:30:33 +0000</lastBuildDate> <language>en-US</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.5.1</generator> <item><title>Simple Background Jobs with Sucker Punch</title><link>http://rubysource.com/simple-background-jobs-with-sucker-punch/</link> <comments>http://rubysource.com/simple-background-jobs-with-sucker-punch/#comments</comments> <pubDate>Thu, 23 May 2013 13:30:33 +0000</pubDate> <dc:creator>Glenn Goodrich</dc:creator> <category><![CDATA[Gems]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=5739</guid> <description><![CDATA[Running background jobs in an app is a great way to keep the UI snappy. Being it sending an email, calling some API, or any long running task, there&#8217;s always something that can be moved to the background. Sometimes, it makes sense&#8230;]]></description> <content:encoded><![CDATA[<p><a
href="http://rubysource.com/?attachment_id=5740" rel="attachment wp-att-5740"><img
class="alignleft size-medium wp-image-5740" alt="suckerpunch" src="http://cdn.rubysource.com/files/2013/05/suckerpunch-300x130.png" width="300" height="130" /></a>Running background jobs in an app is a great way to keep the UI snappy. Being it sending an email, calling some API, or any long running task, there&#8217;s always something that can be moved to the background.</p><p>Sometimes, it makes sense to set up a queue and worker processes, but other times it would be great just to have the method run in background with minimal code changes.</p><p>Enter <a
href="https://github.com/brandonhilkert/sucker_punch">Sucker Punch</a>, a single-process asynchronous processing library. Using the wonderful <a
href="https://github.com/celluloid/celluloid/">celluloid</a> framework, it lets us do asynchronous work in very few steps.</p><h2>Setup</h2><p>First of all, your app needs to be running on Ruby 1.9+, JRuby 1.6+ or Rubinius 2.0 (with Ruby 1.9 mode on the last 2).</p><p>Then, install the gem by running</p><pre class="brush: bash; title: ; notranslate">
  gem install sucker_punch
</pre><p>or add it to you Gemfile via</p><pre class="brush: ruby; title: ; notranslate">
  gem 'sucker_punch'
1&lt;/p&gt;
&lt;p&gt;And that's it! Now lets see how to use it.&lt;/p&gt;
&lt;h2&gt;Creating a Worker&lt;/h2&gt;
&lt;p&gt;Creating a worker is pretty simple. It's just a plain ruby class that includes the  &lt;code&gt;SuckerPunch::Worker&lt;/code&gt; module, and defines one or more instance methods. Sucker Punch suggests just one &lt;code&gt;perform&lt;/code&gt; method, but any name and number of methods can be defined and used. &lt;/p&gt;
1
  class SomeWorker
    include SuckerPunch::Worker
    def perform(some_data)
      # Method code. Do some work.
    end
end
</pre><h2>Calling the Worker</h2><p>Before calling the worker, we need to configure our queues:</p><pre class="brush: ruby; title: ; notranslate">
  SuckerPunch.config do
    queue name: :some_queue, worker: SomeWorker, workers: 5
    queue name: :welcome_queue, worker: WelcomeEmailWorker, workers: 2
  end
</pre><p>We can define as many queues as we want with as many workers as we want (preferable 2+ per queue), but each can have only one worker class per queue.<br
/> More workers equals more parallel jobs that can be performed, but be aware of running out of connections if you&#8217;re using a connecting to an external service like a database or memcached.</p><p>Then you can access the workers on the queues via</p><pre class="brush: ruby; title: ; notranslate">
  SuckerPunch::Queue[:some_queue] # or
  SuckerPunch::Queue.new(:some_queue)`
</pre><p>This will give us a <code>Celluloid::ActorProxy</code> object wrapping our worker object.<br
/> We can call the <code>perform</code> method (or any other method we defined) on that object directly, or use <code>async</code> to return control instantly and make it run in background.</p><pre class="brush: ruby; title: ; notranslate">
  SuckerPunch::Queue[:welcome_queue].perform(1)
  SuckerPunch::Queue[:welcome_queue].async.perform(1)
1&lt;/p&gt;
&lt;p&gt;Just be aware that running the job async will not raise any exception if it fails. It will just fail silently.&lt;/p&gt;
&lt;h2&gt;Testing&lt;/h2&gt;
&lt;p&gt;Everything needs testing, of course!&lt;/p&gt;
&lt;p&gt;Fortunately, testing the worker is pretty easy. You can test it as any Ruby class.
With some rspec flavor:&lt;/p&gt;
&lt;p&gt;1
describe WelcomeEmailWorker
  let(:user){ FactoryGirl.create :user }
  let(:worker){ EmailWorker.new }
  describe &quot;#perform&quot; do
    it &quot;delivers an email&quot; do
      expect{
        worker.perform(user.id)
      }.to change{ UserMailer.deliveries.size }.by(1)
    end
  end
end
</pre><p>To test how it integrates with other methods, there are 2 options:</p><p>1) Test that it calls and enqueues the job.<br
/> To do this you need to require <code>sucker_punch/testing</code>:</p><pre class="brush: ruby; title: ; notranslate">
require 'sucker_punch/testing'
describe User do
  describe &quot;#send_welcome_email&quot; do
    it &quot;delivers an email&quot; do
      let(:user){ FactoryGirl.create :user }
      expect{
        user.send_welcome_email
      }.to change{ SuckerPunch::Queue.new(:email).jobs.size }.by(1)
    end
  end
end
</pre><p>2) Running jobs inline.<br
/> This can be done by requiring <code>sucker_punch/testing/inline</code>, and jobs will <em>always</em> be run synchronously.</p><pre class="brush: ruby; title: ; notranslate">
require 'sucker_punch/testing'
describe User do
  let(:user){ FactoryGirl.create :user }
  it &quot;delivers an email&quot; do
    describe &quot;#send_welcome_email&quot; do
      expect{
        user.send_welcome_email
      }.to change{ UserMailer.deliveries.size }.by(1)
    end
  end
end
</pre><h2>Considerations</h2><h3>Tests Running Inside DB Transactions</h3><p>There&#8217;s one thing to have in mind with tests. If you&#8217;re running each test inside a transaction, you&#8217;ll need to change that to a truncation strategy for Sucker Punch tests. Here&#8217;s an example of how to do it with DatabaseCleaner:<br
/> <a
href="https://gist.github.com/kitop/5248674">https://gist.github.com/kitop/5248674</a>.</p><p>This happens because Sucker Punch workers always run the method in a separate thread, no matter if it is synchronous or asynchronous.</p><h3>Persistence</h3><p>Keep in mind that Sucker Punch runs your jobs on a separate thread, and not polling from an outside queue. That means that, if your app goes down while processing a job, it will not notify nor store that error anywhere by default, and it won&#8217;t retry it either.<br
/> If you need more control over this, you can write your own wrapper. Maybe you could get some inspiration from <a
href="https://github.com/mperham/girl_friday/blob/master/lib/girl_friday/persistence.rb">girl<em>friday</em></a>, or use some other solution like <a
href="https://github.com/resque/resque">resque</a>, <a
href="https://github.com/mperham/sidekiq">sidekiq</a>, or <a
href="https://github.com/tobi/delayed_job">delayedjob</a>.</p><h3>Rails</h3><p>If you&#8217;re working with Rails, workers usually go in the <code>app/workers</code> directory.</p><p>You should be careful with ActiveRecord objects and connections. Workers should receive a record id and not the full object. Preferably, workers should wrap database access related code in a <code>ActiveRecord::Base.connection_pool.with_connection</code> block so it does not exhaust connections in the pool.</p><pre class="brush: ruby; title: ; notranslate">
class WelcomeEmailWorker
  include SuckerPunch::Worker
  def perform(user_id)
    ActiveRecord::Base.connection_pool.with_connection do
      user = User.find(user_id)
      UserMailer.welcome(user).deliver
    end
  end
end
</pre><h3>Connections</h3><p>You have to be careful not just with ActiveRecord connections, but also with any redis, memcache, or any other service that may limit connections.<br
/> It&#8217;s important to also limit the number of workers based on those limits. You don&#8217;t want to have 20 workers, when there are 10 connections max!</p><h3>Unicorn/Passenger</h3><p>If you&#8217;re using Unicorn or Passenger as your web server, there&#8217;s one more step to ensure everything is set up well. That is to define the queues on blocks that run after the server.</p><p>For unicorn (only needed if <code>preload_app true</code> is set):</p><pre class="brush: ruby; title: ; notranslate">
# config/unicorn.rb
after_fork do |server, worker|
  SuckerPunch.config do
    queue name: :log_queue, worker: LogWorker, workers: 10
  end
end
</pre><p>For Passenger:</p><pre class="brush: ruby; title: ; notranslate">
# config/initializers/sucker_punch.rb
if defined?(PhusionPassenger)
  PhusionPassenger.on_event(:starting_worker_process) do |forked|
    SuckerPunch.config do
      queue name: :log_queue, worker: LogWorker, workers: 10
    end
  end
end
</pre><h3>Further Reading:</h3><ul><li><a
href="https://devcenter.heroku.com/articles/concurrency-and-database-connections#connection-pool">Concurrency and Database Connections, Heroku Dev Center</a></li><li><a
href="https://github.com/celluloid/celluloid/wiki/Frequently-Asked-Questions">Celluloid&#8217;s Frequently Asked Questions</a></li><li><a
href="https://github.com/celluloid/celluloid/wiki/Thread-safety-notes">Celluluid&#8217;s Thread safety notes</a></li></ul> ]]></content:encoded> <wfw:commentRss>http://rubysource.com/simple-background-jobs-with-sucker-punch/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Git: Simply Stashing</title><link>http://rubysource.com/git-simply-stashing/</link> <comments>http://rubysource.com/git-simply-stashing/#comments</comments> <pubDate>Mon, 20 May 2013 13:30:54 +0000</pubDate> <dc:creator>Jonathan Jackson</dc:creator> <category><![CDATA[Outside Ruby]]></category> <category><![CDATA[git]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=5741</guid> <description><![CDATA[Most Rubyists find themselves using Git on a day-to-day basis. We use it to organize our projects, protect ourselves from errors, and to make changes with the confidence that our code is safe. Its simple command line interface belies its flexibility and&#8230;]]></description> <content:encoded><![CDATA[<p><a
href="http://rubysource.com/?attachment_id=5742" rel="attachment wp-att-5742"><img
class="alignleft size-medium wp-image-5742" alt="stash" src="http://cdn.rubysource.com/files/2013/05/stash-300x168.png" width="300" height="168" /></a>Most Rubyists find themselves using Git on a day-to-day basis. We use it to organize our projects, protect ourselves from errors, and to make changes with the confidence that our code is safe. Its simple command line interface belies its flexibility and depth. Because of this power, it definitely merits deep study and practice.</p><p>Today, I want to discuss a few of the techniques that I use to make swapping branches easier, specifically the <code>git-stash</code> command.</p><p><code>git-stash</code> is a wonderful way to temporarily hide changes in a dirty working directory. This allows us to work on a feature branch and quickly swap to other branches without the bother of staging / commiting our changes first.</p><p><a
href="http://rubysource.com/?attachment_id=5745" rel="attachment wp-att-5745"><img
class="alignnone size-medium wp-image-5745" alt="typical-stash-workflow" src="http://cdn.rubysource.com/files/2013/05/typical-stash-workflow-172x300.png" width="172" height="300" /></a><br
/> <small>[Typical Stash Workflow]</small></p><p>If you are like most developers, then the extent of your experience with <code>git-stash</code> extends no further than the following two commands:</p><pre class="brush: bash; title: ; notranslate">
git stash
</pre><p>and</p><pre class="brush: bash; title: ; notranslate">
git stash pop
</pre><p>Often, this is enough for what we need, but <code>git-stash</code> has so much more to offer. Let&#8217;s explore some of its myriad abilities.</p><h2>Saving With a Description</h2><p>The simplistic approach to stashing is <code>git stash</code> and is analogous (mostly) to <code>git stash save</code>. The latter takes several arguments, most important of which is a description. If you are going to use stash as part of your normal workflow you <strong>must</strong> use these descriptions to keep better control of each stash. Let&#8217;s see how that&#8217;d work:</p><pre class="brush: bash; title: ; notranslate">
touch gum_drop_mountains.rb   # New feature
git add .                     # git-stash only works on tracked files
                              # unless you pass the --include-untracked flag
                              # to stash save
git stash save &quot;Initial pass on Gum Drop Mountains.&quot;
git stash list
stash@{0}: On master: Initial pass on Gum Drop Mountains.
</pre><p>Now when we view the current stash list we see meaningful messages instead of the default description <code>WIP on &lt;branch&gt; &lt;last commit message&gt;</code>.</p><h2>Stashing In Patches</h2><p>Sometimes you want to stash a specific subset of changes, for this we can use the <code>--patch</code> (<code>-p</code>) flag. This allows us to interactively specify which changes will be included in this stash.</p><pre class="brush: bash; title: ; notranslate">
# edit gum_drop_mountain.rb
git stash save -p &quot;Added GDM tests&quot;
</pre><p>Which will drop you into a prompt like below:</p><p><a
href="http://rubysource.com/?attachment_id=5746" rel="attachment wp-att-5746"><img
class="alignnone size-large wp-image-5746" alt="git-stash-p" src="http://cdn.rubysource.com/files/2013/05/git-stash-p-530x223.png" width="530" height="223" /></a></p><p>Once you&#8217;ve begun the interactive process, you can press <code>?</code> to see a list of available commands. Upon completion you&#8217;ll be able to see your new stash with <code>git stash list</code>. The changes that are left uncommitted (to the stash) will still be in your current directory.</p><h2>Listing Stashes</h2><p>The most common way to view your current stashed changes is to type <code>git stash list</code> as we touched on earlier. This will print:</p><pre class="brush: bash; title: ; notranslate">
# Unless you are cool and passed a description
stash@{0}: WIP on submit: 6ebd0e2... Initial Commit.
stash@{1}: On master: 9cc0589... Add git-stash
</pre><p>Here, the stashes are specified as <code>stash@{&lt;revision&gt;}</code>. Many of the <code>git-stash</code> commands allow this stash identifier to be explicitly set, making it an important piece of information. <code>git stash list</code> takes any of the options accessible to <code>git log</code> which allows for some customization.</p><pre class="brush: bash; title: ; notranslate">
git stash list --pretty=format:'%Cblue%h %Cred* %C(yellow)%s'
</pre><p>Which will output something like:</p><p><a
href="http://rubysource.com/?attachment_id=5747" rel="attachment wp-att-5747"><img
class="alignnone size-large wp-image-5747" alt="pretty-format" src="http://cdn.rubysource.com/files/2013/05/pretty-format-530x26.png" width="530" height="26" /></a></p><p>You can alias <code>git-log</code> with the above formatting, which is common. Doing so with the git stash list is a prime candidate for this style of optimization, as being able to quickly decipher these descriptions can save you time.</p><h4>Showing Individual Stashes</h4><p>Showing stashes can be done with the <code>git stash show</code> command. It accepts an argument for the stash identifier and defaults to <code>stash@{0}</code> (most recent). It will display the diffstat by default.</p><h2>Applying Stashes</h2><p>Let&#8217;s assume we&#8217;ve been working for few hours. During this time we&#8217;ve been pulled off several times to work on other branches in the project. As we were pulled away, we&#8217;ve stashed our dirty working directories and given them all nice messages so we can cobble it all together later. Now that the bugs are fixed, we get to go back to our features. Our stash list looks like this:</p><pre class="brush: bash; title: ; notranslate">
git stash list
stash@{0}: On gum_drop_mountain: Touching up gum drop mountain peaks.
stash@{1}: On candy_cane_lane: Adding some tinsel to candy cane lane
stash@{2}: On master: First pass on gum drop mountains.
</pre><p>We want to go back to the <code>candy_cane_lane</code> feature branch and apply our stashed changes there. To do so, we are going to use the <code>git stash apply</code> command.</p><pre class="brush: bash; title: ; notranslate">
git checkout candy_cane_lane
git stash apply stash@{1}    # Specified stash or most recent (stash@{0})
</pre><p>Once we&#8217;ve done that, our changes are back in their proper place and we can resume work.</p><p>Git is smart here, in that you can apply these changes to branches other than the branch from which they were stashed. Furthermore, the branch you apply the changes to doesn&#8217;t have to be a clean directory! Git will throw a merge conflict to be manually resolved if the stash no longer applies cleanly[1].</p><h2>Removing Stashes</h2><p>Just for funzies, let&#8217;s check the stash list before moving on.</p><pre class="brush: bash; title: ; notranslate">
git stash list
stash@{0}: On gum_drop_mountain: Touching up gum drop mountain peaks.
stash@{1}: On candy_cane_lane: Adding some tinsel to candy cane lane
stash@{2}: On master: First pass on gum drop mountains.
</pre><p>What? Our stash is still present in our list even after applying it. That&#8217;s because <code>apply</code> doesn&#8217;t automatically clean up after itself, we have to use the following command to remove the stash</p><pre class="brush: bash; title: ; notranslate">
git stash drop stash@{1}
</pre><p>Which will drop just the stash we applied. Had we not given it a stash identfier it would have assumed we meant to drop <code>stash@{0}</code> (most recent). If we want to be rid of all stashed changes we can run <code>git stash clear</code>. <strong>Beware</strong> of clearing stashes (with either drop or clear) as they are <strong>impossible</strong> to recover.</p><h2>The Oh-So Convenient Pop</h2><p>After applying a stash of changes to a branch, it is quite frequently a good idea to remove that stash from the list (like above). Luckily, Git provides us with a convenient method that wraps this workflow. Assume we want to apply <code>stash@{1}</code> and remove it from the stash list. We can run the following command:</p><pre class="brush: bash; title: ; notranslate">
git stash pop stash@{1}
</pre><p>If we provide no stash identifier, it would assume most recent (like most <code>git-stash</code> commands). The <code>git-stash</code> <a
href="https://www.kernel.org/pub/software/scm/git/docs/git-stash.html">man page</a> provides a good way of thinking about this command. That it does the inverse of <code>git stash save</code>.</p><h2>Branching a Stash</h2><p>Sometimes you&#8217;ll find yourself in a situation where you&#8217;ve begun changes on the wrong branch (master for example) and want to take your current dirty directory to its own branch. Well you are in luck! Normally you&#8217;d have to do something like this:</p><pre class="brush: bash; title: ; notranslate">
git stash
git checkout -b new_feature
git stash pop
</pre><p>But <code>git-stash</code> has a convenience method to do just this. Simply:</p><pre class="brush: bash; title: ; notranslate">
git stash
git stash branch new_feature
</pre><p>This will create and checkout a new branch <code>new_feature</code> and apply <code>stash@{0}</code> to it.</p><h2>To Stash or Not To Stash</h2><p>Determining when to stash versus when to commit and squash later is a matter of taste. As long as you write descriptive messages about the stash and be sure to keep your stash list clean, then stashing is a great way to quickly get to a clean directory. If, on the other hand, you don&#8217;t take care, you&#8217;ll find yourself painfully wasting time figuring out which stash is which.</p><p>Warnings aside, stashing is a powerful ability that Git provides and with a little practice it can help you navigate your code more quickly and safely. Happy hacking!</p><h4>Resources</h4><ol><li><a
href="http://git-scm.com/book/en/Git-Tools-Stashing">Pro Git (stashing)</a></li><li><a
href="http://gitready.com/">git ready</a></li><li><a
href="https://www.kernel.org/pub/software/scm/git/docs/git-stash.html">git-stash man page</a></li></ol> ]]></content:encoded> <wfw:commentRss>http://rubysource.com/git-simply-stashing/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Interviewing Rogues: David Brady</title><link>http://rubysource.com/interviewing-rogues-david-brady/</link> <comments>http://rubysource.com/interviewing-rogues-david-brady/#comments</comments> <pubDate>Thu, 16 May 2013 13:30:15 +0000</pubDate> <dc:creator>Thom Parkin</dc:creator> <category><![CDATA[Interviews]]></category> <category><![CDATA[rogues]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=5717</guid> <description><![CDATA[David Brady &#8211; Learning and Laughing Every week a group of Ruby Enthusiasts get together in a Virtual Roundtable and explore/discuss/argue some a spect of the Ruby language or the Ruby community. They allow the rest of us to listen in on&#8230;]]></description> <content:encoded><![CDATA[<h2><span
style="font-size: 1.5em"><a
href="http://rubysource.com/interviewing-rogues-david-brady/db/" rel="attachment wp-att-5721"><img
class="alignleft size-medium wp-image-5721" alt="db" src="http://cdn.rubysource.com/files/2013/05/db-300x300.png" width="300" height="300" /></a>David Brady &#8211; Learning and Laughing</span></h2><p>Every week a group of Ruby Enthusiasts get together in a Virtual Roundtable and explore/discuss/argue some a<br
/> spect of the Ruby language or the Ruby community. They allow the rest of us to listen in on their discussion by way of The Ruby Rogues podcast. The podcast can be accessed on iTunes or on <a
href="http://www.rubyrogues.com">the website</a> and offers a ‘fly on the wall’ view of a great bunch of technology savvy people sharing their ideas and opinions.</p><p>In this series of articles I will share the results of my interviews with each member of this special group.</p><h2>Beginning with &#8220;The Basics&#8221;</h2><p><strong>Thom:</strong> Like any interview, in a podcast or in writing, the first question of course is; &#8220;David, please tell us about yourself.&#8221;"</p><p><strong>David:</strong> No, next question.</p><p><strong>Thom:</strong> Okay. That was a short interview!!</p><p><strong>David:</strong> No, I’m kidding&#8230;</p><p>I am kind of an old-school programmer, kind of self-taught. I grew up in rural Utah. When I was in middle school they had some old Apple II computers — well, at the time they were rather new — and they say when you first fall in love with computers, your emotional growth is stunted at that point. So I was 11 and that’s my emotional age to this day because I just absolutely fell in love with computers. I’ve been tinkering and hacking the entire time since.</p><p>I took kind of a weird route through programming. I started in Apple Basic and then I moved to IBM and DOS and so I went into BasicA and GW-Basic and I moved up through the &#8216;Basics&#8217;, moved to Quick Basic &#8211; well QBasic &#8211; Visual Basic, then I sort of went down the rabbit hole. I went into C++ and then into C and then into Assembler.</p><p>At that point I was writing graphics drivers and writing video games and just really, really enjoying it and then somebody said, “You know, does it really have to hurt this much to be efficient? And do you really need to be that efficient?” So I said, “I don’t know, tell me more about what you mean.” He said, “Well, there’s this language called Perl and it’s this very high-level language, much higher than even Basic.” I absolutely fell in love with how much you could get done — yeah, you gave up a ton of efficiency, but for 90% of the things you do, you don’t need super efficiency.</p><p>Also, Perl is write only [as a language], it’s very terse and everything is implicit, they value implicitness, don’t waste time telling me things that I already know.</p><p>A few years later Python came along and I’m like, “Oh my gosh, it’s like Perl, only clear.”<br
/> Python has one right way to do it and every other way is wrong and that started to stick in my craw a little bit. Then I came across Ruby. Ruby’s attitude, of course, is there’s probably one right way to do this, but this general class of problem has nine different types of solutions. Unlike Perl where you should try to use all of them for every situation, Ruby says you should use the best one for each given situation. I think a lot of Python people would say that, too, so I don’t want to kick Python’s tires too much, it’s not a bad language.</p><p>But I finally found my way into Ruby and I’ve just absolutely loved it. It’s a language that lets me do what I want the way I want and communicate to others in the way that I like.</p><p>So that’s more of a programming language history of me. Does that answer the question?</p><p><strong>Thom:</strong> That’s perfect, because my next question was how did you discover Ruby? You just answered it for me.</p><p><strong>David:</strong> Perfect.</p><p><strong>Thom:</strong> This interview is remote control.</p><p><strong>David:</strong> Perfect.</p><h2>Neighbors &#8211; Friends &#8211; Fellow Rogues</h2><p><strong>Thom:</strong> So how did you discover the Ruby Rogues? Did I get an impression that you and Charles Max Wood were friends and had worked together?</p><p><strong>David:</strong> Honestly, &#8220;Location, Location, Location&#8221;, just like in real estate. I lived a mile and a half away from Chuck and I was actually at URUG, the Utah Ruby Users Group, back when there was only one Utah Ruby group [now there’s like five]. I was at URUG the first time Chuck came out to a Ruby meet. We made friends, we found out that we were living — did I say a mile away? More like 500 yards! I mean, literally, he was down my street, hang a left and go down a half a block.</p><p><strong>Thom:</strong> So like if you have a loud party, he’ll know it.</p><p><strong>David:</strong> Yes, exactly. Well, he moved, but it’s a small town, so now he’s two miles away and so we still know where to find each other.</p><p>Yeah, he got talking with James Edward Gray about starting up a panel-based podcast and just kind of out of the blue he’s like, “I think it would be funny to have you on the show,” and I said, “Okay, I will do my best to come and be funny.” I thought I had an opportunity as well to help people, not just be funny, but also to see things in a different way and that’s what I like doing, is getting into a conversation with people and then finding some angle that they haven’t viewed something from, and saying, “Well, have you looked at it this way?” Then there’s that moment when everybody around suddenly goes, “Oh, Wow!” and they make these mental connections that weren’t there before. That’s my favorite thing in the world to do ever.</p><p>I’d like to think that’s why Chuck invited me to be on the show, but the reality is, it’s probably just because I was in the room when he was thinking about it.</p><p><strong>Thom:</strong> I think you don’t give yourself enough credit, but that is funny, the self-deprecating humor seems to be common in the tech world.</p><p><strong>David:</strong> Yeah.</p><p><strong>Thom:</strong> Is there something that you’re working on or some project, is there a soapbox you want to get on?</p><p><strong>David:</strong> No, and I regret that. What I will say is that I have something in the works. I’m not ready to announce yet. I am getting into more of a training and mentoring aspect and so if anybody would like to do that, that’s the direction I’m going with what I’ve got in the works.  But that’s not the main business model, so it’s not even a “You can pay me to train you” thing, it’s just a “Hey, if you want to pair just get a hold of me.”</p><p><strong>Thom:</strong> Terrific. You know you’re going to get a lot of response from that. I may be at the top of that list.</p><p><strong>David:</strong> Absolutely! I think we could, maybe, break something together.</p><p><strong>David:</strong> One of the very first programming books I ever opened up said, “Don’t be afraid of the computer, you can’t hurt the computer by typing on it, unless you type with a hammer.” Yeah, I’ve burned up a disk drive by writing a program that was very disrespectful of the hardware timing. I don’t know how many things I’ve burned up or fried. They lied to me. They absolutely lied to me!</p><p>What is interesting; I actually never programmed in Fortran. I came in right behind it, but everybody that I looked up to and everybody that I mentored under had come out of Fortran. So for, oh gosh, 15 years, I was (in 1999) writing C++ code professionally as my career and I was rigidly formatting my code to 72 columns. I did not know why, and I got with an old-timer who saw that I was still doing that and he just died laughing. He’s said, “Do you know why you’re doing that?” I said, “No.” He said, “Because in Fortran you need to leave 8 characters for the address line on the punch card and you’ve only got 80 columns on the punch card, that’s why you’re doing that. You’ve got a 21-inch monitor,” (those were the biggest ones at the time) “stop formatting your code at 72 characters.” It’s like the old story of cutting off the pot roast because Grandma did it, but Grandma did it because it wouldn’t fit in her oven.</p><p><strong>Thom:</strong> Yes, it wouldn’t fit in the pan she had, right.</p><p><strong>David:</strong> Yes.</p><p><strong>Thom:</strong> So you probably also then &#8211; the habit I can’t break &#8211; put the constant first in a comparison?</p><p><strong>David:</strong> Actually, no, that came after my time. I came from Basic and then into C++, you can do</p><pre class="brush: cpp; title: ; notranslate">
if X = 42
</pre><p><em
id="__mceDel"> Right? And oops, you just assigned X the value 42.</em></p><p><strong>Thom:</strong> Exactly. Instead of comparing, you just assigned it.</p><p><strong>David:</strong> But I had gone through the phase where I was writing loops like <code>while *p++ = *d++</code> which is a de-reference copy and re-reference and then increment both pointers inside the <code>If</code> test.</p><p><strong>Thom:</strong> Right.</p><p><strong>David:</strong> And if you look at the source code for <code>strcpy</code> in the old C-reference, that kind of monkey business was not only allowed but almost praised.</p><p>So when Microsoft introduced the coding style of swapping the constant and the variable, it was just <strong>wrong</strong>. Constant == variable, it just never caught on and I still don’t do it to this day. I tried, I don’t like to throw away an idea without tasting it first and I spent a year where I rigidly did — I think they call LHC style, Left Hand Constant style — and no. It just read wrong to me.</p><p><strong>David:</strong> Ruby does the same thing because you can do</p><pre class="brush: ruby; title: ; notranslate">
if fp = file.open(...)
</pre><p>Then you’ve got an <code>fp</code> variable in the next line.</p><p><strong>Thom:</strong> When I work in VBScript, it just <em>(shudder)</em>&#8230;</p><p><strong>David:</strong> Just between you and me and — You know, I was going to say this is not for print, but you can go ahead and print this. I fed my family for a year writing VBA and VB script and so I don’t tell people that I do it, because if you want to hire me for that skill, I don’t want to work for you.</p><p><strong>Thom:</strong> I agree. In job interviews, when asked if I do VB or VB scripting, I always say, “Yes, but I wash my hands afterwards.”</p><p><strong>David:</strong> Yes. Yes, exactly. So anyway, I just wanted to mention that as a yes, it’s a mark of shame, but I stand with you in solidarity, brother.</p><h2>Revealing a new Perspective</h2><p><strong>Thom:</strong> What do you see your role in the Ruby Rogues?</p><p><strong>David:</strong> Obviously I have a pretty good sense of humor and I like to inject that where appropriate and then a little bit more, as much as I can get away with without annoying people too much.</p><p>Like I mentioned earlier, trying to make people think and see things from different ways is a very big goal. But the open secret that I have when I’m on the show is I love it when I’m on a show with two people that have wildly differing approaches to something. That one is like, “Don’t ever test,” and the other one is, “Over test everything.” When I can look at two people and say, “You know, you guys are in disagreement on 98% here, let me throw one-third thought in here that you guys haven’t mentioned,” and then both of them go, “Oh, my gosh, you’re right.” I absolutely love that.</p><p>I love the <strong><em>MINASWAN</em></strong> — that stands for <em>Matz Is Nice And So We Are Nice</em>. I really like honoring that in the community and when we have somebody on the show that has a really strong opinion about something, I love being able to temper that in such a way that we can have people with strong opinions that suddenly find themselves working together with their strong opinions, rather than opposing each other.</p><p>I think that’s kind of why my favorite episodes I’ve ever been on are the ones that have been on like diversity and drama in the Ruby community and interpersonal relationships. Those are my favorite episodes to be on because they’re such juicy topics for people to be wound up about and yet care deeply. When you can get two people and tap into their caring deeply, you can suddenly make them going from disagreeing violently to agreeing; maybe still violently.</p><p>But that’s really what my favorite thing to do on the show is. I just use the humor to distract from that.</p><p><strong>Thom:</strong> Thanks David</p><p><strong>David:</strong> My pleasure.</p><p>You can find David Brady in the usual places:</p><ul><li><a
href="https://github.com/dbrady">Github</a></li><li><a
href="http://rubyrogues.com">Ruby Rogues</a></li></ul> ]]></content:encoded> <wfw:commentRss>http://rubysource.com/interviewing-rogues-david-brady/feed/</wfw:commentRss> <slash:comments>0</slash:comments> <series:name><![CDATA[Interviewing Rogues]]></series:name> </item> <item><title>A Simple Content Management System in Sinatra</title><link>http://rubysource.com/a-simple-content-management-system-in-sinatra/</link> <comments>http://rubysource.com/a-simple-content-management-system-in-sinatra/#comments</comments> <pubDate>Mon, 13 May 2013 13:30:20 +0000</pubDate> <dc:creator>Darren Jones</dc:creator> <category><![CDATA[Sinatra]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=5698</guid> <description><![CDATA[One of the ideas put forward at the recent Summit Awesome Manchester Hackathon was put forward by Tom Oakley for a simple Content Management System that used Markdown. The idea didn&#8217;t get selected as one of the Hackathon projects, but I thought&#8230;]]></description> <content:encoded><![CDATA[<p>One of the ideas put forward at the recent <a
href="http://summitawesome.co.uk/">Summit Awesome Manchester Hackathon</a> was put forward by <a
href="https://twitter.com/tomoakley125">Tom Oakley</a> for a simple Content Management System that used <a
href="http://daringfireball.net/projects/markdown/">Markdown</a>. The idea didn&#8217;t get selected as one of the Hackathon projects, but I thought it sounded a great fit for writing in Sinatra (being lighweight itself and already having support for Markdown baked in). So, after the hackathon was over I decided to have a go at building it.</p><p>I thought it would be interesting (and hoepfully useful) if I documented the progress I made on RubySource as I went along &#8230; so here is part one of the series!</p><p>My idea is to build some Sinatra middleware that could be bolted on to a project to add some simple content management support. I also want to have taggable pages and the possibility of extensions (such as navigation lists modules, file upload modules, email modules etc).</p><h2>MongoDB and Mongoid</h2><p>I decided to use <a
href="http://www.mongodb.org/">MongoDB</a> for persistance. I&#8217;ve used it a little bit in the past and thought that it would be a good fit here. MongoDB is a <a
href="https://en.wikipedia.org/wiki/NoSQL">NoSQL</a> data store that is fast and scalable. It uses the concept of collections of documents instead of tables and rows. A big advantage is that it is schemaless, and therefore, doesn&#8217;t require any migrations. Also, you can add new fields on the fly (in fact documents in the same collection don&#8217;t even have to have the same fields), making it very flexible and suitable for agile development.</p><p>I chose to use Mongoid as an ORM since I liked the look of the syntax, it is well documented, and under very active development. I found installing MongoDB locally to be a bit of a pain, but the <a
href="http://docs.mongodb.org/manual/installation/">MongoDB documentation</a> should help you out. Once it it installed, you need to start the <code>mongod</code> server. This is operating system specific, so consult the documentation about how to get it done.</p><h2>Setting Up</h2><p>To get started, I created the following Gemfile:</p><pre class="brush: ruby; title: ; notranslate">
    source &quot;https://rubygems.org&quot;
    ruby &quot;2.0.0&quot;
    gem &quot;sinatra&quot;
    gem &quot;slim&quot;
    gem &quot;sass&quot;
    gem &quot;mongoid&quot;
    gem &quot;redcarpet&quot;
</pre><p>First of all, Mongoid requires at least Ruby 1.9.3, so it helps to put this in the Gemfile (specifically for when we come to deploy on Heroku later.) Obviously, we need Sinatra. I will be using Slim for the view logic and Sass for the styles. This is purely my choice and my default development choices currently. To be honest, there won&#8217;t be that much styling going on, but it&#8217;s always easier to use Sass. I&#8217;ve already mentioned Mongoid, which leaves Red Carpet &#8211; an implementation of markdown, created by the kind folks at GitHub.</p><p>To make sure everything is installed run <code>bundle install</code> from a command prompt.</p><p>Mongoid also requires a bit of set up. To do this, create a file called <em>mongoid.yml</em> and save it in the same directory. These are the default settings that I&#8217;ve found work nicely to start with Mongoid:</p><pre class="brush: ruby; title: ; notranslate">
    development:
      sessions:
        default:
          database: cms
          hosts:
            - localhost:27017
    production:
      sessions:
        default:
          uri: &lt;%= ENV['MONGOHQ&lt;em&gt;URL'] %&gt;
          options:
            skip_version_check: true
            safe: true
</pre><p></em><br
/> In the same directory, we need to create a file called main.rb where our main functionality will go. To get things moving, I decided to dive in and build a classic application and will move it to modular later. At the beginning of main.rb, we need to require the relevant gems and configure Mongoid:</p><pre class="brush: ruby; title: ; notranslate">
    require 'sinatra'
    require 'sinatra/reloader' if development?
    require 'mongoid'
    require 'slim'
    require 'redcarpet'
    configure do
      Mongoid.load!(&quot;./mongoid.yml&quot;)
    end
</pre><h2>A Page Class</h2><p>Now we come on to the heart of our CMS - the page model. We'll start simple - give it a title and content field:</p><pre class="brush: ruby; title: ; notranslate">
    class Page
      include Mongoid::Document
      field :title,   type: String
      field :content, type: String
    end
</pre><p>To use Mongoid in a class, all you need to do is include the <code>Mongoid::Document</code> model. After this you can define the fields to be used in the database. Our two fields are 'title' and 'content' and they are both of type string. Now we can have a go at creating some pages!</p><h2>Create Some Pages</h2><p>We don't have any routes defined yet, but that doesn't mean we can't create any pages. It's always useful to be able to run our program through the console. Open up a terminal and start up IRB using the following command:</p><pre class="brush: bash; title: ; notranslate">
$&gt; irb
</pre><p>This should give you a ruby prompt similar to the one below where we can require the main.rb file:</p><pre class="brush: ruby; title: ; notranslate">
2.0.0-p0 :001 &gt; require './main'
=&gt; true
</pre><p>Now we can have a go at adding some pages:</p><pre class="brush: ruby; title: ; notranslate">
2.0.0-p0 :002 &gt; hello = Page.new
 =&gt; #
2.0.0-p0 :003 &gt; hello.title = &quot;Hello World!&quot;
 =&gt; &quot;Hello World!&quot;
2.0.0-p0 :004 &gt; hello.content = &quot;
This is our first page
&quot;
 =&gt; &quot;
This is our first page
&quot;
2.0.0-p0 :005 &gt; hello.save
 =&gt; true
</pre><p>We've just created our first page! This is done using the <code>new</code> method. We assign the page to the variable <code>hello</code>. This has setter methods of <code>title</code> and <code>content</code> provided by Mongoid. Once these have been set, we use the <code>save</code> method to commit the changes to the database.</p><p>Rather than writing out each property one at a time and then saving the object, we can create a document in the database in one line, using the <code>create</code> method:</p><pre class="brush: ruby; title: ; notranslate">
2.0.0-p0 :006 &gt; Page.create(title: &quot;Markdown Page&quot;, content: &quot;This page uses markdown&quot;)
 =&gt; #
</pre><p>We can check that the pages have been created by querying the database. First of all, let's check how many pages have been saved using the <code>count</code> method:</p><pre class="brush: ruby; title: ; notranslate">
2.0.0p0 :007 &gt; Page.count
 =&gt; 2
</pre><p>That's good news - there are 2 pages saved in the database, as we expected. There are numerous ways to search for the pages using Mongoid. Here are a selection of queries that we might use:</p><pre class="brush: ruby; title: ; notranslate">
    2.0.0p0 :009 &gt; Page.first
     =&gt; # Page.last
     =&gt; #, title: &quot;Hello World!&quot;, content: &quot;
This is our first page
&quot;&gt;
</pre><p>The <code>first</code> and <code>last</code> methods do exactly as they say on the tin and find the first and last documents in the database, respectively. We can also find a specific document using the <code>find</code> method with the document's id string as an argument:</p><pre class="brush: ruby; title: ; notranslate">
    2.0.0p0 :011 &gt; Page.find(&quot;5173f574a39401776a000002&quot;)
     =&gt; #
</pre><p>We might not always know the document id, so we can also search by the different fields, using the <code>find_by</code> and <code>where</code> methods:</p><pre class="brush: ruby; title: ; notranslate">
2.0.0p0 :012 &gt; Page.find_by(title: &quot;Hello World!&quot;)
 =&gt; #This is our first page
&quot;&gt;
2.0.0p0 :013 &gt; Page.where(title: &quot;Hello World!&quot;).first
 =&gt; #This is our first page
&quot;&gt;
</pre><p>Although these look the same, there is a subtle difference: <code>find_by</code> returns a Page object, wheras <code>where</code> returns a MongoDB criteria, which is a proxy object that can be chained together. It doesn't query the database until all the criteria have been completed.</p><h2>Viewing the Pages</h2><p>Now that we have some pages, let's create some routes and views so that we can see them!</p><p>First of all, we'll create an index page that lists all of the pages in the database. Add the following route to the bottom of main.rb:</p><pre class="brush: ruby; title: ; notranslate">
get '/pages' do
  @pages = Page.all
  @title = &quot;Simple CMS: Page List&quot;
  slim :index
end
</pre><p>This finds all of the pages in the database and stores them as an array in the instance invariable <code>@pages</code>. We also use the instance variable <code>@title</code> to store the title of the page (this will be used in the layout view). We then render the view called index using slim.<br
/> We need to create that view now. Create a file called 'index.slim' and save it in a folder named views in the root directory. Place the following code inside this file:</p><pre class="brush: ruby; title: ; notranslate">
  h1 Pages
  -if @pages.any?
    ul.pages
    - @pages.each do |page|
      == slim :page, :locals =&gt; {page: page}
  - else
    p No pages here!
</pre><p>This is simply a list of all the pages, but it uses a partial to display the actual information about each page in the line <code>== slim :page, :locals =&gt; {page: page}</code>. This means that we need to create another view called 'page.slim' (also saved in the 'views' folder), containing the following code:</p><pre class="brush: ruby; title: ; notranslate">
li
  a href=&quot;/pages/#{page.id}&quot; =page.title
</pre><p>It's not a huge partial, but it makes sense to keep it in a separate file, as we might want to add more information to what we place in here later or use it in other views.</p><p>The page partial had a link to each individual page. We need to write a route handler to deal with that. This is used to display each individual page - place the following code below:</p><pre class="brush: ruby; title: ; notranslate">
    get '/pages/:id' do
      @page = Page.find(params[:id])
      @title = @page.title
      slim :show
    end
</pre><p>This finds the page based on the id give in the URL and creates an instance variable called <code>@page</code> to store the <code>Page</code> object. We also store the title of the page in the <code>@title</code> instance variable.</p><p>Next, let's create a page to view the actual page. Save the following code in the views directory as 'show.slim':</p><pre class="brush: ruby; title: ; notranslate">
h1= @page.title
- if @page.content
  == markdown @page.content
</pre><p>This is a very straightforward view that shows the title as a level-1 heading and using the <code>markdown</code> method to display the content (if there actually is some content). Notice that both of these are methods of the <code>@page</code> instance variable created in the route handler.</p><p>Before we test this out, we need to create a layout for the application. Here is a basic HTML5 layout that will do just fine:</p><pre class="brush: ruby; title: ; notranslate">
doctype html
html
  head
    title= @title || &quot;Simple Sinatra CMS&quot;
  body
    h1
      a href=&quot;/pages&quot; Simple Sinatra CMS
    == yield
</pre><p>Save the code snippet above as 'layout.slim' in the views directory, then start up a server by typing <code>ruby main.rb</code> into a terminal, then go to <a
href="http://localhost:4567/pages">http://localhost:4567/pages</a>in your browser and you should see a list of pages similar to the following:</p><p><a
href="http://rubysource.com/a-simple-content-management-system-in-sinatra/screenshot1-3/" rel="attachment wp-att-5700"><img
class="alignleft size-medium wp-image-5700" alt="Screenshot1" src="http://cdn.rubysource.com/files/2013/05/Screenshot1-300x212.png" width="300" height="212" /></a></p><p>Have a go at navigating around all the pages!</p><p><a
href="http://rubysource.com/a-simple-content-management-system-in-sinatra/screenshot2-3/" rel="attachment wp-att-5701"><img
class="alignleft size-medium wp-image-5701" alt="Screenshot2" src="http://cdn.rubysource.com/files/2013/05/Screenshot2-300x212.png" width="300" height="212" /></a></p><h2>Creating Pages</h2><p>That was fun, but we only have two pages, so we need to create more ... and in the browser too.</p><p>First of all, we need to create a route handler for the page where we will add a new page. Add the following code to 'main.rb', but make sure that it goes <em>before</em> the '/pages/:id' route (this is because Sinatra always looks for the first route that matches a URL and '/pages/:id' matches '/pages/new' by setting params[:id] to 'new'):</p><pre class="brush: ruby; title: ; notranslate">
get '/pages/new' do
  @page = Page.new
  slim :new
end
</pre><p>This simply creates a new page object and stores it in the instance variable <code>@page</code>, which will be available in the view. We probably won't need this object at the moment, but in the future we might be using the same form to edit a page as we do to create a new one. The edit form will have references to the page that is being edited, which will be the <code>@page</code> instance variable. If this didn't exist then we'd get errors, so we just create a new <code>Page</code> object to avoid this.</p><p>We then display the new view, which we better create in the 'views' folder, here's the code:</p><pre class="brush: ruby; title: ; notranslate">
h1 New Page
form action=&quot;/pages&quot; method=&quot;POST&quot;
  fieldset
    legend Create a new page
    == slim :form
  input type=&quot;submit&quot; value=&quot;Create&quot;
</pre><p>This uses a partial for the actual form (so we can reuse it in the edit page). We also need to save the following as 'form.slim':</p><pre class="brush: ruby; title: ; notranslate">
label for=&quot;title&quot; Title:
input.title type=&quot;text&quot; name=&quot;page[title]&quot; value=&quot;#{@page.title}&quot; size=&quot;32&quot;
label for=&quot;content&quot; Content:
textarea.content name=&quot;page[content]&quot; rows=&quot;12&quot; cols=&quot;72&quot; ==@page.content
</pre><p>At the moment, we only have two fields that are required, so it's a fairly straighforward form. You can see the references to the <code>@page</code> instance variable - these will show up as blank when it is a new page.</p><p>Now we need to sort out what happens when that form is submitted. We can use a route handler to deal with this. We use the URL '/pages' as this is what was in the form's 'action' attribute. But in this case, we use the HTTP verb 'post' as this is the method used by the form when it is submitted:</p><pre class="brush: ruby; title: ; notranslate">
post '/pages' do
   page = Page.create(params[:page])
   redirect to(&quot;/pages/#{page.id}&quot;)
end
</pre><p>In this action, we create a new <code>Page</code> object using the <code>create</code> method that we used in IRB earlier. <code>params[:page]</code> is a hash of all the values entered in the form. We then use Sinatra's helpful <code>redirect</code> and <code>to</code> methods to redirect to the URL for the newly created page.</p><p>Now that all the routes are in place, we just need to add a link on the index page to create a new page. Make the following line somewhere near the top of index.slim:</p><pre class="brush: ruby; title: ; notranslate">
a href='/pages/new' Add a new page
</pre><p>Restart the server (Hold down Ctrl + C then type <code>ruby main</code> again) and have a go at creating some new pages.</p><p><a
href="http://rubysource.com/a-simple-content-management-system-in-sinatra/screenshot3-3/" rel="attachment wp-att-5702"><img
class="alignleft size-medium wp-image-5702" alt="Screenshot3" src="http://cdn.rubysource.com/files/2013/05/Screenshot3-300x227.png" width="300" height="227" /></a></p><p>Eureka! We have the beginnings of our simple content management system. We can create new pages using Markdown and view them.<br
/> In part 2, we'll look at editing and deleting pages and also how to create permalinks with 'pretty URLs' based on the title of the page.</p><p>I hope you've enjoyed this post, please leave any feedback in the comments section. I'd be especially interested in hearing any ideas for the project going forward.</p> ]]></content:encoded> <wfw:commentRss>http://rubysource.com/a-simple-content-management-system-in-sinatra/feed/</wfw:commentRss> <slash:comments>6</slash:comments> </item> <item><title>The Ruby Transmogrifier, Part II</title><link>http://rubysource.com/the-ruby-transmogrifier-part-ii/</link> <comments>http://rubysource.com/the-ruby-transmogrifier-part-ii/#comments</comments> <pubDate>Fri, 10 May 2013 13:30:35 +0000</pubDate> <dc:creator>John Ivanoff</dc:creator> <category><![CDATA[Ruby Tutorials]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=5665</guid> <description><![CDATA[Getting information into the Transmogrifier In our last episode, we transmogrified data from one format into another. Now you need to get data into it using the transmogrifier. We could hard code the file names in there but that will come back&#8230;]]></description> <content:encoded><![CDATA[<p><a
href="http://rubysource.com/the-ruby-transmorgrifier/input-output-graphic-2/" rel="attachment wp-att-5674"><img
src="http://cdn.rubysource.com/files/2013/05/Fotolia_40935614_Subscription_XXL1-300x225.jpg" alt="Input Output Graphic" width="300" height="225" class="alignleft size-medium wp-image-5674" /></a><br
/><h2>Getting information into the Transmogrifier</h2><p>In our last <a
href="http://rubysource.com/the-ruby-transmogrifier">episode</a>, we transmogrified data from one format into another. Now you need to get data into it using the transmogrifier. We could hard code the file names in there but that will come back to haunt us. Let&#8217;s make is so we can load in the definition file, the data file, and the name of the output file.</p><p>You&#8217;ll need to read the csv data file. How do you read a file line by line in Ruby?</p><pre class="brush: ruby; title: ; notranslate">
require 'csv'
File.open(&quot;def.txt&quot;) do |file|
  while line = file.gets
    puts line
  end
end
</pre><p>You can save the file. I saved it as transmogrifier.rb</p><p>You can go ahead and run it.</p><pre class="brush: bash; title: ; notranslate">
$ ruby transmogrifier.rb
FIELD NAME      FORMAT
NAME            A(50)
ADDRESS1        A(50)
ADDRESS2        A(50)
CITY            A(50)
STATE           A(2)
ZIP             A(10)
CONTACT         A(50)
CONTACTPHONE    A(10)
ACCOUNTOPENED   9(8)
</pre><p>It works. We have data now we need to load up the definitions. In the transmogrifier.rb go ahead and add the code to do that. Remember what to do?</p><pre class="brush: ruby; title: ; notranslate">
File.open(&quot;data.csv&quot;) do |file|
  while line = file.gets
    puts line
  end
end
</pre><p>If you&#8217;re curious go ahead and run it.</p><p>Now that you have the definitions and the data loaded you need to send them to the transmogrifier. You need to send the data, length of the field, the type, and the column name. Three of the four come from the definition file.</p><p>Let&#8217;s write out the Type, Length and, Field name. The Field name starts at the beginning of a line<br
/> We could split the line up and put it in an array. Since the file uses spaces, not tabs, for aligning things we can use split.</p><pre class="brush: ruby; title: ; notranslate">
require 'csv'
definitions = Array.new
File.open(&quot;def.txt&quot;) do |file|
  while line = file.gets
    definitions = line.split()
    puts definitions[0]
    puts definitions[1]
  end
end
...
</pre><p>That gets us the field name but the Type and Length are still together. Were you thinking of regular expressions to find the Type and the Length?</p><pre class="brush: ruby; title: ; notranslate">
require 'csv'
definitions = Array.new
File.open(&quot;def.txt&quot;) do |file|
  while line = file.gets
    definitions = line.split()
    field = definitions[0]
    type = definitions[1] =~ /9/? '9' : 'A'
    length = definitions[1].slice(1..definitions[1].length).gsub(/[^0-9]/, &quot;&quot;)
    puts 'field =&gt; ' + field.upcase + ' type =&gt; ' + type + ' length =&gt; ' + length.to_s
  end
end
</pre><p>Go ahead run it.</p><pre class="brush: bash; title: ; notranslate">
$ ruby transmogrifier.rb
field =&gt; FIELD type =&gt; A length =&gt;
field =&gt; NAME type =&gt; A length =&gt; 50
field =&gt; ADDRESS1 type =&gt; A length =&gt; 50
field =&gt; ADDRESS2 type =&gt; A length =&gt; 50
field =&gt; CITY type =&gt; A length =&gt; 50
field =&gt; STATE type =&gt; A length =&gt; 2
field =&gt; ZIP type =&gt; A length =&gt; 10
field =&gt; CONTACT type =&gt; A length =&gt; 50
field =&gt; CONTACTPHONE type =&gt; A length =&gt; 10
field =&gt; ACCOUNTOPENED type =&gt; 9 length =&gt; 8
</pre><p>Now we&#8217;re getting somewhere. Three of the four variables that get passed to the transmogrifier. <em>Maniacal laugh.</em></p><h2>One Queston.</h2><p>How do you think you will get the data into that array?</p><p>What if we throw the array into a hash? Then, take that hash into another hash where the key is the field name and the data is from the first hash.</p><p>All we need to do is loop through data and find the key from the hash that goes with that column and send all that to the transmogrifier. Keeping things simple here.</p><p>First, go ahead and put the definition data into a hash. Then stuff that into a hash. For fun, go ahead and output that last hash so we can see it.</p><pre class="brush: ruby; title: ; notranslate">
require 'csv'
object_name = Hash.new
definitions = Array.new
File.open(&quot;def.txt&quot;) do |file|
  while line = file.gets
    definitions = line.split()
    field = definitions[0]
    type = definitions[1] =~ /9/? '9' : 'A'
    length = definitions[1].slice(1..definitions[1].length).gsub(/[^0-9]/, &quot;&quot;)
    object_formatting = Hash.new
      object_formatting[&quot;type&quot;] = type
      object_formatting[&quot;length&quot;] = length
      object_formatting[&quot;field&quot;] = field
      object_name[field.upcase] = object_formatting
  end
end
puts object_name
</pre><p>Run it and let&#8217;s and see what we have.</p><pre class="brush: bash; title: ; notranslate">
$ ruby transmogrifier.rb
{&quot;FIELD&quot;=&gt;{&quot;type&quot;=&gt;&quot;A&quot;, &quot;length&quot;=&gt;&quot;&quot;, &quot;field&quot;=&gt;&quot;FIELD&quot;}, &quot;NAME&quot;=&gt;{&quot;type&quot;=&gt;&quot;A&quot;, &quot;length&quot;=&gt;&quot;50&quot;, &quot;field&quot;=&gt;&quot;NAME&quot;}, &quot;ADDRESS1&quot;=&gt;{&quot;type&quot;=&gt;&quot;A&quot;, &quot;length&quot;=&gt;&quot;50&quot;, &quot;field&quot;=&gt;&quot;ADDRESS1&quot;}, &quot;ADDRESS2&quot;=&gt;{&quot;type&quot;=&gt;&quot;A&quot;, &quot;length&quot;=&gt;&quot;50&quot;, &quot;field&quot;=&gt;&quot;ADDRESS2&quot;}, &quot;CITY&quot;=&gt;{&quot;type&quot;=&gt;&quot;A&quot;, &quot;length&quot;=&gt;&quot;50&quot;, &quot;field&quot;=&gt;&quot;CITY&quot;}, &quot;STATE&quot;=&gt;{&quot;type&quot;=&gt;&quot;A&quot;, &quot;length&quot;=&gt;&quot;2&quot;, &quot;field&quot;=&gt;&quot;STATE&quot;}, &quot;ZIP&quot;=&gt;{&quot;type&quot;=&gt;&quot;A&quot;, &quot;length&quot;=&gt;&quot;10&quot;, &quot;field&quot;=&gt;&quot;ZIP&quot;}, &quot;CONTACT&quot;=&gt;{&quot;type&quot;=&gt;&quot;A&quot;, &quot;length&quot;=&gt;&quot;50&quot;, &quot;field&quot;=&gt;&quot;CONTACT&quot;}, &quot;CONTACTPHONE&quot;=&gt;{&quot;type&quot;=&gt;&quot;A&quot;, &quot;length&quot;=&gt;&quot;10&quot;, &quot;field&quot;=&gt;&quot;CONTACTPHONE&quot;}, &quot;ACCOUNTOPENED&quot;=&gt;{&quot;type&quot;=&gt;&quot;9&quot;, &quot;length&quot;=&gt;&quot;8&quot;, &quot;field&quot;=&gt;&quot;ACCOUNTOPENED&quot;}}
...
</pre><p>Lovely. Now you need to process the data file. Let&#8217;s get the Field name and the data for that column</p><pre class="brush: ruby; title: ; notranslate">
CSV.foreach(&quot;data.csv&quot;, headers: true) do |data|
  data.headers.each do |field|
    puts field + ' =&gt; ' + data[field].to_s
  end
end
</pre><p>Hold on! What is that CVS.foreach() call? Let&#8217;s <a
href="http://ruby-doc.org/stdlib-1.9.3/libdoc/csv/rdoc/CSV.html">take a look at that</a>.<br
/> That line will process a CSV file and treat the first line as a header and not data.</p><p>When you run that you output should look like</p><pre class="brush: bash; title: ; notranslate">
name =&gt; Wonder widgets
address1 =&gt; 1600 Vassar Street
address2 =&gt;
city =&gt; dallas
state =&gt; tx
zip =&gt; 75220
contact =&gt; Tim Smith
contactPhone =&gt; 214-555-1212
accountOpened =&gt; 12052001
name =&gt; Timmy's Bikes
address1 =&gt; 2723 Auburn Street
address2 =&gt; Building 3
city =&gt; Erie
state =&gt; PA
zip =&gt; 16508-1234
contact =&gt;
contactPhone =&gt; 814-555-4321
accountOpened =&gt; 865289
</pre><h2>We&#8217;re Almost Home.</h2><p>Now we have the data and the definitions. Let&#8217;s send that information to the transmogrifier.</p><p>As we loop through the data, we need to find the matching key form the hash we made earlier. When we match it send it to the transmogrifier</p><pre class="brush: ruby; title: ; notranslate">
CSV.foreach(&quot;data.csv&quot;, headers: true) do |data|
  data.headers.each do |field|
    object_name.each do |key,value|
      if field.upcase == key
        field.upcase!
        line = transmogrifier(data[field].to_s, object_name[field][&quot;length&quot;].to_i, object_name[field][&quot;type&quot;], field)
        print line
      end
    end
  end
  puts ' '
  puts ' --------- '
end
</pre><p>That will do all that we talked about. Had we written tests like in the first part we would catch a bunch of gotchas. What if there is no data for a column? How do we get all the data on one row? How do we save the data? How do we pass the definition and data file names in?</p><p>Here&#8217;s what I came up with before refactoring</p><pre class="brush: ruby; title: ; notranslate">
require 'csv'
def transmogrifier(data,len,type,column)
proper_formatted = ''
unless data.nil?
    case
    when data.length &gt; len
      data = data.slice(0..(len-1))
    when data.length &lt; len
      if type == &quot;A&quot;
        data = data.ljust(len)
      else
        data = data.rjust(len)
      end
    else
      if column == &quot;ACCOUNTOPENED&quot;
        data = data.slice(4..7)+data.slice(0..3)
      else
        data
      end
    end
    proper_formatted += data
  end
  proper_formatted += '|' # added pipes to see where the field ends
  proper_formatted
end
object_name = Hash.new
file = File.new(ARGV[0],&quot;r&quot;)
definitions = Array.new
while (line = file.gets)
    definitions = line.split()
    field = definitions[0]
    type = definitions[1] =~ /9/? '9' : 'A'
    length = definitions[1].slice(1..definitions[1].length).gsub(/[^0-9]/, &quot;&quot;)
    object_formatting = Hash.new
      object_formatting[&quot;type&quot;] = type
      object_formatting[&quot;length&quot;] = length
      object_formatting[&quot;field&quot;] = field
      object_name[field.upcase] = object_formatting
  end
file.close
aFile = File.new(ARGV[2], &quot;w&quot;)
CSV.foreach(ARGV[1], headers: true) do |data|
  data.headers.each do |field|
    object_name.each do |key,value|
      if field.upcase == key
        field.upcase!
        line = transmogrifier(data[field].to_s, object_name[field][&quot;length&quot;].to_i, object_name[field][&quot;type&quot;], field)
        aFile.write(line)
        print line  #so you can see something in the terminal window
      end
    end
  end
  aFile.write(&quot;\n&quot;)
  puts ' '
  puts ' --------- '
end
aFile.close
</pre><p>If you run <code>$ ruby transmogrifier.rb def.txt data.csv formated.txt</code><br
/> You should have a new file in the folder you are working in called formated.txt</p><p>Now go forth and transmogrify <em>Maniacal laugh.</em></p> ]]></content:encoded> <wfw:commentRss>http://rubysource.com/the-ruby-transmogrifier-part-ii/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>The Ruby Transmogrifier</title><link>http://rubysource.com/the-ruby-transmorgrifier/</link> <comments>http://rubysource.com/the-ruby-transmorgrifier/#comments</comments> <pubDate>Thu, 09 May 2013 13:30:52 +0000</pubDate> <dc:creator>John Ivanoff</dc:creator> <category><![CDATA[Ruby Tutorials]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=5662</guid> <description><![CDATA[One of the things computers are good at is moving data. When you have to migrate data from one type to another, I have found that Ruby makes my job a lot easier. A while back I had this a task that&#8230;]]></description> <content:encoded><![CDATA[<p><a
href="http://rubysource.com/the-ruby-transmorgrifier/input-output-graphic/" rel="attachment wp-att-5672"><img
class="alignleft size-full wp-image-5672" alt="Input Output Graphic" src="http://cdn.rubysource.com/files/2013/05/Fotolia_40935614_Subscription_XXL.jpg" width="170" height="127" /></a>One of the things computers are good at is moving data. When you have to migrate data from one type to another, I have found that Ruby makes my job a lot easier. A while back I had this a task that involved moving data. We were getting dozens of data sets that needed to be converted from CSV to a fixed length text file. This is the story of how we got it done.</p><h2>Defining the Finished Output.</h2><p>We are given a, let&#8217;s say, definition file on how the data needed to be formatted for importing. Let&#8217;s say it looked like this.</p><pre class="brush: plain; title: ; notranslate">
FIELD NAME      FORMAT
NAME            A(50)
ADDRESS1        A(50)
ADDRESS2        A(50)
CITY            A(50)
STATE           A(2)
ZIP             A(10)
CONTACT         A(50)
CONTACTPHONE    A(10)
ACCOUNTOPENED   9(8)
</pre><p>What does this mean? We&#8217;re really focused on the format. &#8216;A&#8217; means alphanumeric with white space added to the right. &#8217;9&#8242; means numeric with white space added to the left. The number means length of the field, so &#8216;A(10)&#8217; is a alphanumeric field of 10 characters.</p><p>Our source data is in a CSV file. Something like:</p><pre class="brush: plain; title: ; notranslate">
name,address1,address2,city,state,zip,contact,contactPhone,accountOpened
Wonder widgets,1600 Vassar Street,,dallas,dallas,tx,75220,Tim Smith,214-555-1212,12052001
Timmy's Bikes,2723 Auburn Street,Building 3,Erie,ERIE,PA,16508-1234,,814-555-4321,03232011
</pre><p>The output just needs to be a text file.</p><pre class="brush: plain; title: ; notranslate">
Wonder widgets  1600 Vassar Street             dallas tx75220     Tim Smith 214-555-121220011205
Timmy's Bikes   2723 Auburn Street  Building 3 Erie   PA16508-1234          814-555-432120110323
</pre><p>How would you do that?</p><h2>Time to Start Transmogrifing</h2><p>How would you <em>transmogrify</em> the data? From the target definition, we know the name will be 50 characters long. You can write a method to take the <em>name</em> column and make it 50 characters long. Want to plow down that path? We can re-factor later. Remember you have to pass in text.</p><pre class="brush: ruby; title: ; notranslate">
def nameFixing(name)
  name = name.ljust(50)
end
</pre><p>Good old <a
href="http://www.ruby-doc.org/core-1.9.3/String.html#method-i-ljust">ljust</a> will add the extra white space we need. Does this seem to easy? It is Ruby, but maybe we should test this.</p><pre class="brush: ruby; title: ; notranslate">
require 'test/unit'
def nameFixing(name)
  name = name.ljust(50)
end
class NameLengthTest &lt; Test::Unit::TestCase
  def test_name_short_should_equal_50
    name = nameFixing &quot;boo&quot;
    assert_equal(name.length, 50)
  end
end
</pre><p>Save that file and go ahead and run the test.</p><pre class="brush: bash; title: ; notranslate">
$ ruby ljust_test.rb
Loaded suite ljust_test
Started
.
Finished in 0.000802 seconds.
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
Test run options: --seed 27164
</pre><p>Sweet, It did work.</p><p>What happens if it&#8217;s more than 50 characters long? Go ahead and take stab at it.</p><pre class="brush: ruby; title: ; notranslate">
require 'test/unit'
def nameFixing(name)
  name = name.ljust(50)
end
class NameLengthTest &lt; Test::Unit::TestCase
  def test_name_short_should_equal_50
    name = nameFixing &quot;boo&quot;
    assert_equal(name.length, 50)
  end
def test_long_short_should_equal_50
    name = nameFixing &quot;dgrtefdgdfshrtyutyuykhjkmbnmvcbdfgrthjghjgghvdfgrstthg&quot;
    assert_equal(name.length, 50)
  end
end
</pre><p>&nbsp;</p><pre class="brush: bash; title: ; notranslate">
$ ruby ljust_test.rb
Loaded suite ljust_test
Started
F.
Finished in 0.001179 seconds.
1) Failure:
test_long_short_should_equal_50(NameLengthTest) [ljust_test.rb:15]:
&lt;54&gt; expected but was
&lt;50&gt;.
2 tests, 2 assertions, 1 failures, 0 errors, 0 skips
Test run options: --seed 40377
</pre><p>Did you expect that? <code>ljust</code> adds white space, but does not slice off extra characters. What do you think would slice off the extra characters? You got it <a
href="http://www.ruby-doc.org/core-1.9.3/String.html#method-i-slice">slice</a>.</p><p>If the name is less than 50 we need to add white space to the left. If the name is over 50 we need to slice off the extra characters. If the name has 50 characters we leave it alone. Did we just rewrite our tests?</p><pre class="brush: ruby; title: ; notranslate">
require 'test/unit'
def nameFixing(name)
  case
  when name.length 50
    name = name.slice(0..49)
  when name.length &lt; 50
    name = name.ljust(50)
  else
    name
  end
end
class NameLengthTest &lt; Test::Unit::TestCase
  def test_name_short_should_equal_50
    name = nameFixing &quot;boo&quot;
    assert_equal(name.length, 50)
  end
def test_name_long_should_equal_50
    name = nameFixing &quot;dgrtefdgdfshrtyutyuykhjkmbnmvcbdfgrthjghjgghvdfgrstthg&quot;
    assert_equal(name.length, 50)
  end
def test_name_50_should_equal_50
    name = nameFixing &quot;dgrtefdgdfshrtyutyuymbnmvcbdfgrthjghjgghvdfgrstthg&quot;
    assert_equal(name.length, 50)
  end
end
</pre><p>Do you think this will pass? Try it.</p><p>Now that you have a way to make the name the proper length, we need to continue with the other columns. Did you notice a pattern with the definitions? Alphanumeric or numeric and a certain length.</p><p>Why don&#8217;t we go ahead and tackle the length of the column. You should just be able to pass in a variable to set that. Try it.</p><pre class="brush: ruby; title: ; notranslate">
require 'test/unit'
def nameFixing(name,len)
  case
  when name.length len
    name = name.slice(0..(len-1))
  when name.length &lt; len
    name = name.ljust(len)
  else
    name
  end
end
class NameLengthTest &lt; Test::Unit::TestCase
  def test_name_short_should_equal_50
    name = nameFixing &quot;boo&quot;,50
    assert_equal(name.length, 50)
  end
def test_name_long_should_equal_50
    name = nameFixing &quot;dgrtefdgdfshrtyutyuykhjkmbnmvcbdfgrthjghjgghvdfgrstthg&quot;,50
    assert_equal(name.length, 50)
  end
def test_name_50_should_equal_50
    name = nameFixing &quot;dgrtefdgdfshrtyutyuymbnmvcbdfgrthjghjgghvdfgrstthg&quot;,50
    assert_equal(name.length, 50)
  end
end
</pre><p>Now you&#8217;re passing in the length we need to set it too. Save and rerun the test. Remember Check yourself before you wreck yourself. <em>I need to practice that too.</em></p><pre class="brush: bash; title: ; notranslate">
$ ruby ljust_test.rb
Loaded suite ljust_test
Started
...
Finished in 0.000863 seconds.
3 tests, 3 assertions, 0 failures, 0 errors, 0 skips
Test run options: --seed 30117
</pre><p>Still green. We can set the the length to whatever we want. We&#8217;ve only really worked with the alphanumeric side. For numeric we need to add white space to the left side. We haven&#8217;t checked to see if we are adding white space to the right side of the string. Why don&#8217;t you go ahead and add that to the test. I&#8217;ll wait.</p><pre class="brush: ruby; title: ; notranslate">
def test_name_should_have_white_space_on_the_right
  name = nameFixing &quot;should be 18&quot;, 18
  assert_equal(name, &quot;should be 18      &quot;)
end
</pre><p>Are your tests passing? Cool. Let&#8217;s move on to numeric columns. Pretty much the same idea but white space on the left. Here&#8217;s my quick and dirty code</p><pre class="brush: ruby; title: ; notranslate">
require 'test/unit'
def nameFixing(name,len)
  case
  when name.length len
    name = name.slice(0..(len-1))
  when name.length &lt; len
    name = name.ljust(len)
  else
    name
  end
end
def numbFixing(numb,len)
  case
  when numb.length len
    numb = numb.slice(0..(len-1))
  when numb.length &lt; len
    numb = numb.rjust(len)
  else
    numb
  end
end
class NameLengthTest &lt; Test::Unit::TestCase
  def test_name_short_should_equal_50
    name = nameFixing &quot;boo&quot;,50
    assert_equal(name.length, 50)
  end
def test_name_long_should_equal_50
    name = nameFixing &quot;dgrtefdgdfshrtyutyuykhjkmbnmvcbdfgrthjghjgghvdfgrstthg&quot;,50
    assert_equal(name.length, 50)
  end
def test_name_50_should_equal_50
    name = nameFixing &quot;dgrtefdgdfshrtyutyuymbnmvcbdfgrthjghjgghvdfgrstthg&quot;,50
    assert_equal(name.length, 50)
  end
def test_name_should_have_white_space_on_the_right
    name = nameFixing &quot;should be 18&quot;,18
    assert_equal(name, &quot;should be 18      &quot;)
  end
def test_numb_should_have_white_space_on_the_left
    numb = numbFixing &quot;123&quot;,8
    assert_equal(numb, &quot;     123&quot;)
  end
end
</pre><p>Go ahead, try it. Is it green? Excellent but we&#8217;re not very DRY.</p><h2>Time to Refactor.</h2><p>We con combine the methods and when we need to add white space we could put an if in there for alphanumeric or numeric. Let&#8217;s try that.</p><pre class="brush: ruby; title: ; notranslate">
require 'test/unit'
def allThingsFixing(data,len,type)
  case
  when data.length len
    data = data.slice(0..(len-1))
  when data.length &lt; len
    if type == &quot;A&quot;
      data = data.ljust(len)
    else
      data = data.rjust(len)
    end
  else
    data
  end
end
class NameLengthTest &lt; Test::Unit::TestCase
  def test_name_short_should_equal_50
    data = allThingsFixing &quot;boo&quot;,50,&quot;A&quot;
    assert_equal(data.length, 50)
  end
def test_name_long_should_equal_50
    data = allThingsFixing &quot;dgrtefdgdfshrtyutyuykhjkmbnmvcbdfgrthjghjgghvdfgrstthg&quot;,50,&quot;A&quot;
    assert_equal(data.length, 50)
  end
def test_name_50_should_equal_50
    data = allThingsFixing &quot;dgrtefdgdfshrtyutyuymbnmvcbdfgrthjghjgghvdfgrstthg&quot;,50,&quot;A&quot;
    assert_equal(data.length, 50)
  end
def test_name_should_have_white_space_on_the_right
    data = allThingsFixing &quot;should be 18&quot;,18,&quot;A&quot;
    assert_equal(data, &quot;should be 18      &quot;)
  end
def test_numb_should_have_white_space_on_the_left
    data = allThingsFixing &quot;123&quot;,8,&quot;X&quot;
    assert_equal(data, &quot;     123&quot;)
  end
end
</pre><p>Did you rerun the test? Did they all pass? Brilliant.</p><h2>How to Handle the Date.</h2><p>According to the definition, date needs to be <code>yyyymmdd</code> and luckily we are receiving it <code>mmddyyyy</code>. We just need to cut it up and rearrange it.</p><p>Will you be able to pass that data blindly into our method? We do know that the name of the column is &#8220;accountOpened.&#8221; To keep things moving we could pass a column name into the method. Time to write a test.</p><pre class="brush: ruby; title: ; notranslate">
def test_date_should_format_properly
  data = allThingsFixing &quot;08152003&quot;,8,&quot;9&quot;,&quot;accountOpened&quot;
  assert_equal(data, &quot;20030815&quot;)
end
</pre><p>Did you remember to add the new variable you&#8217;re passing into the method?</p><p>Here&#8217;s what I did with the method</p><pre class="brush: ruby; title: ; notranslate">
def allThingsFixing(data,len,type,column)
  case
  when data.length len
    data = data.slice(0..(len-1))
  when data.length &lt; len
    if type == &quot;A&quot;
      data = data.ljust(len)
    else
      data = data.rjust(len)
    end
  else
    data
  end
end
</pre><p>You can go ahead and run the test. It should fail.</p><pre class="brush: bash; title: ; notranslate">
$ ruby ljust_test.rb
Loaded suite ljust_test
Started
F.....
Finished in 0.001405 seconds.
1) Failure:
test_date_should_format_properly(NameLengthTest) [ljust_test.rb:52]:
&lt;&quot;08152003&quot;expected but was
&lt;&quot;20030815&quot;&gt;.
6 tests, 6 assertions, 1 failures, 0 errors, 0 skips
Test run options: --seed 25697
</pre><p>Now you just need to reformat the date. It looks like we just need to move the year from the back to the front. Now where should we put this? Since there are eight digits in the column and it calls for eight digits in the output, we&#8217;ll put it in the section of its where data.len == len</p><pre class="brush: ruby; title: ; notranslate">
def allThingsFixing(data,len,type,column)
  case
  when data.length len
    data = data.slice(0..(len-1))
  when data.length &lt; len
    if type == &quot;A&quot;
      data = data.ljust(len)
    else
      data = data.rjust(len)
    end
  else
    if column == &quot;accountOpened&quot;
      data = data.slice(4..7)+data.slice(0..3)
    else
      data
    end
  end
end
</pre><p>There seem to be a lot of if statements. That can be re-factored later. Does the test pass?</p><pre class="brush: bash; title: ; notranslate">
$ ruby ljust_test.rb
Loaded suite ljust_test
Started
......
Finished in 0.001030 seconds.
6 tests, 6 assertions, 0 failures, 0 errors, 0 skips
Test run options: --seed 48958
</pre><h3>Transmogrification Complete!</h3><p>How would you get the data into the Transmogrifier? Stay tuned!</p> ]]></content:encoded> <wfw:commentRss>http://rubysource.com/the-ruby-transmorgrifier/feed/</wfw:commentRss> <slash:comments>4</slash:comments> </item> <item><title>Preview Your Rails Mail with Letter Opener</title><link>http://rubysource.com/preview-your-rails-mail-with-letter-opener/</link> <comments>http://rubysource.com/preview-your-rails-mail-with-letter-opener/#comments</comments> <pubDate>Mon, 06 May 2013 13:30:39 +0000</pubDate> <dc:creator>Jochen Hartz</dc:creator> <category><![CDATA[Gems]]></category> <category><![CDATA[Rails Tutorials]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=5621</guid> <description><![CDATA[In this tutorial I will show you the setup and use of a email-preview gem called Letter-Opener by Ryan Bates. In case you are serious about using Rails and you do not know Ryan&#8217;s excellent Railscasts, go watch them. The gem we&#8230;]]></description> <content:encoded><![CDATA[<p><a
href="http://rubysource.com/?attachment_id=5625" rel="attachment wp-att-5625"><img
class="alignleft size-medium wp-image-5625" alt="letteropener" src="http://cdn.rubysource.com/files/2013/04/letteropener-300x224.jpg" width="300" height="224" /></a></p><p>In this tutorial I will show you the setup and use of a email-preview gem called Letter-Opener by Ryan Bates. In case you are serious about using Rails and you do not know Ryan&#8217;s excellent <a
href="http://railscasts.com">Railscasts</a>, go watch them.</p><p>The gem we are using here makes it really easy to preview your application emails the minute they are sent in development mode.</p><h2>Tutorial Details</h2><ul><li>Program: Ruby on Rails, TextMate</li><li>Gem: <a
title="letter_opener" href="https://github.com/ryanb/letter_opener">letter_opener</a></li><li>Versions: Ruby 1.8.7+, Rails 3.0.10+</li><li>Difficulty: easy</li><li>Estimated completion time: 1 hour</li></ul><h2>Introduction</h2><p>Let&#8217;s say you have an Rails application that sends out notifiction emails to a group of users. Action Mailer, which has been completly overhauled with Rails 3, will take care of sending the mails, provided you have configured your application right. But while you&#8217;re developing, you might not want to open your mailbox and wait for the delivery of your mails every time you make a change. This is especially true when your are sending out HTML emails and have lots of minor changes. What you need is the possibility to preview your emails quick and easy. That&#8217;s where Letter-Opener comes in.</p><p>This tutorial walks you through setting up a Rails application quickly, writing the notification-mailer for sending your emails, and shows you how to use Letter Opener to preview them.</p><h2>Step 1 Setting Up a new Rails application quickly.</h2><p>Just go to the console and setup a new Rails application:</p><pre class="brush: ruby; title: ; notranslate">
rails new postbox
</pre><p>We&#8217;ll call the application Postbox.</p><p><a
href="http://rubysource.com/?attachment_id=5626" rel="attachment wp-att-5626"><img
class="size-medium wp-image-5626 alignnone" alt="img1" src="http://cdn.rubysource.com/files/2013/04/img1-300x204.jpg" width="300" height="204" /></a></p><blockquote><p>&#8220;Throughout the tutorial we&#8217;ll be using RVM, the ruby version manager, to create a special gemset to not pollute any other projects.&#8221;</p></blockquote><h2>Step 2 Install Your Gems</h2><p>After you&#8217;ve created the app, just go into the <code>postbox/</code> directory and open your Gemfile in your favorite editor. We&#8217;re using TextMate here.</p><p>Add the letter_opener gem to your Gemfile like this</p><pre class="brush: ruby; title: ; notranslate">
gem &quot;letter_opener&quot;, :group =&gt; :development
</pre><p>It&#8217;s important to use the <code>:group =&gt; :development</code> as you only want to use this gem in your development environment.</p><p><a
href="http://rubysource.com/?attachment_id=5627" rel="attachment wp-att-5627"><img
class="size-medium wp-image-5627 alignnone" alt="img2" src="http://cdn.rubysource.com/files/2013/04/img2-300x283.jpg" width="300" height="283" /></a></p><blockquote><p>&#8220;Grouping your gems is highly recommended, so you do not get them mixed up throughout your environments.&#8221;</p></blockquote><p>After you&#8217;ve done that, run the <code>bundle install</code> command to install the gems. It looks something like this:</p><p><a
href="http://rubysource.com/?attachment_id=5628" rel="attachment wp-att-5628"><img
class="size-medium wp-image-5628 alignnone" alt="img3" src="http://cdn.rubysource.com/files/2013/04/img3-300x188.jpg" width="300" height="188" /></a></p><h2>Step 3 Setting Up the Delivery Method</h2><p>After installing the gem, we need to configure it so that Rails knows to use Letter Opener in our development environment. Again, that&#8217;s pretty easy. Just one line in the <code>config/environments/development.rb</code> file.</p><p>Open it up in the editor and write:</p><pre class="brush: ruby; title: ; notranslate">
config.action_mailer.delivery_method = :letter_opener
</pre><p><a
href="http://rubysource.com/?attachment_id=5629" rel="attachment wp-att-5629"><img
class="size-medium wp-image-5629 alignnone" alt="img4" src="http://cdn.rubysource.com/files/2013/04/img4-300x239.jpg" width="300" height="239" /></a></p><p>Now everything is set up and we can start writing the code for our notification emails.</p><h2>Step 4 Writing the Notification Mailer</h2><p>At the console, type the following command:</p><pre class="brush: ruby; title: ; notranslate">
rails generate mailer NotificationMailer
</pre><p>Rails will automatically create the necessary files for you.</p><p><a
href="http://rubysource.com/?attachment_id=5630" rel="attachment wp-att-5630"><img
class="size-medium wp-image-5630 alignnone" alt="img5" src="http://cdn.rubysource.com/files/2013/04/img5-300x152.jpg" width="300" height="152" /></a></p><p>Let&#8217;s step through the files one by one and see what Rails baked for us.</p><p>1 app/mailers/notification_mailer.rb<br
/> 2 app/views/notification_mailer<br
/> 3 test/functional/notification_mailer_test.rb</p><h3>notification_mailer.rb</h3><p>First file that&#8217;s been created is the notification mailer. It will take control of the details like addresses and content of the emails. Let&#8217;s have a closer look.</p><p>You&#8217;ll find the default parameter for the sender&#8217;s address.</p><p><a
href="http://rubysource.com/?attachment_id=5631" rel="attachment wp-att-5631"><img
class="size-medium wp-image-5631 alignnone" alt="img6" src="http://cdn.rubysource.com/files/2013/04/img6-300x137.jpg" width="300" height="137" /></a></p><blockquote><p>&#8220;Note that mailers always reside in the <code>mailers</code> directory .&#8221;</p></blockquote><p>First of all, change the default parameters for our application.</p><pre class="brush: ruby; title: ; notranslate">
default (:from =&gt; &quot;info@postbox.tut&quot;, :to=&gt;&quot;somelist@postbox.tut&quot;)
</pre><p>Now every email you send from this mailer will be sent from and to the default values. You can override this everytime you want to in a specific method.</p><p>Next we will write a method (notification_mailer) that will actually take care of sending the email. It takes a parameter that is a hash. First, we set two variables and then tell Rails to send out the email. The subject for the email is set in the last line.</p><pre class="brush: ruby; title: ; notranslate">
def notification_mailer(notification)
  @notification = notification
  @url  = &quot;http://postbox.tut/&quot;
  mail(:subject =&gt; &quot;New Notification&quot;)
end
</pre><p>The complete code looks like this.</p><p><a
href="http://rubysource.com/?attachment_id=5632" rel="attachment wp-att-5632"><img
class="size-medium wp-image-5632 alignnone" alt="img7" src="http://cdn.rubysource.com/files/2013/04/img7-300x129.png" width="300" height="129" /></a></p><h3>Views</h3><p>Next, let&#8217;s go to the corresponding <code>views/</code> folder, where our email templates reside. There are no templates yet, so we&#8217;re going to set them up.</p><p><a
href="http://rubysource.com/?attachment_id=5633" rel="attachment wp-att-5633"><img
class="size-medium wp-image-5633 alignnone" alt="img8" src="http://cdn.rubysource.com/files/2013/04/img8-300x130.jpg" width="300" height="130" /></a></p><p>Note that we are going to set up an HTML and a TEXT version of our emails. That way we can send multi-part-emails automatically without further effort.</p><p><a
href="http://rubysource.com/?attachment_id=5634" rel="attachment wp-att-5634"><img
class="size-medium wp-image-5634 alignnone" alt="img9" src="http://cdn.rubysource.com/files/2013/04/img9-289x300.jpg" width="289" height="300" /></a></p><blockquote><p>&#8220;ActionMailer will automatically send out multi-part emails, HTML <strong>and</strong> TEXT versions, if you create both files in the corresponding folder.&#8221;</p></blockquote><p>Ok, now that we have created the templates, let&#8217;s write some code. We want our users to have nice notifications in their inboxes. Start with writing some HTML for this version of the email. The code goes like this:</p><pre class="brush: xml; title: ; notranslate">
&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;&lt;/pre&gt;
&lt;h1&gt;Notification email.&lt;/h1&gt;
&lt;h2&gt;&lt;%= @notification.headline %&gt;&lt;/h2&gt;
&lt;pre&gt;
&lt;%= @notification.content %&gt;
This email was sent out to you, because you are subscribed to the postbox-mailing-list.
If you want to unscribscribe, please use this url: &lt;%= @url %&gt;.
</pre><p>Note that inside the h2 and the following paragraphs we paste in some content through our mailer. From our <code>@notification</code> hash we will fill in the headline and the content of the mail. The link, which can be clicked to unsubscribe the newsletter is set by the <code>@url</code> variable.</p><p>Please note, that the <code>@url</code> is set only for demo-purposes as we will not implement the functionality of unsubscribing throughout this tutorial. Let&#8217;s complete this part by writing our text-version of the email.</p><pre class="brush: plain; title: ; notranslate">
Notification email.
-------------------
&lt;%= @notification.headline %&gt;
&lt;code&gt;&lt;%= @notification.content %&gt;
This email was sent out to you, because you are subscribed to the steamengine-mailing-list.
If you want to unscribscribe, please use this url: &lt;%= @url %&gt;.
</pre><p><strong>Make sure that the method in your mailer (<code>notification_mailer</code>) and the name of your views (<code>notification_mailer.html.erb</code>) are the same. Otherwise, no email will be sent</strong></p><p>Now we are all done&#8230;almost. How do we get the hedline and content into the email? We need a notifications model in our apllication, but first let&#8217;s take a short excursion on testing.</p><h3>notification_email_test.rb</h3><p>Testing and TDD (test-driven-development) are some of the best things that Rails allows you to do. They help in so many situations, whether you have a large distributed team or code on your own. Let&#8217;s spend at some time on testing your mailer.</p><p>We&#8217;ll be creating a unit test for our postbox apllication that will reside in the <code>test/unit/</code> folder and will go by the name <code>notification_email_test.rb</code>.</p><p>You might have noticed, that we&#8217;re not using the functional test, that was created by the generator. Instead, we make up a unit test. This is because we do some basic testing here. I&#8217;ll leave it up to you to expand your testing with a functional test.</p><p><a
href="http://rubysource.com/?attachment_id=5635" rel="attachment wp-att-5635"><img
class="size-medium wp-image-5635 alignnone" alt="img11" src="http://cdn.rubysource.com/files/2013/04/img11-300x179.png" width="300" height="179" /></a></p><pre class="brush: ruby; title: ; notranslate">
require 'test_helper'
class NotificationMailerTest &lt; ActionMailer::TestCase
  tests NotificationMailer
  test &quot;notification_email&quot; do
    expected = new_mail
    expected.from    = 'info@postbox.tut'
    expected.to      = 'somelist@postbox.tut'
    expected.subject = &quot;New Notification&quot;
    expected.date    = Time.now
    actual = nil
    assert_nothing_raised { actual = NotificationMailer.notification_mailer(@recipient) }
    assert_not_nil actual
    expected.message_id = '&lt;123@456&gt;'
    actual.message_id = '&lt;123@456&gt;'
    assert_equal expected.encoded, actual.encoded
   end
end
</pre><p>Let&#8217;s have a look at the test. First, we include the helper, which we&#8217;ll discuss in a minute. Next, a new class is declared that inherits from <code>ActionMailer::Testcase</code>. We&#8217;re defining a test here called <code>notification_email</code>, where we create a new email message and give it the necessary parameters.</p><p>Next we will create the actual email by using our <code>NotifcationMailer</code>. With assertions, we will check that the email was created properly. We&#8217;ll test if anything was raised while creating the email and check that is was really created through the method and is not nil.</p><p>By assigning the same <code>message_id</code> to both of the emails, otherwise our next assertion will fail. The last step is to compare both emails with each other. Ok, so we now have three assertions in our code. Let&#8217;s check the test_helper.</p><p><a
href="http://rubysource.com/?attachment_id=5646" rel="attachment wp-att-5646"><img
class="size-medium wp-image-5646 alignnone" alt="img12" src="http://cdn.rubysource.com/files/2013/04/img12-300x183.jpg" width="300" height="183" /></a></p><pre class="brush: ruby; title: ; notranslate">
def new_mail( charset=&quot;UTF-8&quot; )
  mail = Mail.new
  mail.mime_version = &quot;1.0&quot;
  if charset
    mail.content_type [&quot;text&quot;, &quot;plain&quot;, { &quot;charset&quot; =&gt; charset }]
  end
  mail
end
</pre><p>We created a method called <code>new_mail</code> that sets up a mail everytime we need it. The method accepts a parameter to set the charset of the email, in case you want to change it. If not, it will always fall back to the default.</p><p>We could have done it in the test itself, but it&#8217;s these kinds of methods you should put into the test_helper, so you can easily change them on a global level. Now it is time to run the test, using <code>rake</code>.</p><p><a
href="http://rubysource.com/?attachment_id=5648" rel="attachment wp-att-5648"><img
class="size-medium wp-image-5648 alignnone" alt="img13" src="http://cdn.rubysource.com/files/2013/04/img13-300x167.jpg" width="300" height="167" /></a></p><p>Ok, obviously we&#8217;ve done everything right because the output of our test is</p><pre class="brush: bash; title: ; notranslate">1 tests, 3 assertions, 0 failures, 0 errors</pre><p>Go ahead and play around with the test. Use another sender-address for instance and see what happens.</p><blockquote><p>&#8220;Testing is one of Rails most important features. Use it extensively while you write your code. That&#8217;ll help you to save a lot of time while your code is growing and the complexity of the application is rising!&#8221;</p></blockquote><h2>Step 5 The Notification Model and Controller</h2><p>Now that we have everything in place to send out the email (including tests!), we&#8217;ll setup a nice little interface to write the actual notification and use the mailer to send them.</p><p>For the sake of this tutorial, we will use Rails scaffolding. Remember, we had headline and content in our views, so we will implement them here.</p><pre class="brush: ruby; title: ; notranslate">
rails generate scaffold Notification headline:string content:text
</pre><p>Rails now creates everything we need: the migration for the database, the model, the controller, and the views.</p><p><a
href="http://rubysource.com/?attachment_id=5649" rel="attachment wp-att-5649"><img
class="size-medium wp-image-5649 alignnone" alt="img14" src="http://cdn.rubysource.com/files/2013/04/img14-300x193.jpg" width="300" height="193" /></a></p><p>Let&#8217;s start up the server:</p><pre class="brush: ruby; title: ; notranslate">
rails server
</pre><p>Do not forget to migrate the database by using <code>rake db:migrate</code> This will set up the table for us.</p><p><a
href="http://rubysource.com/?attachment_id=5650" rel="attachment wp-att-5650"><img
class="size-medium wp-image-5650 alignnone" alt="img15" src="http://cdn.rubysource.com/files/2013/04/img15-300x190.jpg" width="300" height="190" /></a></p><p><a
href="http://rubysource.com/?attachment_id=5651" rel="attachment wp-att-5651"><img
class="size-medium wp-image-5651 alignnone" alt="img16" src="http://cdn.rubysource.com/files/2013/04/img16-300x165.jpg" width="300" height="165" /></a></p><p>Next, fire up the browser and point to <a
href="http://localhost:3000/notifications">http://localhost:3000/notifications</a>. It should look something like this:</p><p><a
href="http://rubysource.com/?attachment_id=5640" rel="attachment wp-att-5640"><img
class="size-medium wp-image-5640 alignnone" alt="img17" src="http://cdn.rubysource.com/files/2013/04/img17-300x278.jpg" width="300" height="278" /></a></p><p>Now we navigate to the new notifications page and create a new one by filling out the form.</p><p><a
href="http://rubysource.com/?attachment_id=5641" rel="attachment wp-att-5641"><img
class="size-medium wp-image-5641 alignnone" alt="img18" src="http://cdn.rubysource.com/files/2013/04/img18-300x277.jpg" width="300" height="277" /></a></p><p>Success will look something like this. Of course, you should write tests to be sure that everything works fine. I leave it up to you. ;-)</p><p><a
href="http://rubysource.com/?attachment_id=5642" rel="attachment wp-att-5642"><img
class="size-medium wp-image-5642 alignnone" alt="img19" src="http://cdn.rubysource.com/files/2013/04/img19-300x285.jpg" width="300" height="285" /></a></p><p>One last thing before we can send the email and see the magic of letter_opener. Hopefully, you still remember that we wanted to find out how to use this gem. We now need to send the email, so we head over to the controller and modify the code.</p><p><a
href="http://rubysource.com/?attachment_id=5643" rel="attachment wp-att-5643"><img
class="size-medium wp-image-5643 alignnone" alt="img20" src="http://cdn.rubysource.com/files/2013/04/img20-300x178.jpg" width="300" height="178" /></a></p><p>In the <code>create method</code> we will add one line of code that tells ActionMailer to send out our notification if it was saved succesfully.</p><pre class="brush: ruby; title: ; notranslate">
def create
  @notification = Notification.new(params[:notification])
  respond_to do |format|
    if @notification.save
      **NotificationMailer.notification_mailer(@notification).deliver**
      format.html { redirect_to(@notification, :notice =&gt; 'Notification was successfully created.') }
      format.xml  { render :xml =&gt; @notification, :status =&gt; :created, :location =&gt; @notification }
    else
      format.html { render :action =&gt; &quot;new&quot; }
      format.xml  { render :xml =&gt; @notification.errors, :status =&gt; :unprocessable_entity }
    end
  end
end
</pre><p><a
href="http://rubysource.com/?attachment_id=5644" rel="attachment wp-att-5644"><img
class="size-medium wp-image-5644 alignnone" alt="img21" src="http://cdn.rubysource.com/files/2013/04/img21-300x119.png" width="300" height="119" /></a></p><p>After that, create another notification through the web interface. If we&#8217;ve done everything right, another tab in our browser should show up and we can preview the email we just created! Voila!</p><p><a
href="http://rubysource.com/?attachment_id=5645" rel="attachment wp-att-5645"><img
class="size-medium wp-image-5645 alignnone" alt="img22" src="http://cdn.rubysource.com/files/2013/04/img22-300x109.png" width="300" height="109" /></a></p><p>As you can see, you have a great way to preview emails from your application. Now you can go and modify them. As extra credit, try to find out where on your disk the previews are stored. I&#8217;m giving you a hint, look in the <code>tmp/</code> directory or in the address bar of your browser.</p><p>I hope you enjoyed learning about letter_opener.  The rails app I used for this article can be found <a
href="https://github.com/RubySource/postbox" target="_blank">here</a>.</p> ]]></content:encoded> <wfw:commentRss>http://rubysource.com/preview-your-rails-mail-with-letter-opener/feed/</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>App Search with Thinking Sphinx 3.0</title><link>http://rubysource.com/app-search-with-thinking-sphinx-3-0/</link> <comments>http://rubysource.com/app-search-with-thinking-sphinx-3-0/#comments</comments> <pubDate>Thu, 02 May 2013 13:30:12 +0000</pubDate> <dc:creator>sbhatia</dc:creator> <category><![CDATA[Gems]]></category> <category><![CDATA[Rails Tutorials]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=5615</guid> <description><![CDATA[Thinking Sphinx is now a very standard library for interfacing with Sphinx and has come a long way in its implementation of various features of Sphinx. The new version, 3.0, is a major rewrite and quite a departure especially in terms of&#8230;]]></description> <content:encoded><![CDATA[<p><a
href="http://rubysource.com/app-search-with-thinking-sphinx-3-0/capture-6/" rel="attachment wp-att-5616"><img
class="alignleft size-full wp-image-5616" alt="Capture" src="http://cdn.rubysource.com/files/2013/04/Capture.jpg" width="298" height="114" /></a> Thinking Sphinx is now a very standard library for interfacing with <a
href="http://sphinxsearch.com"> Sphinx </a> and has come a long way in its implementation of various features of Sphinx. The new version, 3.0, is a major rewrite and quite a departure especially in terms of setup. Also, it includes fairly advanced facet searching built into it. Lastly, this version is compatible only with Rails 3.1 or above.</p><h3>Installation</h3><p>Sphinx installation on Linux is pretty straightforward. Downloading and compiling from source is a preferred method of Sphinx installation, despite the fact that apt and yum repositories are almost up-to-date.</p><p>The newer versions of Sphinx has multi-query support with mysql, and hence it&#8217;s better to compile it with mysql development headers included.</p><pre class="brush: bash; title: ; notranslate">
$ wget http://sphinxsearch.com/files/sphinx-2.0.7-release.tar.gz
$ tar xvzf sphinx-2.0.7-release.tar.gz
$ cd sphinx-2.0.7-release/
$ ./configure --with-mysql
$ make
$ sudo make install
</pre><p>Once Sphinx is installed successfully, you can install Thinking Sphinx as a gem or bundle it in your application.</p><pre class="brush: bash; title: ; notranslate">
$ gem install thinking-sphinx -v &quot;~&gt; 3.0.2&quot;
</pre><p>If mysql is your database of choice, you will have to make sure the mysql2 gem version is either locked at 0.3.12b4 or 0.3.12b5. The earlier versions of mysql2 gem will throw the following exception :</p><pre class="brush: ruby; title: ; notranslate">
undefined method `next_result' for Mysql2
</pre><p>The error occurs because the previous versions of the mysql2 gem did not have multiquery support, and Sphinx now has that out-of-the-box.</p><h3>Configuration</h3><p>Thinking Sphinx has two main configuration files. In the earlier versions, the first file was named sphinx.yml, but has been renamed to thinking_sphinx.yml.</p><p>The second file is .sphinx.conf (e.g development.sphinx.conf) and is used to interface Sphinx with the database. Whenever a fresh index is created, a fresh conf file is created which lists all the Sphinx queries based on the defined indices and connection details in database.yml.</p><p>thinking_sphinx.yml is an extension of this file where you can pass extra options to improve your search results.</p><h3>Use Case</h3><p>Let&#8217;s say we have an application with the following models :</p><ul><li>Vendor &#8211; name, rating</li><li>Shop &#8211; name, description, vendor_id, location</li><li>Product &#8211; name, sku, description, price, shop_id</li></ul><p>with the following model associations</p><ul><li>Vendor &#8211; has_many shops</li><li>Shop &#8211; belongs<em>to vendor, has</em>many products</li><li>Product &#8211; belongs_to shop</li></ul><p>We have the following use cases which we will cover in our article:</p><ul><li>Search Vendor, Shop, Product individually</li><li>Search across all models</li><li>Search via asociation</li><li>Define Facets to Create filters</li></ul><h3>Basics &#8211; Definition of Indices.</h3><p>Thinking Sphinx 3.0 takes a cleaner approach to the definition of indices compared to earlier versions.</p><p>To start defining an index, you first need to create a directory named &#8216;indices&#8217; under the app folder.</p><pre class="brush: bash; title: ; notranslate">
$ mkdir indices
</pre><p>Let&#8217;s follow our use case and create three index files in our indices folder:</p><ul><li>vendor_index.rb</li><li>shop_index.rb</li><li>product_index.rb</li></ul><p>The index file definition includes the name of class on which need to search. It should be the same as the model class name.</p><p>The indices for attributes can be defined simply by writing :</p><pre class="brush: ruby; title: ; notranslate">
indexes column_name
</pre><p>However, there are a few <a
href="http://sphinxsearch.com/docs/2.1.1/sphinxql-reserved-keywords.html">reserved keywords</a> for Sphinx (e.g status). To make these columns acceptable by Sphinx, you would have to define them as a symbol.</p><pre class="brush: ruby; title: ; notranslate">
indexes :status
</pre><p>According to our use case, let&#8217;s write our first index for the vendor model. This will index the vendor name and rating. We will use rating as a parameter to sort our search results for the vendor.</p><pre class="brush: ruby; title: ; notranslate">
ThinkingSphinx::Index.define :vendor, :with =&gt; :active_record do
  indexes name
  indexes rating, :sortable =&gt; true
end
</pre><p>Now, while searching for products and stores, we need vendor name to be a common criteria for each search. So, if we need to search for a product called &#8220;Batman Action Figure&#8221; in the vendor &#8220;Marvel Toys&#8221;, we could search for it using the vendor name and find all the products related to it. Here is how it is done:</p><pre class="brush: ruby; title: ; notranslate">
ThinkingSphinx::Index.define :vendor, :with =&gt; :active_record do
  indexes name, :as =&gt; vendor_name
  indexes rating, :sortable =&gt; true
end
</pre><p>Inside our other indexes, the associations are defined as:</p><pre class="brush: ruby; title: ; notranslate">
shop_index.rb
ThinkingSphinx::Index.define :shop, :with =&gt; :active_record do
  indexes name, description, location
  has vendor(:name), :as =&gt; :vendor_name
end
</pre><p>&nbsp;</p><pre class="brush: ruby; title: ; notranslate">
product_index.rb
ThinkingSphinx::Index.define :product, :with =&gt; :active_record do
  indexes name, description
  indexes price, :sortable =&gt; true
  has shop.vendor.name, :as =&gt; :vendor_name
end
</pre><p>In both the files above, we have called <code>vendor_name</code> using associations. Shop belongs to a vendor, so we could make a direct call from vendor and call it <code>vendor_name</code>, whereas in product, we called it via the <code>shop</code> association.</p><h3>Running and Generating the Index</h3><p>Once, we&#8217;re done with writing the indexes in our files, we would need to generate an index and run our Sphinx server.</p><pre class="brush: bash; title: ; notranslate">
$ rake ts:index
$ rake ts:start
</pre><h3>Searching</h3><p>Once our Thinking Sphinx is up and running, we can write controller methods to search and display the results.</p><p>Running search on individual models looks like this:</p><pre class="brush: ruby; title: ; notranslate">
@search_products = Product.search(params[:search], :ranker =&gt; :proximity, :match_mode =&gt; :any)
</pre><p>If you want to create an application wide search, you can call the <code>ThinkingSphinx.search</code> method to define models to be searched. You can tie this to any route and pass the search term as a parameter.</p><pre class="brush: ruby; title: ; notranslate">
def search
   @search =  ThinkingSphinx.search(params[:search],
                             :classes =&gt; [ Vendor, Store, Product],
                             :ranker =&gt; :bm25,
                             :match_mode =&gt; :any,
                             :order =&gt; '@weight DESC',
                             :page =&gt; params[:page],
                             :per_page =&gt; 10)
end
</pre><p>Ranking uses different algorithms like <a
href="http://xapian.org/docs/bm25.html">bm25</a> and proximity for generating best matches within the search results.</p><p>Sorting uses an order field and can be defined to sort the results in different ways. Based on rating, alphabetical, or created_at are just some of the ways it can be sorted.</p><p>@weight is a default keyword that contains Sphinx ranking value.</p><h3>Other Stuff</h3><p>Field Weights :<br
/> We can rank our search results according to different fields inside a particular model:</p><pre class="brush: ruby; title: ; notranslate">
@search_products = Product.search(params[:search],
                                 :ranker =&gt; :proximity,
                                 :match_mode =&gt; :any,
                                 :field_weights =&gt; {:description =&gt; :15,
                                                    :name =&gt; 10})
</pre><p>You can also write field weights inside your index files using the <code>set_property</code> rule like this:</p><pre class="brush: ruby; title: ; notranslate">
:set_property :field_weights =&gt; {:description =&gt; 15,
                                 :name =&gt; 10})
</pre><p>Getting excerpts from the search results:</p><pre class="brush: ruby; title: ; notranslate">
@excerpter = ThinkingSphinx::Excerpter.new 'product_core', params[:search], { :before_match =&gt; '&lt;span class=&quot;match&quot;&gt;',
 :after_match =&gt; '&lt;/span&gt;',
 :chunkseparator =&gt; ' … '}
 </pre><p>Adding Facets :</p><p>Facet-based search is used when you need to filter according to different classes or parameters. Facet definition can be done by simply defining a <code>facet =&gt; true</code> rule against the attribute to facet in the index file.</p><pre class="brush: ruby; title: ; notranslate">
indexes name, :facet =&gt; true
</pre><p>This and then just rebuild the index.</p><pre class="brush: bash; title: ; notranslate">
rake ts:rebuild
</pre><p>In your controller, <code>facet</code> accepts the same parameters as <code>search</code>, so your facet would look like this :</p><pre class="brush: ruby; title: ; notranslate">
 @facets = ThinkingSphinx.facets(params[:search],
                                :class_facet =&gt; false,
                                :page =&gt; params[:page],
                                :per_page =&gt; 10)
</pre><p>If no facets are defined on any model and a facet search is written, it would fall back to class facets, calling the search on individual model classes. You can turn it off by calling false inside your facet rule, as shown above.</p><p>You can display the facet results in your view in the following way:</p><pre class="brush: ruby; title: ; notranslate">
&amp;lt;% @facets[:class].each do |option, count|%&gt;
&amp;lt;%= link_to &quot;#{option} (#{count})&quot;, :params =&gt; { :facet =&gt; option, :page =&gt; 1}%&gt;
&amp;lt;%end%&gt;&lt;br/&gt;
</pre><p>Searching for partial words can be done using <code>min_infix_len</code> inside your thinking_sphinx.yml or inside your index file using <code>set_property</code>. This means it will match at the least 3 characters before declaring it a match. <code>min_infix_len</code> is not advisable to be kept at 1, as it could be a serious memory hog.</p><pre class="brush: ruby; title: ; notranslate">
 development:
   mem_limit: 128M
   min_infix_len: 3
 test:
   mem_limit: 128M
   min_infix&lt;/em&gt;len: 3
 production:
   mem_limit: 128M
   min_infix_len: 3
 </pre><h2>Delta Indexes</h2><p>Sphinx, by default, runs indexes from scratch everytime you run and index command. In order to avoid Sphinx starting from scratch, we can define delta indexes. This will only index the documents that are newly created.</p><pre class="brush: ruby; title: ; notranslate">
  set_property :delta =&gt; true
</pre><h2>Conclusion</h2><p>Thinking Sphinx 3.0 brings along a lot of advancements and bug fixes from previous versions. It takes a very clean approach, placing the search code outside your app models. Therefore, as queries start getting complex, the code remains readable.</p><p>Hopefully, this article has inspired you to give Thinking Sphinx a try in your application.</p> ]]></content:encoded> <wfw:commentRss>http://rubysource.com/app-search-with-thinking-sphinx-3-0/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Rankings: You&#8217;re Doing It Wrong!</title><link>http://rubysource.com/rankings-youre-doing-it-wrong/</link> <comments>http://rubysource.com/rankings-youre-doing-it-wrong/#comments</comments> <pubDate>Mon, 29 Apr 2013 13:30:30 +0000</pubDate> <dc:creator>João M. D. Moura</dc:creator> <category><![CDATA[Misc]]></category> <category><![CDATA[Ruby Tutorials]]></category> <category><![CDATA[gamification]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=5611</guid> <description><![CDATA[We have seen a lot of rankings in our lives, not only in WebApps but also in different media. Often the ranking is part of a gamification feature. But, is it being really effective? In fact, the majority of ranking implementations are&#8230;]]></description> <content:encoded><![CDATA[<p>We have seen a lot of rankings in our lives, not only in WebApps but also in different media. Often the ranking is part of a gamification feature.</p><p>But, is it being really effective? In fact, the majority of ranking implementations are wrong, and can even make your application less attractive. It’s difficult or downright impossible to reach the top spots and you may never find a real challenge against other users. Those are just some of the problems when talking about Rankings.</p><p>Many big compaines, such as Sony, Microsoft, and Nintendo, have experimented with algorithms to make rankings more dynamic and attractive. Lots of great implementations have surfaced from those companies, and we are able to use them in our development processes.</p><h2>Rankings</h2><h3>What is the Proposal?</h3><p>The mathematical description of a ranking is quite simple, as you can see at <a
href="http://en.wikipedia.org/wiki/Ranking">Wikipedia</a></p><blockquote><p>&#8220;A ranking is a relationship between a set of items such that, for any two items, the first is either &#8216;ranked higher than&#8217;, &#8216;ranked lower than&#8217; or &#8216;ranked equal to&#8217; the second. In mathematics, this is known as a weak order or total preorder of objects.&#8221;</p></blockquote><p>A ranking by itself is quite easy to implement, basically sorting a number of items according to some criteria, but what is the purpose of a ranking?</p><p>A ranking is not only an opaque information, it&#8217;s being used on games and applications to engage users to improve themselves and achieve higher positions, usually competing against each other.</p><p>What often happens is the early adopters will reach the top of the ranking really fast, and improve so much that it will be too difficult or even impossible for new users to surpass them or even try to challenge someone with the same skill level.</p><p>So if you were trying to make your application more attractive with rankings, chances are that you have failed! <strong>No one wants to enter in a competition where it is impossible to actually compete.</strong></p><p>But how can we make rankings something more interesting, to transform this old social technique into a powerful tool to <strong>keep users engaged</strong>? That is our goal and the real purpose of a Ranking implementation.</p><h3>Making Rankings Effective</h3><p>Think about a ranking as an piece of data. Data is essentially good and when you have it, you have power. It isn&#8217;t just about sorting items, <strong>it&#8217;s about building an application that fits every level of each user’s needs.</strong></p><p>The idea is that, if you have a ranking, you have a resource.</p><p>For example, when ranking <strong>users</strong>, the parameters can change as the user progresses. Those changes can be the interactions between each other, but the condition to change those parameters and get a higher or lower ranking must be compatible with the resource skills and current position.</p><p>It&#8217;s quite simple and when I thought about the essence of it I came up with the following short list that defines the purpose of a ranking:</p><ul><li>Organize and order a resource.</li><li>Make it possible to change positions.</li><li>Find challenges/conditions that are compatible with the user&#8217;s current rank<br
/> and skills.</li></ul><p>It certainly doesn&#8217;t sound too difficult, but when it comes to technique, it is. But don&#8217;t worry; because big companies are working on this for us.</p><p>The best solution that I&#8217;ve seen so far is the one that <a
href="http://research.microsoft.com/en-us/">Microsoft Research team</a> researched and developed, known as <a
href="http://research.microsoft.com/en-us/projects/trueskill/">True Skill</a></p><h2>True Skill</h2><p>Microsoft has published a lot of research related to the new business areas that it has ventured into in the past few years, such as the game console market. The Xbox is one of the most incredible devices on the market right now and its updates and features are always bringing innovation. One of those features is the <a
href="http://research.microsoft.com/pubs/67956/NIPS2006_0688.pdf">True Skill ranking system</a>.</p><blockquote><p>&#8220;TrueSkill is a Bayesian ranking algorithm developed by Microsoft Research and used in the Xbox matchmaking system built to address some perceived flaws in the Elo rating system. It is an extension of the Glicko rating system to multiplayer games&#8221;</p></blockquote><p>TrueSkill was developed based on the <a
href="http://en.wikipedia.org/wiki/Elo_rating_system">Elo Rating system</a>, one of the most well known algorithms, that is used in a lot of different ranking tools; it was the initial implementation of statistical estimation on rankings.</p><p>It matches the short list that I mention above. It not only sorts users, but it also make it dynamic, providing challenges that fits the skills of every user.</p><h3>How it Works?</h3><p>The basic idea and a basic implementation of True Skill is that every resource has two variables: One that represents the perceived resource skills, that we will reference by using the letter <strong>S</strong>, and another one that represents how much &#8216;confidence&#8217; the system has in the <strong>S</strong> value, that we will reference by using the letter <strong>C</strong>.</p><p>A default value for <strong>S</strong> and <strong>C</strong> have to be defined in order for new resources to have an initial position in the ranking. In Xbox Live, the default values are <strong>S = 25</strong> and <strong>C = 25/3</strong>. The <strong>S</strong> value will increase or decrease after every win or loss, but what will make the difference in the resource position is the value of <strong>C</strong> and how &#8220;surprising&#8221; the outcome of the challenge is to the system. So, if the outcome is actually the expected result (the favorite player wins, for example), it will not produce a huge increase in the user&#8217;s position.</p><p>A Resource rating is estimated by using the following formula <strong>R = S &#8211; 3 * C</strong>. The True Skill system can be applied with any scale, in Xbox Live it&#8217;s using a 0 to 50 scale.</p><p>This is just the beginning of the concepts behind True Skill. There are a lot of details when thinking about all possible results. A draw, for example, or a competition between various players at the same time.</p><p>There are some peculiar results that are quite interesting when using True Skill, for example an initial loss can actually make a user achieve a higher position on the ranking, because the <strong>C</strong> may have a bigger loss then the <strong>S</strong> variable. Let&#8217;s see an example:</p><h4>Example</h4><p>Imagine a new user, <strong>&#8220;John&#8221;</strong>, on Xbox Live. We can assume the <strong>S = 25</strong> and <strong>C = 25/3</strong>, so John&#8217;s rank would be calculated as <strong>R = 25 &#8211; 3 * 25/3</strong>, resulting in a rating <strong>0</strong>.</p><p>In the first challenge, the system is expecting that John will win, but he actually loses. His skill level will decrease by <strong>5</strong>, resulting in <strong>S=20</strong>. It was not the result expected by the system, so the system doesn&#8217;t have much confidence in this new skill. This will result in a bigger loss of the <strong>C</strong> variable, for example <strong>C=15/3</strong>.</p><p>The thing is that John&#8217;s rating result will be higher then the initial one. Losing his first challenge results in <strong>R = 20 &#8211; 15 = 5</strong>, because the system does not believe that the loss represents John&#8217;s correct skills measure. However, if John keeps losing, the confidence of a smaller skill will increase, resulting in a huge rating loss.</p><h3>What and How?</h3><p>Rankings are not only about competition between users, as you can measure anything that you want. Having a good ranking will help you to find out what better fits any position.</p><p>You can rank your users and, depending on their spot, offer different features, interfaces, and &#8216;customizations&#8217; that better fit their skills and how they interact with your application. The rule is simple, if you can rank something, you can make it more personal to everyone.</p><p>The whole True Skill implementation is not available on the web, it&#8217;s <a
href="http://www.google.com/patents/US20090227313?dq=trueskill&amp;ei=C7G2ULrGLMrFmQWP4IHoCw">patented</a> by Microsoft and can only be used if a license is acquired. But derivations of it, such as <a
href="http://en.wikipedia.org/wiki/Glicko_rating_system">Glicko rating system</a> are free to use. Unfortunately, there is just one implementation of <a
href="https://github.com/saulabs/trueskill">True Skill system in Ruby</a> that I&#8217;ve found on the web and it&#8217;s not completed yet. But, you can find a lot of Glicko and Elo implementations in other languages.</p><p>Right now, I&#8217;m implementing a derivation of the True Skill system in <a
href="http://rubysource.com/gioco-the-gamification-gem/">Gioco</a>. I&#8217;ve opened an <a
href="https://github.com/joaomdmoura/gioco/issues/44">issue</a> on github to add this feature in the next release, so it will be easier to have a correct and good ranking implementation in your application that will help you to engage your users. Stay tuned!</p> ]]></content:encoded> <wfw:commentRss>http://rubysource.com/rankings-youre-doing-it-wrong/feed/</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>Editors for Rubyists</title><link>http://rubysource.com/editors-for-rubyists/</link> <comments>http://rubysource.com/editors-for-rubyists/#comments</comments> <pubDate>Thu, 25 Apr 2013 13:30:15 +0000</pubDate> <dc:creator>Dhaivat Pandya</dc:creator> <category><![CDATA[Misc]]></category> <category><![CDATA[Opinion]]></category> <category><![CDATA[editors]]></category> <category><![CDATA[emacs]]></category> <category><![CDATA[rubymine]]></category> <category><![CDATA[sublime text]]></category> <category><![CDATA[vim]]></category> <guid
isPermaLink="false">http://rubysource.com/?p=5599</guid> <description><![CDATA[For any growing language, there&#8217;s always a ton of tools you can use, as well as very strong opinions about them. Ruby is no exception. There&#8217;s several editors, IDEs, etc. that all have excellent Ruby support aimed at keeping you operating at&#8230;]]></description> <content:encoded><![CDATA[<p><a
href="http://rubysource.com/?attachment_id=5600" rel="attachment wp-att-5600"><img
class="alignleft size-medium wp-image-5600" alt="editors" src="http://cdn.rubysource.com/files/2013/04/editors-300x300.png" width="300" height="300" /></a>For any growing language, there&#8217;s always a ton of tools you can use, as well as very strong opinions about them. Ruby is no exception.</p><p>There&#8217;s several editors, IDEs, etc. that all have excellent Ruby support aimed at keeping you operating at maximum &#8220;output&#8221; (I&#8217;m not one of those crazy managers that measures this output with lines of code, so, you get to choose how to define it). In this article, I&#8217;ll go over the advantages, disadvantages and odd points about each of them, from my perspective (read: the article is biased, but, my opinions are correct so it doesn&#8217;t matter :P).</p><p>Let&#8217;s open with a classic.</p><h2>Vi(m)</h2><p>Vi came to be in the days when bandwidth was small and people were patient with their computers. It ran inside your terminal/console, just nibbling on your memory and processor. The featureset was meant to be limited &#8211; it accomplished a few tasks, but it accomplished them pretty darn well. However, the limited featureset did not mean that it constrained you. With its simple and composable commands, vi became a favorite for those who cherished simplicity but needed flexibility and power.</p><p>Vi&#8217;s development gradually fell apart, as various people moved around in the project. But, fear not, because <a
href="http://vim.org/">Vim</a> was invented! Vim is a modified version of vi that is meant to offer a larger featureset while still remaining true to the vi vision of simplicity and respect of the power user.</p><p>So, what has vim got do with Ruby? Well, here&#8217;s where it gets a bit complicated.<br
/> Vim itself doesn&#8217;t really have any features <em>specific</em> to Ruby (aside from syntax highlighting), but Rails/Ruby hackers have always placed the console above a GUI in almost all of their decisions as a community. Vim fills that specification perfectly. In addition, vim has a number of extensions (NerdTree, ctrl+p, vim-rails etc.) that make working with Rails/Ruby applications pleasant.</p><p>There&#8217;s also nothing quite like watching someone experienced with vi(m) work &#8211; fingers flying across the keyboard, where seemingly half the characters typed seem to be inserted by vim itself. For those that do prefer a regular GUI for an editor, vim also include GVim, which is a version of vim using GTK (an interface toolkit).</p><p>But, vim is not all fun and games. Vim is a modal editor, which means that you can&#8217;t just go ahead and start typing and expect to be comfortable &#8211; there&#8217;s a pretty steep learning curve. There are three different modes: insert, visual and normal. Insert is one where you can type, visual is where you do things like select bits of text and the &#8220;normal&#8221; mode is where the keystrokes comprise vim commands. Getting used to this sort of arrangement can be really annoying, especially if you&#8217;re someone that has worked with IDEs and expect Ctrl+C to copy and Ctrl+V to paste (more on that later). One of the most annoying things about the whole modal arrangement (for me) is changing between modes. To do that, you have to hit the escape key. When you first hear of the concept, modes seem like something seldom used. The reality is, you change between modes all the time when using vim, and you have to move across your finger to the escape key EVERY SINGLE TIME. This is especially a problem for me since I have incredibly small hands; might not be an issue for some of you.</p><p>Then, there&#8217;s the command syntax. You get used to it pretty quickly, but it&#8217;s mostly devoid of &#8220;Ctrl&#8221;s and &#8220;Alt&#8221;s, using largely the alphabet (that&#8217;s why there&#8217;s the normal mode). Copy and paste seems incredibly complicated when you first start out. There are terms like &#8220;buffers&#8221; and &#8220;registers&#8221;, which you thought you&#8217;d never come across when trying to figure out how to use a text editor (of all things).</p><p>All in all, vi(m) is awesome if you can adjust yourself to it. Doing that is going to take at least some effort as well as a degree of determination to force yourself to keep using it (a bit like learning touch typing &#8211; there&#8217;s not much point unless you do it all the time). Additionally, if you&#8217;re coming from an IDE, don&#8217;t expect some IDE-like features such as it underlining all the errors for you &#8211; vim isn&#8217;t meant to do that (remember, simplicity is key!), so don&#8217;t expect features specific to e.g. Rails.</p><h2>Emacs</h2><p>Of course, now that we have talked about vim, so it is absolutely necessary to talk about Emacs.</p><p>I haven&#8217;t used Emacs all that much, but it does have some pretty nice Ruby support from the community. I&#8217;ve heard of a few who are quite happy with <a
href="http://rinari.rubyforge.org/">Rinari</a>, which is an emacs mode for Rails in particular. There are many extensions that make Emacs more suited to Ruby development, such as test-runner, which is meant to be used with RSpec, Test::Unit, etc.</p><p>From what I&#8217;ve used of it, there seems to be far less support for Rails/Ruby (i.e. fewer people using it) than with Vim or Sublime Text 2 &#8211; this might be a problem if you run into trouble setting stuff up. Personally speaking, my tiny paws can&#8217;t keep up with the Ctrl and Alt shortcuts of Emacs, so I&#8217;ve stayed fairly clear of it. But, if you&#8217;ve used Emacs for other projects, there&#8217;s no harm in giving it a whirl for Ruby.</p><p>If you&#8217;re starting afresh, try both Vim and Emacs without installing any extensions, see what you like better. Once you&#8217;ve chosen one, write several blog posts insulting those who use the employ the other editor &#8211; it seems like a &#8220;coming of age&#8221; ritual, judging from the sheer quantity of such posts.</p><h2>Sublime Text 2</h2><p>This is one that I&#8217;m pretty excited about, especially because it is a nice GUI editor that doesn&#8217;t have so much stuff piled on it that it starts looking like Eclipse (<em>shudder</em> that brings back memories from Java). It&#8217;s comprised of a perfect middle ground between something like vim and a full-blown IDE.</p><p>I often find (especially when working with larger projects) vim starts to feel a little bit inadequate in terms of just coping with so many files. Switching between NerdTree (a file explorer) and the editing window feels tiresome, and typing in filenames into the &#8220;ctrl+p&#8221; extension even more so. This is where Sublime Text comes in. It is a GUI kind of deal, so if you need to work over SSH or something (I don&#8217;t know, maybe your company thinks Git and Puppet are for heretics) that might not work out.</p><p>First thing you notice is the clean and simple design &#8211; its just the editor and a file tree. I find this to be incredibly helpful in comparision to the Eclipse way of doing things where every thing under the sun that has anything to do with Java has a place somewhere near the editor. You might notice a small box at the right side of the editor &#8211; this the marvelous mini-map. It lets you know where in the source code you are (opening up a longer file will make this clear.) This is incredibly helpful when you&#8217;re moving up and down the source code hunting for that one variable that keeps setting itself to &#8220;false&#8221;.</p><p>ST also has a sensible and human-operable (without much effort) tab scheme that just works without getting in your way, unlike vim. ST doesn&#8217;t try to hide you from the &#8220;horrors&#8221; of the Terminal application (unlike most IDEs) &#8211; it simply &#8220;coexists&#8221;.</p><p>There&#8217;s a few downsides. First of all, if you are a hardcore vim user already, getting used to the new shortcuts is a little bit annoying. Secondly, if you are looking for an IDE, keep reading, because this isn&#8217;t one. Also, after you complete your evaluation of the product, you have to dish out a bit of cash to keep using it (although there&#8217;s no real time limit for evaluation).</p><p>Aside from that, I&#8217;m a really big fan of Sublime Text 2 and I like the direction that the project has taken.</p><h2>RubyMine</h2><p>This is the one I&#8217;ve had least experience with, but I like what I see. RubyMine is a complete IDE meant for Ruby and Rails. Filled to the brim with code completion, Javascript/Coffeescript support, git integration and a pretty cool GUI test runner. I&#8217;ve typically moved away from IDEs in general, but, RubyMine is really quite interesting. It doesn&#8217;t seem to suffer from the same issues as Eclipse and Netbeans (just too much stuff one screen) and has pretty awesome support.</p><p>But, of course, it is an IDE. That means that, if you had rather work in the console than anything else, you might not like it. It is also not the most customizable IDE in the world (as far as I&#8217;ve found), so if you want all the Eclipse plugin magic, it might be a disappointment.</p><p>Regardless, I think it is a great option and if you&#8217;re willing to let loose a little moo-lah, it might just work out.</p><h2>Wrapping it up</h2><p>Hopefully, you enjoyed my summary of some of the tools that Rubyists use to get stuff done. The easiest way to develop an opinion about one of these is to pick one, get thoroughly acquianted with it, and then try to change it up. If the change feels good, you might have found a better option. If it doesn&#8217;t, just roll it back! For extra points, write blog posts making fun of editors you don&#8217;t like and the people that use them.</p> ]]></content:encoded> <wfw:commentRss>http://rubysource.com/editors-for-rubyists/feed/</wfw:commentRss> <slash:comments>12</slash:comments> </item> </channel> </rss>
<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Page Caching using memcached
Database Caching 14/34 queries in 0.029 seconds using memcached
Object Caching 952/1029 objects using memcached
Content Delivery Network via cdn.rubysource.com

Served from: rubysource.com @ 2013-05-23 06:37:38 -->