<?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/"
	>

<channel>
	<title>TechBot</title>
	<atom:link href="http://techbot.me/feed/" rel="self" type="application/rss+xml" />
	<link>http://techbot.me</link>
	<description>My musings on Technology and whatever comes to mind</description>
	<lastBuildDate>Mon, 29 Aug 2011 13:08:17 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Screencast de Introdução a Objective-C &#8211; Parte 2</title>
		<link>http://techbot.me/2011/08/screencast-de-introducao-objective-c-parte-2/</link>
		<comments>http://techbot.me/2011/08/screencast-de-introducao-objective-c-parte-2/#comments</comments>
		<pubDate>Mon, 29 Aug 2011 12:57:56 +0000</pubDate>
		<dc:creator>Maurício Linhares</dc:creator>
				<category><![CDATA[objective-c]]></category>
		<category><![CDATA[ios]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[screencast]]></category>

		<guid isPermaLink="false">http://techbot.me/?p=417</guid>
		<description><![CDATA[E agora temos a segunda parte do screencast de introdução a Objective-C no ar, nessa segunda parte finalmente chegamos aos componentes do Cocoa e implementamos uma interface básica, usando labels, botões e campos de texto, além de conhecer alguns detalhes importantes como lidar com os teclados e um pouco mais sobre gerenciamento de memória. Mais [...]]]></description>
			<content:encoded><![CDATA[<!-- boo-widget start -->
          <script type="text/javascript">
            bb_keywords = "ios";
            bb_bid  = "";
            bb_lang = "";
            bb_name = "custom";bb_limit = "7";bb_format = "bbc";
          </script>
          <script type="text/javascript" src="http://widgets.boo-box.com/javascripts/embed.js"></script>
          <!-- boo-widget end --><p>E agora temos a segunda parte do screencast de introdução a Objective-C no ar, nessa segunda parte finalmente chegamos aos componentes do Cocoa e implementamos uma interface básica, usando labels, botões e campos de texto, além de conhecer alguns detalhes importantes como lidar com os teclados e um pouco mais sobre gerenciamento de memória. Mais uma vez, pra acompanhar esse screencast você precisa ter seguido o screencast anterior e ter pelo menos o XCode 4.0.1 instalado no seu Mac ( <a href="https://github.com/mauricio/objetive-c-tutorial">o código fonte está disponível no GitHub</a> )  :</p>
<p><iframe src="http://player.vimeo.com/video/28293274?title=0&amp;byline=0&amp;portrait=0" width="400" height="250" frameborder="0"></iframe>
<p><a href="http://vimeo.com/28293274">2 &#8211; Introdução a Objective-C &#8211; Parte 2</a> from <a href="http://vimeo.com/mauriciolinhares">Maurício Linhares</a> on <a href="http://vimeo.com">Vimeo</a>.</p>
<p>Se você não viu o screencast anterior, pode encontrar ele <a href="http://techbot.me/2011/08/screencast-introducao-objective-c/">aqui</a> e se você prefere baixar esse screencast e assistir ele quando quiser, é só fazer o download do vídeo <a href="http://vimeo.com/download/video:63299459?v=2&#038;e=1314625355&#038;h=24ea97b012127f68222eb7d9a7c2b429&#038;uh=85c386834c7a91c0ea0b5b549e0087be">aqui</a>.</p>
<p>Os próximos screencasts já estão sendo preparados, então fique de olho aqui no blog, <a href="http://twitter.com/#!/mauriciojr">no meu twitter</a> e no <a href="http://vimeo.com/mauriciolinhares">vimeo</a>. </p>
<p>Se você quer ir mais fundo em desenvolvimento iOS, alguns livros pra seguir em frente:</p>
<ul>
<li><a href="http://www.amazon.com/gp/product/143023024X/ref=as_li_ss_tl?ie=UTF8&#038;tag=ultimaspalavr-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399369&#038;creativeASIN=143023024X">Beginning iPhone 4 Development: Exploring the iOS SDK</a><img src="http://www.assoc-amazon.com/e/ir?t=&#038;l=as2&#038;o=1&#038;a=143023024X&#038;camp=217145&#038;creative=399369" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
</li>
<li><a href="http://www.amazon.com/gp/product/143022505X/ref=as_li_ss_tl?ie=UTF8&#038;tag=ultimaspalavr-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399369&#038;creativeASIN=143022505X">More iPhone 3 Development: Tackling iPhone SDK 3 (Beginning)</a><img src="http://www.assoc-amazon.com/e/ir?t=&#038;l=as2&#038;o=1&#038;a=143022505X&#038;camp=217145&#038;creative=399369" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
</li>
<li><a href="http://www.amazon.com/gp/product/1430218150/ref=as_li_ss_tl?ie=UTF8&#038;tag=ultimaspalavr-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399369&#038;creativeASIN=1430218150">Learn Objective-C on the Mac (Learn Series)</a><img src="http://www.assoc-amazon.com/e/ir?t=&#038;l=as2&#038;o=1&#038;a=1430218150&#038;camp=217145&#038;creative=399369" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://techbot.me/2011/08/screencast-de-introducao-objective-c-parte-2/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Screencast &#8211; Introdução a Objective-C &#8211; Parte 1</title>
		<link>http://techbot.me/2011/08/screencast-introducao-objective-c/</link>
		<comments>http://techbot.me/2011/08/screencast-introducao-objective-c/#comments</comments>
		<pubDate>Fri, 19 Aug 2011 12:22:18 +0000</pubDate>
		<dc:creator>Maurício Linhares</dc:creator>
				<category><![CDATA[objective-c]]></category>
		<category><![CDATA[ios]]></category>
		<category><![CDATA[ipad]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[macos]]></category>
		<category><![CDATA[screencast]]></category>
		<category><![CDATA[xcode]]></category>

		<guid isPermaLink="false">http://techbot.me/?p=403</guid>
		<description><![CDATA[E eis que finalmente surge o primeiro screencast! Demorou mas agora saiu, revitalizando o material de Objective-C e iOS que eu comecei ano passado, resolvi gravar screencasts pra essa atualização em vez de escrever pra ver se funciona melhor e o primeiro vídeo, de introducão a linguagem Objective-C, vocês encontram aí embaixo ( o código [...]]]></description>
			<content:encoded><![CDATA[<!-- boo-widget start -->
          <script type="text/javascript">
            bb_keywords = "macos";
            bb_bid  = "";
            bb_lang = "";
            bb_name = "custom";bb_limit = "7";bb_format = "bbc";
          </script>
          <script type="text/javascript" src="http://widgets.boo-box.com/javascripts/embed.js"></script>
          <!-- boo-widget end --><p>E eis que finalmente surge o primeiro screencast! Demorou mas agora saiu, <a href="http://www.slideshare.net/mauricio.linhares/curso-de-desenvolvimento-de-aplicaes-para-ios-com-objectivec">revitalizando o material de Objective-C e iOS que eu comecei ano passado</a>, resolvi gravar screencasts pra essa atualização em vez de escrever pra ver se funciona melhor e o primeiro vídeo, de introducão a linguagem Objective-C, vocês encontram aí embaixo ( <a href="https://github.com/mauricio/objetive-c-tutorial">o código fonte está disponível no GitHub</a> ) :</p>
<p><iframe src="http://player.vimeo.com/video/27894791?title=0&amp;byline=0&amp;portrait=0" width="400" height="250" frameborder="0"></iframe>
<p><a href="http://vimeo.com/27894791">Introdução a Objective-C</a> from <a href="http://vimeo.com/mauriciolinhares">Maurício Linhares</a> on <a href="http://vimeo.com">Vimeo</a>.</p>
<p>Lembre-se que pra serguir esse tutorial você já deve ter o XCode, pelo menos na versão 4.0.1, instalado na sua máquina.</p>
<p>Comentários, dicas, whatever são todos bem vindos, especialmente porque esse é somente o primeiro vídeo de muitos outros que ainda estão por vir. Os screencasts não vão ser somente sobre Objective-C, já existem alguns sobre Ruby, Scala e JavaScript, mas a primeira leva de vídeos é um material de introdução ao desenvolvimento de aplicações pra iOS, então fiquem de olho aqui no blog, <a href="http://twitter.com/#!/mauriciojr">no twitter</a> e <a href="http://vimeo.com/mauriciolinhares">na minha conta do Vimeo</a> pra estar sempre sabendo quando atualizações aparecerem.</p>
<p><strong>Se você prefere assistir no conforto do seu computador sem fazer streaming, o arquivo pode ser baixado direto do Vimeo <a href="http://vimeo.com/download/video:62311089?v=2&#038;e=1313766937&#038;h=f31e8bb4b60920ce333e12fcecb342e9&#038;uh=85c386834c7a91c0ea0b5b549e0087be">clicando aqui</a>.</strong></p>
<p>A parte 2 desse tutorial está disponível <a href="http://techbot.me/2011/08/screencast-de-introducao-objective-c-parte-2/">aqui</a>.</p>
<p>Se você quer ir mais fundo em desenvolvimento iOS, alguns livros pra seguir em frente:</p>
<ul>
<li><a href="http://www.amazon.com/gp/product/143023024X/ref=as_li_ss_tl?ie=UTF8&#038;tag=ultimaspalavr-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399369&#038;creativeASIN=143023024X">Beginning iPhone 4 Development: Exploring the iOS SDK</a><img src="http://www.assoc-amazon.com/e/ir?t=&#038;l=as2&#038;o=1&#038;a=143023024X&#038;camp=217145&#038;creative=399369" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
</li>
<li><a href="http://www.amazon.com/gp/product/143022505X/ref=as_li_ss_tl?ie=UTF8&#038;tag=ultimaspalavr-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399369&#038;creativeASIN=143022505X">More iPhone 3 Development: Tackling iPhone SDK 3 (Beginning)</a><img src="http://www.assoc-amazon.com/e/ir?t=&#038;l=as2&#038;o=1&#038;a=143022505X&#038;camp=217145&#038;creative=399369" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
</li>
<li><a href="http://www.amazon.com/gp/product/1430218150/ref=as_li_ss_tl?ie=UTF8&#038;tag=ultimaspalavr-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399369&#038;creativeASIN=1430218150">Learn Objective-C on the Mac (Learn Series)</a><img src="http://www.assoc-amazon.com/e/ir?t=&#038;l=as2&#038;o=1&#038;a=1430218150&#038;camp=217145&#038;creative=399369" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://techbot.me/2011/08/screencast-introducao-objective-c/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>Using board games to teach object oriented analysis</title>
		<link>http://techbot.me/2011/07/board-games-teach-object-oriented-analysis/</link>
		<comments>http://techbot.me/2011/07/board-games-teach-object-oriented-analysis/#comments</comments>
		<pubDate>Mon, 18 Jul 2011 17:08:58 +0000</pubDate>
		<dc:creator>Maurício Linhares</dc:creator>
				<category><![CDATA[Education]]></category>
		<category><![CDATA[analysis]]></category>
		<category><![CDATA[education. board games]]></category>
		<category><![CDATA[object oriented design]]></category>

		<guid isPermaLink="false">http://techbot.me/?p=392</guid>
		<description><![CDATA[Just when I arrived from Cambridge back home the coordinator at the college where I usually teach after-grad courses mailed me asking if I’d be available to pick up an “Object Oriented Analysis and Design” class. I was still unpacking my stuff but I just said yes. Why not? It’s something I really like to [...]]]></description>
			<content:encoded><![CDATA[<!-- boo-widget start -->
          <script type="text/javascript">
            bb_keywords = "analysis";
            bb_bid  = "";
            bb_lang = "";
            bb_name = "custom";bb_limit = "7";bb_format = "bbc";
          </script>
          <script type="text/javascript" src="http://widgets.boo-box.com/javascripts/embed.js"></script>
          <!-- boo-widget end --><p>Just when I arrived from Cambridge back home <a href="http://www.linkedin.com/in/erikobrito">the coordinator</a> at <a href="http://www.faculdadeidez.com.br/">the college</a> where I usually teach after-grad courses mailed me asking if I’d be available to pick up an “Object Oriented Analysis and Design” class. I was still unpacking my stuff but I just said yes. Why not? It’s something I really like to teach and talk about, could be great to get me back to teaching after 5 months away from classes.</p>
<p>While preparing the material, I started thinking about how to add something unusual. It wasn’t long until I looked at my wardrobe and saw the pile of board game boxes in there. I looked at them and thought, I can get them to model the games themselves!</p>
<p><span id="more-392"></span></p>
<h3>Meeting board games</h3>
<p><div id="attachment_395" class="wp-caption alignleft" style="width: 610px"><a href="http://techbot.me/wp-content/uploads/2011/07/ticket-to-ride-team-in-action.jpg"><img src="http://techbot.me/wp-content/uploads/2011/07/ticket-to-ride-team-in-action.jpg" alt="Ticket to Ride: Europe team learning how the game goes" title="Ticket to Ride: Europe team learning how the game goes" width="600" height="449" class="size-full wp-image-395" /></a><p class="wp-caption-text">Ticket to Ride: Europe team learning how the game goes</p></div>I started on European style board games on 2007, when doing a presentation at the <a href="http://blog.fragmental.com.br/2007/02/05/erecompal-2007/">ERECOMP-Alagoas</a> with <a href="http://fragmental.tw/">Phil Calçado</a>. After the event we headed back to the hotel and instead of doing nothing we went out to drink something and talk and he said he had met his wife at a <a href="http://www.amazon.com/gp/product/B004DZ8WYE/ref=as_li_ss_tl?ie=UTF8&#038;tag=ultimaspalavr-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399369&#038;creativeASIN=B004DZ8WYE">Carcassonne</a> table, an European board game his friends discovered and everyone was having fun. </p>
<p>I can’t say I was board games fan in the past, mostly because all I knew about them was Risk and Monopoly and you just get bored after playing them for a while. But Phil said these European board games were different, so, why not try them? After the event I found a shop and got Settlers of Catan and Carcassonne. When they arrived, it was like an addiction, we were playing Settlers every weekend and from that time to today the board games collection has grown from those two to more than 10 different games games and their expansions (Dominion alone is now at six boxes).</p>
<h3>How come you’re using board games on classes?</h3>
<p><div id="attachment_397" class="wp-caption alignleft" style="width: 610px"><a href="http://techbot.me/wp-content/uploads/2011/07/settlers-team-in-action.jpg"><img src="http://techbot.me/wp-content/uploads/2011/07/settlers-team-in-action.jpg" alt="Settlers of Catan team picking up resources" title="Settlers of Catan team picking up resources" width="600" height="449" class="size-full wp-image-397" /></a><p class="wp-caption-text">Settlers of Catan team picking up resources</p></div>Why use board games, you might ask. The course itself is an after-grad course (we call it specialization here in Brazil, it would be something like a graduate school in the US), classes happen every 15 days, on Friday nights from 6:30 to 10:30 and Saturdays from 8:00 to 12:00 and again from 13:00 to 17:00. So students see the same teacher and the same class for 12 hours straight over one weekend.</p>
<p>As you might imagine, 12 hours straight of the same teacher talking about the same subject is not the definition of fun, so you have to come up with unorthodox solutions in your class to keep everyone interested in what you’re talking. You need to engage the students on whatever you’re teaching so they’re not drifting away to sleep after lunch on a Saturday afternoon.</p>
<p>I had a problem, coming up with an interesting topic to use as an example. I couldn’t just bring a shallow model, as it would not fit the idea that was to exercise analysis and design but I couldn’t just bring up something that was too difficult or needed too much background to understand. Also, it had to be something the students could easily relate to and enjoy modeling.</p>
<p>Thing is, board games were the best choice ever.</p>
<h3>First contact</h3>
<p><div id="attachment_398" class="wp-caption alignleft" style="width: 610px"><a href="http://techbot.me/wp-content/uploads/2011/07/settlers-team-playing.jpg"><img src="http://techbot.me/wp-content/uploads/2011/07/settlers-team-playing.jpg" alt="Modeling, Modeling, Modeling..." title="Modeling, Modeling, Modeling..." width="600" height="449" class="size-full wp-image-398" /></a><p class="wp-caption-text">Modeling, Modeling, Modeling...</p></div>As expected, the students had no previous experience with any of the two board games I got in, <a href="http://www.amazon.com/gp/product/B000W7JWUA/ref=as_li_ss_tl?ie=UTF8&#038;tag=ultimaspalavr-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399369&#038;creativeASIN=B000W7JWUA">“Settlers of Catan”</a> and <a href="http://www.amazon.com/gp/product/B000809OAO/ref=as_li_ss_tl?ie=UTF8&#038;tag=ultimaspalavr-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399369&#038;creativeASIN=B000809OAO">“Ticket to Ride: Europe”</a> (well, not all of them, <a href="http://twitter.com/#!/lilamoura">one is a close friend of mine</a> that has played most of them, but she has never played “Ticket to Ride”, as I just brought it from the US, so it was her first experience with it also). </p>
<p>The plan is that they have to build a model that’s going to be used to implement these games in the computer, so they have to understand the game rules, themes and development. The fact that they have no previous experience with these games helps simulate the issues we face daily while building software for real. We have to learn the domain as we go, things change and new rules show up at any time.</p>
<p>They had no access to the game rules, I was the only source of information and I intentionally did not give them all the information required to play the game. Information was given in pieces and while they started playing the games to get a feel of them I would come in and bring a new rule. When they got stuck or had no idea how to move forward, they came to me and asked how that would go, as they would ask a client how a feature is supposed to work. While asking and playing the games, they were taking notes and starting to think about how the game is supposed to be modelled. After playing the games for half an hour or a little bit more, they started the real fight, turning those notes into the model.</p>
<h3>Modeling in action</h3>
<p><div id="attachment_399" class="wp-caption alignleft" style="width: 610px"><a href="http://techbot.me/wp-content/uploads/2011/07/ticket-to-ride-team-modelling.jpg"><img src="http://techbot.me/wp-content/uploads/2011/07/ticket-to-ride-team-modelling.jpg" alt="&quot;This is how the train goes!&quot;" title="&quot;This is how the train goes!&quot;" width="600" height="449" class="size-full wp-image-399" /></a><p class="wp-caption-text">&quot;This is how the train goes!&quot;</p></div>As simple as these games look like when you’re playing, bringing this simplicity into a computer is not really that, well, simple. Rules that just make sense when you’re playing the game become sources of complexity in a model, just like the Train Station rule on “Ticket to Ride”. In the game, you’re building rail roads connecting two cities with your own trains, but you can place a train station in a city and use someone else’s trains as if your own if they’re connected to your Train Station. But then these Train Station connections are not valid for the “European Express” objective and they also mean you gave up on points at the end of the game.</p>
<p>Rules that are just common sense for us, players, are not that easy to come by to computers. They have to be told what to do and how to do it and this is where modeling these games is a real challenge that goes really beyond the usual “mapping from the database” we see on nowadays “enterprise” applications. These games have rich models, their own internal language and behaviors, which makes them even more interesting to a design class, as you have many different issues to solve and you’re possibly having fun by doing it about a game.</p>
<h3>Does it work?</h3>
<p>So far, I would say it met my objectives. The students were highly engaged in trying to figure out how to make those rules become objects in a system (and they also loved to meet and play the games! ☺ ). Some specific cases led to interesting discussions, like how can we implement the “action” during a turn on “Ticket to Ride: Europe”, as each player can perform one of four really different actions (pick cards, pick destination tickets, claim routes or place a train station) and how to model the “Settlers of Catan” game board in a way it would be possible to place terrains and also make sure the settlements and roads would be correctly placed.</p>
<p>They are going to be working on refining the models and in 15 days we will meet again for the final presentation and some “show me the code” about how these games could be implemented. I hope that by the end of this class they will have an improved background on object oriented analysis and built on top of real world knowledge, not only reading books or listening to a teacher talking about objects, relationships and design patterns.</p>
<p>My mother, an elementary school teacher, always tried to do something different for her children, with new games, songs, drawings and activities that went beyond the usual “talk, read and write” and she can barely use a computer. Today we have so many different possibilities and materials but we keep ourselves stuck into the “Power point bullets”, confined in a sequential list of topics.</p>
<p>We can do better. We can improve. We just have to try.</p>
<p><strong>And stay tuned to the results of this crazy experience, I will be sure to post my findings here as soon as the class ends.</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://techbot.me/2011/07/board-games-teach-object-oriented-analysis/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Ruby Basics &#8211; Equality operators in Ruby</title>
		<link>http://techbot.me/2011/05/ruby-basics-equality-operators-ruby/</link>
		<comments>http://techbot.me/2011/05/ruby-basics-equality-operators-ruby/#comments</comments>
		<pubDate>Mon, 30 May 2011 05:43:27 +0000</pubDate>
		<dc:creator>Maurício Linhares</dc:creator>
				<category><![CDATA[ruby]]></category>
		<category><![CDATA[basics]]></category>
		<category><![CDATA[comparable]]></category>
		<category><![CDATA[comparator]]></category>
		<category><![CDATA[comparison]]></category>
		<category><![CDATA[equal]]></category>
		<category><![CDATA[equality]]></category>
		<category><![CDATA[learning]]></category>
		<category><![CDATA[rails]]></category>

		<guid isPermaLink="false">http://techbot.me/?p=352</guid>
		<description><![CDATA[Dig deeper into Ruby with this book After Greg Sterndale&#8217;s presentation on a boston-rb hackfest earlier this month I noticed that not everyone knew the operators available for equality and comparisons in Ruby. Why not take the dust away from the blog and write about it, then? Ruby has many equality operators, some of them [...]]]></description>
			<content:encoded><![CDATA[<!-- boo-widget start -->
          <script type="text/javascript">
            bb_keywords = "equality";
            bb_bid  = "";
            bb_lang = "";
            bb_name = "custom";bb_limit = "7";bb_format = "bbc";
          </script>
          <script type="text/javascript" src="http://widgets.boo-box.com/javascripts/embed.js"></script>
          <!-- boo-widget end --><div id="attachment_160" class="wp-caption alignleft" style="width: 132px"><a href="http://www.amazon.com/gp/product/0596516177?ie=UTF8&amp;tag=ultimaspalavr-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0596516177"><img class="size-full wp-image-160" title="Dig deeper into Ruby with this book" src="http://techbot.me/wp-content/uploads/2011/01/ruby1.jpg" alt="Dig deeper into Ruby with this book" width="122" height="160" /></a>
<p class="wp-caption-text">Dig deeper into Ruby with this book</p>
</div>
<p>After <a href="http://gregsterndale.com/">Greg Sterndale&#8217;s</a> presentation on a <a href="http://bostonrb.org/">boston-rb</a> hackfest earlier this month I noticed that not everyone knew the operators available for equality and comparisons in Ruby. Why not take the dust away from the blog and write about it, then?</p>
<p>Ruby has many equality operators, some of them we use and see everywhere in our applications (like the usual double equal &#8211; “==”) and some are also used everywhere but we don’t really get to see them (like the triple equal or case equal operator – “===”). So let’s dig into how Ruby implements comparisons between our objects.</p>
<p>You can see the full source code for this tutorial on <a href="https://github.com/mauricio/ruby-equality-operators">Github</a>.</p>
<p><span id="more-352"></span></p>
<h2>What does it mean to be equal?</h2>
<p>Maybe this is something from my Java past, but I find it to be really useful to first define what being “equal” really means. Objects have identities for the Ruby interpreter, you can easy check this by calling the object_id method:</p>
<pre class="brush:ruby">some_string = 'some string'
=&gt; "some string"
another_string = 'another_string'
=&gt; "another_string"
[ some_string.object_id, another_string.object_id ]
=&gt; [2164900860, 2164888440]</pre>
<p>As you keep on creating new objects, Ruby itself will give you an object_id for each of them and you could possibly use this to identify all your objects. But there’s a little gotcha:</p>
<pre class="brush:ruby">matz = 'matz'
=&gt; "matz"
matz_2 = 'matz'
=&gt; "matz"
[ matz.object_id, matz_2.object_id ]
=&gt; [2164840660, 2164818480]</pre>
<p>The two objects represent exactly the same value, but each of them has it’s own object_id, as the Ruby interpreter has no idea these two objects are the same. So, while object_id could be a shortcut to define the identity of our objects, each object has it’s own way to define what being equal to someone else really means.</p>
<p>Two Strings are equal when they represent exactly the same sequence of characters, two people would be the same if they had the same social security number (or the same CPF if you were in Brazil). To be able to implement this kind of identity the language must offer you with hooks for this and in Ruby these hooks are the equality methods.</p>
<h2>&#8220;==&#8221; &#8211; double equals</h2>
<p>The double equals method should implement the general identity algorithm to an object, which usually means that you should compare the object attributes and not if they are the same object in memory. And given Ruby is dynamically typed language, you should try not to depend on types, but on methods, instead of checking if the object is from an specific class, check if it responds to an specific method:</p>
<pre class="brush:ruby">class Meter

  def initialize( value )
    @value = value
  end

  def to_meters
    @value.to_f
  end

  def ==( other )
    if other.respond_to?( :to_meters )
      self.to_meters == other.to_meters
    end
  end

end</pre>
<p>Instead of checking on the parameter type I check if the object has the method I expect to use, I it has then I do the comparison, if it doesn’t a “false” is sent up, which means that the objects are not equivalent.</p>
<h2>The “hash” method</h2>
<p>If you&#8217;re already implementing &#8220;==&#8221;, you should also implement &#8220;eql?&#8221; and &#8220;hash&#8221; methods, even if you have never seen these methods being called anywhere. The reason is these are the methods the Hash object is going to use to compare your object if you&#8217;re using in as a Hash key. The thing is, Hashes have to be fast to figure out if a key is already in there and to be able to do this they just avoid comparing every single object, they just go by &#8220;clustering&#8221; objects in groups by using the value returned by your object&#8217;s &#8220;hash&#8221; method and then, once in a cluster, they compare the objects themselves using &#8220;eql?&#8221;.</p>
<p>Here’s a very naïve diagram of how it would look:</p>
<p><img src="http://techbot.me/wp-content/uploads/2011/05/Hash.png" alt="Naive implementation for a Hash" title="Naive implementation for a Hash" width="792" height="320" class="size-full" /></a></p>
<p>Then searching for a key in a Hash, they first call “hash” in the key to figure out in which group it would be, then they compare the key with all the other keys in the group using the “eql?” method. In a worst-case scenario, we would compare with 3 objects (and the hash has nine of them), which is quite nice. If you were using an array, your worst case would be 9 comparisons.</p>
<p>For a quick example of how this could affect your code, let’s check this class:</p>
<pre class="brush:ruby">class StringWithoutHash

  def initialize(text)
    @text = text
  end

  def to_text
    @text
  end  

  def ==(other_value)
    if other_value.respond_to?(:to_text)
      self.to_text == other_value.to_text
    end
  end

end</pre>
<p>The &#8220;==&#8221; method has been implemented correctly, but we haven&#8217;t implemented &#8220;hash&#8221; and &#8220;eql?&#8221;, so the two objects will end up in different clusters they we won&#8217;t be able to figure out they&#8217;re the same object inside the hash.</p>
<pre class="brush:ruby">  context 'without hash and eql? methods' do

    it 'should be equal' do
      @first  = StringWithoutHash.new('first')
      @second = StringWithoutHash.new('first')
      @first.should == @second
    end

    it 'should add as two different keys in the hash' do

      @texts = {}

      10.times do
        @texts[ StringWithoutHash.new('first') ] = 'one'
      end

      @texts.keys.size.should == 10

    end

  end</pre>
<p>Even with @first and @second representing exactly the same text (and being equal) they still generate two keys in the Hash instead of one because we did not implement the “hash”  and &#8220;eql?&#8221; methods. The general rule is that if you override “==” you should also override “hash” and &#8220;eql?&#8221;. When implementing “hash”, a basic rule to follow is if two objects are “==” they must generate exactly the same hash value (so they can be found in a Hash object), but two objects can have the same hash value but still be different (they belong to the same group but are not the same object).</p>
<p>Here&#8217;s a subclass of our StringWithoutHash, StringWithHash, that correctly implements both methods (and the &#8220;eql?&#8221; method itself is just using the &#8220;==&#8221;, we don&#8217;t even have to bother writting code for it):</p>
<pre class="brush:ruby">class StringWithHash < StringWithoutHash

  def eql?( other )
    self == other
  end  

  def hash
    @text.hash
  end

end
</pre>
<p>And here are some specs showing how it works correctly now:</p>
<pre class="brush:ruby">context 'with hash and eql? methods' do

    it 'should be equal' do
      @first  = StringWithHash.new('first')
      @second = StringWithHash.new('first')
      @first.should == @second
    end

    it 'should add as a single key in the hash' do

      @texts = {}

      50.times do
        @texts[ StringWithHash.new('first') ] = 'one'
      end

      @texts.keys.size.should == 1

    end

  end
</pre>
<p>So, now that we implemented "eql?" and "hash" correctly even trying to add the object 50 times we stil have a single one, because our code can now be sure that the object is there (or not) as the methods are available.</p>
<p>Unless you know exactly what you’re doing (and you know how Ruby implement it’s hashes) you should not implement your own hashing function, just use a hashing function from a basic object like numbers and strings and you’re done. Here’s the example:</p>
<pre class="brush:ruby">class Meter

  # all the other methods

  def hash
    self.to_meters.hash
  end

  def eql?( other )
    self == other
  end 

end</pre>
<p>Here, instead of adding my own “hash” function I just reuse the hash method on Float, which already does it the right way. And unless you have some specific needs you should be doing the same.</p>
<h2>"===" - triple equals</h2>
<p>Triple equals is an interesting operator, it’s everywhere in Ruby code but most people have never seen it in real code out there. But, how come it’s everywhere and no one has ever seen it? It’s hidden inside a common control structure, the “case/when”. Whenever you’re using a “case/when” you’re using, in fact, the “===” operator and this is what makes the case statement on Ruby much more powerful than it’s counterpart in languages like C or Java.</p>
<p>Let’s look at a statement:</p>
<pre class="brush:ruby">age = 19

case age
  when 1..18
    puts 'just out of college'
  when 19..30
    puts 'wild years'
  when 31..40
    puts 'i better find a job in a big corp'
  else
    puts 'retirement plan'
end</pre>
<p>This one will print ‘wild years’ and it’s using the ‘===’ operator. Can you see how it’s working? Here’s this same case/when done with if’s:</p>
<pre class="brush:ruby">if 1..18 === age
  puts 'just out of college'
elsif 19..30 === age
  puts 'wild years'
elsif 31..40 === age
  puts 'i better find a job in a big corp'
else
  puts 'retirement plan'
end</pre>
<p>In the end, the case/when statement is just a glorified if using the ‘===’ operator to simplify your job (and also make you type less). In the language itself, the triple equals is used mostly to as a “grouping” operator, by getting a value and figuring out at which group it belongs to.</p>
<p>In our example it’s a group of ages represented by Range objects, but you’ll see this being used to figure out if an object is from an specific class, if a string matches a regular expression and the like. And all of this is possible because the method (‘===’) is not called at the object in the “case” definition but on each “when”.</p>
<p>Imagine you have Rectangles and you want to figure out if a specific Point is inside the Rectangle, this is a perfect fit for the triple equals. Let’s look at a sample implementation:</p>
<pre class="brush:ruby">class Point

  include Comparable

  attr_accessor :x, :y

  def initialize(x, y)
    self.x = x
    self.y = y
  end

  def hash
    "#{x}-#{y}".hash
  end

  def &lt;=&gt; (other)

    result = nil

    if other.respond_to?(:x) &amp;&amp; other.respond_to?(:y)

      result = if self.x == other.x &amp;&amp; self.y == other.y
                 0
               elsif self.x &gt;= other.x &amp;&amp; self.y &gt;= other.y
                 1
               else
                 -1
               end

    end

    result

  end

end</pre>
<p>And the Rectangle class:</p>
<pre class="brush:ruby">class Rectangle

  attr_accessor :start, :end

  def initialize( x1, y1, x2, y2 )
    self.start = Point.new( x1, y1 )
    self.end = Point.new( x2, y2 )

    if self.start &gt;= self.end
      raise "Start #{self.start.inspect} should be less than end #{self.end.inspect}"
    end
  end

  def === (other)
    if other.respond_to?(:x) &amp;&amp; other.respond_to?( :y )
       other.between?( self.start, self.end )
    else
      self == other
    end
  end

  def == (other)
    if other.respond_to?( :start ) &amp;&amp; other.respond_to?( :end )
      self.start == other.start &amp;&amp; self.end == other.end
    end
  end

end</pre>
<p>And now some usage:</p>
<pre class="brush:ruby">point = Point.new( 2, 4 )

case point
  when Rectangle.new( 0, 3, 5, 8  )
    puts 'found it here'
  when Rectangle.new( 3, 3, 10, 15  )
    puts 'i will not match'
end</pre>
<p>The ‘===’ method will be called on Rectangle giving it a Point object and it’s going to figure out if that point is inside the Rectangle or not.</p>
<h2>Making your objects Comparable</h2>
<p>If you looked closely at the Point object you will notice that it includes the Comparable module and implements a method defined as “&lt;=&gt;”, the flying saucer operator. The flying saucer operator is to be used as means to sort your objects in a collection, but the Comparable module brings some interesting functionality for classes that implement it.</p>
<p>The idea behind the “&lt;=&gt;” operator is that when you call it on a object, this object must define it’s position compared to the other object given as a parameter. If the receiver of the call is greater than the argument, it should return 1, if they re the same, it should return 0, if the receiver is less than the argument it should return -1 and if they’re not compatible it should return nil.</p>
<p>Once you implement the flying saucer, you can just include Comparable in your class and the following methods are now implemented for you:</p>
<ul>
<li>&gt;</li>
<li>&gt;=</li>
<li>&lt;</li>
<li>&lt;=</li>
<li>==</li>
<li>between?</li>
</ul>
<p>This is why we could use the “between?” method in Point when implementing the “===” operator on Rectangle. By implementing “&lt;=&gt;” and including Comparable we get a lot of functionality for our objects for free and we also have a great example of how you should plan to build your own modules on your projects.</p>
<p>Going back to our first example, the Meter class, we could add new classes for Inch and Foot and have them all share the same equality implementation. First, we define that all our classes will have a to_meters method that will return their value in meters. Then we create our module:</p>
<pre class="brush:ruby">module MeterComparator

  include Comparable

  def &lt;=&gt; (other)
    result = nil

    if other.respond_to?(:to_meters)
      receiver_value = self.to_meters
      argument_value = other.to_meters

      result = if receiver_value == argument_value
                 0
               elsif receiver_value &lt; argument_value
                 -1
               else
                 1
               end
    end

    result
  end

  def inspect
    "#&lt;#{self.class}:#{self.hash} size_in_meters=#{self.to_meters}&gt;"
  end

  def hash
    self.to_meters.hash
  end

end</pre>
<p>We implemented the “&lt;=&gt;” operator and (also the “hash” method, don’t forget it!) for this module and included Comparable, which will make all classes including it to be comparable too. Let’s look at how our new Meter, Inch and Foot will look like now:</p>
<pre class="brush:ruby">class Meter

  include MeterComparator

  def initialize( value )
    @value = value
  end

  def to_meters
    @value.to_f
  end

end</pre>
<p>And Inch:</p>
<pre class="brush:ruby">class Inch

  include MeterComparator

  def initialize( value )
    @value = value
  end

  def to_meters
    @value.to_f / 39.370
  end

end</pre>
<p>And finally Foot:</p>
<pre class="brush:ruby">class Foot

  include MeterComparator

  def initialize( value )
    @value = value
  end

  def to_meters
    @value.to_f / 3.2808
  end

end</pre>
<p>All classes share the same comparison methods so we can use them all interchangeably in our code, we can even safely compare them with each other and they’ll yield the correct results:</p>
<pre class="brush:ruby">  context 'comparing meters with inches' do

    it 'should be true when they both represent the same distance' do
      @meter = Meter.new(4)
      @inch  = Inch.new(157.48)

      @meter.should == @inch
    end

  end

  context 'comparing meters to feet' do

    it 'should be true when they both represent the same distance' do
      @meter = Meter.new(8)
      @foot  = Foot.new(26.2464)

      @meter.should == @foot
    end

  end

  context 'comparing feet to inches' do

    it 'should be true when they both represent the same distance' do
      @foot = Foot.new(26.2464)
      @inch = Inch.new(314.96)

      @foot.should == @inch
    end

  end

  context 'when sorting objects' do

    before do
      @meter    = Meter.new(1.5)
      @inch     = Inch.new(157.48)
      @foot     = Foot.new(26.2464)
      @measures = [@inch, @meter, @foot].sort
    end

    it 'should order them by size correctly' do
      @measures.first == @meter
      @measures[1] == @inch
      @measures.last == @foot
    end

  end</pre>
<p>You can mix and mingle different measures and they’ll all play and compare nicely to each other, they just have to implement the “to_meters” method and include the MeterComparator class, simple and right to the point implementation.</p>
<p>Also, once you include the Comparable module your objects become "sortable" in an array, you can use the "sort" and "sort!" methods in Array. The order is ascending as defined by your "<=>" method implementation.</p>
<h2>“eql?” and “equal?”</h2>
<p>Technically, “eql?” is should behave just like “==” and it's also the method selected by the <strong>Hash</strong> class to figure out of your object is already in a "hash cluster" (as we have discussed above). You compare two objects to see if they represent the same values. Usually you can just override "eql?" and delegate it's call to "==", as we did in our examples. <strong>This is not already done for you</strong>, the default "eql?" implementation at the <strong>Object</strong> class uses comparison between "object_id" values and this is usually <strong>NOT</strong> what you want, so, make sure that if you implement "==" you also override "eql?" and implement "hash".</p>
<p>There’s one exception, though, Numeric objects will convert different types when compared using “==” but will not do this when using “eql?”, so:</p>
<pre class="brush:ruby">5 == 5.0 # is true</pre>
<p>But:</p>
<pre class="brush:ruby">5.eql?( 5.0 ) # is false</pre>
<p>And “equal?” is a little bit exoteric as it will compare if two objects are the same object in memory. You should never ever override this method. In fact, you’re better of ignoring the fact that “equal?” exist at all for your own safety. And don’t say you have not been warned.</p>
<h2>Closing thoughts</h2>
<p>While there’s a lot to be said about comparing objects in Ruby, the final implementation is quite simple and modules like Comparable make it even simpler as long as you know they exist. Now there’s no reason to correctly implement comparison for your Ruby objects and never forget the “hash” method again! ;)</p>
<h2>Want to dig deeper into Ruby?</h2>
<p>Here are some books that will surely help you out:</p>
<ul>
<li><a href="http://www.amazon.com/gp/product/0321584104/ref=as_li_ss_tl?ie=UTF8&#038;tag=techbot-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399349&#038;creativeASIN=0321584104">Eloquent Ruby (Addison-Wesley Professional Ruby Series)</a><img src="http://www.assoc-amazon.com/e/ir?t=&#038;l=as2&#038;o=1&#038;a=0321584104&#038;camp=217145&#038;creative=399349" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
</li>
<li>
<a href="http://www.amazon.com/gp/product/0321490452/ref=as_li_ss_tl?ie=UTF8&#038;tag=techbot-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399349&#038;creativeASIN=0321490452">Design Patterns in Ruby</a><img src="http://www.assoc-amazon.com/e/ir?t=&#038;l=as2&#038;o=1&#038;a=0321490452&#038;camp=217145&#038;creative=399349" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
</li>
<li>
<a href="http://www.amazon.com/gp/product/1933988657/ref=as_li_ss_tl?ie=UTF8&#038;tag=techbot-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399349&#038;creativeASIN=1933988657">The Well-Grounded Rubyist</a><img src="http://www.assoc-amazon.com/e/ir?t=&#038;l=as2&#038;o=1&#038;a=1933988657&#038;camp=217145&#038;creative=399349" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
</li>
</ul>
<h2>Related Posts</h2>
<ul>
<li><a href="http://techbot.me/2009/06/understanding-class_eval-module_eval-and-instance_eval/">Understanding class_eval, module_eval and instance_eval</a></li>
<li><a href="http://techbot.me/2008/09/including-and-extending-modules-in-ruby/">Including and extending modules in Ruby</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://techbot.me/2011/05/ruby-basics-equality-operators-ruby/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Criando um programador</title>
		<link>http://techbot.me/2011/05/criando-um-programador/</link>
		<comments>http://techbot.me/2011/05/criando-um-programador/#comments</comments>
		<pubDate>Sat, 07 May 2011 16:37:40 +0000</pubDate>
		<dc:creator>Maurício Linhares</dc:creator>
				<category><![CDATA[diversos]]></category>

		<guid isPermaLink="false">http://techbot.me/?p=333</guid>
		<description><![CDATA[De tempos em tempos surge nas listas de discussão de tecnologia a conversa sobre estágio, como aprender e como se “formar” um programador. Eu tive uma experiência interessante sobre isso e acho que finalmente é a hora de escrever sobre o assunto. Há algum tempo atrás, quando estava saindo de uma empresa e indo pra [...]]]></description>
			<content:encoded><![CDATA[<p>De tempos em tempos surge nas listas de discussão de tecnologia a conversa sobre estágio, como aprender e como se “formar” um programador. Eu tive uma experiência interessante sobre isso e acho que finalmente é a hora de escrever sobre o assunto.</p>
<p><span id="more-333"></span></p>
<p>Há algum tempo atrás, quando estava saindo de uma empresa e indo pra outra, resolvi que poderia usar o “downtime” entre uma e outra pra escrever um produto que já tinha na cabeça a algum tempo. Sentei a bunda na cadeira e passei 3 semanas escrevendo muito código, quando finalmente comecei a trabalhar no novo emprego, meu tempo disponível pra ferramenta diminuiu consideravelmente e eu pensei que talvez fosse a hora de encontrar alguém pra me ajudar.</p>
<p>Considerando o meu estado de “empreendedor liso”, não poderia contratar um desenvolvedor mesmo, então fui pra contratar <a href="https://groups.google.com/group/pbjug/browse_thread/thread/3f4b542d12b310e2/49ae3a6e00bb5b24">um estagiário</a>. A luta contra os currículos foi dura (deveria ter escrito <a href="http://techbot.me/2011/01/como-ser-chutado-de-uma-avaliacao-pra-uma-vaga-na-fase-de-curriculos/">isso</a> antes ) e no fim só um realmente me interessou, especialmente porque <a href="http://twitter.com/#!/pedromtavares">a pessoa</a> dizia que sabia ler e escrever em inglês.<br />
Primeira conversa</p>
<p>Nos encontramos pra uma entrevista formal, conversamos sobre a empresa, meus planos de dominação mundial e o que ele deveria fazer. Descobri que ele havia morado nos EUA durante um tempo, então realmente sabia ler e escrever inglês e pelo papo parecia ser uma pessoa interessada.</p>
<p>A experiência dele era pouca, não teve uma formação legal em orientação a objetos e, mesmo estando cursando a mesma graduação que eu cursei, o fato dele ter tido professores diferentes não deu a ele o conhecimento que eu esperaria que ele já tivesse com o tempo de curso que ele tinha. Além disso ele teria que aprender Rails, HAML, Solr e todas as tecnologias que já estavam sendo usadas na aplicação e isso não seria uma tarefa simples.</p>
<h2>Shu-Ha-Ri</h2>
<p>Na época, eu estava lendo o <a href="http://www.amazon.com/gp/product/0321482751/ref=as_li_ss_tl?ie=UTF8&#038;tag=ultimaspalavr-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399349&#038;creativeASIN=0321482751">“Agile Software Development: The Cooperative Game”</a> do Cockburn e estava fisgado na idéia do Shu-Ha-Ri, que são os 3 momentos do aprendizado, uma idéia que surgiu no teatro Nô japonês. Segundo o Shu-Ha-Ri, a técnica pode ser dividida em 3 estágios diferentes, Shu ou aprender, Ha ou separar e Ri ou transcender.</p>
<p>No estágio do Shu, o aprendiz somente segue os passos já definidos pelo mestre, sem pensar muito sobre o que está sendo feito. No Ha, o aprendiz já começa a pensar sobre o que ele está fazendo, porque ele deve fazer daquela forma. E no Ri o aprendiz define a sua própria forma, baseado nas suas capacidades, conhecimento e experiências de vida. Ele deixa de ser um aprendiz e agora é um mestre de si mesmo, não mais repetindo o caminho do mestre, mas definindo o seu próprio.</p>
<h2>Começando o caminho</h2>
<p>Pra começar com o Shu, era necessário aprender e repetir, então a primeira atividade dele no estágio não seria mexer na aplicação, que já estava relativamente pronta. O primeiro mês dele deveria ser criar um <a href="http://pedromtavares.wordpress.com/2009/11/10/awdwr-introduction/">blog</a> e escrever resumos com exemplos de código de cada um dos capítulos iniciais do <a href="http://www.amazon.com/gp/product/1934356549/ref=as_li_ss_tl?ie=UTF8&#038;tag=ultimaspalavr-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399349&#038;creativeASIN=1934356549">“Agile Web Development with Rails”</a>. </p>
<p>Na época ele deve ter pensado que eu era meio louco (hoje já tem certeza) e na verdade foi bem um tiro no escuro mesmo, eu nunca tinha feito isso antes com um estagiário, mas eu tinha que arriscar. Outro detalhe importante, ele não ia receber nada por esse primeiro mês, os R$ 500,00 reais que ele iria receber foi comprado em livros (lista no final do post), porque ele ainda tinha muito de teoria a melhorar.</p>
<h2>Seguindo o caminho</h2>
<p>Depois do primeiro mês só de blog e de ver como a aplicação funcionava, ele começou finalmente a escrever código. Nesse momento, ainda saindo de Shu para Ha, era necessário que eu constantemente explicasse os passos necessários pra que ele executasse as tarefas. Primeiro porque eu tinha um apego especial pelo código da aplicação, que era a minha primeira ferramenta própria, e também porque eu sabia das limitações dele na tecnologia nesse momento.</p>
<p>A maior parte dos trabalhos iniciais era estender funcionalidades já existentes e como a aplicação havia sido escrita a uma única mão, os padrões eram claros e fáceis de se entender. O estilo do código era único e nesse momento a maior parte do trabalho dele era continuar seguindo esse estilo e complementando as funcionalidades com código que fosse sempre parecido ou derivado do que já existia.</p>
<p>Com um pouco mais de tempo, ele já estava escrevendo funcionalidades únicas, criando código do zero e implementando os seus próprios modelos, ainda baseados no que já existia dentro da aplicação, mas já estava claramente em seu momento Ha. Não era mais necessário me perguntar como tudo funcionava, ele já havia criado o seu próprio entendimento sobre como e o que era a aplicação e começava a enxergar ela com os seus próprios olhos. Eu já descrevia as funcionalidades sem explicar detalhes específicos de implementação e ele era capaz de implementá-las sem que houvesse necessidade de ser levado pelo caminho.</p>
<h2>Transcendendo o caminho</h2>
<p>O estágio acabou, ele tomou o seu próprio caminho e tem agora o seu próprio “estilo”, chegou ao seu momento de Ri, não do conhecimento, mas dessa primeira fase de entendimento do trabalho de um programador. Hoje eu vejo que os primeiros passos, naqueles primeiros meses, foram essenciais pra formação e a prática aliada a teoria foram as principais responsáveis por fazer com que ele se tornasse capaz de escrever código útil, que continua na ferramenta até hoje, com praticamente meses de experiência em programação.</p>
<p>Acho que o grande problema que temos hoje na formação é o exagero do ensino no Shu e também o exagero dos aprendizes no Ri. Os professores se perdem eternamente na fase Shu mas os alunos gostariam de estar já em Ri, eles não querem mais passar pela fase de conhecer, querem passar diretamente pra fase do criar, pensando que a rápida passada por cima do conhecimento já vai lhes dar condições de criar algo por eles mesmos.</p>
<p>Programação se aprende com leitura e, principalmente, prática. A criação do blog não era somente pra que ele lesse o livro naquele momento, mas que ele lesse e repetisse o conhecimento. Nós fixamos o conhecimento por repetição, os músculos de um praticante de artes marciais tem uma memória dos movimentos que ele precisa fazer porque ele os repetiu a exaustão e assim também é programar, você repete os passos seguidamente marcar o seu cérebro com aquele caminho, de forma que você já saiba exatamente pra onde vai assim que começa a escrever.</p>
<h2>Implementando</h2>
<p>Se você tem aprendizes sob a sua coordenação, aplique o Shu-Ha-Ri com eles. Ajude-os a desenvolver o conhecimento um passo de cada vez, mantenha-os no caminho pra que eles possam apreender o conhecimento de forma que eles apliquem o que foi aprendido corretamente. Você só tem a ganhar ao conseguir formar uma mão de obra mais qualificada e capaz de resolver os problemas.</p>
<p>Se você é um aprendiz, antes de tudo, tenha paciência. Você nunca vai chegar ao Ri sem ter passado pelos estágios anteriores. Não force o seu caminho entre as fases, pois isso só vai garantir que você falhe no futuro. Programar, assim como tudo o mais, exige esforço, repetição e interesse. </p>
<p>A grande vitória dessa minha experiência, não foi minha, mas de Pedro, que se esforçou além do normal pra atingir os objetivos. Leu livros, escreveu sobre as suas experiências e continua fazendo isso (se você continuar seguindo no blog dele, vai ver que ele continuou fazendo resumos sobre os outros livros). Ele poderia ter descansado e feito como todos os outros fazem, se esforçar apenas o suficiente pra ter o básico, mas é esse interesse em ir além que faz as melhores pessoas. Sucesso é muito mais persistência do que talento, já que talento sem ação nunca vai levar ninguém a lugar nenhum.</p>
<p>E você, quais são as suas experiências nisso?</p>
<h2>Apêndice</h2>
<p>E aqui segue a listinha de livros que ele recebeu (ao menos eu acho que tenham sido esses):</p>
<ul>
<li>
<a href="http://www.amazon.com/gp/product/0321603508/ref=as_li_ss_tl?ie=UTF8&#038;tag=ultimaspalavr-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399349&#038;creativeASIN=0321603508">Refactoring: Ruby Edition</a>
</li>
<li><a href="http://www.amazon.com/gp/product/0596007124/ref=as_li_ss_tl?ie=UTF8&#038;tag=ultimaspalavr-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399349&#038;creativeASIN=0596007124">Head First Design Patterns</a></li>
<li><a href="http://www.amazon.com/gp/product/0596008678/ref=as_li_ss_tl?ie=UTF8&#038;tag=ultimaspalavr-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399349&#038;creativeASIN=0596008678">Head First Object Oriented Analysis and Design</a></li>
<li><a href="http://www.amazon.com/gp/product/020161622X/ref=as_li_ss_tl?ie=UTF8&#038;tag=ultimaspalavr-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399349&#038;creativeASIN=020161622X">The Pragmatic Programmer</a></li>
<li><a href="http://www.amazon.com/gp/product/0321490452/ref=as_li_ss_tl?ie=UTF8&#038;tag=ultimaspalavr-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399349&#038;creativeASIN=0321490452">Design Patterns in Ruby</a></li>
<li><a href="http://www.amazon.com/gp/product/0321127420/ref=as_li_ss_tl?ie=UTF8&#038;tag=ultimaspalavr-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399349&#038;creativeASIN=0321127420">Patterns of Enterprise Application Architecture</a></li>
<li><a href="http://www.amazon.com/gp/product/1934356344/ref=as_li_ss_tl?ie=UTF8&#038;tag=ultimaspalavr-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399349&#038;creativeASIN=1934356344">The Passionate Programmer</a></li>
</ul>
<h2> Textos Relacionados </h2>
<ul>
<li> <a href="http://techbot.me/2011/01/agilidade-snake-oil-power-balances-e-a-falacia-do-nos-somos-diferentes/">Agilidade, snake oil, power balances e a falácia do “nós somos diferentes” </a></li>
<li><a href="http://techbot.me/2011/01/como-ser-chutado-de-uma-avaliacao-pra-uma-vaga-na-fase-de-curriculos/">Como ser chutado de uma avaliação pra uma vaga na fase de currículos</a></li>
<li><a href="http://techbot.me/2007/12/quem-quer-ser-gerente/">Quem quer ser gerente?</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://techbot.me/2011/05/criando-um-programador/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Como ser chutado de uma avaliação pra uma vaga na fase de currículos</title>
		<link>http://techbot.me/2011/01/como-ser-chutado-de-uma-avaliacao-pra-uma-vaga-na-fase-de-curriculos/</link>
		<comments>http://techbot.me/2011/01/como-ser-chutado-de-uma-avaliacao-pra-uma-vaga-na-fase-de-curriculos/#comments</comments>
		<pubDate>Thu, 27 Jan 2011 20:25:23 +0000</pubDate>
		<dc:creator>Maurício Linhares</dc:creator>
				<category><![CDATA[diversos]]></category>
		<category><![CDATA[currículos]]></category>
		<category><![CDATA[empregos]]></category>

		<guid isPermaLink="false">http://alinhavado.wordpress.com/?p=45</guid>
		<description><![CDATA[Aviso aos navegantes: esse texto foi publicado originalmente em 27/01/2011, a OfficeDrop não está mais recebendo currículos pra essas vagas. Essa semana anunciamos vagas pra trabalho Home Office pra OfficeDrop: Rails Developer Mac Application Developer Windows Application Developer Recebemos vários currículos, alguns bons e outros inacreditavelmente ruins. Erros crassos na construção do currículo ou completo [...]]]></description>
			<content:encoded><![CDATA[<!-- boo-widget start -->
          <script type="text/javascript">
            bb_keywords = "empregos";
            bb_bid  = "";
            bb_lang = "";
            bb_name = "custom";bb_limit = "7";bb_format = "bbc";
          </script>
          <script type="text/javascript" src="http://widgets.boo-box.com/javascripts/embed.js"></script>
          <!-- boo-widget end --><p><em><strong>Aviso aos navegantes: esse texto foi publicado originalmente em 27/01/2011, a OfficeDrop não está mais recebendo currículos pra essas vagas.</strong></em></p>
<p>Essa semana anunciamos vagas pra trabalho Home Office pra OfficeDrop:</p>
<ul>
<li><a href="http://www.officedrop.com/careers/rails-developer">Rails Developer</a></li>
<li><a href="http://www.officedrop.com/careers/mac-app-developer">Mac Application Developer</a></li>
<li><a href="http://www.officedrop.com/careers/windows-app-developer">Windows Application Developer</a></li>
</ul>
<p>Recebemos vários currículos, alguns bons e outros inacreditavelmente ruins. Erros crassos na construção do currículo ou completo desleixo com o mesmo fizeram com que vários candidatos fossem desconsiderados simplesmente porque era impossível retirar alguma informação útil do documento digital. O currículo ou continha lixo demais ou era tão direto que era impossível de se adivinhar quais eram as tarefas que a pessoa fazia no trabalho.</p>
<p>Resolvi então que seria uma boa hora pra fazer uma listinha dos erros que nós encontramos pra ficar pra posteridade.</p>
<p><span id="more-233"></span></p>
<h3>Apresentação</h3>
<p>Várias pessoas enviaram currículos como arquivos “.doc” do Office. Se você trabalha com tecnologia a tempo o suficiente pra saber utilizar mouse, teclado e ler ebooks provavelmente sabe que PDF é o formato padrão e mais bem aceito no mercado. Quando você envia um arquivo “.doc” e a pessoa abre no OpenOffice a formatação pode ser perdida ou ele simplesmente não funcionar e se não funcionar, você está fora. Sempre que for enviar um currículo, faça-o em PDF, não use os formatos do Office, OpenOffice e muito menos do Pages no Mac.</p>
<p>Vagas de tecnologia normalmente não exigem que você tenha uma boa aparência, então não é necessário colocar fotos. Sempre que vejo um currículo com fotos lembro do primeiro currículo que fiz aos 14 anos de idade. Também não use imagens ou logotipos de empresas das quais você usa as ferramentas. Quando você tira uma certificação Java, ganha o direito de usar a marca, mas isso não quer dizer que você deve encher o seu currículo com os logos do Java somente pra dizer que você é certificado. Quem é mais importante, a certificação ou você?</p>
<p>Ao enviar o seu currículo, pense que várias outras pessoas vão fazer o mesmo, então mandar um anexo com o nome “Curriculum Vitae” ou “Resume” dá mais trabalho pra o avaliador. Sempre coloque o seu nome no arquivo anexado, de forma que fique claro, mesmo sem abrir o documento, de quem é aquele currículo (como em “Currículo &#8211; José da Silva.pdf”).</p>
<p>As vagas pra OfficeDrop foram anunciadas em inglês, a empresa é americana e espera-se que o candidato seja capaz de se comunicar de forma escrita e falada em inglês (está na descrição de cada uma das vagas). Em que língua você acha que o seu currículo deve estar? Quando você não presta atenção na descrição da vaga, já sinaliza que pode ser apenas mais um “atirador de currículos” e provavelmente vai ser sumariamente cortado da avaliação. Se você não é capaz de traduzir o seu currículo pra inglês e recorre a ferramentas como o Google Translate pra fazer isso, você não se encaixa no “ser capaz de ler, escrever e falar inglês”.</p>
<h3>Experiência de trabalho</h3>
<p>Todas as vagas que nós publicamos exigem experiência prévia com programação e todas elas são pra trabalhar programando em tecnologias atuais, como Ruby, C# e Objective-C (essa nem tão atual né&#8230;), então a única coisa que nos interessa no seu currículo são trabalhos relacionados a programação. Se você dá aulas de religião na escolinha da sua igreja, isso não interessa (não nessa parte, vai interessar mais a frente).</p>
<p>O seu currículo deve começar com uma breve descrição das tecnologias que você já trabalhou e os papéis que já desempenhou nos seus diversos trabalhos e projetos.</p>
<p>Ao colocar experiência de trabalho, não escreva somente:</p>
<blockquote><p>“Janeiro 2008, Dezembro 2010” – Analista de sistemas, FooBar Tecnologia</p></blockquote>
<p>Descreva em algumas linhas o que você fazia, quais tecnologias você utilizava, o que foi desenvolvido no projeto. Explique quais eram as suas atribuições e, principalmente, tenha certeza de que tudo o que você colocou no seu resumo de experiências esteja citado nas suas experiências de trabalho. Se você coloca que “sabe” Rails e nenhuma das suas experiências diz que você já usou Rails, vou ter que duvidar das suas palavras.</p>
<p>Se você trabalha há muito tempo e teve empregos não relacionados a programação, o melhor é não coloca-los no seu currículo, porque eles vão somente aumentar o tamanho dele e terminam por não dizer nada de você. Quanto maior o seu currículo, maiores as chances do avaliador se chatear e começar a “pular” partes dele.</p>
<h3>Eventos</h3>
<p>Isso é um dos comentários mais antigos sobre o assunto, mas as pessoas continuam cometendo o mesmo erro, o valor de você “ir” a um evento é basicamente nulo. Por que? Porque é muito difícil de se mensurar o conhecimento adquirido e, principalmente, todo mundo sabe que ninguém vai a evento somente pra assistir palestras. Quer colocar que participou de um evento no seu currículo? Demonstre que ter participado desse evento teve algum efeito na sua formação profissional com código.</p>
<p>Já se você palestrou ou ministrou cursos durante o evento, você deve colocar isso no seu currículo, junto com links pra Slideshare se você tiver usado, projeto no GitHub desenvolvido durante o curso/palestra ou qualquer outro material que você tenha usado durante a apresentação. Dizer que você falou sobre “A dicotomia capitalista de Atirei o Pau no Gato” sem nenhum material de suporte também é inútil.</p>
<h3>Cursos complementares</h3>
<p>Quando além da sua formação acadêmica você fez cursos complementares, aqui vale a mesma regrinha da experiência de trabalho, só coloque cursos que estejam relacionados ao trabalho que você vai executar na vaga. Vários currículos que nós recebemos tinham “Curso de montagem e manutenção de computadores” ou “Operador Office: Word, Excel e Power Point”.</p>
<p>Se você não fez nenhum curso relacionado ao assunto da vaga, não coloque nada. Controle esse seu ímpeto de querer fazer o maior currículo do mundo, quanto maior o seu currículo, menores as chances de você ser selecionado, porque provavelmente vai ser tachado de enrolão.</p>
<p>Um plus pra cursos é, assim como nos eventos, você ter material pra mostrar com base no que aprendeu no curso. Fez um curso de desenvolvimento pra iOS? Faça um projetinho com o que você aprendeu e coloque no GitHub. Isso demonstra que você não estava somente de corpo presente nas aulas.</p>
<h3>Certificações</h3>
<p>Em empresas menos enterprisey, certificações já não tem lá muito valor, mas mesmo assim são uma fonte interessante de dados sobre a pessoa no currículo. Todo mundo tem uma certificação SCJP, mas quase ninguém tem uma SCEA, colocar uma SCEA no currículo realmente faz diferença.</p>
<p>Evite colocar certificações pouco conhecidas ou de sites obscuros, como as do “RentACoder.com”, “Brainbench” ou correlatos. Ninguém conhece e, mais uma vez, parece que você quer “gerar conteúdo” pra o seu currículo em vez de realmente demonstrar conhecimentos na área.</p>
<h3>Github, side-projets e hobbys</h3>
<p>Se você tem conta no GitHub, só coloque ela como “referencia” no seu currículo se você realmente tem alguma coisa própria lá. Se tudo o que você fez foi dar forks em milhares de projetos e nunca deu commit em nenhum deles, é melhor nem colocar o link. Se você tem side projects, é interessante colocar eles também no seu currículo falando o que fez, pra que fez e se ainda está mantendo o projeto.</p>
<p>Algumas empresas também gostam de saber se as pessoas que estão sendo contratadas são normais e fazem outras coisas da vida além de trabalhar. Então, dependendo do perfil da vaga, você pode adicionar uma seção de hobbys no seu currículo, falando que você dá aulas na escola dominical da igreja, gosta de jogar vídeo game, surfar ou whatever.</p>
<h3>Fechando</h3>
<p>Pense no seu currículo como a primeira impressão que você vai causar e pense que ele nem pode ser pequeno demais pra não dizer nada nem deve ser grande demais pra não parecer enrolação. Currículos com até 3 folhas são aceitáveis, depois disso pode ter certeza de que o avaliador não está mais olhando pro conteúdo e está contando os minutos pra esse sofrimento acabar.</p>
<p>Seja direto, coloque informações que qualifiquem você pra vaga em questão e preste atenção nas entrelinhas da vaga, pra não enviar um currículo em português pra uma empresa americana. Mas não se empolgue, o currículo é somente pra que você seja considerado pra vaga, a entrevista e os assigments que vão ser passados é quem vão dizer se você realmente vai chegar lá ou não, mas é sempre bom causar uma boa primeira impressão.</p>
<h2>Textos Relacionados</h2>
<ul>
<li> <a href="http://techbot.me/2011/01/agilidade-snake-oil-power-balances-e-a-falacia-do-nos-somos-diferentes/">Agilidade, snake oil, power balances e a falácia do “nós somos diferentes” </a></li>
<li><a href="http://techbot.me/2011/05/criando-um-programador/">Criando um programador</a></li>
<li><a href="http://techbot.me/2007/12/quem-quer-ser-gerente/">Quem quer ser gerente?</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://techbot.me/2011/01/como-ser-chutado-de-uma-avaliacao-pra-uma-vaga-na-fase-de-curriculos/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Handling various rubies at the same time in your machine with RVM – Ruby Version Manager</title>
		<link>http://techbot.me/2011/01/handling-various-rubies-at-the-same-time-in-your-machine-with-rvm-%e2%80%93-ruby-version-manager/</link>
		<comments>http://techbot.me/2011/01/handling-various-rubies-at-the-same-time-in-your-machine-with-rvm-%e2%80%93-ruby-version-manager/#comments</comments>
		<pubDate>Tue, 25 Jan 2011 17:34:50 +0000</pubDate>
		<dc:creator>Maurício Linhares</dc:creator>
				<category><![CDATA[ruby]]></category>
		<category><![CDATA[bundler]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[rvm]]></category>

		<guid isPermaLink="false">http://codeshooter.wordpress.com/?p=162</guid>
		<description><![CDATA[If you’ve been working in Ruby for more than a year you have probably seen a lot of changes in the landscape. We saw a lot of gems adding compatibility layers to run on Ruby 1.9.2, Rails 3 was finally released (also supporting 1.9.2) and new gems using 1.9.2 features are also showing up. But [...]]]></description>
			<content:encoded><![CDATA[<!-- boo-widget start -->
          <script type="text/javascript">
            bb_keywords = "rvm";
            bb_bid  = "";
            bb_lang = "";
            bb_name = "custom";bb_limit = "7";bb_format = "bbc";
          </script>
          <script type="text/javascript" src="http://widgets.boo-box.com/javascripts/embed.js"></script>
          <!-- boo-widget end --><div id="attachment_160" class="wp-caption alignleft" style="width: 132px"><a href="http://www.amazon.com/gp/product/0596516177?ie=UTF8&amp;tag=ultimaspalavr-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0596516177"><img class="size-full wp-image-160" title="Dig deeper into Ruby with this book" src="http://techbot.me/wp-content/uploads/2011/01/ruby1.jpg" alt="Dig deeper into Ruby with this book" width="122" height="160" /></a><p class="wp-caption-text">Dig deeper into Ruby with this book</p></div>
<p>If you’ve been working in Ruby for more than a year you have probably seen a lot of changes in the landscape. We saw a lot of gems adding compatibility layers to run on Ruby 1.9.2, Rails 3 was finally released (also supporting 1.9.2) and new gems using 1.9.2 features are also showing up.</p>
<p>But guess what? You’re stuck at 1.8 (1.8.7 if you’re lucky) for a lot of projects and you’re in deep fear that installing the latest Ruby 1.9.2 to try all these new fancy things is going to wreak havoc on your environment and you’re going to be <a href="http://en.wikipedia.org/wiki/FUBAR">FUBAR</a>.</p>
<p>Worse, you still have projects running on Ruby 1.8.6 (with that nasty SMTP-TLS bug) and if you upgrade to a newer Ruby you might have false positives in your codebase and things are going to break in production. You’re already FUBAR, you might think.</p>
<p>But fear not! There’s a knight in shiny armor riding for his damsel in distress! (yes, YOU) And this knight is <a href="http://rvm.beginrescueend.com/">RVM</a>!</p>
<p><span id="more-162"></span></p>
<p>RVM is a set of bash scripts, written by <a href="http://twitter.com/#!/wayneeseguin">Wayne E. Seguin</a> (and others) that allows you to do just that, install various different Rubies on your machine in a way that they do not conflict with each other. You can even go farther and install different “gem groups” for every project you have so that each one lives in it’s own sandbox without breaking the dependencies or installing gems that are needed for only one application.</p>
<p>Here you’ll learn to setup, install and do the basic workflow when using RVM on your daily development.</p>
<h3>Installing RVM</h3>
<p>First make sure that your system has a sane build environment by installing compilers/make/whatever it is needed in your OS to build source code. If you’re on an Ubuntu machine, the “build-essential” package should do it, on a Mac it’s the XCode Unix Tools. Once you’re done with that, install Git and from a console type:</p>
<pre class="brush:shell">bash &lt;&lt; ( curl http://rvm.beginrescueend.com/releases/rvm-install-head )</pre>
<p>Once finished, open up your “.bash_profile” (or equivalent) and append this to the end of it:</p>
<pre class="brush:shell">[[ -s "$HOME/.rvm/scripts/rvm" ]] &amp;&amp; . "$HOME/.rvm/scripts/rvm"</pre>
<p>Close your console and open it again, type “rvm -v” and you should see the version info for your RVM install.</p>
<h3>Installing Rubies</h3>
<p>Once you have RVM ready, the first thing to do is to start installing the rubies you’ll want to use. Different than what you might be used to, RVM installs the rubies and gems in your user’s directory (under “~/.rvm”), so you will NEVER use “sudo” when installing anything with RVM. Everything is always installed in your user’s home directory (you could possibly install RVM as root, but you <strong>DO NOT WANT </strong>to do that, trust me).</p>
<p>Let’s start by installing Ruby 1.8.7, Ruby 1.9.2, <a href="http://www.rubyenterpriseedition.com/">Ruby Enterprise Edition</a> and <a href="http://www.jruby.org/">JRuby</a>:</p>
<pre class="brush:shell">rvm install ruby-1.8.7,ruby-1.9.2,ree,jruby</pre>
<p>This is going to take a while as RVM is going to pull the source code for all these rubies and build them in your environment. Once it’s over, tell RVM which one is your “default” Ruby (the one that’s going to be loaded by default on your consoles). Let’s use “ruby-1.8.7” as the default:</p>
<pre class="brush:shell">rvm  --default ruby-1.8.7</pre>
<p>Now, every console that opens up will use the Ruby 1.8.7. If you want to switch to another Ruby, here’s how you’d do it:</p>
<pre class="brush:shell">rvm ruby-1.9.2</pre>
<p>To see which Rubies you have installed currently:</p>
<pre class="brush:shell">rvm list</pre>
<h3>Installing gems</h3>
<p>Before start installing gems you should probably fine tune your “~/.gemrc” file by telling Rubygems not to generate “ri” or “rdoc”, this will absurdly speed up gem installs (unless you really want that). Here&#8217;s my &#8220;~/.gemrc&#8221; file (the most important configs are &#8220;install&#8221; and &#8220;update&#8221;):</p>
<pre><code>---
:verbose: true
:bulk_threshold: 1000
install: --no-ri --no-rdoc --env-shebang
update: --no-ri --no-rdoc --env-shebang
:sources:
- http://gemcutter.org
- http://gems.rubyforge.org/
- http://gems.github.com
:benchmark: false
:backtrace: false
:update_sources: true</code></pre>
<p>Now that you’re done, each Ruby that you installed comes with it’s own separate Rubygems environment, which means you can install gems in one Ruby and it isn’t going to affect the other ones. With RVM you can even go further and define “gemsets” that are groups of gems that are independent even from the “main” gems available for that specific Ruby.</p>
<p>The way you’ll work with your rubies and gemsets depends on what you really want. I, personally, love to have a gemset for every single application and that’s what I’m going to talk about now, I hope this workflow works for you too.</p>
<p>First thing to do is to define which are the most common needs you’re going to have using these rubies. In my case, I have Ruby 1.8.7 for my Rails 2.3 projects, so I’ll install Rails and some other common gems on it:</p>
<pre class="brush:shell">rvm  ruby-1.8.7
gem install rails –v 2.3.10
gem install will_paginate nokogiri mysql sunspot sunspot_rails</pre>
<p>Install here only the gems you believe you’re going to use on most of your projects.</p>
<p>Now I’d like to have the Ruby 1.9.2 for Rails 3 development, so I’m going to install the latest Rails on it:</p>
<pre class="brush:shell">rvm ruby-1.9.2
gem install rails nokogiri mysql2</pre>
<p>Once you have the “base” gems ready for every Ruby, you can start defining “gem sets” for your projects. The best way to do this is to create a “.rvmrc” file at the root directory of every project, here’s how the file would look like:</p>
<pre class="brush:shell">rvm ruby-1.9.2@my_application --create</pre>
<p>Once you enter the directory that has the “.rvmrc” file, RVM will look at it and ask you if you want to “trust” the file, once you select “yes”, it’s going to create the gemset named in this file (in our case “my_application” using Ruby 1.9.2). RVM automatically switches to the Ruby and the gemset defined in the file whenever you enter the directory, you don’t have to do anything.</p>
<p>Now, if you run a “gem list” inside this directory you’ll see that there are only two gems in this gemset, “bundler” and “rake”. Not really super, right? But we have already installed Rails 3 in the main Ruby 1.9.2 path, so we can easily migrate them to this new gemset, here’s how you’d do it:</p>
<pre class="brush:shell">rvm gemset copy ruby-1.9.2 ruby-1.9.2@my_application</pre>
<p>This will get all gems we placed in the base ruby-1.9.2 install and copy them over to our application’s gemset. You could even copy the gems from another gemset that you already have.</p>
<p>The beauty in using “.rvmrc” files and gemsets for you projects is that once you push this file to your source control system, everyone that accesses the project will be “forced” to have a gemset and it’s surely going to be easier to manage dependencies.</p>
<p>Also, if you’re combining RVM with <a href="http://gembundler.com/">Bundler</a> you’ll have a much more reliable and repeatable environment, you can just move this project anywhere, create the gemset, install the necessary gems with Bundler and just start coding.</p>
<h3>Conclusion</h3>
<p>RVM is one of those tools you MUST have in your toolset when writing Ruby applications, specially if you have to handle many different Rubies, projects or environments and don’t want to start growing white hair (or worse, LOSE your hair) while doing it.</p>
<h2>Related Posts</h2>
<ul>
<li> <a href="http://techbot.me/2011/01/full-text-search-in-in-rails-with-sunspot-and-solr/">Full text search in in Rails with Sunspot and Solr </a> </li>
<li> <a href="http://techbot.me/2010/08/deployment-recipes-deploying-monitoring-and-securing-your-rails-application-to-a-clean-ubuntu-10-04-install-using-nginx-and-unicorn/">Deployment Recipes – Deploying, monitoring and securing your Rails application to a clean Ubuntu 10.04 install using Nginx and Unicorn</a> </li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://techbot.me/2011/01/handling-various-rubies-at-the-same-time-in-your-machine-with-rvm-%e2%80%93-ruby-version-manager/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Full text search in in Rails with Sunspot and Solr</title>
		<link>http://techbot.me/2011/01/full-text-search-in-in-rails-with-sunspot-and-solr/</link>
		<comments>http://techbot.me/2011/01/full-text-search-in-in-rails-with-sunspot-and-solr/#comments</comments>
		<pubDate>Thu, 13 Jan 2011 22:52:23 +0000</pubDate>
		<dc:creator>Maurício Linhares</dc:creator>
				<category><![CDATA[ruby]]></category>
		<category><![CDATA[ruby on rails]]></category>
		<category><![CDATA[solr]]></category>
		<category><![CDATA[databases]]></category>
		<category><![CDATA[full text search]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[sunspot]]></category>

		<guid isPermaLink="false">http://codeshooter.wordpress.com/?p=134</guid>
		<description><![CDATA[Click here if you want to see a PDF version of this tutorial. Full source code for this tutorial is available at GitHub. Everyone wants to take their databases to run everything as fast as possible. We usually say query less, add more caching mechanisms, add indexes to the columns being searched, but another solution [...]]]></description>
			<content:encoded><![CDATA[<!-- boo-widget start -->
          <script type="text/javascript">
            bb_keywords = "sunspot";
            bb_bid  = "";
            bb_lang = "";
            bb_name = "custom";bb_limit = "7";bb_format = "bbc";
          </script>
          <script type="text/javascript" src="http://widgets.boo-box.com/javascripts/embed.js"></script>
          <!-- boo-widget end --><p><div id="attachment_147" class="wp-caption alignleft" style="width: 140px"><a href="http://www.amazon.com/gp/product/1847195881?ie=UTF8&amp;tag=ultimaspalavr-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=1847195881"><img src="http://techbot.me/wp-content/uploads/2011/01/solr.jpg" alt="The book you should get to dig deeper into Solr" title="The book you should get to dig deeper into Solr" width="130" height="160" class="size-full wp-image-147" /></a><p class="wp-caption-text">The book you should get to dig deeper into Solr</p></div><a href="http://www.slideshare.net/mauricio.linhares/full-text-search-in-in-rails-with-sunspot-and-solr?from=embed">Click here if you want to see a PDF version of this tutorial.</a></p>
<p><a href="https://github.com/mauricio/sunspot_tutorial">Full source code for this tutorial is available at GitHub.</a></p>
<p>Everyone wants to take their databases to run everything as fast as possible. We usually say query less, add more caching mechanisms, add indexes to the columns being searched, but another solution is not to use the database at all and look for better solutions for your querying needs.</p>
<p><span id="more-134"></span></p>
<p>When querying for text in our databases, we’re often doing “LIKE” searches. Like searches are only performant if we have an index in that field and the query is written in a way that the index is used. Imagine that you have a field “name” and it contains the text “Battlestar Galactica”. This query would be able to run and use the index:</p>
<pre class="brush:sql">SELECT p.* FROM products p WHERE p.name LIKE “Battlestar%”</pre>
<p>The database would be able to optimize this query and use the index to find the expected row. But, what if the query was like this one:</p>
<pre class="brush:sql">SELECT p.* FROM products p WHERE p.name LIKE “%Galactica”</pre>
<p><div id="attachment_135" class="wp-caption alignright" style="width: 310px"><a href="http://techbot.me/wp-content/uploads/2011/01/morningstar.png"><img src="http://techbot.me/wp-content/uploads/2011/01/morningstar.png?w=300" alt="Your DBA getting ready to hit you" title="Your DBA getting ready to hit you" width="300" height="250" class="size-medium wp-image-135" /></a><p class="wp-caption-text">Your DBA getting ready to hit you</p></div>Database indexes usually match from left to right, so, unless you have a nasty trick under your sleeve, this query will just look at ALL the rows in the products table and perform a match on every “name” column before returning a result. And that’s Really Bad News for you, as the DBA will probably come for you holding a Morning Star to beat you badly. So, querying with “LIKE” when you what you need is full text search isn’t nice.</p>
<p>That’s where full text search based solutions come in for help. Tools like <a href="http://lucene.apache.org/solr/">Solr</a> allow you to perform optimized text searches, filter input, categorization and even features like Google’s “Did you mean?”.</p>
<p>In this tutorial you’ll learn how to add full text searching capabilities to your <a href="http://rubyonrails.org/">Rails</a> application using <a href="https://github.com/outoftime/sunspot">Sunpot</a> and Solr. We will also delve a little bit into Solr’s configuration and learn how to use specific tokenizers to clear input, perform partial matching of words and faceting results.</p>
<p>This project uses Rails 3 and Ruby 1.9.2, you’ll find a <a href="http://gembundler.com/">Gemfile</a> and and “.rvmrc” with all dependencies declared, it should be pretty easy to follow or setup your environment based on it (if you’re not using <a href="http://rvm.beginrescueend.com/">RVM</a>, that’s a GREAT time to learn using it).</p>
<p>You can possibly follow this tutorial with a previous Rails version and without Bundler or RVM, given all models and most of the code will look exactly the same in Rails 2 and Sunspot is compatible to Rails 2 too.</p>
<p>The source code for this example application is available at GitHub <a href="https://github.com/mauricio/sunspot_tutorial">here</a>.</p>
<h3>Starting the engines</h3>
<p>Download the Sunspot <a href="https://github.com/outoftime/sunspot">source code from Github</a>.</p>
<p>Enter the project folder and go to “sunspot/solr-1.3”, inside that folder you should see a “solr” folder, copy this folder into your project’s folder. This is where the general Solr configuration is going to live, don’t worry about these files just yet, we’ll get to them later in this tutorial.</p>
<p>Now create a “sunspot.yml” file under your project’s “config” folder, here’s a sample:</p>
<h3><a href="https://github.com/mauricio/sunspot_tutorial/blob/master/config/sunspot.yml">Listing 1 – sunspot.yml</a></h3>
<pre><code>development:
  solr:
    hostname: localhost
    port: 8980
    log_level: INFO
  auto_commit_after_delete_request: true

test:
  solr:
    hostname: localhost
    port: 8981
    log_level: OFF

production:
  solr:
    hostname: localhost
    port: 8982
    log_level: WARNING
  auto_commit_after_request: true  </code></pre>
<p>You can have different configurations for every environment you’re running. To see all configuration options, go to the Sunspot source code and head to the <em>“sunspot_rails/lib/sunspot/rails/configuration.rb”</em> file.</p>
<p>Now we’ll create two models, <strong>Product</strong> and <strong>Category</strong>, so let’s start by creating the migration that will setup them:</p>
<pre class="brush:shell">rails g migration create_base_tables</pre>
<h4>Listing 2 – create_base_tables.rb</h4>
<pre class="brush:ruby">class CreateBaseTables &lt; ActiveRecord::Migration

  def self.up
    create_table :categories do |t|
      t.string :name, :null =&gt; false
    end

    create_table :products do |t|
      t.string  :name, :null =&gt; false
      t.decimal :price, :scale =&gt; 2, :precision =&gt; 16, :null =&gt; false
      t.text    :description
      t.integer :category_id, :null =&gt; false
    end

    add_index :products, :category_id

  end

  def self.down
    drop_table :categories
    drop_table :products
  end

end
</pre>
<p>Now we move on to the basic models, starting with the <strong>Category</strong> model:</p>
<h4>Listing 3 – category.rb</h4>
<pre class="brush:ruby">class Category &lt; ActiveRecord::Base

  has_many :products

  validates_presence_of :name
  validates_uniqueness_of :name, :allow_blank =&gt; true

  searchable :auto_index =&gt; true, :auto_remove =&gt; true do
    text :name
  end

  def to_s
    self.name
  end

end
</pre>
<p>Here in the Category class we see our first reference to Sunspot, the “searchable” method, where we configure the fields that should be indexed by Solr. At the Category class, there’s only one field that’s useful at this moment, the “name”, so we tell Sunspot to configure the field name to be indexed as “text” (you usually don’t want your text indexed as “string”, as it will only be a hit in a full match).</p>
<p>The :auto_index and :auto_remove options are there to let Sunspot automatically send your model to be indexed at Solr when it is created/updated/destroyed. The default is “false” for both values, which means you have to manually send your data to Solr and unless you really want to do that, you should keep both of these values as “true” in your models.</p>
<p>Now lets look at the <strong>Product</strong> class:</p>
<h4>Listing 4 – product.rb</h4>
<pre class="brush:ruby">class Product &lt; ActiveRecord::Base

  belongs_to :category

  validates_presence_of :name, :description, :category_id, :price
  validates_uniqueness_of :name, :allow_blank =&gt; true

  searchable :auto_index =&gt; true, :auto_remove =&gt; true do
    text :name, :boost =&gt; 2.0
    text :description
    float :price
    integer :category_id
  end

  def to_s
    self.name
  end

end
</pre>
<p>In our Product class things are a little bit different, we have more fields (and more kinds) being indexed. “float” and “integer” are pretty self explanatory, but the “name” field has some black magic floating around, with the “boost” parameter. Boosting a field when indexing means that if the match is in that specific field, it has more “relevance” than if found somewhere else.</p>
<p>Imagine that you’re looking for Iron Maiden’s “Powerslave” album. You go to Iron Maiden’s Online Store and search for “powerslave”, hoping that the album will be the first hit, but then you see “Live After Dead” before “Powerslave”. Why did it happen? The “Live After Dead” album contains the “Powerslave” song in it’s track listing, so it’s a match as much as the real “Powerslave” album. What we need here is to tell the search tool that if a match is on an album name, it has higher relevance than if the hit is in the track listing.</p>
<p>Boosting allows you to reduce these issues. Some fields are inherently more important than others and you can tell that to Solr by configuring a “:boost” value for them. When something matches on them, the relevance of that match will be improved and it should come up before the other results in search.</p>
<h3>Searching</h3>
<p>Now let’s take a look at the ProductsController to see how we perform the search:<br />
 </p>
<h4>Listing 4 – products_controller.rb</h4>
<pre class="brush:ruby">class ProductsController &lt; ApplicationController

  def index
    @products = if params[:q].blank?
      Product.all :order =&gt; 'name ASC'
    else
      Product.solr_search do |s|
        s.keywords params[:q]
      end
    end
  end

end
</pre>
<p>As you can see, searching is quite simple, you just call the solr_search method and send in the text to be searched for. One thing that I don’t like about Sunspot is that searches do not return an Array like object, you get a Sunspot::Search::StandardSearch object that has, as a property, the results array which contains the records returned by the search.</p>
<p>Here’s a simple way to fix this issue (I usually place the contents of this file inside an initializer in “config/initializers”):</p>
<h4>Listing 5 – sunspot_hack.rb</h4>
<pre class="brush:ruby">::Sunspot::Search::StandardSearch.class_eval do

  include Enumerable

  delegate(
    :current_page,
    :per_page,
    :total_entries,
    :total_pages,
    :offset,
    :previous_page,
    :next_page,
    :out_of_bounds?,
    :each,
    :in_groups_of,
    :blank?,
    :[],
    :to =&gt; :results)

end
</pre>
<p>This simple monkeypatch makes the search object itself behave like an Enumerable/Array and you can use it to navigate directly in the results, without having to call the “results” method. The methods usually used by will_paginate helpers are also included so you can pass this object to a will_paginate call in your view and it’s just going to work.</p>
<h3>Indexing</h3>
<p>Now that all the models are in place, we can start fine tuning the Solr indexing process. First thing to understand here is what happens when you send text to be indexed by Solr, let’s get into the tool, starting the server:</p>
<pre class="brush:shell">rake sunspot:solr:run</pre>
<p>This rake task starts Solr in the foreground (if you wanted to start it in the background, you’d use “sunspot:solr:start”). With Solr running, you should add some data to the database, this tutorial’s project on Github contains a <a href="https://github.com/mauricio/sunspot_tutorial/blob/master/db/seeds.rb">“seed.rb”</a> file with some basic data for testing, just copy it over your project.</p>
<p>Also copy the <a href="https://github.com/mauricio/sunspot_tutorial/blob/master/lib/tasks/db.rake">“lib/tasks/db.rake</a>” from the project to your project, it contains a “db:prepare” task that truncates the database, seeds it and then indexes all items in Solr and we’re doing to be reindexing data a lot.</p>
<p>With everything copied, run the “db:prepare” task:</p>
<pre class="brush:shell">rake db:prepare</pre>
<p>This will add the categories and products to your database and also index them in Solr. If this task did run successfully, head to the Solr administration interface, at this URL:</p>
<p><a href="http://localhost:8980/solr/admin/schema.jsp">http://localhost:8980/solr/admin/schema.jsp</a></p>
<p>Once you go to it, click on the “FIELDS”, then on “NAME_TEXT”, you should see a screen just like the one in image 1:<br />
<div class="wp-caption alignnone" style="width: 1033px"><img alt="Image 1 – Solr schema browser" src="http://img.skitch.com/20110113-esa6g3uwcxu1fcn93kdah2yd13.jpg" title="Image 1 – Solr schema browser" width="1023" height="718" /><p class="wp-caption-text">Image 1 – Solr schema browser</p></div></p>
<p>If you don’t see all the fields that are available in this image, your “rake db:prepare” command has probably failed or Solr wasn’t running when you called it.</p>
<p>What we see here is the information about the fields we’re indexing. This specific field contains all data from the name properties from both Category and Product classes, as you can notice from the top 10 terms.</p>
<p>The name field is not indexed by it’s full content, as a relational database would usually do, the text is broken into tokens, by the solr.StandardTokenizerFactory class in Solr. This class receives our text, like <a href="http://www.amazon.com/gp/product/1589944607?ie=UTF8&amp;tag=ultimaspalavr-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=1589944607">“Battlestar Galactica: The Boardgame”</a> and turns it into:</p>
<pre class="brush:ruby">[“Battlestar”, “Galactica”, “The”, “Boardgame”]</pre>
<p>This is what gets indexed and, ultimately, searched by Solr. If you open the web application now and try to search for “battle”, you won’t have any matches. If you search for “Battlestar”, you get the two products that match the name.</p>
<p>Everything when indexing information in Solr revolves around building the best “tokens” available for your input. You have to teach Solr to crunch your data in a way that makes sense and makes it easy to search for, and adding filters to the indexing process does this. While in the same page as Image 1 above, click on the “DETAILS” links as shown in Image 2:</p>
<div class="wp-caption alignnone" style="width: 563px"><img alt="Image 2 – Viewing the analysis and search filters" src="http://img.skitch.com/20110113-939xbwkq9ehtkea5d6q5cnksp.jpg" title="Image 2 – Viewing the analysis and search filters" width="553" height="546" /><p class="wp-caption-text">Image 2 – Viewing the analysis and search filters</p></div>
<p>Each field in Solr has two analyzers, one is the “index” analyzer, that prepares the input to be indexed and the other is the “query” analyzer that prepares the search input to finally perform a search. Unless you have some special need, both of them are usually the same.</p>
<p>In our current configuration, we have the same two filters for both of the analyzers. The StandardFilterFactory filter removes punctuation characters from our input (the “:” in “Battlestar Galactica: The Boardgame” is not in our tokens) and the LowerCaseFilterFactory makes all input lowercased so we can search with “baTTle”, “BATTLE”, “BaTtLe” and they’re all going to work.</p>
<p>Before we move on to add more filters to our analyzers, let’s take a look at the analyzer screen in Solr Admin at &#8211; <a href="http://localhost:8980/solr/admin/analysis.jsp?highlight=on">http://localhost:8980/solr/admin/analysis.jsp?highlight=on</a></p>
<p>In this screen we see how our input is going to be transformed into tokens by the configured analyzers.</p>
<div class="wp-caption alignnone" style="width: 1029px"><img alt="Image 3 – Solr analyzer page" src="http://img.skitch.com/20110113-ncqkyrgrn4us5fbxn4p727t5f3.jpg" title="Image 3 – Solr analyzer page" width="1019" height="540" /><p class="wp-caption-text">Image 3 – Solr analyzer page</p></div>
<p>In this screen we have selected the “name_text” field in Solr. In the “Field value (Index)” you enter the values you’re sending to be indexed, just like you would send from your model property, in the “Field value (Query)” you enter the values you’d use to search.</p>
<p>Once you type and hit “Analyze” you should see the output just below the form as we see in Image 3. This output shows how your input is transformed into tokens by the tokenizer and filters, this way you can easily experiment by adding more filters and seeing if the output really matches the way you’d expect it to. This analysis view is your best friend when debugging search/indexing related issues or trying out ways to improve the way Solr indexes and matches your data.</p>
<h3>Customizing fields</h3>
<p>Now that you have an idea about how the indexing and searching process work, let’s start to customize the fields in Solr, open up the “solr/conf/schema.xml” file and look for this reference:</p>
<h4>Listing 6 – solr/conf/schema.xml except</h4>
<pre class="brush:xml">&lt;fieldtype class=&quot;solr.TextField&quot; positionIncrementGap=&quot;100&quot; name=&quot;text&quot;&gt;
      &lt;analyzer&gt;
        &lt;tokenizer class=&quot;solr.StandardTokenizerFactory&quot;/&gt;
        &lt;filter class=&quot;solr.StandardFilterFactory&quot;/&gt;
        &lt;filter class=&quot;solr.LowerCaseFilterFactory&quot;/&gt;
      &lt;/analyzer&gt;
    &lt;/fieldtype&gt;
</pre>
<p>If you look at Image 1, where we saw the “name_text” configuration, you’ll see that the field type is “text”, this except above is the configuration for all fields of type “text”, which means that if we add more filters here we’ll affect all fields of this type. This greatly simplifies the way we configure the tool, as we don’t have to define explicit configurations for every single field that our models have, we can just reuse this same “text” config for all fields that are supposed to be indexed as text.</p>
<p>But that’s a lot of talking, let’s get into action!</p>
<p>Let’s start the job by looking at our indexed data from before:</p>
<pre class="brush:ruby">[“battlestar”, “galactica”, “the”, “boardgame”]</pre>
<p>The “the” is mostly useless, as it’s going to be available in almost all properties and no one is ever going to search for “the” (oh yeah, there might be that ONE guy that does it). In Information Retrieval lingo, “the” is a stop word, it usually doesn’t have meaning by itself and doesn’t represent valuable information for our indexer, removing all stop words from your input improves performance and the relevance of your results.</p>
<p>Given that this is a common operation, Solr already contains a filter that’s capable of removing all stop words from your data, the solr.StopFilterFactory, let’s see how we can add it to our config:</p>
<h4>Listing 7 – solr/config/schema.xml except</h4>
<pre class="brush:xml">&lt;fieldtype class=&quot;solr.TextField&quot; positionIncrementGap=&quot;100&quot; name=&quot;text&quot;&gt;
  &lt;analyzer&gt;
    &lt;tokenizer class=&quot;solr.StandardTokenizerFactory&quot;/&gt;
    &lt;filter class=&quot;solr.StandardFilterFactory&quot;/&gt;
    &lt;filter class=&quot;solr.LowerCaseFilterFactory&quot;/&gt;
    &lt;filter class=&quot;solr.StopFilterFactory&quot; words=&quot;stopwords.txt&quot; ignoreCase=&quot;true&quot;/&gt;
    &lt;filter class=&quot;solr.ISOLatin1AccentFilterFactory&quot;/&gt;
    &lt;filter class=&quot;solr.TrimFilterFactory&quot; /&gt;
  &lt;/analyzer&gt;
&lt;/fieldtype&gt;
</pre>
<p>If you look at the “solr/config” folder you’ll se a “stopwords.txt” file that already contains most of the common stop words in English, you can add or remove words from there as needed and if you’re not indexing English text you can just remove the English names and add your language’s stop words. Now change this in your “solr/config/schema.xml” file and stop and start Solr again and open the analyzer:</p>
<div class="wp-caption alignnone" style="width: 1021px"><a href="http://img.skitch.com/20110113-8c4ynujuesm1w991nxfbs8dnme.jpg"><img alt="Image 4 – Solr analyzer page " src="http://img.skitch.com/20110113-8c4ynujuesm1w991nxfbs8dnme.jpg" title="Image 4 – Solr analyzer page " width="1011" height="710" /></a><p class="wp-caption-text">Image 4 – Solr analyzer page </p></div>
<p>As you can see, in the last step, the “the” was removed from both the index input and the query input, we’re maintaining only the pieces of information that are really useful, this makes our index smaller and also speeds up searching.</p>
<p>While you were not looking, we have also added two other filters, solr.ISOLatin1AccentFilterFactory, that removes accents from words in Latin based languages, like Portuguese. If the input is “não”, it becomes “nao”. And after that there’s solr.TrimFilterFactory, that removes unnecessary spaces from our tokens.</p>
<h3>Partial matching</h3>
<p>Another pretty common need is to be able to match only a part of a word, usually a prefix. In the beginning of the tutorial, we saw that searching for “battle” doesn’t yield any results, while “battlestar” does. This happens because Solr, by default, only sees a match if it’s a full match. The word you entered must be exactly the same as a token that’s available in the index, if there is no exact match, Solr you tell you that there are no results.</p>
<p>If you look at <a href="http://lucene.apache.org/java/2_9_1/queryparsersyntax.html">Lucene’s Query Parser Syntax</a>  (Solr is somewhat a web interface to Lucene) you’ll see that you can use the “*” operator to perform a partial match. We could then search for “battle*” and this would yield the results we expect, but doing this kind of partial matching is slow and could possibly become a bottleneck for your application, so we have to figure out another way to do this.</p>
<p>When all you need is prefixed partial matching, the solr.EdgeNGramFilterFactory is your best friend. It will break words into pieces that will then be added to the index, so it looks like you have partial matching, but in fact the partials are tokens by themselves in the index, let’s see how our config would look like in this case:</p>
<h4>Listing 8 – solr/config/schema.xml except</h4>
<pre class="brush:xml">&lt;fieldtype class=&quot;solr.TextField&quot; positionIncrementGap=&quot;100&quot; name=&quot;text&quot;&gt;
  &lt;analyzer type=&quot;index&quot;&gt;
    &lt;tokenizer class=&quot;solr.StandardTokenizerFactory&quot;/&gt;
    &lt;filter class=&quot;solr.StandardFilterFactory&quot;/&gt;
    &lt;filter class=&quot;solr.LowerCaseFilterFactory&quot;/&gt;
    &lt;filter class=&quot;solr.StopFilterFactory&quot; words=&quot;stopwords.txt&quot; ignoreCase=&quot;true&quot;/&gt;
    &lt;filter class=&quot;solr.ISOLatin1AccentFilterFactory&quot;/&gt;
    &lt;filter class=&quot;solr.TrimFilterFactory&quot; /&gt;
    &lt;filter class=&quot;solr.EdgeNGramFilterFactory&quot;
      minGramSize=&quot;3&quot;
      maxGramSize=&quot;30&quot;/&gt;
  &lt;/analyzer&gt;
  &lt;analyzer type=&quot;query&quot;&gt;
    &lt;tokenizer class=&quot;solr.StandardTokenizerFactory&quot;/&gt;
    &lt;filter class=&quot;solr.StandardFilterFactory&quot;/&gt;
    &lt;filter class=&quot;solr.LowerCaseFilterFactory&quot;/&gt;
    &lt;filter class=&quot;solr.StopFilterFactory&quot; words=&quot;stopwords.txt&quot; ignoreCase=&quot;true&quot;/&gt;
    &lt;filter class=&quot;solr.ISOLatin1AccentFilterFactory&quot;/&gt;
    &lt;filter class=&quot;solr.TrimFilterFactory&quot; /&gt;
  &lt;/analyzer&gt;
&lt;/fieldtype&gt;
</pre>
<p>As you can see, now we have two  sections in our , one of the analyzers is for “index” and the other is for “query”. This is needed because we don’t want to have our search parameters being transformed for a partial match. If the user is searching for “battle”, it doesn’t makes sense to show him results for “bat”, so the generation of pieces of each word should be done only when indexing information.</p>
<p>Now restart your Solr instance and head run again the form we had in the analyzer view, you should see something like Image 5:</p>
<div class="wp-caption alignnone" style="width: 1340px"><a href="https://img.skitch.com/20110113-dq7u2aeafnjt7ccdq3pxub23pb.jpg"><img alt="Image 5 – Analyzer output with partial matching enabled" src="https://img.skitch.com/20110113-dq7u2aeafnjt7ccdq3pxub23pb.jpg" title="Image 5 – Analyzer output with partial matching enabled" width="1330" height="732" /></a><p class="wp-caption-text">Image 5 – Analyzer output with partial matching enabled</p></div>
<p>Looking at the output, “battlestar” became:</p>
<pre class="brush:ruby">[“bat”, “batt”, “battl”, “battle”, “battles”, “battlest”, “battlesta”, “battlestar”]</pre>
<p>Now, if you search for “battle”, you should find all products that have “battle” as a prefix in any of their words and the search input is not affected by this change.</p>
<h2>Faceting</h2>
<p>Faceting of results is <strong>YACF (Yet Another Cool Feature)</strong> that you have when using Solr and Sunspot. “What does that mean?”, you might ask, it means that Solr is able to organize your results based on one of it’s properties and tell you how many results did match for every property value.</p>
<p>“I still don’t get it”, you might be thinking now. In our Product model we’re indexing the “category_id” property, we’ll tell Sunspot to facet our search based on the “category_id” field and Sunspot will tell us how many matches each category had, even if we’re paginating the results. Let’s see how our searching code would change:<br />
 </p>
<h4>Listing 9 – products_controller.rb except</h4>
<pre class="brush:ruby">
  def index
    @page = (params[:page] || 1).to_i
    @products = if params[:q].blank?
      Product.paginate :order =&gt; 'name ASC', :per_page =&gt; 3, :page =&gt; @page
    else

      result = Product.solr_search do |s|
        s.keywords params[:q]
        unless params[:category_id].blank?
          s.with( :category_id ).equal_to( params[:category_id].to_i )
        else
          s.facet :category_id
        end
        s.paginate :per_page =&gt; 3, :page =&gt; @page
      end

      if result.facet( :category_id )
        @facet_rows = result.facet(:category_id).rows
      end

      result
    end
  end
</pre>
<p>The search code really changed a lot, now if there’s a “category_id” parameter we will use that to filter our search, if there isn’t we’re going to perform faceting with the “s.facet :category_id” call. There’s also a slight change to the “product.rb” class, let’s see it:</p>
<h4>Listing 10 – product.rb except</h4>
<pre class="brush:ruby">
  searchable :auto_index =&gt; true, :auto_remove =&gt; true do
    text :name, :boost =&gt; 2.0
    text :description
    float :price
    integer :category_id, :references =&gt; ::Category
  end
</pre>
<p>We’ve added the “:references =&gt; ::Category” to the “:category_id” field configuration so Sunspot knows that this field is, in fact, a foreign key to another object, this will allow Sunspot to load the categories in the facets automatically for you.</p>
<p>The “result.facet(:category_id)” asks the search object for the array that contains the facets returned for the :category_id field in this search. Each row in this list contains an “instance” (which, in our case, is an Category object) and a “count”, that’s the number of hits in that specific facet. Once you get your hands at the rows, we can use it in our view, let’s see how we used them:<br />
 </p>
<h4><a href="https://github.com/mauricio/sunspot_tutorial/blob/master/app/views/products/index.html.haml">Listing 11 – products/index.html.haml except</a></h4>
<pre><code>  - if !@facet_rows.blank? &amp;&amp; @facet_rows.size &gt; 1
    %ul
      - for row in @facet_rows
        %li= link_to( "#{row.instance} (#{row.count})", products_path( :q =&gt; params[:q], :category_id =&gt; row.instance ) )</code></pre>
<p>If there are facets available, we use them to add links that will make the user filter based on each specific facet, each row object has an instance and a count, and we use both in the interface to tell the user which category is it and how many hits it had. Look at how our user interface looks like:</p>
<div class="wp-caption alignnone" style="width: 386px"><img alt="Image 6 – Faceting information" src="https://img.skitch.com/20110113-8u9sqb5arch31japkx372dq6fs.jpg" title="Image 6 – Faceting information" width="376" height="467" /><p class="wp-caption-text">Image 6 – Faceting information</p></div>
<p>And now you finally have search functionality added to a Rails project, with partial matching, faceting, pagination and input cleanup. Just forget that you have ever performed a “SELECT p.* FROM products p WHERE p.name LIKE ‘%battle%’” and be happy to be using a great full text search solution.</p>
<h2>Conclusion</h2>
<p>Hopefully this tutorial should be enough to get you up and running with Solr, for more advanced features I’d recommend you to search on the <a href="http://wiki.apache.org/solr/FrontPage">Solr wiki</a> and buy <a href="http://www.amazon.com/gp/product/1847195881?ie=UTF8&amp;tag=ultimaspalavr-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=1847195881">“Solr 1.4 – Enterprise Search Server”</a>  by David Smiley and Erick Pugh.</p>
<h2>Related Posts</h2>
<ul>
<li><a href="http://techbot.me/2010/08/deployment-recipes-deploying-monitoring-and-securing-your-rails-application-to-a-clean-ubuntu-10-04-install-using-nginx-and-unicorn/">Deployment Recipes – Deploying, monitoring and securing your Rails application to a clean Ubuntu 10.04 install using Nginx and Unicorn</a></li>
<li><a href="http://techbot.me/2011/01/handling-various-rubies-at-the-same-time-in-your-machine-with-rvm-%E2%80%93-ruby-version-manager/">Handling various rubies at the same time in your machine with RVM – Ruby Version Manager</a></li>
<li><a href="http://techbot.me/2010/07/asynchronous-email-deliveries-using-resque-and-resque_action_mailer_backend/">Asynchronous email deliveries using Resque and resque_action_mailer_backend</a></li>
<li><a href="http://techbot.me/2008/12/sql-functions-in-where-clauses-are-evil/">SQL functions in WHERE clauses are evil</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://techbot.me/2011/01/full-text-search-in-in-rails-with-sunspot-and-solr/feed/</wfw:commentRss>
		<slash:comments>28</slash:comments>
		</item>
		<item>
		<title>Agilidade, snake oil, power balances e a falácia do “nós somos diferentes”</title>
		<link>http://techbot.me/2011/01/agilidade-snake-oil-power-balances-e-a-falacia-do-nos-somos-diferentes/</link>
		<comments>http://techbot.me/2011/01/agilidade-snake-oil-power-balances-e-a-falacia-do-nos-somos-diferentes/#comments</comments>
		<pubDate>Thu, 06 Jan 2011 02:13:43 +0000</pubDate>
		<dc:creator>Maurício Linhares</dc:creator>
				<category><![CDATA[gerenciamento]]></category>

		<guid isPermaLink="false">http://alinhavado.wordpress.com/?p=37</guid>
		<description><![CDATA[Um companheiro de uma das listas que participo recebeu um email bem interessante, e eu preciso fazer alguns comentários sobre isso. From: Oleg Puzyreff Date: Tue, Jan 4, 2011 at 9:36 PM Subject: Optimized Scrum training course with Dr. Jeff Sutherland, co founder of Scrum To: fulano@sicrano.com Dear Fulano, qaSignature in partnership with Scrum Inc. [...]]]></description>
			<content:encoded><![CDATA[<p>Um companheiro de uma das listas que participo recebeu um email bem interessante, e eu preciso fazer alguns comentários sobre isso.</p>
<p><span id="more-37"></span></p>
<blockquote><p>From: Oleg Puzyreff<br />
Date: Tue, Jan 4, 2011 at 9:36 PM<br />
Subject: Optimized Scrum training course with Dr. Jeff Sutherland, co founder of Scrum<br />
To: fulano@sicrano.com</p>
<p>Dear Fulano,</p>
<p>qaSignature in partnership with Scrum Inc. is offering Optimized Scrum, a new Scrum training course designed to super-accelerate software development and testing.</p>
<p>DO NOT take this class if:</p>
<p>•	You are happy with your job being outsourced;<br />
•	You already know everything and do not need to learn the latest trends in Software development;<br />
•	You love to be busy to the point you work late and on weekends;</p>
<p>This course is offered two ways:</p>
<p>As a one-day course for CSMs, developers and QA staff: http://scrumtraininginstitute.com/classes/show/458</p>
<p>OR</p>
<p>As an optional course* packaged as a third day with Scrim Inc&#8217;s CSM training:  http://scrumtraininginstitute.com/classes/show/459</p>
<p> *This optional course is not part of CSM certification program and not required for ScrumMaster certification.</p>
<p>I hope to see you there, and let me know if you have any questions.</p></blockquote>
<p>Dizer que isso é charlatanismo é chover no molhado. Nós vemos produtos e serviços que prometem soluções mágicas pros nossos problemas diariamente, é só olhar todo o burburinho que foi gerado pelas pulseiras <a href="http://www1.folha.uol.com.br/equilibrioesaude/854797-fabricante-da-pulseira-power-balance-admite-que-produto-nao-funciona.shtml">Power Balance</a>.</p>
<p>Marketing é vender sonhos, transformar produtos que fazem a mesma coisa que todos os outros fazem em únicos e exclusivos para os seus clientes. O homem atual já sofreu tanta influência desse mesmo marketing que é capaz de perceber onde termina a realidade e começa o bla-bla-bla publicitário.</p>
<p>Mas se as pessoas já tem conhecimento do que é verdade e mentira em uma campanha de marketing, por que elas se dão ao trabalho de comprar produtos como o Power Balance?</p>
<p>Na mini-série que antecede o seriado <a href="http://en.wikipedia.org/wiki/Battlestar_Galactica_(2004_TV_series)">Battlestar Galactica</a>, a ciborgue <a href="http://en.wikipedia.org/wiki/Number_Six_(Battlestar_Galactica)">Caprica Six</a> faz um comentário interessante para o humano <a href="http://en.wikipedia.org/wiki/Gaius_Baltar">Gaius Baltar</a>, assim que ele descobre que foi usado como ferramenta de espionagem:</p>
<p><div id="attachment_38" class="wp-caption alignright" style="width: 310px"><a href="http://techbot.me/wp-content/uploads/2011/01/caprica-six-gaius-baltar.jpg"><img src="http://techbot.me/wp-content/uploads/2011/01/caprica-six-gaius-baltar.jpg?w=300" alt="" title="Gaius Baltar e Caprica Six" width="300" height="224" class="size-medium wp-image-38" /></a><p class="wp-caption-text">Gaius Baltar e Caprica Six</p></div><br />
<blockquote><strong>Gaius Baltar</strong>: I had nothing to do with this. You know I had nothing to do with this.<br />
<strong>Caprica Six</strong>: You have an amazing capacity for self-deception. How do you do that?</p></blockquote>
<p>Gaius tenta, de todas as formas, negar a realidade para evitar ter que lidar com as consequências dos seus atos. Com essa ação, ele não está somente demonstrando uma parte da sua personalidade, mas representando um traço comum pra grande parte dos seres humanos, queremos a solução mais simples para os problemas, mesmo que pra isso tenhamos que fingir que não somos mais seres racionais.</p>
<p>A propaganda do curso de Scrum segue a mesma idéia das propagandas do Power Balance. Em momento nenhum o email fala de fatos, ele apela para os sentimentos do receptor da mensagem, começa por um ponto fraco comum, o medo de perder o emprego, depois pra humildade, pois ninguém sabe de tudo e finaliza com a idéia de que o receptor trabalha muito além do necessário.</p>
<p>Do ponto de vista do marketing, seria uma peça ridícula, típica dos primórdios da profissão ou de profissionais de baixa qualidade. Mas quando essa mensagem atinge as pessoas, ela fala o que elas querem ouvir. Ela leva a mensagem de que, magicamente, todos os problemas do indivíduo vão ser resolvidos, afinal, é um curso com o Doutor Jeff Sutherland, co-fundador do Scrum.</p>
<p>Você tem palavras mágicas, dignas dos melhores – ou piores – livros de auto-ajuda disponíveis e um rockstar dos processos de desenvolvimento, o que poderia dar errado?</p>
<p>Por mais que a propaganda seja completamente irreal e o mais puro e completo snake oil, as pessoas vão correr e se matricular, na esperança de que seja dessa vez que eles finalmente vão encontrar o santo graal do software. A solução definitiva que vai aumentar a produtividade absurdamente e transformar você no novo Gandalf da programação, capaz de rescrever o kernel do Linux todo usando somente o Emacs.</p>
<p>A ilusão de que existe uma forma absurdamente fácil e simples de se desenvolver software, que Fred Brooks já comentou não existir no texto<a href="http://en.wikipedia.org/wiki/No_Silver_Bullet"> “No Silver Bullet — Essence and Accidents of Software Engineering”</a> de 1986, continua sendo uma realidade. As pessoas continuam buscando essa ilusão e, ainda pior, a comunidade ágil, que um dia brigou ferozmente contra essa concepção, tornou-se o status quo e agora vende o mesmo snake oil que os consultores de processo vendiam nos anos 90.</p>
<p><div id="attachment_39" class="wp-caption alignleft" style="width: 310px"><a href="http://techbot.me/wp-content/uploads/2011/01/snakeoil.jpg"><img src="http://techbot.me/wp-content/uploads/2011/01/snakeoil.jpg" alt="" title="snakeoil" width="300" height="300" class="size-full wp-image-39" /></a><p class="wp-caption-text">Óleo de Cobra - A solução para todos os seus problemas</p></div>Hoje os livros e textos sobre desenvolvimento ágil de software são equivalentes aos mais ralos livros de auto-ajuda e motivação pessoal que temos no mercado. Autores ignoram fontes, escondem inspirações e se definem como criadores únicos e intocáveis dos seus processos, negando inclusive que tenham sido influenciados por outros processos semelhantes que já existiam fora da esfera do software, como o Lean.</p>
<p>A assinatura do manifesto ágil foi um passo na direção certa, colocou os processos de software a par das mudanças anteriores (de mais de uma década antes) da gestão de pessoas nas disciplinas de administração de empresas. Antes que acordemos tarde, do que nunca acordar, mas não podemos parar aqui.</p>
<p>A comunidade ágil se apresentou como sendo algo novo, sem os vícios do passado, aprendendo com o que as outras disciplinas, fora da computação, já faziam. Não podemos vender charlatanismo, mentiras, poções mágicas que curam ou ressuscitam projetos. Desenvolver software em equipes é difícil, negar isso é tão surreal quanto negar que a gravidade existe.</p>
<p>Se ainda se espera que um dia a engenharia de software possa ser realmente reconhecida como uma engenharia, é melhor pararmos com essa conversa fiada e voltarmos a desenvolver software, que é o que realmente importa de verdade.</p>
]]></content:encoded>
			<wfw:commentRss>http://techbot.me/2011/01/agilidade-snake-oil-power-balances-e-a-falacia-do-nos-somos-diferentes/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Meu ambiente de desenvolvimento em 7 itens</title>
		<link>http://techbot.me/2010/12/meu-ambiente-de-desenvolvimento-em-7-itens/</link>
		<comments>http://techbot.me/2010/12/meu-ambiente-de-desenvolvimento-em-7-itens/#comments</comments>
		<pubDate>Fri, 31 Dec 2010 16:16:10 +0000</pubDate>
		<dc:creator>Maurício Linhares</dc:creator>
				<category><![CDATA[diversos]]></category>

		<guid isPermaLink="false">http://alinhavado.wordpress.com/?p=33</guid>
		<description><![CDATA[Recebi o convite do @cmilfont e agora estou entrando na brincadeira também, vamos lá. Máquina/SO Uso um desktop com Ubuntu Linux + VirtualBox (com 3 Windows instalados) pra testes e também pra tocar música no escritório. O trabalho do dia a dia acontece num MacBookPro de 15’ rodando Snow Leopard, sempre ligado num monitor AOC [...]]]></description>
			<content:encoded><![CDATA[<p>Recebi o convite do <a href="http://twitter.com/cmilfont">@cmilfont</a> e agora estou entrando na brincadeira também, vamos lá.</p>
<p><span id="more-231"></span></p>
<h3>Máquina/SO</h3>
<p>Uso um desktop com Ubuntu Linux + VirtualBox (com 3 Windows instalados) pra testes e também pra tocar música no escritório. O trabalho do dia a dia acontece num MacBookPro de 15’ rodando Snow Leopard, sempre ligado num monitor AOC de 24’. Quando preciso usar o Linux, normalmente conecto nele via VNC (ou simplesmente troco os cabos do monitor). Usei o desktop como máquina principal por uns 2 anos, mas desde o meio do ano estou trabalhando somente no Mac e o Linux ficou somente pra testes e rodar as máquinas virtuais.</p>
<p>No Mac eu uso o <a href="http://www.macports.org/">Ports</a> pra instalar dependências Unix, que são básicas pro ambiente de desenvolvimento, tentei Homebrew mas ele não se deu muito bem na minha máquina.</p>
<h3>Editor/IDE</h3>
<p>Aqui a salada é grande, trabalhando com Ruby, Java e Objective-C, não tem como ficar em uma só, então eu termino usando várias. Começa com <a href="http://aptana.com/">Aptana</a> e <a href="http://netbeans.org/">NetBeans</a> pra Ruby, hoje eu tenho usado mais o Aptana por causa do suporte melhor pra <a href="http://haml-lang.com/">HAML</a>, mas de vez em quando ainda volto pro NetBeans pra relembrar os velhos tempos.</p>
<p>Quando é hora de trabalhar com Java, fico no <a href="http://eclipse.org/">Eclipse</a> mesmo, com o mínimo de plug-ins possível (normalmente uso o pacote que vem direto do site mesmo), já que ele está ficando cada dia mais cheio de inutilidades e eu quase não faço mais desenvolvimento web com ele.</p>
<p>E finalmente, na hora de trabalhar com Mac/iPhone/iPad dev eu vou pro <a href="http://developer.apple.com/technologies/xcode.html">Xcode</a>, que, perto do Eclipse, é basicamente um Notepad mais engraçadinho. Foi meio difícil de entender como tudo funciona no ambiente, ele tem vários quirks bizarros, mas o Interface Builder faz a coisa toda valer a pena, se você estiver trabalhando pra desenvolvimento desktop/mobile. Ele é levinho, só dá trabalho mesmo quando você tem que compilar um projeto grande pra 4 arquiteturas diferentes quando vai gerar um release.</p>
<h3>Terminal</h3>
<p>Meu terminal é comum, a única coisa de especial que ele tem é o <a href="http://rvm.beginrescueend.com/">RVM</a> (indispensável se você trabalha com Ruby), não gosto muito de firulas nesse pedaço do meu dia a dia.</p>
<h3>Browser</h3>
<p>Aqui é Chrome na cabeça, pro uso diário. Quando preciso fazer debug de alguma coisa, vou pro Firefox velho de guerra com <a href="http://getfirebug.com/">Firebug</a>, <a href="http://chrispederick.com/work/web-developer/">WebDeveloper</a> e <a href="http://developer.yahoo.com/yslow/">YSlow</a>.</p>
<h3>Software</h3>
<p>Essenciais pro meu dia a dia são o <a href="http://www.apple.com/downloads/macosx/networking_security/chickenofthevnc.html">Chicken of the VNC</a>, Office for Mac, Skype, Chrome, <a href="http://skitch.com/">Skitch</a>, Tweetie e <a href="http://manytricks.com/witch/">Witch</a>.</p>
<h3>Source code</h3>
<p>Não dá pra inventar muito aqui, sempre que posso, mando os projetos pra contas privadas (ou públicas) no <a href="https://github.com/mauricio">github</a>, atualmente tenho sofrido maus bocados com o <a href="http://mercurial.selenic.com/">Mercurial</a>, é bem diferente de se utilizar Git, mas estou sobrevivendo a experiência.</p>
<h3>Música</h3>
<p>O iTunes não aguentou o tranco de indexar toda a minha lista de músicas, então o trabalho principal do meu desktop hoje é tocar as minhas músicas no <a href="http://amarok.kde.org/">Amarok</a> e mandar os scrobbles pro <a href="http://www.last.fm/user/maujr">last.fm</a>, lugar onde eu sempre encontro alguma coisa interessante pra ouvir.</p>
<p>E pra continuar a brincadeira eu indico o <a href="http://twitter.com/pedromtavares">@pedromtavares</a>, <a href="http://twitter.com/yurimalheiros">@yurimalheiros</a> e <a href="http://twitter.com/vitorbaptista">@vitorbaptista</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://techbot.me/2010/12/meu-ambiente-de-desenvolvimento-em-7-itens/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
<!-- This Quick Cache file was built for (  techbot.me/feed/ ) in 0.93428 seconds, on May 19th, 2012 at 1:29 pm UTC. -->
<!-- This Quick Cache file will automatically expire ( and be re-built automatically ) on May 19th, 2012 at 2:29 pm UTC -->
