<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Pablo Cantero</title>
 <link href="http://www.pablocantero.com//atom.xml" rel="self"/>
 <link href="http://www.pablocantero.com//"/>
 <updated>2019-03-13T13:08:12+00:00</updated>
 <id>http://www.pablocantero.com/</id>
 <author>
   <name>Pablo Cantero</name>
   <email></email>
 </author>

 
 <entry>
   <title>Using Ruby and Amazon SQS FIFO Queues</title>
   <link href="http://www.pablocantero.com//blog/2017/08/06/using-ruby-and-amazon-sqs-fifo-queues/"/>
   <updated>2017-08-06T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2017/08/06/using-ruby-and-amazon-sqs-fifo-queues</id>
   
   <content type="html">&lt;p&gt;For those not familiar with &lt;a href=&quot;https://aws.amazon.com/sqs/&quot;&gt;Amazon SQS&lt;/a&gt;, it’s a reliable and high-scalable fully managed queue service. If you need to process in background, and all that jazz, but without the overhead of managing your queue service, go for it. It can be also &lt;a href=&quot;https://aws.amazon.com/sqs/pricing/&quot;&gt;free depending on our usage&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post is about &lt;a href=&quot;https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/FIFO-queues.html&quot;&gt;Amazon SQS FIFO Queues&lt;/a&gt;, which is a type of queue designed to guarantee that messages are processed exactly once and in the exact order that they are sent.&lt;/p&gt;

&lt;h2 id=&quot;shoryuken&quot;&gt;Shoryuken&lt;/h2&gt;

&lt;p&gt;The examples in this post are based on &lt;a href=&quot;https://github.com/phstc/shoryuken&quot;&gt;Shoryuken&lt;/a&gt;, a SQS thread based message processor in Ruby.&lt;/p&gt;

&lt;p&gt;Why Shoryuken? &lt;a href=&quot;https://github.com/aws/aws-sdk-ruby&quot;&gt;aws-sdk-ruby&lt;/a&gt; is great, but it’s just an API client. Shoryuken empowers the SDK by adding support for continuously polling and thread based processing, besides that, it also adds abstractions simplifying the integration with SQS, including &lt;a href=&quot;https://github.com/phstc/shoryuken/wiki/Rails-Integration-Active-Job&quot;&gt;Active Job support&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;fifo-queues&quot;&gt;FIFO Queues&lt;/h2&gt;

&lt;p&gt;There are two key attributes when working with FIFO Queues: Message Group ID and Message Deduplication ID.&lt;/p&gt;

&lt;h3 id=&quot;message-group-id&quot;&gt;Message Group ID&lt;/h3&gt;

&lt;p&gt;For the Message Group ID you can either use the same message group for all messages, or group them by some business logic, let’s say &lt;code class=&quot;highlighter-rouge&quot;&gt;account_id&lt;/code&gt;, or whatever that makes sense to you. The messages will be received in the order they were sent to their group.&lt;/p&gt;

&lt;p&gt;Shoryuken automatically sets the Message Group ID to &lt;code class=&quot;highlighter-rouge&quot;&gt;ShoryukenMessage&lt;/code&gt; if it is not provided.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Shoryuken will set the message_group_id to ShoryukenMessage&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;MyWorker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perform_async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'test'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 

&lt;span class=&quot;c1&quot;&gt;# User-defined message_group_id&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;MyWorker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perform_async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'test'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;message_group_id: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'...'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;See &lt;a href=&quot;https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/FIFO-queues.html#FIFO-queues-message-order&quot;&gt;Message Group ID&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;message-deduplication-id&quot;&gt;Message Deduplication ID&lt;/h3&gt;

&lt;p&gt;For exactly-once processing, FIFO Queues use the Message Deduplication ID for identifying and rejecting duplicates sent within a 5 minutes interval. The Message Deduplication is auto generated based on the message body if you enable content-based deduplication when creating your queue.&lt;/p&gt;

&lt;p&gt;Shoryuken automatically sets the Deduplication ID to a SHA-256 hash of the message body if it is not provided.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Shoryuken will set the messaged_deduplication_id to a SHA-256 hash using the message body&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;MyWorker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perform_async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'test'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 

&lt;span class=&quot;c1&quot;&gt;# User-defined message_deduplication_id&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;MyWorker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perform_async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'test'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;message_deduplication_id: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'...'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;See &lt;a href=&quot;https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/FIFO-queues.html#FIFO-queues-exactly-once-processing&quot;&gt;Message Deduplication ID&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;getting-started&quot;&gt;Getting started&lt;/h3&gt;

&lt;p&gt;A few steps for using message groups and Shoryuken.&lt;/p&gt;

&lt;h4 id=&quot;create-a-fifo-queue&quot;&gt;Create a FIFO Queue&lt;/h4&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;shoryuken sqs create queue.fifo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;create-a-worker&quot;&gt;Create a worker&lt;/h4&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HelloWorker&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Shoryuken&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Worker&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;shoryuken_options&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;queue: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'queue.fifo'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;auto_delete: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;perform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqs_msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Hello, &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;send-messages&quot;&gt;Send messages&lt;/h4&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;HelloWorker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perform_async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Ken 1'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;message_group_id: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'hello'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;HelloWorker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perform_async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Ken 2'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;message_group_id: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'hello'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;HelloWorker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perform_async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Ken 3'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;message_group_id: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'hello'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;start-shoryuken&quot;&gt;Start Shoryuken&lt;/h4&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;shoryuken &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; queue &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; ./hello_worker.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it. Shoryuken will start processing your messages.&lt;/p&gt;

&lt;h3 id=&quot;sequential-processing-per-group&quot;&gt;Sequential processing per group&lt;/h3&gt;

&lt;p&gt;By default, Shoryuken tries to receive as many messages as it can, limited to the number of the available threads (see &lt;a href=&quot;https://github.com/phstc/shoryuken/wiki/Shoryuken-options#concurrency&quot;&gt;concurrency&lt;/a&gt;) and the SQS limit of 10 messages per request. If there are 10 available threads, it may receive up to 10 messages for the same Message Group ID and process them all in parallel.&lt;/p&gt;

&lt;p&gt;If you want to sequentially process one message at a time, set &lt;code class=&quot;highlighter-rouge&quot;&gt;max_number_of_messages: 1&lt;/code&gt; (see &lt;a href=&quot;https://github.com/phstc/shoryuken/wiki/Receive-Message-options&quot;&gt;receive options&lt;/a&gt;), with that, if you send messages as follows:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;ExpireMembershipsWorker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perform_async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActivatePendingMembershipsWorker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perform_async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;RenewMembershipsWorker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perform_async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Shoryuken will receive the message for &lt;code class=&quot;highlighter-rouge&quot;&gt;ExpireMembershipsWorker&lt;/code&gt;, process, receive the message for&lt;code class=&quot;highlighter-rouge&quot;&gt;ActivatePendingMembershipsWorker&lt;/code&gt;, process, receive the message for &lt;code class=&quot;highlighter-rouge&quot;&gt;RenewMembershipsWorker&lt;/code&gt; and process, one by one.&lt;/p&gt;

&lt;h3 id=&quot;how-about-multiple-processes-and-other-clients&quot;&gt;How about multiple processes and other clients?&lt;/h3&gt;

&lt;p&gt;Every time Shoryuken (or any other SQS client) receives a message associated with a Message Group ID, SQS locks the group until the message gets deleted or its visibility timeout expires. Preventing Shoryuken or other SQS clients to receive messages for the locked group.&lt;/p&gt;

&lt;h3 id=&quot;using-fifo-queues-for-rate-limiting&quot;&gt;Using FIFO Queues for rate limiting&lt;/h3&gt;

&lt;p&gt;Given that, you can sequentially process messages per group, you could easily use FIFO for controlling the number of requests per second.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Shoryuken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sqs_client_receive_message_opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;max_number_of_messages: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SyncWithExternalAPIWorker&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Shoryuken&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Worker&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;shoryuken_options&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;queue: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'queue.fifo'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;auto_delete: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;perform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqs_msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# call an external API that supports max 10 calls per second...&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# limit the max number processing per second to 10&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I don’t recommend long running workers, I would go with &lt;code class=&quot;highlighter-rouge&quot;&gt;sleep&lt;/code&gt; only for a short delay. For longer delays, you can try to take advantage of the &lt;a href=&quot;https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html&quot;&gt;visibility timeout&lt;/a&gt; or  &lt;a href=&quot;https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-delay-queues.html&quot;&gt;delay queues&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;should-i-use-fifo-queues&quot;&gt;Should I use FIFO Queues?&lt;/h3&gt;

&lt;h5 id=&quot;if-you-want-to-reject-duplicated-messages-sent-within-a-5-minutes-interval&quot;&gt;If you want to reject duplicated messages sent within a 5 minutes interval.&lt;/h5&gt;

&lt;p&gt;Yes.&lt;/p&gt;

&lt;h5 id=&quot;if-you-want-to-control-the-order-that-the-messages-are-received&quot;&gt;If you want to control the order that the messages are received.&lt;/h5&gt;

&lt;p&gt;Yes.&lt;/p&gt;

&lt;h5 id=&quot;if-you-want-to-control-the-order-but-you-want-to-receive-duplicates&quot;&gt;If you want to control the order, but you want to receive duplicates.&lt;/h5&gt;

&lt;p&gt;Yes. Just set &lt;code class=&quot;highlighter-rouge&quot;&gt;message_deduplication_id&lt;/code&gt; to something unique (&lt;code class=&quot;highlighter-rouge&quot;&gt;SecureRandom.uuid&lt;/code&gt;) and SQS won’t reject as a duplicate.&lt;/p&gt;

&lt;p&gt;Same applies if you want to reject duplicates and don’t want to control the order, just set the &lt;code class=&quot;highlighter-rouge&quot;&gt;message_group_id&lt;/code&gt; to something unique.&lt;/p&gt;

&lt;h5 id=&quot;if-you-dont-care-about-the-order-and-duplicates&quot;&gt;If you don’t care about the order and duplicates.&lt;/h5&gt;

&lt;p&gt;No. Go with standard queues.&lt;/p&gt;

&lt;h5 id=&quot;if-you-plan-to-make-more-than-300-transactions-per-second&quot;&gt;If you plan to make more than 300 transactions per second.&lt;/h5&gt;

&lt;p&gt;Maybe. FIFO Queues are limited to 300 transactions per second (TPS). If you can split the transactions across multiple queues avoiding the limit of 300 TPS per queue, you are good, otherwise, you better go with standard queues.&lt;/p&gt;

&lt;h5 id=&quot;if-you-want-per-message-delays&quot;&gt;If you want per-message delays&lt;/h5&gt;

&lt;p&gt;No. FIFO queues don’t support per-message delays, only per-queue delays.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Test emails with automated testing tools</title>
   <link href="http://www.pablocantero.com//blog/2015/08/05/test-emails-with-automated-testing-tools/"/>
   <updated>2015-08-05T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2015/08/05/test-emails-with-automated-testing-tools</id>
   
   <content type="html">&lt;p&gt;Usually, for automated tests that can send emails, because of a step in the test, we either stub or don’t check the emails being sent. We don’t go to Gmail or any other email client for confirming (&lt;code class=&quot;highlighter-rouge&quot;&gt;assert&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;expect&lt;/code&gt;) that the email was delivered.&lt;/p&gt;

&lt;p&gt;Not testing emails, like for real, in most cases can be okay. For example, if you are using &lt;a href=&quot;https://aws.amazon.com/ses/&quot;&gt;Amazon SES&lt;/a&gt; or similar, you just trust them, and usually, the configuration you have in place for sending emails does not change often. If it worked once, it’s most likely it will continue to work (famous last words).&lt;/p&gt;

&lt;p&gt;But if the email delivery is a critical part of your application, and you do want to make sure it’s working, this post is for you.&lt;/p&gt;

&lt;h2 id=&quot;say-hello-to-putsbox&quot;&gt;Say Hello to PutsBox&lt;/h2&gt;

&lt;p&gt;Let me introduce you &lt;a href=&quot;http://putsbox.com&quot;&gt;PutsBox&lt;/a&gt;. It’s a mix of disposable email tools and HTTP request recorders tools such as &lt;a href=&quot;http://putsreq.com&quot;&gt;PutsReq&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;

&lt;p&gt;Send an email to &lt;code class=&quot;highlighter-rouge&quot;&gt;whatever-recipient-you-want-to@putsbox.com&lt;/code&gt;, give it a few seconds (don’t blame PutsBox, email delivery services may take a few seconds to process) then check &lt;code class=&quot;highlighter-rouge&quot;&gt;https://preview.putsbox.com/p/whatever-recipient-you-want-to/last&lt;/code&gt;, voalá, it will respond with your email content.&lt;/p&gt;

&lt;h3 id=&quot;json-representation&quot;&gt;JSON representation&lt;/h3&gt;

&lt;p&gt;You can also append &lt;code class=&quot;highlighter-rouge&quot;&gt;.json&lt;/code&gt; to the &lt;code class=&quot;highlighter-rouge&quot;&gt;/inspect&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;/last&lt;/code&gt; URLs to get a JSON representation.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;https://putsbox.com/whatever-recipient-you-want-to/inspect.json&lt;/li&gt;
  &lt;li&gt;https://preview.putsbox.com/p/whatever-recipient-you-want-to/last.json&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No sign up required, PutsBox is free and &lt;a href=&quot;https://github.com/phstc/putsbox&quot;&gt;open source&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;capybara-example&quot;&gt;Capybara example&lt;/h2&gt;

&lt;p&gt;Here’s an example with &lt;a href=&quot;https://github.com/jnicklas/capybara&quot;&gt;Capybara&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;visit&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/'&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;click_on&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'a[href=&quot;/users/sign_in&quot;]'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;click_on&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'a[href=&quot;/users/password/new&quot;]'&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;fill_in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'#user_email'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;with: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'test-reset-password@putsbox.com'&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;click_on&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'input[name=&quot;commit&quot;]'&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Waits for the email. Email delivery services may take a few seconds&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Opens the email&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;visit&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'http://preview.putsbox.com/p/test-reset-password/last'&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Clicks on the reset link (sent in the body of your email)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;click_on&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'#reset-password'&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Resets the password&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fill_in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'#user_password'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;with: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'password'&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;fill_in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'#user_password_confirmation'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;with: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'password'&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;click_on&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'input[name=&quot;commit&quot;]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Cool, isn’t?&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Don't disqualify yourself</title>
   <link href="http://www.pablocantero.com//blog/2015/05/17/dont-disqualify-yourself/"/>
   <updated>2015-05-17T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2015/05/17/dont-disqualify-yourself</id>
   
   <content type="html">&lt;p&gt;Companies are looking for good developers!&lt;/p&gt;

&lt;p&gt;But sometimes due to the lack of experience in interviews or for never having been on the other side (as an interviewer), some good developers get disqualified.&lt;/p&gt;

&lt;p&gt;The intent of this post is to give some tips to help developers to present themselves better.&lt;/p&gt;

&lt;p&gt;I’m a developer, I’m not a recruiter or HR person. Therefore, the content of this post is from a perspective of a developer interviewing and filtering developer candidates to work with.&lt;/p&gt;

&lt;h1 id=&quot;github&quot;&gt;GitHub&lt;/h1&gt;

&lt;h2 id=&quot;make-a-good-first-impression&quot;&gt;Make a good first impression&lt;/h2&gt;

&lt;p&gt;Try to make the README of your projects as clear and objective as possible, it’s the first content that people see when they access a repository.&lt;/p&gt;

&lt;p&gt;A well-written README can tell how good you are at sharing knowledge and explaining problems. Like a good team player should be.&lt;/p&gt;

&lt;p&gt;Usually, good writing skills don’t happen naturally, you have to practice them. You can use READMEs, Wiki pages, and blog posts to exercise these skills.&lt;/p&gt;

&lt;h2 id=&quot;not-so-proud-of-some-of-your-code&quot;&gt;Not so proud of some of your code?&lt;/h2&gt;

&lt;p&gt;So you have some code that you aren’t super proud of? Don’t worry, everyone has.&lt;/p&gt;

&lt;p&gt;But if an interviewer asks you something like: “Is there something that would you like to have done differently in this code?”. Take this as an opportunity to impress him by acknowledging the code weaknesses and explaining how you could have done it differently with the knowledge you have now, it can cause a huge good impression.&lt;/p&gt;

&lt;p&gt;As a good practice, I suggest to review your projects from time to time. Do you know that boring Sunday, in which you don’t have anything to do? Well, why not spend some time practicing code refactor in old projects?&lt;/p&gt;

&lt;h2 id=&quot;make-a-song-of-your-profile&quot;&gt;Make a song of your profile&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://song-of-github.herokuapp.com&quot;&gt;Does your profile play a song&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;If you don’t have any public project or activity (or a GitHub profile). You may lose some points compared with candidates who have, but it’s something you can overcome while telling about your experience.&lt;/p&gt;

&lt;p&gt;P-l-e-a-s-e, don’t say that you don’t have public projects or activity because the company that you work for doesn’t support open source. IMO open source is much more an individual initiative than a company one. If your company uses open source, it supports open source, but it doesn’t mean that your boss will tell you to spend working hours in open source, just to make you a better GitHub profile.&lt;/p&gt;

&lt;h2 id=&quot;engage-users&quot;&gt;Engage users&lt;/h2&gt;

&lt;p&gt;It’s very positive when a candidate has projects with stars on GitHub, it shows that he’s doing projects with impact in the open source community.&lt;/p&gt;

&lt;p&gt;People can’t use, what they don’t know. If you developed something that can be useful for other people, share it. A blog post, Reddit, Stack Overflow and Twitter can help you with that.&lt;/p&gt;

&lt;p&gt;On a side note, remember to star (like) the projects that you use and like on GitHub, it motivates the project owners and also gives credibility to the projects.&lt;/p&gt;

&lt;h2 id=&quot;why-so-many-forks&quot;&gt;Why so many forks?&lt;/h2&gt;

&lt;p&gt;If you aren’t contributing to a project, you don’t need to fork it. If you only want to play with it locally, you can clone it from the original repository.&lt;/p&gt;

&lt;p&gt;A profile full of forks without contributions to them can pollute your real activity, hiding your own code. And at least for me, it commonly raises a question: “Why is this person doing so many forks? Does he know how it works?”.&lt;/p&gt;

&lt;h1 id=&quot;linkedin&quot;&gt;Linkedin&lt;/h1&gt;

&lt;h2 id=&quot;be-brief&quot;&gt;Be brief&lt;/h2&gt;

&lt;p&gt;Detail your mission and achievements for each one of your experiences, as very other article will suggest. But there’s no need to write a letter for each one of them, be brief on the details. If an interviewer gets interested on a specific experience, he will ask you during the interview. And I also recommend to use something like “Main technologies”:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Full stack developer lorem ipsum dolor sit amet, consectetur adipiscing elit.

Main technologies:
Ruby, Rails, MongoDB, EC2 and SQS.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It helps for a quick matching between candidates and position requirements.&lt;/p&gt;

&lt;h2 id=&quot;relevant-links&quot;&gt;Relevant links&lt;/h2&gt;

&lt;p&gt;Add your GitHub, Blog and other relevant links in your profile, facilitate your interviewers’ life. Don’t make them to Google you to find your GitHub.&lt;/p&gt;

&lt;p&gt;LinkedIn has a specific section in which you can add links, but as a profile viewer, you need to click on Contact Info to access them. Did you know it? Maybe some interviewers don’t. My suggestion is to besides adding those to the Websites section, there’s no harm to also add them to your Summary.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;My summary lorem ipsum dolor sit amet, consectetur adipiscing elit.

* GitHub https://github.com/your-name
* LinkedIn https://linkedin.com/your-name
* Blog https://your-name
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Try to get a website.com/&lt;strong&gt;your-name&lt;/strong&gt; when possible, that helps when googling you and sharing your links:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Hey Pablo,

Could you have a look at these two candidates:

* https://linkedin.com/in/john-something

* https://linkedin.com/profile/view?id=123456
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which one makes a better first impression, John Something or 123456?&lt;/p&gt;

&lt;h2 id=&quot;dont-be-a-default-avatar-person&quot;&gt;Don’t be a default avatar person&lt;/h2&gt;

&lt;p&gt;You need to show your face out, interviewers want to see you. It’s hard to sympathize when there’s no face.&lt;/p&gt;

&lt;p&gt;The same applies for video interviews, remember to turn your camera on.&lt;/p&gt;

&lt;h2 id=&quot;view-profile-as&quot;&gt;View profile as&lt;/h2&gt;

&lt;p&gt;After changing your profile, try to access it as a profile viewer instead of profile owner. LinkedIn has a “View profile as” button for that.&lt;/p&gt;

&lt;h1 id=&quot;before-the-interview&quot;&gt;Before the interview&lt;/h1&gt;

&lt;h2 id=&quot;compatibility-matching&quot;&gt;Compatibility matching&lt;/h2&gt;

&lt;p&gt;If the position is for a “Full stack Ruby developer”, candidates with this title, will be prioritized over “iOS and web developer” ones.&lt;/p&gt;

&lt;p&gt;A (brief) cover letter (our a LinkedIn summary) is also a good place to expose the matches between you and the position expectations.&lt;/p&gt;

&lt;h2 id=&quot;be-organized&quot;&gt;Be organized&lt;/h2&gt;

&lt;p&gt;Make some notes on what you would like to bring during the interview, whether telling about you or asking about the company.&lt;/p&gt;

&lt;p&gt;You don’t need to use your notes as a teleprompter. But you can create some clues for frequently asked questions, such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Tell me about yourself&lt;/li&gt;
  &lt;li&gt;Why did you leave (or want to leave) your last (current) job?&lt;/li&gt;
  &lt;li&gt;What have you been doing lately?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prepare yourself in advance to answer them well.&lt;/p&gt;

&lt;h2 id=&quot;research-the-company&quot;&gt;Research the company&lt;/h2&gt;

&lt;p&gt;Visit the company website, check the about page, mission, and values.&lt;/p&gt;

&lt;p&gt;If they have a public list of values, try to match with yours and expose it during the interview.&lt;/p&gt;

&lt;h2 id=&quot;research-your-interviewers&quot;&gt;Research your interviewers&lt;/h2&gt;

&lt;p&gt;Interviewers research and talk about candidates to work colleagues. They spend a reasonable amount of time preparing themselves for an interview.&lt;/p&gt;

&lt;p&gt;I strongly recommend to all candidates to give it back by also researching on them before the interview.&lt;/p&gt;

&lt;p&gt;Once when I was participating in an interview process as a candidate, and I found a presentation from my next interviewer, in which he was talking about why the code in Node.js often gets rejected in coding challenge submissions. How lucky was I?! I had a chance to review my code challenging, which coincidently was written in Node.js, and also bring some topics during the interview that I knew he was interested in.&lt;/p&gt;

&lt;p&gt;It’s also nice to say something to your interviewer like: “I saw your blog and I found your post about X interesting” or a mention of a project that he’s working on GitHub. You don’t need to lie if you didn’t like what you saw, feel free to keep that yourself or to consider if he’s the type of professional you want to work with.&lt;/p&gt;

&lt;h1 id=&quot;during-the-interview&quot;&gt;During the interview&lt;/h1&gt;

&lt;h2 id=&quot;remote-interviews&quot;&gt;Remote interviews&lt;/h2&gt;

&lt;p&gt;Close IMs and everything else not related to the interview that might distract you. Give all your attention to your interviewer. It’s terrible to interview someone and see or hear that he’s typing to someone else.&lt;/p&gt;

&lt;p&gt;If you like to make notes during the interview, use a psychical notepad, and handwrite, otherwise he may think you are typing to someone else.&lt;/p&gt;

&lt;p&gt;On making notes, if your interviewer likes to talk, let him talk, but make notes on the topics he’s talking about. So when it’s your time to talk, you can return to all relevant topics he mentioned during his “speech”. Showing that you are a good listener and organized.&lt;/p&gt;

&lt;h1 id=&quot;rejection-notice&quot;&gt;Rejection notice&lt;/h1&gt;

&lt;p&gt;No one likes to be rejected. But I would say that in most cases being rejected, does not mean that you aren’t qualified enough. Hiring is complex, it involves people and subjective perceptions, you can get rejected for various reasons.&lt;/p&gt;

&lt;h2 id=&quot;what-can-i-do-better&quot;&gt;What can I do better?&lt;/h2&gt;

&lt;p&gt;Feel free to ask for feedback to do better next time. But don’t get disappointed if the company can’t give you one.&lt;/p&gt;

&lt;p&gt;Although some feedback can be constructive, most of the people aren’t prepared for, and some rejections can be because of details: “He seems to be an experienced developer, but he was typing to someone else during the interview. The next candidate I interviewed is equivalent experienced and gave me his total attention”.&lt;/p&gt;

&lt;p&gt;It’s hard to give feedback on subjective perceptions.&lt;/p&gt;

&lt;h1 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h1&gt;

&lt;p&gt;As a friend of mine used to say: “Doing well at interviews, only proves that you do well at interviews”, you need to practice it, the more you do, the more you get good at. There are a ton of people that do much better at interviews than at work. If you are a good professional, don’t let them disqualify you.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Sidekiq (Redis) vs Shoryuken (AWS SQS)</title>
   <link href="http://www.pablocantero.com//blog/2015/03/14/sidekiq-redis-vs-shoryuken-aws-sqs/"/>
   <updated>2015-03-14T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2015/03/14/sidekiq-redis-vs-shoryuken-aws-sqs</id>
   
   <content type="html">&lt;p&gt;DISCLAIMER: This post is not intended to compare &lt;a href=&quot;http://sidekiq.org/&quot;&gt;Sidekiq&lt;/a&gt; and &lt;a href=&quot;https://github.com/phstc/shoryuken&quot;&gt;Shoryuken&lt;/a&gt; implementations. Both are very similar, actually Shoryuken started as a &lt;a href=&quot;https://github.com/phstc/shoryuken#credits&quot;&gt;shameless copy of Sidekiq&lt;/a&gt; &amp;lt;3 open source. The idea of this post is to compare Sidekiq as a Redis based queue system with Shoryuken as a &lt;a href=&quot;https://aws.amazon.com/sqs/&quot;&gt;SQS&lt;/a&gt; client.&lt;/p&gt;

&lt;h2 id=&quot;resque--sidekiq--shoryuken&quot;&gt;Resque &amp;gt; Sidekiq &amp;gt; Shoryuken&lt;/h2&gt;

&lt;p&gt;Before Sidekiq, &lt;a href=&quot;https://github.com/resque/resque&quot;&gt;Resque&lt;/a&gt; was the main option to go for background jobs for many Rubyists. But Sidekiq changed that, mainly because it uses threads instead of process forking (Resque). No matter how many improvements MRI is receiving on process forking, we can’t get the same performance (usage of resources) using forks as we get with threads. You can check some &lt;a href=&quot;https://github.com/mperham/sidekiq/wiki/Testimonials&quot;&gt;Sidekiq testimonials&lt;/a&gt; confirming that.&lt;/p&gt;

&lt;p&gt;But the game is changing again - it’s Shoryuken time.&lt;/p&gt;

&lt;p&gt;With Sidekiq:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If you are not using the &lt;a href=&quot;http://sidekiq.org/pro/&quot;&gt;Pro version&lt;/a&gt; (which costs $750/year) and your process crashes while consuming jobs, you will lose your jobs&lt;/li&gt;
  &lt;li&gt;If your Redis master crashes before having replicated your jobs to the slaves, you will lose your jobs&lt;/li&gt;
  &lt;li&gt;If your jobs load increases, you will need a more powerful Redis. More jobs, more Redis, more Money&lt;/li&gt;
  &lt;li&gt;If you don’t have a Redis cluster, eventually you will lose everything&lt;/li&gt;
  &lt;li&gt;&lt;del&gt;No Dead Letter Queues support&lt;/del&gt; &lt;a href=&quot;https://github.com/mperham/sidekiq/wiki/Error-Handling#dead-job-queue&quot;&gt;Dead Job Queue support&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;No Fanout support&lt;/li&gt;
  &lt;li&gt;You need to host its dashboard (console)&lt;/li&gt;
  &lt;li&gt;You can use &lt;a href=&quot;https://github.com/phstc/sidekiq-statsd&quot;&gt;sidekiq-statsd&lt;/a&gt; or &lt;a href=&quot;https://github.com/mperham/sidekiq/wiki/Metrics&quot;&gt;Sidekiq pro/Metrics&lt;/a&gt; to send metrics to Statsd for distribution to Graphite, Librato Metrics, etc, but you will need these services running somewhere&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Shoryuken:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If your process crashes, your jobs will return to the queue after its &lt;a href=&quot;http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/AboutVT.html&quot;&gt;visibility timeout&lt;/a&gt; expiration, nothing will be lost&lt;/li&gt;
  &lt;li&gt;If a SQS node crashes, you don’t care, nothing will be lost&lt;/li&gt;
  &lt;li&gt;If your jobs load increases from 1 to 1 million, you don’t care, SQS handles that for you&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2014/11/29/sqs-to-the-rescue/#dead-letter-queues&quot;&gt;Dead Letter Queues support&lt;/a&gt; - move messages after a configurable number of failures to a specific queue&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2014/11/29/sqs-to-the-rescue/#sns-to-sqs&quot;&gt;Fanout support via SNS&lt;/a&gt; - send a message once and distribute across multiple queues&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://console.aws.amazon.com/sqs/home?region=us-east-1&quot;&gt;Hosted console&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;You can &lt;a href=&quot;http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/MonitorSQSwithCloudWatch.html#SQS_metricscollected&quot;&gt;monitor your queues with CloudWatch&lt;/a&gt; and send &lt;a href=&quot;http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/AlarmThatSendsEmail.html&quot;&gt;alarms&lt;/a&gt; (Email, SMS, HTTP calls etc) when a threshold is breached&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;perfomance&quot;&gt;Perfomance&lt;/h2&gt;

&lt;p&gt;WARNING: SUBJECTIVE PERFORMANCE TESTS!!!&lt;/p&gt;

&lt;p&gt;Are you wondering about performance? I would be, Redis is super fast.&lt;/p&gt;

&lt;p&gt;To test performance, I created two projects:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/phstc/sidekiq-putsreq&quot;&gt;https://github.com/phstc/sidekiq-putsreq&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/phstc/shoryuken-putsreq&quot;&gt;https://github.com/phstc/shoryuken-putsreq&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test plan:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Send 1k jobs&lt;/li&gt;
  &lt;li&gt;Make a HTTP call per job to a &lt;a href=&quot;http://putsreq.com/&quot;&gt;PutsReq&lt;/a&gt; bucket&lt;/li&gt;
  &lt;li&gt;Compare the “From first to last” result&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Result:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Sidekiq took 13 seconds to consume 1k jobs&lt;/li&gt;
  &lt;li&gt;Shoryuken took 14 seconds to consume 1k jobs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;http://www.pablocantero.com/assets/images/posts/sidekiq-putsreq.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.pablocantero.com/assets/images/posts/shoryuken-putsreq.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Although the jobs consumption result was pretty close, sending jobs with Sidekiq is clearly faster than with Shoryuken.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Sidekiq took 2278.37ms to send 1k jobs (2.28ms per send)&lt;/li&gt;
  &lt;li&gt;Shoryuken took 55629.93ms to send 1k jobs (55.63ms per send)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;The jobs where enqueued using Heroku (Amazon EC2, us-east-1) via &lt;code class=&quot;highlighter-rouge&quot;&gt;heroku run rake populate&lt;/code&gt; - this Rake task is available on the linked projects above.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Yeah, Redis write performance is amazing. But is 55.63ms much?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Every time you run these tests you will get a different result, but in most cases Sidekiq is slightly faster to consume and clearly faster to send jobs than Shoryuken.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;More than once while trying bigger batches with Sidekiq, I got less requests on PutsReq than the jobs sent. Shoryuken was consistent, the number of jobs sent was the number of requests shown on PutsReq.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;pricing&quot;&gt;Pricing&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;SQS is a paid solution. You can run Sidekiq for free using Heroku with something like Redis To Go.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nope! SQS can be also free or cheaper than Sidekiq:&lt;/p&gt;

&lt;h3 id=&quot;shoryuken&quot;&gt;Shoryuken&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/phstc/shoryuken&quot;&gt;Shoryuken pricing&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;$0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://aws.amazon.com/sqs/pricing/&quot;&gt;SQS pricing&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;First 1 million Amazon SQS Requests per month are free&lt;/li&gt;
  &lt;li&gt;$0.50 per 1 million Amazon SQS Requests per month thereafter ($0.00000050 per SQS Request)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;sidekiq&quot;&gt;Sidekiq&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://sidekiq.org&quot;&gt;Sidekiq pricing&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;$0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;http://sidekiq.org/pro&quot;&gt;Sidekiq Pro pricing&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;$750/year&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://addons.heroku.com/redistogo&quot;&gt;Redis To Go pricing&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Starts at $0 for a very limited option. But keep in mind that Redis To Go isn’t an &lt;a href=&quot;https://en.wikipedia.org/wiki/High_availability&quot;&gt;HA solution&lt;/a&gt;, they don’t offer a Redis cluster, so it’s a &lt;a href=&quot;https://en.wikipedia.org/wiki/Single_point_of_failure&quot;&gt;SPOF&lt;/a&gt;. For an HA solution they recommend &lt;a href=&quot;https://objectrocket.com/pricing&quot;&gt;ObjectRocket&lt;/a&gt;, which starts at $59/month&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;from-sidekiq-to-shoryuken&quot;&gt;From Sidekiq to Shoryuken&lt;/h2&gt;

&lt;p&gt;Did I convince you?&lt;/p&gt;

&lt;p&gt;Have a look at the migration steps: &lt;a href=&quot;https://github.com/phstc/shoryuken/wiki/From-Sidekiq-to-Shoryuken&quot;&gt;From Sidekiq to Shoryuken&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;There’s no silver bullet solution. If your workers aren’t thread safe workers, maybe you should stick with Resque. If the performance difference between Shoryuken and Sidekiq matters to you, go with Sidekiq.&lt;/p&gt;

&lt;p&gt;The point I’m trying to make is that in general for background jobs in Ruby, IMO Shoryuken/SQS is the new main option to go. It’s fast, durable, distributed, auto scalable, cheap (or even free) and you don’t need to care about the infrastructure, AWS will handle that for you.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>SQS to the rescue</title>
   <link href="http://www.pablocantero.com//blog/2014/11/29/sqs-to-the-rescue/"/>
   <updated>2014-11-29T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2014/11/29/sqs-to-the-rescue</id>
   
   <content type="html">&lt;p&gt;&lt;strong&gt;tl;dr&lt;/strong&gt; &lt;a href=&quot;https://aws.amazon.com/sqs/&quot;&gt;Amazon SQS&lt;/a&gt; is currently one of the best options for a queue service. It’s &lt;a href=&quot;https://aws.amazon.com/sqs/pricing/&quot;&gt;inexpensive
&lt;/a&gt; (first 1 million requests per month are free, thereafter $0.50 per 1 million requests), fast, reliable, scalable, fully managed message queuing service.  It just works.&lt;/p&gt;

&lt;p&gt;For the following examples I’m using the &lt;a href=&quot;https://github.com/aws/aws-sdk-ruby&quot;&gt;aws-sdk-ruby&lt;/a&gt;, which is a great SDK because it’s transparent to the &lt;a href=&quot;http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/Welcome.html&quot;&gt;official API documentation&lt;/a&gt;, no abstractions, no different method names etc, what you see in the documentation is what you get on the SDK.&lt;/p&gt;

&lt;h2 id=&quot;getting-your-hands-dirty&quot;&gt;Getting your hands dirty&lt;/h2&gt;

&lt;p&gt;Let’s begin by bootstrapping a queue using the &lt;a href=&quot;http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SQS/QueueCollection.html#create-instance_method&quot;&gt;create&lt;/a&gt; method:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sqs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'myqueue'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;visibility_timeout: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;message_retention_period: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;345600&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then let’s send a message using the &lt;a href=&quot;http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SQS/Queue.html#send_message-instance_method&quot;&gt;send_message&lt;/a&gt; method:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sqs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'myqueue'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send_message&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'HELLO'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally let’s consume a message using the &lt;a href=&quot;http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SQS/Queue.html#receive_message-instance_method&quot;&gt;receive_message&lt;/a&gt; method:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sqs_msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'myqueue'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;receive_message&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;do_something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqs_msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sqs_msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Catching things up - we created a queue that holds messages for 4 days &lt;code class=&quot;highlighter-rouge&quot;&gt;message_retention_period: 345600&lt;/code&gt;, which means: if we don’t consume a message within 4 days the message will be lost.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;visibility_timeout&lt;/code&gt; is the time in seconds we have to consume a message after its reception. When we send a message to SQS &lt;code class=&quot;highlighter-rouge&quot;&gt;send_message&lt;/code&gt;, the message is listed on “Messages Available”, when we receive a message &lt;code class=&quot;highlighter-rouge&quot;&gt;receive_message&lt;/code&gt; the message is moved to “Messages in Flight”, if we don’t &lt;code class=&quot;highlighter-rouge&quot;&gt;delete&lt;/code&gt; the message within 600 seconds (which is the visibility timeout we configured above) the message will be moved back to “Messages Available” and become ready to be consumed again. &lt;a href=&quot;https://aws.amazon.com/sqs/details/#Amazon_SQS_Message_Lifecycle&quot;&gt;Read more about the SQS Message Lifecycle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Be generous while configuring the default &lt;code class=&quot;highlighter-rouge&quot;&gt;visibility_timeout&lt;/code&gt; for a queue. If your worker in the worst case takes 2 minutes to consume a message, set the &lt;code class=&quot;highlighter-rouge&quot;&gt;visibility_timeout&lt;/code&gt; to at least 4 minutes. It doesn’t hurt and it will be better than having the same message being consumed more than the expected.&lt;/p&gt;

&lt;h2 id=&quot;auto-retrying-errors&quot;&gt;Auto retrying errors&lt;/h2&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;sqs_msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'myqueue'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;receive_message&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;do_something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqs_msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;sqs_msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delete&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the code above if &lt;code class=&quot;highlighter-rouge&quot;&gt;do_something&lt;/code&gt; raises an exception, the message will become available again as soon as its &lt;code class=&quot;highlighter-rouge&quot;&gt;visibility_timeout&lt;/code&gt; expires, so other workers will be able to re-attempt the message.&lt;/p&gt;

&lt;p&gt;This behavior will also cover in case your process or server crashes. Basically, if you don’t call &lt;code class=&quot;highlighter-rouge&quot;&gt;sqs_msg.delete&lt;/code&gt;, the message will become available again no matter what :)&lt;/p&gt;

&lt;h2 id=&quot;dead-letter-queues&quot;&gt;Dead Letter Queues&lt;/h2&gt;

&lt;p&gt;Let’s retry stuff, but not forever, right? Sometimes no matter how many times we re-attempt a message, it will continue failing. To do not discard or retry forever these messages addicted to failure, we can let them rest in peace using a &lt;a href=&quot;http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/SQSDeadLetterQueue.html&quot;&gt;Dead Letter Queue
&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The code below creates &lt;code class=&quot;highlighter-rouge&quot;&gt;myqueue_failures&lt;/code&gt; and associates it with &lt;code class=&quot;highlighter-rouge&quot;&gt;myqueue&lt;/code&gt; as a dead letter queue:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;dl_queue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'myqueue_failures'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;sqs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'myqueue'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;redrive_policy: &lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%Q{ {&quot;maxReceiveCount&quot;:&quot;3&quot;, &quot;deadLetterTargetArn&quot;:&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dl_queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;arn&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;&quot;}&quot; }&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;redrive_policy&lt;/code&gt; above tells SQS to move a message from &lt;code class=&quot;highlighter-rouge&quot;&gt;myqueue&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;myqueue_failures&lt;/code&gt; if its receive count reaches 3.&lt;/p&gt;

&lt;h2 id=&quot;delaying-a-message-perform_in&quot;&gt;Delaying a message &lt;code class=&quot;highlighter-rouge&quot;&gt;perform_in&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;SQS supports delaying a message up to 15 minutes, before it becomes available to be consumed using the  &lt;a href=&quot;http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SQS/Queue.html#send_message-instance_method&quot;&gt;delay_seconds&lt;/a&gt; option:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sqs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'myqueue'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send_message&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'HELLO'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;delay_seconds: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But as we are all &lt;a href=&quot;http://www.imdb.com/title/tt0113243/&quot;&gt;hackerz&lt;/a&gt; and we know that the &lt;code class=&quot;highlighter-rouge&quot;&gt;visibility_timeout&lt;/code&gt; &lt;a href=&quot;http://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_ChangeMessageVisibility.html&quot;&gt;supports up to 12 hours&lt;/a&gt;, we can use it to extend the delay.&lt;/p&gt;

&lt;h3 id=&quot;sending-a-message-with-extended-delay&quot;&gt;Sending a message with extended delay&lt;/h3&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;perform_at&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;hour&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# max: 12.hours&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;sqs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'myqueue'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send_message&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'H311O'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
  &lt;span class=&quot;ss&quot;&gt;message_attributes: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;s1&quot;&gt;'perform_at'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;string_value: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;perform_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                      &lt;span class=&quot;ss&quot;&gt;data_type: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'String'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;receiving-a-message-with-extended-delay&quot;&gt;Receiving a message with extended delay&lt;/h3&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sqs_msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'myqueue'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;receive_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;message_attribute_names: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'perform_at'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;delay&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqs_msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;message_attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'perform_at'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:string_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delay&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;sqs_msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;visibility_timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_i&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;do_something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqs_msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;sqs_msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delete&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Be careful with this workaround, because it will increase the message receive count.&lt;/p&gt;

&lt;h2 id=&quot;1-million--1-million&quot;&gt;1 million != 1 million&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://aws.amazon.com/sqs/pricing/&quot;&gt;first 1 million requests per month are free, thereafter $0.50 per 1 million requests&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;1 million requests don’t mean 1 million jobs consumed, as we need at least 3 requests to fully consume a message:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;send_message&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;receive_message&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;sqs_msg.delete&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Although these requests can be executed in batches up to 10 messages, you will need all of them to consume a message, and even if you are using the &lt;a href=&quot;http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SQS/Queue.html#poll-instance_method&quot;&gt;poll&lt;/a&gt; method with a high &lt;code class=&quot;highlighter-rouge&quot;&gt;wait_time_seconds&lt;/code&gt;, you will probably make some empty requests while looking for new messages.&lt;/p&gt;

&lt;h2 id=&quot;sns-to-sqs&quot;&gt;SNS to SQS&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://aws.amazon.com/sns/&quot;&gt;SNS&lt;/a&gt; distributes messages across multiple SQS queues &lt;a href=&quot;http://docs.aws.amazon.com/sns/latest/dg/SNS_Scenarios.html&quot;&gt;and more&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Sending the same &lt;code class=&quot;highlighter-rouge&quot;&gt;msg&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;my_queue1&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;my_queue2&lt;/code&gt; using SQS:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sqs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'my_queue1'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sqs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'my_queue2'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Using &lt;a href=&quot;http://docs.aws.amazon.com/sns/latest/dg/SendMessageToSQS.html&quot;&gt;SNS to SQS&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# sends to my_queue1 and my_queue2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;SNS will fanout the message to both queues, consequently, you will pay (&lt;a href=&quot;https://aws.amazon.com/sns/pricing/&quot;&gt;SNS to SQS is free&lt;/a&gt;) and wait for only one request to SNS, instead of two to SQS.&lt;/p&gt;

&lt;h2 id=&quot;trouble-in-paradise&quot;&gt;Trouble in Paradise&lt;/h2&gt;

&lt;p&gt;SQS is really good, the Ruby SDK is great, but… how to consume messages continuously?&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# myqueue_worker.rb&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sqs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'myqueue'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;poll&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqs_msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;do_something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqs_msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ruby myqueue_worker.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Doesn’t seem to be very reliable, right? No threads, consumes messages one by one, and if you need to consume from another queue you will need to start a new process &lt;code class=&quot;highlighter-rouge&quot;&gt;ruby myotherqueue_worker.rb&lt;/code&gt;, which is a waste of resources when one queue is empty and others have messages available.&lt;/p&gt;

&lt;h3 id=&quot;introducing-shoryuken&quot;&gt;Introducing Shoryuken&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/phstc/shoryuken&quot;&gt;Shoryuken&lt;/a&gt; is a project based on &lt;a href=&quot;https://github.com/mperham/sidekiq&quot;&gt;Sidekiq&lt;/a&gt;, that uses all the cool stuff Sidekiq did with processes, &lt;a href=&quot;https://github.com/celluloid/celluloid&quot;&gt;Celluloid&lt;/a&gt;, CLI etc, but for SQS.&lt;/p&gt;

&lt;p&gt;No more &lt;code class=&quot;highlighter-rouge&quot;&gt;ruby myotherqueue_worker.rb&lt;/code&gt;, &lt;a href=&quot;https://github.com/phstc/shoryuken#start-shoryuken&quot;&gt;Shoryuken will handle that for you&lt;/a&gt;, including &lt;a href=&quot;https://github.com/phstc/shoryuken/wiki/Signals&quot;&gt;signal handling&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A single Shoryuken process can consume messages from multiple queues, &lt;a href=&quot;https://github.com/phstc/shoryuken#load-balancing&quot;&gt;load balancing the consumption&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s transparent to SQS, it passes the &lt;a href=&quot;docs.aws.amazon.com/AWSRubySDK/latest/AWS/SQS/ReceivedMessage.html&quot;&gt;ReceivedMessage&lt;/a&gt; to the &lt;a href=&quot;https://github.com/phstc/shoryuken#worker-class&quot;&gt;workers&lt;/a&gt;, no abstractions, no Rescue compatibility, SQS as is!&lt;/p&gt;

&lt;p&gt;It accepts &lt;a href=&quot;https://github.com/phstc/shoryuken/wiki/Sending-a-message#multiple-workers-for-the-same-queue&quot;&gt;multiple workers per queue&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Check more on the &lt;a href=&quot;https://github.com/phstc/shoryuken&quot;&gt;project’s README&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;SQS is cheap, you don’t need to worry about managing your queue services, setup Redis etc.&lt;/p&gt;

&lt;p&gt;Keep your focus on your workers/jobs (which should be the most important to you) and let Amazon take care of the infrastructure, it works!&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Play Rock-paper-scissors with Slack and PutsReq</title>
   <link href="http://www.pablocantero.com//blog/2014/10/12/play-rock-paper-scissors-with-slack-and-putsreq/"/>
   <updated>2014-10-12T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2014/10/12/play-rock-paper-scissors-with-slack-and-putsreq</id>
   
   <content type="html">&lt;p&gt;Inspired by &lt;a href=&quot;https://wombat.co/blog/say-hello-world-to-wombat-using-putsreq&quot;&gt;Say Hello World to Wombat using PutsReq
&lt;/a&gt;, this post will give you a step by step tutorial to create a dead simple &lt;a href=&quot;https://my.slack.com/services/new/outgoing-webhook&quot;&gt;Slack Outgoing webhook&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Outgoing webhooks allows you to get data in and out of Slack in real time when someone sends a message to a specific channel or with a trigger word.&lt;/p&gt;

&lt;h2 id=&quot;fork-rock-paper-scissors-webhook&quot;&gt;Fork Rock-paper-scissors webhook&lt;/h2&gt;

&lt;p&gt;First, let’s fork the Rock-paper-scissors webhook on PutsReq.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Open &lt;a href=&quot;https://putsreq.herokuapp.com/hfep98jThPU0ffRlmWBx/inspect&quot;&gt;PutsReq Rock-paper-scissors&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Click on Fork&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;UPDATE: The link above is no longer available, PutsReq auto expires buckets without recent activity. Note to self: Use a gist next time.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.pablocantero.com/assets/images/posts/slack-putsreq/putsreq-fork.png&quot; alt=&quot;PutsReq Fork&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Copy your PutsReq URL, it will be used as your webhook URL in the next step&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;http://www.pablocantero.com/assets/images/posts/slack-putsreq/copy-putsreq-url.png&quot; alt=&quot;Copy PutsReq URL&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;add-rock-paper-scissors-outgoing-webhook&quot;&gt;Add Rock-paper-scissors Outgoing webhook&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Access your &lt;a href=&quot;https://my.slack.com/services/new&quot;&gt;Slack Services&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Click on Outgoing WebHooks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;http://www.pablocantero.com/assets/images/posts/slack-putsreq/outgoing-webhooks.png&quot; alt=&quot;Slack Outgoing WebHooks&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Click on Add Outgoing Webhook&lt;/li&gt;
  &lt;li&gt;Fill Channel: Any, Trigger Word(s): rock, scissors, paper and URL(s): your PutsReq URL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;http://www.pablocantero.com/assets/images/posts/slack-putsreq/integration-settings.png&quot; alt=&quot;Slack Integration Settings&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Click on Save&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;play-it&quot;&gt;Play it&lt;/h2&gt;

&lt;p&gt;All set, now it’s time to test it:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.pablocantero.com/assets/images/posts/slack-putsreq/play.png&quot; alt=&quot;Slack Rock-paper-scissors&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;more&quot;&gt;More?&lt;/h2&gt;

&lt;p&gt;Now that your &lt;a href=&quot;https://en.wikipedia.org/wiki/Rock-paper-scissors&quot;&gt;Rock-paper-scissors&lt;/a&gt; is working, why not expand it to the &lt;a href=&quot;https://en.wikipedia.org/wiki/Rock-paper-scissors-lizard-Spock&quot;&gt;Rock-paper-scissors-lizard-Spock&lt;/a&gt; version? If you do that, please share! :)&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Say Hello World to Wombat using PutsReq</title>
   <link href="http://www.pablocantero.com//blog/2014/10/05/say-hello-world-to-wombat-using-putsreq/"/>
   <updated>2014-10-05T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2014/10/05/say-hello-world-to-wombat-using-putsreq</id>
   
   <content type="html">&lt;p&gt;When starting a new programming language, there’s nothing better than printing out a &lt;a href=&quot;https://en.wikipedia.org/wiki/%22Hello,_world!%22_program&quot;&gt;Hello World&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, the idea of this post is to demonstrate how to integrate with Wombat as simple as a Hello World using &lt;a href=&quot;https://wombat.co&quot;&gt;Wombat&lt;/a&gt; could be.&lt;/p&gt;

&lt;p&gt;Read the full post: &lt;a href=&quot;https://wombat.co/blog/say-hello-world-to-wombat-using-putsreq/&quot;&gt;Say Hello World to Wombat using PutsReq&lt;/a&gt;&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Easy distributed locks using Redis</title>
   <link href="http://www.pablocantero.com//blog/2014/09/08/easy_distributed_locks_using_redis/"/>
   <updated>2014-09-08T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2014/09/08/easy_distributed_locks_using_redis</id>
   
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://wombat.co&quot;&gt;Wombat&lt;/a&gt; is a very concurrent application. It runs on multiple hosts, processes and threads, and sometimes we need to lock an execution to be exclusively performed across all threads, no matter the host or process. As any other distributed application, we can’t use a simple &lt;a href=&quot;http://www.ruby-doc.org/core-2.1.1/Mutex.html&quot;&gt;Ruby Mutex&lt;/a&gt;, so we implemented a Mutex using Redis, which can be shared across all threads.&lt;/p&gt;

&lt;p&gt;We decided to use &lt;a href=&quot;http://redis.io/&quot;&gt;Redis&lt;/a&gt;, because it’s fast, consistent and has two super useful commands for locking: &lt;a href=&quot;http://redis.io/commands/setnx&quot;&gt;SETNX&lt;/a&gt; and &lt;a href=&quot;http://redis.io/commands/setnx&quot;&gt;EXPIRE&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Read the full post: &lt;a href=&quot;https://wombat.co/blog/easy_distributed_locks_using_redis/&quot;&gt;Easy distributed locks using Redis&lt;/a&gt;&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Quick and Dirty: Simple Ruby Profiler</title>
   <link href="http://www.pablocantero.com//blog/2014/08/17/quick-and-dirty-simple-ruby-profiler/"/>
   <updated>2014-08-17T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2014/08/17/quick-and-dirty-simple-ruby-profiler</id>
   
   <content type="html">&lt;p&gt;You know when your code work well in test, development and staging environments, but when you roll out to production you notice it very slow? Even testing it pre-production, only production has the real data and load.&lt;/p&gt;

&lt;p&gt;As Ruby is so awesome, there is a super easy way to find slowness in our code. I’m calling it a profiler, but it isn’t nothing fancy, just a super simple hack to measure the methods execution time, no &lt;a href=&quot;https://en.wikipedia.org/wiki/Profiling_(computer_programming)&quot;&gt;memory, complexity etc measurements&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s do it! Go to your special production host, the one you use to test in production (testing like a boss). Everyone has a production host which is used for bad things.&lt;/p&gt;

&lt;p&gt;Paste the snippet below in something available to the class scope you want to profile.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;log_time&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;beginning_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;end_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;caller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; - Completed in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beginning_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; ms&quot;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Wrap the code you want to profile:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UserRegistration&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;log_time&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;do_something&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

     &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;log_time&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;something?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
       &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
     &lt;span class=&quot;k&quot;&gt;elsif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;some_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;log_time&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;do_something_else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
       &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
     &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then you will get something like that in your logs output:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;/data/app/releases/…/user_registration.rb:3:in register’ - Completed in 300.00 ms
/data/app/releases/…/user_registration.rb:5:in register’ - Completed in 500.00 ms
/data/app/releases/…/user_registration.rb:10:in register’ - Completed in 2000.00 ms&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And as expected, your database will be guilty in most cases. MOAR indexes.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Why ActiveRecord callbacks suck?</title>
   <link href="http://www.pablocantero.com//blog/2014/08/16/why-activerecord-callbacks-suck/"/>
   <updated>2014-08-16T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2014/08/16/why-activerecord-callbacks-suck</id>
   
   <content type="html">&lt;p&gt;It’s a kind of cliché in the Rails community to associate ActiveRecord callbacks with something bad or wrong.&lt;/p&gt;

&lt;p&gt;Given that every time I start typing &lt;code class=&quot;highlighter-rouge&quot;&gt;after_&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;before_&lt;/code&gt;, I hear a voice in my head: “Are you sure?”. This voice usually attempts me to fight the evil (poor design choices) with more evil.&lt;/p&gt;

&lt;p&gt;Callbacks are a sugar syntax to violate the &lt;a href=&quot;https://en.wikipedia.org/wiki/Single_responsibility_principle&quot;&gt;Single Responsibility Principle (SRP)&lt;/a&gt; and they can slow down your tests. That’s why I believe they can suck, but it doesn’t happen all the time. So before attempting an improvement to remove or avoiding to write a callback, pause and think if the responsibility you want to add belongs to the save context or not.&lt;/p&gt;

&lt;h2 id=&quot;violating-the-single-responsibility-principle-srp&quot;&gt;Violating the Single Responsibility Principle (SRP)&lt;/h2&gt;

&lt;p&gt;If you follow SRP by the book, you might end up with hundreds of single-public-method classes, and it isn’t what we want.&lt;/p&gt;

&lt;p&gt;If you need to send a confirmation email every time you create a new user, you have two responsibilities: one to create a user and another to send an email and they need specific classes and methods. If you do it all in the same place class/method, it can result in unexpected processing &lt;strong&gt;depending on your context&lt;/strong&gt; or become hard to maintain, because to update one code you need to change the other consequently.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Model&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;after_create&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:send_confirmation_email&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;send_confirmation_email&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;UserMailer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;registration_confirmation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;deliver&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We have two classes: &lt;code class=&quot;highlighter-rouge&quot;&gt;User&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;UserMailer&lt;/code&gt; and two methods: &lt;code class=&quot;highlighter-rouge&quot;&gt;User#save&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;UserMailer.registration_confirmation&lt;/code&gt; to create a user and send an email respectively.&lt;/p&gt;

&lt;p&gt;With this design if you want to create a new user in the console without sending the confirmation email, you can’t. The user creation is coupled with the email sending, so it isn’t the best design choice, but how frequently do you need to create a user without sending a confirmation email? Does it deserve a &lt;code class=&quot;highlighter-rouge&quot;&gt;UserRegistration&lt;/code&gt;?&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UserRegistration&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;UserMailer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;registration_confirmation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;deliver&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Being honest, I would go with a &lt;code class=&quot;highlighter-rouge&quot;&gt;UserRegistration&lt;/code&gt; class as an improvement on demand, based on business requirements (i.e. a requirement to allow users creation without sending the confirmation email). If you do it prematurely, someone can use &lt;code class=&quot;highlighter-rouge&quot;&gt;User#save&lt;/code&gt; instead of &lt;code class=&quot;highlighter-rouge&quot;&gt;UserRegistration.new(user).register&lt;/code&gt;, just because it is what he does “by default” using Rails, so that innocently skipping the confirmation email. I prefer to keep the common usage simple and trivial as possible and only treat the exceptions with special code.&lt;/p&gt;

&lt;p&gt;I believe the problem above is one of the simplest problem with callbacks, but frequently on our daily basis. So before over-engineering it, ignore the voice in your head and think about it.&lt;/p&gt;

&lt;p&gt;Violating SRP hurts when it makes things unexpected, hard to understand (hard to debug).&lt;/p&gt;

&lt;p&gt;Imagine every time you create an order, besides persisting it you need also to synchronise with a third party app, send an email and update inventories. And now, imagine if all this stuff (or even more) is done by an &lt;code class=&quot;highlighter-rouge&quot;&gt;after_save&lt;/code&gt;, called after an unpretentious &lt;code class=&quot;highlighter-rouge&quot;&gt;Order#save&lt;/code&gt;. Would you expect that?&lt;/p&gt;

&lt;p&gt;A new developer joins the project, and starts a task to update all orders &lt;code class=&quot;highlighter-rouge&quot;&gt;origin&lt;/code&gt; attribute.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find_each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# update `origin`&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;Fuck this. Fuck that. Fuck those too. Fuck all these. This thing in particular.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s what is going to happen. That’s is one of the reasons that callbacks can suck, having more responsibilities than the expected.&lt;/p&gt;

&lt;p&gt;What’s expected and unexpected is relative, I know. But I suggest to consider much more your context, than just framework based classes or method names.&lt;/p&gt;

&lt;h1 id=&quot;slow-down-your-tests&quot;&gt;Slow down your tests&lt;/h1&gt;

&lt;p&gt;Callbacks can slow down your tests (RSpec etc), not your production code. If you need to do more stuff after persisting an order, no matter how you do it:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Model&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;protect&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:save&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;place&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# do something else&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Creating new methods and changing others visibility, creating other classes/methods, observers, &lt;strong&gt;callbacks&lt;/strong&gt; etc.&lt;/p&gt;

&lt;p&gt;You need to do that. So when a user click on “Place Order”, he will need to wait all this processing, no matter which abstractions were used to implement it. That’s it.&lt;/p&gt;

&lt;p&gt;Why it can slow down your tests? Imagine you create orders in many many tests, and the &lt;code class=&quot;highlighter-rouge&quot;&gt;Order#save&lt;/code&gt; does something magical using an &lt;code class=&quot;highlighter-rouge&quot;&gt;after_save&lt;/code&gt; then you forgot to stub it &lt;code class=&quot;highlighter-rouge&quot;&gt;before&lt;/code&gt; running your tests. Your orders creation will take much longer to finish, because they are doing something you don’t need in most of your tests. If you use like a &lt;code class=&quot;highlighter-rouge&quot;&gt;UserRegistration&lt;/code&gt; you will have the code and specs separately for each responsibility, win! Win? Would you change your production code just to make your tests faster?&lt;/p&gt;

&lt;p&gt;If callbacks start to slow down your production code, I would recommend to return to the &lt;strong&gt;Violating the SRP&lt;/strong&gt; topic, probably you are using them in the wrong way.&lt;/p&gt;

&lt;h2 id=&quot;sum-up&quot;&gt;Sum up&lt;/h2&gt;

&lt;p&gt;Callbacks “can” suck, correct! But avoiding them without considering the context, just because they are “bad” can suck more.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Opening a Rails console with Capistrano 3</title>
   <link href="http://www.pablocantero.com//blog/2014/06/16/opening-a-rails-console-with-capistrano-3/"/>
   <updated>2014-06-16T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2014/06/16/opening-a-rails-console-with-capistrano-3</id>
   
   <content type="html">&lt;h2 id=&quot;opening-a-rails-console&quot;&gt;Opening a Rails console&lt;/h2&gt;

&lt;p&gt;To open a Rails console with Capistrano add the snippet below in your &lt;code class=&quot;highlighter-rouge&quot;&gt;config/deploy.rb&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:rails&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Open a rails console `cap [staging] rails:console [server_index default: 0]`'&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:console&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ARGV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Opening a console on: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;hostname&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;….&quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ssh &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;hostname&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; -t 'cd &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:deploy_to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/current &amp;amp;&amp;amp; RAILS_ENV=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:rails_env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; bundle exec rails console'&quot;&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Usage:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# To open the first server in the servers list&lt;/span&gt;
cap &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;staging] rails:console

&lt;span class=&quot;c&quot;&gt;# To open the second server in the servers list&lt;/span&gt;
cap &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;staging] rails:console 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;bonus-1-opening-a-ssh-session&quot;&gt;BONUS 1: Opening a SSH session&lt;/h2&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Open ssh `cap [staging] ssh [server_index default: 0]`'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ssh&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ARGV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Opening a console on: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;hostname&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;….&quot;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ssh &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;hostname&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Usage:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# To open the first server in the servers list&lt;/span&gt;
cap &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;staging] ssh

&lt;span class=&quot;c&quot;&gt;# To open the second server in the servers list&lt;/span&gt;
cap &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;staging] ssh 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;bonus-2-opening-multiple-ssh-sessions&quot;&gt;BONUS 2: Opening multiple SSH sessions&lt;/h2&gt;

&lt;p&gt;Make sure you have &lt;a href=&quot;https://code.google.com/p/csshx/&quot;&gt;csshx&lt;/a&gt; installed. On OS X: &lt;code class=&quot;highlighter-rouge&quot;&gt;brew install csshx&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Open csshx `cap [staging] csshx`'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:csshx&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;servers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;hostname&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;csshx &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;servers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Usage:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cap &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;staging] csshx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</content>

 </entry>
 
 <entry>
   <title>I coded a Backbone App in Rails and it took much less code</title>
   <link href="http://www.pablocantero.com//blog/2014/04/20/i-coded-a-backbone-app-in-rails-and-it-took-much-less-code/"/>
   <updated>2014-04-20T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2014/04/20/i-coded-a-backbone-app-in-rails-and-it-took-much-less-code</id>
   
   <content type="html">&lt;p&gt;&lt;strong&gt;tl;dr&lt;/strong&gt; single-page applications (SPA) are awesome and fast. But if you don’t have a strong requirement to create a single-page application, don’t do that. It will slow down your development time. You can rewrite your application to be a single-page application later as an improvement, but if you start it as a single-page application and you need to deliver fast, you can give a big headache to yourself and maybe need to rewrite it as a normal web application (in our case in Rails) to meet the deadlines.&lt;/p&gt;

&lt;p&gt;DISCLAIMER: Although the AngularJS first commit (&lt;a href=&quot;https://github.com/angular/angular.js/commit/c9c176a53b1632ca2b1c6ed27382ab72ac21d45d&quot;&gt;Jan 05, 2010&lt;/a&gt;) is older than the Backbone one (&lt;a href=&quot;https://github.com/jashkenas/backbone/commit/8a960b479859d343a6c734eb1a5817a2ff6c2b52&quot;&gt;Sep 30, 2010&lt;/a&gt;), for me, Backbone became a reference for MVW before AngularJS or any other, so that’s why I will refer to Backbone as the oldest one.&lt;/p&gt;

&lt;p&gt;Another day a friend of mine shared a post with me - “&lt;a href=&quot;http://blog.42floors.com/coded-angular-tutorial-app-backbone-took-260-code&quot;&gt;I Coded the AngularJS Tutorial App in Backbone and it Took 260% More Code&lt;/a&gt;”. It is an interesting post showing that newer MVW frameworks such as AngularJS require less code to do the work than the “old ones” like Backbone. This comparison reminded me of a long time ago, in the Java world, the changes from HttpServlets (spaghetti servlets), then Struts (struts-config.xml), then Spring MVC (annotations) and so on.&lt;/p&gt;

&lt;p&gt;In my opinion it is the way to go. Backbone formalised a convention to write Rich Internet Applications by creating “classes” with specific responsibilities, it uses Routes, Models, Collections, Views and Templates instead of spaghetti JavaScript with Ajax calls. But on the other hand you have to write all callbacks, bindings etc and it isn’t magically implemented by a convention, like using data attributes (ng-*).&lt;/p&gt;

&lt;p&gt;So, if you have a requirement to be a single-page application, I mean, if it must be a single-page application, go and check what’s the best, maybe the “standard” isn’t the best one anymore.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you don’t have a strong requirement to create a single-page application, DON’T DO THAT!&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;single-page-applications-can-slow-down-your-development-time&quot;&gt;Single-page applications can slow down your development time&lt;/h2&gt;

&lt;h3 id=&quot;ruby-vs-javascript&quot;&gt;Ruby vs JavaScript&lt;/h3&gt;

&lt;p&gt;No matter how much you like JavaScript, calculate how many hours you spend coding in JavaScript and how many you spend coding in Ruby.&lt;/p&gt;

&lt;p&gt;I really like JavaScript and I think I’m good at, but I usually spend many many more hours coding in Ruby than in JavaScript. So it isn’t fair to compare my proficiency in Ruby which I’m coding all the time with JavaScript.&lt;/p&gt;

&lt;p&gt;So, if you need to deliver fast, go with the language and tools you know more!&lt;/p&gt;

&lt;h3 id=&quot;write-everything-twice&quot;&gt;Write everything twice&lt;/h3&gt;

&lt;p&gt;For a normal Customer CRUD on Rails, you usually have:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Customer Model&lt;/li&gt;
  &lt;li&gt;Customer Controller&lt;/li&gt;
  &lt;li&gt;Customer views: index.html.erb, show.html.erb, new.html.erb and _form.html.erb&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a normal Customer CRUD on Rails and Backbone, you usually have:&lt;/p&gt;

&lt;p&gt;On Rails side:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Customer Model&lt;/li&gt;
  &lt;li&gt;Customer Controller&lt;/li&gt;
  &lt;li&gt;Customer views: index.rabl, show.rabl&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/nesquena/rabl&quot;&gt;RABL&lt;/a&gt; is just an example, it could be with &lt;a href=&quot;https://github.com/rails-api/active_model_serializers&quot;&gt;ActiveModel::Serializers&lt;/a&gt; or whatever, but don’t believe that you will be able to use only &lt;a href=&quot;http://api.rubyonrails.org/classes/ActionController/Responder.html&quot;&gt;Responders&lt;/a&gt; and models all the time.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;On Backbone side:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Customer Collection&lt;/li&gt;
  &lt;li&gt;Customer Model&lt;/li&gt;
  &lt;li&gt;Customer View&lt;/li&gt;
  &lt;li&gt;Customer templates: index.jst.eco, show.jst.eco, new.jst.eco and _form.jst.eco&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You have much more code and you can’t reuse Rails helpers. You can try to use &lt;a href=&quot;https://github.com/railsware/js-routes&quot;&gt;js-routes&lt;/a&gt;, but you will probably find yourself trying to find some  JavaScript libraries analogue for &lt;a href=&quot;http://api.rubyonrails.org/classes/ActionView/Helpers/TextHelper.html&quot;&gt;Rails TextHelpers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/amatsuda/kaminari&quot;&gt;Kaminari&lt;/a&gt; for pagination,  no way, find something similar or write one by yourself.&lt;/p&gt;

&lt;h3 id=&quot;everything-will-be-an-api&quot;&gt;Everything will be an API&lt;/h3&gt;

&lt;p&gt;Sometimes in Rails we write some very specific actions i.e. &lt;code class=&quot;highlighter-rouge&quot;&gt;PUT users/:id/bio&lt;/code&gt;, because there is a specific page, which only updates the user bio and has some specific logic. As it is something internal, we don’t mind the non-Restful like route, because it “isn’t publicly exposed”. But when we expose it as an API to access from Backbone, even though it isn’t a real API, we can start overthinking on the “best fit” URL, nouns, verbs etc.&lt;/p&gt;

&lt;p&gt;Sometimes they can be evil, but who’s never used a &lt;a href=&quot;http://apidock.com/rails/ActionController/Helpers/ClassMethods/helper_method&quot;&gt;helper_method&lt;/a&gt;? You can’t use it anymore. If you want to, you will need to expose it as an API.&lt;/p&gt;

&lt;p&gt;Backbone works “automatically” with Rails resources URLs, but for custom URLs, we can’t easily create a form routing to the new specific URL. We need to create a specific method in the User model to make a request to the specific URL and a binding in the User view to call this specific method.&lt;/p&gt;

&lt;h3 id=&quot;page-reload-is-a-user-feedback&quot;&gt;Page reload is a user feedback&lt;/h3&gt;

&lt;p&gt;It can be a poor user feedback, but page reload is a user feedback. If a user clicks on the Save button and the page is reloaded, the user knows that something happened, even without explicit feedback. But if the Save is handled by Ajax, you must do something: show an ajax loader, alert or whatever, otherwise it will be frustrating, the users will not be sure if the action was properly done and they will probably click on the button multiple times, just to make sure it was done, causing much more requests to your server.&lt;/p&gt;

&lt;h3 id=&quot;page-reload-is-a-garbage-collector&quot;&gt;Page reload is a garbage collector&lt;/h3&gt;

&lt;p&gt;Do you clean your Backbone objects? I mean, if the user opens a view which loads N objects (collection, models, sub-views, templates etc), paginate, load more N objects then triggers a new route which loads a new view and other objects. Do you erase the previous view and nested objects? If the user returns to the previous view, do you reuse the previous loaded view to keep the previous state? If yes, if the user opens 20 views with N objects each, do you keep all these objects in memory?&lt;/p&gt;

&lt;p&gt;These are the questions you have to ask to yourself when developing a real single-page application. You have to take care   of the memory usage by hand - it isn’t magically handled by Backbone. If you never used Chrome/Developer/Profile or similar, you should to!&lt;/p&gt;

&lt;p&gt;You can get surprised on how a single-page application with many many views can become slow and consumes a lot of memory.&lt;/p&gt;

&lt;p&gt;Page reload erases the page memory for almost every user action.&lt;/p&gt;

&lt;h3 id=&quot;test&quot;&gt;Test&lt;/h3&gt;

&lt;p&gt;It can be subjective, but in my opinion RSpec and Capybara are much easier and complete than any other JavaScript test framework I know.&lt;/p&gt;

&lt;p&gt;For those who are already working on a single-page application, do you feel confidence with your JavaScript test coverage? Can you unit test a Backbone View manipulating DOM objects and feel proud on what we had to do to test it? I think many people can, but I couldn’t.&lt;/p&gt;

&lt;h2 id=&quot;are-single-page-applications-good&quot;&gt;Are single-page applications good?&lt;/h2&gt;

&lt;p&gt;Yes, they are awesome. I love when I’m using Gmail and it doesn’t reload the page and also keeps the previous state. I can do things without Internet connection, because they are loaded in the browser.&lt;/p&gt;

&lt;p&gt;My point is just this: single-page applications frameworks/ecosystem aren’t ready enough for everything; might be in the future, but for now it can be too expensive to develop and the users are more used to the UX of normal web applications than the single-page (desktop like) ones. You can’t do the same UX for both.&lt;/p&gt;

&lt;p&gt;You can rewrite your application to be a single-page application in the future, the bad is when you need to rollback to a normal one. If you start in Rails, you can rewrite one page then another and so on, but if you start a project only with the JavaScript ecosystem outside Ruby/Rails (it was our case), it will be hard to migrate to Rails smoothly.&lt;/p&gt;

&lt;p&gt;Something I did before, which worked great, was to develop just specific parts of the application with Backbone (can be any other MVW). For example, we developed a Store Palette Editor using Backbone Models and Views, without Routers and Templates. It worked great, we could use the Backbone convention for Models and Views, isolate the classes with specific responsibilities, the UX was great, we coded it fast and it was easy to test.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Adventures in MongoDB land</title>
   <link href="http://www.pablocantero.com//blog/2014/02/08/adventures-in-mongodb-land/"/>
   <updated>2014-02-08T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2014/02/08/adventures-in-mongodb-land</id>
   
   <content type="html">&lt;p&gt;Pablo’s adventures in MongoDB-land™.&lt;/p&gt;

&lt;h2 id=&quot;primary-key-on-steroids&quot;&gt;Primary key on steroids&lt;/h2&gt;

&lt;p&gt;MongoDB’s primary key &lt;code class=&quot;highlighter-rouge&quot;&gt;_id&lt;/code&gt; by default is an &lt;a href=&quot;http://docs.mongodb.org/manual/reference/object-id/&quot;&gt;ObjectId&lt;/a&gt;, which is automatically added to your documents while inserting them into a collection.&lt;/p&gt;

&lt;p&gt;But you can also set your own &lt;code class=&quot;highlighter-rouge&quot;&gt;_id&lt;/code&gt; if you want.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;db.my_collection.insert&lt;span class=&quot;o&quot;&gt;({&lt;/span&gt; _id: &lt;span class=&quot;s1&quot;&gt;'user@example.com'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Have the ability of writing your own &lt;code class=&quot;highlighter-rouge&quot;&gt;_id&lt;/code&gt; can be very useful. We have an internal system which uses MongoDB as a queue database. When we move messages across the queues (collections), we preserve the original &lt;code class=&quot;highlighter-rouge&quot;&gt;_id&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// find a message to be processed
message &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; db.incoming.find_one&lt;span class=&quot;o&quot;&gt;({&lt;/span&gt; _id: ObjectId&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;52f578c1ecf69b714400004a&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;

// some processing

db.accepted.insert&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
// &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; ObjectId&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;52f578c1ecf69b714400004a&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

// remove the message from the incoming queue
db.incoming.remove&lt;span class=&quot;o&quot;&gt;({&lt;/span&gt; _id: message[&lt;span class=&quot;s2&quot;&gt;&quot;_id&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The message will have the same &lt;code class=&quot;highlighter-rouge&quot;&gt;_id&lt;/code&gt; no matter where it is sitting on.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;MongoDB adds an index and a unique constraint on &lt;code class=&quot;highlighter-rouge&quot;&gt;_id&lt;/code&gt; by default.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;objectid&quot;&gt;ObjectId&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;ObjectId is a 12-byte BSON type, constructed using:&lt;/p&gt;

  &lt;p&gt;a 4-byte value representing the seconds since the Unix epoch,&lt;/p&gt;

  &lt;p&gt;a 3-byte machine identifier,&lt;/p&gt;

  &lt;p&gt;a 2-byte process id, and&lt;/p&gt;

  &lt;p&gt;a 3-byte counter, starting with a random value.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;a 4-byte value representing the seconds since the Unix epoch&lt;/strong&gt; means &lt;code class=&quot;highlighter-rouge&quot;&gt;created_at&lt;/code&gt; field for free! Which we can use to sort, search and retrieve the creation date.&lt;/p&gt;

&lt;h4 id=&quot;sort-by-_id&quot;&gt;Sort by _id&lt;/h4&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// sort by created_at &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;descending order
db.my_collection.find&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;.sort&lt;span class=&quot;o&quot;&gt;({&lt;/span&gt; _id: &lt;span class=&quot;nt&quot;&gt;-1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;

// sort by created_at &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;ascending order
db.my_collection.find&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;.sort&lt;span class=&quot;o&quot;&gt;({&lt;/span&gt; _id: 1 &lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;_id-to-timestamp&quot;&gt;_id to timestamp&lt;/h4&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ObjectId&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;507f191e810c19729de860ea&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;.getTimestamp&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
// &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; ISODate&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2012-10-17T20:46:22Z&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ruby example:&lt;/p&gt;

&lt;div class=&quot;language-erb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
&lt;span class=&quot;nt&quot;&gt;&amp;lt;thead&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Id&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Foo&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Created at&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/thread&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;tbody&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;td&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%= document['_id'] &lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt; document['foo'] &lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt; document['_id'].generation_time &lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt; end &lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;filter-by-_id&quot;&gt;Filter by _id&lt;/h5&gt;

&lt;p&gt;If you don’t have a &lt;code class=&quot;highlighter-rouge&quot;&gt;created_at&lt;/code&gt; field by your own and someone asks you to give a list of documents created between &lt;code class=&quot;highlighter-rouge&quot;&gt;something&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;something else&lt;/code&gt;, can you do it? Yes, you can!&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;time_something &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; Time.utc&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;2010, 1, 1&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
time_something_else &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; Time.utc&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;2010, 1, 10&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

time_something_id &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; ObjectId.from_time&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;time_something&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
time_something_else_id &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; ObjectId.from_time&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;time_something_else&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

collection.find&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'_id'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'$gt'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; time_something, &lt;span class=&quot;s1&quot;&gt;'$lt'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;  time_something_else &lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Unfortunately &lt;code class=&quot;highlighter-rouge&quot;&gt;ObjectId.from_time(time)&lt;/code&gt; isn’t available in the MongoDB Shell. But it is available in the &lt;a href=&quot;http://api.mongodb.org/ruby/current/BSON/ObjectId.html#from_time-class_method&quot;&gt;mongo-ruby-driver&lt;/a&gt;. Ruby rocks!&lt;/p&gt;

&lt;h2 id=&quot;expensive-counts&quot;&gt;Expensive counts&lt;/h2&gt;

&lt;p&gt;When you have collections with million of documents, you will notice the searches aren’t that fast, even when using indexes. It also applies for counts, if you use a simple count without any criteria, it will be fine, fast, because MongoDB knows the collection size, but &lt;a href=&quot;https://www.google.com/webhp?#q=slow%20count%20mongodb&quot;&gt;if you need to filter the count, it might be very expensive&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Notification.where&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;account_id: &lt;span class=&quot;s1&quot;&gt;'5347c18a69702d1ef10c0000'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;.count
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# Completed in 15.354520466 seconds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It took 15 seconds to return the filtered count in a collection with 6120054 documents. To be fair with MongoDB, I have to mention that there are lot of documents (4951369) that matches the criteria above, which makes the query even more expensive, but it’s a real use case in my application.&lt;/p&gt;

&lt;p&gt;To make it faster, we limit the count total, so instead of showing “4951369” found we show “99+”.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Notification.where&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;account_id: &lt;span class=&quot;s1&quot;&gt;'5371ec6869702d131d010000'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;.limit&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-100&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;.only&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;:_id&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;.count&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# Completed in 0.021699112 seconds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Pretty fast, hum?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;count(true)&lt;/code&gt; - sets the &lt;a href=&quot;http://docs.mongodb.org/manual/reference/method/cursor.count/&quot;&gt;applySkipLimit&lt;/a&gt; = true, so the count respects the limit and skip when supplied. By default the count ignores the limit and skip.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;limit(-100)&lt;/code&gt; - a negative limit will ask &lt;a href=&quot;http://docs.mongodb.org/manual/reference/method/cursor.limit/&quot;&gt;MongoDB to return that number and close the cursor&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;only(:_id)&lt;/code&gt; - returns only the _id.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For sure the &lt;code class=&quot;highlighter-rouge&quot;&gt;count(true)&lt;/code&gt; is the most responsible for the performance improvement, but you will also notice that the negative limit and specifying only the _id to return helps with the performance too. Performance improvements is usually a lot of minor milliseconds improvements.&lt;/p&gt;

&lt;p&gt;If you are using &lt;a href=&quot;http://mongoid.org/&quot;&gt;Mongoid&lt;/a&gt;, you can add the &lt;code class=&quot;highlighter-rouge&quot;&gt;capped_count&lt;/code&gt; to your Models.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Notification&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;capped_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;limit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;only&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Notification.where(account_id: '5371ec6869702d131d010000').capped_count&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;mongorestore-drop&quot;&gt;mongorestore –drop&lt;/h2&gt;

&lt;p&gt;Use &lt;code class=&quot;highlighter-rouge&quot;&gt;--drop&lt;/code&gt; for a full restore. By default the &lt;a href=&quot;http://docs.mongodb.org/manual/reference/program/mongorestore/&quot;&gt;mongorestore&lt;/a&gt; utility only inserts documents. Any existing data in the target database will be left intact. The &lt;code class=&quot;highlighter-rouge&quot;&gt;--drop&lt;/code&gt; option drops every collection from the target database before restoring the collection from the dumped backup.&lt;/p&gt;

&lt;h2 id=&quot;warning-collection-names-should-not-begin-with-underscores&quot;&gt;WARNING: Collection names should not begin with underscores&lt;/h2&gt;

&lt;p&gt;If you are using one of the most advanced backup techniques which consists in renaming collections with a underscore prefix, you should beware of that.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;db.my_collection.renameCollection&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'_my_collection'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
show collections
// &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; _my_collections

db._my_collections.find&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
// &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; Sat Feb  8 10:25:21.233 TypeError: Cannot call method &lt;span class=&quot;s1&quot;&gt;'find'&lt;/span&gt; of undefined

db[&lt;span class=&quot;s1&quot;&gt;'_my_collections'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;.find&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
// &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; Sat Feb  8 10:25:21.233 TypeError: Cannot call method &lt;span class=&quot;s1&quot;&gt;'find'&lt;/span&gt; of undefined
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;MongoDB issue: &lt;a href=&quot;https://jira.mongodb.org/browse/SERVER-445&quot;&gt;Can’t reference collection names beginning with an underscore in the mongo shell
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To retrieve the underscored collection you can use: &lt;code class=&quot;highlighter-rouge&quot;&gt;db.getCollection('_my_collection')&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Underscore suffixes work fine:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;db.my_collection.renameCollection&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'my_collection_'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
db.my_collections_.find&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
// &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; success
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;capped-collections&quot;&gt;Capped collections&lt;/h2&gt;

&lt;h3 id=&quot;convert-to-capped&quot;&gt;Convert to capped&lt;/h3&gt;

&lt;p&gt;When you convert a collection to a capped collection, the collection loses the indexes.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;db.runCommand&lt;span class=&quot;o&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;convertToCapped&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;my_collection&quot;&lt;/span&gt;, size: sizeInBytes, max: maxNumberOfDocuments &lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Remember to recreate the indexes. Even the ones with TTL:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;db. my_collection.ensureIndex&lt;span class=&quot;o&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;created_at&quot;&lt;/span&gt;: 1 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;, &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; expireAfterSeconds: 604800 &lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;mongodb-likes-your-storage-as-much-as-you&quot;&gt;MongoDB likes your storage as much as you&lt;/h2&gt;

&lt;h3 id=&quot;log&quot;&gt;Log&lt;/h3&gt;

&lt;p&gt;Pay attention to the log files.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;du &lt;span class=&quot;nt&quot;&gt;-h&lt;/span&gt; /usr/local/var/log/mongodb/mongo.log
9.5G	/usr/local/var/log/mongodb/mongo.log
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Short-term solution:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rm  /usr/local/var/log/mongodb/mongo.log
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Long-term solution:&lt;/p&gt;

&lt;p&gt;Edit your &lt;code class=&quot;highlighter-rouge&quot;&gt;/usr/local/etc/mongod.conf&lt;/code&gt; and enable &lt;a href=&quot;http://docs.mongodb.org/manual/tutorial/rotate-log-files/#syslog-log-rotation&quot;&gt;Syslog Rotation&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Append logs to /usr/local/var/log/mongodb/mongo.log&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# logpath = /usr/local/var/log/mongodb/mongo.log&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# logappend = true&lt;/span&gt;
syslog &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://groups.google.com/d/msg/mongodb-user/XyGzMZ4dTYY/XzjyLfdL7RIJ&quot;&gt;On OS X &amp;gt;= 10.9.1 we need to set up some extra rules to enable syslog&lt;/a&gt;, otherwise it will not fail, but we will not be able to get the log outputs.&lt;/p&gt;

&lt;p&gt;Log all messages via syslog to a separate mongodb.log:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;vim /etc/asl/org.mongodb


? &lt;span class=&quot;o&quot;&gt;[=&lt;/span&gt; Sender mongod.27017] claim only
&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; file /var/log/mongodb.log &lt;span class=&quot;nv&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0640 compress &lt;span class=&quot;nv&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;bsd &lt;span class=&quot;nv&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;seq &lt;span class=&quot;nv&quot;&gt;file_max&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;5M &lt;span class=&quot;nv&quot;&gt;all_max&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;20M
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;or&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Save all messages to system.log:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;? &lt;span class=&quot;o&quot;&gt;[=&lt;/span&gt; Sender mongod.27017] file /var/log/system.log
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then reload the syslogd configuration:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pkill &lt;span class=&quot;nt&quot;&gt;-HUP&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; syslogd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;local-databases&quot;&gt;Local databases&lt;/h3&gt;

&lt;p&gt;Check your local databases you might have some which you don’t use anymore.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;du &lt;span class=&quot;nt&quot;&gt;-h&lt;/span&gt; /usr/local/var/mongodb
13G    /usr/local/var/mongodb

mongo
show dbs
// &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; foo
use foo
db.dropDatabase&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>

 </entry>
 
 <entry>
   <title>Automate multiple servers startup</title>
   <link href="http://www.pablocantero.com//blog/2013/10/07/automate-multiple-servers-startup/"/>
   <updated>2013-10-07T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2013/10/07/automate-multiple-servers-startup</id>
   
   <content type="html">&lt;p&gt;So, do you need to startup multiple servers or services to start your development environment? I do!&lt;/p&gt;

&lt;p&gt;We can split the Terminal in panels and start each server manually, it will work, but when we need to do it every day, it becomes boring.&lt;/p&gt;

&lt;p&gt;There are many ways to automate this startup, but the two that I like most are with &lt;a href=&quot;https://github.com/aziz/tmuxinator&quot;&gt;Tmuxinator&lt;/a&gt; or with &lt;a href=&quot;https://github.com/ddollar/foreman&quot;&gt;Foreman&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I usually use more Tmuxinator than Foreman for this automation, as I use &lt;a href=&quot;http://tmux.sourceforge.net/&quot;&gt;Tmux&lt;/a&gt; for my development environment. But both work fine and also work as a documentation. If someone wants to run an app that depends on others, you can send the Tmuxinator project file or Procfile to describe what’s required to get the app up and running.&lt;/p&gt;

&lt;h2 id=&quot;tmuxinator&quot;&gt;Tmuxinator&lt;/h2&gt;

&lt;p&gt;Create a &lt;code class=&quot;highlighter-rouge&quot;&gt;~/.tmuxinator/development.yml&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# ~/.tmuxinator/development.yml
project_name: development
project_root: /Users/pablo/workspace/
windows:
  - servers:
      layout: even-vertical
      panes:
        - mongod
        - cd /Users/pablo/workspace/project1 &amp;amp;&amp;amp; foreman start
        - cd /Users/pablo/workspace/project2 &amp;amp;&amp;amp; rails s -p 4000
        - cd /Users/pablo/workspace/project3 &amp;amp;&amp;amp; TOKEN=123 rackup
        - cd /Users/pablo/workspace/sandbox  &amp;amp;&amp;amp; rails s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then run:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ mux development
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;tips&quot;&gt;Tips&lt;/h3&gt;

&lt;h4 id=&quot;tmux-status-line&quot;&gt;Tmux Status Line&lt;/h4&gt;

&lt;p&gt;I show the &lt;code class=&quot;highlighter-rouge&quot;&gt;tmuxinator project name/pane title:window number&lt;/code&gt; in &lt;a href=&quot;https://github.com/phstc/dotfiles/blob/master/tmux.conf#L93-L110&quot;&gt;my left status line&lt;/a&gt;. The pane title is very useful when you have multiples panes in the same window.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# tmux.conf
set -g status-utf8 on

# status line left side
set -g status-left-length 40
set -g status-left &quot;#[bold,fg=green]#S/#T:#[fg=yellow]#I&quot;

# set -g status-left-length 100
# set -g status-left '#[fg=green] #T#[default]'

# status line right side
# 15% | 28 Nov 08:15AM
set -g status-right &quot;#[bg=colour251,fg=colour240]%I:%M%p #[bg=colour240,fg=colour251] %d %b&quot;

# update the status bar every sixty seconds
set -g status-interval 30

# center the window list
set -g status-justify centre
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To define the pane title I have this function in my &lt;a href=&quot;https://github.com/phstc/dotfiles/blob/master/bashrc#L71-L73&quot;&gt;~/.bashrc&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# ~/.bashrc
func set_tmux_pane_title() {
  printf &quot;\033]2;%s\033\\&quot; &quot;$*&quot;;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then in the Tmuxinator project file I set the pane title as follows:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# ~/.tmuxinator/development.yml
project_name: development
project_root: /Users/pablo/workspace/
windows:
  - servers:
      layout: even-vertical
      panes:
        - set_tmux_pane_title mongo    &amp;amp;&amp;amp; mongod
        - set_tmux_pane_title project1 &amp;amp;&amp;amp; cd /Users/pablo/workspace/project1 &amp;amp;&amp;amp; foreman start
        - set_tmux_pane_title project2 &amp;amp;&amp;amp; cd /Users/pablo/workspace/project2 &amp;amp;&amp;amp; rails s -p 4000
        - set_tmux_pane_title project3 &amp;amp;&amp;amp; cd /Users/pablo/workspace/project3 &amp;amp;&amp;amp; TOKEN=123 rackup
        - set_tmux_pane_title sandbox  &amp;amp;&amp;amp; cd /Users/pablo/workspace/sandbox  &amp;amp;&amp;amp; rails s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;switch-panes&quot;&gt;Switch panes&lt;/h4&gt;

&lt;p&gt;To &lt;a href=&quot;https://github.com/phstc/dotfiles/blob/master/tmux.conf#L57-L61&quot;&gt;switch panes (up/down/left/right)&lt;/a&gt; I use &lt;code class=&quot;highlighter-rouge&quot;&gt;ctrl+shift+arrow keys&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# ~/.tmux.conf
bind -n S-C-Up select-pane -U
bind -n S-C-Down select-pane -D
bind -n S-C-Left select-pane -L
bind -n S-C-Right select-pane -R
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;zoom-inzoom-out&quot;&gt;Zoom in/Zoom out&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + b + z&lt;/code&gt; - zoom in&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl + b + z&lt;/code&gt; - zoom out&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;foreman&quot;&gt;Foreman&lt;/h2&gt;

&lt;p&gt;Create a &lt;code class=&quot;highlighter-rouge&quot;&gt;Procfile_development&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# ~/Procfile_development
mongo: mongod
project1: sh -c 'cd /Users/pablo/workspace/project1 &amp;amp;&amp;amp; foreman start'
project2: sh -c 'cd /Users/pablo/workspace/project2 &amp;amp;&amp;amp; rails s -p 4000'
project3: sh -c 'cd /Users/pablo/workspace/project3 &amp;amp;&amp;amp; TOKEN=123 rackup'
sandbox: sh -c 'cd /Users/pablo/workspace/sandbox &amp;amp;&amp;amp; rails s'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then run:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ foreman start -f ~/Procfile_development
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>

 </entry>
 
 <entry>
   <title>Quick and Dirty: Ruby interpolation</title>
   <link href="http://www.pablocantero.com//blog/2013/07/26/quick-and-dirty-ruby-interpolation/"/>
   <updated>2013-07-26T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2013/07/26/quick-and-dirty-ruby-interpolation</id>
   
   <content type="html">&lt;p&gt;Hey, it’s been a while since I last wrote something here.&lt;/p&gt;

&lt;p&gt;I would like to share something that is trivial and everybody knows, but always when I see or code in this way, it seems cool and smart.&lt;/p&gt;

&lt;p&gt;Let’s look at some &lt;code class=&quot;highlighter-rouge&quot;&gt;foo&lt;/code&gt; code.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;def generate_awesome_key(some_object)
  # [customer].[server].[source].[msg].[dest].awesome_key

  &quot;#{some_object.customer}.#{ENV['SERVER_NAME']}.some_object.source}.#{some_object.message_name}.#{some_object.destination_name}.awesome_key&quot;
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(If you guessed that it is something to generate &lt;a href=&quot;https://github.com/etsy/statsd&quot;&gt;StatsD&lt;/a&gt; key/bucket path, you got it.)&lt;/p&gt;

&lt;p&gt;That’s how interpolation works: some magic double quotes &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;&quot;&lt;/code&gt; + &lt;code class=&quot;highlighter-rouge&quot;&gt;#{}&lt;/code&gt; + expression.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# hello.rb
name = ARGV[0]
puts &quot;Hello World, #{name}&quot;

# ruby hello.rb Pablo
# =&amp;gt; Hello World, Pablo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Even though interpolation is the first manner that comes to mind when we need to concatenate strings + variables, in the first example the code is hard to read. I’m not a big fan of code comments in general (don’t misunderstand it, please), but in this case, it makes sense due to the code readability.&lt;/p&gt;

&lt;p&gt;There are lot of ways to write (refactor) this code - that’s how Ruby works. We can do the same thing in a variety of ways, most of them really bad, but some valuable.&lt;/p&gt;

&lt;p&gt;One of them which I demonstrate here is with arrays.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;def generate_awesome_key(some_object)
  # [customer].[server].[source].[msg].[dest].awesome_key

  [ some_object.customer,
    ENV['SERVER_NAME'],
    some_object.source,
    some_object.message_name,
    some_object.destination_name,
    'awesome_key' ].join '.'
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or more “generic”.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;def generate_awesome_key(some_object, *keys)
  # [customer].[server].[source].[msg].[dest].[keys…]

  ([ some_object.customer,
    ENV['SERVER_NAME'],
    some_object.source,
    some_object.message_name,
    some_object.destination_name,
   ] + keys).join '.'
end

# generate_awesome_key(some_object, 'awesome1', 'awesome2')
# =&amp;gt; customer.server.source.msg.dest.awesome1.awesome2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>

 </entry>
 
 <entry>
   <title>Auto scale Sidekiq workers on Amazon EC2</title>
   <link href="http://www.pablocantero.com//blog/2013/05/04/auto-scale-sidekiq-workers-on-amazon-ec2/"/>
   <updated>2013-05-04T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2013/05/04/auto-scale-sidekiq-workers-on-amazon-ec2</id>
   
   <content type="html">&lt;p&gt;We use &lt;a href=&quot;https://github.com/mperham/sidekiq&quot;&gt;Sidekiq&lt;/a&gt; a lot, from image conversion to shipment tracking numbers generation.&lt;/p&gt;

&lt;p&gt;The size of our queues change drastically during the day. When it happens, we increase or decrease our workers by adding or removing EC2 instances automatically, thanks to &lt;a href=&quot;http://aws.amazon.com/autoscaling/&quot;&gt;Amazon Auto Scaling&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;amazon-auto-scaling&quot;&gt;Amazon Auto Scaling&lt;/h2&gt;

&lt;p&gt;I will not get into the details of Amazon Auto Scaling, as it deserves an entire post about it.&lt;/p&gt;

&lt;p&gt;Roughly you have to create an Auto Scaling Group, Launch Configuration and Scaling Up/Down policies.&lt;/p&gt;

&lt;p&gt;Remember that Amazon Auto Scaling will not remove your instances automatically. If you create Scaling Up policies, you will need to create Scaling Down as well to remove them.&lt;/p&gt;

&lt;h3 id=&quot;netflix-asgard&quot;&gt;Netflix Asgard&lt;/h3&gt;

&lt;p&gt;Instead of creating your own Auto Scale scripts with &lt;a href=&quot;http://aws.amazon.com/sdkforruby/&quot;&gt;AWS-SDK&lt;/a&gt;, I recommend &lt;a href=&quot;https://github.com/Netflix/asgard&quot;&gt;Netflix Asgard&lt;/a&gt;. It isn’t a killer tool, but it works.&lt;/p&gt;

&lt;h4 id=&quot;asgard-considerations&quot;&gt;Asgard considerations&lt;/h4&gt;

&lt;p&gt;It doesn’t work well with other Tomcat apps. To work properly with Tomcat, it must be the ROOT app. Another option is to run it as a &lt;a href=&quot;https://github.com/Netflix/asgard/wiki/Quick-Start-Guide&quot;&gt;standalone app&lt;/a&gt;, this is how I use it.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;JAVA_HOME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home java &lt;span class=&quot;nt&quot;&gt;-Xmx1024M&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-XX&lt;/span&gt;:MaxPermSize&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;128m &lt;span class=&quot;nt&quot;&gt;-jar&lt;/span&gt; asgard-standalone.jar &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt; localhost 8888
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(it works better on Java 6)&lt;/p&gt;

&lt;h2 id=&quot;queue-size-metric&quot;&gt;Queue Size Metric&lt;/h2&gt;

&lt;p&gt;To trigger your Scale Up/Down policies based on the Queue Size, you have to publish a new &lt;a href=&quot;http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/publishingMetrics.html&quot;&gt;Custom Metric on CloudWatch&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;get-the-queue-size&quot;&gt;Get the Queue Size&lt;/h3&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;sidekiq&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SidekiqMetric&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;queue_size&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;stats&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Sidekiq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inject&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;publish-the-queue-size&quot;&gt;Publish the Queue Size&lt;/h3&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws-sdk&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;QueueSizeMetric&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;AWS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;access_key_id: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;…&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;secret_access_key: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;…&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;put&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;metric&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;AWS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CloudWatch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Metric&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Worker&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;QueueSize&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SidekiqMetric&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;queue_size&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;metric&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;put_data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;keep-the-metrics-updated&quot;&gt;Keep the metrics updated&lt;/h3&gt;

&lt;p&gt;We use the &lt;a href=&quot;https://github.com/javan/whenever&quot;&gt;whenever gem&lt;/a&gt; to create a cron job to update the queue size every minute.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# schedule.rb&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;every&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;minutes&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;cd /home/ubuntu/queues/current &amp;amp;&amp;amp; bundle exec rake update_queue_size_metric&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# RakeFile&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:update_queue_size_metric&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;QueueSizeMetric&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;put&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are many other ways to keep the metrics updated, for example using a worker to update the metrics.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# app/workers/queue_size_metric_worker.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;QueueSizeMetricWorker&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Sidekiq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Worker&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;perform&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;QueueSizeMetric&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;put&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;QueueSizeMetricWorker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perform_in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;minute&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The important thing is to keep them updated, no matter how. Otherwise your Auto Scaling will not behave as expected.&lt;/p&gt;

&lt;h2 id=&quot;all-done&quot;&gt;All Done&lt;/h2&gt;

&lt;p&gt;Now when creating your Scale Policies, set the metric namespace to &lt;strong&gt;Worker&lt;/strong&gt; and name to &lt;strong&gt;QueueSize&lt;/strong&gt;.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>My setup</title>
   <link href="http://www.pablocantero.com//blog/2013/04/16/my-setup/"/>
   <updated>2013-04-16T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2013/04/16/my-setup</id>
   
   <content type="html">&lt;p&gt;[UPDATE] Now I’m in &lt;a href=&quot;http://usesthis.com/community/&quot;&gt;The Setup/Community&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is a post about my setup, what I use to get stuff done. It was inspired by &lt;a href=&quot;http://usesthis.com/&quot;&gt;The Setup&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;who-are-you-and-what-do-you-do&quot;&gt;Who are you, and what do you do?&lt;/h2&gt;

&lt;p&gt;Hi, my name is Pablo Cantero, I’m software developer, currently working mostly with Ruby ecosystem, from web servers provisioning to web app development, including all cool stuff from this fancy world.&lt;/p&gt;

&lt;p&gt;Before Ruby I worked with JEE and I also worked with Classic ASP in my first job as a developer (2002-2004). Even using ASP, it was a really great experience for a first job because, the company was small, I was able to do everything from front-end to database and server.&lt;/p&gt;

&lt;p&gt;As a fan of JavaScript, I also play around with Node.js.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://br.linkedin.com/in/pablocantero/&quot;&gt;My Linkedin profile&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;what-hardware-do-you-use&quot;&gt;What hardware do you use?&lt;/h2&gt;

&lt;p&gt;I’m on a 13-inch MacBook Pro, bought in 2009. Although it is an old lady, it got some fresh blood after I updated it with 256GB SSD and 8GB of RAM.&lt;/p&gt;

&lt;p&gt;I use an iPad 2 3G 16GB for reading (Flipboard and Stanza).&lt;/p&gt;

&lt;p&gt;I use an iPhone 4s 16GB for reading (Instapaper), games, WhatsApp, music (Amazon Cloud Player) and phone functions.&lt;/p&gt;

&lt;p&gt;I recently bought a Kindle PaperWhite for reading in place of iPad + Stanza. The idea of the Kindle is to use it in the train going to my work. The iPad isn’t so comfortable for this purpose.&lt;/p&gt;

&lt;h2 id=&quot;and-what-software&quot;&gt;And what software?&lt;/h2&gt;

&lt;p&gt;For development I use Vim. My &lt;a href=&quot;https://github.com/phstc/vimfiles&quot;&gt;vimfiles&lt;/a&gt; has some plugins and shortcuts (maps) to make it even more productive, mostly are specific for the Ruby environment. I also use &lt;a href=&quot;https://chrome.google.com/webstore/detail/vimium/dbepggeogbaibhgnhhndojpepiihcmeb&quot;&gt;Vimium&lt;/a&gt; on Chrome.&lt;/p&gt;

&lt;p&gt;For terminal I use &lt;a href=&quot;http://www.iterm2.com/#/section/home&quot;&gt;iTerm 2&lt;/a&gt; plus &lt;a href=&quot;https://github.com/robbyrussell/oh-my-zsh&quot;&gt;oh-my-zsh&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For password management I use 1Password. Before that I used LastPass.   I only memorize essential passwords. Most of my passwords were generated by these managers.&lt;/p&gt;

&lt;p&gt;For writing as my blog is in Markdown (powered by &lt;a href=&quot;https://github.com/mojombo/jekyll&quot;&gt;Jekyll&lt;/a&gt;), I use &lt;a href=&quot;http://www.iawriter.com/&quot;&gt;iA Writer&lt;/a&gt;. It keeps my focus when I’m writing and doesn’t overkill with useless options.&lt;/p&gt;

&lt;p&gt;For productivity I use &lt;a href=&quot;http://www.alfredapp.com/powerpack/&quot;&gt;AlfredApp + Powerpack&lt;/a&gt;. I replaced the Spotlight shortcut (CMD + Space) to the Alfred prompt; the Powerpack is very handy, specially to fast browse directories and for clipboard management (before I used Flycut), it can be also integrated with 1Password. I use &lt;a href=&quot;http://mizage.com/divvy/&quot;&gt;Divvy&lt;/a&gt; for Window Management, I configured useful shortcuts to resize windows to 100%, 50% left, 50% right, up, bottom etc. I use &lt;a href=&quot;http://totalfinder.binaryage.com/&quot;&gt;TotalFinder&lt;/a&gt; to bring tabs and cut (CMD + X) to Finder. I got a free license contributing to the project on GitHub.&lt;/p&gt;

&lt;p&gt;For web browsing I use Chrome!&lt;/p&gt;

&lt;p&gt;For email I use Gmail with shortcuts enabled.&lt;/p&gt;

&lt;h2 id=&quot;what-would-be-your-dream-setup&quot;&gt;What would be your dream setup?&lt;/h2&gt;

&lt;p&gt;I don’t have a big ambition for a dream setup, maybe a MacBook Air, but I’m not totally sure about it anymore after the new MacBook Pro, as it got really thin without the optical drive.&lt;/p&gt;

&lt;p&gt;Something that I would like to have is an Apple Time Capsule. To save my backups using Time Machine, I have to attach an external drive (this external drive was the original MacBook Hard Disk that I replaced with a 256GB SSD), and with Time Capsule I can do it wirelessly. But I need more courage to buy a Time Capsule, because it is too expensive for my needs.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Monit restarts Unicorn USR2 - You are doing it wrong</title>
   <link href="http://www.pablocantero.com//blog/2013/04/02/monit-restarts-unicorn-usr2-you-are-doing-it-wrong/"/>
   <updated>2013-04-02T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2013/04/02/monit-restarts-unicorn-usr2-you-are-doing-it-wrong</id>
   
   <content type="html">&lt;p&gt;&lt;strong&gt;tl;dr&lt;/strong&gt; Use Monit to &lt;code class=&quot;highlighter-rouge&quot;&gt;start&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;stop&lt;/code&gt; Unicorn, if you want to graceful &lt;code class=&quot;highlighter-rouge&quot;&gt;restart&lt;/code&gt;, do it outside Monit (&lt;a href=&quot;https://gist.github.com/phstc/5312520&quot;&gt;sample scripts&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;monit&quot;&gt;Monit&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://mmonit.com/monit/&quot;&gt;Monit&lt;/a&gt; is an awesome open source tool for managing and monitoring processes, programs, files etc.&lt;/p&gt;

&lt;p&gt;Although Monit is awesome, unfortunately for process monitoring, it hasn’t a &lt;code class=&quot;highlighter-rouge&quot;&gt;restart&lt;/code&gt; action, but only &lt;code class=&quot;highlighter-rouge&quot;&gt;start&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;stop&lt;/code&gt; actions.&lt;/p&gt;

&lt;p&gt;In fact, as you can see in the &lt;a href=&quot;http://mmonit.com/monit/download/&quot;&gt;Monit source code&lt;/a&gt;, the &lt;code class=&quot;highlighter-rouge&quot;&gt;restart&lt;/code&gt; explicit executes &lt;code class=&quot;highlighter-rouge&quot;&gt;stop&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;start&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;restart)
	$0 stop
	$0 start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;monit-life-cycle&quot;&gt;Monit life cycle&lt;/h3&gt;

&lt;p&gt;Monit checks for a pid or process matching (depending on your configuration) in a specific interval defined in &lt;code class=&quot;highlighter-rouge&quot;&gt;monitrc&lt;/code&gt; (&lt;code class=&quot;highlighter-rouge&quot;&gt;set daemon n&lt;/code&gt;) and, if the process doesn’t exist, Monit will execute the &lt;code class=&quot;highlighter-rouge&quot;&gt;start&lt;/code&gt; action.&lt;/p&gt;

&lt;p&gt;Monit only restarts (&lt;code class=&quot;highlighter-rouge&quot;&gt;stop&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;start&lt;/code&gt;) a process, if you manually execute &lt;code class=&quot;highlighter-rouge&quot;&gt;monit restart all|name&lt;/code&gt; or if you defined a resource testing triggering a restart.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;check process unicorn_master with pidfile &quot;...&quot;
      start = &quot;...&quot;
      stop = &quot;...&quot;
      if cpu is greater than 50% for 5 cycles then restart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;unicorn&quot;&gt;Unicorn&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://unicorn.bogomips.org/&quot;&gt;Unicorn&lt;/a&gt; is a &lt;a href=&quot;http://www.youtube.com/watch?v=zc16ABAWTRk&amp;amp;feature=youtu.be&amp;amp;t=6m12s&quot;&gt;great ass&lt;/a&gt; Rack HTTP server.&lt;/p&gt;

&lt;p&gt;The combination of Monit and Unicorn is the motivation for this post.&lt;/p&gt;

&lt;p&gt;I saw several posts over the internet suggesting this configuration.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;check process unicorn_master with pidfile &quot;…&quot;
  start program = &quot;/bin/true&quot;
  stop program = &quot;set ruby;set gems path; kill -USR2 `cat pidfile`&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;People use this suggested configuration above to keep Unicorn always running and restarts it gracefully.&lt;/p&gt;

&lt;p&gt;I’m not saying that it is wrong. If it works for you, it’s ok. But even if it works, don’t use a long command in the Monit actions because it is hard to maintain so instead, create a bash script and set it in the action.&lt;/p&gt;

&lt;p&gt;Unicorn accepts &lt;a href=&quot;http://unicorn.bogomips.org/SIGNALS.html&quot;&gt;signals&lt;/a&gt; in kill action. The signal &lt;code class=&quot;highlighter-rouge&quot;&gt;USR2&lt;/code&gt;, reexecutes the running binary. When the new process is up and running, it kills the original one, keeping the server always running. The advantage to use &lt;code class=&quot;highlighter-rouge&quot;&gt;USR2&lt;/code&gt; in place of &lt;code class=&quot;highlighter-rouge&quot;&gt;HUP&lt;/code&gt; is because you are probably (should be) using &lt;code class=&quot;highlighter-rouge&quot;&gt;preload_app true&lt;/code&gt; directive, specially in a Rails environment. The kill &lt;code class=&quot;highlighter-rouge&quot;&gt;USR2&lt;/code&gt; works when your code was updated and the new process needs to get the fresh code. If you use &lt;code class=&quot;highlighter-rouge&quot;&gt;HUP&lt;/code&gt; instead and &lt;code class=&quot;highlighter-rouge&quot;&gt;preload_app true&lt;/code&gt; the code will not be updated.&lt;/p&gt;

&lt;h2 id=&quot;what-is-my-point&quot;&gt;What is my point?&lt;/h2&gt;

&lt;p&gt;For me this &lt;code class=&quot;highlighter-rouge&quot;&gt;start&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;stop&lt;/code&gt; actions mentioned in the example above are a dirty workaround. I don’t like them. Sometimes my Monit doesn’t work well and I can’t blame it because I’m using a workaround. When Monit doesn’t behave as expected, I usually do a manually combination of &lt;code class=&quot;highlighter-rouge&quot;&gt;pgrep&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;pkill&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;My suggestion is to use Monit as is.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;check process unicorn_master with pidfile &quot;…&quot;
  start program = &quot;/usr/bin/unicorn_start&quot;
  stop program = &quot;/usr/bin/unicorn_stop&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;ok-ok-how-about-graceful-restart&quot;&gt;Ok ok… How about graceful restart?&lt;/h3&gt;

&lt;p&gt;In most scenarios you only need graceful restart when:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Your application source code was updated. In this scenario, when you make a new deploy, instead of executing &lt;code class=&quot;highlighter-rouge&quot;&gt;monit restart name&lt;/code&gt;, you can run the script &lt;code class=&quot;highlighter-rouge&quot;&gt;/usr/bin/unicorn_restart&lt;/code&gt; in the end of your deploy. We use a &lt;a href=&quot;https://github.com/capistrano/capistrano&quot;&gt;Capistrano&lt;/a&gt; after deploy hook to execute the restart &lt;code class=&quot;highlighter-rouge&quot;&gt;run &quot;pkill -USR2 -f unicorn_rails.*master&quot;&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A resource testing triggers a &lt;code class=&quot;highlighter-rouge&quot;&gt;restart&lt;/code&gt;. In place of using &lt;code class=&quot;highlighter-rouge&quot;&gt;restart&lt;/code&gt; action, try to execute a command &lt;code class=&quot;highlighter-rouge&quot;&gt;if cpu is greater than 50% for 5 cycles then exec &quot;/usr/bin/unicorn_restart&quot;&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;resource-testing&quot;&gt;Resource testing&lt;/h3&gt;

&lt;p&gt;Beware of using Resource Testing.&lt;/p&gt;

&lt;p&gt;Firstly, you can hide an internal problem. You don’t need to be a hacker like the &lt;a href=&quot;http://blog.nelhage.com/2013/03/tracking-an-eventmachine-leak/&quot;&gt;Stripe guys&lt;/a&gt; going too deep to discover the cause of the memory leak, but you should, at least have a look at.&lt;/p&gt;

&lt;p&gt;Secondly, if you are using &lt;a href=&quot;http://aws.amazon.com/autoscaling/&quot;&gt;AWS AutoScaling&lt;/a&gt;, you probably use AutoScaling Policies (&lt;a href=&quot;http://pablocantero.com/blog/2012/09/07/use-auto-scaling-avoid-cloud-smells/&quot;&gt;avoid Cloud Smells&lt;/a&gt;). In this scenario your Resource Testing can conflict with AutoScaling Policies. If you have a Scale Up &lt;code class=&quot;highlighter-rouge&quot;&gt;if cpu &amp;gt; 80%&lt;/code&gt; and a Resource Testing for the same, one will nullify the other.&lt;/p&gt;

</content>

 </entry>
 
 <entry>
   <title>Look Who's Talking</title>
   <link href="http://www.pablocantero.com//blog/2013/02/21/look-whos-talking/"/>
   <updated>2013-02-21T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2013/02/21/look-whos-talking</id>
   
   <content type="html">&lt;p&gt;&lt;img src=&quot;http://www.pablocantero.com/assets/images/posts/look-who-is-talking.jpg&quot; alt=&quot;Look who's talking&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;once-upon-a-time&quot;&gt;Once Upon a Time&lt;/h1&gt;

&lt;p&gt;Have a seat, I will tell you a story.&lt;/p&gt;

&lt;p&gt;Before talking about &lt;a href=&quot;http://www.twilio.com/&quot;&gt;Twilio&lt;/a&gt;, I’m going to talk about my motivation to use it. It was for a support phone app, &lt;a href=&quot;https://github.com/phstc/support-roulette&quot;&gt;Support Roulette&lt;/a&gt;. After all, we didn’t use it exactly as is, although we used part of it.&lt;/p&gt;

&lt;p&gt;Due to the easiness to implement an app able to answer a call, record a voice message, send and receive a text message, in other words, an app which talks, I got inspired to write this post and also to present a &lt;a href=&quot;https://github.com/phstc/support-roulette/tree/master/slides&quot;&gt;Tech Talk&lt;/a&gt; (Vim talk hehe) about it.&lt;/p&gt;

&lt;h2 id=&quot;support-roulette&quot;&gt;Support Roulette&lt;/h2&gt;

&lt;p&gt;We needed a phone support 24x7 in case the website becomes unavailable and other possible urgent issues.&lt;/p&gt;

&lt;p&gt;Our idea was to nominate a support developer weekly.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;http://en.wikipedia.org/wiki/Minimum_viable_product&quot;&gt;MVP&lt;/a&gt; was to buy a cheap cellphone (its battery is infinite, on the other hand, it doesn’t even have the &lt;a href=&quot;http://en.wikipedia.org/wiki/Snake_(video_game\)&quot;&gt;snake game&lt;/a&gt;) and keep it with the  developer on duty. The idea was mainly to have a fixed phone number for our customer service call, in case of emergency.&lt;/p&gt;

&lt;h3 id=&quot;problems-with-the-cheap-cellphone&quot;&gt;Problems with the cheap cellphone&lt;/h3&gt;

&lt;p&gt;Everybody already has a cellphone. Carrying another is annoying. Easy, very easy to forget.&lt;/p&gt;

&lt;h3 id=&quot;solution&quot;&gt;Solution&lt;/h3&gt;

&lt;p&gt;We decided to use a Communication as a Service (CaaS) to register a fixed phone number in Brazil, which receives a call and redirects it to the developer on duty cellphone.&lt;/p&gt;

&lt;h3 id=&quot;voip&quot;&gt;Voip&lt;/h3&gt;

&lt;p&gt;The standard Voip services that we evaluated, even though they allow to register a fixed phone number in Brazil and redirect the calls, don’t allow to change the redirect target number via API. We would have to access their control panel weekly and configure the new redirect target number. As developers, we want to schedule the support agenda and have an app that will notify the new developer on duty and make the redirects.&lt;/p&gt;

&lt;h2 id=&quot;why-twilio&quot;&gt;Why Twilio?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;No Contracts&lt;/li&gt;
  &lt;li&gt;No Up Front&lt;/li&gt;
  &lt;li&gt;PAY AS YOU GO&lt;/li&gt;
  &lt;li&gt;Simple and well documented API&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;other-options&quot;&gt;Other options&lt;/h3&gt;

&lt;p&gt;There are &lt;a href=&quot;http://en.wikipedia.org/wiki/Twilio#Competitors&quot;&gt;other options&lt;/a&gt; of CaaS, however not all of them offer fixed phone number in Brazil or simple and well documented API. Based on what we evaluated, &lt;a href=&quot;http://www.plivo.com/&quot;&gt;Plivio&lt;/a&gt; seemed to be the best competitor.&lt;/p&gt;

&lt;h2 id=&quot;apps-which-talk&quot;&gt;Apps which talk&lt;/h2&gt;

&lt;p&gt;Given communication (voice and text) powers to an app, we can add many interesting features, such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Alerts &amp;amp; Notifications
    &lt;ul&gt;
      &lt;li&gt;Product stock updates&lt;/li&gt;
      &lt;li&gt;Notify when a website is unavailable. It is kind of “easy” to implement a homemade &lt;a href=&quot;https://www.pingdom.com/&quot;&gt;Pingdom&lt;/a&gt; with &lt;a href=&quot;http://mmonit.com/monit/&quot;&gt;Monit&lt;/a&gt; + Twilio&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Promotions
    &lt;ul&gt;
      &lt;li&gt;Send promotion codes&lt;/li&gt;
      &lt;li&gt;Rewards “Send a text to … to win a …”&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Reminders&lt;/li&gt;
  &lt;li&gt;Surveys&lt;/li&gt;
  &lt;li&gt;System administration
    &lt;ul&gt;
      &lt;li&gt;Retrieve system diagnostics&lt;/li&gt;
      &lt;li&gt;Execute remote commands&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Sales Automation
    &lt;ul&gt;
      &lt;li&gt;Order or consult products&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Bike Sampa
    &lt;ul&gt;
      &lt;li&gt;In São Paulo we have renting bike stations throughout the city. To release the bike we need to open the Bike Sampa app (iOS or Android), select the station and the bike then press “rent it”. It’s ok, but we need an iPhone or Android, the app and 3G. It would be much easier to send a text with the station and the bike instead of the whole app flow (iOS or Android, app and 3G).&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Identity Verification
    &lt;ul&gt;
      &lt;li&gt;Validate user by phone&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Support Roulette ;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have look at &lt;a href=&quot;http://www.twilio.com/gallery/customers&quot;&gt;Twilio Customer Success Stories&lt;/a&gt; for other use cases.&lt;/p&gt;

&lt;h3 id=&quot;homemade-pingdom-with-monit--twilio&quot;&gt;Homemade Pingdom with Monit + Twilio&lt;/h3&gt;

&lt;p&gt;Monit is an utility for managing and monitoring process, programs etc.&lt;/p&gt;

&lt;p&gt;Pingdom is a service for monitoring uptime, downtime and performance of websites.&lt;/p&gt;

&lt;p&gt;An usual usage of Pingdom is to use it only to monitor uptime, basically to notify someone if the website is down. We can achieve it in a homemade way with Monit + Twilio.&lt;/p&gt;

&lt;h4 id=&quot;monit-script&quot;&gt;Monit script&lt;/h4&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# /etc/monitrc

check host rexnuke with address 127.0.0.1
  if failed port 8080
   with timeout 15 seconds
   then
   exec &quot;ruby notify-site-is-down.rb&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;ruby-script&quot;&gt;Ruby script&lt;/h4&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# /usr/bin/notify-site-is-down.rb
require &quot;rubygems&quot;
require &quot;twilio-ruby&quot;

account_sid = &quot;ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&quot;
auth_token = &quot;yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy&quot;

@client = Twilio::REST::Client.new account_sid, auth_token

@client.account.sms.messages.create(
  from: &quot;+5511…&quot;,
  to: &quot;+5511…&quot;,
  body: &quot;Houston we have a problem, the website is down.&quot;
)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;costs&quot;&gt;Costs&lt;/h3&gt;

&lt;p&gt;In the case of Support Roulette, there are two costs: one for voice, when Twilio receives a call and redirects it and another for text, to notify the new developer on duty (“Congratulations, you are the new developer on duty :trollface:”).&lt;/p&gt;

&lt;h4 id=&quot;voice&quot;&gt;Voice&lt;/h4&gt;

&lt;p&gt;The voice cost is in two legs: to receive and redirect (make) a call.&lt;/p&gt;

&lt;p&gt;To receive a call: 1¢/minute&lt;/p&gt;

&lt;p&gt;To make a call: 33¢/minute&lt;/p&gt;

&lt;h4 id=&quot;text&quot;&gt;Text&lt;/h4&gt;

&lt;blockquote&gt;
  &lt;p&gt;SMS messaging from an American Twilio phone number to Brazil starts at 1.2¢ however this price will vary depending on the carrier you are sending SMS messages to.
Elaine Tsai, Twilio Support&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Brazilian fixed number phone lines don’t send text yet. To send a text to Brazil, an American number is required. The main inconvenience of doing it, is, in case it’s needed to send or reply a message, it must be to an American number.&lt;/p&gt;

&lt;p&gt;To send a text: starts at 1.2¢/message&lt;/p&gt;

&lt;h4 id=&quot;fixed-number&quot;&gt;Fixed number&lt;/h4&gt;

&lt;p&gt;Fixed brazilian number: 3$/month&lt;/p&gt;

&lt;p&gt;Fixed american number: 1$/month&lt;/p&gt;

&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;

&lt;p&gt;The initial steps are: &lt;a href=&quot;http://www.twilio.com/docs/api/twiml&quot;&gt;TwiMLTM: the Twilio Markup Language&lt;/a&gt; and
&lt;a href=&quot;http://www.twilio.com/docs/api/rest&quot;&gt;Twilio REST Web Service Interface&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Example of TwiMLTM:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Say&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;voice=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;woman&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Please leave a message after the tone.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I also recommend you to have a look at Support Roulete &lt;a href=&quot;https://github.com/phstc/support-roulette&quot;&gt;source code&lt;/a&gt;, it is a simple &lt;a href=&quot;https://github.com/sinatra/sinatra&quot;&gt;Sinatra&lt;/a&gt; application which uses the &lt;a href=&quot;https://github.com/twilio/twilio-ruby&quot;&gt;gem twilio-ruby&lt;/a&gt; and &lt;a href=&quot;https://github.com/phstc/support-roulette/blob/master/views/support_roulette_call.builder&quot;&gt;builders&lt;/a&gt; to generate TwiMLTM. It can be free hosted on Heroku.&lt;/p&gt;

&lt;h2 id=&quot;twimlets&quot;&gt;Twimlets&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.twilio.com/labs/twimlets&quot;&gt;Twimlets&lt;/a&gt; are tiny web applications that implement basic communication functionality, such as: &lt;a href=&quot;https://www.twilio.com/labs/twimlets/forward&quot;&gt;call forward&lt;/a&gt;, &lt;a href=&quot;https://www.twilio.com/labs/twimlets/voicemail&quot;&gt;voicemail&lt;/a&gt; etc.&lt;/p&gt;

&lt;p&gt;If you are in New York but you want a fixed number in Rio de Janeiro, you can easily create a Twilio account, configure a twimlet for a forward call and in less than 10 minutes everything will be working as expected. Awesome!&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Olha quem está falando</title>
   <link href="http://www.pablocantero.com//blog/2013/02/19/olha-quem-esta-falando/"/>
   <updated>2013-02-19T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2013/02/19/olha-quem-esta-falando</id>
   
   <content type="html">&lt;p&gt;&lt;img src=&quot;http://www.pablocantero.com/assets/images/posts/look-who-is-talking.jpg&quot; alt=&quot;Olha quem está falando&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;senta-que-lá-vem-a-história&quot;&gt;Senta que lá vem a história&lt;/h1&gt;

&lt;p&gt;Antes de começar a falar do &lt;a href=&quot;http://www.twilio.com/&quot;&gt;Twilio&lt;/a&gt;, vou falar a minha motivação para usá-lo. Foi para um aplicativo de suporte por telefone, o &lt;a href=&quot;https://github.com/phstc/support-roulette&quot;&gt;Support Roulette&lt;/a&gt;. No final, acabamos não usando exatamente o Support Roulette, mas foi uma ótima base.&lt;/p&gt;

&lt;p&gt;Devido à facilidade de implementar uma aplicação que pode responder uma chamada telefônica, gravar uma mensagem de voz, enviar e receber SMS, ou seja, uma aplicação que fala, me inspirei para escrever esse post e inclusive apresentar um &lt;a href=&quot;https://github.com/phstc/support-roulette/tree/master/slides&quot;&gt;Tech Talk&lt;/a&gt; (Vim talk rs) sobre o assunto.&lt;/p&gt;

&lt;h2 id=&quot;support-roulette&quot;&gt;Support Roulette&lt;/h2&gt;

&lt;p&gt;No trabalho, precisamos de um telefone de suporte 24x7 caso o site fique indisponível e para outras possíveis incidências urgentes.&lt;/p&gt;

&lt;p&gt;Nossa proposta inicial foi nomear um desenvolvedor por semana como suporte.&lt;/p&gt;

&lt;p&gt;O &lt;a href=&quot;http://en.wikipedia.org/wiki/Minimum_viable_product&quot;&gt;MVP&lt;/a&gt; foi comprar um celular baratinho (a bateria do danado é infinita, mas também, não tem nem o &lt;a href=&quot;http://en.wikipedia.org/wiki/Snake_(video_game\)&quot;&gt;jogo da cobrinha&lt;/a&gt;) e deixá-lo com o suporte da vez. A ideia do celular foi principalmente para o nosso atendimento poder ligar em um número fixo “Suporte Site”, não precisando buscar quem é o desenvolvedor de plantão, pegar o número do celular e ligar.&lt;/p&gt;

&lt;h3 id=&quot;problemas-com-o-celular-baratinho&quot;&gt;Problemas com o celular baratinho&lt;/h3&gt;

&lt;p&gt;Todo mundo já tem um celular. Carregar um segundo é um tanto quanto incômodo. Fácil, fácil de esquecer.&lt;/p&gt;

&lt;h3 id=&quot;solução&quot;&gt;Solução&lt;/h3&gt;

&lt;p&gt;Nossa solução foi usar algum serviço de “Communication as a Service” (CaaS).&lt;/p&gt;

&lt;p&gt;Usando um serviço de CaaS, podemos registrar um número fixo no Brasil, que, ao receber uma chamada telefônica, a redireciona para o número do celular do desenvolvedor de plantão.&lt;/p&gt;

&lt;h3 id=&quot;voip&quot;&gt;Voip&lt;/h3&gt;

&lt;p&gt;Os serviços convencionais de Voip que avaliamos, apesar de permitirem a criação de número fixo no Brasil e inclusive permitirem o redirecionamento de ligação, não permitem que alteremos o número de redirecionamento via API. Teríamos que acessar um painel de controle semanalmente para configurar o novo número de redirecionamento. Isso é inviável pois somos programadores, queremos agendar as escalas de suporte e ter um sistema que automaticamente avise o desenvolvedor de plantão e faça o redirecionamento.&lt;/p&gt;

&lt;h2 id=&quot;por-que-twilio&quot;&gt;Por que Twilio?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Não precisa de contrato&lt;/li&gt;
  &lt;li&gt;Não tem valor mínimo de serviço&lt;/li&gt;
  &lt;li&gt;Não tem taxa de adesão&lt;/li&gt;
  &lt;li&gt;É Pré pago, não tem mensalidade&lt;/li&gt;
  &lt;li&gt;Tem API simples e bem documentada&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;outras-opções&quot;&gt;Outras opções&lt;/h3&gt;

&lt;p&gt;Existem &lt;a href=&quot;http://en.wikipedia.org/wiki/Twilio#Competitors&quot;&gt;outras opções&lt;/a&gt; de CaaS, porém nem todas disponibilizam número no Brasil ou possuem API simples e bem documentada. Das que eu avaliei, o &lt;a href=&quot;http://www.plivo.com/&quot;&gt;Plivio&lt;/a&gt; pareceu o concorrente mais forte.&lt;/p&gt;

&lt;h2 id=&quot;apps-que-se-comunicam&quot;&gt;Apps que se comunicam&lt;/h2&gt;

&lt;p&gt;Concedendo poderes de comunicação (voz e SMS) para uma aplicação, podemos adicionar diversos recursos, como:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Alertas e notificações
    &lt;ul&gt;
      &lt;li&gt;Atualização de estoque de produtos&lt;/li&gt;
      &lt;li&gt;Notificação de site indisponível. Dá para “facilmente” implementar um &lt;a href=&quot;https://www.pingdom.com/&quot;&gt;Pingdom&lt;/a&gt; caseiro com o &lt;a href=&quot;http://mmonit.com/monit/&quot;&gt;Monit&lt;/a&gt; + Twilio&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Promoções
    &lt;ul&gt;
      &lt;li&gt;Códigos de promoções por SMS&lt;/li&gt;
      &lt;li&gt;Perguntas premiadas “Envie um SMS para … e concorra …”&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Lembretes
    &lt;ul&gt;
      &lt;li&gt;Notificações de agendas e eventos&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Questionários
    &lt;ul&gt;
      &lt;li&gt;Pesquisas de satisfação que podem ser respondidas tanto por SMS quanto por voz. Dá para facilmente implementar uma &lt;a href=&quot;http://pt.wikipedia.org/wiki/Unidade_de_resposta_aud%C3%ADvel&quot;&gt;URA&lt;/a&gt;, capturando as respostas pelos números digitados&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Administrações de sistemas
    &lt;ul&gt;
      &lt;li&gt;Obter diagnósticos do sistema&lt;/li&gt;
      &lt;li&gt;Executar comandos remotos&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Automação de vendas
    &lt;ul&gt;
      &lt;li&gt;Enviar SMS para comprar ou consultar estoque de produtos&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Sistema de identificação de usuário
    &lt;ul&gt;
      &lt;li&gt;Validar o usuário por telefone&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Bike Sampa
    &lt;ul&gt;
      &lt;li&gt;No &lt;a href=&quot;http://www.bikesampa.com.br/&quot;&gt;Bike Sampa&lt;/a&gt; seria bem interessante se ao invés de ter que instalar um aplicativo e precisar de 3G, pudéssemos cadastrar nosso celular, enviar um SMS com número do ponto e bicicleta e liberá-la, sem tráfego 3G, sem aplicativo… até o celular baratinho sem o jogo da cobrinha poderia ser usado&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Support Roulette ;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No site do Twilio tem “&lt;a href=&quot;http://www.twilio.com/gallery/customers&quot;&gt;Customer Success Stories&lt;/a&gt;”, com alguns casos de uso de empresas que utilizam o Twilio.&lt;/p&gt;

&lt;h3 id=&quot;pingdom-caseiro-com-monit--twilio&quot;&gt;Pingdom caseiro com Monit + Twilio&lt;/h3&gt;

&lt;p&gt;O Monit é um sistema para gerenciamento e monitoramento de processo, programas etc.&lt;/p&gt;

&lt;p&gt;Pingdom é um serviço para monitoramento de uptime, downtime e performance de websites.&lt;/p&gt;

&lt;p&gt;Um caso de uso do Pingdom padrão é usá-lo somente para notificar se o site ficou indisponível. De uma forma caseira é possível fazer isso com o Monit + Twilio.&lt;/p&gt;

&lt;h4 id=&quot;monit-script&quot;&gt;Monit script&lt;/h4&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# /etc/monitrc

check host rexnuke with address 127.0.0.1
  if failed port 8080
   with timeout 15 seconds
   then &quot;ruby notify-site-is-down.rb&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;ruby-script&quot;&gt;Ruby script&lt;/h4&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# /usr/bin/notify-site-is-down.rb
require &quot;rubygems&quot;
require &quot;twilio-ruby&quot;

account_sid = &quot;ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&quot;
auth_token = &quot;yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy&quot;

@client = Twilio::REST::Client.new account_sid, auth_token

@client.account.sms.messages.create(
  from: &quot;+5511…&quot;,
  to: &quot;+5511…&quot;,
  body: &quot;Houston we have a problem, the website is down.&quot;
)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;custos&quot;&gt;Custos&lt;/h3&gt;

&lt;p&gt;No caso do Support Roulette há dois custos: um de voz, quando o Twilio recebe uma ligação e redireciona para o desenvolvedor de plantão e outro de SMS, para notificá-lo que ele é o plantonista da vez.&lt;/p&gt;

&lt;h4 id=&quot;voz&quot;&gt;Voz&lt;/h4&gt;

&lt;p&gt;O custo para redirecionamento de voz é de duas pernas, é cobrado para receber e também para fazer (redirecionar) a ligação.&lt;/p&gt;

&lt;p&gt;Para receber uma ligação: USD 1¢/minuto&lt;/p&gt;

&lt;p&gt;Para fazer uma ligação: USD 33¢/minuto&lt;/p&gt;

&lt;h4 id=&quot;sms&quot;&gt;SMS&lt;/h4&gt;

&lt;blockquote&gt;
  &lt;p&gt;SMS messaging from an American Twilio phone number to Brazil starts at 1.2¢ however this price will vary depending on the carrier you are sending SMS messages to.
Elaine Tsai, Twilio Support&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Número fixo no Brasil ainda não envia SMS. Para enviar um SMS Tupiniquim é necessário ter um número americano. O principal inconveniente em usar um número americano para enviar SMS é no caso de resposta, ter que responder para um número americano.&lt;/p&gt;

&lt;p&gt;Para enviar um SMS: a partir de USD 1.2¢/mensagem&lt;/p&gt;

&lt;h4 id=&quot;número-fixo&quot;&gt;Número fixo&lt;/h4&gt;

&lt;p&gt;Número fixo brasileiro: USD 3$/mês&lt;/p&gt;

&lt;p&gt;Número fixo americano: USD 1$/mês&lt;/p&gt;

&lt;h2 id=&quot;começando-com-o-twilio&quot;&gt;Começando com o Twilio&lt;/h2&gt;

&lt;p&gt;Os passos iniciais do Twilio são: &lt;a href=&quot;http://www.twilio.com/docs/api/twiml&quot;&gt;TwiMLTM: the Twilio Markup Language&lt;/a&gt; e
&lt;a href=&quot;http://www.twilio.com/docs/api/rest&quot;&gt;Twilio REST Web Service Interface&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Exemplo de TwiMLTM:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Say&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;voice=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;woman&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Please leave a message after the tone.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Recomendo também uma olhada no &lt;a href=&quot;https://github.com/phstc/support-roulette&quot;&gt;Support Roulete&lt;/a&gt;, pois ele é uma aplicação &lt;a href=&quot;https://github.com/sinatra/sinatra&quot;&gt;Sinatra&lt;/a&gt; super simples, que usa a &lt;a href=&quot;https://github.com/twilio/twilio-ruby&quot;&gt;gem twilio-ruby&lt;/a&gt; e &lt;a href=&quot;https://github.com/phstc/support-roulette/blob/master/views/support_roulette_call.builder&quot;&gt;builders&lt;/a&gt; para gerar TwiMLTM, podendo ser hospedada gratuitamente no Heroku.&lt;/p&gt;

&lt;h2 id=&quot;twimlets&quot;&gt;Twimlets&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.twilio.com/labs/twimlets&quot;&gt;Twimlets&lt;/a&gt; são aplicações prontas que implementam funcionalidades básicas, como &lt;a href=&quot;https://www.twilio.com/labs/twimlets/forward&quot;&gt;redirecionamento de ligação&lt;/a&gt;, &lt;a href=&quot;https://www.twilio.com/labs/twimlets/voicemail&quot;&gt;caixa postal&lt;/a&gt; etc.&lt;/p&gt;

&lt;p&gt;Se você quiser hoje mesmo ter um número fixo em São Paulo que redirecione para um celular do Acre, basta  criar uma conta no Twilio, configurar um twimlet de redirecionemto e, em menos de 10 minutos, estará tudo funcionado.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Use AspirinJS to improve your JavaScript code</title>
   <link href="http://www.pablocantero.com//blog/2012/11/16/use-aspirinjs-to-improve-your-javascript-code/"/>
   <updated>2012-11-16T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2012/11/16/use-aspirinjs-to-improve-your-javascript-code</id>
   
   <content type="html">&lt;p&gt;Have you a JavaScript causing you a headache? Will you write a JavaScript that you don’t want to cause you a headache?&lt;/p&gt;

&lt;p&gt;AspirinJS is a collection of refactories to convert monolithic JavaScript code into maintainable, modular and testable JavaScript without any framework.&lt;/p&gt;

&lt;p&gt;AspirinJS isn’t a JavaScript Framework! It is only a few practices to be used as a reference to improve JavaScript code smoothly.&lt;/p&gt;

&lt;h2 id=&quot;why&quot;&gt;Why?&lt;/h2&gt;

&lt;p&gt;Jeff Atwood wrote an interesting post arguing about how difficult it is to maintain a legacy code &lt;a href=&quot;http://www.codinghorror.com/blog/2006/09/when-understanding-means-rewriting.html&quot;&gt;When Understanding means Rewriting&lt;/a&gt;. One of the most interesting points in this post is this graphic:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://codinghorror.typepad.com/.a/6a0120a85dcdae970b0120a86d7477970b-pi&quot; alt=&quot;When Understanding means Rewriting&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We spend more time understanding and editing code than writing new code. Writing a procedural Javascript with lot of callbacks is easy and cool when it is new, but when we need to maintain that code, it becomes too hard to understand and edit even if the code was written by ourselves.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Any fool can write code that a computer can understand. Good programmers write code that humans can understand. [Martin Fowler, Refactoring: Improving the Design of Existing Code]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;motivation&quot;&gt;Motivation&lt;/h2&gt;

&lt;p&gt;I do like &lt;a href=&quot;http://backbonejs.org&quot;&gt;Backbone&lt;/a&gt; and encourage its use and other Javascript frameworks as well, but sometimes it is too hard to add them to legacy code, e.g. projects which the resource urls don’t follow any convention or no well defined resources etc.&lt;/p&gt;

&lt;p&gt;JavaScript is a beautiful and sexy programming language, therefore we can write good code with pure JavaScript.&lt;/p&gt;

&lt;h2 id=&quot;disclaimer&quot;&gt;Disclaimer&lt;/h2&gt;

&lt;p&gt;I’m using a little jQuery in AspirinJS - little means $.ajax, $.extend, event handler (trigger and on) and DOM manipulation. I used jQuery only to avoid boilerplate code - it isn’t directly related to the refactory.&lt;/p&gt;

&lt;h2 id=&quot;lets-go-show-me-the-code&quot;&gt;Let’s go, show me the code.&lt;/h2&gt;

&lt;p&gt;The full version of AspirinJS is available on GitHub &lt;a href=&quot;https://github.com/phstc/aspirinjs/&quot;&gt;github.com/phstc/aspirinjs/&lt;/a&gt;. Here I will show only the before and after, on GitHub you can have look in each version and its own achievement.&lt;/p&gt;

&lt;h3 id=&quot;before&quot;&gt;Before&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;index.html&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Twitter search&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;#search-query&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;#search-form&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;#search-results&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Loading...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;searchQuery&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;#search-query&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ajax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;http://search.twitter.com/search.json?q=&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;searchQuery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;dataType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;jsonp&quot;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;resultsContainer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;ul&amp;gt;&amp;lt;/ul&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
              &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
              &lt;span class=&quot;nx&quot;&gt;resultsContainer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;li&amp;gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;/li&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;#search-results&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;resultsContainer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Twitter search&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;search-form&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;search-query&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;placeholder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Search query&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;ni&quot;&gt;&amp;amp;nbsp;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Search&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Search results&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;search-results&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;after&quot;&gt;After&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;index.html&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Twitter search&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;assets/javascripts/app/application.js&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Twitter search&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;search-form&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;search-query&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;placeholder=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Search query&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;ni&quot;&gt;&amp;amp;nbsp;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Search&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Search results&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;search-results&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;assets/javascripts/app/models/data_model.coffee&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class app.models.DataModel

  getData:  (url, data={}) -&amp;gt;
    $.ajax url, data
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;assets/javascripts/app/models/twitter_model.coffee&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class app.models.TwitterModel extends app.models.DataModel

  search: (query) -&amp;gt;
    url = &quot;http://search.twitter.com/search.json?q=#{query}&quot;
    this.getData(url, dataType: &quot;jsonp&quot;).
      success (data) -&amp;gt;
        $(window).trigger &quot;TwitterModel::search&quot;, data
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;assets/javascripts/app/application.coffee&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;window.app =
  models: {}
  views: {}

$ -&amp;gt;
  twitterView = new app.views.TwitterView
  twitterView.render()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;assets/javascripts/app/views/twitter_view.coffee&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class app.views.TwitterView

  twitterModel = new app.models.TwitterModel

  render: -&amp;gt;
    $(&quot;#search-query&quot;).focus()
    $(&quot;#search-form&quot;).submit (event) =&amp;gt;
      event.preventDefault()
      @search()
    $(window).on(&quot;TwitterModel::search&quot;, @printSearchResults)

  search: -&amp;gt;
    $(&quot;#search-results&quot;).html &quot;Loading...&quot;
    searchQuery = $(&quot;#search-query&quot;).val()

    twitterModel.search searchQuery

  printSearchResults: (event, data) -&amp;gt;
    resultsContainer = $ &quot;&amp;lt;ul&amp;gt;&amp;lt;/ul&amp;gt;&quot;
    for result in data.results
      resultsContainer.append &quot;&amp;lt;li&amp;gt;#{result.text}&amp;lt;/li&amp;gt;&quot;
    $(&quot;#search-results&quot;).html resultsContainer

$ -&amp;gt;
  twitterView = new app.views.TwitterView
  twitterView.render()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;specs/javascripts/app/models/data_model_spec.coffee&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;require &quot;//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js&quot;
require &quot;/application.js&quot;
require &quot;/models/data_model.js&quot;

describe &quot;Data model&quot;, -&amp;gt;

  it &quot;gets data from an url&quot;, -&amp;gt;
    data_model = new app.models.DataModel
    spyOn $, &quot;ajax&quot;
    data_model.getData &quot;localhost&quot;
    expect($.ajax).toHaveBeenCalledWith &quot;localhost&quot;, {}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;specs/javascripts/app/models/twitter_model_spec.coffee&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;require &quot;//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js&quot;
require &quot;/application.js&quot;

describe &quot;Twitter Model&quot;, -&amp;gt;

  it &quot;searches tweets&quot;, -&amp;gt;
    twitter = new app.models.TwitterModel
    spyOn($, &quot;ajax&quot;).andReturn success: -&amp;gt;
    twitter.search &quot;vapormvc&quot;
    expect($.ajax).toHaveBeenCalledWith &quot;http://search.twitter.com/search.json?q=vapormvc&quot;, dataType:  &quot;jsonp&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;too-much-code&quot;&gt;Too much code?&lt;/h2&gt;

&lt;p&gt;Even if it seemed to much code for a basic example of “Tweet search”, believe me, all code start simple and easy to understand, but as they are used, they grow. Modular code following &lt;a href=&quot;http://en.wikipedia.org/wiki/Single_responsibility_principle&quot;&gt;Single Responsibility Principle&lt;/a&gt;, will avoid a huge headache in the future.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We spend more time understanding and editing code than writing new code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;http://www.codinghorror.com/blog/2006/09/when-understanding-means-rewriting.html&quot;&gt;When Understanding means Rewriting&lt;/a&gt;&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Use Auto Scaling, avoid Cloud Smells</title>
   <link href="http://www.pablocantero.com//blog/2012/09/07/use-auto-scaling-avoid-cloud-smells/"/>
   <updated>2012-09-07T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2012/09/07/use-auto-scaling-avoid-cloud-smells</id>
   
   <content type="html">&lt;p&gt;A Cloud hosting has two main characteristics:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Pay-as-you-go&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Scale Resources up or down based on demand (Elastic Architecture)&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the features that differentiate it from a traditional hosting.&lt;/p&gt;

&lt;p&gt;If you’re in the Cloud, you should also follow these characteristics.&lt;/p&gt;

&lt;p&gt;To be in the Cloud:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;You must pay only for the resources needed for your application without waste&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Your application must be able to scale up and down on demand&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can host an application in a Cloud, but it doesn’t mean that your application is Cloud capable (Cloudable). In the same way that your &lt;a href=&quot;http://en.wikipedia.org/wiki/Code_smell&quot;&gt;Code can Smell&lt;/a&gt;, your Cloud can Smell too.&lt;/p&gt;

&lt;h2 id=&quot;pay-as-you-go&quot;&gt;Pay as you go&lt;/h2&gt;

&lt;p&gt;Cloud hosting typically charges by minute, hour, storage (MB, GB etc) or bandwidth. The bandwidth is usually the villain. You might think &lt;em&gt;“The instance is cheap, the storage is cheap, it is really cool. I’m totally in love with it”&lt;/em&gt;, but when your application starts to grow, you will probably notice that your CDN (&lt;a href=&quot;http://aws.amazon.com/cloudfront&quot;&gt;CloudFront&lt;/a&gt;) is not as cheap as it was before etc.&lt;/p&gt;

&lt;p&gt;Traditional hosting typically charges by longer periods (monthly, semiannually, annually etc).&lt;/p&gt;

&lt;h2 id=&quot;scale-resources-up-or-down-based-on-demand-elastic-architecture&quot;&gt;Scale Resources up or down based on demand (Elastic Architecture)&lt;/h2&gt;

&lt;p&gt;Elastic Architecture allows you to add or remove resources on demand. If your website needs more resources during the day you can scale up, but if at night it doesn’t need these resources you can easily scale down.&lt;/p&gt;

&lt;p&gt;Basically, if you have more than one instance, you should use Auto Scaling. Normal websites don’t have the same traffic at night as during the day. If all of your instances are running 24x7, it probably means that your Cloud Smells.&lt;/p&gt;

&lt;p&gt;Traditional hosting is more straight. They usually require contracts to provide resources. You can’t easily add or remove them.&lt;/p&gt;

&lt;h2 id=&quot;auto-scaling&quot;&gt;Auto Scaling&lt;/h2&gt;

&lt;p&gt;Amazon AWS offers &lt;a href=&quot;http://aws.amazon.com/autoscaling&quot;&gt;Auto Scaling&lt;/a&gt;, which allows you to automatically scale up or down your application according your needs.&lt;/p&gt;

&lt;p&gt;Steps to use AWS Auto Scaling:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Create a launch configuration&lt;/em&gt;. The launch configuration are parameters to launch instances, such as image (AMI), instance type, security group, key pair and user data.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Create an auto scaling group&lt;/em&gt;. The auto scaling group defines the auto scaling conditions, minimum and maximum number of instances, available zones, default cool down and load balancer (ELB).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Create a scaling policy&lt;/em&gt;. The scaling policy defines how the auto scaling should scale up or down, if it is by size or percent and the cool down.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Create an alarm metric&lt;/em&gt;. The alarm metric is the metric used to decide when to increase or decrease the instances. Usually by response time. The metrics are provided by &lt;a href=&quot;http://aws.amazon.com/cloudwatch&quot;&gt;CloudWatch&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Create an alarm request&lt;/em&gt;. It is the last step to setup the Auto Scaling. The alarm request defines how to measure the metrics defined by the alarm metric, for example the time and period to evaluate the metric.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can setup Auto Scaling using &lt;a href=&quot;http://aws.amazon.com/developertools&quot;&gt;Command Line Tools, Java, Ruby etc&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;auto-deployable-applications&quot;&gt;Auto deployable applications&lt;/h3&gt;

&lt;p&gt;If your application never changes or doesn’t change frequently, you can create an image with your application, but in general it isn’t true, applications usually change frequently.&lt;/p&gt;

&lt;p&gt;Although it isn’t obligatory to be auto deployable, I strongly recommend to turn your application able to be deployed by scripts without manual intervention.&lt;/p&gt;

&lt;p&gt;For auto deployable applications in the Auto Scaling launch configuration you can define an user data (it is a script) to get a fresh version of your application. If your application is easy to deploy, maybe just a git clone is enough, if your application has several steps to be deployed, you can create a bash script or download a “RPM” etc.&lt;/p&gt;

&lt;p&gt;You can also setup your infrastructure in the user data by using &lt;a href=&quot;http://puppetlabs.com&quot;&gt;Puppet&lt;/a&gt;, &lt;a href=&quot;http://www.opscode.com/chef&quot;&gt;Chef&lt;/a&gt; or &lt;a href=&quot;http://aws.amazon.com/cloudformation&quot;&gt;Amazon Cloud Formation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also mix the infrastructure and deployment automation, but it usually takes longer to create new instances.&lt;/p&gt;

&lt;h2 id=&quot;some-advices&quot;&gt;Some advices&lt;/h2&gt;

&lt;h3 id=&quot;dont-name-your-instances&quot;&gt;Don’t name your instances&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;You should change your instances as you change your underwear. &lt;a href=&quot;https://twitter.com/lucastex&quot;&gt;Lucas Teixeira&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I know that is cool to name your instances with hipster names, but avoid it. It only leads you to create non-automated systems and applications. Don’t be attached to your instances, it is a Cloud Smell.&lt;/p&gt;

&lt;p&gt;For each deploy of your application, you should create a new and  fresh instance.&lt;/p&gt;

&lt;h3 id=&quot;logging&quot;&gt;Logging&lt;/h3&gt;

&lt;p&gt;An important advice is to use &lt;a href=&quot;http://en.wikipedia.org/wiki/Rsyslog&quot;&gt;Rsyslog&lt;/a&gt; or other similar tool. If you have more than one instance running, you will need a centralized logging system.&lt;/p&gt;

&lt;h3 id=&quot;scripting&quot;&gt;Scripting&lt;/h3&gt;

&lt;p&gt;You can write bash scripts to manage your instances, auto scaling etc using &lt;a href=&quot;http://aws.amazon.com/developertools&quot;&gt;Command Line tools&lt;/a&gt;. They are really easy to use, but I recommend to create them with languages that you are strongly familiar with it. In the beginning bash scripts seem to be easy, but as they grow, and they will, they start to be hard to maintain.&lt;/p&gt;

&lt;p&gt;I developed with Lucas Teixeira &lt;a href=&quot;https://github.com/lucastex/aws-tools&quot;&gt;“scripts”&lt;/a&gt; in Java to create instances and auto scaling automation. They are a plain implementation, not an abstraction. I recommend them to learn “Auto Scaling guided by Code”. Why in Java? I don’t have any idea, maybe nostalgic feelings. Lucas started it in Java, then I followed him.&lt;/p&gt;

</content>

 </entry>
 
 <entry>
   <title>Website monitoring. You will become addicted</title>
   <link href="http://www.pablocantero.com//blog/2012/08/11/website-monitoring-you-will-become-addicted/"/>
   <updated>2012-08-11T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2012/08/11/website-monitoring-you-will-become-addicted</id>
   
   <content type="html">&lt;p&gt;This year (2012) I attended to a &lt;a href=&quot;http://www.thoughtworks.com/articles/technology-radar-march-2012&quot;&gt;ThoughtWorks Radar&lt;/a&gt; presentation. The guys from TW talked about radar topics, including Health Check Pages technique, which is really interesting and motived my self to create this post.&lt;/p&gt;

&lt;h2 id=&quot;health-check-pages&quot;&gt;Health Check Pages&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;We have found adding simple health check pages to applications is incredibly useful. This allows people to quickly understand the health of an individual node. We often extend them to add metrics like the number of orders placed, error rates, or similar information. Using simple embedded web servers, even non-web based services can easily expose internal information over HTTP. By using microformats, these web pages can easily be scraped by other monitoring tools to become part of holistic monitoring.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since then, I started to create dashboards in my websites. They aren’t admins - I also create admins - but these dashboards can be apart of them. They are simple pages listing last orders, messages sent, counters etc.&lt;/p&gt;

&lt;p&gt;These dashboards are addictive, I usually reload my browser many times per day to see the changes. Although the information is also available in the admin, it’s much more attractive a page only showing the most important data.&lt;/p&gt;

&lt;h2 id=&quot;real-user-monitoring-rum&quot;&gt;Real User Monitoring (RUM)&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Real_user_monitoring&quot;&gt;RUM&lt;/a&gt; is a technique to record user interactions, in our case, in a website.&lt;/p&gt;

&lt;p&gt;It is similar to the concept of Health Check Pages mentioned before, maybe the same, but RUM is the buzzword. :)&lt;/p&gt;

&lt;p&gt;Are you a hipster? You should use RUM.&lt;/p&gt;

&lt;p&gt;I’m working for the biggest crafts marketplace in Latin America, and this means that we have high traffic. It is exciting to see graphics from our website interactions, as we have a huge number of data to feed our graphics.&lt;/p&gt;

&lt;p&gt;We are using &lt;a href=&quot;https://github.com/etsy/statsd/&quot;&gt;StatsD&lt;/a&gt; and &lt;a href=&quot;http://graphite.wikidot.com/&quot;&gt;Graphite&lt;/a&gt; to monitor our website interactions. We monitor our logins (from Facebook, direct etc), searches, products visualization, orders completion, exceptions thrown etc, we monitor everything interesting for us.&lt;/p&gt;

&lt;p&gt;These graphics are very useful. They help us:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;check if there is something broken. Once we broke our login with Facebook after a deploy, we quickly realized the ‘login with Facebook’ graphic falling and fixed it really fast. These graphics make us feel more confident to “continuous” deploy.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;check the traffic curves during the day.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;check if our last e-mail campaign increased the traffic.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…&lt;/p&gt;

&lt;p&gt;I strongly recommend this article &lt;a href=&quot;http://codeascraft.etsy.com/2011/02/15/measure-anything-measure-everything/&quot;&gt;Measure Anything, Measure Everything&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;make-it-nicer&quot;&gt;Make it nicer&lt;/h2&gt;

&lt;p&gt;It is nice to have an URL where everybody can access these graphics. But…. it is nicer to have a big television showing these graphics in the middle of the office.&lt;/p&gt;

&lt;p&gt;We did it, we have a computer, an Apple TV and a television. The computer has Chrome tabs opened for Graphite, Google Analytics Realtime and Hudson (green or red). We also use a Google Chrome extension to automatically switch the tabs in a specific interval.&lt;/p&gt;

&lt;p&gt;The Apple TV can be expensive, but it is really useful. We can switch from the computer to an iPad streaming showing the Olympics Games or a presentation etc.&lt;/p&gt;

&lt;h2 id=&quot;reinventing-the-wheel&quot;&gt;Reinventing the wheel?&lt;/h2&gt;

&lt;p&gt;Maybe you are thinking… “Why not Google Analytics (Standard and Realtime), New Relic…”, we do use them, but they achieve different purposes… we are talking about simple pages to list our specific metrics in real time.&lt;/p&gt;

&lt;h2 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h2&gt;

&lt;p&gt;If it moves, you can track it. Be creative and you will become addicted.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Using SSH Tunneling on AWS EC2 to receive API callbacks</title>
   <link href="http://www.pablocantero.com//blog/2012/07/27/using-ssh-tunneling-on-aws-ec2-to-receive-api-callbacks/"/>
   <updated>2012-07-27T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2012/07/27/using-ssh-tunneling-on-aws-ec2-to-receive-api-callbacks</id>
   
   <content type="html">&lt;p&gt;Some APIs use callbacks in their communication process, e.g. payment gateways. They usually use callbacks to notify when a payment status was changed (STARTED, ANALYSING, AUTHORIZED, COMPLETED etc).&lt;/p&gt;

&lt;p&gt;But in the development environment our application is under “localhost”, meaning the API providers couldn’t reach our application.&lt;/p&gt;

&lt;p&gt;A good and easy way to enable external callbacks to reach our internal application is using &lt;a href=&quot;http://en.wikipedia.org/wiki/Tunneling_protocol&quot;&gt;SSH Tunneling&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;enabling-ssh-tunneling&quot;&gt;Enabling SSH Tunneling&lt;/h2&gt;

&lt;p&gt;The steps bellow assume that you are familiar with &lt;a href=&quot;http://aws.amazon.com/ec2/&quot;&gt;AWS EC2&lt;/a&gt; and have an EC2 Linux based instance running.&lt;/p&gt;

&lt;p&gt;###Security Groups&lt;/p&gt;

&lt;p&gt;In AWS Web Console go to Security Group and enable port 8100. This port will be used by callbacks.&lt;/p&gt;

&lt;p&gt;In your API Provider Administration or in the API Request message (it depends on the API Provider) - configure &lt;code class=&quot;highlighter-rouge&quot;&gt;ec2-100-10-100-10.compute-1.amazonaws.com:8100/AWESOME-CALLBACK-PATH&lt;/code&gt; as your callback url.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ec2-100-10-100-10.compute-1.amazonaws.com&lt;/code&gt; is your EC2 instance Public DNS.&lt;/p&gt;

&lt;p&gt;###In your EC2 Instance&lt;/p&gt;

&lt;p&gt;Open &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/ssh/sshd_config&lt;/code&gt; uncomment the line GatewayPorts or create one if it doesn’t exist and set it to “yes”&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# /etc/ssh/sshd_config
GatewayPorts yes
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;###In your machine “localhost”&lt;/p&gt;

&lt;p&gt;Execute&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ssh -N -R 8100:localhost:3000 -i YOUR_AWS.pem YOUR_USER@ec2-100-10-100-10.compute-1.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The port 3000 is the local port of your application.&lt;/p&gt;

&lt;p&gt;Don’t forget to change YOUR_AWS.pem and YOUR_USER to your specific parameters.&lt;/p&gt;

&lt;p&gt;If everything worked as expected, when you open http://ec2-100-10-100-10.compute-1.amazonaws.com:8100, your local application will be invoked.&lt;/p&gt;

</content>

 </entry>
 
 <entry>
   <title>Refactoring Legacy Code - Reducing coupling</title>
   <link href="http://www.pablocantero.com//blog/2012/07/19/refactoring-legacy-code-reducing-coupling/"/>
   <updated>2012-07-19T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2012/07/19/refactoring-legacy-code---reducing-coupling</id>
   
   <content type="html">&lt;p&gt;I’m working in a Java Legacy Code and facing high coupling and low cohesion, kind of trivial problems in this scenario.&lt;/p&gt;

&lt;p&gt;I needed to refactor the code, not only to improve the design, but in this case was firstly to make it unit testable. The design improvements started as a “side effect”.&lt;/p&gt;

&lt;p&gt;This post concerns about the baby steps that I used to refactor the code.&lt;/p&gt;

&lt;h2 id=&quot;legacy-code&quot;&gt;Legacy code&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public class OrderDAO {
	public boolean delete(int id) {
	// ...
	}
}

public class OrderService {
	public boolean delete(int id) {
		return new OrderDAO().delete(id);
	}
}

public class OrderServiceTest {
	@Test
	public void shouldDeleteAnOrder() {
		OrderService subject = new OrderService();
		assertTrue(subject.delete(1));
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;OrderService is high coupled with OrderDAO&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;OrderDAO will be created all times that delete is called&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is impossible to unit test it. I can’t mock the OrderDAO because it is initiated within the method.&lt;/p&gt;

&lt;p&gt;The test is an integration test, not unit test. To run it, I need to provide a datasource… and I don’t want to.&lt;/p&gt;

&lt;h2 id=&quot;first-refactory&quot;&gt;First refactory&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public class OrderService {
	public boolean delete(int id) {
		return getOrderDAO().delete(id);
	}
	protected OrderDAO getOrderDAO() {
		return new OrderDAO();
	}
}

public class OrderServiceTest {
	@Test
	public void shouldDeleteAnOrder() {
		final OrderDAO orderDAO = mock(OrderDAO.class);
		doReturn(true).when(orderDAO).delete(anyInt());
		/*
		 * Anonymous class rocks
		*/
		OrderService subject = new OrderService(){
			protected OrderDAO getOrderDAO(){
				return orderDAO;
			}
		};
		assertTrue(subject.delete(1));
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Great! It isn’t the state of the art, but at least I can mock the OrderDAO and there isn’t a side effect. I don’t need to change the classes that use OrderService. Its behaviour is still the same.&lt;/p&gt;

&lt;p&gt;Mockito has a convenient method called &lt;a href=&quot;http://mockito.googlecode.com/svn/branches/1.6/javadoc/org/mockito/Mockito.html#spy(T)&quot;&gt;spy&lt;/a&gt;, that can be used instead of the Anonymous class, but I don’t feel confortable with the idea of using a test subject from Mockito/CGLIB.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;OrderDAO orderDAO = mock(OrderDAO.class);
doReturn(true).when(orderDAO).delete(anyInt());
OrderService subject = spy(OrderService.class);
doReturn(orderDAO).when(subject).getOrderDAO();
assertTrue(subject.delete(1));
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;second-refactory&quot;&gt;Second refactory&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public class OrderService {
	private OrderDAO orderDAO;

	public OrderService() {
		orderDAO = new OrderDAO();
	}
	public OrderService(OrderDAO orderDAO) {
		this.orderDAO = orderDAO;
	}
	public boolean delete(int id) {
		return orderDAO.delete(id);
	}
}

public class OrderServiceTest {
	@Test
	public void shouldDeleteAnOrder() {
		final OrderDAO orderDAO = mock(OrderDAO.class);
		doReturn(true).when(orderDAO).delete(anyInt());
		OrderService subject = new OrderService(orderDAO);
		assertTrue(subject.delete(1));
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A little bit better, still having a strong dependency of OrderDAO, but at least it will be created once.&lt;/p&gt;

&lt;h2 id=&quot;third-refactory&quot;&gt;Third refactory&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public interface Persistable {
	public boolean delete(int id);
}

public class OrderDAO implements Persistable {
	public boolean delete(int id) {
        // ...
	}
}

public class OrderService {
	private Persistable orderDAO;

	public OrderService() {
		orderDAO = new OrderDAO();
	}
	public OrderService(Persistable orderDAO) {
		this.orderDAO = orderDAO;
	}
	public boolean delete(int id) {
		return orderDAO.delete(id);
	}
}

public class OrderServiceTest {
	@Test
	public void shouldDeleteAnOrder() {
		final Persistable orderDAO = mock(Persistable.class);
		doReturn(true).when(orderDAO).delete(1);
		OrderService subject = new OrderService(orderDAO);
		assertTrue(subject.delete(1));
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The strong dependency of OrderDAO is optional. It’s just for backward compatibility.&lt;/p&gt;

&lt;h2 id=&quot;next-refactory&quot;&gt;Next refactory?&lt;/h2&gt;

&lt;p&gt;I stopped here, not because I think that it is over, but I don’t want to be too pragramatic. I improved it a little and the most important is that I mantained the backward compatibility.&lt;/p&gt;

&lt;p&gt;My probable next step could be to add &lt;a href=&quot;http://jcp.org/en/jsr/detail?id=299&quot;&gt;Contexts and Dependency Injection&lt;/a&gt; CDI. The motivation to use the Dependency Injection is to encapsulate the object creation, as a class that uses an object doesn’t need to know how to create it. Other point is that DI frameworks have contexts, such as Request, Session and Application, and it reduces unnecessary creations of objects.&lt;/p&gt;

&lt;p&gt;I wrote a gist with these steps &lt;a href=&quot;https://gist.github.com/3108659&quot;&gt;gist.github.com/3108659&lt;/a&gt;.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Mocha and subdirectories</title>
   <link href="http://www.pablocantero.com//blog/2012/06/22/mocha-and-subdirectories/"/>
   <updated>2012-06-22T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2012/06/22/mocha-and-subdirectories</id>
   
   <content type="html">&lt;p&gt;So, did you run &lt;code class=&quot;highlighter-rouge&quot;&gt;mocha&lt;/code&gt; and your tests under subdirectories of &lt;code class=&quot;highlighter-rouge&quot;&gt;test/&lt;/code&gt; weren’t executed?&lt;/p&gt;

&lt;p&gt;Don’t worry, &lt;a href=&quot;https://github.com/visionmedia/mocha/issues/106&quot;&gt;you aren’t alone&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s an expected behaviour of mocha.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://visionmedia.github.com/mocha/#best-practices&quot;&gt;visionmedia.github.com/mocha/#best-practices&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Best practices
test/*
By default mocha(1) will use the pattern ./test/*.js, so it’s usually a good place to put your tests.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;a href=&quot;http://visionmedia.github.com/mocha/#best-practices&quot;&gt;recommended approach&lt;/a&gt; is to use a Makefile (create one if you don’t have it) and add your own test task.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Makefile
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;TESTS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;shell&lt;/span&gt; find &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*test.js&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nl&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;./node_modules/.bin/mocha&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$(TESTS)&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;--reporter&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;list&lt;/span&gt;

&lt;span class=&quot;nl&quot;&gt;.PHONY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then &lt;code class=&quot;highlighter-rouge&quot;&gt;make test&lt;/code&gt;.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Use .gitignore templates!</title>
   <link href="http://www.pablocantero.com//blog/2012/05/02/use-gitignore-templates/"/>
   <updated>2012-05-02T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2012/05/02/use-gitignore-templates</id>
   
   <content type="html">&lt;p&gt;Com o tempo acabamos criando alguns arquivos &lt;a href=&quot;http://help.github.com/ignore-files&quot;&gt;.gitignore&lt;/a&gt; de acordo com o environment que trabalhamos. Geralmente quando eu inicio um novo projeto eu copio o .gitignore de um projeto similar que eu já trabalhei.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cp ~/workspace/old_rails_project/.gitignore ~/workspace/new_rails_project
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Funciona bem, mas recentemente encontrei o projeto &lt;a href=&quot;https://github.com/github/gitignore&quot;&gt;.gitignore templates&lt;/a&gt; que ajuda nesse processo, podendo até evitar que arquivos indesejados passem despercebidos nos nossos arquivos .gitignore.&lt;/p&gt;

&lt;p&gt;##.gitignore templates&lt;/p&gt;

&lt;p&gt;O projeto contém vários templates de arquivos .gitignore por tipo de projeto/framework e globais por sistema operacional e editores.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/github/gitignore/blob/master/Java.gitignore&quot;&gt;Java.gitignore&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;*.class

# Package Files #
*.jar
*.war
*.ear
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Os arquivos .gitignore globais ficam separados na pasta &lt;a href=&quot;https://github.com/github/gitignore/tree/master/Global&quot;&gt;Global&lt;/a&gt; do projeto.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/github/gitignore/blob/master/Global/OSX.gitignore&quot;&gt;OSX.gitignore&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.DS_Store

# Thumbnails
._*

# Files that might appear on external disk
.Spotlight-V100
.Trashes
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;###.gitignore global&lt;/p&gt;

&lt;p&gt;Uma boa prática se você quiser ignorar arquivos do seu environment, isso quer dizer do seu sistema operacional, editor etc, que é específico para você e não para o projeto é colocá-los no .gitignore global do seu Git.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ touch ~/.gitignore_global
Adicione suas configurações, arquivos temporários do vim, do OSX etc.
$ git config --global core.excludesfile ~/.gitignore_global
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;##gemignore&lt;/p&gt;

&lt;p&gt;Para facilitar ainda mais a vida, criaram a gem &lt;a href=&quot;https://github.com/x3ro/gemignore&quot;&gt;gemignore&lt;/a&gt;, que adiciona snippets do .gitignore templates no arquivo .gitignore do diretório atual.&lt;/p&gt;

&lt;p&gt;Instalando a gem.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ gem install gemignore
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Adicionando um template.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ gemignore add Rails
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Adicionado um template Global.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ gemignore add Global/OSX
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;O comando &lt;code class=&quot;highlighter-rouge&quot;&gt;add&lt;/code&gt; dá um append no arquivo &lt;code class=&quot;highlighter-rouge&quot;&gt;.gitignore&lt;/code&gt; do diretório atual. Caso não exista exibirá uma mensagem informando que não encontrou o arquivo.&lt;/p&gt;

</content>

 </entry>
 
 <entry>
   <title>Use Capybara!</title>
   <link href="http://www.pablocantero.com//blog/2012/03/14/use-capybara/"/>
   <updated>2012-03-14T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2012/03/14/use-capybara</id>
   
   <content type="html">&lt;p&gt;Aplicações não podem falhar, um usuário não pode clicar em algo e receber um erro. Principalmente em aplicações web abertas. Imaginem uma busca no Google retornando “500 Internal Server Error”.&lt;/p&gt;

&lt;p&gt;O Twitter para evitar possíveis erros usava “twitter is over capacity”. Não é excelente, mas é melhor do que retornar um “503 Service Unavailable”.&lt;/p&gt;

&lt;p&gt;##Minizando erros&lt;/p&gt;

&lt;p&gt;Uma forma para minizar esses são com &lt;a href=&quot;http://en.wikipedia.org/wiki/Acceptance_testing&quot;&gt;Testes de aceitação&lt;/a&gt;, que também são conhecidos como Testes funcionais ou Testes de caixa-preta, estes testes podem ajudar muito para garantir que sua aplicação tenha o comportamento esperado.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/jnicklas/capybara&quot;&gt;Capybara&lt;/a&gt; é um framework de Testes de aceitação para aplicações web. Com ele é possível testar que dado o input A a aplicação deve retornar o output B. Mas precisamente com Capybara se submeto o form de login informando o usuário e senha válidos a aplicação deve me redirecionar para a página de usuário logado.&lt;/p&gt;

&lt;p&gt;##Teste no Controller sem Capybara&lt;/p&gt;

&lt;p&gt;Com &lt;a href=&quot;http://rubyonrails.org/&quot;&gt;Rails&lt;/a&gt; e &lt;a href=&quot;https://github.com/rspec/rspec&quot;&gt;RSpec&lt;/a&gt; é bem fácil para os controllers, porém não testa as views no máximo se elas estão renderizando corretamente se usar o  &lt;a href=&quot;http://rubydoc.info/gems/rspec-rails/file/README.md#render_views&quot;&gt;render_views&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;O generator padrão do Rails cria automáticamente testes para os controllers.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;describe PessoasController do
  describe &quot;GET 'index'&quot; do
    it &quot;returns http success&quot; do
      get 'index'
      response.should be_success
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Já ajuda bastante. Pelo menos garantirá que não tem nenhum erro no método index. Porém não testa o input e output.&lt;/p&gt;

&lt;p&gt;Adicionando mais assertions.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;describe PessoasController do
  describe &quot;GET 'index'&quot; do
    it &quot;deve filtrar por nome&quot; do
      pablo = Factory :pessoa, nome: &quot;Pablo&quot;
      Factory :pessoa, nome: &quot;Megan Fox&quot;
      get 'index', nome: &quot;Pablo&quot;
      assigns[:pessoas].should eq [pablo]
      response.should be_success
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Já melhora um pouco, já testa se o controller adicionou o Array de pessoas no request.&lt;/p&gt;

&lt;h2 id=&quot;teste-com-capybara&quot;&gt;Teste com Capybara&lt;/h2&gt;

&lt;p&gt;Com o Capybara além de testar o controller, testa a view, inclusive com Javascript. É um pouco diferente do teste acima, pois é um teste caixa-preta, não vai testar se existe algum atributo no request, aliás o request não está nem disponível no contexto do Capybara.&lt;/p&gt;

&lt;p&gt;Teste de login usando Capybara.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;describe &quot;Login com sucesso&quot;, :type =&amp;gt; :request do
  it &quot;login com sucesso deve redirecionar para a página novas corridas&quot; do
    password = &quot;123123123&quot;
    user = Factory.create :user, password: password
    visit new_user_session_path
    fill_in &quot;user[email]&quot;,                 with: user.email
    fill_in &quot;user[password]&quot;,              with: password
    click_button &quot;user_submit&quot;
    current_path.should == corridas_proximas_path
  end
end

describe &quot;Login sem sucesso&quot;, :type =&amp;gt; :request do
  it &quot;login sem sucesso deve voltar para a página de login&quot; do
    visit new_user_session_path
    fill_in &quot;user[email]&quot;,                 with: &quot;nonono&quot;
    fill_in &quot;user[password]&quot;,              with: &quot;nonono&quot;
    click_button &quot;user_submit&quot;
    current_path.should == new_user_session_path
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;##Cucumber&lt;/p&gt;

&lt;p&gt;Testes de aceitação baseados em ferramentas como o &lt;a href=&quot;http://cukes.info&quot;&gt;Cucumber&lt;/a&gt; (&lt;a href=&quot;http://pt.wikipedia.org/wiki/Behavior_Driven_Development&quot;&gt;BDD&lt;/a&gt;) também são uma alternativa para testar funcionalidades.&lt;/p&gt;

&lt;p&gt;Eu particularmente acho mais prático e rápido escrever testes com Capybara do que com o Cucumber, porém se as &lt;a href=&quot;http://dannorth.net/whats-in-a-story/&quot;&gt;User Stories&lt;/a&gt; já fizeram parte do processo e vierem em um formato que seja fácil para copiar, colar e executar, talvez seja mais interessante usar o Cucumber.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Por que eu troquei o WordPress pelo Jekyll?</title>
   <link href="http://www.pablocantero.com//blog/2012/03/14/por-que-eu-troquei-o-wordpress-pelo-jekyll/"/>
   <updated>2012-03-14T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2012/03/14/por-que-eu-troquei-o-wordpress-pelo-jekyll</id>
   
   <content type="html">&lt;p&gt;Eu já estava utilizando o &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt; (WP) desde o meu primeiro post que foi em &lt;a href=&quot;http://pablocantero.com/blog/2010/07/22/guia-rapido-de-configuracao-do-cucumber/&quot;&gt;22/07/2010&lt;/a&gt;. Não tenho muitas críticas ao WP, ele funciona bem. O principal motivo para migrar para o  &lt;a href=&quot;https://github.com/mojombo/jekyll&quot;&gt;Jekyll&lt;/a&gt; foi pela simplicidade e redução custos.&lt;/p&gt;

&lt;h2 id=&quot;jekyll&quot;&gt;Jekyll&lt;/h2&gt;

&lt;p&gt;Jekyll é um gerador de blog estático, você cria seus posts em &lt;a href=&quot;http://pt.wikipedia.org/wiki/Markdown&quot;&gt;Markdown&lt;/a&gt; e ele converte para HTML. É estático, portanto não usa banco de dados ou conteúdo dinâmico.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rake post &lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;
Creating new post: _posts/2012-03-14-hello-world.md

&lt;span class=&quot;c&quot;&gt;# _posts/2012-03-14-hello-world.md&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt;
layout: post
title: &lt;span class=&quot;s2&quot;&gt;&quot;Hello Word&quot;&lt;/span&gt;
category:
tags: &lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt;
Post content.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ele é muito simples, porém exige um certo conhecimento técnico. Recomendo para pessoas que saibam instalar uma &lt;a href=&quot;http://rubygems.org/&quot;&gt;Ruby gem&lt;/a&gt;, que conheçam &lt;a href=&quot;https://en.wikipedia.org/wiki/Git_(software)&quot;&gt;Git&lt;/a&gt; e Markdown.&lt;/p&gt;

&lt;p&gt;Você não precisa necessariamente conhecer Git, porém se conhecer poderá &lt;a href=&quot;https://help.github.com/articles/using-jekyll-with-pages/&quot;&gt;hospedar no GitHub gratuitamente&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A página &lt;a href=&quot;http://jekyllbootstrap.com/&quot;&gt;jekyllbootstrap.com&lt;/a&gt; ensina os passos para hospedar no GitHub.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Zero to Hosted Jekyll Blog in 3 Minutes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;markdown&quot;&gt;Markdown&lt;/h2&gt;

&lt;p&gt;Usar Markdown facilita muito a vida, é muito simples e prático, dá para editar em qualquer editor de texto. Usar o &lt;a href=&quot;http://pt.wikipedia.org/wiki/WYSIWYG&quot;&gt;WYSIWYG&lt;/a&gt; do WP, sempre foi muito traumático para mim. O modo preview sempre dá problema, quebra HTML e gera código horrível, ainda mais se usar algum plugin para syntax highlighting.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Com Jekyll basta eu colocar quatro espaços antes do código
que ele gerará um HTML do tipo:
	`&amp;lt;pre&amp;gt;&amp;lt;code&amp;gt;...&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;`
Que posso colocar o style (CSS) que eu quiser.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Mais sobre &lt;a href=&quot;http://daringfireball.net/projects/markdown/syntax&quot;&gt;Markdown&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;versionamento&quot;&gt;Versionamento&lt;/h2&gt;

&lt;p&gt;Eu uso o Jekyll com Git, portanto tenho o versionamento das minhas alterações. Se quero restaurar ou comparar as versões de um post é muito simples.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
git log
commit fe6d22281b53129b554a26b4645c8bbd340df6fe
Author: Pablo Cantero &amp;lt;pablo@pablocantero.com&amp;gt;
Date:   Wed Mar 14 10:09:13 2012 &lt;span class=&quot;nt&quot;&gt;-0300&lt;/span&gt;

   Quebra de linha por css
...

git diff fe6d22281b53129b554a26b4645c8bbd340df6fe

diff &lt;span class=&quot;nt&quot;&gt;--git&lt;/span&gt; a/assets/themes/tom/css/screen.css b/assets/themes/tom/css/screen.css
index bc975ca..bfd514d 100644
&lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt; a/assets/themes/tom/css/screen.css
+++ b/assets/themes/tom/css/screen.css
@@ &lt;span class=&quot;nt&quot;&gt;-174&lt;/span&gt;,6 +174,7 @@ ul.posts &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    background-color: &lt;span class=&quot;c&quot;&gt;#eef;&lt;/span&gt;
    font-size: 85%&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    padding: 0 .2em&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
+    word-break: break-word
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;#post pre code {&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;migração&quot;&gt;Migração&lt;/h2&gt;

&lt;p&gt;Eu segui os passos de migração da &lt;a href=&quot;https://github.com/mojombo/jekyll/wiki/Blog-Migrations&quot;&gt;documentação oficial&lt;/a&gt;, gerei um WordPress export file e importei para o Jekyll.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ruby &lt;span class=&quot;nt&quot;&gt;-rubygems&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'require &quot;jekyll/migrators/wordpressdotcom&quot;;Jekyll::WordpressDotCom.process(&quot;pablocantero.com.xml&quot;)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Funciona, porém ele importa o conteúdo em HTML. O Jekyll também funciona com HTML, se o arquivo for &lt;code class=&quot;highlighter-rouge&quot;&gt;.md&lt;/code&gt; ele interpretará como Markdown, se for &lt;code class=&quot;highlighter-rouge&quot;&gt;.html&lt;/code&gt; como HTML.&lt;/p&gt;

&lt;p&gt;Porém eu queria Markdown, portanto tive que editar manualmente todos os meus posts para convertê-los.&lt;/p&gt;

&lt;h2 id=&quot;redução-de-custos&quot;&gt;Redução de custos&lt;/h2&gt;

&lt;p&gt;Agora eu consigo hospedar gratuitamente no GitHub. No EC2 na região mais barata, com a instância mais barata (on demand) eu gastava aproximadamente &lt;a href=&quot;http://calculator.s3.amazonaws.com/calc5.html&quot;&gt;$37 por mês&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Claro, não tem comparação uma coisa com a outra, no EC2 eu tinha um servidor, porém para o meu blog o &lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub Pages&lt;/a&gt; é mais do que suficiente.&lt;/p&gt;

&lt;h2 id=&quot;domínio-próprio&quot;&gt;Domínio próprio&lt;/h2&gt;

&lt;p&gt;Por padrão no GitHub o site/blog ficará disponível em SEU-USUÁRIO.github.com, para utilizar com domínio próprio basta seguir a &lt;a href=&quot;http://pages.github.com/#custom_domains&quot;&gt;documentação oficial do GitHub Pages sobre custom domains&lt;/a&gt;.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Configurando o Node.js no Amazon EC2</title>
   <link href="http://www.pablocantero.com//blog/2012/01/04/configurando-o-node-js-no-amazon-ec2/"/>
   <updated>2012-01-04T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2012/01/04/configurando-o-node-js-no-amazon-ec2</id>
   
   <content type="html">&lt;p&gt;Esse post é &lt;del&gt;copiado&lt;/del&gt; baseado no post &lt;a href=&quot;http://cuppster.com/2011/05/12/diy-node-js-server-on-amazon-ec2/&quot;&gt;DIY node.js server on Amazon EC2&lt;/a&gt;, que foi a melhor referência que encontrei de como configurar um servidor &lt;a href=&quot;http://nodejs.org&quot;&gt;node.js&lt;/a&gt; completo em uma instância do EC2, no caso uma instância com Ubuntu (ubuntu-maverick-10.10-i386-server).&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Como eu disse estou me baseando com apenas pequenas adaptações no post DIY node.js server on Amazon EC2. Este post me motivou, pois ele funciona, é quase um script, vc pode copiar e colar os trechos de código que funciona. :)&lt;/p&gt;

&lt;h2 id=&quot;seleção-de-instância-no-ec2&quot;&gt;Seleção de instância no EC2&lt;/h2&gt;

&lt;p&gt;Eu criei uma instância ubuntu-maverick-10.10-i386-server no EC2, porém deverá funcionar com outras versões do Ubuntu e até em outros Linux com pequenas adaptações.&lt;/p&gt;

&lt;h2 id=&quot;configuração-do-ambiente&quot;&gt;Configuração do ambiente&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo apt-get update
$ sudo apt-get -y upgrade
$ sudo apt-get install rcconf
$ sudo apt-get install build-essential
$ sudo apt-get install libssl-dev
$ sudo apt-get install git-core
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;instalação-do-nodejs&quot;&gt;Instalação do node.js&lt;/h2&gt;

&lt;p&gt;O make demora bastante. ;)&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ wget http://nodejs.org/dist/node-latest.tar.gz
$ tar xzf node-latest.tar.gz
$ cd node-v0.6.2
$ ./configure --prefix=/usr
$ make
$ sudo make install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Quando eu escrevi esse post o node-latest era o 0.6.2, talvez a última versão já seja outra, verifique na hora de executar o “cd node-v0.6.2”. Vc também pode instalar baixando pelo git, assim vc poderá escolher a versão que quer instalar.&lt;/p&gt;

&lt;h2 id=&quot;instalação-do-nodejs-pelo-git&quot;&gt;Instalação do node.js pelo git&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ git clone git://github.com/ry/node.git
$ # git checkout origin/v0.4  #OPCIONAL
$ cd node
$ ./configure --prefix=/usr
$ make
$ sudo make install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;instalação-no-npm&quot;&gt;Instalação no npm&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cd ~
$ git clone http://github.com/isaacs/npm.git
$ cd npm
$ sudo make install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;instalação-do-nginx&quot;&gt;Instalação do nginx&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo apt-get install nginx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;configuração-do-nginx&quot;&gt;Configuração do nginx&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo vi /etc/nginx/sites-enabled/default
location / {
        proxy_pass      http://127.0.0.1:3000/;
}

location ^(/images|/javascripts|/press|/stylesheets|/templates)$ {
        root  /home/ubuntu/www/public;
        autoindex on;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Para o primeiro location verifique se a porta é mesma que você subirá o seu servidor node.js, a porta default é 3000.&lt;/p&gt;

&lt;p&gt;O segundo location é para fazer com que o nginix responda pelo o conteúdo estático sem passar pelo servidor node.js.&lt;/p&gt;

&lt;h2 id=&quot;integrando-com-o-git&quot;&gt;Integrando com o git&lt;/h2&gt;

&lt;p&gt;Uma coisa bacana é integrar o seu servidor como o git, transformando-o em um remote do git. Fazendo como o Heroku faz, que ao receber um git push, faz o deploy da aplicação.&lt;/p&gt;

&lt;p&gt;Para isso:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ mkdir ~/www
$ mkdir ~/repo
$ cd ~/repo
$ git init --bare
$ cat &amp;gt; hooks/post-receive

#!/bin/sh
GIT_WORK_TREE=/home/ubuntu/www
export GIT_WORK_TREE
git checkout -f

$ chmod +x hooks/post-receive
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;adicionando-sua-chave-pública-no-servidor&quot;&gt;Adicionando sua chave pública no servidor&lt;/h2&gt;

&lt;p&gt;No sua MAQUINA, LOCALHOST.&lt;/p&gt;

&lt;p&gt;Troque o amazon-instance-public-dns pelo o public-dns da sua instância.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cat ~/.ssh/id_rsa.pub | ssh -i amazon-generated-key.pem ec2-user@amazon-instance-public-dns &quot;cat&amp;gt;&amp;gt; ~/.ssh/authorized_keys&quot;

$ mkdir helloworld
$ cd helloworld
$ git init
$ git remote add ec2 ssh://ubuntu@your.static.ip/home/ubuntu/repo

$ cat &amp;gt; server.js

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(3000, &quot;127.0.0.1&quot;);
console.log('Server running at http://127.0.0.1:3000/');

$ git add server.js
$ git commit -m 'first'
$ git push ec2 +master:refs/heads/master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Pronto, se tudo funcionou como deveria, você conseguirá acessar pelo navegador http://amazon-instance-public-dns.&lt;/p&gt;

&lt;h2 id=&quot;mantendo-o-seu-nodejs-sempre-de-pé&quot;&gt;Mantendo o seu node.js sempre de pé&lt;/h2&gt;

&lt;p&gt;Para manter o node.js sempre de pé em caso de reboot ou crashing, estou usando o supervisor, exatamente igual o post que estou &lt;del&gt;copiando&lt;/del&gt; me baseando.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo apt-get install python-setuptools
$ sudo easy_install supervisor
$ curl https://raw.github.com/gist/176149/88d0d68c4af22a7474ad1d011659ea2d27e35b8d/supervisord.sh &amp;gt; supervisord
$ chmod +x supervisord
$ sudo mv supervisord /etc/init.d/supervisord
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;marque-o-supervisor-como-serviço&quot;&gt;Marque o supervisor como serviço&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo rcconf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;configurando-o-supervisor&quot;&gt;Configurando o supervisor&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo echo_supervisord_conf &amp;gt; supervisord.conf
$ sudo mv supervisord.conf /etc/supervisord.conf
$ sudo vi /etc/supervisord.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ainda no arquivo supervisord.conf, na seção [supervisord] configure:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[supervisord]
;...
user=ubuntu
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Logo acima da seção [program:theprogramname] adicione:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[program:node]
command=node app.js
directory=/home/ubuntu/www
environment=NODE_ENV=production

;[program:theprogramname]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Reload no supervisor.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ /etc/init.d/supervisord restart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;ou&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ supervisorctl reload
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;integrando-o-supervisor-com-o-git&quot;&gt;Integrando o supervisor com o git&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ vi ~/repo/hooks/post-receive

#!/bin/sh
GIT_WORK_TREE=/home/ubuntu/www
export GIT_WORK_TREE
git checkout -f
sudo supervisorctl restart node
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Seguindo os passos acima o seu servidor node.js deverá funcionar direitinho no EC2. Boa sorte!&lt;/p&gt;

</content>

 </entry>
 
 <entry>
   <title>Como trocar uma tela quebrada no iPhone 3GS</title>
   <link href="http://www.pablocantero.com//blog/2011/12/06/como-trocar-uma-tela-quebrada-no-iphone-3gs/"/>
   <updated>2011-12-06T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2011/12/06/como-trocar-uma-tela-quebrada-no-iphone-3gs</id>
   
   <content type="html">&lt;p&gt;Quebrou a tela do seu iPhone? Ihhh…, como consertar? Busquei várias &lt;a href=&quot;https://www.google.com.br/search?q=assist%C3%AAncia+t%C3%A9cnica+iphone&quot;&gt;assistências técnicas&lt;/a&gt; para o conserto, porém os orçamentos foram à partir de 400 reais para trocar o LCD e tela (digitizer/touch screen). Hein?!&lt;/p&gt;

&lt;h2 id=&quot;como-tudo-começou&quot;&gt;Como tudo começou&lt;/h2&gt;

&lt;p&gt;Na verdade inicialmente eu não tinha quebrado a tela do meu iPhone, a minha tela tinha apenas um micro risco interno, um defeito de fabricação. Eu por algum motivo (&lt;a href=&quot;http://pt.wikipedia.org/wiki/Transtorno_obsessivo-compulsivo&quot;&gt;TOC???&lt;/a&gt;), resolvi trocá-la. Meu amigo &lt;a href=&quot;http://rodger.com.br/&quot;&gt;Glauber Rodger&lt;/a&gt; tinha me comentado que era bem difícil, trabalhoso etc. Porém eu que sou muito inteligente (leia-se burro), fui influenciado por &lt;a href=&quot;http://www.youtube.com/results?search_query=replace+digitizer+iphone+3gs&amp;amp;aq=f&quot;&gt;vários vídeos no youtube&lt;/a&gt; e pensei: “Que isso Glauber, é bico”. Resumindo, comprei uma tela (digitizer/touch screen) no &lt;a href=&quot;http://www.ebay.com/sch/i.html?_nkw=iphone+3gs+digitizer&quot;&gt;eBay&lt;/a&gt; e ao tentar tirá-la , quebrei o LCD e frame. É muito difícil, os vídeos fazem parecer muito fácil, mas não é. Não se iluda.&lt;/p&gt;

&lt;h2 id=&quot;como-faz-então&quot;&gt;Como faz então?&lt;/h2&gt;

&lt;p&gt;Por sorte os chinas vendem o &lt;a href=&quot;http://www.ebay.com/sch/i.html?_nkw=iphone+3gs+frame+lcd+touch&quot;&gt;kit completo&lt;/a&gt; Tela + LCD + Frame, necessitando apenas plugar 3 conectadores.&lt;/p&gt;

&lt;p&gt;Mesmo que vc tenha quebrado apenas a Tela ou LCD ou Frame, recomendo o kit completo. Se tentar trocar apenas uma parte você corre o risco de quebrar outras. A diferença de valor entre o kit completo e as partes separadas não justifica a possível emoção.&lt;/p&gt;

&lt;p&gt;A maioria dos kits no eBay já acompanham as ferramentas para abrir o iPhone e trocar as partes necessárias. Preste atenção na hora da compra!&lt;/p&gt;

&lt;p&gt;Com o kit em mãos a troca é bastante simples:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Desparafusar dois parafusos na parte inferior do iPhone (ficam entre o conector do carregador/cabo de dados)&lt;/li&gt;
  &lt;li&gt;Puxar a tela usando uma ventosa (tome cuidado com os 3 conectores da parte superior)&lt;/li&gt;
  &lt;li&gt;Desconectar os 3 conectores da parte superior&lt;/li&gt;
  &lt;li&gt;Conectar o kit novo&lt;/li&gt;
  &lt;li&gt;Fechar a tela&lt;/li&gt;
  &lt;li&gt;Parafusar a tela nova&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;O conector número 1 exige bastante atenção para encaixá-lo, pois não é só plugar, tem que encaixar em um “furo” bem difícil. Já os outros dois conectores basta plugá-los.&lt;/p&gt;

&lt;p&gt;Dedique total atenção para essa troca (nada de tv ligada, celular, papagaio etc).&lt;/p&gt;

&lt;h2 id=&quot;assistência-técnica&quot;&gt;Assistência Técnica&lt;/h2&gt;

&lt;p&gt;Eu super recomendo assistência técnica, se você estiver disposto a pagar mais de 400 reais, não perca o seu tempo, mande para uma assistência. No meu caso eu preferi fazer, pois não achava que a relação custo x benefício no meu iPhone valeria a pena.&lt;/p&gt;

&lt;h2 id=&quot;qual-kit&quot;&gt;Qual kit?&lt;/h2&gt;

&lt;p&gt;No geral os kits são todos iguais, porém tome cuidado, pois quando eu comprei &lt;a href=&quot;http://www.ebay.com/itm/350476987492&quot;&gt;somente a tela&lt;/a&gt; (não o kit completo), veio uma tela bem ruim, bem diferente da original. &lt;a href=&quot;http://toolhaus.org/&quot;&gt;Busque sempre pelas qualificações do item e vendedor&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.ebay.com/itm/170658644696&quot;&gt;Kit que eu comprei&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Boa sorte!&lt;/p&gt;

</content>

 </entry>
 
 <entry>
   <title>Habilitando SSL no BitNami Tomcat Stack</title>
   <link href="http://www.pablocantero.com//blog/2011/07/11/habilitando-ssl-no-bitnami-tomcat-stack/"/>
   <updated>2011-07-11T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2011/07/11/habilitando-ssl-no-bitnami-tomcat-stack</id>
   
   <content type="html">&lt;p&gt;Em continuação ao &lt;a href=&quot;http://pablocantero.com/blog/2011/07/08/primeiros-passos-no-amazon-ec2/&quot;&gt;Primeiros passos no Amazon EC2&lt;/a&gt;, este post descreve os passos para habilitar SSL no BitNami Tomcat Stack.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;##Habilitando SSL&lt;/p&gt;

&lt;p&gt;Não vou detalhar muito os passos abaixo, pois esses detalhes podem ser encontrados facilmente na internet. Este é um guia copy&amp;amp;paste para criação de SSL self-signed usando o OpenSSL no BitNami Tomcat Stack. Na verdade esses passos funcionam para Ubuntu e Apache2, não necessariamente só para o Tomcat Stack.&lt;/p&gt;

&lt;p&gt;##Criando um certificado self-signed&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Substituindo o usuário corrente (bitnami) para o root.
$ sudo su root

# Instalando o OpenSSL.
$ apt-get install openssl

# Criando o diretório para guardar os certificados.
$ cd ~
$ mkdir sslcert
$ cd sslcert

# Criação das chaves pública e privada.
$ openssl genrsa -des3 -out server.key 1024

# No &quot;Common Name (eg, YOUR name)&quot; preencher com o domínio ou ip que o servidor que será acessado.
$ openssl req -new -key server.key -out server.csr

# O parâmetro &quot;-days 1825&quot;, quer dizer que a chave só expirará em 5 anos.
$ openssl x509 -req -days 1825 -in server.csr -signkey server.key -out server.crt

# Criando um chave sem senha, caso contrário o Apache sempre pedirá ao iniciar a senha definida na criação do server.key.
$ cp server.key server.key.secure
$ openssl rsa -in server.key.secure -out server.key

# Colocando permissões somente de leitura para o owner do arquivo e sem permissão para os demais.
$ chmod 400 server.csr
$ chmod 400 server.crt
$ chmod 400 server.key
$ chmod 400 server.key.secure

# Copiando arquivos necessários para o diretório do Apache.
$ mkdir /opt/bitnami/apache2/conf/sslcert
$ cp server.crt  /opt/bitnami/apache2/conf/sslcert
$ cp server.key  /opt/bitnami/apache2/conf/sslcert
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;##Habilitando SSL no Apache&lt;/p&gt;

&lt;p&gt;Descomentar a linha &lt;code class=&quot;highlighter-rouge&quot;&gt;Include conf/extra/httpd-ssl.conf&lt;/code&gt; no &lt;code class=&quot;highlighter-rouge&quot;&gt;/opt/bitnami/apache2/conf/httpd.conf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Alterar as linhas no arquivo &lt;code class=&quot;highlighter-rouge&quot;&gt;/opt/bitnami/apache2/conf/extras/httpd-ssl.conf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;De&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SSLCertificateFile &quot;/opt/bitnami/apache2/conf/server.crt&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Para&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SSLCertificateFile &quot;/opt/bitnami/apache2/conf/sslcert/server.crt&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;De&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SSLCertificateKeyFile &quot;/opt/bitnami/apache2/conf/server.key&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Para&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SSLCertificateKeyFile &quot;/opt/bitnami/apache2/conf/sslcert/server.key&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Basta reiniciar o Apache que o SSL estará pronto para o uso.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apachectl restart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>

 </entry>
 
 <entry>
   <title>TDC São Paulo 2011: Trilhas Java, TV Digital e Arduino</title>
   <link href="http://www.pablocantero.com//blog/2011/07/08/tdc-sao-paulo-2011-trilhas-java-tv-digital-e-arduino/"/>
   <updated>2011-07-08T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2011/07/08/tdc-sao-paulo-2011-trilhas-java-tv-digital-e-arduino</id>
   
   <content type="html">&lt;p&gt;Esse ano fui ao &lt;a href=&quot;http://www.thedevelopersconference.com.br/tdc/2011/index.html#saopaulo&quot;&gt;TDC São Paulo 2011&lt;/a&gt;, onde assisti às trilhas de &lt;a href=&quot;http://www.thedevelopersconference.com.br/tdc/2011/saopaulo/trilha-java&quot;&gt;Java&lt;/a&gt;, &lt;a href=&quot;http://www.thedevelopersconference.com.br/tdc/2011/saopaulo/trilha-tvdigital&quot;&gt;TV Digital&lt;/a&gt; e &lt;a href=&quot;http://www.thedevelopersconference.com.br/tdc/2011/saopaulo/trilha-arduino&quot;&gt;Arduino&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nesse post relato as minhas percepções sobre as trilhas e evento.&lt;/p&gt;

&lt;h2 id=&quot;tdc-são-paulo-2011&quot;&gt;TDC São Paulo 2011&lt;/h2&gt;

&lt;p&gt;A organização do evento foi muito boa, atendeu bem as expectativas. O modelo de trilha é bem interessante, você paga somente pelo o que quer assistir. Cada trilha custava 60 reais, porém consegui um código promocional que vi em um tweet que barateou para 49,80 reais. Além da trilha o preço inclui um kit almoço do Subway (lanche, bebida e biscoito).&lt;/p&gt;

&lt;h2 id=&quot;trilha-java&quot;&gt;Trilha Java&lt;/h2&gt;

&lt;p&gt;Na trilha Java teve o lançamento mundial do Java 7,  que foi apresentado simultaneamente na Califórnia, Londres e São Paulo. Sendo principalmente apresentado na Califórnia, com chamadas para Londres e São Paulo. Incluir o Brasil no lançamento foi um reconhecimento muito importante para a comunidade Java no Brasil. Sinceramente fiquei orgulhoso. Em Londres a sala era pequena, com poucas pessoas e sem animação. No Brasil era um auditório grande, cheio de pessoas e com muita animação.&lt;/p&gt;

&lt;p&gt;A Oracle está fortalecendo o marketing da JVM rodando outras linguagens. Um dos grandes lançamentos foi o InvokeDynamic, que beneficiará muita outras linguagens sobre a JVM, principalmente em performance e diminuição de complexidade de código. Usaram como exemplo o JRuby, dizendo que poderiam apagar cerca de 2000 classes utilizando esse novo recurso, pois atualmente eles fazem malabarismos para simular métodos dinâmicos.&lt;/p&gt;

&lt;p&gt;Acho muito sábia essa campanha da JVM rodando outras   linguagens, pois assim a Oracle poderá continuar forte no mercado, mesmo que não seja com Java.&lt;/p&gt;

&lt;p&gt;Java também se beneficiará do InvokeDanymaic, principalmente em substituição ao Reflection. Quase todos os frameworks/projetos atuais em Java utilizam Reflection de alguma forma. O exemplo mais corriqueiro é com Inversão de Controle (IoC).&lt;/p&gt;

&lt;p&gt;Outro acontecimento importante que acredito estar totalmente relacionado a  manter-se forte no mercado, foi oficializarem o OpenJDK como Reference Implementation (RI). Com o OpenJDK sendo RI, não existirão mais desculpas do tipo “Não funcionou no OpenJDK”, como o caso  clássico do programa do IRPF. Se não funcionar no OpenJDK ou programa está mal feito ou a JVM em que ele foi testado está mal feita.&lt;/p&gt;

&lt;p&gt;Java teve algumas atualizações estéticas com o Projeto Coin.  Embora outras alterações bem significativas como lambda, só virão com o Coin 2 no Java 8.&lt;/p&gt;

&lt;p&gt;No evento também foi apresentado o NIO.2.&lt;/p&gt;

&lt;p&gt;Fiquei contente com as atualizações na linguagem Java, porém acho que o código está ficando esteticamente um Frankenstein.&lt;/p&gt;

&lt;p&gt;No InfoQ tem mais detalhes sobre essas atualizações.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.infoq.com/br/news/2011/07/lancamento-java7&quot;&gt;infoq.com/br/news/2011/07/lancamento-java7&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.infoq.com/br/articles/java7coin&quot;&gt;infoq.com/br/articles/java7coin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;trilha-tv-digital&quot;&gt;Trilha TV Digital&lt;/h2&gt;

&lt;p&gt;Na trilha de TV Digital assisti apenas à apresentação “Interatividade na TV Digital - Ginga e Plataformas de Desenvolvimento”.  Sinceramente, fiquei chateado, pois vi que ainda existe um abismo para desenvolvedores autônomos. NCL, Lua, Ginga etc, funcionam para emissoras de TV, porém para fazer um programa em casa e publicar ainda é um processo bem complicado. Acredito que a luz no fim do  túnel só ocorrerá com sistemas operacionais para TV, como o Google TV,  que certamente terá um Apps Marketplace.&lt;/p&gt;

&lt;h2 id=&quot;trilha-arduino&quot;&gt;Trilha Arduino&lt;/h2&gt;

&lt;p&gt;A trilha de Arduino foi OK, exatamente isso, OK. Não foi ruim, porém acredito que tenha faltado um caso real, “Usei para fazer tal protótipo, funcionou bem blah blah…”.  Não apenas “dá para fazer isso, dá para fazer aquilo”. Comento isso, pois quiseram introduzir o Arduino como algo além de hobby, e infelizmente as palestras só demonstraram hobbies. Claro, a partir desses hobbies podem nascer muitos projetos, porém os apresentados estavam bem “verdes” ainda.&lt;/p&gt;

&lt;p&gt;Em resumo, aprendi o que é &lt;a href=&quot;http://en.wikipedia.org/wiki/XBee&quot;&gt;XBee&lt;/a&gt; e conheci o &lt;a href=&quot;http://www.arduino.cc/en/Reference/Firmata&quot;&gt;Firmata&lt;/a&gt;.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Primeiros passos no Amazon EC2</title>
   <link href="http://www.pablocantero.com//blog/2011/07/08/primeiros-passos-no-amazon-ec2/"/>
   <updated>2011-07-08T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2011/07/08/primeiros-passos-no-amazon-ec2</id>
   
   <content type="html">&lt;h2 id=&quot;por-que-estou-migrando-para-a-amazon&quot;&gt;Por que estou migrando para a Amazon?&lt;/h2&gt;

&lt;p&gt;Há alguns anos utilizo uma maquina virtual (VM) com o CentOS hospedada fora do Brasil, obviamente está fora devido ao custo favorável. Nessa VM estou rodando esse blog, uma aplicação Rails, uma aplicação Java e algumas vezes utilizo para testes diversos. Apesar do ambiente misto com PHP, Ruby e Java, essas aplicações não consomem muito processamento e nem memória.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Além do preço favorável, outro fator que me motivou para iniciar essa migração para a Amazon, foi a facilidade para gerenciar as maquinas virtuais e recursos. Posso subir uma instância para o blog, outra para Rails, outra para Java, facilmente pelo Web Console da Amazon ou por linha de comando ou por API.&lt;/p&gt;

&lt;p&gt;Se tudo estivesse bem na minha VM, eu não teria iniciado essa migração. Porém fizeram uma alteração na infraestrutura de onde hospedo minha VM, que deixou o meu Tomcat muito instável. Para resolver essa instabilidade eu teria que aumentar a memória da VM, implicando no aumento da mensalidade, para mim não faz sentido, pois antes funciona “perfeitamente”. Enfim, vamos lá… também não deixa de ser uma oportunidade para aprender um pouco sobre o &lt;a href=&quot;http://aws.amazon.com/ec2/&quot;&gt;Amazon EC2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Esse post é focado no primeiros passos que utilizei no EC2 para migrar minha aplicação Java, que apesar de ser super simples, pouco mais que um Web Service, é uma aplicação bastante importante para o meu cliente. Ela foi o ponta pé inicial para essa migração, pois meu cliente estava sendo bastante afetado com a instabilidade do Tomcat.&lt;/p&gt;

&lt;h2 id=&quot;quanto-custa&quot;&gt;Quanto custa?&lt;/h2&gt;

&lt;p&gt;O EC2 pode ser usado gratuitamente por durante 1 ano (&lt;a href=&quot;http://aws.amazon.com/free/&quot;&gt;AWS Free Usage Tier&lt;/a&gt;). A versão gratuita já é bastante interessante. O poder de processamento dessa versão supera o poder de processamento da minha VM com CentOS.&lt;/p&gt;

&lt;p&gt;Após o período gratuito ou caso você opte por algum plano pago, os valores são bem atrativos. Com planos a partir de 0.02 dólares por hora. Com o plano mais barato, caso a instância seja utilizada 24/7, o custo máximo será por volta de 14 dólares mensais. Para maiores detalhes &lt;a href=&quot;http://aws.amazon.com/ec2/pricing/&quot;&gt;Amazon EC2 Pricing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Lembrando que mesmo usando a versão gratuita, você terá que cadastrar um cartão de crédito internacional.&lt;/p&gt;

&lt;h2 id=&quot;criação-da-primeira-vm&quot;&gt;Criação da primeira VM&lt;/h2&gt;

&lt;p&gt;Para iniciar no cloud, você precisa criar uma nova instância de uma VM utilizando uma Amazon Machine Image (AMI). Existem várias imagens disponíveis para Linux e Windows. Imagens pré definidas pela Amazon e outras pela comunidade (Community AMIs). No meu caso escolhi a imagem comunidade &lt;a href=&quot;http://bitnami.org/stack/tomcatstack&quot;&gt;bitnami-tomcatstack&lt;/a&gt;, AMI ID: ami-0eec1167, que vem com o Ubuntu, Tomcat 6, Java 6, Apache e MySQL. As instruções desse post serão baseadas nesse imagem.&lt;/p&gt;

&lt;p&gt;Para criar a instância você utilizará um wizard (next, next … and next) dentro do &lt;a href=&quot;http://aws.amazon.com/console/&quot;&gt;AWS Management Console&lt;/a&gt;. Essa criação possui alguns detalhes que estão comentados abaixo.&lt;/p&gt;

&lt;p&gt;Instance tags - Você pode colocar tags na sua instância, “web server”, “database”, “homologação”, “produção”, “queridinho” etc. Esses tags fazem muito sentido para quem possui muitas instâncias, sendo muito fácil para aplicar filtros.&lt;/p&gt;

&lt;p&gt;Security Group - É o lugar onde você pode liberar as portas do seu servidor, a versão Default vem com tudo bloqueado. No meu caso criei um Security Group novo e liberei as portas de SSH (22) e HTTP (80).&lt;/p&gt;

&lt;p&gt;Key pair name - Serve para criar uma chave pública e privada para conectar (SSH) com a sua instância. Faça o download da chave na hora da criação, pois depois não é possível baixar Key pair via o AWS Management Console.&lt;/p&gt;

&lt;h2 id=&quot;endereço-da-sua-instância&quot;&gt;Endereço da sua instância&lt;/h2&gt;

&lt;p&gt;O Amazon criará automaticamente um endereço Public DNS para sua instância, que será algo do tipo ec2-11-22-3-444.compute-1.amazonaws.com. Porém se você quiser adicionar um IP, basta ir em Elastic IPs no AWS Management Console, criar um IP e associá-lo a sua instância.&lt;/p&gt;

&lt;h2 id=&quot;acessando-a-sua-instância-usando-ssh&quot;&gt;Acessando a sua instância usando SSH&lt;/h2&gt;

&lt;p&gt;Basta clicar na sua instância com o botão direito e ir na opção Connect. Será exibido a linha de comando para conectar via SSH. Não se esqueça de aplicar as permissões &lt;code class=&quot;highlighter-rouge&quot;&gt;chmod 400&lt;/code&gt; no arquivo .pem, sem essas permissões o SSH não funcionará. O arquivo .pem é o arquivo que foi baixado na criação do seu Key pair.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Use chmod to make sure your key file isn’t publicly viewable, ssh won’t work otherwise:
chmod 400 instancia.pem&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lembrando de alterar o usuário “ubuntu” que aparece na linha de comando para se conectar via SSH, pelo usuário bitnami, que é o usuário padrão criado pela imagem que bitnami.&lt;/p&gt;

&lt;p&gt;De
   ssh -i instancia .pem bitnami @ec2-50-16-0-227.compute-1.amazonaws.com
   Para
   ssh -i instancia .pem bitnami ec2-50-16-0-227.compute-1.amazonaws.com&lt;/p&gt;

&lt;h2 id=&quot;alterando-a-senha-padrão-do-tomcat-manager&quot;&gt;Alterando a senha padrão do Tomcat Manager&lt;/h2&gt;

&lt;p&gt;O usuário e senha padrão para o Tomcat Manager são manager e bitnami respectivamente. Para alterá-los basta acessar o arquivo /opt/bitnami/apache-tomcat/conf/tomcat-users.xml e alterar para usuário e senha mais convenientes.&lt;/p&gt;

&lt;p&gt;sudo vi /opt/bitnami/apache-tomcat/conf/tomcat-users.xml&lt;/p&gt;

&lt;h2 id=&quot;alterando-a-senha-do-root-do-mysql&quot;&gt;Alterando a senha do root do MySQL&lt;/h2&gt;

&lt;p&gt;O usuário root do MySQL da instalação padrão do bitnami não possui senha, para defina-la basta executar o comando abaixo.&lt;/p&gt;

&lt;p&gt;mysqladmin -u root password NEWPASSWORD&lt;/p&gt;

&lt;h2 id=&quot;criando-um-usuário-e-um-banco-de-dados-no-mysql&quot;&gt;Criando um usuário e um banco de dados no MySQL&lt;/h2&gt;

&lt;p&gt;Apesar de trivial, a receita de bolo abaixo é bem útil.&lt;/p&gt;

&lt;p&gt;mysql –user=root
   mysql&amp;gt; create database meu-banco;
   mysql&amp;gt; grant usage on &lt;em&gt;.&lt;/em&gt; to meu-user@localhost identified by ‘minha-senha’;
   # Eu preciso de todos os privilégios para executar DDL e DML
   mysql&amp;gt; grant all privileges on meu-banco.* to meu-user@localhost;&lt;/p&gt;

&lt;h2 id=&quot;fazendo-o-deploy-da-sua-aplicação&quot;&gt;Fazendo o deploy da sua aplicação&lt;/h2&gt;

&lt;p&gt;A maneira mais simples de fazer o deploy é diretamente pelo Tomcat Manager. Na minha aplicação o arquivo WAR tem por volta de 10MB. A primeira vez que tentei fazer o deploy através do Tomcat Manager não funcionou, tive que repetir o processo pelo menos duas vezes até funcionar.&lt;/p&gt;

&lt;h2 id=&quot;cuidado-com-o-botão-terminate&quot;&gt;Cuidado com o botão Terminate!!!&lt;/h2&gt;

&lt;p&gt;No AWS Management Console, se você clicar com o botão direito na sua instância, aparecerá a opção Terminate logo acima de Reboot, Stop e Start. Essa opção não é para fazer Stop forçado, essa opção é para apagar a sua instância, você não poderá usá-la novamente. Ela ficará na sua lista de instâncias por algum tempo (no meu caso ficou menos de uma hora), porém logo mais sumirá.&lt;/p&gt;

&lt;h2 id=&quot;conclusão&quot;&gt;Conclusão&lt;/h2&gt;

&lt;p&gt;Com os poucos passos acima a minha aplicação Java está funcionando perfeitamente no EC2. Por enquanto essa experiência está sendo muito boa, muito menos restritiva da que foi com o GAE. Restrições que caracterizam umas das grandes diferenças entre  &lt;a href=&quot;http://en.wikipedia.org/wiki/Infrastructure_as_a_service#Infrastructure&quot;&gt;IaaS&lt;/a&gt; (EC2) e &lt;a href=&quot;http://en.wikipedia.org/wiki/Platform_as_a_service&quot;&gt;PaaS&lt;/a&gt; (GAE).&lt;/p&gt;

&lt;p&gt;Algo que não posso deixar de levar em consideração, é que com a hospedagem anterior eu tinha um suporte diferenciado, bastava eu abrir um Support Ticket ou Sales Ticket, que em menos de uma hora eu recebia uma resposta. Na Amazon esse tipo de atendimento só está disponível para as contas com suporte Premium através do &lt;a href=&quot;http://aws.amazon.com/premiumsupport/&quot;&gt;AWS Premium Support&lt;/a&gt;. Por outro lado, podemos obter muita informação através de fóruns, blogs (como esse que vos fala) e obviamente na própria documentação da Amazon.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>RESTful: How to use POST for Asynchronous operations</title>
   <link href="http://www.pablocantero.com//blog/2011/07/04/restful-how-to-use-post-for-asynchronous-operations/"/>
   <updated>2011-07-04T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2011/07/04/restful-how-to-use-post-for-asynchronous-operations</id>
   
   <content type="html">&lt;p&gt;I’m writing this post inspired by the book &lt;a href=&quot;http://www.amazon.com/dp/0596801688/&quot;&gt;RESTful Web Services Cookbook&lt;/a&gt;. This book has many useful examples on how to use &lt;a href=&quot;http://en.wikipedia.org/wiki/Representational_State_Transfer#RESTful_web_services&quot;&gt;RESTful Web Services&lt;/a&gt;. If you are looking for a book on RESTFul I highly recommend this book.&lt;/p&gt;

&lt;p&gt;This post is an example of an Asynchronous POST using RESTful. I chose this example, because it covers good principles of RESTful, for example the proper usage of HTTP codes.&lt;/p&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;The Asynchronous POST will be used on this example is to create a new product in an external application “ABCSuite”. This operation needs to be async, because it can take long to complete.&lt;/p&gt;

&lt;p&gt;Mapping resources and operations in RESTful is typically done by using nouns as resources and verbs (HTTP) as operations, similar to the technique used in OOP mapping.&lt;/p&gt;

&lt;p&gt;Although RESTful hasn’t an official standard for HTTP verbs into operations. We will follow the convention below to operate the resources.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Create = POST&lt;/li&gt;
  &lt;li&gt;Retrieve = GET&lt;/li&gt;
  &lt;li&gt;Update = PUT&lt;/li&gt;
  &lt;li&gt;Delete = DELETE&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;create-a-product&quot;&gt;Create a product&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# REQUEST
POST /products HTTP/1.1
Host: www.abcsuite.com
Content-Type: application/json

{
  &quot;id&quot;: &quot;1&quot;,
  &quot;name&quot;: &quot;Awesome Product&quot;,
  &quot;price&quot;: 1.00
}

# RESPONSE
HTTP/1.1 202 Accepted
Content-Type: application/json
Content-Location: http://www.abcsuite.com/products/tasks/1
Date: Mon, 4 July 2011 17:25:10 GMT

{
  &quot;status&quot;: &quot;pending&quot;
  &quot;links&quot;: [
     &quot;rel&quot;: &quot;self&quot;
     &quot;href&quot;: &quot;http://www.abcsuite.com/products/tasks/1&quot;
   ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;content-type-applicationjson&quot;&gt;Content-Type: application/json&lt;/h3&gt;

&lt;p&gt;The charset wasn’t explicitly specified, because JSON uses &lt;a href=&quot;http://www.ietf.org/rfc/rfc4627.txt&quot;&gt;UTF-8 by default&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;http11-202-accepted&quot;&gt;HTTP/1.1 202 Accepted&lt;/h3&gt;

&lt;p&gt;The response &lt;code class=&quot;highlighter-rouge&quot;&gt;202 Accepted&lt;/code&gt; was used to represent that the operation wasn’t completed yet. The operation was only accepted - “We are working on it. Check it later”.&lt;/p&gt;

&lt;h2 id=&quot;checking-the-production-creation-status&quot;&gt;Checking the production creation status&lt;/h2&gt;

&lt;p&gt;By using the previous &lt;code class=&quot;highlighter-rouge&quot;&gt;link rel&lt;/code&gt; received in the last response, we can check the production creation status.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# REQUEST
GET /products/tasks/1 HTTP/1.1
Host: www.abcsuite.com

# RESPONSE
HTTP/1.1 200 OK
Content-Type: application/json
Content-Location: http://ww.abcsuite.com/products/tasks/1
Date: Mon, 4 July 2011 17:25:10 GMT

{
  &quot;status&quot;: &quot;pending&quot;
  &quot;links&quot;: [
     &quot;rel&quot;: &quot;self&quot;
     &quot;href&quot;: &quot;http://ww.abcsuite.com/products/tasks/1&quot;
   ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The response code above was &lt;code class=&quot;highlighter-rouge&quot;&gt;200 OK&lt;/code&gt;, because the operation “Checking the product creation status” was completed.&lt;/p&gt;

&lt;h2 id=&quot;the-server-successfully-completes-the-processing&quot;&gt;The server successfully completes the processing&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# REQUEST
GET /products/tasks/1 HTTP/1.1
Host: www.abcsuite.com

# RESPONSE
HTTP/1.1 302 See Other
Content-Type: application/json
Location: http://ww.abcsuite.com/products/1
Content-Location: http://ww.abcsuite.com/products/taks/1
Date: Mon, 4 July 2011 17:25:10 GMT

{
  &quot;status&quot;: &quot;done&quot;
  &quot;links&quot;: [
     &quot;rel&quot;: &quot;self&quot;
     &quot;href&quot;: &quot;http://ww.abcsuite.com/products/1&quot;
   ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After the server completes the processing, the response code will be &lt;code class=&quot;highlighter-rouge&quot;&gt;303 See Other&lt;/code&gt; and the &lt;code class=&quot;highlighter-rouge&quot;&gt;Content-Location&lt;/code&gt; header will contain the url to access the created product.&lt;/p&gt;

</content>

 </entry>
 
 <entry>
   <title>AntiPattern: Voyeuristic Model/Solution: Follow the Law of Demeter</title>
   <link href="http://www.pablocantero.com//blog/2011/06/20/antipattern-voyeuristic-modelsolution-follow-the-law-of-demeter/"/>
   <updated>2011-06-20T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2011/06/20/antipattern-voyeuristic-modelsolution-follow-the-law-of-demeter</id>
   
   <content type="html">&lt;p&gt;Esse post é um pedacinho do capitulo 1, do livro &lt;a href=&quot;http://www.amazon.com/Rails-AntiPatterns-Refactoring-Addison-Wesley-Professional/dp/0321604814&quot;&gt;Rails Antipatterns&lt;/a&gt;. Focando no uso do método &lt;a href=&quot;http://api.rubyonrails.org/classes/Module.html#method-i-delegate&quot;&gt;delegate&lt;/a&gt; do Rails, referênciado no tópico Solution: Follow the &lt;a href=&quot;http://en.wikipedia.org/wiki/Law_of_Demeter&quot;&gt;Law of Demeter&lt;/a&gt;.&lt;/p&gt;

&lt;!--more--&gt;
&lt;p&gt;Antes de iniciar a explicação sobre o delegate, vamos a definição de &lt;a href=&quot;http://en.wikipedia.org/wiki/Anti-pattern&quot;&gt;Antipattern&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;In software engineering, an anti-pattern (or antipattern) is a pattern  that may be commonly used but is ineffective and/or counterproductive in  practice. The term was coined in 1995 by Andrew Koenig, inspired by Gang of  Four’s book Design Patterns, which developed the concept of design  patterns in the software field. The term was widely popularized three  years later by the book AntiPatterns, which extended the use of the term  beyond the field of software design and into general social  interaction. According to the authors of the latter, there must be at  least two key elements present to formally distinguish an actual  anti-pattern from a simple bad habit, bad practice, or bad idea:&lt;/p&gt;

  &lt;p&gt;Some repeated pattern of action, process or structure that initially  appears to be beneficial, but ultimately produces more bad consequences  than beneficial results, and&lt;/p&gt;

  &lt;p&gt;A refactored solution exists that is clearly documented, proven in actual practice and repeatable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Definição de &lt;a href=&quot;http://en.wikipedia.org/wiki/Law_of_Demeter&quot;&gt;Law of Demeter&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The Law of Demeter (LoD) or Principle of Least Knowledge is a design guideline for developing software, particularly object-oriented programs. In its general form, the LoD is a specific case of loose coupling. The guideline was invented at Northeastern University towards the end of 1987, and can be succinctly summarized in one of the following ways: Each unit should have only limited knowledge about other units: only units “closely” related to the current unit.&lt;/p&gt;

  &lt;p&gt;Each unit should only talk to its friends; don’t talk to strangers.&lt;/p&gt;

  &lt;p&gt;Only talk to your immediate friends.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;##Follow the Law of Demeter&lt;/p&gt;

&lt;p&gt;Uma Invoice tem um Customer que tem um Address. Este é um exemplo clássico de associações em desenvolvimento de software.&lt;/p&gt;

&lt;p&gt;##Implementação trivial em Rails&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class Address &amp;lt; ActiveRecord::Base
  belongs_to :customer
end

class Customer &amp;lt; ActiveRecord::Base
  has_one :address
  has_many :invoices
end

class Invoice &amp;lt; ActiveRecord::Base
  belongs_to :customer
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Exemplo de view acessando esses modelos.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;%= @invoice.customer.name %&amp;gt;
&amp;lt;%= @invoice.customer.address.street %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Em boa parte dos sistemas, essa implementação funcionará perfeitamente. Porém com um pequeno refactoring, aumentaremos muito a capacidade de evolução desse código (extensibility e maintainability).&lt;/p&gt;

&lt;p&gt;Se um dia o relacionamento de Invoice com Customer ou de Customer com Address for alterado, poderá implicar na alteração em todas as referências para Customer ou Address a partir de Invoice.&lt;/p&gt;

&lt;p&gt;##Implementação trivial de encapsulamento&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class Invoice &amp;lt; ActiveRecord::Base
  belongs_to :customer

  def customer_name
    customer.name
  end

  def customer_street
    customer.street
  end
end

class Customer &amp;lt; ActiveRecord::Base
  belongs_to :address

  def street
    address.street
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Exemplo de view acessando esses modelos.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;%= @invoice.customer_name %&amp;gt;
&amp;lt;%= @invoice.customer_street %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;O exemplo acima resolverá o problema encapsulando as referências de Customer e Address, porém gerará muito código, pois teremos que reescrever todos os getters e setters. Para contornar isso, podemos usar o método delegate do Rails.&lt;/p&gt;

&lt;p&gt;##Implementação utilizado o método delegate&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class Invoice &amp;lt; ActiveRecord::Base
  belongs_to :customer
  delegate :name,
           :street,
           :to     =&amp;gt; :customer,
           :prefix =&amp;gt; true
end

class Customer &amp;lt; ActiveRecord::Base
  belongs_to :address
  delegate :street,
           :to     =&amp;gt; address,
           :prefix =&amp;gt; false
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Exemplo de view acessando esses modelos.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;%= @invoice.customer_name %&amp;gt;
&amp;lt;%= @invoice.customer_street %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Utilizando o método delegate os modelos ficaram muito mais enxutos e com grande capacidade evolutiva.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>RVM Utilizando arquivos .rvmrc por projeto</title>
   <link href="http://www.pablocantero.com//blog/2011/06/07/rvm-utilizando-arquivos-rvmrc-por-projeto/"/>
   <updated>2011-06-07T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2011/06/07/rvm-utilizando-arquivos-rvmrc-por-projeto</id>
   
   <content type="html">&lt;p&gt;O &lt;a href=&quot;https://rvm.beginrescueend.com/workflow/rvmrc#project&quot;&gt;rvmrc&lt;/a&gt; por projeto é um recurso muito útil do &lt;a href=&quot;https://rvm.beginrescueend.com&quot;&gt;Ruby Version Manager (RVM)&lt;/a&gt;. Com ele você pode configurar qual &lt;a href=&quot;https://rvm.beginrescueend.com/rubies&quot;&gt;rubie&lt;/a&gt; e qual &lt;a href=&quot;https://rvm.beginrescueend.com/gemsets/&quot;&gt;gemset&lt;/a&gt; o seu projeto utilizará, evitando possíveis conflitos entre projetos.
&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Para utilizar o rvmrc, basta criar um arquivo .rvmrc na raiz do seu projeto definindo a rubie e gemset serão utilizados.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rvm gemset create hello_world
mkdir hello_world
cd hello_world
echo 'rvm 1.8.7@hello_world' &amp;gt; .rvmrc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Desta forma o RVM automaticamente utilizará o rubie e gemset definidos no arquivos .rvmc. Sensacional!&lt;/p&gt;

&lt;p&gt;Além do rvmrc por projeto, você também pode usar um rvmrc por sistema (default) ou por usuário. Por usuário basta criar o rvmrc na raiz do “$HOME/.rvmrc”.&lt;/p&gt;

&lt;p&gt;A ordem de precedência do rvmrc é por projeto, por usuário e por último por sistema.&lt;/p&gt;

&lt;p&gt;O link abaixo detalha os três tipos de rvmrc.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://rvm.beginrescueend.com/workflow/rvmrc&quot;&gt;rvm.beginrescueend.com/workflow/rvmrc/&lt;/a&gt;&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Adding images to iPhone Simulator</title>
   <link href="http://www.pablocantero.com//blog/2011/06/07/adding-images-to-iphone-simulator/"/>
   <updated>2011-06-07T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2011/06/07/adding-images-to-iphone-simulator</id>
   
   <content type="html">&lt;p&gt;This post is based on this thread “&lt;a href=&quot;http://stackoverflow.com/questions/468879/adding-images-to-iphone-simulator/&quot;&gt;Adding images to iPhone Simulator&lt;/a&gt;” on &lt;a href=&quot;http://stackoverflow.com/&quot;&gt;Stack Overflow&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;http://stackoverflow.com/questions/468879/adding-images-to-iphone-simulator/2039856#2039856&quot;&gt;easiest answer&lt;/a&gt; which worked for me to add pictures in the Simulator Photo Gallery was:
&lt;!--more--&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1. Drag it to simulator, then Safari opens (or browse to the Image in the internet using Safari);
2. Click and hold on the image;
3. When the pop-up appears, choose Save Image and enjoy. ;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Answer by &lt;a href=&quot;http://stackoverflow.com/users/192800/thanh-cong-vo&quot;&gt;Thanh-Cong Vo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The answers suggesting to save the images in “~/Library/Application Support/iPhone Simulator/…”, didn’t work for me.&lt;/p&gt;

&lt;p&gt;I created this post specially because I needed to do these steps again, but I forgot how to do it, then I needed to find it again. :(&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Equal, equal ou equal, equal, equal</title>
   <link href="http://www.pablocantero.com//blog/2011/03/11/equal-equal-ou-equal-equal-equal/"/>
   <updated>2011-03-11T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2011/03/11/equal-equal-ou-equal-equal-equal</id>
   
   <content type="html">&lt;h2 id=&quot;é-igual-mas-é-diferente&quot;&gt;É igual, mas é diferente&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://pt.wikipedia.org/wiki/JavaScript&quot;&gt;JavaScript&lt;/a&gt; permite comparar a igualdade de valores usando ==, ===, !==, !===. Esses operadores podem ter um comportamento igual em muitos casos, porém possuem uma sutil diferença que pode gerar muita dor de cabeça.
&lt;!--more--&gt;&lt;/p&gt;

&lt;h2 id=&quot;equal-equal&quot;&gt;Equal, equal&lt;/h2&gt;

&lt;p&gt;Os operadores == e != fazem a comparação &lt;strong&gt;sem checagem de tipo&lt;/strong&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if(1 == 1){
  // Vai passar por aqui
}
if('1' == 1){
  // Vai passar por aqui
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;equal-equal-equal&quot;&gt;Equal, equal, equal&lt;/h2&gt;

&lt;p&gt;Os operadores === e !=== fazem a comparação &lt;strong&gt;com a checagem de tipo&lt;/strong&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if(1 === 1){
  // Vai passar por aqui
}
if('1' === 1){
  // Não vai passar por aqui
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;sutil-diferença&quot;&gt;Sutil diferença&lt;/h2&gt;

&lt;p&gt;Nos exemplos acima provavelmente não teremos nenhum problema, porém temos que levar em consideração algumas peculiaridades do JavaScript.&lt;/p&gt;

&lt;p&gt;No JavaScript uma string vazia ‘’ e o número 0 são considerados FALSE.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if(''){
  // Não vai passar por aqui
}
if(0){
  // Não vai passar por aqui
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Portanto se fizermos as seguintes comparações.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if('' == false){
  // Vai passar por aqui
}
if('0' == false){
  // Vai vai passar por aqui
}
if(null == undefined){
  // Vai vai passar por aqui
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Lembrando que null (sem valor) e undefined (não declarado ou não instanciado) não passam em ifs, mas não são considerados FALSE tanto usando == quanto ===.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if(null){
  // Não vai passar aqui
}
if(undefined){
  // Não vai passar aqui
}
// Mesmo assim não &amp;amp;quot;iguais&amp;amp;quot; a false
if(null == false || null === false){
  // Não vai passar aqui
}
if(undefined == false || undefined === false){
  // Não vai passar aqui
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Para evitarmos esse tipo de comportamento podemos fazer a comparação com checagem de tipo.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if('' === false){
  // Não vai passar por aqui
}
if('0' === false){
  // Não vai vai passar por aqui
}
if(null === undefined){
  // Não vai vai passar por aqui
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;conclusão&quot;&gt;Conclusão&lt;/h2&gt;

&lt;p&gt;Segundo Douglas Crockford’s o autor do livro &lt;a href=&quot;http://www.submarino.com.br/produto/1/21471189/melhor+do+javascript,+o&quot;&gt;O melhor do JavaScript&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;blockquote&gt;
    &lt;p&gt;JavaScript has two sets of equality operators: === and !==, and their evil twins == and !=.  The good ones work the way you would expect.  If the two operands are of the same type and have the same value, then === produces true and !== produces false.    The evil twins do the right thing when the operands are of the same   type, but if they are of different types, they attempt to coerce the   values.  the rules by which they do that are complicated and unmemorable&lt;/p&gt;
  &lt;/blockquote&gt;
&lt;/blockquote&gt;

&lt;p&gt;Eu particularmente uso equal equal, pois tive problemas com algum navegador, acredito que alguma versão do IE não reconhece equal equal equal.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Heroku + GitHub, ativar</title>
   <link href="http://www.pablocantero.com//blog/2011/02/11/heroku-github-ativar/"/>
   <updated>2011-02-11T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2011/02/11/heroku-github-ativar</id>
   
   <content type="html">&lt;p&gt;Esse post descreve como utilizar o Heroku e GitHub simultaneamente Quem se lembra do &lt;a href=&quot;http://pt.wikipedia.org/wiki/Superg%C3%AAmeos&quot;&gt;Supergêmeos&lt;/a&gt;, ativar?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.pablocantero.com/assets/images/posts/supergemeos-ativar.jpg&quot; alt=&quot;Supergêmeos, ativar!&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A utilização simultânea do Heroku e GitHub é dada graças ao Git, pois ele permite trabalhar com mais de um server remoto (remote) por repositório. Para isso, após criar um repositório com o Heroku, basta adicionar o GitHub como remote ou vice-versa.&lt;/p&gt;

&lt;h2 id=&quot;heroku--github-ativar&quot;&gt;Heroku + GitHub, ativar&lt;/h2&gt;

&lt;p&gt;Quando executamos o comando “heroku create”, a gem do Heroku simplesmente adiciona o Heroku como remote.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ git remote show heroku
* remote heroku
  Fetch URL: git@heroku.com:meu-projeto.git
  Push  URL: git@heroku.com:meu-projeto.git
  HEAD branch: master
  Remote branch:
    master tracked
  Local ref configured for 'git push':
    master pushes to master (up to date)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Para adicionar o remote do GitHub, basta executar o &lt;a href=&quot;http://www.kernel.org/pub/software/scm/git/docs/git-remote.html&quot;&gt;git-remote&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Adicionando o GitHub.
git remote add github git@github.com:phstc/meu-projeto.git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Na próxima fez que for executar o push basta informar o name do remote desejado.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Push no Heroku.
git push heroku master
# Push no GitHub.
git push github master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;remote-origin&quot;&gt;Remote origin&lt;/h2&gt;

&lt;p&gt;Se você adicionar um remote com name “origin”, você indicará ao Git que ele é o remote default. Com isso podemos omitir o name quando fizermos um push, pois o Git assumirá o “origin” como default.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Adicionando o GitHub como remote default.
git remote add origin git@github.com:phstc/meu-projeto.git
# Push no GitHub.
git push
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;principais-referências&quot;&gt;Principais referências&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.kernel.org/pub/software/scm/git/docs/&quot;&gt;Documentação oficial&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://pt.wikipedia.org/wiki/Git&quot;&gt;Página na Wikipedia sobre o Git&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>

 </entry>
 
 <entry>
   <title>Cola rápida para criar Cron Jobs</title>
   <link href="http://www.pablocantero.com//blog/2011/01/10/cola-rapida-para-criar-cron-jobs/"/>
   <updated>2011-01-10T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2011/01/10/cola-rapida-para-criar-cron-jobs</id>
   
   <content type="html">&lt;p&gt;As instruções abaixo são baseados no linux &lt;a href=&quot;http://en.wikipedia.org/wiki/Centos&quot;&gt;CentOS&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;motivação&quot;&gt;Motivação&lt;/h2&gt;

&lt;p&gt;A motivação para escrever esse post, foi para criar um Cron para ser executado de hora em hora no &lt;a href=&quot;http://heroku.com/&quot;&gt;Heroku&lt;/a&gt;, pois o &lt;a href=&quot;http://addons.heroku.com/cron&quot;&gt;Add-on Cron&lt;/a&gt; do Heroku só é gratuito para uma execução por dia, eu precisava executar de hora em hora.&lt;/p&gt;

&lt;p&gt;Para isso criei um Cron que invoca a minha url no Heroku de hora em hora.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#! /bin/sh&lt;/span&gt;
wget pablocantero.com/service
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;como-fazer-de-graça&quot;&gt;Como fazer de graça?&lt;/h2&gt;

&lt;p&gt;Existem serviços gratuitos na internet para isso, como o &lt;a href=&quot;http://www.mywebcron.com&quot;&gt;www.mywebcron.com&lt;/a&gt;, que podem ser uma opção interessante para invocar URLs de tempos em tempos.&lt;/p&gt;

&lt;h2 id=&quot;crontab&quot;&gt;Crontab&lt;/h2&gt;

&lt;p&gt;Para criar um Cron você pode utilizar o programa &lt;a href=&quot;http://pt.wikipedia.org/wiki/Crontab&quot;&gt;Crontab&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;crontab usage: crontab [-u user] file
 crontab [-u user] [ -e | -l | -r ]
 (default operation is replace, per 1003.2)
 -e  (edit user's crontab)
 -l  (list user's crontab)
 -r  (delete user's crontab)
 -i  (prompt before deleting user's crontab)
 -s  (selinux context)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;editando-com-o-vi&quot;&gt;Editando com o Vi&lt;/h2&gt;

&lt;p&gt;No CentOS por padrão ao usar o crontab -e, abrirá o &lt;a href=&quot;http://en.wikipedia.org/wiki/Nano_%28text_editor%29&quot;&gt;Nano&lt;/a&gt;, que não estou muito acostumado. Portanto prefiro editar com o &lt;a href=&quot;http://pt.wikipedia.org/wiki/Vi&quot;&gt;Vi&lt;/a&gt;, para isso basta abrir diretamente o arquivo Cron do usuário.&lt;/p&gt;

&lt;p&gt;O arquivo Cron do usuário fica no caminho /var/spool/cron/, no meu caso o root.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vi /var/spool/cron/root
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;**Diretórios auxiliares&lt;/p&gt;

&lt;p&gt;Qualquer script que esteja nos diretórios abaixo será executado de hora em hora, diariamente, semanalmente ou mensalmente, de acordo com o diretório.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;/etc/cron.hourly - De hora em hora&lt;/li&gt;
  &lt;li&gt;/etc/cron.daily - Diariamente&lt;/li&gt;
  &lt;li&gt;/etc/cron.weekly - Semanalmente&lt;/li&gt;
  &lt;li&gt;/etc/cron.monthly - Mensalmente&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Essa configuração fica no arquivo /etc/crontab.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

# run-parts
21 * * * * root run-parts /etc/cron.hourly
35 1 * * * root run-parts /etc/cron.daily
58 5 * * 0 root run-parts /etc/cron.weekly
23 2 18 * * root run-parts /etc/cron.monthly
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;parâmetros-para-criar-um-cron&quot;&gt;Parâmetros para criar um Cron&lt;/h2&gt;

&lt;p&gt;A configuração de um Cron é feita com o seguinte padrão:&lt;/p&gt;

&lt;p&gt;[minutos] [horas] [dias do mês] [mês] [dias da semana] [usuário] [comando]&lt;/p&gt;

&lt;p&gt;Sendo:&lt;/p&gt;

&lt;p&gt;Minuto: 0-59
Hora: 0-23
Dia do mês: 1-31
Mês: 1-12
Dia da semana: 0-6, onde domingo = 0, segunda = 1 etc&lt;/p&gt;

&lt;h2 id=&quot;exemplo-de-cron&quot;&gt;Exemplo de Cron&lt;/h2&gt;

&lt;p&gt;Para executar um script que recupera o &lt;a href=&quot;http://www1.caixa.gov.br/loterias/loterias/megasena/megasena_resultado.asp&quot;&gt;resultado da Mega Sena&lt;/a&gt; no site Caixa. Precisamos executar um script &lt;a href=&quot;http://en.wikipedia.org/wiki/Web_scraping&quot;&gt;Web scraping&lt;/a&gt;, das 18h até 22h, toda quarta-feira e sábado. Que é quando ocorre os sorteios da Mega Sena.&lt;/p&gt;

&lt;p&gt;Para isso teremos que adicionar a seguinte configuração.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vi /var/spool/cron/root
# No final do arquivo adicionar
59 17,18,19,20,21,22 * * 3,6 /meus_scripts/megasena_web_scraping.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;O megasena_web_scraping.sh será executado 17:59, 18:59, 19:59, 20:59, 21:59 e 22:50, toda quarta-feira = 3 e sábado = 6&lt;/p&gt;

&lt;p&gt;Seguindo o &lt;a href=&quot;http://pablocantero.com/blog/2011/01/10/cola-rapida-para-criar-cron-jobs/?preview=true&amp;amp;preview_id=933&amp;amp;preview_nonce=7b5bb8def6#comment-127304159&quot;&gt;comentário&lt;/a&gt; do &lt;a href=&quot;http://twitter.com/nadilsons&quot;&gt;@nadilsons&lt;/a&gt;, também possível gerenciar os intervalos com 17-22, simplificando o Cron acima.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;59 17-22 * * 3,6 /meus_scripts/megasena_web_scraping.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Lembre-se que é importante definir o parâmetro minuto, pois se deixar com *, o script será executado de minuto em minuto.&lt;/p&gt;

&lt;h2 id=&quot;executando-em-intervalo-de-minutos&quot;&gt;Executando em intervalo de minutos&lt;/h2&gt;

&lt;p&gt;Se somente o parâmetro de minuto estiver configurado, isso não fará que o script seja executado a cada minuto, na verdade ele será executado sempre no minuto configurado no intervalo de hora em hora.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1 * * * * /meus_scripts/script.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Portanto o script acima será executado sempre no minuto 1 de hora em hora. Para fazer com que esse script seja executado a cada um minuto, o intervalo de minutos precisa ser configurado da seguintes formas:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0-59 * * * * /meus_scripts/script.sh Ou

*/1 * * * * /meus_scripts/script.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Onde */1, indica o intervalo de 1 minuto.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>New Gem with Bundler - Sample Rakefile</title>
   <link href="http://www.pablocantero.com//blog/2011/01/02/new-gem-with-bundler-sample-rakefile/"/>
   <updated>2011-01-02T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2011/01/02/new-gem-with-bundler-sample-rakefile</id>
   
   <content type="html">&lt;p&gt;This post is an extension of the Ryan Bates post &lt;a href=&quot;http://railscasts.com/episodes/245-new-gem-with-bundler&quot;&gt;New Gem with Bundler&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The idea of this post is only to demonstrate a very useful Rakefile to deploy and publish a gem.&lt;/p&gt;

&lt;p&gt;If you want a step by step guide about the New Gem with Bundler, I recommend to check out the Ryan Bates &lt;a href=&quot;http://railscasts.com/episodes/245-new-gem-with-bundler&quot;&gt;Railscasts&lt;/a&gt; or &lt;a href=&quot;http://asciicasts.com/episodes/245-new-gem-with-bundler&quot;&gt;ASCIIcasts&lt;/a&gt;. :)&lt;/p&gt;

&lt;p&gt;The Rakefile demonstrated in this post is based on &lt;a href=&quot;https://github.com/jnunemaker/mongomapper/blob/master/Rakefile&quot;&gt;MongoMapper Rakefile&lt;/a&gt;. I used this Rakefile when I created &lt;a href=&quot;https://github.com/phstc/mongomapper_id2&quot;&gt;mongomapper_id2&lt;/a&gt;, It’s a MongoMapper’s plugin to add an auto incremented id to your MongoMapper’s documents.&lt;/p&gt;

&lt;h2 id=&quot;lets-go-show-me-the-code&quot;&gt;Let’s go, show me the code&lt;/h2&gt;

&lt;p&gt;Change the references of mongomapper_id2 to your gem’s name.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://gist.github.com/762965&quot;&gt;gist.github.com/762965&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;require 'rubygems'
require 'rake'
require 'rake/testtask'
require File.expand_path('../lib/mongomapper_id2/version', __FILE__)

Rake::TestTask.new(:test) do |test|
  test.libs &amp;lt;&amp;lt; 'lib' &amp;lt;&amp;lt; 'test'
  test.pattern = 'test/{functional,unit}/**/*_test.rb'
end

namespace :test do
  Rake::TestTask.new(:lint) do |test|
    test.libs &amp;lt;&amp;lt; 'lib' &amp;lt;&amp;lt; 'test'
    test.pattern = 'test/test_active_model_lint.rb'
  end

  task :all =&amp;gt; ['test', 'test:lint']
end

task :default =&amp;gt; 'test:all'

desc 'Builds the gem'
task :build do
  sh &quot;gem build mongomapper_id2.gemspec&quot;
end

desc 'Builds and installs the gem'
task :install =&amp;gt; :build do
  sh &quot;gem install mongomapper_id2-#{MongomapperId2::VERSION}&quot;
end

desc 'Tags version, pushes to remote, and pushes gem'
task :release =&amp;gt; :build do
  sh &quot;git tag v#{MongomapperId2::VERSION}&quot;
  sh &quot;git push origin master&quot;
  sh &quot;git push origin v#{MongomapperId2::VERSION}&quot;
  sh &quot;gem push mongomapper_id2-#{MongomapperId2::VERSION}.gem&quot;
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The purpose of the available tasks in this Rakefile are quite descriptive.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rake test # to test
rake build # to build
rake install # to install the gem locally
rake release # to release. explained bellow
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;##rake release&lt;/p&gt;

&lt;p&gt;For me, the most interesting task is the &lt;code class=&quot;highlighter-rouge&quot;&gt;rake release&lt;/code&gt;. This task creates a tag on GitHub with the version of your gem, push the locally committed files to the master on GitHub and publishes your gem on &lt;a href=&quot;http://rubygems.org&quot;&gt;RubyGems.org&lt;/a&gt;. Remember, commit your changes, before run this task.&lt;/p&gt;

&lt;h2 id=&quot;usage&quot;&gt;Usage&lt;/h2&gt;

&lt;p&gt;Create your gem, run your tests.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rake test
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Build and install locally your gem.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rake build
rake install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Test your gem.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# test.rb
require 'my-gem-name'
# code to execute your gem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If everything goes right… Publish your gem.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git commit -a -m 'My commit'
rake release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>

 </entry>
 
 <entry>
   <title>jQuery Twitter Search Plugin</title>
   <link href="http://www.pablocantero.com//blog/2010/12/29/jquery-twitter-search-plugin/"/>
   <updated>2010-12-29T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/12/29/jquery-twitter-search-plugin</id>
   
   <content type="html">&lt;p&gt;It’s a jQuery plugin only for tweets search. Where you need to implement the layout to display the tweets. Actually, it means that you are free to use your own layout. ;)
&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Check out on GitHub to see the full documentation &lt;a href=&quot;https://github.com/phstc/jquery-twitter&quot;&gt;github.com/phstc/jquery-twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;##Motivation&lt;/p&gt;

&lt;p&gt;I worked in a project where I needed to display the last tweets messages containing reference to a website.&lt;/p&gt;

&lt;p&gt;I found many jQuery Twitter Plugins, but most of them already worked with searching and displaying the tweets. In the project I mentioned the layout to display the tweets was specific. So I couldn’t use the plugins’ layout.&lt;/p&gt;

&lt;p&gt;##What’s the difference&lt;/p&gt;

&lt;p&gt;The available plugins on the internet use an ugly syntax to search.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$.twitter.search({
   from: 'pablocantero',
   text: 'hello+world',
   container: 'myDivId'
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Twitter Search Plugin is DSL based.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;var tweets = new Twitter.tweets();
tweets.containing('hello')
   .and('world')
   .from('pablocantero')
   .limit(4)
   .order('recent')
   .all(function(data){
      alert('It’s a customized function to display the tweets');
   });
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;##Download it&lt;/p&gt;

&lt;p&gt;The jQuery Twitter Search Plugin is available on GitHub &lt;a href=&quot;https://github.com/phstc/jquery-twitter&quot;&gt;github.com/phstc/jquery-twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;##Example&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://gist.github.com/758666&quot;&gt;gist.github.com/758666&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;##Do you want to improve this JavaScript?&lt;/p&gt;

&lt;p&gt;The project is available on github as an open source project &lt;a href=&quot;https://github.com/phstc/jquery-twitter&quot;&gt;github.com/phstc/jquery-twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You’re welcome to make your contributions and send them as a pull request.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href=&quot;http://twitter.com/#!/rdgr&quot;&gt;@rdgr&lt;/a&gt;.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>isFollowing Bookmarklet - Who is following who?</title>
   <link href="http://www.pablocantero.com//blog/2010/12/20/isfollowing-bookmarklet-who-is-following-who/"/>
   <updated>2010-12-20T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/12/20/isfollowing-bookmarklet-who-is-following-who</id>
   
   <content type="html">&lt;p&gt;Have you ever wanted to check if a twitter profile is following another twitter profile, this bookmarklet can help you.
&lt;!--more--&gt;
isFollowing Bookmarklet – it’s a very simple JavaScript created to check if screen name (b) is following screen name (a).&lt;/p&gt;

&lt;p&gt;##How to use it?&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Click and drag &lt;a href=&quot;javascript:(function(){/*@author Pablo Cantero - http://pablocantero.com/blog/2010/12/20/isfollowing-bookmarklet-who-is-following-who*/var isFollowing = function(twitterScreenNameA, twitterScreenNameB){jQuery.getJSON('http://twitter.com/statuses/followers.json?screen_name=' + twitterScreenNameA + '&amp;amp;callback=?', function(data){for(var i = 0; i &amp;lt; data.length; i++){if(data[i].screen_name === twitterScreenNameB.replace('@', '')){alert(twitterScreenNameB + ' is following ' + twitterScreenNameA);return;}}alert(twitterScreenNameB + ' is not following ' + twitterScreenNameA);});};var isFollowingPrompt = function(){var twitterScreenNameA = window.prompt('input the twitter screen name (a)', jQuery('.screen-name').text());var twitterScreenNameB = window.prompt('input the twitter screen name (b)');if(twitterScreenNameA === null || twitterScreenNameB === null){alert('you must provide the screen names (a) and (b)');} else {isFollowing(twitterScreenNameA, twitterScreenNameB);isFollowing(twitterScreenNameB, twitterScreenNameA);}};if(typeof jQuery === 'undefined') {var jQueryScript = document.createElement('script');jQueryScript.setAttribute('src','http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js');jQueryScript.setAttribute('type','text/javascript');jQueryScript.onreadystatechange= function () {if (this.readyState === 'complete' || this.readyState === 'loaded'){isFollowingPrompt();}};document.getElementsByTagName('head')[0].appendChild(jQueryScript);} else {isFollowingPrompt();}})()&quot; title=&quot;it’s a very simple JavaScript created to check if screen name (b) is following screen name (a)&quot;&gt;isFollowing&lt;/a&gt; to your bookmark bar in the browser;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Click in isFollowing in your bookmark bar;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Fill the screen name (a);&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Fill the screen name (b).&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;##The JavaScript source code&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://gist.github.com/744291&quot;&gt;gist.github.com/744291&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;##Do you want to improve this JavaScript?&lt;/p&gt;

&lt;p&gt;The project is available on github as an open source project &lt;a href=&quot;https://gist.github.com/744291&quot;&gt;gist.github.com/744291&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You’re welcome to make your contributions and send them as a pull request.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Scroll it - A Javascript bookmarklet to automatically scroll the page</title>
   <link href="http://www.pablocantero.com//blog/2010/12/09/scroll-it-a-javascript-bookmarklet-to-automatically-scroll-the-page/"/>
   <updated>2010-12-09T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/12/09/scroll-it-a-javascript-bookmarklet-to-automatically-scroll-the-page</id>
   
   <content type="html">&lt;p&gt;Have you ever had trouble when trying to play a song in your guitar when reading the chords from a web site? Don’t you always need to stop playing to scroll the page?&lt;/p&gt;

&lt;p&gt;Scroll It – it’s a very simple JavaScript created to solve this kind of problem! It makes automatic scrolls in the page in a very comfortable speed.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;##How to use it?&lt;/p&gt;

&lt;p&gt;&lt;del&gt;&lt;/del&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Click and drag Scroll it to your bookmark bar in the browser;
&amp;lt;/del&amp;gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Unfortunately I needed to remove the Bookmarklet link. The Markdown compiler didn’t worked with it.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Go to the site that your want to use Scroll it;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Click on Scroll It in your bookmark bar. That’s all.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To stop the automatic scroll, you only need to click again on Scroll it in your bookmark bar. ;)&lt;/p&gt;

&lt;p&gt;##The JavaScript source code&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;javascript:(function(){
	/* @author Pablo Cantero http://pablocantero.com */
	if(window.scrollIt === undefined){
		window.scrollIt = {
			intervalId: null
		};
	}
	function getScrollMaxSize(){
		return document.body.clientHeight;
	};
	function getScrollCurrentSize(){
		return document.documentElement.scrollTop;
	};
	window.scrollIt.initialSize = getScrollCurrentSize();
	window.scrollIt.scrolled = getScrollCurrentSize();
	var scroll = function(){
		window.scrollIt.scrolled += 10;
		window.scrollTo(0, window.scrollIt.scrolled);
		if(window.scrollIt.scrolled &amp;amp;amp;gt;= getScrollMaxSize()){
			window.scrollIt.scrolled = window.scrollIt.initialSize;
		}
	};
	var stop = function(){
		window.clearInterval(window.scrollIt.intervalId);
		window.scrollIt.intervalId = null;
	};
	var start = function(){
		window.scrollIt.intervalId = window.setInterval(scroll, 1000);
	};
	if(window.scrollIt.intervalId != null){
		stop();
	} else {
		start();
	};
})()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;##Do you want to improve this JavaScript?&lt;/p&gt;

&lt;p&gt;The project is available on github as an open source project.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/phstc/scroll-it&quot;&gt;github.com/phstc/scroll-it&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’re welcome to make your contributions and send them as a pull request.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href=&quot;http://twitter.com/#!/rdgr&quot;&gt;@rdgr&lt;/a&gt;.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Anotações TODO, FIXME, OPTIMIZE com Rails usando o rake:notes</title>
   <link href="http://www.pablocantero.com//blog/2010/11/18/anotacoes-todo-fixme-optimize-com-rails-usando-o-rakenotes/"/>
   <updated>2010-11-18T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/11/18/anotacoes-todo-fixme-optimize-com-rails-usando-o-rakenotes</id>
   
   <content type="html">&lt;p&gt;Comentários do tipo TODO são bastante úteis para deixar pontos de atenção no seu código.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# TODO Inserir validação caso o input seja nulo.
def some_method(input)
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Algumas IDEs como Eclipse e Netbeans possuem uma view “Tasks”, que exibe estes comentários.&lt;/p&gt;

&lt;p&gt;Com Rails a visualização dessas comentários pode ser feita por linha de comando usando &lt;code class=&quot;highlighter-rouge&quot;&gt;rake:notes&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;todo-fixme-optimize&quot;&gt;TODO, FIXME, OPTIMIZE&lt;/h2&gt;

&lt;p&gt;Rails além de comentário do tipo TODO, possui o FIXME e OPTIMIZE.&lt;/p&gt;

&lt;p&gt;Para visualizar os comentários:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Exibe listagem com todos os comentários TODO, FIXME e OPTIMIZE.
rake notes
# Filtra por comentários TODO.
rake notes:todo
# Filtra por comentários FIXME.
rake notes:fixme
# Filtra por comentários OPTIMIZE.
rake notes:optimize
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;exemplo-real&quot;&gt;Exemplo real&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# app/models/article.rb
class Article &amp;lt; ActiveRecord::Base

 # TODO add named_scopes
 def some_method1
   # ...
 end

 # FIXME method A is broken
 def method_a
   # ...
 end

 # OPTIMIZE improve the code
 def some_method2
   # ...
 end
end

$ rake notes
app/models/article.rb:
  * [4] [TODO] add named_scopes
  * [9] [FIXME] method A is broken
  * [14] [OPTIMIZE] improve the code
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Os valores 4, 9 e 14 são as linhas onde estão os respectivos comentários.&lt;/p&gt;

&lt;h2 id=&quot;custom&quot;&gt;CUSTOM&lt;/h2&gt;

&lt;p&gt;Existe uma opção muito interessante que é o &lt;code class=&quot;highlighter-rouge&quot;&gt;rake notes:custom&lt;/code&gt;, que permite filtrar por comentários específicos.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Filtra por comentários COLL
rake notes:custom ANNOTATION=COOL
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;qual-usar&quot;&gt;Qual usar?&lt;/h2&gt;

&lt;p&gt;Eu particularmente acho confuso ter várias tipos de anotações. Acho que TODO é bem genérico e atende muito bem como lembrete. Lembretes que devem ser vistos em algum momento, como nas folgas dos projetos e não serem usados apenas para aliviar a consciência.&lt;/p&gt;

&lt;h2 id=&quot;principais-referências&quot;&gt;Principais referências&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://rubyquicktips.tumblr.com/post/385665023/fixme-todo-and-optimize-code-comments&quot;&gt;rubyquicktips.tumblr.com/post/385665023/fixme-todo-and-optimize-code-comments&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.rubyyot.com/2009/05/gtd-on-rails-with-annotations&quot;&gt;blog.rubyyot.com/2009/05/gtd-on-rails-with-annotations/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://ryandaigle.com/archives/2007/2&quot;&gt;ryandaigle.com/archives/2007/2&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://rbjl.net/13-organise-your-code-comments-with-rake-notes-todo&quot;&gt;rbjl.net/13-organise-your-code-comments-with-rake-notes-todo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>

 </entry>
 
 <entry>
   <title>Configurando o Meta description no WordPress</title>
   <link href="http://www.pablocantero.com//blog/2010/10/23/configurando-o-meta-description-no-wordpress/"/>
   <updated>2010-10-23T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/10/23/configurando-o-meta-description-no-wordpress</id>
   
   <content type="html">&lt;p&gt;Nem sei mais o quão importante são as tags de Meta description para indexação de conteúdo hoje em dia. Já vi sites bem posicionados que não usavam tags Meta description.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Porém no &lt;a href=&quot;http://www.google.com/webmasters/tools/&quot;&gt;Webmaster Tools&lt;/a&gt; na seção “Google em Diagnostics -&amp;gt; HTML suggestions” o primeiro item e sobre Meta description.&lt;/p&gt;

&lt;p&gt;No meu caso todas as páginas estavam com a mesma tag Meta description.&lt;/p&gt;

&lt;p&gt;Para resolver esse problema fiz uma pequena alteração no meu template através dos seguintes passos:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Acesse “WordPress Admin -&amp;gt; Aparência -&amp;gt; Editor”.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Selecione o template desejado para fazer a alteração.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Selecione o cabeçalho (header.php) no menu da direita.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Comente o atual Meta description se existir.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;?php
/*
&amp;lt;Meta name=&quot;description&quot; content=&quot;&amp;lt;?php bloginfo('description'); ?&amp;gt;&quot; /&amp;gt;
*/
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Insira o código abaixo para gerar o Meta description baseada na página (post, tag, categoria ou página).&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;?php
function get_page_meta_description(){
	$custom_field_meta_description = get_post_meta(get_the_ID(), 'meta_description_field', true);
	if($custom_field_meta_description != ''){
		return $custom_field_meta_description;
	} elseif ( is_single() ) {
		return get_the_title();
	} elseif ( is_category() ) {
		return get_single_cat_title();
	/*
	} elseif ( is_tag() ) {
		return get_single_tag_title(); get single tag não está funcionando
	*/
	} elseif ( is_page() ){
		return get_the_title();
	} else {
		return get_bloginfo('description');
	}
}
?&amp;gt;
&amp;lt;meta name=&quot;description&quot; content=&quot;&amp;lt;?php echo get_page_meta_description(); ?&amp;gt;&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A opção “meta_description_field” serve para definir uma Meta description especifica através um campo personalizado.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/Screen-shot-2010-10-23-at-2.11.07-PM.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>LazyInitializationException? Dá-lhe OpenEntityManagerInViewFilter</title>
   <link href="http://www.pablocantero.com//blog/2010/10/22/lazyinitializationexception-da-lhe-openentitymanagerinviewfilter/"/>
   <updated>2010-10-22T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/10/22/lazyinitializationexception-da-lhe-openentitymanagerinviewfilter</id>
   
   <content type="html">&lt;p&gt;Quem nunca recebeu o “gratificante” erro LazyInitializationException?&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;org.hibernate.LazyInitializationException: failed to lazily initialize&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Para resolvê-lo basta adicionar o filtro &lt;a href=&quot;http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/orm/jpa/support/OpenEntityManagerInViewFilter.html&quot;&gt;OpenEntityManagerInViewFilter&lt;/a&gt; no web.xml, que manterá o JPA EntityManager aberto por todo o request permitindo o lazy loading a partir da view.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;!-- web.xml --&amp;gt;
&amp;lt;filter&amp;gt;
    &amp;lt;filter-name&amp;gt;Spring OpenEntityManagerInViewFilter&amp;lt;/filter-name&amp;gt;
    &amp;lt;filter-class&amp;gt;org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter&amp;lt;/filter-class&amp;gt;
    &amp;lt;init-param&amp;gt;
        &amp;lt;param-name&amp;gt;singleSession&amp;lt;/param-name&amp;gt;
        &amp;lt;param-value&amp;gt;true&amp;lt;/param-value&amp;gt;
    &amp;lt;/init-param&amp;gt;
&amp;lt;/filter&amp;gt;
&amp;lt;filter-mapping&amp;gt;
    &amp;lt;filter-name&amp;gt;Spring OpenEntityManagerInViewFilter&amp;lt;/filter-name&amp;gt;
    &amp;lt;url-pattern&amp;gt;/*&amp;lt;/url-pattern&amp;gt;
&amp;lt;/filter-mapping&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>

 </entry>
 
 <entry>
   <title>GitHub - Múltiplos forks para o mesmo repositório</title>
   <link href="http://www.pablocantero.com//blog/2010/10/20/github-multiplos-forks-para-o-mesmo-repositorio/"/>
   <updated>2010-10-20T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/10/20/github-multiplos-forks-para-o-mesmo-repositorio</id>
   
   <content type="html">&lt;p&gt;No GitHub não é possível fazer vários forks de um mesmo projeto.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;github.com/user/projetoa
github.com/user/projetoa(2)
github.com/user/projetoa(3)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Uma forma de simular isso é com branches.&lt;/p&gt;

&lt;p&gt;##Motivação&lt;/p&gt;

&lt;p&gt;Fiz um &lt;a href=&quot;http://help.github.com/forking/&quot;&gt;fork&lt;/a&gt; de um projeto onde fiz muitas alterações, algumas eram interessantes para um &lt;a href=&quot;http://help.github.com/pull-requests/&quot;&gt;pull request&lt;/a&gt; e outras eram interessantes só para mim.&lt;/p&gt;

&lt;p&gt;Pensei em fazer um segundo fork e subir as alterações para o pull request nesse novo fork. Porém isso não é possível.&lt;/p&gt;

&lt;p&gt;##Solução&lt;/p&gt;

&lt;p&gt;Para solucionar criei um novo branch “original-nome-do-projeto” no meu fork com HEAD do projeto original.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cd workspace/gesture-translate/gtranslate
$ git remote add upstream http://github.com/bpierre/gtranslate.git
$ git fetch upstream
$ git branch original-gtranslate upstream/master
$ git checkout original-gtranslate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Fiz as alterações no branch para enviar como pull request.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ git commit -a -m 'Mudanças relevantes para o projeto original';
$ git push origin original-gtranslate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Na página do GitHub mudei do branch master para o branch original-gtranslate.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/Screen-shot-2010-10-20-at-1.44.03-AM.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Fiz um pull request das mudanças feitas no branch original-gtranslate.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/Screen-shot-2010-10-20-at-1.45.19-AM.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Essa solução foi baseada em um &lt;a href=&quot;http://support.github.com/discussions/repos/2420-multiple-forks#comment_958975&quot;&gt;comentário&lt;/a&gt; do support staff do GitHub.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>GAE - Primeiras impressões - Envio de e-mail</title>
   <link href="http://www.pablocantero.com//blog/2010/10/03/gae-primeiras-impressoes-envio-de-e-mail/"/>
   <updated>2010-10-03T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/10/03/gae-primeiras-impressoes-envio-de-e-mail</id>
   
   <content type="html">Esse post foi baseado em um e-mail que eu enviei para uma lista de e-mail que faço parte, graças a esse e-mail que eu me motivei para criar esse blog, mas só agora depois de vários posts que resolvi publica-lo

Meus primeiros instantes com o &lt;a href=&quot;http://code.google.com/appengine/&quot;&gt;GAE&lt;/a&gt; foram emocionantes, eu estava ali com um &lt;a href=&quot;http://en.wikipedia.org/wiki/Platform_as_a_service&quot;&gt;PaaS&lt;/a&gt; na mão, com o poder de aumentar recursos da minha plataforma com apenas alguns cliques e um cartão de crédito :D

Lembrando que ao adicionar recursos na plataforma do GAE, vc adiciona um pagar até...

&lt;a rel=&quot;attachment wp-att-505&quot; href=&quot;http://pablocantero.com/blog/2010/10/03/gae-primeiras-impressoes-envio-de-e-mail/screen-shot-2010-10-03-at-10-41-00-pm/&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-505&quot; title=&quot;Screen shot 2010-10-03 at 10.41.00 PM&quot; src=&quot;http://pablocantero.com/blog/wp-content/uploads/2010/10/Screen-shot-2010-10-03-at-10.41.00-PM.png&quot; alt=&quot;&quot; width=&quot;557&quot; height=&quot;158&quot; /&gt;&lt;/a&gt;
Se você adicionar 10 dólares de CPU hour, não quer dizer que vai pagar 10 dólares, e sim, que pagará até no máximo 10 dólares, somente se o recurso for consumido. Sendo que as quotas são renovadas diariamente. Fantástico, né?!

&lt;strong&gt;Google Plugin for Eclipse&lt;/strong&gt;

Para os primeiros passos com o GAE, o &lt;a href=&quot;http://code.google.com/eclipse/&quot;&gt;Google Plugin for Eclipse&lt;/a&gt; é altamente recomendado, ele é uma forma rápida e fácil de criar projetos, testar localmente e fazer deploys em produção

&lt;strong&gt;Meu primeiro projeto&lt;/strong&gt;

Depois de uma serie de projetos de &lt;span style=&quot;text-decoration: line-through;&quot;&gt;brincadeirinha&lt;/span&gt; prova de conceito, Hello World, &lt;a href=&quot;http://pt.wikipedia.org/wiki/CRUD&quot;&gt;CRUD&lt;/a&gt; Básico etc. Achei um amigo que tinha uma necessidade de enviar Clipping Diário de Notícias, que consistia em um cadastro de de contatos e envio de e-mail diário com um pdf em anexo (&lt;em&gt;upload via &lt;a href=&quot;http://code.google.com/appengine/docs/java/blobstore/overview.html&quot;&gt;BlobStore&lt;/a&gt;&lt;/em&gt;)

&lt;strong&gt;Péssima idéia&lt;/strong&gt;

O GAE não é recomendado para sistemas de envio de e-mail em massa devido algumas restrições da plataforma, como quantidade de e-mail por minuto, tamanho de anexo no e-mails etc. Mas só fui descobrir após experimentar

&lt;strong&gt;Restrição do Sender
&lt;/strong&gt;

O GAE tem algumas restrições para o remetente do e-mail, que precisa ser um e-mail de um dos administradores da aplicação ou o usuário corrente logado. Se você tentar enviar um e-mail pelo GAE com um remetente do tipo contato@suaempresa.com.br, noreply@suaempresa.com.br, clipping@suaempresa.com.br etc. Esses e-mails deverão ser os mesmos usados pelo usuário corrente logado ou estarem cadastrados como administradores da aplicação. Em nenhuma das situação é comum ter um contato@suaempresa.com.br, como usuário logado ou admintrador, né?!

Descrição do sender na Low-level API para envio de e-mail&lt;a href=&quot;http://code.google.com/appengine/docs/java/javadoc/com/google/appengine/api/mail/MailService.Message.html#setSender%28java.lang.String%29&quot; target=&quot;_blank&quot;&gt;
http://code.google.com/appengine/docs/java/javadoc/com/google/appengine/api/mail/MailService.Message.html#setSender%28java.lang.String%29&lt;/a&gt;
&lt;blockquote&gt;&lt;strong&gt;Sender&lt;/strong&gt;
must correspond to the valid email address of one of  the admins for this application, or to the email address of the  currently logged-in user. Sender is really the From: field of the  email.&lt;/blockquote&gt;
Na hora que li isso, pensei: vou usar a API do Java Mail apontando para o meu servidor SMTP, assim tenho total controle sobre o envio de e-mail
&lt;div&gt;&lt;a href=&quot;http://code.google.com/appengine/docs/java/mail/usingjavamail.html&quot; target=&quot;_blank&quot;&gt;http://code.google.com/appengine/docs/java/mail/usingjavamail.html&lt;/a&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;strong&gt;An app cannot use the JavaMail interface to connect to other mail  services for sending or receiving email messages.  SMTP configuration  added to the Transport or Session is ignored. &lt;/strong&gt;&lt;/div&gt;&lt;/blockquote&gt;
Para mandar e-mail pelo GAE você só pode usar o serviço de envio de e-mail do GAE. Isso não impede de enviar e-mail usado outros serviços por outros meios Web Services, REST, requisição http etc,  e esses outros meios delegarem para outros serviços de envio de e-mail

Lembrando que se cadastrar contato@suaempresa.com.br como adminstrador, esse e-mail terá privilégios de acessar a administração do GAE e inclusive adicionar recursos ($$$) na plataforma

&lt;strong&gt;Restrição de envio de e-mails por minuto&lt;/strong&gt;

Na conta free do GAE, vc tem uma restrição de 1 e-mail (&lt;em&gt;leia-se um destinatário&lt;/em&gt;) por minuto, quando  vc ativa a versão paga, muda para 5200, tudo aumenta, processamento etc,  mas sem custo, o custo só acontece depois que vc excede o que ganhou a  mais, só por habilitar a opção paga

&lt;strong&gt;Acoplamento com GAE&lt;/strong&gt;

É quase inevitável não acoplar seu  sistema com o GAE, devido uma serie &lt;a href=&quot;http://groups.google.com/group/google-appengine-java/web/will-it-play-in-app-engine?pli=1&quot;&gt;Not supported APIs do GAE&lt;/a&gt;, e a facilidade para usar alguns recursos do GAE usando bibliotecas do Google.  Migrar uma aplicação do GAE para outro container ou application server, muito provavelmente será uma tarefa árdua

&lt;strong&gt;Ambiente de desenvolvimento vs Produção&lt;/strong&gt;

Talvez isso já esteja melhor, mas quando eu testei com Google Plugin for Eclipse, funcionar em desenvolvimento não garante que funcionará em produção. Mais de uma vez, testei em desenvolvimento, estava funcionando, quando eu fazia o deploy em produção não funcionava

&lt;strong&gt;Datastore Indexes&lt;/strong&gt;

Um coisa bem legal é o &lt;a href=&quot;http://code.google.com/appengine/docs/java/config/indexconfig.html&quot;&gt;Datastore Index&lt;/a&gt; que pode ser configurada para ser gerada automaticamente em desenvolvimento. Habilitando a opção datastore-indexes autoGenerate=&quot;true&quot; no datastore-indexes.xml

[xml]
&amp;lt;datastore-indexes autoGenerate=&amp;quot;true&amp;quot;&amp;gt;
[/xml]

Conforme vc for usando suas entidades no seu ambiente de desenvolvimento, ele vai automaticamente criando indexes para suas entidades com base na utilização. Muito bom!
&lt;div&gt;O lado ruim e que as vezes o GAE fica horas para gerar os indexes em  produção, e só após a geração desses indexes as entidades ficam disponíveis&lt;/div&gt;
&lt;div&gt;&lt;a rel=&quot;attachment wp-att-507&quot; href=&quot;http://pablocantero.com/blog/2010/10/03/gae-primeiras-impressoes-envio-de-e-mail/screen-shot-2010-10-03-at-11-09-30-pm/&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-507&quot; title=&quot;Screen shot 2010-10-03 at 11.09.30 PM&quot; src=&quot;http://pablocantero.com/blog/wp-content/uploads/2010/10/Screen-shot-2010-10-03-at-11.09.30-PM.png&quot; alt=&quot;&quot; width=&quot;553&quot; height=&quot;124&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;

&lt;strong&gt;BigTable&lt;/strong&gt;

Com o  &lt;a href=&quot;http://pt.wikipedia.org/wiki/BigTable&quot;&gt;BigTable&lt;/a&gt; estou usando &lt;a href=&quot;http://code.google.com/appengine/docs/java/gettingstarted/usingdatastore.html&quot;&gt;JDO&lt;/a&gt; (&lt;em&gt;achei que os tutorias do GAE incentivam mais para usar JDO que JPA&lt;/em&gt;). Devido o modelo não relacional do BigTable, perdi um pouco das facilidades do ORM

MailModel tem uma lista ContactModel

Como ContactModel não é filho de MailModel (&lt;em&gt;owned relationship&lt;/em&gt;), para fazer esse relacionamento tive que fazer

[java]
List&amp;lt;Long&amp;gt; contactsId;
[/java]

&lt;/div&gt;
ContactModel é uma unowned, ele tem uma PrimaryKey Long e não uma Key (&lt;em&gt;com.google.appengine.api.datastore.Key&lt;/em&gt;). Para mais detalhes &lt;a href=&quot;http://code.google.com/appengine/docs/java/datastore/dataclasses.html&quot;&gt;http://code.google.com/appengine/docs/java/datastore/dataclasses.html&lt;/a&gt;

Portanto no meu Service, após fazer o load de qualquer MailModel,  tenho que carregar os ContactModel manualmente com base nos contactsId (&lt;em&gt;tchau lazy load, olá eager load&lt;/em&gt;?!)

&lt;strong&gt;Conclusões&lt;/strong&gt;

Apesar de eu talvez ter enfatizado um lado restritivo do GAE, ainda é uma ótima plataforma, claro, com algumas restrições. Tem que levar em consideração o que você quer desenvolver, para um sistema de envio de e-mail não é bom, mas para outras coisas, pode ser

No post &lt;a href=&quot;http://pablocantero.com/blog/2010/08/20/gae-eclipse-maven/&quot;&gt;GAE + Eclipse + Maven&lt;/a&gt;, demonstro como usar o GAE diretamente com Maven que pode ser mais produtivo posteriormente que com somente o Google Plugin for Eclipse

&lt;strong&gt;Spring + GAE&lt;/strong&gt;

Para usar o Spring MVC com o GAE tem algumas restrições de bibliotecas, baixar o archetype do Spring MVC não funcionará diretamente. Criei um projeto &lt;a href=&quot;http://github.com/phstc/spring-gae&quot;&gt;http://github.com/phstc/spring-gae&lt;/a&gt; no github, com uma aplicação com Maven + Google Plugin for Eclipse + Spring MVC 3 pronta para uso! :D
</content>

 </entry>
 
 <entry>
   <title>Upload com Spring MVC</title>
   <link href="http://www.pablocantero.com//blog/2010/09/29/upload-com-spring-mvc/"/>
   <updated>2010-09-29T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/09/29/upload-com-spring-mvc</id>
   
   <content type="html">&lt;p&gt;Esse post é um tutorial passo a passo para fazer upload com Spring MVC.&lt;/p&gt;

&lt;p&gt;Os passos desdes posts foram feitos com o projeto template “Spring MVC Project” do &lt;a href=&quot;http://pablocantero.com/blog/2010/08/27/springsource-tools-suite-e-nao-e-que-e-bom/&quot;&gt;SpringSource Tool Suite&lt;/a&gt;, a versão do Spring MVC utilizada foi a 3.0.1.RELEASE.&lt;/p&gt;

&lt;h2 id=&quot;primeiro-passo&quot;&gt;Primeiro passo&lt;/h2&gt;

&lt;p&gt;Adicionar as dependências commons-fileupload e commons-io no pom.xml.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;commons-fileupload.version&amp;gt;&lt;/span&gt;1.2.1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/commons-fileupload.version&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;commons-io.version&amp;gt;&lt;/span&gt;1.4&lt;span class=&quot;nt&quot;&gt;&amp;lt;/commons-io.version&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;commons-fileupload&lt;span class=&quot;nt&quot;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;commons-fileupload&lt;span class=&quot;nt&quot;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${commons-fileupload.version}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;commons-io&lt;span class=&quot;nt&quot;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;commons-io&lt;span class=&quot;nt&quot;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${commons-io.version}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;segundo-passo&quot;&gt;Segundo passo&lt;/h2&gt;

&lt;p&gt;Adicionar o CommonsMultipartResolver no mvc-config.xml. Graças a ele será possível fazer o cast (MultipartHttpServletRequest) request&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;multipartResolver&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;org.springframework.web.multipart.commons.CommonsMultipartResolver&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- one of the properties available; the maximum file size in bytes --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;property&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;maxUploadSize&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1000000&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/bean&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;terceiro-passo&quot;&gt;Terceiro passo&lt;/h2&gt;

&lt;p&gt;Criar o formulário multipart/form-data  para upload.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/meu-projeto/upload&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;post&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;enctype=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;multipart/form-data&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;label&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;for=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;file&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Arquivo para fazer upload&lt;span class=&quot;nt&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;file&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;file&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Upload&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;quarto-é-último-passo&quot;&gt;Quarto é último passo&lt;/h2&gt;

&lt;p&gt;Criar a action de upload.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@RequestMapping&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;upload&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RequestMethod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;upload&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpServletRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;MultipartHttpServletRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;multipartRequest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MultipartHttpServletRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;MultipartFile&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;multipartFile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;multipartRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;file&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;redirect:upload-success&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>

 </entry>
 
 <entry>
   <title>Ler excel com Java usando o JExcelApi</title>
   <link href="http://www.pablocantero.com//blog/2010/09/29/ler-excel-com-java-usando-o-jexcelapi/"/>
   <updated>2010-09-29T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/09/29/ler-excel-com-java-usando-o-jexcelapi</id>
   
   <content type="html">&lt;p&gt;Esse post é uma continuação do post &lt;a href=&quot;http://pablocantero.com/blog/2010/09/29/upload-com-spring-mvc&quot;&gt;Upload com Spring MVC&lt;/a&gt;, pois além do upload eu precisava ler o Excel e adicionar as informações no Banco de Dados.&lt;/p&gt;

&lt;p&gt;Para ler Excel estou usando o &lt;a href=&quot;http://jexcelapi.sourceforge.net/&quot;&gt;JExcelApi&lt;/a&gt;. Outra opção seria o &lt;a href=&quot;http://poi.apache.org/&quot;&gt;Apache POI&lt;/a&gt;, que é mais popular.&lt;/p&gt;

&lt;h2 id=&quot;primeiro-passo&quot;&gt;Primeiro passo&lt;/h2&gt;

&lt;p&gt;Adicionar a dependência JExcelApi no  &lt;code class=&quot;highlighter-rouge&quot;&gt;pom.xml&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;jexcelapi.version&amp;gt;&lt;/span&gt;2.6&lt;span class=&quot;nt&quot;&gt;&amp;lt;/jexcelapi.version&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;net.sourceforge.jexcelapi&lt;span class=&quot;nt&quot;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jxl&lt;span class=&quot;nt&quot;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${jexcelapi.version}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;segundo-passo&quot;&gt;Segundo passo&lt;/h2&gt;

&lt;p&gt;Escrever o código para ler o Excel. Estou usando a action de upload do artigo &lt;a href=&quot;http://pablocantero.com/blog/2010/09/29/upload-com-spring-mvc/&quot;&gt;Upload com Spring MVC&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@RequestMapping&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;upload&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RequestMethod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;upload&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpServletRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BiffException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;MultipartHttpServletRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;multipartRequest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MultipartHttpServletRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;MultipartFile&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;multipartFile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;multipartRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;file&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;Workbook&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;workbook&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Workbook&lt;/span&gt;
  	&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getWorkbook&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;multipartFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getInputStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;Sheet&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sheet&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;workbook&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSheet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Nome da planilha&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sheet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getRows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sheet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getColumns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++){&lt;/span&gt;
  		&lt;span class=&quot;n&quot;&gt;Cell&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;celulaJ&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sheet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getCell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
  		&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;
  			&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Conteúdo da célula &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;celularJ&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getContents&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
  	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;redirect:upload-success&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;principais-referências&quot;&gt;Principais referências&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.oficinadanet.com.br/artigo/java/lendo_planilhas_excel_com_java&quot;&gt;oficinadanet.com.br/artigo/java/lendo_planilhas_excel_com_java&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>

 </entry>
 
 <entry>
   <title>Programando na Espanha</title>
   <link href="http://www.pablocantero.com//blog/2010/09/15/programando-na-espanha/"/>
   <updated>2010-09-15T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/09/15/programando-na-espanha</id>
   
   <content type="html">&lt;p&gt;Pensando em ir para trabalhar na Espanha como programador? Nesse post conto um pouco da minha experiência trabalhando por um ano como programador Java na Espanha.&lt;/p&gt;

&lt;h2 id=&quot;onde-procurar-emprego&quot;&gt;Onde procurar emprego&lt;/h2&gt;

&lt;p&gt;Em comparação com o Brasil, eu diria que Madrid está para São Paulo e Barcelona para o Rio de Janeiro, em quantidade de ofertas x salário. Claro, há vagas em outras cidades, como Valência, Bilbau etc, porém geralmente a relação ofertas x salário é inferior.&lt;/p&gt;

&lt;p&gt;Para buscar vagas, usei principalmente dois sites: &lt;a href=&quot;http://www.tecnoempleo.com&quot;&gt;Tecnoemploe&lt;/a&gt; e &lt;a href=&quot;http://www.infojobs.net&quot;&gt;InfoJobs&lt;/a&gt;, que são parecidos com o &lt;a href=&quot;http://www.apinfo.com&quot;&gt;APinfo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;O Tecnoempleo foi o site que eu mais gostei e onde encontrei meu primeiro emprego.&lt;/p&gt;

&lt;h2 id=&quot;crise&quot;&gt;Crise?&lt;/h2&gt;

&lt;p&gt;Por mais que a crise tenha sido muito divulgada pela mídia, o mercado está aquecido, principalmente nas grandes capitais. Eu consegui meu primeiro emprego duas semanas após a minha chegada na Espanha.&lt;/p&gt;

&lt;h2 id=&quot;idioma&quot;&gt;Idioma&lt;/h2&gt;

&lt;p&gt;Em tecnologia geralmente usamos muitos termos em inglês. No Brasil, tentamos falar em inglês, pelo menos os que são fáceis para falarmos. Já na Espanha é muito comum falar da forma que se lê, portanto Oracle eles vão falar O-r-a-c-l-e e não &lt;a href=&quot;http://www.thefreedictionary.com/oracle&quot;&gt;ˈorəkl&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Na minha primeira entrevista na Espanha, meu Espanhol não era dos melhores, a entrevista pareceu uma nuvem de tags: JEE, TDD, Hudson, CI, Spring, jQuery etc, mas deu certo, consegui meu primeiro emprego.&lt;/p&gt;

&lt;p&gt;Escrevi esse &lt;a href=&quot;http://www.pablocantero.com/blog/2015/05/17/dont-disqualify-yourself/&quot;&gt;post&lt;/a&gt; com algumas dicas para se preparar para entrevistas, recomendo a leitura.&lt;/p&gt;

&lt;h2 id=&quot;vantagem-competitiva&quot;&gt;Vantagem competitiva?&lt;/h2&gt;

&lt;p&gt;Acredito que no geral brasileiros tenham alguma vantagem na relação idade vs experiência. Geralmente no Brasil temos que trabalhar para pagar a faculdade, e em tecnologia, é muito comum já iniciar com um trabalho na área, que soma na experiência.&lt;/p&gt;

&lt;p&gt;Na Espanha, assim com em muitos outros países na Europa, é comum fazer faculdade, e só depois buscar trabalho. Eu cheguei na Espanha com 27 anos, com 8 anos de experiência como programador, lá é comum pessoas com a mesma idade iniciando a carreira.&lt;/p&gt;

&lt;h2 id=&quot;qualidade-vida&quot;&gt;Qualidade vida&lt;/h2&gt;

&lt;p&gt;Eu achei a qualidade vida muito boa. Eu ficava muito contente em poder voltar para casa de madrugada me guiando pelo GPS do celular, sem medo de ser abordado por um assaltante.&lt;/p&gt;

&lt;p&gt;Viajar na Europa é muito fácil, dá para encontrar passagens bem baratas, fazer um bate-volta para outro país é viável.&lt;/p&gt;

&lt;p&gt;Serviços que demandam mão de obra, geralmente são mais caros. Tínhamos uma pessoa para limpar o apartamento (dividido com dois Espanhóis), pagávamos por hora, 2 horas por semana, a horista na Espanha, seria a diarista no Brasil.&lt;/p&gt;

&lt;h2 id=&quot;visto&quot;&gt;Visto&lt;/h2&gt;

&lt;p&gt;Eu tenho dupla cidadania, meu pai é Espanhol. Portanto não tive barreiras legais para trabalhar. Estar legal no país é muito importante, pois não contratam mão de obra qualificada sem visto.&lt;/p&gt;

&lt;h2 id=&quot;formas-de-contratação&quot;&gt;Formas de contratação&lt;/h2&gt;

&lt;p&gt;Na Espanha quase 100% das ofertas de trabalho são “CLT”, ou seja, registrado. Poucas pessoas trabalham como “Pessoa Jurídica”.&lt;/p&gt;

&lt;h2 id=&quot;salário&quot;&gt;Salário&lt;/h2&gt;

&lt;p&gt;Nos sites de vagas, alguns anunciantes colocam a faixa salarial, com alguma busca básica dá para ter uma noção dos salários atualizados. O valor informado geralmente é o total anual bruto, sendo que esse valor pode ser dividido em 12, 13 e até 14 vezes.&lt;/p&gt;

&lt;p&gt;Proporcionalmente mesmo fazendo a conversão de &lt;a href=&quot;http://www.google.com.br/search?q=1+EUR+to+BRL&quot;&gt;Euro para Real&lt;/a&gt;, os salários no Brasil costumam ser melhores, porém essa conta não é tão exata quando se compara a qualidade e custo de vida.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>JavaScript para notificar se um site for alterado</title>
   <link href="http://www.pablocantero.com//blog/2010/09/15/javascript-para-notificar-se-um-site-for-alterado/"/>
   <updated>2010-09-15T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/09/15/javascript-para-notificar-se-um-site-for-alterado</id>
   
   <content type="html">&lt;p&gt;Inspirado em uma thread de um grupo de e-mail que participo, onde aguardávamos ansiosamente a abertura das inscrições para o Google Developer Day 2010 (F5, F5, F5 …). Criei um Bookmarklet para notificar quando um site for alterado.
&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;##Como usar&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Clique e arraste &lt;a title=&quot;Notificar alterações do site&quot; href=&quot;javascript:(function(){/*@author Pablo Cantero - http://pablocantero.com*/var xmlHttp = getXMLHttpObj();if(xmlHttp == null){alert('Failed to load XMLHTTP');return;}var actual = getPageContent(xmlHttp, window.location.href);var intervalId = window.setInterval(function(){var current = getPageContent(xmlHttp, window.location.href);if(actual != current){alert('This page has been modified since you opened it');window.clearInterval(intervalId);}}, 1000);function getPageContent(xmlHttp, url){xmlHttp.open('GET', window.location.href, false);xmlHttp.send('');return xmlHttp.responseText;}function getXMLHttpObj(){if(typeof(XMLHttpRequest)!='undefined')return new XMLHttpRequest();var axO=['Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.4.0','Msxml2.XMLHTTP.3.0', 'Msxml2.XMLHTTP', 'Microsoft.XMLHTTP'];for(var i = 0; i &amp;lt; axO.length; i++){try{return new ActiveXObject(axO[i]);}catch(e){}}return null;}})()&quot;&gt;Notificar Alterações&lt;/a&gt; até a barra de favoritos do navegador;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Acesse o site que você deseja ser notificado;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Clique no link “Notificar Alterações” na barra de favoritos.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pronto! Toda vez que o site for alterado, o Bookmarklet disparará um alert notificando que houve alterações.&lt;/p&gt;

&lt;p&gt;##Código JavaScript&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://gist.github.com/751586&quot;&gt;gist.github.com/751586&lt;/a&gt;&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Relógio – tacômetro, taquímetro, tachometer, tachymeter?!?!</title>
   <link href="http://www.pablocantero.com//blog/2010/09/14/relogio-tacometro-taquimetro-tachometer-tachymeter/"/>
   <updated>2010-09-14T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/09/14/relogio-tacometro-taquimetro-tachometer-tachymeter</id>
   
   <content type="html">&lt;h2 id=&quot;atualização-do-post-21032012&quot;&gt;Atualização do post 21/03/2012&lt;/h2&gt;

&lt;p&gt;Segundo o comentário &lt;a href=&quot;http://pablocantero.com/blog/2010/09/14/relogio-tacometro-taquimetro-tachometer-tachymeter/#comment-341334916&quot;&gt;comment-341334916&lt;/a&gt;, no caso do relógio Orient CEM65001B citado no post, o nome correto é apenas contador de minutos. Portanto esse post é sobre contador de minutos.&lt;/p&gt;

&lt;p&gt;–&lt;/p&gt;

&lt;p&gt;&lt;del&gt;Esse é o meu primeiro post sobre assuntos gerais, no caso sobre &lt;a href=&quot;http://pt.wikipedia.org/wiki/Tac%C3%B4metro&quot;&gt;tacômetro&lt;/a&gt;, também conhecido por taquímetro. Sendo que em muitos relógios estão em inglês, portanto tachometer ou tachymeter.&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;&lt;del&gt;Segunda a definição na Wikipédia.&lt;/del&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;del&gt;O &lt;strong&gt;tacômetro&lt;/strong&gt;, também conhecido como &lt;strong&gt;taquímetro&lt;/strong&gt;, é um instrumento de medição do número de rotações (geralmente por minuto, &lt;a href=&quot;http://pt.wikipedia.org/wiki/RPM&quot;&gt;RPM&lt;/a&gt;) de um &lt;a href=&quot;http://pt.wikipedia.org/wiki/Motor&quot;&gt;motor&lt;/a&gt;. Pode ser chamado também de conta-giros.&lt;/del&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A pouco tempo voltei a me interessar por relógios, nada demais, só para usá-los como acessório mesmo. Parei de usar relógios anos atrás, por usar mais o celular para ver as horas do que no relógio que estava no meu pulso.&lt;/p&gt;

&lt;p&gt;Eu queria um relógio barato e que fosse versátil, esporte e clássico ao mesmo tempo. Encontrei o relógio Orient CEM65001B no site &lt;a href=&quot;http://www.askmen.com/fashion/trends_500/533b_watches-for-under-300-part-iii.html&quot;&gt;askmen&lt;/a&gt;, aparentemente ideal para o que eu queria.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Orient CEM65001M&lt;/p&gt;

  &lt;p&gt;&lt;img src=&quot;http://www.pablocantero.com/assets/images/posts/relogio-orient-cem65001b.jpg&quot; alt=&quot;Relógio Orient CEM65001B&quot; /&gt;&lt;/p&gt;

  &lt;p&gt;Cost: $190&lt;/p&gt;

  &lt;p&gt;What if you’re looking for an everyday watch with an automatic movement  and a classic, sporty design? You can do no better than this  dive-inspired Orient. It features a solid Japanese automatic movement, a  &lt;a href=&quot;http://www.askmen.com/fashion/watch/rolex-submariner-16800.html&quot;&gt;Rolex Submariner&lt;/a&gt;-esque design and it’s rated to 200 meters.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;O modelo citado no askmen não é o CEM65001M (versão laranja) e sim CEM65001B (versão preta).&lt;/p&gt;

&lt;p&gt;Comprei o relógio no &lt;a href=&quot;http://cgi.ebay.com/ORIENT-AUTOMATIC-SCUBA-DIVER-MEN-WATCH-CEM65001-/290395562061?pt=Wristwatches&amp;amp;hash=item439ceca04d&quot;&gt;eBay&lt;/a&gt;, chegou em menos de 2 semanas e não fui tributado./&lt;/p&gt;

&lt;p&gt;Assim que o relógio chegou eu fiquei na dúvida “para que serve essa roda preta com números?”.&lt;/p&gt;

&lt;p&gt;Essa roda serve para proteger o relógio e para marcação de tempo (contador de minutos) de uma forma bem simples e super inteligente. Projetada para mergulhadores controlarem o tempo que estão debaixo da água, por conta do tubo de oxigênio que tem limitação de tempo.&lt;/p&gt;

&lt;h2 id=&quot;usando-o-contador-de-minutos&quot;&gt;Usando o contador de minutos&lt;/h2&gt;

&lt;p&gt;Geralmente o contador de minutos tem um símbolo inicial como um triângulo, que na figura abaixo está acima da marcação de 12h, esse triângulo é o ponto zero do contador de minutos.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.pablocantero.com/assets/images/posts/relogio-orient-cem65001b-5h49.jpg&quot; alt=&quot;Relógio Orient CEM65001B 5h49&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Para usá-lo basta girar o triângulo até a marcação de minutos (&lt;strong&gt;49m&lt;/strong&gt;) da hora atual, no caso 5h&lt;strong&gt;49m&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Após 30 minutos veremos que ponteiro de minutos estará apontando para o número 30, que quer dizer que se passarem 30 minutos, desde do ajuste anterior que apontava para 5h49.&lt;/p&gt;

&lt;p&gt;Para onde o ponteiro de minutos apontar, será o tempo em minutos desde que giramos o triângulo.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.pablocantero.com/assets/images/posts/relogio-orient-cem65001b-5h49.jpg&quot; alt=&quot;Relógio Orient CEM65001B 6h14&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;rodinha-dura-para-girar&quot;&gt;Rodinha dura para girar?&lt;/h2&gt;

&lt;p&gt;Não estranhe se a roda for dura para girar ou que só gire para um lado, pois é propositalmente feito assim, para que nenhum esbarrão consiga movê-la indevidamente.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>jQuery Plugin - Javascript for java.util.Date.toString() format</title>
   <link href="http://www.pablocantero.com//blog/2010/09/04/jquery-plugin-javascript-for-java-util-date-tostring-format/"/>
   <updated>2010-09-04T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/09/04/jquery-plugin-javascript-for-java-util-date-tostring-format</id>
   
   <content type="html">&lt;p&gt;&lt;strong&gt;Please have a look at &lt;a href=&quot;http://github.com/phstc/jquery-dateFormat&quot;&gt;jquery-dateFormat on GitHub&lt;/a&gt; for the plugin’s documentation.&lt;/strong&gt;&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Spring MVC 3 + MongoDB - Parte 2</title>
   <link href="http://www.pablocantero.com//blog/2010/09/03/spring-mvc-3-mongodb-parte-2/"/>
   <updated>2010-09-03T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/09/03/spring-mvc-3-mongodb-parte-2</id>
   
   <content type="html">&lt;p&gt;Em continuação ao &lt;a href=&quot;http://pablocantero.com/blog/2010/09/03/spring-mvc-3-mongodb-parte-1&quot;&gt;Spring MVC 3  + MongoDB - Parte 1&lt;/a&gt;, vou me basear na utilização do DbFactoryBean do post &lt;a href=&quot;http://java.dzone.com/articles/plain-simple-mongodb-spring&quot;&gt;Plain Simple MongoDB Spring Integration&lt;/a&gt;, com algumas observações adicionais.&lt;/p&gt;

&lt;h2 id=&quot;baixando-o-mongodb&quot;&gt;Baixando o MongoDB&lt;/h2&gt;

&lt;p&gt;O MongoDB usado para escrever esse post foi OS X 64-bit - Versão 1.6.2, disponível na páginas de &lt;a href=&quot;http://www.mongodb.org/downloads&quot;&gt;downloads&lt;/a&gt; do MongoDB.&lt;/p&gt;

&lt;p&gt;Baixei e salvei no meu workspace.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ /Users/pablo/workspace/mongodb-osx-x86_64-1.6.2/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;driver-para-o-mongodb&quot;&gt;Driver para o MongoDB&lt;/h2&gt;

&lt;p&gt;Eu não achei, mas também não procurei muito um repositório Maven para o driver do MongoDB, portanto eu simplesmente baixei o &lt;a href=&quot;http://github.com/downloads/mongodb/mongo-java-driver/mongo-2.1.jar&quot;&gt;mongo-2.1.jar&lt;/a&gt; e salvei no diretório &lt;code class=&quot;highlighter-rouge&quot;&gt;WEB-INF/lib&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mkdir workspace/spring-mongodb/src/main/webapp/WEB-INF/lib
cd workspace/spring-mongodb/src/main/webapp/WEB-INF/lib
wget http://github.com/downloads/mongodb/mongo-java-driver/mongo-2.1.jar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Após baixar e atualizar F5 o projeto no Eclipse.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/Screen-shot-2010-09-03-at-12.58.17-PM.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;adicionando-o-mongo-21jar-no-build-path&quot;&gt;Adicionando o mongo-2.1.jar no Build Path&lt;/h2&gt;

&lt;p&gt;Botão direito no projeto -&amp;gt; Propriedades -&amp;gt; Java Build Path -&amp;gt; Libraries -&amp;gt; Add JARs…&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/Screen-shot-2010-09-03-at-12.57.17-PM.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;configurando-app-configxml&quot;&gt;Configurando app-config.xml&lt;/h2&gt;

&lt;p&gt;Para configurar o app-config.xml, basta adicionar as seguintes linhas.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;bean id=&quot;propertiesPlacholder&quot;
 class=&quot;org.springframework.beans.factory.config.PropertyPlaceholderConfigurer&quot;
 lazy-init=&quot;false&quot;&amp;gt;
 &amp;lt;property name=&quot;location&quot; value=&quot;classpath:/resources/app-config.properties&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;context:property-placeholder location=&quot;classpath:db.properties&quot; /&amp;gt;
 &amp;lt;bean id=&quot;mongo&quot;&amp;gt;
 &amp;lt;constructor-arg value=&quot;${db.host}&quot; /&amp;gt;
 &amp;lt;constructor-arg value=&quot;${db.port}&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;
 &amp;lt;bean id=&quot;db&quot;&amp;gt;
 &amp;lt;property name=&quot;mongo&quot; ref=&quot;mongo&quot; /&amp;gt;
 &amp;lt;property name=&quot;name&quot; value=&quot;${app.db.name}&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;app-configproperties&quot;&gt;#app-config.properties&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;db.host=localhost
#porta default
db.port=27017
app.db.name=spring-mongodb-app
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;No post &lt;a href=&quot;http://pablocantero.com/blog/2010/09/03/propertyplaceholderconfigurer-usando-properties-nas-configuracoes-do-sprin/&quot;&gt;PropertyPlaceholderConfigurer – Usando properties nas configurações do Spring&lt;/a&gt; tem mais informações sobre Spring e arquivos properties.&lt;/p&gt;

&lt;h2 id=&quot;classe-dbfactory&quot;&gt;Classe DbFactory&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cantero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spring_mongodb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dao&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;springframework&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beans&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FactoryBean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;springframework&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beans&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;annotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;springframework&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mongodb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mongodb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mongo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbFactoryBean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FactoryBean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

 &lt;span class=&quot;n&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mongo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mongo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

 &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Override&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DB&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;notNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mongo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;notNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mongo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

 &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Override&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getObjectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

 &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Override&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isSingleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

 &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Required&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setMongo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mongo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mongo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mongo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mongo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

 &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Required&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;relembrando-factory&quot;&gt;Relembrando Factory&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Factory_method_pattern&quot;&gt;Factory Method&lt;/a&gt; e &lt;a href=&quot;http://en.wikipedia.org/wiki/Abstract_factory_pattern&quot;&gt;Abstract Factory&lt;/a&gt; são listados no &lt;a href=&quot;http://c2.com/cgi/wiki?GangOfFour&quot;&gt;GoF&lt;/a&gt; como patterns creacionais, que lidam com a instanciação de objetos.&lt;/p&gt;

&lt;p&gt;Factory Method provê uma classe de decisão, na qual retorna uma das muitas possíveis subclasses de uma classe base abstrata, dependendo do dado fornecido.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CarroAbstract carro = CarroFactory.get(&quot;Vectra&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Abstract Factory provê uma interface para se criar e retornar uma de muitas famílias de objetos relacionados.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FabricaAbstract fabrica = AbstractFabricaFactory.getInstance(&quot;Chevrolet&quot;);
CarroAbstract carro = fabrica.get(&quot;Vectra&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;singletonprototype&quot;&gt;Singleton/Prototype&lt;/h2&gt;

&lt;p&gt;O FactoryBean pode ser &lt;a href=&quot;http://static.springsource.org/spring/docs/2.0.x/reference/beans.html#beans-factory-scopes-singleton&quot;&gt;Spring Singleton&lt;/a&gt; (uma única instância para todas as requisições) ou &lt;a href=&quot;http://static.springsource.org/spring/docs/2.0.x/reference/beans.html#beans-factory-scopes-prototype&quot;&gt;Spring Prototype&lt;/a&gt; (uma nova instancia para cada requisição), lembrando que apesar do nome, são implementações essecialmente iguais, mas implementadas de forma diferente do que os respectivos patterns no GoF.&lt;/p&gt;

&lt;p&gt;Singleton é o escopo padrão para criação de Beans no Spring, que pode ser alterado na declaração do Bean.&lt;/p&gt;

&lt;h2 id=&quot;voltando-basedaoimpl&quot;&gt;Voltando… BaseDAOImpl&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;bean id=&quot;baseDAOImpl&quot; class=&quot;com.cantero.spring_mongodb.dao.BaseDAOImpl&quot;&amp;gt;
 &amp;lt;property name=&quot;db&quot; ref=&quot;dbFactory&quot;/&amp;gt;
&amp;lt;/bean&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;basedaoimpljava&quot;&gt;#BaseDAOImpl.java&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cantero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spring_mongodb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dao&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mongodb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BaseDAOImpl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IBaseDAO&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

 &lt;span class=&quot;p&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Logger&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;slf4j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LoggerFactory&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;  &lt;/span&gt; &lt;span class=&quot;err&quot;&gt;   &lt;/span&gt; &lt;span class=&quot;err&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BaseDAOImpl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

 &lt;span class=&quot;n&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DB&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

 &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DB&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

 &lt;span class=&quot;n&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DB&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;utilizado-o-basedaoimpl-nos-services&quot;&gt;Utilizado o BaseDAOImpl nos Services&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cantero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spring_mongodb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;springframework&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beans&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;annotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Autowired&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;springframework&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stereotype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cantero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spring_mongodb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dao&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IBaseDAO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Service&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PessoaServiceImpl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPessoaService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

 &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Autowired&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;IBaseDAO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;baseDAO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;no-controller&quot;&gt;No Controller&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cantero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spring_mongodb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;springframework&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beans&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;annotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Autowired&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;springframework&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stereotype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cantero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spring_mongodb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPessoaService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Controller&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PessoaController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

 &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Autowired&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;IPessoaService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pessoaService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;iniciando-o-mongodb&quot;&gt;Iniciando o MongoDB&lt;/h2&gt;

&lt;p&gt;A primeira vez que for iniciar o MongoDB lembre-se de criar o diretório /data/db&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ mkdir /data/db
$ cd workspace/mongodb-osx-x86_64-1.6.2/bin/
$ ./mongod
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Continuará…&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Spring MVC 3 + MongoDB - Parte 1</title>
   <link href="http://www.pablocantero.com//blog/2010/09/03/spring-mvc-3-mongodb-parte-1/"/>
   <updated>2010-09-03T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/09/03/spring-mvc-3-mongodb-parte-1</id>
   
   <content type="html">Aproveitando o momento que está em alta o &lt;a href=&quot;http://pt.wikipedia.org/wiki/NoSQL&quot;&gt;NoSQL&lt;/a&gt;, resolvi criar esse artigo para testar na prática a utilização de um banco de dados não relacional

A idéia desse artigo é fazer um projeto do ZERO com Spring MVC 3 + &lt;a href=&quot;http://www.mongodb.org/&quot;&gt;MongoDB&lt;/a&gt;

A primeira que ouvi falar do movimento NoSQL foi através do &lt;a href=&quot;http://couchdb.apache.org/&quot;&gt;CouchDB&lt;/a&gt;, mas na época eu fiz pouco caso

Só por curiosidade, a primeira edição do &lt;a href=&quot;http://en.wikipedia.org/wiki/NoSQL&quot;&gt;http://en.wikipedia.org/wiki/NoSQL&lt;/a&gt; foi em 13/Agosto/2009, é algo bem recente

Este post ficará dividido em duas parte

&lt;strong&gt;Parte 1) Criação de um projeto com Spring MVC 3
&lt;/strong&gt;

&lt;strong&gt;Parte 2) &lt;a href=&quot;http://pablocantero.com/blog/2010/09/03/spring-mvc-3-mongodb-parte-2/&quot;&gt;Configuração do MongoDB&lt;/a&gt;&lt;/strong&gt;

Para criar o projeto com Spring MVC vou utilizar o SpringSource Tool Suite (&lt;em&gt;versão 2.3.2.RELEASE&lt;/em&gt;), citado no post &lt;a href=&quot;http://pablocantero.com/blog/2010/08/27/springsource-tools-suite-e-nao-e-que-e-bom/&quot;&gt;SpringSource Tools Suite – E não é que é bom&lt;/a&gt;

O SpringSource Tools Suite facilita todo o processo de criação de uma arquitetura inicial com Spring MVC

&lt;a rel=&quot;attachment wp-att-368&quot; href=&quot;http://pablocantero.com/blog/2010/09/03/spring-mvc-3-mongodb-parte-1/screen-shot-2010-09-03-at-1-45-33-am/&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-368&quot; title=&quot;Screen shot 2010-09-03 at 1.45.33 AM&quot; src=&quot;http://pablocantero.com/blog/wp-content/uploads/2010/09/Screen-shot-2010-09-03-at-1.45.33-AM.png&quot; alt=&quot;&quot; width=&quot;612&quot; height=&quot;309&quot; /&gt;&lt;/a&gt;

&lt;strong&gt;Criação do Projeto&lt;/strong&gt;

Para criar um novo projeto File -&amp;gt; New -&amp;gt; Other ...

&lt;a rel=&quot;attachment wp-att-367&quot; href=&quot;http://pablocantero.com/blog/2010/09/03/spring-mvc-3-mongodb-parte-1/screen-shot-2010-09-03-at-1-47-01-am/&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-367&quot; title=&quot;Screen shot 2010-09-03 at 1.47.01 AM&quot; src=&quot;http://pablocantero.com/blog/wp-content/uploads/2010/09/Screen-shot-2010-09-03-at-1.47.01-AM.png&quot; alt=&quot;&quot; width=&quot;612&quot; height=&quot;330&quot; /&gt;&lt;/a&gt;

Selecione o Wizard SpringSource Tool Suite -&amp;gt; Spring Template Project

&lt;a rel=&quot;attachment wp-att-366&quot; href=&quot;http://pablocantero.com/blog/2010/09/03/spring-mvc-3-mongodb-parte-1/screen-shot-2010-09-03-at-1-47-12-am/&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-366&quot; title=&quot;Screen shot 2010-09-03 at 1.47.12 AM&quot; src=&quot;http://pablocantero.com/blog/wp-content/uploads/2010/09/Screen-shot-2010-09-03-at-1.47.12-AM.png&quot; alt=&quot;&quot; width=&quot;531&quot; height=&quot;503&quot; /&gt;&lt;/a&gt;

Selecione o template Spring MVC Project

&lt;a rel=&quot;attachment wp-att-365&quot; href=&quot;http://pablocantero.com/blog/2010/09/03/spring-mvc-3-mongodb-parte-1/screen-shot-2010-09-03-at-1-47-22-am/&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-365&quot; title=&quot;Screen shot 2010-09-03 at 1.47.22 AM&quot; src=&quot;http://pablocantero.com/blog/wp-content/uploads/2010/09/Screen-shot-2010-09-03-at-1.47.22-AM.png&quot; alt=&quot;&quot; width=&quot;525&quot; height=&quot;508&quot; /&gt;&lt;/a&gt;

Configurações básicas do projeto

&lt;a rel=&quot;attachment wp-att-364&quot; href=&quot;http://pablocantero.com/blog/2010/09/03/spring-mvc-3-mongodb-parte-1/screen-shot-2010-09-03-at-1-47-56-am/&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-364&quot; title=&quot;Screen shot 2010-09-03 at 1.47.56 AM&quot; src=&quot;http://pablocantero.com/blog/wp-content/uploads/2010/09/Screen-shot-2010-09-03-at-1.47.56-AM.png&quot; alt=&quot;&quot; width=&quot;573&quot; height=&quot;505&quot; /&gt;&lt;/a&gt;

Voilà - Projecto criado

&lt;a rel=&quot;attachment wp-att-363&quot; href=&quot;http://pablocantero.com/blog/2010/09/03/spring-mvc-3-mongodb-parte-1/screen-shot-2010-09-03-at-1-48-14-am/&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-363&quot; title=&quot;Screen shot 2010-09-03 at 1.48.14 AM&quot; src=&quot;http://pablocantero.com/blog/wp-content/uploads/2010/09/Screen-shot-2010-09-03-at-1.48.14-AM.png&quot; alt=&quot;&quot; width=&quot;308&quot; height=&quot;236&quot; /&gt;&lt;/a&gt;

Junto com o SpringSource Tools Suite vem o SpringSource Cloud Foundry v1.0 (&lt;em&gt;para o &lt;a href=&quot;http://www.cloudfoundry.com/&quot;&gt;cloudfoundry&lt;/a&gt;&lt;/em&gt;) e &lt;a href=&quot;http://www.springsource.com/products/tcserver&quot;&gt;SpringSource tc Server&lt;/a&gt; v6.0 (&lt;em&gt;tomcat customizado&lt;/em&gt;),
a parte eu instalei o &lt;a href=&quot;http://tomcat.apache.org/download-60.cgi&quot;&gt;Tomcat 6&lt;/a&gt;

Para adicionar sua aplicação no Tomcat basta botão direito sobre o Tomcat

Add and Remove... -&amp;gt; Selecionar o projeto (&lt;em&gt;spring-mongodb&lt;/em&gt;) -&amp;gt; Add -&amp;gt; Finish

&lt;a rel=&quot;attachment wp-att-371&quot; href=&quot;http://pablocantero.com/blog/2010/09/03/spring-mvc-3-mongodb-parte-1/screen-shot-2010-09-03-at-11-40-56-am/&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-371&quot; title=&quot;Screen shot 2010-09-03 at 11.40.56 AM&quot; src=&quot;http://pablocantero.com/blog/wp-content/uploads/2010/09/Screen-shot-2010-09-03-at-11.40.56-AM.png&quot; alt=&quot;&quot; width=&quot;430&quot; height=&quot;160&quot; /&gt;&lt;/a&gt;

Após iniciar o Tomcat (&lt;em&gt;botão direito sobre o Tomcat -&amp;gt; Start&lt;/em&gt;)  e acessar &lt;a href=&quot;http://localhost:8080/spring-mongodb&quot;&gt;http://localhost:8080/spring-mongodb&lt;/a&gt; no navegador

Voilà (2)

&lt;a rel=&quot;attachment wp-att-372&quot; href=&quot;http://pablocantero.com/blog/2010/09/03/spring-mvc-3-mongodb-parte-1/screen-shot-2010-09-03-at-11-50-02-am/&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-372&quot; title=&quot;Screen shot 2010-09-03 at 11.50.02 AM&quot; src=&quot;http://pablocantero.com/blog/wp-content/uploads/2010/09/Screen-shot-2010-09-03-at-11.50.02-AM.png&quot; alt=&quot;&quot; width=&quot;614&quot; height=&quot;159&quot; /&gt;&lt;/a&gt;

&lt;strong&gt;BUG&lt;/strong&gt;
&lt;blockquote&gt;org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/spring-mongodb/app/styles/site.css] in DispatcherServlet with name 'Spring MVC Dispatcher Servlet'&lt;/blockquote&gt;
O Spring MVC Project vem por padrão com 3 pastas: images, scripts e styles

&lt;a rel=&quot;attachment wp-att-373&quot; href=&quot;http://pablocantero.com/blog/2010/09/03/spring-mvc-3-mongodb-parte-1/screen-shot-2010-09-03-at-11-54-54-am/&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-373&quot; title=&quot;Screen shot 2010-09-03 at 11.54.54 AM&quot; src=&quot;http://pablocantero.com/blog/wp-content/uploads/2010/09/Screen-shot-2010-09-03-at-11.54.54-AM.png&quot; alt=&quot;&quot; width=&quot;299&quot; height=&quot;277&quot; /&gt;&lt;/a&gt;

Só que se você tentar inserir um recursos dessas pastas, como o include css abaixo

&lt;strong&gt;welcome.jsp
&lt;/strong&gt;

[html]
&amp;lt;%@ taglib uri=&amp;quot;http://java.sun.com/jsp/jstl/core&amp;quot; prefix=&amp;quot;c&amp;quot; %&amp;gt;
&amp;lt;%@ page session=&amp;quot;false&amp;quot; %&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
 &amp;lt;title&amp;gt;Welcome&amp;lt;/title&amp;gt;
 &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; href=&amp;quot;/spring-mongodb/styles/site.css&amp;quot; /&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h1&amp;gt;
 Congratulations! You're running Spring!
&amp;lt;/h1&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
[/html]

Retornará o org.springframework.web.servlet.PageNotFound, pois o Spring tentará resolver o site.css como uma url de Controller/Action

Para resolver isso basta alterar o urlrewrite.xml

[xml]
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;
&amp;lt;!DOCTYPE urlrewrite PUBLIC &amp;quot;-//tuckey.org//DTD UrlRewrite 3.0//EN&amp;quot; &amp;quot;http://tuckey.org/res/dtds/urlrewrite3.0.dtd&amp;quot;&amp;gt;
&amp;lt;urlrewrite default-match-type=&amp;quot;wildcard&amp;quot;&amp;gt;
 &amp;lt;rule&amp;gt;
 &amp;lt;from&amp;gt;/&amp;lt;/from&amp;gt;
 &amp;lt;to&amp;gt;/app/welcome&amp;lt;/to&amp;gt;
 &amp;lt;/rule&amp;gt;
 &amp;lt;rule&amp;gt;
 &amp;lt;from&amp;gt;/scripts/**&amp;lt;/from&amp;gt;
 &amp;lt;to&amp;gt;/scripts/$1&amp;lt;/to&amp;gt;
 &amp;lt;/rule&amp;gt;
 &amp;lt;rule&amp;gt;
 &amp;lt;from&amp;gt;/styles/**&amp;lt;/from&amp;gt;
 &amp;lt;to&amp;gt;/styles/$1&amp;lt;/to&amp;gt;
 &amp;lt;/rule&amp;gt;
 &amp;lt;rule&amp;gt;
 &amp;lt;from&amp;gt;/images/**&amp;lt;/from&amp;gt;
 &amp;lt;to&amp;gt;/images/$1&amp;lt;/to&amp;gt;
 &amp;lt;/rule&amp;gt;
 &amp;lt;rule&amp;gt;
 &amp;lt;from&amp;gt;/**&amp;lt;/from&amp;gt;
 &amp;lt;to&amp;gt;/app/$1&amp;lt;/to&amp;gt;
 &amp;lt;/rule&amp;gt;
 &amp;lt;outbound-rule&amp;gt;
 &amp;lt;from&amp;gt;/app/**&amp;lt;/from&amp;gt;
 &amp;lt;to&amp;gt;/$1&amp;lt;/to&amp;gt;
 &amp;lt;/outbound-rule&amp;gt;
&amp;lt;/urlrewrite&amp;gt;
[/xml]

Essa foi a solução que eu postei no &lt;a href=&quot;http://stackoverflow.com/questions/1510781/no-mapping-found-for-http-request-with-uri-in-a-spring-mvc-app/3614086#3614086&quot;&gt;stackoverflow&lt;/a&gt; e &lt;a href=&quot;http://forum.springsource.org/showthread.php?p=316989#post316989&quot;&gt;forum.springsource&lt;/a&gt;

Habilitando o uso de REST no Spring MVC 3

web.xml

[xml]
&amp;lt;filter&amp;gt;
 &amp;lt;filter-name&amp;gt;httpMethodFilter&amp;lt;/filter-name&amp;gt;
 &amp;lt;filter-class&amp;gt;org.springframework.web.filter.HiddenHttpMethodFilter&amp;lt;/filter-class&amp;gt;
 &amp;lt;/filter&amp;gt;

 &amp;lt;filter-mapping&amp;gt;
 &amp;lt;filter-name&amp;gt;httpMethodFilter&amp;lt;/filter-name&amp;gt;
 &amp;lt;servlet-name&amp;gt;Spring MVC Dispatcher Servlet&amp;lt;/servlet-name&amp;gt;
 &amp;lt;/filter-mapping&amp;gt;
[/xml]
</content>

 </entry>
 
 <entry>
   <title>Property files com PropertyPlaceholderConfigurer</title>
   <link href="http://www.pablocantero.com//blog/2010/09/03/property-files-com-propertyPlaceholderConfigurer/"/>
   <updated>2010-09-03T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/09/03/property-files-com-propertyPlaceholderConfigurer</id>
   
   <content type="html">&lt;p&gt;Os arquivos de propriedades são muito úteis para configurar variáveis de ambiente, parâmetros de inicialização etc. Quem nunca usou um &lt;a href=&quot;http://en.wikipedia.org/wiki/.properties&quot;&gt;.properties&lt;/a&gt;?!&lt;/p&gt;

&lt;p&gt;Esse post é um guia rápido de utilização do Spring &lt;a href=&quot;http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.html&quot;&gt;PropertyPlaceholderConfigurer&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;criando-um-properties&quot;&gt;Criando um properties&lt;/h2&gt;

&lt;p&gt;Utilizei a seguinte estrutura &lt;code class=&quot;highlighter-rouge&quot;&gt;src/resources/app-config.properties&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# WS Config
ws.baseAddress=http://127.0.0.1:9801/
# DB Config
db.url=jdbc:mysql://localhost:3306/db
db.username=username
db.password=password
db.driverClass=com.mysql.jdbc.Driver
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;configurando-o-app-configxml&quot;&gt;Configurando o app-config.xml&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;bean id=&quot;propertiesPlacholder&quot; class=&quot;org.springframework.beans.factory.config.PropertyPlaceholderConfigurer&quot; lazy-init=&quot;false&quot;&amp;gt;
	&amp;lt;property name=&quot;location&quot; value=&quot;classpath:/resources/app-config.properties&quot; /&amp;gt;
&amp;lt;/bean&amp;gt;
&amp;lt;bean&amp;gt;
	&amp;lt;property name=&quot;baseAddress&quot; value=&quot;${ws.baseAddress}&quot; /&amp;gt;
&amp;lt;/bean&amp;gt;
&amp;lt;!-- DataSource SQL Server --&amp;gt;
&amp;lt;bean id=&quot;dataSource&quot; destroy-method=&quot;close&quot; scope=&quot;singleton&quot;&amp;gt;
	&amp;lt;property name=&quot;driverClassName&quot; value=&quot;#{db.driverClass}&quot; /&amp;gt;
	&amp;lt;property name=&quot;url&quot; value=&quot;${db.url}&quot; /&amp;gt;
	&amp;lt;property name=&quot;username&quot; value=&quot;${db.username}&quot; /&amp;gt;
	&amp;lt;property name=&quot;password&quot; value=&quot;${db.password}&quot; /&amp;gt;
&amp;lt;/bean&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;O app-config.xml é o nome do arquivo de configuração do Spring, que pode mudar de acordo com o seu ambiente.&lt;/p&gt;

&lt;p&gt;Exemplo de configuração do app-config.xml.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;servlet&amp;gt;
	&amp;lt;servlet-name&amp;gt;Spring MVC Dispatcher Servlet&amp;lt;/servlet-name&amp;gt;
	&amp;lt;servlet-class&amp;gt;org.springframework.web.servlet.DispatcherServlet&amp;lt;/servlet-class&amp;gt;
	&amp;lt;init-param&amp;gt;
		&amp;lt;param-name&amp;gt;contextConfigLocation&amp;lt;/param-name&amp;gt;
		&amp;lt;param-value&amp;gt;/WEB-INF/spring/app-config.xml&amp;lt;/param-value&amp;gt;
	&amp;lt;/init-param&amp;gt;
	&amp;lt;load-on-startup&amp;gt;1&amp;lt;/load-on-startup&amp;gt;
&amp;lt;/servlet&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;utilizando-múltiplos-properties&quot;&gt;Utilizando múltiplos properties&lt;/h2&gt;

&lt;p&gt;Caso sua aplicação utilize múltiplos properties, basta adicioná-los na declaração do bean PropertyPlaceholderConfigurer.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;bean id=&quot;propertiesPlacholder&quot; class=&quot;org.springframework.beans.factory.config.PropertyPlaceholderConfigurer&quot; lazy-init=&quot;false&quot;&amp;gt;
	&amp;lt;property name=&quot;locations&quot;&amp;gt;
		&amp;lt;list&amp;gt;
			&amp;lt;value&amp;gt;classpath:/resources/app-config-db.properties&amp;lt;/value&amp;gt;
			&amp;lt;value&amp;gt;classpath:/resources/app-config-ws.properties&amp;lt;/value&amp;gt;
		&amp;lt;/list&amp;gt;
	&amp;lt;/property&amp;gt;
&amp;lt;/bean&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;principais-referências&quot;&gt;Principais referências&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://stackoverflow.com/questions/534199/how-to-collect-spring-properties-from-multiple-files-for-use-on-a-single-bean&quot;&gt;stackoverflow.com/questions/534199/how-to-collect-spring-properties-from-multiple-files-for-use-on-a-single-bean&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.html&quot;&gt;static.springsource.org/spring/docs/2.0.x/api/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>

 </entry>
 
 <entry>
   <title>O que são Trackbacks e Pingbacks?</title>
   <link href="http://www.pablocantero.com//blog/2010/09/02/wordpress-o-que-sao-trackbacks-e-pingbacks/"/>
   <updated>2010-09-02T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/09/02/wordpress-o-que-sao-trackbacks-e-pingbacks</id>
   
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://make.wordpress.org/support/user-manual/building-your-wordpress-community/trackbacks-and-pingbacks/&quot;&gt;No WordPress, trackbacks e pingbacks são formas de notificar um  outro blog WordPress quando você faz um link para ele&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Dado Blog A e Blog B:&lt;/p&gt;

&lt;p&gt;Blog B faz um post com um link para um post do Blog A. O Blog A vai receber uma notificação:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Hey, Blog B está comentando o seu post em um post dele, você aceita colocar o link do post do Blog B no seu post?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Se o Blog A aceitar, o Blog A vai ter um link de volta para o Blog B, ou seja, ambos os sites vão referenciar um ao outro.&lt;/p&gt;

&lt;p&gt;Essa referência mútua é positiva, pois no post do Blog A terá o link do post do Blog B, que pode agregar ao conteúdo do post do Blog A. Além de positivo para o conteúdo, pode ser bom para SEO, dado que aumentará as referências para ambos blogs.&lt;/p&gt;

&lt;h2 id=&quot;trackbacks&quot;&gt;Trackbacks&lt;/h2&gt;

&lt;p&gt;Para fazer um trackback basta adicionar a URL do post que você está referenciando como trackback no seu post WordPress.&lt;/p&gt;

&lt;p&gt;Trackback é uma forma mais ultrapassada de referenciar posts, o WordPress recomenda pingbacks ao invés de trackbacks.&lt;/p&gt;

&lt;h2 id=&quot;pingbacks&quot;&gt;Pingbacks&lt;/h2&gt;

&lt;p&gt;Pingbacks são parecidos com os trackbacks, só que não precisam de inserção manual. Ao salvar um post o WordPress identificará automaticamente os links de outros blogs e enviará as solicitações de pingback.&lt;/p&gt;

&lt;h2 id=&quot;habilitado-por-padrão&quot;&gt;Habilitado por padrão&lt;/h2&gt;

&lt;p&gt;Por padrão trackbacks e pingbacks vêem habilitados, mas podem ser desabilitados na inclusão ou alteração do post.&lt;/p&gt;

&lt;h2 id=&quot;aprovação&quot;&gt;Aprovação&lt;/h2&gt;

&lt;p&gt;Trackbacks e pingbacks de blogs externos precisam de aprovação, portanto só serão exibidos se o blog referenciado aprovar a solicitação.&lt;/p&gt;

&lt;p&gt;Já trackbacks e pingbacks de um blog para o si próprio são automaticamente aprovados, porém podem ser rejeitados posteriormente.&lt;/p&gt;

&lt;h2 id=&quot;seo&quot;&gt;SEO&lt;/h2&gt;

&lt;p&gt;Trackbacks e pingbacks ajudam na otimização de buscas &lt;a href=&quot;http://en.wikipedia.org/wiki/Search_engine_optimization&quot;&gt;SEO&lt;/a&gt;, dado que aumentam os links para o seu blog, e consequentemente o seu posicionamento.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Tag Files - E não é que é bom</title>
   <link href="http://www.pablocantero.com//blog/2010/09/02/tag-files-e-nao-e-que-e-bom-2/"/>
   <updated>2010-09-02T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/09/02/tag-files-e-nao-e-que-e-bom-2</id>
   
   <content type="html">&lt;p&gt;Tag Files são uma forma rápida de encapsular conteúdos em páginas JSP. Elas são muito “mais fáceis” de implementar que uma tag library, mesmo que seja uma &lt;a href=&quot;http://download-llnw.oracle.com/javaee/1.4/api/javax/servlet/jsp/tagext/SimpleTagSupport.html&quot;&gt;javax.servlet.jsp.tagext.SimpleTagSupport&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Como disse um &lt;a href=&quot;http://blogs.abril.com.br/java-cabeca&quot;&gt;grande amigo meu&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Tag File é um JSP com mais poderes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Vou utilizar um exemplo real, que eu precisava exibir a data de sincronização de um sistema legado com um web services, inicialmente eu estava exibindo a data da última sincronização, sempre que sincronizava eu atribuía um &lt;code class=&quot;highlighter-rouge&quot;&gt;new Date&lt;/code&gt; para no atributo lastSync. O problema é o timezone do servidor era diferente do que o do cliente. Para resolver o problema optei para exibir a diferença em minutos, horas e dias da última sincronização.&lt;/p&gt;

&lt;h2 id=&quot;tag-file-datediff&quot;&gt;Tag File DateDiff&lt;/h2&gt;

&lt;p&gt;Para armazenar as Tag Files eu criei o seguinte diretório WEB-INF/tags/, sendo que qualquer arquivo .tag, que for salvo nesse diretório será uma nova Tag File.&lt;/p&gt;

&lt;h2 id=&quot;web-inftagsdatedifftag&quot;&gt;WEB-INF/tags/dateDiff.tag&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;%@ tag import=&quot;java.util.Date&quot; body-content=&quot;empty&quot;%&amp;gt;
&amp;lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/core&quot; prefix=&quot;c&quot;%&amp;gt;
&amp;lt;%@ attribute name=&quot;date1&quot; required=&quot;true&quot; rtexprvalue=&quot;true&quot; type=&quot;java.util.Date&quot;%&amp;gt;
&amp;lt;%@ attribute name=&quot;date2&quot; required=&quot;false&quot; rtexprvalue=&quot;true&quot; type=&quot;java.util.Date&quot;%&amp;gt;
&amp;lt;%
Date date1 = (Date) jspContext.getAttribute(&quot;date1&quot;);
Date date2 = (Date) jspContext.getAttribute(&quot;date2&quot;);
if (date2 == null) {
date2 = new Date();
}
int diffMinutes = (int) ((date2.getTime() - date1.getTime()) / 1000) / 60;
int diffHours = diffMinutes / 60;
int diffDays = diffHours / 24;
String dateDiffMsg = &quot;&quot;;
if (diffMinutes &amp;lt; 60) {
dateDiffMsg = diffMinutes + &quot; minutos&quot;;
} else if (diffHours &amp;lt; 24) {
dateDiffMsg = diffHours + &quot; horas&quot;;
} else {
dateDiffMsg = diffDays + &quot; dias&quot;;
}
jspContext.setAttribute(&quot;dateDiffMsg&quot;, dateDiffMsg);
%&amp;gt;
${dateDiffMsg}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(discussões a parte sobre utilizar scriptlets ou não)&lt;/p&gt;

&lt;h2 id=&quot;para-utilizar-a-tag-file-datedifftag&quot;&gt;Para utilizar a Tag File dateDiff.tag&lt;/h2&gt;

&lt;p&gt;Todos os .tag salvos em WEB-INF/tags podem ser acessados como métodos após a inclusão da diretiva taglib.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;%@&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;taglib&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uri=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://java.sun.com/jsp/jstl/core&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;prefix=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;%@&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;taglib&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;prefix=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fn&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uri=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://java.sun.com/jsp/jstl/functions&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;%@&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;taglib&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;tagdir=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/WEB-INF/tags&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;prefix=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;h&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
...
&lt;span class=&quot;nt&quot;&gt;&amp;lt;table&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;box-table-a&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;thead&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Name&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Full Name&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Quantity On Hand&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Quantity On SalesOrder&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Quantity On Order&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Última Sincronização&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/thead&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;tfoot&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;td&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;colspan=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;5&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Total de registros&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;${fn:length(requestScope.itemInventoryRetList)}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/tfoot&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;tbody&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;c:forEach&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;items=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;${requestScope.itemInventoryRetList}&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;var=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;itemInventoryRet&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;${itemInventoryRet.name}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;${itemInventoryRet.fullName}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;${itemInventoryRet.quantityOnHand}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;${itemInventoryRet.quantityOnSalesOrder}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;${itemInventoryRet.quantityOnOrder}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Nossa Tag File em ação --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;h:dateDiff&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;date1=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;${itemInventoryRet.lastQuickBooksSync}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/c:forEach&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</content>

 </entry>
 
 <entry>
   <title>Criação de Virtual Hosts: MAMP + Apache + Hosts</title>
   <link href="http://www.pablocantero.com//blog/2010/09/01/criacao-de-virtual-hosts-mamp-apache-hosts/"/>
   <updated>2010-09-01T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/09/01/criacao-de-virtual-hosts-mamp-apache-hosts</id>
   
   <content type="html">&lt;p&gt;O acrônimo &lt;a href=&quot;http://www.mamp.info&quot;&gt;MAMP&lt;/a&gt; é um programa free para o Mac OSX que instala e configura automáticamente o Apache, MySQL, PHP e phpMyAdmin.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;__M__ac OSX&lt;/li&gt;
  &lt;li&gt;__A__pache&lt;/li&gt;
  &lt;li&gt;__M__ySQL&lt;/li&gt;
  &lt;li&gt;__P__HP&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;virtual-hosts&quot;&gt;Virtual Hosts&lt;/h2&gt;

&lt;p&gt;Primeiro passo é configurar o arquivos hosts para resolver nomes como projeto1.local, projeto2.local etc como 127.0.0.1.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo vi /private/etc/hosts##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1 localhost
255.255.255.255 broadcasthost
::1 localhost
fe80::1%lo0 localhost
127.0.0.1 projeto1.local
127.0.0.1 projeto2.local
# ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Lembrando que para salvar e sair do vi o comando é ESC + “:” + wq!.&lt;/p&gt;

&lt;p&gt;Feito isso o MAC irá resolver &lt;a href=&quot;http://projeto1.local&quot;&gt;projeto1.local&lt;/a&gt; como &lt;a href=&quot;http://127.0.0.1&quot;&gt;127.0.0.1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Para adicionar o Virtual Host basta adicionar no &lt;code class=&quot;highlighter-rouge&quot;&gt;/Applications/MAMP/conf/apache/httpd.conf&lt;/code&gt; as entradas abaixo.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NameVirtualHost *

&amp;lt;VirtualHost *&amp;gt;
 DocumentRoot &quot;/Applications/MAMP/htdocs&quot;
 ServerName localhost
&amp;lt;/VirtualHost&amp;gt;

&amp;lt;VirtualHost *&amp;gt;
 DocumentRoot &quot;/Users/pablo/workspace/projeto1&quot;
 ServerName projeto1
&amp;lt;/VirtualHost&amp;gt;

&amp;lt;VirtualHost *&amp;gt;
 DocumentRoot &quot;/Users/pablo/workspace/projeto2&quot;
 ServerName projeto2
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Pronto! Agora basta acessar &lt;a href=&quot;http://projeto1.local&quot;&gt;projeto1.local&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;principais-referências&quot;&gt;Principais referências&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://www.sawmac.com/mamp/virtualhosts/index.php&quot;&gt;sawmac.com/mamp/virtualhosts/&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://support.apple.com/kb/TA27291?viewlocale=en_US&quot;&gt;support.apple.com/kb/TA27291?viewlocale=en_US&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</content>

 </entry>
 
 <entry>
   <title>Publicando um web service JAX-WS + Spring MVC 3</title>
   <link href="http://www.pablocantero.com//blog/2010/08/28/publicando-um-web-service-jax-ws-spring-mvc-3/"/>
   <updated>2010-08-28T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/08/28/publicando-um-web-service-jax-ws-spring-mvc-3</id>
   
   <content type="html">&lt;p&gt;Em continuação aos posts &lt;a href=&quot;http://pablocantero.com/blog/2010/08/17/guia-pratico-do-quickbooks-web-connector-com-java/&quot;&gt;Guia prático do QuickBooks Web Connector com Java&lt;/a&gt; e &lt;a href=&quot;http://pablocantero.com/blog/2010/08/27/springsource-tools-suite-e-nao-e-que-e-bom/&quot;&gt;SpringSource Tools Suite – E não é que é bom&lt;/a&gt;, vou apresentar uma forma fácil de integrar os Web Services gerados com o &lt;a href=&quot;https://jax-ws.dev.java.net/jax-ws-ea3/docs/wsimport.html&quot;&gt;wsimport&lt;/a&gt; com o Spring MVC 3, criando um projeto do ZERO, utilizando o template “Spring MVC Project” do SpringSource Tools Suite.&lt;/p&gt;

&lt;p&gt;##Primeiro passo - Criando um projeto Spring MVC&lt;/p&gt;

&lt;p&gt;File -&amp;gt; New -&amp;gt; Other … -&amp;gt; SpringSource Tools Suite -&amp;gt; Spring Template Project -&amp;gt; Spring MVC Project&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/Screen-shot-2010-08-28-at-6.46.38-PM.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;##Segundo passo - Criando um Web Services com wsimport&lt;/p&gt;

&lt;p&gt;Para criar o Web Service basta acessar o diretório src/main/java e executar o wsimport, conforme o post &lt;a href=&quot;http://pablocantero.com/blog/2010/08/17/guia-pratico-do-quickbooks-web-connector-com-java/&quot;&gt;Guia prático do QuickBooks Web Connector com Java&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cd /Users/pablo/workspace/JavaQuickBooksWebConnector/src/
wsimport http://developer.intuit.com/uploadedFiles/Support/QBWebConnectorSvc.wsdl -s . -p com.cantero.quickbooks.ws
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;##Terceiro passo - Criando uma classe de implementação&lt;/p&gt;

&lt;p&gt;File -&amp;gt; New -&amp;gt; Class&lt;/p&gt;

&lt;p&gt;Vamos criar uma nova classe para implementar a interface QBWebConnectorSvcSoap criada anteriormente pelo wsimport. Lembre-se de atualizar (F5) o Eclipse após o wsimport, para ele carregar as classes e interfaces recém geradas pelo wsimport por fora do Eclipse.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/Screen-shot-2010-08-28-at-6.52.52-PM.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Após criada a classe basta adicionar as anotações &lt;a href=&quot;http://jax-ws.java.net/&quot;&gt;JAX-WS&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cantero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;teste_ws&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ws&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;javax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jws&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WebService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WebService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;serviceName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;QBWebConnectorSvcSoapImpl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;endpointInterface&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;com.cantero.teste_ws.ws.QBWebConnectorSvcSoap&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QBWebConnectorSvcSoapImpl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QBWebConnectorSvcSoap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;##Quarto passo - Publicando o Web Service&lt;/p&gt;

&lt;p&gt;Adicionando as definições do Web Service gerado no arquivo app-config.xml gerado pelo template Spring MVC Project.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;bean&amp;gt;
 &amp;lt;property name=&quot;baseAddress&quot; value=&quot;http://192.168.0.137:9801/&quot; /&amp;gt;
&amp;lt;/bean&amp;gt;

&amp;lt;bean id=&quot;QBWebConnectorSvcSoapImpl&quot;
 class=&quot;com.cantero.teste_ws.ws.QBWebConnectorSvcSoapImpl&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;##Quinta passo - Acessando seu Web Service&lt;/p&gt;

&lt;p&gt;Se tudo deu certo após iniciar o seu servidor seu Web Service estará disponível na url abaixo.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://192.168.0.137:9801/QBWebConnectorSvcSoapImpl?wsdl&quot;&gt;192.168.0.137:9801/QBWebConnectorSvcSoapImpl?wsdl&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;##Considereções&lt;/p&gt;

&lt;p&gt;Existem outras formas para integrar Web Services com Spring, algumas delas estão disponíveis na própria &lt;a href=&quot;http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/remoting.html#remoting-web-services-jaxws-export-standalone&quot;&gt;documentação do Spring&lt;/a&gt;, que foi da onde eu me baseie para criar esse post.&lt;/p&gt;

&lt;p&gt;A integração com &lt;a href=&quot;http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/remoting/jaxws/SimpleJaxWsServiceExporter.html&quot;&gt;SimpleJaxWsServiceExporter&lt;/a&gt; é semelhante ao EndPoint.publish disponível no Java SE.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public class Main {
 public static void main(String[] args) {
 Endpoint.publish(&quot;http://192.168.0.137:9801/QBWebConnectorSvcSoapImpl&quot;,
 new QBWebConnectorSvcSoapImpl());
 }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Fiz um exemplo com o EndPoint.publish no post &lt;a href=&quot;http://pablocantero.com/blog/2010/08/17/guia-pratico-do-quickbooks-web-connector-com-java/&quot;&gt;Guia prático do QuickBooks Web Connector com Java&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Tem uma &lt;a href=&quot;http://forum.springsource.org/showthread.php?t=50814&quot;&gt;thread no forum do SpringSource&lt;/a&gt; que questiona se utilizar o SimpleJaxWsServiceExporter pode acarretar em algum problema performance.&lt;/p&gt;

&lt;p&gt;##Principais referências&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/remoting.html#remoting-web-services-jaxws-export-standalone&quot;&gt;static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/remoting.html#remoting-web-services-jaxws-export-standalone&lt;/a&gt;&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>SpringSource Tools Suite</title>
   <link href="http://www.pablocantero.com//blog/2010/08/27/springsource-tools-suite-e-nao-e-que-e-bom/"/>
   <updated>2010-08-27T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/08/27/springsource-tools-suite-e-nao-e-que-e-bom</id>
   
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://www.springsource.com/products/springsource-tool-suite-download&quot;&gt;SpringSource Tools Suite&lt;/a&gt; é mais uma IDE baseada no Eclipse, mas não é por ser mais uma que não vale a pena. Pelo contrário, é uma ótima IDE, principalmente para quem trabalha com o família Spring-*, FACILITA MUITO!&lt;/p&gt;

&lt;p&gt;E o melhor agora é &lt;a href=&quot;http://www.springsource.com/products/springsource-tool-suite-download&quot;&gt;FREE&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Preencher o cadastro e pronto!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/Screen-shot-2010-08-27-at-5.38.28-PM.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;O Wizard de New Project do Eclipse &lt;code class=&quot;highlighter-rouge&quot;&gt;File -&amp;gt; New -&amp;gt; Other...&lt;/code&gt; tem várias opções para criação de projetos.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/Screen-shot-2010-08-27-at-5.37.36-PM.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Escolhendo Spring Template Project.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/Screen-shot-2010-08-27-at-5.37.29-PM.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Escolhendo Spring MVC Project, ele já cria um projeto utilizando maven archetype &lt;a href=&quot;http://appfuse.org&quot;&gt;AppFuse&lt;/a&gt; com Spring MVC 3,
toda a estrutura do projeto é montada com Maven Capabilities, TDD etc.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Criando uma aplicação no Heroku</title>
   <link href="http://www.pablocantero.com//blog/2010/08/21/criando-uma-aplicacao-no-heroku/"/>
   <updated>2010-08-21T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/08/21/criando-uma-aplicacao-no-heroku</id>
   
   <content type="html">&lt;h2 id=&quot;breve-introdução&quot;&gt;Breve introdução&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://heroku.com/&quot;&gt;Heroku&lt;/a&gt; é um cloud &lt;a href=&quot;http://en.wikipedia.org/wiki/Platform_as_a_service&quot;&gt;PaaS&lt;/a&gt;, muito fácil para utilizar em projetos Ruby e agora com o novo stack o &lt;a href=&quot;https://devcenter.heroku.com/articles/cedar&quot;&gt;Cedar&lt;/a&gt;, também é possível utilizá-lo com outras linguagens e plataformas.&lt;/p&gt;

&lt;p&gt;A idéia de um PaaS é de abstrair a complexidade de um sysadmin, toda a parte de hardware e configuração do servidor não fica sobe a responsabilidade de quem desenvolve, fica por conta de quem provê o serviço.&lt;/p&gt;

&lt;h2 id=&quot;o-que-precisa-para-funcionar&quot;&gt;O que precisa para funcionar?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Ruby on Rails instalado&lt;/li&gt;
  &lt;li&gt;Git instalado&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://devcenter.heroku.com/articles/quickstart&quot;&gt;Ler o Getting Started with Heroku&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Ter &lt;a href=&quot;http://heroku.com/signup&quot;&gt;uma conta no Heroku&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Ter o &lt;a href=&quot;https://toolbelt.heroku.com/&quot;&gt;Heroku toolbelt&lt;/a&gt; ou a &lt;a href=&quot;http://rubygems.org/gems/heroku&quot;&gt;gem Heroku&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Configurar as chaves ssh: &lt;a href=&quot;http://help.github.com/mac-key-setup/&quot;&gt;Mac&lt;/a&gt;, &lt;a href=&quot;http://help.github.com/msysgit-key-setup/&quot;&gt;Windows&lt;/a&gt;, &lt;a href=&quot;http://help.github.com/linux-key-setup/&quot;&gt;Linux&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;criando-uma-aplicação-básica&quot;&gt;Criando uma aplicação básica&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rails myapp
cd myapp
git init
git add .
git commit -a -m 'first commit'
heroku create myapp
git push heroku master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;configurando-gems&quot;&gt;Configurando gems&lt;/h2&gt;

&lt;p&gt;Se a sua aplicação usar gems, basta configurar um arquivo &lt;a href=&quot;http://docs.heroku.com/gems&quot;&gt;.gems&lt;/a&gt; ou Gemfile que o Heroku se encarregará de instalar as gems.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;touch .gems
git add .gems
git commit -a -m &quot;Adding .gems&quot;
git push heroku master
cat .gems
rails -v 2.3.5
geokit -v 1.5.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;para-executar-os-migrates&quot;&gt;Para executar os migrates&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;heroku rake db:migrate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>

 </entry>
 
 <entry>
   <title>GAE + Eclipse + Maven</title>
   <link href="http://www.pablocantero.com//blog/2010/08/20/gae-eclipse-maven/"/>
   <updated>2010-08-20T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/08/20/gae-eclipse-maven</id>
   
   <content type="html">&lt;p&gt;Esse é um tutorial para demonstrar como utilizar o &lt;a href=&quot;http://code.google.com/appengine&quot;&gt;GAE&lt;/a&gt;, com &lt;a href=&quot;http://maven.apache.org&quot;&gt;Maven&lt;/a&gt; (&lt;a href=&quot;http://code.google.com/p/maven-gae-plugin/&quot;&gt;maven-gae-plugin&lt;/a&gt;) no &lt;a href=&quot;http://www.eclipse.org&quot;&gt;Eclipse&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;O código fonte do guia está disponível no &lt;a href=&quot;http://github.com/phstc/testapp-gae-maven-eclipse&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;meu-setup&quot;&gt;Meu setup&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Mac OSX 10.6.4 x86_64&lt;/li&gt;
  &lt;li&gt;Java version: 1.6.0_20&lt;/li&gt;
  &lt;li&gt;Eclipse JEE Helios - &lt;a href=&quot;http://www.eclipse.org/downloads/download.php?file=/technology/epp/downloads/release/helios/R/eclipse-jee-helios-macosx-cocoa-x86_64.tar.gz&quot;&gt;Mac OS X&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Apache Maven 2.2.0&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;criação-da-aplicação-com-um-archetype&quot;&gt;Criação da aplicação com um archetype&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;matilde:workspace pablo&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;mvn archetype:generate &lt;span class=&quot;nt&quot;&gt;-DarchetypeGroupId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;net.kindleit &lt;span class=&quot;nt&quot;&gt;-DarchetypeArtifactId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;gae-archetype-jsp &lt;span class=&quot;nt&quot;&gt;-DarchetypeVersion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0.6.0 &lt;span class=&quot;nt&quot;&gt;-DgroupId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;com.cantero.testapp_cantero &lt;span class=&quot;nt&quot;&gt;-DartifactId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;testapp-cantero &lt;span class=&quot;nt&quot;&gt;-DarchetypeRepository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;http://maven-gae-plugin.googlecode.com/svn/repository
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] Scanning &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;projects...
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] Searching repository &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;plugin with prefix: &lt;span class=&quot;s1&quot;&gt;'archetype'&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] &lt;span class=&quot;nt&quot;&gt;------------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] Building Maven Default Project
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO]    task-segment: &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;archetype:generate] &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;aggregator-style&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] &lt;span class=&quot;nt&quot;&gt;------------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] Preparing archetype:generate
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] No goals needed &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;project - skipping
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;archetype:generate &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;execution: default-cli&lt;span class=&quot;o&quot;&gt;}]&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] Generating project &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;Interactive mode
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] Archetype defined by properties
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] Using property: groupId &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; com.cantero.testapp_cantero
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] Using property: artifactId &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; testapp_cantero
Define value &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;property &lt;span class=&quot;s1&quot;&gt;'version'&lt;/span&gt;: 1.0-SNAPSHOT:
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] Using property: package &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; com.cantero.testapp_cantero
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] Using property: package &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; com.cantero.testapp_cantero
Define value &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;property &lt;span class=&quot;s1&quot;&gt;'gaeApplicationName'&lt;/span&gt;: : testapp-cantero
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] Using property: gaeApplicationVersion &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 0
Define value &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;property &lt;span class=&quot;s1&quot;&gt;'gaePluginVersion'&lt;/span&gt;: : 0.6.0
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] Using property: gaeVersion &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 1.3.5
Confirm properties configuration:
groupId: com.cantero.testapp_cantero
artifactId: testapp_cantero
version: 1.0-SNAPSHOT
package: com.cantero.testapp_cantero
package: com.cantero.testapp_cantero
gaeApplicationName: testapp_cantero
gaeApplicationVersion: 0
gaePluginVersion: 0.6.0
gaeVersion: 1.3.5
Y: Y
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] &lt;span class=&quot;nt&quot;&gt;------------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] BUILD SUCCESSFUL
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] &lt;span class=&quot;nt&quot;&gt;------------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] Total &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;: 1 minute 56 seconds
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] Finished at: Thu Aug 19 20:25:20 BRT 2010
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] Final Memory: 16M/81M
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] &lt;span class=&quot;nt&quot;&gt;------------------------------------------------------------------------&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;O nome quer for informado no &lt;code class=&quot;highlighter-rouge&quot;&gt;gaeApplicationName&lt;/code&gt;, deve ser um nome válido de aplicação no GAE, ele será posteriormente usado no deploy.&lt;/p&gt;

&lt;p&gt;Para o &lt;code class=&quot;highlighter-rouge&quot;&gt;gaePluginVersion&lt;/code&gt; foi utilizada a versão 0.6.0.
As versões estão disponíveis no &lt;a href=&quot;http://maven-gae-plugin.googlecode.com/svn/repository/net/kindleit/gae-archetype-jsp/&quot;&gt;repositório&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;adicionando-eclipse-capabilities&quot;&gt;Adicionando Eclipse capabilities&lt;/h2&gt;

&lt;p&gt;``bash
mvn eclipse:eclipse&lt;/p&gt;

&lt;p&gt;matilde:workspace pablo$ cd testapp-cantero/
matilde:testapp-cantero pablo$ mvn eclipse:eclipse
[INFO] Scanning for projects…
[INFO] ————————————————————————
[ERROR] FATAL ERROR
[INFO] ————————————————————————
[INFO] Error building POM (may not be this project’s POM).&lt;/p&gt;

&lt;p&gt;Project ID: com.cantero.testapp_cantero:testapp-cantero
POM Location: /Users/pablo/workspace/testapp-cantero/pom.xml&lt;/p&gt;

&lt;p&gt;Reason: Resolving expression: ‘${name}’: Detected the following recursive expression cycle: [name] for project com.cantero.testapp_cantero:testapp_cantero at /Users/pablo/workspace/testapp-cantero/pom.xml&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
Caso aparece esse erro, bastar alterar o pom.xml do projeto

```xml
&amp;lt;name&amp;gt;testapp-cantero&amp;lt;/name&amp;gt;
&amp;lt;description&amp;gt;Aplicação teste GAE + Eclipse + Maven&amp;lt;/description&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voltando a executar &lt;code class=&quot;highlighter-rouge&quot;&gt;mvn eclipse:eclipse&lt;/code&gt; deve retornar BUILD SUCCESSFUL.&lt;/p&gt;

&lt;h2 id=&quot;importando-o-projeto-para-o-eclipse&quot;&gt;Importando o projeto para o Eclipse&lt;/h2&gt;

&lt;p&gt;Para importar o projeto para o Eclipse, bastar ir em File -&amp;gt; Import -&amp;gt; Existing Projects into Workspace -&amp;gt; [selecionar o diretório do projeto] e importar.&lt;/p&gt;

&lt;p&gt;Quando eu importei o projeto para o Eclipse, tinha algumas classes padrões criadas pelo archetype, mas elas estavam com packages errados, bastou corrigir os packages e os imports que o projeto ficou sem erros.&lt;/p&gt;

&lt;p&gt;Não esquecendo também de corrigir o caminho do &lt;code class=&quot;highlighter-rouge&quot;&gt;IndexServlet&lt;/code&gt; no &lt;code class=&quot;highlighter-rouge&quot;&gt;web.xml&lt;/code&gt; para o package selecionado.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;servlet&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;servlet-name&amp;gt;&lt;/span&gt;IndexServlet&lt;span class=&quot;nt&quot;&gt;&amp;lt;/servlet-name&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;servlet-class&amp;gt;&lt;/span&gt;com.cantero.testapp_cantero.web.IndexServlet&lt;span class=&quot;nt&quot;&gt;&amp;lt;/servlet-class&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/servlet&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;primeiro-deploy-no-gae&quot;&gt;Primeiro deploy no GAE&lt;/h2&gt;

&lt;p&gt;Na primeira vez é necessário alterar o código do pom.xml&lt;/p&gt;

&lt;p&gt;De:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;gae.version&amp;gt;1.3.5&amp;lt;/gae.version&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Para:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;gae.version&amp;gt;1.3.1&amp;lt;/gae.version&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A versão 1.3.5 não está disponível (até a data desse post no &lt;a href=&quot;http://mvnrepository.com/artifact/com.google.appengine/appengine-java-sdk&quot;&gt;repositório oficial do maven&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Para o primeiro deploy é necessário executar:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;mvn gae:unpack
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;mvn gae:deploy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Se tudo deu certo, sua aplicação estará disponível no GAE.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Guia prático do QuickBooks Web Connector com Java</title>
   <link href="http://www.pablocantero.com//blog/2010/08/17/guia-pratico-do-quickbooks-web-connector-com-java/"/>
   <updated>2010-08-17T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/08/17/guia-pratico-do-quickbooks-web-connector-com-java</id>
   
   <content type="html">&lt;p&gt;O &lt;a href=&quot;http://marketplace.intuit.com/webconnector&quot;&gt;QuickBooks Web Connector&lt;/a&gt; é uma ótima alternativa para fazer integrações com o &lt;a href=&quot;http://quickbooks.intuit.com&quot;&gt;QuickBooks&lt;/a&gt;, pois com QBWC não é necessário utilizar os componentes COM+, que limitam a escolha da tecnologia para ser utilizado na integração.&lt;/p&gt;

&lt;p&gt;O código fonte do guia está disponível no GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/phstc/JavaQuickBooksWebConnector&quot;&gt;github.com/phstc/JavaQuickBooksWebConnector&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;vantagens-do-quickbooks-web-connector&quot;&gt;Vantagens do QuickBooks Web Connector&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;A comunicação com o seu servidor e realizada através de Web services, dessa forma não é necessário atrelar sua aplicação com componentes COM+ (windows).&lt;/li&gt;
  &lt;li&gt;Os Web services podem ser desenvolvidos em qualquer linguagem e em qualquer plataforma, desde de que tenha suporte para Web services.&lt;/li&gt;
  &lt;li&gt;O QBWC pode ser configurado para invocar seu servidor de tempos em tempos, portanto não é necessário que o usuário faça uma intervenção manual, tendo que abrir um aplicativo, clicar em sincronizar etc. Basta configurar o QBWC para invocar o Web services no intervalo de tempo desejado.&lt;/li&gt;
  &lt;li&gt;É mais fácil para criar aplicações genéricas (SaaS), pois a cada requisição do QBWC, além de usuário e senha, ele envia identificação da aplicação, portanto um Web services pode atender a diversos clientes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;ambiente&quot;&gt;Ambiente&lt;/h2&gt;

&lt;p&gt;Para desenvolver a integração com Java, estou usando:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Eclipse&lt;/li&gt;
  &lt;li&gt;QuickBooks Enterprise Edition 10.0&lt;/li&gt;
  &lt;li&gt;QuickBooks Web Connector 2.0.0.139&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;primeiro-passo---criação-do-arquivo-qwc&quot;&gt;Primeiro passo - Criação do arquivo QWC&lt;/h2&gt;

&lt;p&gt;O arquivo QWC é o arquivo de configuração do seu Web services no QBWC.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;QBWCXML&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppName&amp;gt;&lt;/span&gt;WCWebService1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AppName&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppID&amp;gt;&amp;lt;/AppID&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppURL&amp;gt;&lt;/span&gt;http://localhost/WCWebService/WCWebService.asmx&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AppURL&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppDescription&amp;gt;&lt;/span&gt;A short description for WCWebService1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AppDescription&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;AppSupport&amp;gt;&lt;/span&gt;http://developer.intuit.com&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AppSupport&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;UserName&amp;gt;&lt;/span&gt;iqbal1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UserName&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;OwnerID&amp;gt;&lt;/span&gt;{57F3B9B1-86F1-4fcc-B1EE-566DE1813D20}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OwnerID&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;FileID&amp;gt;&lt;/span&gt;{90A44FB5-33D9-4815-AC85-BC87A7E7D1EB}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/FileID&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;QBType&amp;gt;&lt;/span&gt;QBFS&lt;span class=&quot;nt&quot;&gt;&amp;lt;/QBType&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Scheduler&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;RunEveryNMinutes&amp;gt;&lt;/span&gt;2&lt;span class=&quot;nt&quot;&gt;&amp;lt;/RunEveryNMinutes&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Scheduler&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/QBWCXML&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;segundo-passo---wsimport-qbwebconnectorsvcwsdl&quot;&gt;Segundo passo - wsimport QBWebConnectorSvc.wsdl&lt;/h2&gt;

&lt;p&gt;Para construir um Web services a partir do WSDL será utilizado o &lt;a href=&quot;http://download.oracle.com/docs/cd/E17802_01/webservices/webservices/docs/2.0/jaxws/wsimport.html&quot;&gt;wsimport&lt;/a&gt;, que gerará o Web Services JAX-WS compatível.&lt;/p&gt;

&lt;p&gt;O WSDL para o Web Services está disponível na url &lt;a href=&quot;http://developer.intuit.com/uploadedFiles/Support/QBWebConnectorSvc.wsdl&quot;&gt;developer.intuit.com/uploadedFiles/Support/QBWebConnectorSvc.wsdl&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Para iniciar criei um projeto Java Básico com o Eclipse “File -&amp;gt; New -&amp;gt; Java Project -&amp;gt; JavaQuickBooksWebConnector”.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /Users/pablo/workspace/JavaQuickBooksWebConnector/src/

wsimport http://developer.intuit.com/uploadedFiles/Support/QBWebConnectorSvc.wsdl &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; com.cantero.quickbooks.ws
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Parâmetros&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;-s para especificar o diretório onde será gerando o código fonte&lt;/li&gt;
  &lt;li&gt;-p para especificar o package&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;terceiro-passo---criando-uma-classe-de-implementação&quot;&gt;Terceiro passo - Criando uma classe de implementação&lt;/h2&gt;

&lt;p&gt;A definição dos métodos exigidos pela interface QBWebConnectorSvcSoap estão definidos no &lt;a href=&quot;http://developer.intuit.com/qbsdk-current/doc/pdf/qbwc_proguide.pdf&quot;&gt;QuickBooks Web Connector Programmer’s Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A linguagem para requisições do QBWC é &lt;a href=&quot;http://developer.intuit.com/qbSDK-Current/Common/newOSR/index.html&quot;&gt;qbXML&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;classe-de-implementação&quot;&gt;Classe de implementação&lt;/h3&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;cantero&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;quickbooks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ws&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;java.util.ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;javax.jws.WebService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;cm&quot;&gt;/*
* http://developer.intuit.com/qbsdk-current/doc/pdf/qbwc_proguide.pdf
*/&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@WebService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endpointInterface&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;com.cantero.ws.client.QBWebConnectorSvcSoap&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ItemQueryRqSoapImpl&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QBWebConnectorSvcSoap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ArrayOfString&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;authenticate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strUserName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strPassword&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ArrayOfString&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ArrayOfString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The first element is a token for the web connector’s session&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//To use the currently open company, specify an empty string&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;closeConnection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ticket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// TODO Auto-generated method stub&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;connectionError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ticket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hresult&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// TODO Auto-generated method stub&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getLastError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ticket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// TODO Auto-generated method stub&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;cm&quot;&gt;/*
  * A positive integer less than 100 represents the percentage of work completed. A value of 1 means one percent complete, a value of 100 means 100 percent complete--there is no more work. A negative value means an error has occurred and the Web Connector responds to this with a getLastError call. The negative value could be used as a custom error code.
  */&lt;/span&gt;
  &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;receiveResponseXML&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ticket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hresult&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// a value of 100 means 100 percent complete--there is no more work&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sendRequestXML&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ticket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strHCPResponse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strCompanyFileName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qbXMLCountry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qbXMLMajorVers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qbXMLMinorVers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Example qbXML to Query for an Item&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// http://www.consolibyte.com/wiki/doku.php/quickbooks_qbxml_itemquery&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;?xml version=\&quot;1.0\&quot; encoding=\&quot;utf-8\&quot;?&amp;gt;&amp;lt;?qbxml version=\&quot;7.0\&quot;?&amp;gt;&amp;lt;QBXML&amp;gt;&amp;lt;QBXMLMsgsRq onError=\&quot;stopOnError\&quot;&amp;gt;&amp;lt;ItemQueryRq requestID=\&quot;SXRlbVF1ZXJ5fDEyMA==\&quot;&amp;gt;&amp;lt;OwnerID&amp;gt;0&amp;lt;/OwnerID&amp;gt;&amp;lt;/ItemQueryRq&amp;gt;&amp;lt;/QBXMLMsgsRq&amp;gt;&amp;lt;/QBXML&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A instrução &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;OwnerID&amp;gt;0&amp;lt;/OwnerID&amp;gt;&lt;/code&gt; é utilizada para que o QuickBooks retorne os Custom Fields. Na versão QuickBooks Enterprise Edition 10, pode ser inserido mais de 5 custom fields, &lt;a href=&quot;https://idnforums.intuit.com/messageview.aspx?catid=7&amp;amp;threadid=13998&quot;&gt;mas até o momento só é possível recuperar os 5 primeiros utilizando qbXML&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;quarto-passo---importando-o-qwc-no-qbwc&quot;&gt;Quarto passo - Importando o QWC no QBWC&lt;/h2&gt;

&lt;p&gt;Basta ir ao QuickBooks Web Connector “Add an application -&amp;gt; Selecionar o QWC -&amp;gt; Autorizar acesso no QuickBooks”.&lt;/p&gt;

&lt;h2 id=&quot;quinto-passo---testando-o-web-services&quot;&gt;Quinto passo - Testando o Web services&lt;/h2&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;cantero&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;quickbooks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ws&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;javax.xml.ws.Endpoint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Main&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Endpoint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://192.168.0.137:54321/ItemQueryRqSoapImpl&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ItemQueryRqSoapImpl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Para executar o Main diretamente pelo Eclipse basta ir em “Run As -&amp;gt; Java Application, Debug As -&amp;gt; Java Application”.&lt;/p&gt;

&lt;p&gt;Se tudo deu certo a url &lt;a href=&quot;http://192.168.0.137:54321/ItemQueryRqSoapImpl?wsdl&quot;&gt;http://192.168.0.137:54321/ItemQueryRqSoapImpl?wsdl&lt;/a&gt; deverá exibir o seu wsdl.&lt;/p&gt;

&lt;h2 id=&quot;principais-referências&quot;&gt;Principais referências&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://marketplace.intuit.com/webconnector&quot;&gt;marketplace.intuit.com/webconnector&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://developer.intuit.com/qbsdk-current/doc/pdf/qbwc_proguide.pdf&quot;&gt;QuickBooks Web Connector Programmer’s Guide&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://developer.intuit.com/qbSDK-Current/Common/newOSR/index.html&quot;&gt;Referência para qbXML&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>

 </entry>
 
 <entry>
   <title>Usando ActiveRecordStore nas Rails Sessions</title>
   <link href="http://www.pablocantero.com//blog/2010/08/12/usando-activerecordstore-nas-rails-sessions/"/>
   <updated>2010-08-12T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/08/12/usando-activerecordstore-nas-rails-sessions</id>
   
   <content type="html">&lt;p&gt;A sessão padrão do Rails é o Cookie Store, que tem um limite de 4K, sendo usada tipicamente usada para armazenar ids de objetos persistidos na base de dados ou pequenas informações.&lt;/p&gt;

&lt;p&gt;Caso você receba um &lt;code class=&quot;highlighter-rouge&quot;&gt;ActionController::Session::CookieStore::CookieOverflow&lt;/code&gt; como o abaixo, você pode tentar o ActiveRecordStore ao invés do Cookie Store.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/!\ FAILSAFE /!\ Thu Jul 29 21:23:39 -0300 2010
 Status: 500 Internal Server Error
 ActionController::Session::CookieStore::CookieOverflow
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/actionpack-2.3.5/lib/action_controller/session/cookie_store.rb:102:in `call'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(eu só consegui resolver esse problema removendo os cookies do browser, reiniciar o servidor não resolveu para mim.)&lt;/p&gt;

&lt;p&gt;##ActiveRecordStore&lt;/p&gt;

&lt;p&gt;O ActiveRecordStore é mais lento que o Cookie Store, porém seu tamanho só é limitado pela base dados.&lt;/p&gt;

&lt;p&gt;Para usar o ActiveRecordStore basta adicionar a seguinte linha no environment.rb.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; config.action_controller.session_store = :active_record_store
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Criar as tabelas para armazenar a sessão.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rake db:sessions:create
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Esse é um exemplo básico para usar o ActiveRecordStore, na documentação oficial &lt;a href=&quot;http://oldwiki.rubyonrails.org/rails/pages/HowtoChangeSessionStore&quot;&gt;HowtoChangeSessionStore&lt;/a&gt; tem outras opções de configuração, assim como outras opções de persistência para a sessão.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Polymorphic associations com Rails</title>
   <link href="http://www.pablocantero.com//blog/2010/08/12/polymorphic-associations-com-rails-2/"/>
   <updated>2010-08-12T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/08/12/polymorphic-associations-com-rails-2</id>
   
   <content type="html">&lt;h2 id=&quot;polymorphic-associations&quot;&gt;Polymorphic associations&lt;/h2&gt;

&lt;p&gt;Se você precisa de um modelo genérico, por exemplo Address, que será associado com User, Customer, Store etc,  polymorphic associations pode ser uma boa opcão.&lt;/p&gt;

&lt;h2 id=&quot;passo-1---criar-o-modelo&quot;&gt;Passo 1 - Criar o modelo&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;script/generate model Address addressable_type:string addressable_id:integer name:string street1:string street1_number:string street2:string zip_code:string city:string state:string country:string

rake db:migrate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;passo-2-adicionar-belongs_to-no-addressrb&quot;&gt;Passo 2: Adicionar belongs_to no address.rb&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;belongs_to :addressable, :polymorphic =&amp;gt; true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;passo-3-adicionar-o-has_many-nos-models-que-utilizaram-address&quot;&gt;Passo 3: Adicionar o has_many nos models que utilizaram Address&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# customer.rb
class Customer &amp;lt; ActiveRecord::Base
has_many :addresses, :as =&amp;gt; :addressable, :dependent =&amp;gt; :destroy

# user.rb
class User &amp;lt; ActiveRecord::Base
has_many :addresses, :as =&amp;gt; :addressable, :dependent =&amp;gt; :destroy

# store.rb
class Store &amp;lt; ActiveRecord::Base
has_many :addresses, :as =&amp;gt; :addressable, :dependent =&amp;gt; :destroy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;principais-referências&quot;&gt;Principais referências&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://railsforum.com/viewtopic.php?id=5882&quot;&gt;railsforum.com/viewtopic.php?id=5882&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://m.onkey.org/2007/8/14/excuse-me-wtf-is-polymorphs&quot;&gt;m.onkey.org/2007/8/14/excuse-me-wtf-is-polymorphs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>

 </entry>
 
 <entry>
   <title>Criação de urls amigáveis com Rails</title>
   <link href="http://www.pablocantero.com//blog/2010/08/07/criacao-de-urls-amigaveis-com-rails/"/>
   <updated>2010-08-07T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/08/07/criacao-de-urls-amigaveis-com-rails</id>
   
   <content type="html">&lt;h2 id=&quot;atualização-do-post-18032012&quot;&gt;Atualização do post 18/03/2012&lt;/h2&gt;

&lt;p&gt;Para implementar urls amigáveis recomendo a gem &lt;a href=&quot;https://github.com/norman/friendly_id&quot;&gt;FriendlyId&lt;/a&gt;. &lt;a href=&quot;https://twitter.com/#!/rbates&quot;&gt;Ryan Bates&lt;/a&gt; fez um &lt;a href=&quot;http://railscasts.com/episodes/314-pretty-urls-with-friendlyid&quot;&gt;screencast&lt;/a&gt; bem ditático sobre a gem, recomendo!&lt;/p&gt;

&lt;p&gt;–&lt;/p&gt;

&lt;p&gt;Para quem usa Rails a partir da versão 2.2 é muito fácil criar urls amigáveis utilizando ActiveSupport::Inflector.parameterize.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ActiveSupport::Inflector.parameterize &quot;Criação de url amigáveis Rails&quot;
&amp;gt;&amp;gt; criacao-de-url-amigaveis-rails
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;O parameterize é um método adicionado diretamente na classe String.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class Product &amp;lt; ActiveRecord::Base
    def to_param
        &quot;#{id}-#{name.parameterize}&quot;
    end
end

Product.create(:name =&amp;gt; &quot;Produto Amigável&quot;) # Product#1
product_path(product_1) # /products/1-produto-amigavel
Product.find(&quot;1-produto-amigavel&quot;) # Product#1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;activesupportinflector&quot;&gt;ActiveSupport::Inflector&lt;/h2&gt;

&lt;p&gt;A classe &lt;a href=&quot;http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html&quot;&gt;Inflector&lt;/a&gt; tem muitos outros métodos que valem a pena serem conferidos.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Breve definição sobre EJB 3</title>
   <link href="http://www.pablocantero.com//blog/2010/08/05/breve-definicao-sobre-ejb-3/"/>
   <updated>2010-08-05T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/08/05/breve-definicao-sobre-ejb-3</id>
   
   <content type="html">&lt;p&gt;EJB 3 classifica os beans em três tipos fundamentais, baseados no que eles fazem:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Session beans&lt;/li&gt;
  &lt;li&gt;Message driven beans&lt;/li&gt;
  &lt;li&gt;Entity beans&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;session-beans&quot;&gt;Session beans&lt;/h2&gt;

&lt;p&gt;São beans com o propósito de realizar alguma operação de negócio  específica, como consultar o histórico de crédito de um cliente.&lt;/p&gt;

&lt;p&gt;Pode ser tanto com estado ou sem estado. Um &lt;strong&gt;session bean com estado&lt;/strong&gt; é bean típico para um carrinho de compras, onde os itens são adicionados no  carrinho passo a passo, por isso o bean precisa manter o estado. Já um &lt;strong&gt;session bean sem estado&lt;/strong&gt; é um bean geralmente usado para operações como um consultar o histórico de crédito de um cliente, ou seja, para operações que não precisam guardar estado.&lt;/p&gt;

&lt;p&gt;Um session bean pode ser executado local, remoto por &lt;a href=&quot;http://pt.wikipedia.org/wiki/RMI&quot;&gt;RMI&lt;/a&gt; ou por Web Services. Sendo que somente os session beans sem estado, podem ser expostos como &lt;a href=&quot;http://pt.wikipedia.org/wiki/Web_service&quot;&gt;Web Services&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Um session bean não sobrevive a queda ou parada temporário do servidor.&lt;/p&gt;

&lt;p&gt;Os sessions beans necessitam de um EJB container para serem executados.&lt;/p&gt;

&lt;h2 id=&quot;message-driven-beans&quot;&gt;Message driven beans&lt;/h2&gt;

&lt;p&gt;Assim como os session beans, seu propósito é para realizar operações de negócio. A diferença e que as operação não recebem resposta automática, são executadas de forma assíncrona.&lt;/p&gt;

&lt;p&gt;Os MDBs são invocados  através de mensagens enviadas ao servidor de mensagens como IBM WebSphere MQ, SonicMQ, Oracle Advanced Queueing e TIBCO.&lt;/p&gt;

&lt;p&gt;Um exemplo comum de sua utilização é para operações que podem ser lentas e que não necessitem de resposta automática. Desde de um simples envio de email a integrações entre sistemas.&lt;/p&gt;

&lt;p&gt;Os MDBs necessitam de um EJB container para serem executados.&lt;/p&gt;

&lt;h2 id=&quot;entity-beans&quot;&gt;Entity beans&lt;/h2&gt;

&lt;p&gt;Os entities beans são os beans mais conhecidos e mais  utilizados, são as conhecidas entitdades JPA.&lt;/p&gt;

&lt;p&gt;JPA é uma especificação de &lt;a href=&quot;http://en.wikipedia.org/wiki/Object-relational_mapping&quot;&gt;ORM&lt;/a&gt;, onde as implementações mais conhecidas são pelos frameworks &lt;a href=&quot;http://pt.wikipedia.org/wiki/Hibernate&quot;&gt;Hibernate&lt;/a&gt; e &lt;a href=&quot;http://en.wikipedia.org/wiki/TopLink&quot;&gt;Oracle TopLink&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Entity bean é a forma que EJB 3 controla a persistência de dados, definindo o padrão para:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Criação de metadados, mapeamento de entidades para tabelas do modelo relacional&lt;/li&gt;
  &lt;li&gt;API EntityManager, API para realização de CRUD (create, retrieve, update e delete)&lt;/li&gt;
  &lt;li&gt;Java Persistence Query Language (JPQL), linguagem parece com o SQL ANSI para pesquisa de dados persistidos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Os entities beans não necessitam de um container EJB para serem executados.&lt;/p&gt;
</content>

 </entry>
 
 <entry>
   <title>Utilizando o Rails form select</title>
   <link href="http://www.pablocantero.com//blog/2010/07/26/utilizando-o-rails-form-select/"/>
   <updated>2010-07-26T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/07/26/utilizando-o-rails-form-select</id>
   
   <content type="html">&lt;p&gt;Pesquisei bastante na internet, até achar uma &lt;a href=&quot;http://www.ruby-forum.com/topic/160291&quot;&gt;thread&lt;/a&gt; demonstrando como utilizar o form select com um array de objetos.&lt;/p&gt;

&lt;p&gt;Geralmente meus selects são populados com arrays do &lt;a href=&quot;http://api.rubyonrails.org/classes/ActiveRecord/Base.html#M001780&quot;&gt;ActiveRecord&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@users = User.all
&amp;lt;% form_for @user, :html =&amp;gt; { :method =&amp;gt; :post } do |f| %&amp;gt;
 &amp;lt;%= f.select :user_id, @users %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Desta forma o select imprimirá com o &lt;a href=&quot;http://ruby-doc.org/core/classes/Object.html#M000359&quot;&gt;to_s&lt;/a&gt; de cada objeto do array, para exibir o key (id) e value (descrição), basta utilizar o método &lt;a href=&quot;http://ruby-doc.org/core/classes/Array.html#M002187&quot;&gt;collect&lt;/a&gt;, que é uma maneira fácil e rápida para esse propósito.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;% form_for @user, :html =&amp;gt; { :method =&amp;gt; :post } do |f| %&amp;gt;
 &amp;lt;%= f.select :user_id, @users.collect { |user| [user.name, user.id] } %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;outras-maneiras-para-para-usar-form-select&quot;&gt;Outras maneiras para para usar form select&lt;/h2&gt;

&lt;p&gt;Com um array simples:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;%= f.select :estados, ['SP', 'PA', 'RJ'] %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Com um array com arrays:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;%= f.select :estados, [['São Paulo', 'SP'], ['Pará', 'PA'], ['Rio de Janeiro', 'RJ']] %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;principais-referências&quot;&gt;Principais referências&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html&quot;&gt;Documentação oficial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>

 </entry>
 
 <entry>
   <title>CentOS start Mongrel no boot</title>
   <link href="http://www.pablocantero.com//blog/2010/07/25/centos-start-mongrel-no-boot/"/>
   <updated>2010-07-25T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/07/25/centos-start-mongrel-no-boot</id>
   
   <content type="html">&lt;p&gt;Este post detalha os passos para iniciar, parar ou reiniciar uma aplicação Rails no CentOS.&lt;/p&gt;

&lt;h2 id=&quot;primeiro-passo---criando-um-script-bash&quot;&gt;Primeiro passo - Criando um script bash&lt;/h2&gt;

&lt;p&gt;Esse arquivo deve ser criado ou ter um link simbólico em &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/init.d/ror&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;permissões-para-no-arquivo&quot;&gt;Permissões para no arquivo:&lt;/h3&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;chmod 754 /etc/init.d/ror
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;7 - Dono do arquivo tem todas permissões sobre o arquivo (leitura, execução e gravação)&lt;/li&gt;
  &lt;li&gt;5 - O grupo poder ler e executar&lt;/li&gt;
  &lt;li&gt;4 - Outros podem ler&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;script-bash&quot;&gt;Script bash&lt;/h3&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# ror&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# chkconfig: 2345 82 82&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# description: ror&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;start_rails&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
 &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Starting app/current&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/home/user/rails_apps/app/current&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 mongrel_rails start &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; production &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 12006 &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;stop_rails&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
 &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Stopping app/current&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/home/user/rails_apps/app/current&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 mongrel_rails stop&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in
 &lt;/span&gt;start&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   start_rails&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
 stop&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   stop_rails&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
 restart&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   stop_rails&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
   start_rails&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;esac&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;segundo-passo---adicionando-o-script-bash-como-serviço&quot;&gt;Segundo passo - Adicionando o script bash como serviço&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;chkconfig --add ror
chkconfig --list ror
ror 0:off 1:off 2:on 3:on 4:on 5:on 6:off
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;principais-referências&quot;&gt;Principais referências&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://pt.wikipedia.org/wiki/Chmod&quot;&gt;Chmod&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.centos.org/docs/5/html/Deployment_Guide-en-US/s1-services-chkconfig.html&quot;&gt;CentOS chkconfig&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>

 </entry>
 
 <entry>
   <title>Guia rápido de configuração do Capistrano com o GitHub</title>
   <link href="http://www.pablocantero.com//blog/2010/07/23/guia-rapido-de-configuracao-do-capistrano-com-o-github/"/>
   <updated>2010-07-23T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/07/23/guia-rapido-de-configuracao-do-capistrano-com-o-github</id>
   
   <content type="html">&lt;p&gt;Esse é um guia rápido de configuração do &lt;a href=&quot;http://www.capify.org/&quot;&gt;Capistrano&lt;/a&gt; com o &lt;a href=&quot;http://github.com&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;del&gt;Os passos desses post foram utilizados no projeto &lt;a href=&quot;http://click-anywhere-to-leave-a-message.com&quot;&gt;click-anywhere-to-leave-a-message.com&lt;/a&gt;, disponível para download no &lt;a href=&quot;http://github.com/phstc/click-anywhere-to-leave-a-message.com&quot;&gt;GitHub&lt;/a&gt;.&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;Após logado no GitHub, no seu &lt;a href=&quot;https://github.com&quot;&gt;dashboard&lt;/a&gt; aparecerá seus repositórios e um botão para criar um novo repositório.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.pablocantero.com/assets/images/posts/github-your-repositories-new-repository.png&quot; alt=&quot;GitHub - Your Repositories - New Repository&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Após criado o repositório, o GitHub mostrará as instruções para o seu primeiro commit.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; cd click-anywhere-to-leave-a-message.com
 git init
 touch README
 git add README
 git commit -m 'first commit'
 git remote add origin git@github.com:phstc/click-anywhere-to-leave-a-message.com.git
 git push origin master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;gitignore&quot;&gt;.gitignore&lt;/h2&gt;

&lt;p&gt;Para não compartilhar alguns arquivos no repositório, como credenciais do banco de dados, logs, arquivos temporários etc, basta criar um arquivo &lt;code class=&quot;highlighter-rouge&quot;&gt;.gitignore&lt;/code&gt; na raiz do projeto.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cat .gitignore
.DS_Store
*~
*.swp
tmp/*
log/*
backup/*
config/database.yml
Capfile
config/deploy.rb
db/*.sqlite3
mkmf.log
db/schema.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;instalando-e-adicionando-o-capistrano&quot;&gt;Instalando e adicionando o Capistrano&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cd click-anywhere-to-leave-a-message.com
gem install capistrano
capify .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;configurandoo-deployrb&quot;&gt;Configurando o deploy.rb&lt;/h2&gt;

&lt;p&gt;Após adicionar o Capistrano, bastará configurar o arquivo &lt;code class=&quot;highlighter-rouge&quot;&gt;deploy.rb&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;set :application, 'sua aplicacao'
set :user, 'seu usuario ssh'
set :password, 'sua senha shh'
set :domain, 'seu dominio'
set :mongrel_port, 'sua porta'
set :mongrel_nodes, &quot;4&quot;
set :rails_env,     &quot;production&quot;
set :server_hostname, 'seu hostname'
set :git_account, 'seu usuario do github'
set :scm_passphrase,  Proc.new { Capistrano::CLI.password_prompt('sua senha') }
role :web, 'seu dominio'
role :app, 'seu dominio'
role :db, 'seu dominio', :primary =&amp;gt; true
default_run_options[:pty] = true
set :repository,  &quot;git@github.com:#{git_account}/#{application}.git&quot;
set :scm, &quot;git&quot;
set :user, user
ssh_options[:forward_agent] = true
set :branch, &quot;master&quot;
set :deploy_via, :checkout
set :git_shallow_clone, 1
set :git_enable_submodules, 1
set :use_sudo, false
set :deploy_to, &quot;/home/#{user}/rails_apps/#{application}&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;adicionado-sua-public-key-no-github&quot;&gt;Adicionado sua public key no GitHub&lt;/h2&gt;

&lt;p&gt;Se aparecerem problemas como &lt;code class=&quot;highlighter-rouge&quot;&gt;Permission denied (publickey)&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;servers: [&quot;click-anywhere-to-leave-a-message.com&quot;]
 [click-anywhere-to-leave-a-message.com] executing command
 ** [click-anywhere-to-leave-a-message.com :: out] Permission denied (publickey).
 ** [click-anywhere-to-leave-a-message.com :: out] fatal: The remote end hung up unexpectedly
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Você deverá seguir o tutorial do próprio GitHub: &lt;a href=&quot;http://help.github.com/linux-key-setup/&quot;&gt;linux&lt;/a&gt;, &lt;a href=&quot;http://help.github.com/mac-key-setup/&quot;&gt;mac&lt;/a&gt; e &lt;a href=&quot;http://help.github.com/msysgit-key-setup/&quot;&gt;windows&lt;/a&gt; para configurar sua chave pública.&lt;/p&gt;

&lt;h2 id=&quot;fazendo-o-deploy-com-o-capistrano&quot;&gt;Fazendo o deploy com o Capistrano&lt;/h2&gt;

&lt;p&gt;Para fazer o deploy basta executar a instrução &lt;code class=&quot;highlighter-rouge&quot;&gt;cap deploy&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cd click-anywhere-to-leave-a-message.com
cap deploy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Se não aparecer nenhum erro (será???), seu Capistrano estará funcionando perfeitamente, no meu caso apareceu o seguinte erro:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;current/script/process/reaper: No such file or directory
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Para corrigi-lo eu tive que adicionar as tasks de start, stop e restart no deploy.rb.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;namespace :deploy do
 task :start, :roles =&amp;gt; :app do
 run &quot;cd #{current_path} &amp;amp;&amp;amp; mongrel_rails start -e production -p #{mongrel_port} -d&quot;
 end
 task :restart, :roles =&amp;gt; :app do
 run &quot;cd #{current_path} &amp;amp;&amp;amp; mongrel_rails restart&quot;
 end
 task :stop, :roles =&amp;gt; :app do
 run &quot;cd #{current_path} &amp;amp;&amp;amp; mongrel_rails stop&quot;
 end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;criando-link-simbólico-para-arquivos-não-compartilhados-no-repositório&quot;&gt;Criando link simbólico para arquivos não compartilhados no repositório&lt;/h2&gt;

&lt;p&gt;Meu database.yml e outros arquivos estão somente no servidor e não no repositório, portanto sempre após o deploy com o Capistrano eu crio um link simbólico para esses arquivos.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;after &quot;deploy:finalize_update&quot;, &quot;deploy:symlink2&quot;
#...
namespace :deploy do
#...
desc &quot;Make symlink for config files&quot;
 task :symlink2 do
 run &quot;ln -nsf /home/#{user}/rails_apps/#{application}/database.yml #{release_path}/config/database.yml&quot;
 end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;diretório-log&quot;&gt;Diretório log&lt;/h2&gt;

&lt;p&gt;O Capistrano cria automáticamente um link simbólico para o diretório de log do projeto apontando para &lt;code class=&quot;highlighter-rouge&quot;&gt;/home/#{user}/rails_apps/#{application}/shared/log&lt;/code&gt;. Como ele não cria automáticamente o diretório só o link simbólico, precisamos cria-lo manualmente.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mkdir /home/#{user}/rails_apps/#{application}/shared/
mkdir /home/#{user}/rails_apps/#{application}/shared/log
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;principais-referências&quot;&gt;Principais referências&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.capify.org&quot;&gt;Capistrano site oficial&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.areacriacoes.com.br/2008/6/25/capistrano-com-git-tutorial-b-sico&quot;&gt;Capistrano com GIT, Tutorial Básico&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://help.github.com/troubleshooting-ssh/&quot;&gt;GitHub Troubleshooting SSH issues&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://groups.google.com/group/capistrano/browse_thread/thread/158118e51477a4f9&quot;&gt;Thread no Google Groups sobre o current/script/process/reaper: No such file or directory&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.capify.org/index.php/After#See_Also&quot;&gt;Capistrano After&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>

 </entry>
 
 <entry>
   <title>Guia para integrar o RCov com o Cucumber</title>
   <link href="http://www.pablocantero.com//blog/2010/07/23/guia-para-integrar-o-rcov-com-o-cucumber/"/>
   <updated>2010-07-23T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/07/23/guia-para-integrar-o-rcov-com-o-cucumber</id>
   
   <content type="html">&lt;p&gt;Esse post é um guia rápido para integrar o &lt;a href=&quot;https://github.com/relevance/rcov&quot;&gt;RCov&lt;/a&gt; com o &lt;a href=&quot;http://cukes.info/&quot;&gt;Cucumber&lt;/a&gt;. O RCov é uma ferramenta para gerar relatórios de &lt;a href=&quot;http://en.wikipedia.org/wiki/Code_coverage&quot;&gt;cobertura de testes&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;instalação-do-rcov&quot;&gt;Instalação do RCov&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gem install rcov
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;integrando-o-rcov-com-o-cucumber&quot;&gt;Integrando o RCov com o Cucumber&lt;/h2&gt;

&lt;p&gt;Para integrá-los basta modificar a task &lt;code class=&quot;highlighter-rouge&quot;&gt;cucumber:ok&lt;/code&gt; no arquivo &lt;code class=&quot;highlighter-rouge&quot;&gt;cucumber.rake&lt;/code&gt; adicionando as instruções abaixo delimitadas por #Inicio RCov e #Fim RCov.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# cucumber.rake
Cucumber::Rake::Task.new({:ok =&amp;gt; 'db:test:prepare'}, 'Run features that should pass') do |t|
 t.binary = vendored_cucumber_bin # If nil, the gem's binary is used.
 t.fork = true # You may get faster startup if you set this to false
 t.profile = 'default'
 #Início RCov - CODE COVERAGE
 t.rcov = true
 t.rcov_opts = %w{--rails --exclude osx\/objc,gems\/,spec\/,features\/ --aggregate coverage.data}
 t.rcov_opts &amp;lt;&amp;lt; %[-o &quot;coverage&quot;]
 #Fim RCov - CODE COVERAGE
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Caso você não queira que seja gerada a cobertura de testes todas as vezes que executar os testes, basta criar uma nova task específica para geração da cobertura de testes.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Cucumber::Rake::Task.new(:rcov) do |t|
 t.rcov = true
 t.rcov_opts = %w{--rails --exclude osx\/objc,gems\/,spec\/,features\/ --aggregate coverage.data}
 t.rcov_opts &amp;lt;&amp;lt; %[-o &quot;coverage&quot;]
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Para executá-la.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rake cucumber:rcov
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;sugestão-para-o-gitignore&quot;&gt;Sugestão para o .gitignore&lt;/h2&gt;

&lt;p&gt;Para ignorar os arquivos gerados pelo RCov, basta adicionar as linhas abaixo no arquivo .gitignore.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;coverage/*
coverage.data
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;principais-referências&quot;&gt;Principais referências&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://github.com/relevance/rcov&quot;&gt;RCov&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://cukes.info&quot;&gt;Cucumber&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>

 </entry>
 
 <entry>
   <title>SEO - Redirect 301 vs 302</title>
   <link href="http://www.pablocantero.com//blog/2010/07/22/seo-redirect-301-vs-302/"/>
   <updated>2010-07-22T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/07/22/seo-redirect-301-vs-302</id>
   
   <content type="html">&lt;p&gt;Uma das primeiras preocupações com o blog foi como fazer o redirect de http://pablocantero.com para http://pablocantero.com/blog e ao mesmo tempo ser indexado corretamente.&lt;/p&gt;

&lt;p&gt;Não tenho conteúdo em http://pablocantero.com, mas algum dia eu posso colocar algo, portanto quero que os motores de busca indexem http://pablocantero.com e http://pablocantero.com/blog.&lt;/p&gt;

&lt;p&gt;Depois de uma &lt;a href=&quot;http://www.google.com.br/search?hl=pt-BR&amp;amp;client=firefox-a&amp;amp;hs=ElT&amp;amp;rls=org.mozilla:en-US:official&amp;amp;&amp;amp;sa=X&amp;amp;ei=Vc5HTPffD8GJuAflkb0u&amp;amp;ved=0CBgQBSgA&amp;amp;q=seo+301+302+redirect&amp;amp;spell=1&quot;&gt;busca no Google&lt;/a&gt;, encontrei vários comentários não muito conclusivos, onde acabei assumindo a seguinte definição:&lt;/p&gt;

&lt;h2 id=&quot;301---redirect-permanente&quot;&gt;301 - Redirect permanente&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Redirect 301: http://pablocantero.com para http://pablocantero.com/blog.
Redirect 301: http://pablocantero.com para http://outrosite.com.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Em ambos os casos o motor de busca indexará somente as páginas targets http://pablocantero.com/blog e http://outrosite.com.&lt;/p&gt;

&lt;h2 id=&quot;302---redirect-temporário-no-mesmo-domínio&quot;&gt;302 - Redirect temporário no mesmo domínio&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Redirect 302: http://pablocantero.com para http://pablocantero.com/blog.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;O motor de busca indexará pagina source http://pablocantero.com.&lt;/p&gt;

&lt;h3 id=&quot;302---redirect-temporário-para-outro-domínio&quot;&gt;302 - Redirect temporário para outro domínio&lt;/h3&gt;

&lt;p&gt;Redirect 302: http://pablocantero.com para http://outrosite.com.&lt;/p&gt;

&lt;p&gt;O motor de busca indexará a página target http://outrosite.com.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No meu caso estou usando o 302&lt;/strong&gt;, pois quero que tanto http://pablocantero.com e http://pablocantero.com/blog sejam indexados. Também acredito que seja o mais correto para o meu caso uso segundo a especificação &lt;a href=&quot;http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.3&quot;&gt;RFC 2616&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The requested resource resides temporarily under a different URI. Since  the redirection might be altered on occasion, the client SHOULD continue  to use the Request-URI for future requests.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;cuidado-com-o-meta-refresh&quot;&gt;Cuidado com o Meta refresh&lt;/h2&gt;

&lt;p&gt;Segundo o &lt;a href=&quot;http://www.google.com/support/webmasters/bin/answer.py?hl=en&amp;amp;answer=79812&quot;&gt;Google&lt;/a&gt; e &lt;a href=&quot;http://www.w3.org/TR/WCAG10-HTML-TECHS/#meta-element&quot;&gt;W3C&lt;/a&gt; não é recomendado usar o redirect usando Meta refresh&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This meta tag sends the user to a new URL after a certain amount of  time, and is sometimes used as a simple form of redirection. However, it  is not supported by all browsers and can be confusing to the user. The W3C recommends that this tag not be used. We recommend using a server-side 301 redirect instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;redirect-301-server-side-php&quot;&gt;Redirect 301 Server-side PHP&lt;/h2&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;Header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;HTTP/1.1 301 Moved Permanently&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Location: http://pablocantero.com/blog&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;redirect-302-server-side-php&quot;&gt;Redirect 302 Server-side PHP&lt;/h2&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Location: http://pablocantero.com/blog&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;principais-referências&quot;&gt;&lt;em&gt;Principais&lt;/em&gt; referências&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.mattcutts.com/blog/seo-advice-discussing-302-redirects/&quot;&gt;Matt Cutts - SEO advice: discussing 302 redirects&lt;/a&gt;. Segundo esse &lt;a href=&quot;http://chasesagum.com/301-redirects-vs-302-redirects&quot;&gt;blog&lt;/a&gt; ele é o “the search engine super master genius at google”.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://help.yahoo.com/l/us/yahoo/search/webcrawler/slurp-11.html&quot;&gt;How the Yahoo! Web Crawler Handles Redirects&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.google.com/support/webmasters/bin/answer.py?hl=en&amp;amp;answer=79812&quot;&gt;Meta tags - Google Webmaster Tools Help&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.internetofficer.com/seo-tool/redirect-check/&quot;&gt;Ferramenta para checar o tipo de redirect que está sendo utilizando&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
</content>

 </entry>
 
 <entry>
   <title>Guia rápido de configuração do Cucumber</title>
   <link href="http://www.pablocantero.com//blog/2010/07/22/guia-rapido-de-configuracao-do-cucumber/"/>
   <updated>2010-07-22T00:00:00+00:00</updated>
   <id>http://www.pablocantero.com//blog/2010/07/22/guia-rapido-de-configuracao-do-cucumber</id>
   
   <content type="html">&lt;p&gt;&lt;del&gt;Os passos desses post foram utilizados no projeto &lt;a href=&quot;http://click-anywhere-to-leave-a-message.com&quot;&gt;click-anywhere-to-leave-a-message.com&lt;/a&gt;, disponível para download no &lt;a href=&quot;http://github.com/phstc/click-anywhere-to-leave-a-message.com&quot;&gt;GitHub&lt;/a&gt;.&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://cukes.info/&quot;&gt;Cucumber&lt;/a&gt; é um framework de &lt;a href=&quot;http://en.wikipedia.org/wiki/Behavior_Driven_Development&quot;&gt;Behavior Driven Development&lt;/a&gt; (BDD), que permite a criação de testes automatizados por comportamento usando uma linguagem mais natural chamada &lt;a href=&quot;https://github.com/aslakhellesoy/cucumber/wiki/Gherkin&quot;&gt;Gherkin&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Cucumber lets software development teams describe how software should behave in plain text. The text is written in a business-readable domain-specific language and serves as documentation, automated tests and development-aid - all rolled into one format.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A instalação do Cucumber deve ser seguida pela url oficial do projeto &lt;a href=&quot;http://wiki.github.com/aslakhellesoy/cucumber/ruby-on-rails&quot;&gt;Cucumber Ruby on Rails&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;adicionando-o-cucumber-em-um-projeto-rails&quot;&gt;Adicionando o Cucumber em um projeto Rails&lt;/h2&gt;

&lt;p&gt;Para começar vamos criar um projeto &lt;a href=&quot;http://rubyonrails.org&quot;&gt;Rails&lt;/a&gt; e adicionar Cucumber.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rails click-anyware-to-leave-a-message
cd click-anyware-to-leave-a-message
script/generate cucumber
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Para testar se o Cucumber foi instalado corretamente.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rake cucumber
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;descrevendo-uma-funcionalidade&quot;&gt;Descrevendo uma funcionalidade&lt;/h2&gt;

&lt;p&gt;Após adicionar o Cucumber será criado o diretório features na raiz do projeto. O Cucumber busca pelo pattern &lt;code class=&quot;highlighter-rouge&quot;&gt;meu-projeto/features/\\*.feature&lt;/code&gt; para executar as features.&lt;/p&gt;

&lt;p&gt;A primeira feature será a &lt;code class=&quot;highlighter-rouge&quot;&gt;messages.feature&lt;/code&gt;, que descreverá as funcionalidades do &lt;a href=&quot;http://en.wikipedia.org/wiki/Scaffold_%28programming%29#Scaffolding_in_Ruby_on_Rails&quot;&gt;“scaffold”&lt;/a&gt; de mensagens do projeto.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Feature: Manage Messages
In order to create a new message
As an author
I want to create and manage messages

Scenario: List messages
Given that I have created a message &quot;Olá, essa é a minha primeira mensagem&quot; at position &quot;0&quot;, &quot;0&quot;
When I go to the homepage
And I should see &quot;Olá, essa é a minha primeira mensagem&quot; at position &quot;0&quot;, &quot;0&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;when-i-go-to-the-homepage&quot;&gt;When I go to the homepage&lt;/h3&gt;

&lt;p&gt;Caso você mude o path &lt;code class=&quot;highlighter-rouge&quot;&gt;the homepage&lt;/code&gt; e o teste pare de funcionar, verifique o arquivo &lt;code class=&quot;highlighter-rouge&quot;&gt;features/support/paths.rb&lt;/code&gt;, pois é lá onde estão definidos os paths.&lt;/p&gt;

&lt;p&gt;Ao executar pela primeira vez os testes o Cucumber retornará:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rake cucumber
1 scenario (1 undefined)
3 steps (2 skipped, 1 undefined)
0m0.109s

You can implement step definitions for undefined steps with these snippets:

Given /^that I have created a message &quot;([^\&quot;]*)&quot; at position &quot;([^\&quot;]*)&quot;, &quot;([^\&quot;]*)&quot;$/ do |arg1, arg2, arg3|
 pending # express the regexp above with the code you wish you had
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Informando que eu tenho que criar o passo:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Given that I have created a message &quot;Olá, essa é a minha primeira mensagem.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;criação-do-passo&quot;&gt;Criação do passo&lt;/h3&gt;

&lt;p&gt;Para que o passo funcione, você pode adicioná-lo diretamente no arquivo &lt;code class=&quot;highlighter-rouge&quot;&gt;features/step_definitions/web_steps.rb&lt;/code&gt; ou criar um novo arquivo de step definitions específico.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# features/step_definitions/messages_steps.rb
Given /^that I have created a message &quot;([^\&quot;]*)&quot; at position &quot;([^\&quot;]*)&quot;, &quot;([^\&quot;]*)&quot;$/ do |msg, top, left|
 Message.create!(
 :message =&amp;gt; msg,
 :author =&amp;gt; 'session_id',
 :ip =&amp;gt; '127.0.0.1',
 :left =&amp;gt; left,
 :top =&amp;gt; top,
 :status =&amp;gt; Message::SAVED_BY_THE_USER)
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;principais-referências&quot;&gt;Principais referências&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://cukes.info&quot;&gt;Cucumber site oficial&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.ultrasaurus.com/sarahblog/2008/12/rails-2-day-3-behavior-driven-development/#install&quot;&gt;rails 2 day 3: behavior-driven development&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://railscasts.com/episodes/155-beginning-with-cucumber&quot;&gt;Railscasts Beginning with Cucumber&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</content>

 </entry>
 

</feed>
