Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
815 lines (618 sloc) 77.4 KB
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[Felipe Lima]]></title>
<link href="http://felipecsl.com/atom.xml" rel="self"/>
<link href="http://felipecsl.com/"/>
<updated>2016-03-05T01:35:17-03:00</updated>
<id>http://felipecsl.com/</id>
<author>
<name><![CDATA[Felipe Lima]]></name>
</author>
<generator uri="http://octopress.org/">Octopress</generator>
<entry>
<title type="html"><![CDATA[Moving to Medium]]></title>
<link href="http://felipecsl.com/blog/2016/03/05/moving-to-medium/"/>
<updated>2016-03-05T01:25:58-03:00</updated>
<id>http://felipecsl.com/blog/2016/03/05/moving-to-medium</id>
<content type="html"><![CDATA[<p>I&rsquo;ve now started blogging on <a href="https://medium.com/@felipecsl">Medium</a>. You can see my most recent articles <a href="https://medium.com/@felipecsl">there</a>!</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Publishing an Android Library to Maven Central with Gradle]]></title>
<link href="http://felipecsl.com/blog/2013/12/06/publishing-an-android-library-to-maven-central/"/>
<updated>2013-12-06T14:19:00-02:00</updated>
<id>http://felipecsl.com/blog/2013/12/06/publishing-an-android-library-to-maven-central</id>
<content type="html"><![CDATA[<p>I recently migrated the <a href="https://play.google.com/store/apps/details?id=com.weheartit">We Heart It Android app</a> project to Android Studio and I thought I&rsquo;d migrate the <a href="https://github.com/felipecsl/Android-ImageManager">Android-ImageManager</a> library into the new format as well. That was the easy part. Another thing I wanted to do was to upload it to Maven Central, so it would make people&rsquo;s lives easier when using it.</p>
<p>Ok this was painful. Here are the steps I took in order to achieve it, so hopefully people won&rsquo;t struggle that much doing it in the future:</p>
<ol>
<li>Follow Chris Banes&#8217; <a href="http://chris.banes.me/blog/2013/08/27/pushing-aars-to-maven-central/">steps</a>;</li>
<li>Follow the steps <a href="https://github.com/chrisbanes/gradle-mvn-push">here</a> to configure the upload username and password;</li>
<li>Make sure you edit <code>gradle.properties</code> and <code>build.gradle</code> with your library&rsquo;s information (version, description, links, etc);</li>
<li>Follow <a href="https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide">this</a> guide. To summarize, you&rsquo;ll need to create a Sonatype <a href="https://issues.sonatype.org/secure/Dashboard.jspa">JIRA</a> account and then you&rsquo;ll have to create an issue there in order to create your project. That is basically apply for open source project hosting (that&rsquo;s basically the steps 2 and 3 on the user guide mentioned above);</li>
<li>Wait for your issue to be &ldquo;Resolved&rdquo;, that may take up to 2 business days (!!!). Someone from Sonatype will do it and you&rsquo;ll get an email;</li>
<li>Run <code>./gradlew clean build uploadArchives</code> to build and upload your project into Sonatype. IMPORTANT: You have to upload a Debug build first, otherwise it is not gonna work. You do that by adding <code>-SNAPSHOT</code> at the end of your <code>VERSION_NAME</code> on <code>gradle.properties</code>, like this, eg.: <code>VERSION_NAME=1.0.0-SNAPSHOT</code></li>
<li>After that, if all went well, you should see your project <a href="http://oss.sonatype.org/content/repositories/snapshots/">here</a>. You can navigate through the folders. My project&rsquo;s group id was <code>com.felipecsl.android</code>, so I could find it at <a href="http://oss.sonatype.org/content/repositories/snapshots/com/felipecsl/android/">http://oss.sonatype.org/content/repositories/snapshots/com/felipecsl/android/</a></li>
</ol>
<p>I still havent figured out how to promote a Staging Repository. Right now it is failing to close the repository since the package is not signed. I will update this post once I have a solution.
In the meantime, you can already use your snapshot with Maven/Gradle.
Here is how your <code>build.gradle</code> would look like:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class='groovy'><span class='line'><span class="n">buildscript</span> <span class="o">{</span>
</span><span class='line'> <span class="n">repositories</span> <span class="o">{</span>
</span><span class='line'> <span class="n">mavenCentral</span><span class="o">()</span>
</span><span class='line'> <span class="o">}</span>
</span><span class='line'> <span class="n">dependencies</span> <span class="o">{</span>
</span><span class='line'> <span class="n">classpath</span> <span class="s1">&#39;com.android.tools.build:gradle:0.6.+&#39;</span>
</span><span class='line'> <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'><span class="n">apply</span> <span class="nl">plugin:</span> <span class="s1">&#39;android&#39;</span>
</span><span class='line'>
</span><span class='line'><span class="n">repositories</span> <span class="o">{</span>
</span><span class='line'> <span class="n">maven</span> <span class="o">{</span> <span class="n">url</span> <span class="s2">&quot;https://oss.sonatype.org/content/repositories/snapshots/&quot;</span> <span class="o">}</span>
</span><span class='line'> <span class="n">mavenCentral</span><span class="o">()</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">dependencies</span> <span class="o">{</span>
</span><span class='line'> <span class="n">compile</span> <span class="s1">&#39;com.felipecsl.android:library:1.0.0-SNAPSHOT&#39;</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>Well that is pretty much it for now. It took me almost an entire afternoon to figure this all out, hope it is useful for other people.
If you&rsquo;re interested in a simpler and a lot more elegant solution for Open Source library project hosting, check <a href="http://rubygems.org">rubygems.org</a> which is used to, guess what, host Ruby gems. That is really piece of cake compared to all this Java mess :sigh:</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Building a Custom View with Android]]></title>
<link href="http://felipecsl.com/blog/2013/11/12/building-a-custom-view-with-android/"/>
<updated>2013-11-12T11:40:00-02:00</updated>
<id>http://felipecsl.com/blog/2013/11/12/building-a-custom-view-with-android</id>
<content type="html"><![CDATA[<p>This week I&rsquo;m attending <a href="http://andevcon.com/">AnDevCon</a> in San Francisco and in the first tutorial in the first day of the event, I watched a really nice workshot by <a href="https://twitter.com/chiuki">Chiu-Ki Chan&rsquo;s</a> titled <code>Hands-on Android Custom View Workshop</code>. The code is here on <a href="https://github.com/chiuki/android-fraction-view">Github</a>.</p>
<p>We built a simple custom view using circles, arcs and <code>onDraw</code> to draw some kind of pie chart that reacts to click, sends change events, etc.</p>
<p>I implemented the FractionView with some small changes, animating it into some kind of clock/stopwatch thing that animates by itself. Below is a GIF with the sample app running.</p>
<p>Please feel free to play with it and check out my fork on <a href="https://github.com/felipecsl/android-fraction-view">Github</a>.</p>
<p><img src="http://felipecsl.com/images/fractionView.gif" alt="" /></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Handling Animated GIFs with Android the right way]]></title>
<link href="http://felipecsl.com/blog/2013/08/20/animated-gifs-with-android/"/>
<updated>2013-08-20T21:05:00-03:00</updated>
<id>http://felipecsl.com/blog/2013/08/20/animated-gifs-with-android</id>
<content type="html"><![CDATA[<p>Working with animated GIFs on Android can be a painful task. In the <a href="https://play.google.com/store/apps/details?id=com.weheartit">We Heart It app</a> we deal with a lot of images, but also animated GIFs. Since we support Android back to API Level 8 (2.2), <code>WebView</code> is by far the simplest solution, however is not an option for us, since it <a href="https://code.google.com/p/android/issues/detail?id=3422">doesn&rsquo;t work</a> on most of older Android versions.</p>
<p>By looking at this excellent tutorial by <a href="http://droid-blog.net/2011/10/15/tutorial-how-to-play-animated-gifs-in-android-%E2%80%93-part-2/">Johannes Borchardt</a>, I decided to take <code>GifDecoder</code> approach, which seems to be the most reliable solution. However, the <a href="https://code.google.com/p/android-gifview/source/browse/trunk/GifPlayer/src/jp/tomorrowkey/android/gifplayer/GifDecoder.java">GifDecoder</a> class suggested by Johannes is not very memory efficient since it keeps in memory the bitmap data for every frame in the GIF.</p>
<p>By doing some more research, I found this great <a href="https://gist.github.com/devunwired/4479231">gist</a> with an optimized implementation of <code>GifDecoder</code> that, as described there, &ldquo;decodes images on-the-fly, and only the minimum data to create the next frame in the sequence is kept&rdquo;.</p>
<p>The class interface is however not exactly the same as the one exposed by the original <code>GifDecoder</code> class provided, thus the <code>GifDecoderView</code> class had to be adjusted. I made the adjustments required to interact with this version and also start/stop the animation. By default it will loop the GIF animation, even if the view is not on the screen, so you have to be careful to call <code>startAnimation()</code> and <code>stopAnimation()</code> correctly to avoid GIFs playing in the background, which can eat up all your memory very quickly.</p>
<p>My interaction looks basically like this:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'> <span class="c1">// In my fragment class...</span>
</span><span class='line'>
</span><span class='line'> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onCreate</span><span class="o">()</span> <span class="o">{</span>
</span><span class='line'> <span class="n">gifView</span> <span class="o">=</span> <span class="k">new</span> <span class="nf">GifDecoderView</span><span class="o">(</span><span class="n">getActivity</span><span class="o">());</span>
</span><span class='line'> <span class="n">gifView</span><span class="o">.</span><span class="na">setBytes</span><span class="o">(</span><span class="n">bitmapData</span><span class="o">);</span>
</span><span class='line'> <span class="n">gifView</span><span class="o">.</span><span class="na">startAnimation</span><span class="o">();</span>
</span><span class='line'> <span class="o">}</span>
</span><span class='line'>
</span><span class='line'> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setUserVisibleHint</span><span class="o">(</span><span class="kt">boolean</span> <span class="n">isVisibleToUser</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'> <span class="kd">super</span><span class="o">.</span><span class="na">setUserVisibleHint</span><span class="o">(</span><span class="n">isVisibleToUser</span><span class="o">);</span>
</span><span class='line'>
</span><span class='line'> <span class="k">if</span> <span class="o">(!</span><span class="n">isVisibleToUser</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'> <span class="n">gifView</span><span class="o">.</span><span class="na">stopAnimation</span><span class="o">();</span>
</span><span class='line'> <span class="o">}</span>
</span><span class='line'> <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>You can get all the code here in this <a href="https://gist.github.com/felipecsl/6289457">Gist</a>.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Starting with Android - managing images]]></title>
<link href="http://felipecsl.com/blog/2013/03/31/starting-with-android-managing-images/"/>
<updated>2013-03-31T13:03:00-03:00</updated>
<id>http://felipecsl.com/blog/2013/03/31/starting-with-android-managing-images</id>
<content type="html"><![CDATA[<p>I&rsquo;ve started with Android development a few months ago with the upcoming <a href="http://weheartit.com">We Heart It</a> app. Here are some random thought and impressions I had during this period, and the motivation behind building the Android-ImageManager library:</p>
<ul>
<li><p><strong>API Complexity</strong>: Compared to the iOS SDK, at first, the Android SDK seemed to me overly complicated and over-engineered. There are specific patterns you need to follow for most common scenarios and avoiding them just end up making it harder, so you basically have no choice. For example, the <a href="http://developer.android.com/training/id-auth/identify.html">AccountManager API</a>, which you need to use if you want to create an Android built in user account. There are several interfaces you have to implement, services to create, methods to override, etc. It is very painful overall.</p></li>
<li><p><strong>Version Compatibility</strong>: If you want to support a decent range of devices and <a href="http://developer.android.com/about/dashboards/index.html">Android versions</a>, you&rsquo;ll spend countless hours of testing and have to buy 10 different phones and tablets, since the Emulator is simply unusable (terribly slow). Stick with already proven open source libraries for a less painful development experience, like <a href="http://actionbarsherlock.com/">ActionbarSherlock</a>, <a href="https://github.com/JakeWharton/Android-ViewPagerIndicator">ViewPagerIndicator</a>, <a href="https://github.com/prototik/HoloEverywhere">HoloEverywhere</a>, <a href="https://github.com/JakeWharton/DiskLruCache">DiskLruCache</a>, etc. You can thank <a href="http://twitter.com/jakewharton">Jake Wharton</a> for most of that awesome work.</p></li>
<li><p><strong>Automated Testing</strong>: Painful as well. We decided to go with <a href="http://robolectric.blogspot.com/">Robolectric</a> which basically stubs all the Android APIs for you. That has pros and cons, but definitely good that your tests run super fast (like seconds) and you dont have to keep stubbing everything in order to make your tests work. All you need is a simple jUnit project. Haven&rsquo;t looked much into Integrated UI tests yet, but we definitely want to check that out soon. Android provides <a href="http://developer.android.com/reference/android/test/InstrumentationTestCase.html">some classes</a> for this job.</p></li>
<li><p><strong>Memory Management</strong>: This would deserve an entire article just for it. If you start working with images, galleries etc. or just do something that is not plain trivial, you&rsquo;ll sooner or later start seeing <code>OutOfMemoryError</code>. It is hard to get it right, and there are many things you need to be aware of when building an application, specially for We Heart It, which is totally image-heavy. Some of the techniques definitely include</p>
<ol>
<li><a href="http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html">Cache your images</a> so you don&rsquo;t keep requesting the same images over and over again and wasting all the user&rsquo;s data bandwidth;</li>
<li><a href="http://developer.android.com/training/displaying-bitmaps/load-bitmap.html">Request a sampled version of the images</a> that is adequate for the screen size/display dimensions where it is gonna be used;</li>
<li><a href="http://developer.android.com/training/displaying-bitmaps/manage-memory.html">Recycle</a> your bitmap when it is not being used anymore, so you can reclaim unused memory faster.</li>
<li>Avoid creating too many instances: When working with a REST API, it is easy to instantiate new objects all the time and keep them around, send to the Activities, etc. Try to keep your footprint to a minimum. Think about all object instantiation and whether it is needed or not. Sometimes all you need is a simpler version of your domain object to be shown in the UI, so keep a simpler version of your model that contains only the needed information to be displayed.</li>
</ol>
</li>
</ul>
<p>Some of these reasons above motivated me to create the library <a href="https://github.com/felipecsl/Android-ImageManager">Android-ImageManager</a>, which takes care of caching and storing the displaying efficiently. It uses a two level cache, first in-memory using an `LruCache&#8220;, then in disk, using DiskLruCache to save the files to the device&rsquo;s SD Card. If an image is found in the memory cache, it is fetched from there, otherwise, it tries to retrieve it from disk cache. If it can&rsquo;t be found either, then it finally downloads the image and stores it in both caches. Everything is done assynchonously, obviously. The library is still in early stage, as I plan to add more samples and polish it a bit more. So check out the Github <a href="https://github.com/felipecsl/Android-ImageManager">repository</a>.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[When two identical strings are different?]]></title>
<link href="http://felipecsl.com/blog/2012/08/01/when-two-identical-strings-are-different/"/>
<updated>2012-08-01T00:46:00-03:00</updated>
<id>http://felipecsl.com/blog/2012/08/01/when-two-identical-strings-are-different</id>
<content type="html"><![CDATA[<p>Think these two strings are the same?</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="s2">&quot;R. Padre Chagas 342&quot;</span>
</span><span class='line'><span class="s2">&quot;R. Padre Chagas 342&quot;</span>
</span></code></pre></td></tr></table></div></figure>
<p>Pretty much, right? So we open IRB:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="mi">1</span><span class="o">.</span><span class="mi">9</span><span class="o">.</span><span class="mi">3</span><span class="n">p0</span> <span class="p">:</span><span class="mi">085</span> <span class="o">&gt;</span> <span class="s2">&quot;R. Padre Chagas 342&quot;</span> <span class="o">==</span> <span class="s2">&quot;R. Padre Chagas 342&quot;</span>
</span><span class='line'> <span class="c1"># =&gt; false</span>
</span></code></pre></td></tr></table></div></figure>
<p><img src="http://i0.kym-cdn.com/photos/images/original/000/199/693/disgusted-mother-of-god.png?1321272571" alt="" /></p>
<p>WTF!!? Took me a couple minutes of head scratching to figure out.. let’s look closer:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="mi">1</span><span class="o">.</span><span class="mi">9</span><span class="o">.</span><span class="mi">3</span><span class="n">p0</span> <span class="p">:</span><span class="mi">086</span> <span class="o">&gt;</span> <span class="s2">&quot;R. Padre Chagas 342&quot;</span><span class="o">.</span><span class="n">bytes</span><span class="o">.</span><span class="n">to_a</span>
</span><span class='line'> <span class="c1"># =&gt; [82, 46, 32, 80, ... , 67, 104, 97, 103, 97, 115, 32, 51, 52, 50] </span>
</span><span class='line'><span class="mi">1</span><span class="o">.</span><span class="mi">9</span><span class="o">.</span><span class="mi">3</span><span class="n">p0</span> <span class="p">:</span><span class="mi">087</span> <span class="o">&gt;</span> <span class="s2">&quot;R. Padre Chagas 342&quot;</span><span class="o">.</span><span class="n">bytes</span><span class="o">.</span><span class="n">to_a</span>
</span><span class='line'> <span class="c1"># =&gt; [82, 46, 32, 80, ... , 67, 104, 97, 103, 97, 115, 194, 160, 51, 52, 50]</span>
</span></code></pre></td></tr></table></div></figure>
<p>`</p>
<p>Haa. There it is.. an extra hidden byte!. Crazy huh? Even closer:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="mi">1</span><span class="o">.</span><span class="mi">9</span><span class="o">.</span><span class="mi">3</span><span class="n">p0</span> <span class="p">:</span><span class="mi">088</span> <span class="o">&gt;</span> <span class="s2">&quot;R. Padre Chagas 342&quot;</span><span class="o">.</span><span class="n">byteslice</span><span class="p">(</span><span class="mi">9</span><span class="o">.</span><span class="n">.</span><span class="mi">15</span><span class="p">)</span>
</span><span class='line'> <span class="c1"># =&gt; &quot;Chagas &quot; </span>
</span><span class='line'><span class="mi">1</span><span class="o">.</span><span class="mi">9</span><span class="o">.</span><span class="mi">3</span><span class="n">p0</span> <span class="p">:</span><span class="mi">089</span> <span class="o">&gt;</span> <span class="s2">&quot;R. Padre Chagas 342&quot;</span><span class="o">.</span><span class="n">byteslice</span><span class="p">(</span><span class="mi">9</span><span class="o">.</span><span class="n">.</span><span class="mi">15</span><span class="p">)</span>
</span><span class='line'> <span class="c1"># =&gt; &quot;Chagas\xC2&quot;</span>
</span></code></pre></td></tr></table></div></figure>
<p>This is what happens when you scrape data from the web… Do not believe everything you see :)</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Getting started with Chef]]></title>
<link href="http://felipecsl.com/blog/2012/06/15/getting-started-with-chef/"/>
<updated>2012-06-15T00:46:00-03:00</updated>
<id>http://felipecsl.com/blog/2012/06/15/getting-started-with-chef</id>
<content type="html"><![CDATA[<p>If you never heard of it, <a href="http://www.opscode.com/chef/">Chef</a> is a server automation tool for server management tasks. It is often related to the terms <a href="http://en.wikipedia.org/wiki/DevOps">DevOps</a> and <a href="http://blog.carlossanchez.eu/2012/03/13/infrastructure-as-code/">Infrastructure as Code</a> which have started to gain quite a bit of attention lately. If you&rsquo;ve ever seen yourself doing the exact same steps, like installing MySql on a server several times in a row, always doing the exact same steps, and wondered if there was a more intelligent way of doing that, then Chef is for you.</p>
<p><img src="http://www.rit.edu/news/lib/filelib/200708/new_chef.jpg" alt="" /></p>
<p><em>No, not this kind of chef :)</em></p>
<p>This is not the only option available. Other well known tools include <a href="http://puppetlabs.com/">Puppet</a> and <a href="http://vagrantup.com/">Vagrant</a>. The latter, though, being targeted at virtualized development environments, while the former, are targeted at real (production) machines.</p>
<p>So, &lsquo;nuff said, here are some notes I took from my first time playing with Chef. It was far from being a straightforward process, that is why a good old blog post comes handy. Turns out it was pretty painful just to get it started, and I didn&rsquo;t even get to the part where you prepare the recipes and cookbooks (hmmm :)).</p>
<p>So to get started, I fired up a brand new clean Ubuntu 12 instance in <a href="https://console.aws.amazon.com/ec2/home">Amazon EC2</a>. If you need help doing that, there are plenty of documentation online for how to get started on AWS. Go there and search, I will wait here.</p>
<p>Ok, so now that you have an instance up and running, connect to it via ssh (tip: you will need the private key file .pem):</p>
<pre><code>ssh -i &lt;path_to_your_pem_file&gt; ubuntu@ec2-xx-xx-xxx-xxx.us-west-1.compute.amazonaws.com
</code></pre>
<p>Install chef-server. There is a wiki for that <a href="http://wiki.opscode.com/display/chef/Installing+Chef+Server+on+Debian+or+Ubuntu+using+Packages">here</a>. After you run <strong>apt-get install chef chef-server</strong>, most likely, at t he end, chef-server will fail to start (at least that is what happened to me)</p>
<p>Ok, if you try to start the server manually via the command <strong>chef-server</strong>, then it might end up exploding with this error:</p>
<pre><code>NOTE: Gem.activate is deprecated, use Specification#activate. 
It will be removed on or after 2011-10-01.
</code></pre>
<p>This is basically saying that you are using the wrong version of ruby and/or rubygems. The plain ubuntu install only comes with ruby 1.8 installed which is probably not what we want. So we&rsquo;ll install <a href="https://rvm.io//">rvm</a> and ruby 1.9.</p>
<p>But, before that, let&rsquo;s install some dependencies:</p>
<p>Run this to install some required packages that you gonna need&hellip; some of them are probably not needed for this, but it wont hurt to install them anyways.</p>
<pre><code>sudo apt-get install build-essential bison openssl libreadline6 libreadline6-dev curl 
git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev 
sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev -y
</code></pre>
<p>Done that, install rvm, ruby and some required gems:</p>
<pre><code>curl -L https://get.rvm.io | bash -s stable --ruby
gem install yajl-ruby -v 0.7.7
gem install chef
gem install chef-server
</code></pre>
<p>At this point, you should be able to just run <strong>chef-server</strong> see it running. If all goes well, you might want to do <strong>chef-server -e production -d</strong> to run it in production environment and daemonize, so it runs in background. You will also need to start the webui via <strong>chef-server-webui</strong> <strong>-e production -d.</strong> If all goes smoothly, you should be able to access the webui at **<a href="http://ec2-xx-xx-xxx-xxx.us-west-1.compute.amazonaws.com:4040/**.">http://ec2-xx-xx-xxx-xxx.us-west-1.compute.amazonaws.com:4040/**.</a> As you probably noticed, the webui runs in the port 4040, while the chef-server runs in 4000. Don&rsquo;t forget to open these ports in the AWS Security Group and allow your machine IP to access them!  But wait, this was only the start hahah! Now comes the server configuration&hellip;</p>
<p><a href="http://felipecsl.com/images/2012/06/Screen-Shot-2012-06-14-at-11.43.09-PM.png"><img src="http://felipecsl.com/images/2012/06/Screen-Shot-2012-06-14-at-11.43.09-PM.png" alt="" /></a></p>
<p>If you are still following the wiki steps (you should), by now you should be running <strong>knife configure -i</strong>. Knife is chef&rsquo;s command line configuration tool, it can do a lot of stuff. This is gonna try to create an API user. If you are unlucky, like me, you will see this error:</p>
<pre><code>Creating initial API user...
ERROR: Server returned error for &lt;a href="http://ec2-50-18-239-212.us-west-1.compute.amazonaws.com:4000/clients/ubuntu"&gt;http://ec2-xx-xx-xxx-xxx.us-west-1.compute.amazonaws.com:4000/clients/client_name&lt;/a&gt;, retrying 1/5 in 3s
...
</code></pre>
<p>This probably means chef cannot connect to RabbitMQ (the guy that does the messaging between chef components). To double check, run this:</p>
<pre><code>sudo rabbitmqctl list_permissions -p /chef
</code></pre>
<p>&hellip; and you should see this error:</p>
<pre><code>Listing permissions in vhost "/chef" ...
Error: {no_such_vhost,&lt;&lt;"/chef"&gt;&gt;}
</code></pre>
<p>So this is how you fix it:</p>
<pre><code>sudo rabbitmqctl add_vhost /chef
sudo rabbitmqctl add_user chef &lt;password&gt;
sudo rabbitmqctl set_permissions -p /chef chef ".*" ".*" ".*"
</code></pre>
<p>Gotcha: The rabbitmq password has to be the same one found in /etc/chef/server.rb config file (mine was &lsquo;root&rsquo;)</p>
<p>Ok, this should get you all set. Last step, verify the knife configuration running <strong>knife client list</strong>. And guess what?! More errors!</p>
<pre><code>ERROR: Your private key could not be loaded from /home/ubuntu/.chef/ubuntu.pem
Check your configuration file and ensure that your private key is readable
</code></pre>
<p>This happened to me because when I ran knife configure -i, I set as the new username, a username that was somehow already in use (ubuntu). So the solution was to run it again and set a different new username when asked. This should create your API client and get you all set.</p>
<pre><code>ubuntu@ip-xx-xxx-xxx-xxx:~/.chef$ knife client list
Lixaredo.local
admin
chef-validator
chef-webui
felipecsl
local
</code></pre>
<p>Ok here is another gotcha. If you ever turn off your instance and bring it back later, it will most likely change its public dns (<strong><strong>*.compute.amazonaws.com&hellip;). Make sure you update it in your </strong>knife.rb</strong> file or it wont work. Mine was located at ~/.chef/knife.rb</p>
<pre><code>log_level :info
log_location STDOUT
node_name 'local'
client_key '/home/ubuntu/.chef/local.pem'
validation_client_name 'chef-validator'
validation_key '/etc/chef/validation.pem'
&lt;strong&gt;chef_server_url 'http://ec2-xx-xx-xx-xx.us-west-1.compute.amazonaws.com:4000/'&lt;/strong&gt;
cache_type 'BasicFile'
cache_options( :path =&gt; '/home/ubuntu/.chef/checksums' )
</code></pre>
<p>Ok that&rsquo;s it for today. Next time I will talk about recipes and cookbooks!</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Protip: Delete words on iTerm2 (Mac OSX)]]></title>
<link href="http://felipecsl.com/blog/2012/06/05/protip-delete-words-on-iterm2-mac-osx/"/>
<updated>2012-06-05T13:18:19-03:00</updated>
<id>http://felipecsl.com/blog/2012/06/05/protip-delete-words-on-iterm2-mac-osx</id>
<content type="html"><![CDATA[<p>I don&rsquo;t usually post short tips, but this one is really killer. To delete one word at a time when pressing ⌘+Delete in the iTerm2, use this configuration as shown in the first line of the screenshot below (click to expand).</p>
<p><a href="http://felipecsl.com/images/2012/06/Screen-Shot-2012-06-05-at-12.00.02-PM.png"><img src="http://felipecsl.com/images/2012/06/Screen-Shot-2012-06-05-at-12.00.02-PM.png" alt="" /></a></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Wombat 0.4.0 released!]]></title>
<link href="http://felipecsl.com/blog/2012/05/25/wombat-0-4-0-released/"/>
<updated>2012-05-25T01:46:22-03:00</updated>
<id>http://felipecsl.com/blog/2012/05/25/wombat-0-4-0-released</id>
<content type="html"><![CDATA[<p>It has been a while since I&rsquo;ve been working on this Ruby <a href="https://rubygems.org/gems/wombat">gem</a> called <a href="https://github.com/felipecsl/wombat">wombat</a> and thought it would be worth talking about it here. I just released today the version 0.4.0 with some bug fixes and the addition of another helper method, as you can see in the GitHub <a href="https://github.com/felipecsl/wombat">readme</a>.</p>
<p>Wombat is a gem for making extracting information from web pages, or just crawling, a little less painful. If you were to do that today, you&rsquo;d probably have to fall back to Nokogiri or another lower level gem and process the data yourself. With Wombat, you can define a structure of how your data should look like in a DSL like format, and Wombat does the work for you. It is pretty handy actually.</p>
<p>It all started with the project <a href="http://noitehoje.com.br">Noite Hoje</a> where we used to grab shows and parties informations from 3rd party sites and display there, like an indexer. When working on that, I noticed how painful and repetitive it can be to crawl web pages sometimes.</p>
<p>Anyway, if Wombat is/was useful for you, please don&rsquo;t hesitate to drop me a line in the comments, I would be very happy to know.</p>
<p>After meeting great people like <a href="https://twitter.com/#!/paul_irish">Paul Irish</a> and <a href="https://twitter.com/#!/wycats">Yehuda Katz</a>, you kinda feel almost obligated to share some knowledge with your peers and give back to the open source community.</p>
<p>By the way, if you work with Resque, you should probably also check the great <a href="https://github.com/kensodev/perform_later">perform_later</a> gem by my friend and coworker <a href="http://kensodev.com/">Avi Tzurel</a>, to which I also have been contributing.</p>
<p>Cheers!</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[My story about moving to the Silicon Valley]]></title>
<link href="http://felipecsl.com/blog/2012/02/13/my-story-about-moving-to-the-silicon-valley/"/>
<updated>2012-02-13T02:27:49-02:00</updated>
<id>http://felipecsl.com/blog/2012/02/13/my-story-about-moving-to-the-silicon-valley</id>
<content type="html"><![CDATA[<p>A lot of people I meet seem very curious and I am usually asked some details about how the hell I left my job at <a href="http://thoughtworks.com">ThoughtWorks</a> Brazil and moved to San Francisco to work for <a href="http://gogobot.com/">Gogobot</a>. Overall, the transition was pretty simple. Getting the H1B visa was a lot simpler and faster than I expected. It took me around 2 months from the date Gogobot entered the petition to the US Gov. to the day I got my passport back with the visa stamped on it. However I was a bit lucky, since there is an early quota of 60 thousand H1B visas that US will distribute, starting every year in April. I applied around October, so the quota was almost over, but I was lucky enough to make it. Another friend that applied a few weeks later wasn&rsquo;t so lucky and will have to wait till April till the window opens again.</p>
<p>Another thing that people usually ask me is how I got to know the company and how did I get the job. From my personal experience and from what I heard from other fellow developers, companies are not very receptive to the idea of sponsoring an H1B visa to a foreign developer, probably due to the high cost and risk involved with it. The usual flow is: you get in touch with the company you are interested in, probably send an email with a nice cover letter and your resume attached, and get no response at all. The only advice I will say is: If you really want that, be persistant and keep trying. Write a nice cover letter, with a brief introduction of who you are and why you want to work for that company. Try to be creative, original, otherwise you will be treated just like yet another average foreign candidate. Also extremely important is showing your achievements, having one or more pet projects is a big plus, actively contributing to open source, etc.</p>
<p>It is definitely not an easy task, nor an easy decision to leave everything behind and move to another country. Anyways, I still believe it is something extremely valuable in terms of career growth and life experience. If you can, I would advice you to do it! The sooner the better. As you get older, buy a house, have kids, marry, etc., it will be harder and harder to do something like this. For the same reasons, I support <a href="http://felipecsl.com/2011/03/o-que-eu-aprendi-empreendendo-e-porque-voce-tambem-deveria-tentar/">entrepreneurism</a>.</p>
<p>I&rsquo;ve been here for almost one month so far, and right now, I can say I am very happy to have taken this decision, despite having to leave friends, family and my beloved fiancee behind. We will try to soften the hurt by visiting each other periodically.</p>
<p><a href="http://www.gogobot.com/?utm_source=web&amp;utm_medium=link&amp;utm_campaign=goodies&amp;utm_content=medium_alt_btn_grey"><img src="http://cdn1.gbot.me/img/buttons/gbLargeAltGrey_01.png" alt="Gogobot" /></a></p>
<p>All in all, <a href="http://gogobot.com">Gogobot</a> is a great place to be, where I have the opportunity to work very talented and nice people and also experience the uniqueness of working for a Silicon Valley tech startup with milions of users and great media exposure!</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[ThoughtWorks 6 meses depois]]></title>
<link href="http://felipecsl.com/blog/2011/07/06/thoughtworks-6-meses-depois/"/>
<updated>2011-07-06T22:14:08-03:00</updated>
<id>http://felipecsl.com/blog/2011/07/06/thoughtworks-6-meses-depois</id>
<content type="html"><![CDATA[<p>Desde Janeiro estou trabalhando como consultor na ThoughtWorks, no &ldquo;prédio novo&rdquo; do TecnoPUC. Posso afirmar com plena convicção que estou muito feliz de ter tomado essa decisão e, por isso, resolvi que seria legal compartilhar um pouco de como é o nosso dia-a-dia lá dentro.</p>
<p>A primeira impressão ao entrar no escritório é de que algo ali é diferente das empresas de TI tradicionais. Não há baias, apenas grandes mesas sem divisórias. Pessoas trabalhando em pares, ou seja, dois para cada computador, <em><a href="http://en.wikipedia.org/wiki/Nerf_Blasters">NERF darts</a></em> pelo chão, video-games no <em>lounge</em> e pessoas falando alto.</p>
<p><img src="http://a.yfrog.com/img736/1552/xebt.jpg" alt="" /></p>
<p>A ThoughtWorks é conhecida mundialmente por ser uma das pioneiras na prática de metodologias ágeis. Isto faz com que ela tenha uma cultura à parte, que incentiva a colaboração e entrega de alto valor agregado. A empresa tem uma missão um tanto quanto ousada: &ldquo;Revolucionar a TI&rdquo;. Para tal, possui um modelo de <a href="http://martinfowler.com/bliki/ThreePillars.html">três pilares</a> que descrevem os seus principais propósitos: <strong><em>Sustainable Business</em></strong> (negócio sustentável), <strong><em>Software Excellence </em></strong>(excelência em software)  e <strong><em>Social Justice </em></strong>(justiça social). Os dois primeiros são totalmente esperados para uma empresa de TI, entretanto, justiça social é um valor que poucas empresas valorizam tanto quanto a ThoughtWorks. Isto faz com que procuremos antender apenas clientes que se alinhem com o nosso perfil, evitando fazer negócios com aquelas que vão de encontro com estes valores.</p>
<p>Entretanto, o que mais me chamou a atenção lá foi a autonomia que é dada a cada funcionário. Em primeiro lugar, não há uma hierarquia definida e nem um conceito de &ldquo;chefe&rdquo;. Cada um exerce seu papel e é exigido de acordo com o que é esperado dele. Caso você não esteja satisfeito com alguma coisa, você tem toda a liberdade para propor uma sugestão ou conversar com seus colegas para tentar identificar problemas e propor soluções. Essa é uma característica bastante única na minha opinião e foi a que levei mais tempo para assimilar completamente. Por estes motivos, não é raro ouvir pessoas falando que é necessário &ldquo;desconstruir&rdquo; para então &ldquo;reconstruir&rdquo; vários conceitos quando se entra na empresa, quase como uma lavagem cerebral. :)</p>
<p>O processo seletivo da ThoughtWorks é bastante completo, exigente e extensivo. A empresa se propõe a ser um lugar para as mentes mais brilhantes naquilo que fazem. Por isso, costuma-se <a href="http://www.thoughtworks.com/our-process">dizer</a> que ser um <em>Thoughtworker</em> não é para qualquer um. São testadas várias características do candidato, desde valores e perfil até capacidade de raciocínio lógico e habilidades como programador. São várias etapas classificatórias e eliminatórias. O processo todo não costuma levar menos de um mês por candidato, conversando com cerca de 10 pessoas no total. Apesar de difícil, não é impossível, afinal, eu consegui. :)</p>
<p>A TW também é uma empresa global por natureza. Com diversos escritórios pelo mundo todo, a rotação entre países é incentivada. Com isso, é muito comum termos muitos Thoughtworkers viajando entre escritórios e trabalhando nos clientes. Durante qualquer período do ano, temos pelo menos 10% de <em>expats</em> (TWers que vieram de outros países) no escritório da TW Brazil. Lá dentro, a língua oficial é o Inglês. O <a href="http://www.leonardoborges.com/writings/2011/04/25/one-year-of-thoughtworks-a-retrospective/">Leonardo Borges</a>, que trabalha na TW Austrália e escreveu sobre as experiências dele, não me deixa mentir! Nenhum escritório tem mais de aproximadamente 150 funcionários. Quando ele atinge este limite, geralmente ele pára de crescer e procura-se outro lugar para abrir um novo. O motivo disto é muito simples: Depois de uma certa quantidade de pessoas dentro de um escritório, acaba se tornando muito difícil de conhecer todo mundo e de se manter um relacionamento estreito entre as pessoas. Por isso, existe este limite que procura garantir a identidade da empresa e a interação entre as pessoas.</p>
<p><img src="http://farm5.static.flickr.com/4068/4644495269_aae6fa26d9_z.jpg" alt="" /></p>
<p>Com esse post, espero ter passado uma noção de como funciona nosso dia-a-dia na TW e ter mostrado como ela é uma empresa realmente diferente. Para quem já trabalhou em uma startup ou uma empresa bem pequena (menos de uns 20 funcionários, por exemplo), vai se sentir em casa lá dentro, pois, apesar de ter mais de 1600 funcionários no mundo todo, a TW parece continuar mantendo as boas características de startups de tecnologia onde tudo é flexível e se trabalha com prazer. :)</p>
<p>Se você curtiu a TW, é apaixonado pelo que faz e gostaria trabalhar conosco, não deixe de entrar em contato. Afinal, <a href="http://www.thoughtworks.com/current-opportunities">estamos contratando</a>. :D</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Dica de ferramenta para Mac: TotalFinder]]></title>
<link href="http://felipecsl.com/blog/2011/04/17/dica-de-ferramenta-para-mac-totalfinder/"/>
<updated>2011-04-17T16:05:11-03:00</updated>
<id>http://felipecsl.com/blog/2011/04/17/dica-de-ferramenta-para-mac-totalfinder</id>
<content type="html"><![CDATA[<p>Assistindo <a href="http://net.tutsplus.com/tutorials/html-css-techniques/automated-optimization-with-html5-boilerplate-build/">esse</a> screencast muito interessante do Paul Irish sobre o build script do ótimo projeto HTML5 boilerplate, também criado por ele mesmo, prestei atenção nas ferramentas que ele utiliza no Mac. Entre elas, pude notar o <a href="http://www.iterm2.com/#/section/home">iTerm 2</a>, que é um ótimo substituto para o Terminal padrão do Mac, e o excelente <a href="http://totalfinder.binaryage.com/">TotalFinder</a>, que adiciona várias funcionalidades muito úteis ao Finder padrão, como Tabs e Cut &amp; Paste por exemplo, ambas extremamente úteis, na minha opinião. Fica aqui então a recomendação destas duas ótimas ferramentas pra Mac, especialmente o TotalFinder :)</p>
<p>Até mais!</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[O que eu aprendi empreendendo e porque você também deveria tentar]]></title>
<link href="http://felipecsl.com/blog/2011/03/29/o-que-eu-aprendi-empreendendo-e-porque-voce-tambem-deveria-tentar/"/>
<updated>2011-03-29T21:54:44-03:00</updated>
<id>http://felipecsl.com/blog/2011/03/29/o-que-eu-aprendi-empreendendo-e-porque-voce-tambem-deveria-tentar</id>
<content type="html"><![CDATA[<p>Me motivei a escrever um pouco sobre empreendedorismo pois tenho visto muitas pessoas que no fundo têm vontade de inovar, empreender, mudar, mas geralmente sentem medo de dar uma virada brusca em suas vidas, sair da zona de conforto e arriscar. Pois eu vou relatar um pouco sobre o que eu aprendi com isso tudo durante o último ano, em que eu me envolvi bastante com este tópico.</p>
<p>Em primeiro lugar, vejo o perfil de empreendedor como sendo, antes de tudo, um <em>mindset.</em> Você não precisa desistir do seu emprego, abrir uma empresa, arriscar tudo o que tem tentando um negócio feito louco só pra ver como é empreender. Vejo este perfil em algumas pessoas que não necessariamente arriscaram tudo, assim como eu fiz, mas mesmo assim possuem este <em>mindset.</em> Eu me refiro a pensar &ldquo;fora da caixa&rdquo;, inovar, arriscar, tentar coisas diferentes. Essa semana um aluno de Ciência da Computação da UFRGS criou um site chamado <a href="http://poabus.com.br">poabus.com.br</a> para mapear as linhas de ônibus de Porto Alegre, já que o sistema que a EPTC oferece é praticamente inusável. Eu certamente diria que este cidadão chamado Bruno Jurkovski tem um perfil empreendedor, pois teve atitude para pensar diferente e experimentar uma nova solução para um problema existente. Eu diria que este tipo de perfil já caracteriza um empreendedor. Ou seja, você não precisa largar seu emprego de full-time para ser um empreendedor. Você pode começar hoje mesmo pensando em como você pode fazer alguma coisa para mudar o mundo à sua volta, utilizar as suas <em>skills</em> para contribuir para um mundo melhor. Um bom começo é contribuir para um projeto open source, por exemplo. Caso você não seja da área de TI, procure outras formas de inovar, solucionar problemas e deixe sua contribuição para a humanidade fazendo algo diferente, que tenha impacto sobre o mundo ao seu redor.</p>
<p>Alguns podem se perguntar &ldquo;por que&rdquo;? Por que isso é legal? O que eu ganho com isso? Eu posso afirmar que você ganha muito, tanto como pessoa, quanto como profissional. No meu caso, como um dos fundadores da <a href="http://quavio.com.br">Quavio</a>, aprendi tanto em um ano como se tivesse se passado 5 anos. Você aprende aquilo que nenhum colégio te ensina: como se virar sozinho, como contratar um funcionário, como cuidar das finanças da empresa, como atender bem o cliente, como vender o seu produto, como se posicionar no mercado, entre muitas outras coisas que você acaba nem notando! No final das contas, o resultado disso tudo é que você acaba se tornando uma pessoa melhor. E isto é extraordinário! Fundar uma empresa pra mim foi uma tempestade de novas experiências a cada dia. Não vou dizer que tudo são mil maravilhas, pois não são. Você acaba tendo que engolir muito sapo, aceitar críticas, aceitar perder, errar, cair e tentar novamente. Errar faz parte do processo diário. É apenas errando que realmente aprendemos como não fazer :)</p>
<p>Certa vez eu li em algum lugar que, geralmente, aquilo que te faz sentir um nervoso, um frio na barriga e apreensão é aquilo que te trará um aprendizado que não irá esquecer. E isto acontece todo dia na vida de um empreendedor :)</p>
<p>Hoje não faço mais parte da Quavio por motivos principalmente financeiros meus, entretanto, mantenho meu <strong>mindset</strong> empreendedor, sempre tentando encontrar maneiras de resolver problemas de um jeito diferente. É por isso que este tipo de pessoa costuma sempre ter uma idéia brilhante para compartilhar e receber feedback de um amigo. Até porque, se você acha que a sua idéia é brilhante e valiosa demais para ser aberta para os outros, provavelmente você está errado :) Se você tem medo que alguém roube sua idéia revolucionária, não se preocupe com isso. Uma idéia não vale nada. Muito mais vale uma idéia bem executada. Se alguém roubar a sua idéia e colocá-la em prática, isto significa que provavelmente aquela era a pessoa certa para executá-la, e não você :)</p>
<p>Atualmente tenho me dedicado a um projeto pessoal com alguns amigos, chamado <a href="http://noitehoje.com.br">Noite Hoje</a>. Nossa idéia é mudar a maneira como as pessoas procuram uma festa ou um show para ir, acessando de uma maneira simples e rápida de qualquer lugar, tanto o seu computador, quando seu smartphone.</p>
<p>Para quem quiser ler mais, o site <a href="http://www.softwarebyrob.com/">Software by Rob</a> é focado no assunto e tem ótimas boas leituras. Enfim, espero ter plantado a semente em alguém que esteja se sentindo desconfortável com a sua atual situação de vida e quer dar um real sentido para ela. Este é o seu momento hehehe.</p>
<p>Até a próxima :D</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Heroku + MongoHQ + Mongoid]]></title>
<link href="http://felipecsl.com/blog/2011/01/31/heroku-mongohq-mongoid/"/>
<updated>2011-01-31T21:00:08-02:00</updated>
<id>http://felipecsl.com/blog/2011/01/31/heroku-mongohq-mongoid</id>
<content type="html"><![CDATA[<p>O <a href="http://heroku.com">Heroku</a> possui um Add-on muito interessante para trabalhar com <a href="http://www.mongodb.org/">MongoDB</a> chamado <a href="https://mongohq.com/home">MongoHQ</a>. Como de costume, existe tanto um plano grátis, quanto planos pagos.</p>
<p>Isso significa que é possível levantar um site com Ruby on Rails ou Sinatra e MongoDB rapidinho e sem desembolsar um centavo com o Heroku.. Damn good.</p>
<p>Como nem tudo é perfeito, a <a href="http://docs.heroku.com/mongohq#gem-options-and-setup">documentação</a> do add-on MongoHQ ainda é um tanto quanto &ldquo;magra&rdquo; e praticamente nem fala no Mongoid. Perdi uns vários minutos googleando e nada. Até que então encontrei o método from_uri na classe Connection do driver de mongo para Ruby.</p>
<p>Ao habilitarmos este add-on, o Heroku seta uma variável de ambiente chamada MONGOHQ_URL, que utilizamos para estabelecer a conexão com o Mongo server. Não encontrei em lugar nenhum como fazer esta conexão, então deixo aqui caso alguém precise:</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Problemas com IFrame no IE?]]></title>
<link href="http://felipecsl.com/blog/2011/01/11/problemas-com-iframe-no-ie/"/>
<updated>2011-01-11T13:03:33-02:00</updated>
<id>http://felipecsl.com/blog/2011/01/11/problemas-com-iframe-no-ie</id>
<content type="html"><![CDATA[<p>Pois é. Você não é o único. Apesar de funcionar às mil maravilhas em outros navegadores, o IE parece considerar o IFrame uma ameaça de segurança e se recusa a aceitar cookies de sites de terceiros dentro de um IFrame. Legal né? Isto dificulta qualquer tentativa de colocar um formulário de login dentro de um IFrame, por exemplo ou, ainda mais comum, colocar a janela de pagamento de um gateway (no meu caso, <a href="https://pagseguro.uol.com.br/">PagSeguro</a>) dentro de um IFrame funcionando como uma sobretela. Isto funciona perfeitamente, mas não no IE.</p>
<p>A questão toda é que o IE não irá aceitar nenhum cookie do PagSeguro, pois ele está fora do domínio principal do site que está sendo acessado no momento (o site onde você está tentando comprar algum produto, por exemplo). A maneira para contornarmos esse problema, seria implementando um mecanismo chamado <a href="http://www.w3.org/P3P/">P3P</a>.O que este negócio aí faz, é criar um mecanismo de expor para o browser qual é a política de privacidade do site de maneira programática (através de um cabeçalho no response).</p>
<p>Exemplo:</p>
<pre><code>response["P3P"] = 'CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"'
</code></pre>
<p>Cada uma destas siglas de 3 ou 4 letras acima tem um significado, como por exemplo, expor como o site utiliza os dados do usuário. Esta propriedade deve ser incluida no cabeçalho de todas as páginas que setam um cookie no browser. Mais informações <a href="http://stackoverflow.com/questions/389456/cookie-blocked-not-saved-in-iframe-in-internet-explorer">aqui</a> e <a href="http://adamyoung.net/IE-Blocking-iFrame-Cookies">aqui</a>.</p>
<p>Isto é o que acontece quando os seus cookies forem bloqueados :)</p>
<p><a href="http://felipecsl.com/images/2011/01/p3p.png"><img src="http://felipecsl.com/images/2011/01/p3p-300x211.png" alt="" /></a></p>
<p>No caso do PagSeguro, o que acabava acontecendo é um erro dizendo &ldquo;Sua sessão expirou.&rdquo;.</p>
<p>Na minha opinião, é responsabilidade do PagSeguro implementar P3P em suas páginas para possibilitar que os desenvolvedores possam utilizar este recurso, já que, como não temos controle sobre o PagSeguro, não temos como implementar o cabeçalho customizado do P3P nas responses.</p>
<p>Com isso, acabamos tendo que mudar a interface para não utilizarmos mais o IFrame, pois não encontramos solução para o problema!</p>
<p>Bom, fica aí mais um motivo pra você que já odiava o IE, agora tem um motivo a mais :D</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Problemas de Timezone com Ruby?]]></title>
<link href="http://felipecsl.com/blog/2010/12/12/problemas-de-timezone-com-ruby/"/>
<updated>2010-12-12T20:45:15-02:00</updated>
<id>http://felipecsl.com/blog/2010/12/12/problemas-de-timezone-com-ruby</id>
<content type="html"><![CDATA[<p>Timezones podem ser uma dor de cabeça quando trabalhamos muito com datas. Em ruby, existem pelo menos três classes diferentes para manipulação de data/hora: Date, Time e DateTime.</p>
<p>O nosso grande problema foi que, ao salvar uma data/hora no banco mySQL via ActiveRecord, o timezone estava sendo normalizado para UTC +00:00, enquanto o timezone do sistema é GMT -02:00, pois estamos em horário brasileiro de verão. Normalmente seria GMT -03:00).</p>
<p>Isto se torna um problema quando precisamos comparar datas, que vêm do banco de dados, com a data atual. Utilizando Time.now, estávamos observando um offset de 2h dado pela diferença entre o timezone do banco e o do sistema.</p>
<p>Para solucionar o problema, precisamos normalizar os dois objetos para o mesmo timezone. Resolvemos isto utilizando o método Time.utc, que monta um novo objeto Time baseado no UTC:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">module</span> <span class="nn">ApplicationHelper</span>
</span><span class='line'> <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">get_utc_time</span>
</span><span class='line'> <span class="n">now</span> <span class="o">=</span> <span class="no">Time</span><span class="o">.</span><span class="n">now</span>
</span><span class='line'> <span class="no">Time</span><span class="o">.</span><span class="n">utc</span><span class="p">(</span><span class="n">now</span><span class="o">.</span><span class="n">year</span><span class="p">,</span> <span class="n">now</span><span class="o">.</span><span class="n">month</span><span class="p">,</span> <span class="n">now</span><span class="o">.</span><span class="n">day</span><span class="p">,</span> <span class="n">now</span><span class="o">.</span><span class="n">hour</span><span class="p">,</span> <span class="n">now</span><span class="o">.</span><span class="n">min</span><span class="p">,</span> <span class="n">now</span><span class="o">.</span><span class="n">sec</span><span class="p">)</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<p>Caso alguém tenha uma solução mais elegante para o problema, aceito sugestões :)</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Traduzindo HTML utilizando a API do Google Translate]]></title>
<link href="http://felipecsl.com/blog/2010/11/23/traduzindo-html-utilizando-a-api-do-google-translate/"/>
<updated>2010-11-23T11:44:42-02:00</updated>
<id>http://felipecsl.com/blog/2010/11/23/traduzindo-html-utilizando-a-api-do-google-translate</id>
<content type="html"><![CDATA[<p>Pessoal,</p>
<p>Para quem quiser utilizar a API do Google Translate para traduzir partes de um documento HTML, segue uma implementação que tive que fazer recentemente utilizando jQuery e ASP.NET MVC:</p>
<ul>
<li>Controller Action para fazer o request para a API REST:</li>
</ul>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
</pre></td><td class='code'><pre><code class='c#'><span class='line'><span class="na">[ValidateInput(false)]</span>
</span><span class='line'><span class="k">public</span> <span class="n">ActionResult</span> <span class="nf">Translate</span><span class="p">(</span><span class="n">FormCollection</span> <span class="n">vars</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'><span class="kt">string</span> <span class="n">reqUri</span> <span class="p">=</span> <span class="s">&quot;https://www.googleapis.com/language/translate/v2?key=YOUR_GOOGLE_API_KEY&amp;source=pt&amp;target=en&amp;format=html&quot;</span><span class="p">;</span><span class="err">`</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="kt">var</span> <span class="n">webRequest</span> <span class="p">=</span> <span class="p">(</span><span class="n">HttpWebRequest</span><span class="p">)</span><span class="n">WebRequest</span><span class="p">.</span><span class="n">Create</span><span class="p">(</span><span class="n">reqUri</span><span class="p">);</span><span class="err">`</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="n">webRequest</span><span class="p">.</span><span class="n">ContentType</span> <span class="p">=</span> <span class="s">&quot;application/x-www-form-urlencoded&quot;</span><span class="p">;</span>
</span><span class='line'><span class="n">webRequest</span><span class="p">.</span><span class="n">Method</span> <span class="p">=</span> <span class="s">&quot;POST&quot;</span><span class="p">;</span><span class="err">`</span>
</span><span class='line'>
</span><span class='line'><span class="kt">byte</span><span class="p">[]</span> <span class="n">bytes</span> <span class="p">=</span> <span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="n">GetBytes</span><span class="p">(</span><span class="s">&quot;q=&quot;</span> <span class="p">+</span> <span class="n">vars</span><span class="p">[</span><span class="s">&quot;q&quot;</span><span class="p">]);</span>
</span><span class='line'><span class="n">webRequest</span><span class="p">.</span><span class="n">ContentLength</span> <span class="p">=</span> <span class="n">bytes</span><span class="p">.</span><span class="n">Length</span><span class="p">;</span>
</span><span class='line'><span class="n">webRequest</span><span class="p">.</span><span class="n">Headers</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="s">&quot;X-HTTP-Method-Override&quot;</span><span class="p">,</span> <span class="s">&quot;GET&quot;</span><span class="p">);</span><span class="err">`</span>
</span><span class='line'>
</span><span class='line'><span class="k">using</span> <span class="p">(</span><span class="n">Stream</span> <span class="n">outputStream</span> <span class="p">=</span> <span class="n">webRequest</span><span class="p">.</span><span class="n">GetRequestStream</span><span class="p">())</span> <span class="p">{</span>
</span><span class='line'><span class="n">outputStream</span><span class="p">.</span><span class="n">Write</span><span class="p">(</span><span class="n">bytes</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">bytes</span><span class="p">.</span><span class="n">Length</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="k">using</span> <span class="p">(</span><span class="n">WebResponse</span> <span class="n">webResponse</span> <span class="p">=</span> <span class="n">webRequest</span><span class="p">.</span><span class="n">GetResponse</span><span class="p">())</span> <span class="p">{</span>
</span><span class='line'><span class="k">if</span> <span class="p">(</span><span class="n">webResponse</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'><span class="k">return</span> <span class="k">null</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'><span class="k">using</span> <span class="p">(</span><span class="n">StreamReader</span> <span class="n">sr</span> <span class="p">=</span> <span class="k">new</span> <span class="n">StreamReader</span><span class="p">(</span><span class="n">webResponse</span><span class="p">.</span><span class="n">GetResponseStream</span><span class="p">()))</span> <span class="p">{</span>
</span><span class='line'><span class="k">return</span> <span class="nf">Content</span><span class="p">(</span><span class="n">sr</span><span class="p">.</span><span class="n">ReadToEnd</span><span class="p">().</span><span class="n">Trim</span><span class="p">(),</span> <span class="s">&quot;text/json&quot;</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>Algumas considerações sobre esta action:</p>
<ul>
<li><p>Não estamos validando o parâmetro <strong>q</strong> de entrada. Seria interessante validar a presença deste parâmetro no form e lançar uma exceção caso ele não esteja presente;</p></li>
<li><p>Não esqueça de passar para o parâmetro <strong>key</strong> a sua chave da API do google;</p></li>
<li><p>Uma melhoria seria receber via form também o código do idioma fonte e destino (parâmetros <strong>source</strong> e <strong>target</strong> da URL).</p></li>
</ul>
<p>Vamos agora para o código de cliente (JavaScript) para atualizar a view:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">var</span> <span class="nx">translateUrl</span> <span class="o">=</span> <span class="s1">&#39;&lt;%= Url.Action(&quot;Translate&quot;) %&gt;&#39;</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="nx">$</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span><span class='line'><span class="nx">translateElement</span><span class="p">(</span><span class="s2">&quot;caseText&quot;</span><span class="p">);</span>
</span><span class='line'><span class="nx">translateElement</span><span class="p">(</span><span class="s2">&quot;caseTitle&quot;</span><span class="p">);</span>
</span><span class='line'><span class="p">});</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="kd">function</span> <span class="nx">translateElement</span><span class="p">(</span><span class="nx">elemId</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'><span class="nx">$</span><span class="p">.</span><span class="nx">ajax</span><span class="p">({</span>
</span><span class='line'><span class="nx">url</span><span class="o">:</span> <span class="nx">translateUrl</span><span class="p">,</span>
</span><span class='line'><span class="nx">dataType</span><span class="o">:</span> <span class="s1">&#39;json&#39;</span><span class="p">,</span>
</span><span class='line'><span class="nx">type</span><span class="o">:</span> <span class="s1">&#39;POST&#39;</span><span class="p">,</span>
</span><span class='line'><span class="nx">data</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'><span class="nx">q</span><span class="o">:</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&quot;#&quot;</span> <span class="o">+</span> <span class="nx">elemId</span><span class="p">).</span><span class="nx">html</span><span class="p">()</span>
</span><span class='line'><span class="p">},</span>
</span><span class='line'><span class="nx">success</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">json</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'><span class="c1">// TODO: I&#39;ve found that the returned translated text from Google may be incomplete</span>
</span><span class='line'><span class="c1">// if we send any   elements within the text. We should remove any occurrences</span>
</span><span class='line'><span class="c1">// of this in the input string.</span>
</span><span class='line'><span class="nx">$</span><span class="p">(</span><span class="s2">&quot;#&quot;</span> <span class="o">+</span> <span class="nx">elemId</span><span class="p">).</span><span class="nx">html</span><span class="p">(</span><span class="nx">unescape</span><span class="p">(</span><span class="nx">json</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">translations</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">translatedText</span><span class="p">));</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'><span class="p">});</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>O que este código faz é apenas chamar a Action <strong>Translate</strong> que, por sua vez, chama a API do Google e retorna o JSON com o texto traduzido. Feito isso, a função <strong>translateElement</strong> apenas atualiza o conteúdo do elemento com a tradução.</p>
<p>Acredito que o código seja auto explicativo. Qualquer dúvida, por favor perguntem nos comentários :)</p>
<p>Documentação da api <a href="http://code.google.com/apis/language/translate/v2/using_rest.html">aqui</a> e <a href="http://code.google.com/apis/language/translate/v2/getting_started.html#JSONP">aqui</a>.</p>
<p>Abraços!</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Configurando uma ferramenta de diff visual para SVN no Linux]]></title>
<link href="http://felipecsl.com/blog/2010/11/08/configurando-uma-ferramenta-de-diff-visual-para-svn-no-linux/"/>
<updated>2010-11-08T21:37:56-02:00</updated>
<id>http://felipecsl.com/blog/2010/11/08/configurando-uma-ferramenta-de-diff-visual-para-svn-no-linux</id>
<content type="html"><![CDATA[<p>Cá estou eu aqui postando após vários dias parado. Tenho trabalhado diariamente com Ubuntu durante os últimos dias devido a um projeto em Rails 3 que estamos tocando aqui na <a href="http://www.quavio.com.br">Quavio</a>. Por isso, tenho penado bastante com configuração de ambiente, familiarização com o framework (ainda estou aprendendo muito) e, consequentemente, tenho tido ainda menos tempo do que o usual.</p>
<p>Vou aproveitar só para deixar um rápido passo-a-passo que utilizei para configurar uma ferramenta de diff visual para SVN no Ubuntu. No Windows eu vinha usando o ótimo <a href="http://winmerge.org/">WinMerge</a>. No Linux, fui recomendado ao <a href="http://meld.sourceforge.net/">meld</a> nesta <a href="http://stackoverflow.com/questions/1141686/visual-svn-diff-and-compare-tools-for-linux">pergunta</a> no stackoverflow. Bom, vamos aos passos:</p>
<ul>
<li><p><code>sudo apt-get install meld</code></p></li>
<li><p><code>gedit ~/diffwrap.sh</code></p></li>
<li><p>Cole o seguinte conteúdo dentro do arquivo recém criado e salve (adaptado <a href="http://svnbook.red-bean.com/en/1.2/svn.advanced.externaldifftools.html">daqui</a>):</p>
<p>!/bin/sh</p>
<h1>Configure your favorite diff program here.</h1>
<p>DIFF=&ldquo;/usr/local/bin/my-diff-tool&rdquo;</p>
<h1>Subversion provides the paths we need as the sixth and seventh</h1>
<h1>parameters.</h1>
<p>LEFT=${6}
RIGHT=${7}</p>
<h1>Call the diff command (change the following line to make sense for</h1>
<h1>your merge program).</h1>
<p>$DIFF $LEFT $RIGHT</p>
<h1>Return an errorcode of 0 if no differences were detected, 1 if some were.</h1>
<h1>Any other errorcode will be treated as fatal.</h1></li>
<li><p><code>chmod a+x diffwrap.sh</code></p></li>
<li><p><code>gedit ~/.subversion/config</code></p></li>
<li><p>Procure a linha onde diz &ldquo;diff-cmd&rdquo; (linha 57, no meu arquivo)</p></li>
<li><p>Remove o &ldquo;#&rdquo; do início da linha para descomentá-la</p></li>
<li><p>Digite /home/<nome do seu usuário>/diffwrap.sh no final da linha. O resultado deve ser algo assim: <code>diff-cmd = /home/&lt;nome do usuario&gt;/diffwrap.sh
</code>. Não esqueça de trocar <strong><nome do usuário></strong> pelo nome real do seu usuário no sistema</p></li>
<li><p>Pronto. Ao digitar <code>svn diff arquivo.rb</code>, o meld será executado com dois arquivos abertos: o seu, com suas modificações locais, e a cópia origina que está no repositório.</p></li>
</ul>
<p>Por hoje é isso. Nos próximos dias pretendo escrever um guia sobre como publicar uma aplicação Rails 3 do zero num cloud server do Rackspace.</p>
<p>Até mais!</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Opções de hospedagem para Ruby]]></title>
<link href="http://felipecsl.com/blog/2010/10/14/opcoes-de-hospedagem-para-ruby/"/>
<updated>2010-10-14T20:34:37-03:00</updated>
<id>http://felipecsl.com/blog/2010/10/14/opcoes-de-hospedagem-para-ruby</id>
<content type="html"><![CDATA[<p>Na Quavio, empresa onde trabalho e sou um dos sócios, uma boa parcela dos projetos executados são basicamente sites e hotsites de clientes. Isto implica em necessidades específicas de hospedagem, como por exemplo, uma boa infraestrutura para envio/recebimento de emails (com webmail), painel de controle, suporte técnico para eventuais problemas de instabilidade, baixo custo mensal, pouca utilização de espaço em disco, backup automático, etc. Basicamente, não queremos nos responsabilizar pelo gerenciamento da infra-estrutura que abriga cada um destes projetos, que acabam se tornando muitos com o passar do tempo.</p>
<p>Resumindo, precisamos de um fornecedor de hospedagem que atenda a estas necessidades e que, ao mesmo tempo, ofereça um bom suporte às tecnologias que utilizamos.</p>
<p>Recentemente, decidimos por utilizar exclusivamente Ruby e, preferencialmente, Sinatra (ou Rails) para este tipo de projeto devido a uma série de fatores. Eu diria que os principais seriam a extrema simplicidade de uso e alta produtividade que obtemos com estas ferramentas.</p>
<p>Esta decisão, por sua vez, implica em uma maior dificuldade na escolha do nosso fornecedor de hospedagem, visto que a maioria deles simplesmente não possui suporte a estas tecnologias ou, se possui, é bastante precária ou ineficiente.</p>
<p>Vínhamos utilizando até então, preferencialmente, a <a href="http://www.kinghost.com.br">Kinghost</a> para este fim. Entretanto, chegamos à conclusão de que o suporte a Ruby on Rails é bastante precário e limitado. Isso pra não falar de Sinatra. Ao ligar para o Call Center, o atendente, que se dizia técnico, me pediu que soletrasse a palavra &ldquo;Sinatra&rdquo; para ele. Desisti na hora.</p>
<p>Outra opção era a <a href="http://www.kinghost.com.br">Locaweb</a>, que também vinhamos utilizamos em alguns casos específicos e que se mostrou relativamente satisfatória em se tratando de suporte a Ruby. Entretanto, considero o custo/benefício da Locaweb baixo (o preço da hospedagem compartilhada parte de R$ 29/mês) quando comparada a outras hospedagens gringas. Além disso, tivemos uns problemas sérios lá com SSH recentemente, o que nos deixou bastante insatisfeitos com o serviço da Locaweb também.</p>
<p>Não nos restaram muitas opções no Brasil, por isso estamos pensando seriamente em procurar alternativas fora daqui para hospedar nossos projetos. Eu realmente não queria ter que fazer isso, pois queriamos muito fomentar o desenvolvimento das hospedagens para essas tecnologias no Brasil, mas infelizmente não nos resta outra alternativa, já que não encontramos um fornecedor que nos atenda de forma satisfatória por aqui.</p>
<p>Não é à toa que vemos manifestações <a href="http://blog.kinghost.com.br/2010/09/empresas-de-hospedagem-de-sites-divulgam-esclarecimento/">deste tipo</a> no mercado brasileiro, pois concordo que a reclamação dos consumidores tem fundamento.</p>
<p>Aceito sugestões de boas hospedagens para Ruby/Sinatra/Rails no Brasil ou fora.</p>
<p>Me surpreendi positivamente esses dias com o excelente serviço que <a href="http://heroku.com">heroku</a> oferece. Achei fantástico. O deploy é simplesmente um git push. Mais fácil, impossível!</p>
<p>Seria muito bom termos algo nesse nível aqui no Brasil!</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[How to map subdomains in Windows 7]]></title>
<link href="http://felipecsl.com/blog/2010/08/20/how-to-map-subdomains-in-windows-7/"/>
<updated>2010-08-20T10:10:00-03:00</updated>
<id>http://felipecsl.com/blog/2010/08/20/how-to-map-subdomains-in-windows-7</id>
<content type="html"><![CDATA[<p>Over the last days, I&rsquo;ve been working on implementing multiple subdomains with authentication in asp.net for the webapp I&rsquo;m currently working on.</p>
<p>There are a few steps needed in order to configure IIS, DNS and authentication.</p>
<ol>
<li>Select the website you&rsquo;re working on, in the IIS Manager screen, in the left side tree. Then, click the &ldquo;Bindings&rdquo; option in the right pane</li>
</ol>
<p><img src="http://felipecsl.com/images/2010/8/bindings.png" alt="" /></p>
<ol>
<li><p>In the Site Bindings window, add each subdomain that you want to map to your site. Unfortunately, wildcard mapping is not supported, so you&rsquo;ll have to manually type all the subdomains before using it.</p></li>
<li><p>In the windows hosts file (C:\Windows\system32\drivers\etc\hosts), add up each subdomain again, pointing to 127.0.0.1 (see below):</p></li>
</ol>
<p><img src="http://felipecsl.com/images/2010/8/hosts.png" alt="" /></p>
<ol>
<li>Lastly, you&rsquo;ll need to update your web.config cookies configuration, so that it is set to work with any subdomain. By default, your login cookies will only be valid for the default subdomain (example.com and www.example.com)</li>
</ol>
<p><authentication mode="Forms"></p>
<p><forms loginUrl="~/Account/Login" timeout="500000" **domain=".example.com"** /></p>
<p></authentication></p>
<p>Pay attention for the starting dot in the domain attribute. It is needed in order to work with any subdomain ;)</p>
]]></content>
</entry>
</feed>