Jekyll2022-04-25T15:23:07+00:00http://pauloancheta.com//feed.xmlSoftware DeveloperThis Code is Sh*t2018-09-20T00:00:00+00:002018-09-20T00:00:00+00:00http://pauloancheta.com//ruby/clean%20code/2018/09/20/shitty-code<p>Coming from a consultancy advocating for TDD (Test-Driven-Design) and clean code, it’s easy to look
at one method or class and say “this is pretty horrible; look at all the complexity of this method.
I could squish it down to 2 lines at most!” which leads to saying, “wtf”s and “omg”s which greatly
reduces the team morale because of the number of facepalms per minute per developer. However, I’ve
been on both sides of the spectrum. I have written methods and classes that have been a headache to
fix for some people.</p>
<h2 id="is-bad-code-actually-bad">Is Bad Code Actually Bad?</h2>
<p>The short answer is yes. There is no excuse for a badly named method or an obscure class that does
not tell any history of what happened. But developers are writers. We write code for other developers to
read and understand. It is useless to write a one liner if no one understands it, but in most cases,
we forget one thing that is part of every spoken language, rhetoric.</p>
<p>Oxford dictionary defines rhetoric as:</p>
<blockquote>
<p>“1The art of effective or persuasive speaking or writing, especially the exploitation of figures
of speech and other compositional techniques.”</p>
</blockquote>
<p>It does not matter if you can write the most verbose method or class, because if it is not
effectively trying to communicate to the reader, context will be lost, and it will still be a “WTF”
for the reader.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># create a payment when user has</span>
<span class="c1"># enough money to pay for the item</span>
<span class="k">if</span> <span class="n">wallet</span><span class="p">.</span><span class="nf">has_money?</span> <span class="o">&&</span> <span class="n">wallet</span><span class="p">.</span><span class="nf">money</span> <span class="o">></span> <span class="n">item</span><span class="p">.</span><span class="nf">price</span>
<span class="n">wallet</span><span class="p">.</span><span class="nf">charge!</span><span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="nf">price</span><span class="p">)</span>
<span class="k">end</span>
<span class="c1"># move the logic into a smaller method</span>
<span class="k">if</span> <span class="n">wallet</span><span class="p">.</span><span class="nf">has_enough_for?</span><span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="nf">price</span><span class="p">)</span>
<span class="n">wallet</span><span class="p">.</span><span class="nf">charge!</span><span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="nf">price</span><span class="p">)</span>
<span class="k">end</span>
<span class="c1"># Further abstraction to a service object</span>
<span class="no">UserPayment</span><span class="p">.</span><span class="nf">charge!</span><span class="p">(</span><span class="ss">item_price: </span><span class="n">item</span><span class="p">.</span><span class="nf">price</span><span class="p">)</span></code></pre></figure>
<p>But writing effectively in software development is harder, much harder.</p>
<p>Software development is a collaborative effort. It can be done with one person but in most cases,
projects are co-authored by more than 2 people. And with every group of people, rhetoric changes.
Effective communication and writing differs per language, per team and per company. To make it more
difficult, employees come and go. Writing style changes and projects start to evolve. We have a name
for this change, it is called legacy code.</p>
<h2 id="legacy-code-is-not-bad">Legacy Code Is Not Bad</h2>
<p>I personally believe that no individual should be blamed for badly written code. Writing software is
a collaborative effort, peer reviews get rid of the badly written code. A member of the team should
read the code and make sure that it’s readable, elegant and free from possible typographical error.
And since badly written code gets fixed during review, how then do we still have facepalm moments?
I believe it is because of rhetoric.</p>
<div style="width:100%;height:0;padding-bottom:56%;position:relative;"><iframe src="https://giphy.com/embed/jICAI3lhoypRHHMV1i" width="100%" height="100%" style="position:absolute" frameborder="0" class="giphy-embed" allowfullscreen=""></iframe></div>
<p><a href="https://giphy.com/gifs/Team-Penske-jICAI3lhoypRHHMV1i">via GIPHY</a></p>
<p>Our language changes, it evolves, it is alive. In the last century, people did not use the word
“cool” to describe something that has astounded them. “Awesome” was reserved for something that
truly takes their breath away, though we use these words sparingly in today’s rhetoric.</p>
<p>Software practices change, it evolves, it’s alive. Most languages that we use today gets new commits
almost everyday. New improvements happen every day. The new hip way to write <code class="language-plaintext highlighter-rouge">Service Objects</code> 2
years ago, will change in the next few years if it hasn’t because software rhetoric is alive and
changing. Yesterday microservices was the new hot thing, and having monoliths were shunned upon; but now, I’m not so sure about that.</p>
<h2 id="if-you-cant-beat-them-join-them">If You Can’t Beat Them, Join Them</h2>
<p>We cannot simply update everything that has been written to have the new rhetoric. In writing bug
fixes or adding any change, consider the past, consider the change, and consider future readers
because pushing a fix is never enough. When introducing new rhetoric, be aware of the change of
writing. Some legacy code could be avoided by simply understanding how other methods or classes
have been written. It is always easier to rewrite a class but it is almost always not the best
solution because we lose history, and it adds the potential to introduce new bugs.</p>
<p>And before you judge someone’s code, consider the amount of bugs they had to go through before
reaching that ugly solution. Good code looks good because it has not gone through the test of time.</p>Coming from a consultancy advocating for TDD (Test-Driven-Design) and clean code, it’s easy to look at one method or class and say “this is pretty horrible; look at all the complexity of this method. I could squish it down to 2 lines at most!” which leads to saying, “wtf”s and “omg”s which greatly reduces the team morale because of the number of facepalms per minute per developer. However, I’ve been on both sides of the spectrum. I have written methods and classes that have been a headache to fix for some people.4 Reasons Why I Think Google Cloud Will Take Over Cloud Computing Space2018-05-10T00:00:00+00:002018-05-10T00:00:00+00:00http://pauloancheta.com//aws/google%20cloud%20platform/ruby/2018/05/10/gcloud-vs-aws<p>Few weeks ago, if you asked me, “which cloud services platform should I use?”. I would’ve said
heroku hands down, but if you have more time and you would want more flexibility, consider using
AWS (Amazon Web Services). With all the services AWS releases and how fast they are developing, it
seems like an obvious choice. Hands down you should use AWS S3 to store all your objects.
Cloudformation makes it really easy to create and tear down AWS services. AWS has dominated the
market for years. The support for their services are also pretty outstanding. The community usually
has answers for most of your needs. But in the past few days, I’ve been exploring the alternative:
Google Cloud – and here’s what I’ve seen so far.</p>
<h2 id="a-better-client-api">A better Client API</h2>
<p>Back when I was studying at <a href="https://codecore.ca">CodeCore Bootcamp</a>, they have taught us how to use
AWS S3 which stands for “Simple Storage Service”. Using their ruby client, it is really easy to
store files and retrieve files.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">require</span> <span class="s1">'aws'</span>
<span class="n">client</span> <span class="o">=</span> <span class="no">AWS</span><span class="o">::</span><span class="no">S3</span><span class="o">::</span><span class="no">Client</span><span class="p">.</span><span class="nf">new</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="p">.</span><span class="nf">list_buckets</span>
<span class="n">response</span><span class="p">.</span><span class="nf">buckets</span><span class="p">.</span><span class="nf">first</span>
<span class="c1"># => {:name=>"paulos-bucket-of-fun", :creation_date=>"2017-08-18T17:30:09.000Z"}</span></code></pre></figure>
<p>Each bucket is a file storage, filenames should be globally unique, and each files inside that
bucket is called an object. But the problem is, while it seems pretty straight forward, it does not
feel like ruby because of its conventions.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">client</span> <span class="o">=</span> <span class="no">AWS</span><span class="o">::</span><span class="no">S3</span><span class="o">::</span><span class="no">Client</span><span class="p">.</span><span class="nf">new</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="p">.</span><span class="nf">list_buckets</span>
<span class="c1"># {:buckets => [{:name="paulos-bucket-of-fun, :creation_date=>"2017-08-18T17:30:09.000Z"}]}</span>
<span class="n">response</span><span class="p">.</span><span class="nf">class</span>
<span class="c1"># => AWS::Core::Response < Object</span>
<span class="c1"># A response object is a hash-like object that contains `{buckets: collection}`</span>
<span class="n">my_bucket</span> <span class="o">=</span> <span class="n">response</span><span class="p">.</span><span class="nf">buckets</span><span class="p">.</span><span class="nf">first</span>
<span class="c1"># => {:name=>"paulos-bucket-of-fun", :creation_date=>"2017-08-18T17:30:09.000Z"}</span>
<span class="c1"># Now, what other methods can I use to query my bucket?</span>
<span class="n">my_bucket</span><span class="p">.</span><span class="nf">methods</span>
<span class="c1"># => ArgumentError: wrong number of arguments (given 2, expected 1)</span></code></pre></figure>
<iframe src="https://giphy.com/embed/QajHhLKW3VRcs" width="480" height="318" frameborder="0" class="giphy-embed" allowfullscreen="">
</iframe>
<p><a href="https://giphy.com/gifs/QajHhLKW3VRcs">via GIPHY</a></p>
<p>Oh no, the <code class="language-plaintext highlighter-rouge">#methods</code> has been overridden! Let’s see how this compares to Google Cloud gem</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">require</span> <span class="s2">"google/cloud/storage"</span>
<span class="n">storage</span> <span class="o">=</span> <span class="no">Google</span><span class="o">::</span><span class="no">Cloud</span><span class="o">::</span><span class="no">Storage</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">project_id: </span><span class="s1">'paulos-example-project-id'</span><span class="p">)</span>
<span class="c1"># => #<Google::Cloud::Storage::Project:0x007faae1898aa8</span>
<span class="n">storage</span><span class="p">.</span><span class="nf">buckets</span><span class="p">.</span><span class="nf">class</span>
<span class="c1"># => Google::Cloud::Storage::Bucket::List < #<Class:0x007faae290c9c0></span>
<span class="c1"># YAY! It returns an actual list</span>
<span class="n">storage</span><span class="p">.</span><span class="nf">buckets</span><span class="p">.</span><span class="nf">first</span><span class="p">.</span><span class="nf">methods</span>
<span class="c1"># ... A whole range of methods available to me</span></code></pre></figure>
<p>In my opinion, the Google Cloud api is much more straight forward, easy to use and conforms with the
conventions of ruby. They did not need to return a response object. It returns what I expect an
object to return. The naming sends a clearer message to a point that I can pretty much guess what
the method name is (<code class="language-plaintext highlighter-rouge">buckets.first.files</code> is much clearer to me than the aws equivalent
<code class="language-plaintext highlighter-rouge">s3.list_objects_v2(bucket: bucket_name)</code>).</p>
<h2 id="iam">IAM</h2>
<iframe src="https://giphy.com/embed/26BREWCvD66KImhc4" width="480" height="360" frameborder="0" class="giphy-embed" allowfullscreen=""></iframe>
<p><a href="https://giphy.com/gifs/majorkey-future-jay-z-dj-khaled-26BREWCvD66KImhc4">via GIPHY</a></p>
<p>While this is a VERY broad topic, I particularly like Google Cloud Platform IAM because it is tied
to my google account. This means, I don’t have to create another account in another platform. While
it may seem that this might pose a risk for security, Google has made it clear on how you should
handle IAM for services. There’s also no way (or at least I haven’t found any) to embed my own
credentials inside a service. This means, if I want one my services to access a certain part of
my microservices or Google Cloud, I’ll have to generate another type of access. Credentials are json
files. There are p12 files as well but that’s the old way, we should be using the new hipster way.</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"service_account"</span><span class="p">,</span><span class="w">
</span><span class="nl">"project_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"project_id"</span><span class="p">,</span><span class="w">
</span><span class="nl">"private_key_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"some hash"</span><span class="p">,</span><span class="w">
</span><span class="nl">"private_key"</span><span class="p">:</span><span class="w"> </span><span class="s2">"-----BEGIN PRIVATE KEY-----</span><span class="se">\n</span><span class="s2">-----END PRIVATE KEY-----</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span><span class="w">
</span><span class="nl">"client_email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"service-account@project_id.iam.gserviceaccount.com"</span><span class="p">,</span><span class="w">
</span><span class="nl">"client_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"123"</span><span class="p">,</span><span class="w">
</span><span class="nl">"auth_uri"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://accounts.google.com/o/oauth2/auth"</span><span class="p">,</span><span class="w">
</span><span class="nl">"token_uri"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://accounts.google.com/o/oauth2/token"</span><span class="p">,</span><span class="w">
</span><span class="nl">"auth_provider_x509_cert_url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://www.googleapis.com/oauth2/v1/certs"</span><span class="p">,</span><span class="w">
</span><span class="nl">"client_x509_cert_url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://www.googleapis.com/robot/v1/metadata/x509/service-account%40project_id.iam.gserviceaccount.com"</span><span class="w">
</span><span class="p">}</span></code></pre></figure>
<p>From this file, I know that I am using a service account. I know that if I off-board one of the
developers, all our apps will be up and none of them will have any problems, all tests will still
work and it will still be a good day.</p>
<p>AWS IAM looks the same for an account owned by a person and by a machine. All we need to use is an
<code class="language-plaintext highlighter-rouge">AWS_ACCESS_KEY_ID</code> and <code class="language-plaintext highlighter-rouge">AWS_SECRET_ACCESS_KEY</code> and it should work. There will be no trace of what
kind of account uses these credentials and it is like trying to find a needle in a haystack just to
trace it back to the owner. The problem with this is that when the owner leaves the company, one of
the service just stops working. All this can be prevented by implementing good practices, but as
long as there is still a way for us to do it, mistakes can still happen.</p>
<h2 id="monitoring">Monitoring</h2>
<p>While AWS has really extensive monitoring, to make them consumable, I prefer hooking up other 3rd
party services such as Sumologic, Datadog, and/or NewRelic for error reports. I have always thought
that it would be a dream if all of these was in one application. And I guess my dream has finally
been heard by the gods and they have given me <a href="https://cloud.google.com/stackdriver/docs/">Google Stackdriver</a></p>
<iframe src="https://giphy.com/embed/dUNoNFUOYV8Yw" width="480" height="270" frameborder="0" class="giphy-embed" allowfullscreen=""></iframe>
<p><a href="https://giphy.com/gifs/graph-dUNoNFUOYV8Yw">via GIPHY</a></p>
<p>Right off the bat, when you release an application on Google App Engine, you get system reports,
which is in most cases good enough. The response monitoring, error logs are in most cases what you
get for the paid plan of the other 3rd party services. But for $8 a month, you get all that, plus
more.</p>
<p>Ever had a theory that the bottleneck of your app is that on nasty method that makes all the queries
really slow but have no way to prove it? Google Stackdriver Trace can do all of that for you. Here’s
a <a href="https://www.youtube.com/watch?v=U6weNkNmC7s">talk</a> that demonstrates the power of installing
one gem in your Rails application.</p>
<h2 id="deploying-a-rails-application">Deploying a Rails Application</h2>
<p>Before you can deploy a Rails application to AWS, you’ll have to learn how to use cloudformation,
route53, EC2, Load Balancing, Auto Scaling, VPC’s, and write a UserData. And while that gives you
a lot of flexibility, the barrier of entry is pretty steep. Because of that, I’ve always just
recommended using <a href="https://heroku.com">Heroku</a> because if Heroku is becoming too expensive, then
you are likely in a position to hire an ops team to migrate to AWS or you’re application is doing
well enough to pay for the price.</p>
<p>Google App Engine is almost as easy as using heroku, but having the power of AWS. All I had to do
to deploy a trial Rails application that scales automatically was to install 1
<a href="https://github.com/GoogleCloudPlatform/appengine-ruby">gem</a> and write 9 lines of <code class="language-plaintext highlighter-rouge">YAML</code>. Their
tutorial to deploy a sinatra app can be done in less than 5 minutes. My jaw dropped on my first
time of deploying it.</p>
<iframe src="https://giphy.com/embed/xT77XWum9yH7zNkFW0" width="480" height="270" frameborder="0" class="giphy-embed" allowfullscreen=""></iframe>
<p><a href="https://giphy.com/gifs/9jumpin-wow-nice-well-done-xT77XWum9yH7zNkFW0">via GIPHY</a></p>
<hr />
<p>While I think that AWS will still dominate the space for a few more years because all their niche
products, I’d really consider looking at the Google Cloud Platform. It may not be for your company
or your startup but at least you’ve seen the other side.</p>
<p>What do you or your company currently use to deploy your applications? Do you use Google Cloud and
not like it? I’m interested to hear more about it! Tweet me, my handle is @pauloancheta.</p>
<p>BTW, Google is not paying me to write all this. I wish they did but they don’t. Just sharing what
I have experienced using their products.</p>Few weeks ago, if you asked me, “which cloud services platform should I use?”. I would’ve said heroku hands down, but if you have more time and you would want more flexibility, consider using AWS (Amazon Web Services). With all the services AWS releases and how fast they are developing, it seems like an obvious choice. Hands down you should use AWS S3 to store all your objects. Cloudformation makes it really easy to create and tear down AWS services. AWS has dominated the market for years. The support for their services are also pretty outstanding. The community usually has answers for most of your needs. But in the past few days, I’ve been exploring the alternative: Google Cloud – and here’s what I’ve seen so far.Understanding a Ruby WAT2018-03-23T00:00:00+00:002018-03-23T00:00:00+00:00http://pauloancheta.com//ruby/wat/2018/03/23/explaining-wats<p>In my last post, I really tried to write about devops since that is what I’m currently doing
professionally. But, it just doesn’t tickle my brain. If you, my freakin awesome reader have read
the latest <a href="https://insights.stackoverflow.com/survey/2018/?utm_source=Iterable&utm_medium=email&utm_campaign=dev-survey-2018-promotion">StackOverflow Survey</a>,
devops is <a href="https://insights.stackoverflow.com/survey/2018/?utm_source=Iterable&utm_medium=email&utm_campaign=dev-survey-2018-promotion#work-salary-by-developer-type">one of the highest paid positions</a>.
Judging from that I should be writing more about devops, but I just don’t think it is my passion
at the moment. But Ruby is. Or at least for now. So, here’s what I found in Ruby in the past few weeks</p>
<blockquote>
<p>If you haven’t seen <a href="https://archive.org/details/wat_destroyallsoftware">WAT</a>, I really think you should watch it first before reading on.</p>
</blockquote>
<h2 id="lets-talk-about-ruby">Let’s talk about Ruby</h2>
<p>Few weeks ago, a co-worker found something really interesting. He posted on our slack channel this
line of code. Does anyone know what the output of this is?</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="s2">"(data)"</span><span class="p">.</span><span class="nf">gsub</span><span class="p">(</span><span class="s2">"(data)"</span><span class="p">,</span> <span class="s2">"15</span><span class="se">\\</span><span class="s2">01</span><span class="se">\\</span><span class="s2">2018"</span><span class="p">)</span></code></pre></figure>
<p>(If you were too lazy to open your irb here’s the output: <code class="language-plaintext highlighter-rouge">"15(data)1018"</code>)</p>
<iframe src="http://gifimage.net/wp-content/uploads/2017/06/wat-gif-10.gif" width="500" height="300" frameborder="0" allowfullscreen="">
</iframe>
<h2 id="mansplaining-the-wat">Mansplaining the WAT</h2>
<p>Well, honestly, the real WATs are the code that we just don’t understand. In Gary Bernhardt’s example
of the ruby WAT is more like a ruby worst practice. If you’re careful, you won’t really get to see it.</p>
<p>In a pretty long slack thread, one co-worker found out what was going on in that line of code.
He just exclaimed, “AH! That is a regex backreference!”.</p>
<iframe src="https://giphy.com/embed/rmi45iyhIPuRG" width="356" height="480" frameborder="0" class="giphy-embed" allowfullscreen=""></iframe>
<p><a href="https://giphy.com/gifs/yes-score-rmi45iyhIPuRG">via GIPHY</a></p>
<p>Cool, interesting, but, I didn’t really understand what he meant.</p>
<p>After a quick research, I did remember that there was a <a href="https://www.rubytapas.com/">Ruby Tapas</a> for
this. I know that I have seen an episode where Avdi briefly touched on ruby backreference using
gsub. (If you are not subscribed to his tapas, you should be ashamed. Subscribe now and watch
<a href="https://www.rubytapas.com/2015/01/19/episode-274-backreference/">this video</a>).</p>
<p>Let’s do a few experiments. From the tapas, we now know that <code class="language-plaintext highlighter-rouge">\\0</code> is a back reference. My first
theory of <code class="language-plaintext highlighter-rouge">\\0</code> was that it is the first capture group. But according to the tapas, <code class="language-plaintext highlighter-rouge">\\1</code> is the
first capture group.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="s2">"hello world"</span><span class="p">.</span><span class="nf">gsub</span><span class="p">(</span><span class="s2">"hello"</span><span class="p">,</span> <span class="s2">"</span><span class="se">\\</span><span class="s2">0 mundo"</span><span class="p">)</span>
<span class="c1"># => "hello mundo world"</span></code></pre></figure>
<p><img src="http://i0.kym-cdn.com/photos/images/original/000/173/580/Wat.jpg" width="500" height="300" frameborder="0" /></p>
<p>Let’s do another experiment. This time, let’s use one word with more indicators</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="s2">"hello"</span><span class="p">.</span><span class="nf">gsub</span><span class="p">(</span><span class="s2">"hello"</span><span class="p">,</span> <span class="s2">"[0]: </span><span class="se">\\</span><span class="s2">0 [1]: </span><span class="se">\\</span><span class="s2">1"</span><span class="p">)</span>
<span class="c1"># => "[0]: hello [1]:"</span>
<span class="s2">"hello"</span><span class="p">.</span><span class="nf">gsub</span><span class="p">(</span><span class="sr">/(\w)(\w)/</span><span class="p">,</span> <span class="s2">"[0]: </span><span class="se">\\</span><span class="s2">0 [1]: </span><span class="se">\\</span><span class="s2">1"</span><span class="p">)</span>
<span class="c1"># => "[0]: he [1]: h[0]: ll [1]: lo"</span></code></pre></figure>
<p>Well from that, we can infer that <code class="language-plaintext highlighter-rouge">\\1</code> (1st capture group), is the first letter that matches the
first parenthesis in the regex, or the first match. <code class="language-plaintext highlighter-rouge">\\0</code> on the other hand is anything that matches
the whole regex.</p>
<p>The method <code class="language-plaintext highlighter-rouge">gsub</code> iterates over the whole string, trying to search for matches, hence having two
<code class="language-plaintext highlighter-rouge">[0]</code> capture groups and two <code class="language-plaintext highlighter-rouge">[1]</code> capture groups.</p>
<p>If we look at the last output closer, the last <code class="language-plaintext highlighter-rouge">[1]</code> capture group captured two characters instead
of one. That is because gsub returned the rest of the chars from the capture group.</p>
<h2 id="back-to-the-first-problem">Back to the first problem</h2>
<p>Let’s try the example again but without the back references:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="s2">"(data)"</span><span class="p">.</span><span class="nf">gsub</span><span class="p">(</span><span class="s2">"(data)"</span><span class="p">,</span> <span class="s2">"15 01 2018"</span><span class="p">)</span>
<span class="o">^</span> <span class="o">^</span> <span class="o">^</span>
<span class="o">|</span> <span class="o">|</span> <span class="o">|</span>
<span class="n">data</span> <span class="o">|</span> <span class="o">|</span>
<span class="n">matcher</span> <span class="o">|</span>
<span class="n">replace</span> <span class="n">data</span> <span class="n">with</span> <span class="n">this</span>
<span class="c1"># => "15 01 2018"</span></code></pre></figure>
<p>Now using back references:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="s2">"(data)"</span><span class="p">.</span><span class="nf">gsub</span><span class="p">(</span><span class="s2">"(data)"</span><span class="p">,</span> <span class="s2">"15</span><span class="se">\\</span><span class="s2">01</span><span class="se">\\</span><span class="s2">2018"</span><span class="p">)</span>
<span class="o">^</span> <span class="o">^</span>
<span class="o">|</span> <span class="o">|</span>
<span class="o">|</span> <span class="n">does</span> <span class="n">not</span> <span class="n">match</span> <span class="n">anything</span>
<span class="n">replace</span> <span class="n">this</span> <span class="n">with</span> <span class="n">what</span> <span class="n">was</span> <span class="n">captured</span>
<span class="c1"># => "15(data)1018"</span></code></pre></figure>
<p>In Ruby Conference Australia few weeks ago, <a href="https://twitter.com/glasnt">Katie McLaughlin</a> gave a
talk on WAT’s and it has inspired me to dig deep and find out for myself why WAT’s happen. (Her
slides are in <a href="https://github.com/glasnt/talks/tree/gh-pages/2018_03_RubyConfAU/">here</a>.) It is not
because the language is bad; it is usually a part of the language that we don’t understand.
Understanding is the key.</p>
<hr />
<p>If you liked this blog post, or if you think I can make this better, I would love to hear from you!
Send a tweet or an email; whatever suits your fancy.</p>In my last post, I really tried to write about devops since that is what I’m currently doing professionally. But, it just doesn’t tickle my brain. If you, my freakin awesome reader have read the latest StackOverflow Survey, devops is one of the highest paid positions. Judging from that I should be writing more about devops, but I just don’t think it is my passion at the moment. But Ruby is. Or at least for now. So, here’s what I found in Ruby in the past few weeksScalable Rails Application using AWS and Kubernetes Part 12018-02-26T00:00:00+00:002018-02-26T00:00:00+00:00http://pauloancheta.com//docker/rails/postgres/nginx/2018/02/26/scalable-rails<p>Most devs who learn how to code, learn by deploying to <a href="https://www.heroku.com">Heroku</a>.
Which by all means a really good place to start. I’ve used Heroku for most of my personal projects
and I have never found a reason to move out of it. However, most enterprise companies are not set
up with heroku; most often they choose <a href="https://aws.amazon.com">AWS</a> for a variety of reasons. Some
would choose AWS/ rolling their own deployment because of security reasons. With AWS IAM roles,
all instances can be configured into a specific access. EC2 Auto-scaling group, servers can be
configured to scale up and scale down within a trigger.</p>
<p>All that is neat but we can go beyond what AWS can offer by using <a href="https://kubernetes.io/">Kubernetes</a>.
Kubernetes is a project from Google. It is an open source system for automating deployments,
scaling and managing containers.</p>
<h2 id="introduction-to-docker">Introduction to Docker</h2>
<p>In order for us to deploy our rails application, we will first have to containerize our application
using <a href="https://www.docker.com/">Docker</a>. By using docker, we can pull a docker “image” and use that
to run our rails application. Here’s the final <code class="language-plaintext highlighter-rouge">Dockerfile</code> that should be on the parent directory
of your application.</p>
<figure class="highlight"><pre><code class="language-docker" data-lang="docker"><span class="k">FROM</span><span class="s"> ruby:2.4.2</span>
<span class="k">RUN </span>apt-get update <span class="nt">-qq</span> <span class="o">&&</span> apt-get <span class="nb">install</span> <span class="nt">-y</span> build-essential libpq-dev nodejs
<span class="k">WORKDIR</span><span class="s"> /hello_container</span>
<span class="k">ADD</span><span class="s"> Gemfile ./Gemfile</span>
<span class="k">ADD</span><span class="s"> Gemfile.lock ./Gemfile.lock</span>
<span class="k">RUN </span>bundle <span class="nb">install</span>
<span class="k">ADD</span><span class="s"> . .</span>
<span class="k">EXPOSE</span><span class="s"> 3000</span>
<span class="k">CMD</span><span class="s"> ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]</span></code></pre></figure>
<p>Now that we have the file we need, let’s dive into the code and understand line by line on I am doing
here. Think of docker as a layered cake. The base of the cake doesn’t change much and the layer on top
are open for variations. You can still remove the bottom of the cake and change the bottom layer
completely, but that is always a tedious process.</p>
<iframe src="https://giphy.com/embed/vtmWF8WdeqIKY" width="480" height="392" frameborder="0" class="giphy-embed" allowfullscreen="">
</iframe>
<p><a href="https://giphy.com/gifs/food-colors-cake-vtmWF8WdeqIKY">via GIPHY</a></p>
<h2 id="understanding-layers">Understanding Layers</h2>
<p>The idea is that your layers are reusable. You can reorder how your cake is layered but there is
always a preferred order. Think of the <code class="language-plaintext highlighter-rouge">FROM</code> statement as the base cardboard where the layers go
on top of. A <code class="language-plaintext highlighter-rouge">FROM</code> directive is almost always first. It can only be preceded by an <code class="language-plaintext highlighter-rouge">ARG</code>, but we
don’t need to concern ourselves of that at the moment. In the Dockerfile example, <code class="language-plaintext highlighter-rouge">FROM</code> dictates
that we are going to install <code class="language-plaintext highlighter-rouge">ruby:2.4.2</code>. This is called the “base image” of our Dockerfile.</p>
<p>The <code class="language-plaintext highlighter-rouge">RUN</code> directive runs a bash or shell script. In this example, it runs <code class="language-plaintext highlighter-rouge">apt-get</code> to our base
image. All it does here is making sure to install 3 libraries in our file <code class="language-plaintext highlighter-rouge">build-essential</code>,
<code class="language-plaintext highlighter-rouge">libpq-dev</code>, and <code class="language-plaintext highlighter-rouge">nodejs</code>. <code class="language-plaintext highlighter-rouge">build-essential</code> installs packages required debian packages, <code class="language-plaintext highlighter-rouge">libpq-dev</code>
installs postgresql, and <code class="language-plaintext highlighter-rouge">nodejs</code> installs node.</p>
<p>The next directice creates a directory, and sets it as the working directory. This is accomplished
with <code class="language-plaintext highlighter-rouge">WORKDIR</code>.</p>
<p>Here comes an important bit. We then Add the <code class="language-plaintext highlighter-rouge">Gemfile</code> and <code class="language-plaintext highlighter-rouge">Gemfile.lock</code>. The reason why we’re
adding these files first, is that so we can run <code class="language-plaintext highlighter-rouge">bundle install</code> and not bundle again every time we
change something. Ideally, we only run bundle install if <code class="language-plaintext highlighter-rouge">Gemfile</code> and <code class="language-plaintext highlighter-rouge">Gemfile.lock</code> changes again.</p>
<p>Next we add all the other files to the current directory. Everytime we change the other files with
the exception of the Gemfiles, we are only changing this layer which should make it really easy for
us to compile.</p>
<h2 id="testing">Testing</h2>
<p>We can test our <code class="language-plaintext highlighter-rouge">Dockerfile</code> by running <code class="language-plaintext highlighter-rouge">docker build . -t hello-container</code>. This tells docker, “Hey docker,
I want to build this current directory and tag it with <code class="language-plaintext highlighter-rouge">hello-container</code>”. Docker then would run all
directives one by one and register a layer for each.</p>
<p>To run it, we can use <code class="language-plaintext highlighter-rouge">docker run -it --rm hello-container</code>. This should run the server of rails
server.</p>
<hr />
<p>I’ve covered enough in here for today. In my next post, I’ll write a brief introduction to K8s!</p>Most devs who learn how to code, learn by deploying to Heroku. Which by all means a really good place to start. I’ve used Heroku for most of my personal projects and I have never found a reason to move out of it. However, most enterprise companies are not set up with heroku; most often they choose AWS for a variety of reasons. Some would choose AWS/ rolling their own deployment because of security reasons. With AWS IAM roles, all instances can be configured into a specific access. EC2 Auto-scaling group, servers can be configured to scale up and scale down within a trigger.Remote Reflections2018-01-15T00:00:00+00:002018-01-15T00:00:00+00:00http://pauloancheta.com//remote-work/2018/01/15/remote-reflections<p>I’ve been working remotely for the past 5 months. I have left Vancouver, Canada,
sold my car, put all my furniture on storage, packed my clothes and laptop and
currently living in Sydney, Australia at the time of this writing.</p>
<p>It has been an amazing adventure so far. I have met a lot of interesting people
on the way. Working remotely has not been easy. There had been a few things that
I needed to learn quickly and be efficient at. It is one of those things that
you have to learn on the job since no one would teach how to work efficiently
while working remotely. No one teaches how work with remote people, or at least
none that I know of. So here are the things that I thought was hard:</p>
<h2 id="being-part-of-the-culture-while-being-away">Being part of the culture while being away.</h2>
<p><img src="https://media.giphy.com/media/l0MYt5jPR6QX5pnqM/giphy.gif" alt="celebrate" /></p>
<p>While being away from my work, I do get jealous that they do company outings,
beers on Friday afternoon, or even team lunch when welcoming a new team member.
But it’s one of those things that there isn’t actually a solution for. The
company cannot just give me a bonus to eat out by myself because even if they
did, nothing would changed, that is still not a company culture.
On special occasions, it is nice that my employer gives out a few incentives
such as gift cards or additional allowance for celebrations that I have missed
such as the company Christmas party but the fact is, I will still be celebrating
without my team.</p>
<p>Slack is a great tool for remote culture. I have found that I keep up with slack
more that I’ve been working remotely since that is the only way I can feel like
I’m still part of the team. Slack jokes, memes and gifs has become my way to
interact and be present with the company.</p>
<h2 id="working-together-standups-planning-sprints-submitting-or-receiving-code-reviews-delivery">Working together: standups, planning sprints, submitting or receiving code reviews, delivery</h2>
<p>I’ve worked on a team where a synchronous standup worked best and a team where
asynchronous standup has more value. I find that when a team writes blog posts
from time to time, the asynchronous standup has more value to the team because
each day, the asynchrnous standup (or more of a roundup of what you have done
during the day) is detailed to a point where the pull request just made sense.
I can follow the thinking behind pull-requests from standups or their github
descriptions. Some people might not like it because each pull request
or github issue becomes more like a thesis which definitely is not for a team
who does not like reading or writing.</p>
<p>When a team does is not composed of writers, synchronous stand up is more
valuable. I would admit that team cohesion is far better when doing synchronous
standups. But as far as team productivity, I lean towards asynchronous
communication.</p>
<p>Programming is in my point of view, very asynchronous. When we create
pull-requests, we don’t need everyone to work at the same time unless we are
pair programming. Code review does not need to be synchronous as well because
even if it is synchronous, you don’t get a review in an instant anyway.</p>
<p>Architecting software is very synchronous, but when there is too many cooks in
the kitchen, it spoils the food. I most often think that when architectural
meetings take place, I don’t have to be present on the meeting because most
people who go to that meeting have already set their minds on how to do things.
I tend to just follow what the presented idea is and then iterate over it.</p>
<p>Since I am not interrupted in most of my work day, I tend to finish tickets
a bit faster and it would stay in review for longer time than usual because of
asnychronosity. My solution for this problem is to have a perfectionist
mentality where my tickets should just go through the board without hindrance
after I have put it in review. It seldom happens since there are still points
that I have not considered, but it certainly made my tickets go through faster
than when I was working synchronously with my co-workers. When I review tickets,
I would usually just leave a comment and not block them in any way so that
others can review the same ticket.</p>
<p>I make it a point that my peers should not worry about the app on my timezone.
If there’s anything that is happening, I will look after it when I can so their
evening should not be interrupted. My only ask from them is to respond to alert
when I’m sleeping which is the time they are in the office anyway. I try to
empower them to deploy past 4pm Monday to Thursday as well. This means, I’ll
take over the deployment and watch over the app after they leave the office.</p>
<h2 id="cabin-fever-not-talking-to-anyone-for-days">Cabin fever. Not talking to anyone for days.</h2>
<p>It is very likely that in a span of few days, I don’t see people. When this
happens, it does get very lonely. I’m lucky that I have my wife with me. But I
have found that working out, going to the gym as much as I can has helped.
Exploring the city is also good but it is definitely easy to run out of free
fun.</p>
<p>Being as active as I can in meetups has been really helpful as well.
I have attended a variety of meetups in the city– ruby and go meetups are my
faveourites so far.</p>
<p>I also have a tendency to over-work. I forget to take my lunch or even have a
5 minute breather in between tickets. I have found that I get burnt out when I
forget, so I have alarms to remind myself to take a break or eat lunch. For some
people, <a href="https://en.wikipedia.org/wiki/Pomodoro_Technique">pomodoro</a> is a good
technique for this, however, it takes me more than 20 minutes to gain focus and
be on my zone. So, I have a 2 hour increment in between my breaks.</p>
<h2 id="learning">Learning</h2>
<p>If you, person who is reading this until now, has considered or considering
working remotely and have an opportunity to do so, do it! You’d see that you
will have more time to be productive and learn new languages and read books that
has been in your reading list for years. You’ll meet amazing people that will
inspire you to write different apps in the new hipster language that you might
learn while being away from the routine of getting up semi-early to work in your
desk with fancy tea or coffee made by a tattooed barista with a man bun and
beard.</p>
<p><img src="https://media.giphy.com/media/NldZOXRUZM8QU/giphy.gif" alt="celebrate" /></p>
<p>A lot of people have told me, “Paulo, you are really lucky working remote and
just waking up, rolling out of bed, getting your laptop and start working.”. And
while this is true, it is not without complications. I have more autonomy over
my time since there is no one looking over my shoulder, but this also entails a
lot of discipline.</p>
<p>I would work for synchronously again in a few months and I’m interested on how
that would play out. Maybe I will like working in an office again, or maybe I
would just want to work in some ratchet hostel surrounded my mosquitoes so I can
talk to different people of different cultures after work. Maybe it’s the
millenial in me that I think no one should ever work in a confined space, or
maybe it will be important for me to have a high performance team that won’t
have a work-life balance.</p>
<p>Whatever the future holds, I am thankful that I have done this experiment of
woring remotely because I have learned more about my career and myself way more
than when I was working in an office.</p>I’ve been working remotely for the past 5 months. I have left Vancouver, Canada, sold my car, put all my furniture on storage, packed my clothes and laptop and currently living in Sydney, Australia at the time of this writing.Getting Started with Cloudformation2017-11-20T00:00:00+00:002017-11-20T00:00:00+00:00http://pauloancheta.com//aws/cloudformation/2017/11/20/get-started-with-cloudformation<p>Few weeks ago, I tried to learn how to use AWS Cloudformation. For those who are not familiar,
Cloudformation is an AWS service that provides an easy way to create and manage AWS Services.
Cloudformation uses templates in a format of JSON or YAML which can be uploaded from AWS console,
or through AWS CLI.</p>
<h2 id="requirements">Requirements</h2>
<p>This tutorial requires installing <a href="http://docs.aws.amazon.com/cli/latest/userguide/installing.html">aws-cli</a>.
So, if you don’t have it yet, I recommend installing it since it will be easier to run a few
commands than clicking around AWS console.</p>
<h2 id="the-mve">The MVE</h2>
<p>Let’s start with the MVE (minimum viable example) by creating an AWS S3 bucket. The goal here is to
create a stack by writing the least amount of code so that we can understand each component easier.</p>
<p>The only requirement that a Cloudformation template has is a <code class="language-plaintext highlighter-rouge">Resource</code>. This dictates to AWS which
resource to build. The template also accepts a AwsTemplateFormatVersion (which has not changed since
they made it), Description, Metadata, Parameters, Mappings, Conditions, Transform, Resources,
and Outputs. You can find a detailed explanation on what these optional values does in
<a href="http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-anatomy.html">here</a>.</p>
<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="c1"># template.yaml</span>
<span class="na">AWSTemplateFormatVersion</span><span class="pi">:</span> <span class="s2">"</span><span class="s">2010-09-09"</span> <span class="c1"># this is not required since they only have 1 version.</span>
<span class="na">Description</span><span class="pi">:</span> <span class="s">Optional description of your stack.</span>
<span class="na">Resources</span><span class="pi">:</span>
<span class="na">yourUniqueBucketName</span><span class="pi">:</span>
<span class="na">Type</span><span class="pi">:</span> <span class="s">AWS::S3::Bucket</span></code></pre></figure>
<p>Each <code class="language-plaintext highlighter-rouge">Resource</code> requires two components, it should always have a key or identifier. In this example,
I am using S3 bucket. An S3 bucket’s identifier is globally namespaced, so the key <code class="language-plaintext highlighter-rouge">yourUniqueBucketName</code>
should be used to your own unique bucket name. The second requirement for a resource is the <code class="language-plaintext highlighter-rouge">Type</code>.
This tells cloudformation what kind of resource you are trying to create. The list of resource types can be found
<a href="http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html">here</a></p>
<p>I find that when I create a stack, I create and delete it a few times so I find myself creating a
bash script to save my fingers from typing too much in case I want to edit a few details.</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c"># create-cfn.sh</span>
<span class="c"># run me with `bash create-fn.sh`</span>
<span class="c">#!/bin/bash</span>
aws cloudformation create-stack <span class="se">\</span>
<span class="nt">--stack-name</span> MyFirstCFNStack <span class="se">\</span>
<span class="nt">--template-body</span> file://<span class="k">${</span><span class="nv">PWD</span><span class="k">}</span>/template.yml
<span class="c"># delete-cfn.sh</span>
<span class="c"># run me with `bash delete-cfn.sh`</span>
<span class="c">#!/bin/bash</span>
aws cloudformation delete-stack <span class="se">\</span>
<span class="nt">--stack-name</span> MyFirstCFNStack <span class="se">\</span></code></pre></figure>
<p>A few things to note here, <code class="language-plaintext highlighter-rouge">--stack-name</code> and <code class="language-plaintext highlighter-rouge">--template-body</code> are required. If you have your
Cloudformation template in an s3 file for example, you can pass in <code class="language-plaintext highlighter-rouge">--template-url</code> instead, but
you cannot use both. When this has run, it should create an S3 bucket. It is pretty simple but the
power of Cloudfomation can be seen when creating a stack that includes more than one AWS service.</p>
<h2 id="example-10">Example 1.0</h2>
<p>To see the real power of Cloudformation, we will have to create more than one resource. Let’s now
try to create a Lambda that uses a specific Role to execute it.</p>
<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">AWSTemplateFormatVersion</span><span class="pi">:</span> <span class="s2">"</span><span class="s">2010-09-09"</span>
<span class="na">Description</span><span class="pi">:</span> <span class="s">Optional description of your stack.</span>
<span class="na">Resources</span><span class="pi">:</span>
<span class="na">MyAwesomeLambda</span><span class="pi">:</span>
<span class="na">Type</span><span class="pi">:</span> <span class="s">AWS::Lambda::Function</span>
<span class="na">Properties</span><span class="pi">:</span>
<span class="na">Runtime</span><span class="pi">:</span> <span class="s">nodejs4.3</span>
<span class="na">Handler</span><span class="pi">:</span> <span class="s">index.handler</span>
<span class="na">Role</span><span class="pi">:</span> <span class="kt">!GetAtt</span> <span class="s">LambdaExecutionRole.Arn</span>
<span class="na">Code</span><span class="pi">:</span>
<span class="na">ZipFile</span><span class="pi">:</span> <span class="kt">!Sub</span> <span class="pi">|</span>
<span class="s">var response = require('cfn-response');</span>
<span class="s">exports.handler = function(event, context) {</span>
<span class="s">var responseData = {Value: event.ResourceProperties.List};</span>
<span class="s">responseData.Value.push(event.ResourceProperties.AppendedItem);</span>
<span class="s">response.send(event, context, response.SUCCESS, responseData);</span>
<span class="s">};</span>
<span class="na">LambdaExecutionRole</span><span class="pi">:</span>
<span class="na">Type</span><span class="pi">:</span> <span class="s">AWS::IAM::Role</span>
<span class="na">Properties</span><span class="pi">:</span>
<span class="na">AssumeRolePolicyDocument</span><span class="pi">:</span>
<span class="na">Version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">2012-10-17'</span>
<span class="na">Statement</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">Effect</span><span class="pi">:</span> <span class="s">Allow</span>
<span class="na">Principal</span><span class="pi">:</span>
<span class="na">Service</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">lambda.amazonaws.com</span>
<span class="na">Action</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">sts:AssumeRole</span></code></pre></figure>
<p>As you can see, we have a lot more going on in this example although this is still the minimum
template required to create a Lambda Function.
A <a href="http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html">AWS::Lambda::Function</a>
requires 4 <code class="language-plaintext highlighter-rouge">Properties</code>: <code class="language-plaintext highlighter-rouge">Runtime</code>, <code class="language-plaintext highlighter-rouge">Handler</code>, <code class="language-plaintext highlighter-rouge">Role</code> and <code class="language-plaintext highlighter-rouge">Code</code>. Sadly, the documentation is not
ordered by the required properties. Pro tip: find <code class="language-plaintext highlighter-rouge">Required: Yes</code> to quickly find required properties.</p>
<p>Since IAM Role is a required property of a Lambda, it is created separately. However, if we are
creating multiple Lambda resource, we only need to create the role once unless the Lambda requires
a different policy.</p>
<hr />
<p>AWS Cloudformation is a really powerful tool. I’m only scratching the surface in here. AWS
documentation tends to be a bit heavy for me but they are very detailed. It does makes sense because
each AWS resource is packed with so much features.</p>Few weeks ago, I tried to learn how to use AWS Cloudformation. For those who are not familiar, Cloudformation is an AWS service that provides an easy way to create and manage AWS Services. Cloudformation uses templates in a format of JSON or YAML which can be uploaded from AWS console, or through AWS CLI.A PR has been submitted2017-06-16T00:00:00+00:002017-06-16T00:00:00+00:00http://pauloancheta.com//ruby/clean-code/2017/06/16/ruby-pr<p>Kudos to you, person who contributed to a Ruby repository. I am actually thankful that you did not just create an issue but you actually tried to solve the problem by creating a Pull Request. Now my job, is 10x easier since I don’t have to stop the other things that I am trying to do.</p>
<p>But actually, a reviewer has a big job. A reviewer has to care, a lot, about the repository and the health of the code base. Not saying that I care about the repository the most and I am being too picky of how Ruby code should be written. But in general, yes, I do care about the code that you want me to merge to fix your problem. And here’s why:</p>
<h2 id="you-love-instance-variables">You love instance variables</h2>
<p>Yes, I love instance variables too, but it’s more like “I love that I can use instance variables but if I can avoid it I would”. In most programming languages, instance variables are a good way to pass down an object around. With the right usage, it can definitely make a class smaller. But here is my problem</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="vi">@car</span><span class="p">.</span><span class="nf">name</span> <span class="c1"># => NoMethodError: undefined method `name` for nil:NilClass</span>
<span class="n">car</span><span class="p">.</span><span class="nf">name</span> <span class="c1"># => NameError: undefined local variable or method `car` for main:Object</span></code></pre></figure>
<p>Did that give you the hint? Yes, an instance variable returns nil! So what would happen if it was interpolated inside a string?</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="s2">"That </span><span class="si">#{</span><span class="n">car</span><span class="si">}</span><span class="s2"> is so fast! I should get one of those"</span> <span class="c1"># => NameError: undefined local variable or method `car` for main:Object</span>
<span class="s2">"That </span><span class="si">#{</span><span class="vi">@car</span><span class="si">}</span><span class="s2"> is so fast! I should get one of those"</span> <span class="c1"># => "That is so fast! I should get one of those"</span></code></pre></figure>
<p>This would be a big problem wouldn’t it? Silent failures are hard to find. Inside a string, ruby calls <code class="language-plaintext highlighter-rouge">to_s</code> on the object. And <code class="language-plaintext highlighter-rouge">nil.to_s</code> is just an empty string.</p>
<h2 id="you-access-hashes-with-hashkey">You access hashes with Hash[key]</h2>
<p>Yes, I know, it works as well. And yes, you avoid a lot of errors in doing so. But actually, it is better to know where the error started. Think about a scenario where there is something that you want to access and its a key inside a hash inside another hash (oh the nightmare of hash-ception).</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># nightmare hash from hell</span>
<span class="n">nhfh</span> <span class="o">=</span> <span class="p">{</span><span class="ss">a: </span><span class="p">{</span><span class="ss">b: </span><span class="p">{</span><span class="ss">c: </span><span class="p">{</span><span class="ss">d: </span><span class="s2">"canada"</span><span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span>
<span class="c1"># worst way to do it</span>
<span class="n">nhfh</span><span class="p">[</span><span class="ss">:a</span><span class="p">][</span><span class="ss">:b</span><span class="p">][</span><span class="ss">:c</span><span class="p">][</span><span class="ss">:d</span><span class="p">]</span> <span class="c1"># => "canada"</span>
<span class="c1"># slightly better if you are using ruby 2.3</span>
<span class="n">nhfh</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="ss">:a</span><span class="p">,</span> <span class="ss">:b</span><span class="p">,</span> <span class="ss">:c</span><span class="p">,</span> <span class="ss">:d</span><span class="p">)</span></code></pre></figure>
<p>API’s change. Everything changes all the time. Our apps tend to grow up to be nasty children who don’t tell us what’s wrong if we don’t teach them to tell us what’s wrong when something aweful happens. If for example I changed the API of the hash to something more meaningful <code class="language-plaintext highlighter-rouge">nhfh = {company: {person: {address: {country: "canada"} } } }</code>, can you guess what happens to the other accessors I have used? Yes, it returns <code class="language-plaintext highlighter-rouge">nil</code>.</p>
<p>So you want to know how to solve this eh? Silver medal solution will be to use a better hash accessor <code class="language-plaintext highlighter-rouge">fetch</code>.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">nhfh</span> <span class="o">=</span> <span class="p">{</span><span class="ss">a: </span><span class="p">{</span><span class="ss">b: </span><span class="p">{</span><span class="ss">c: </span><span class="p">{</span><span class="ss">d: </span><span class="s2">"canada"</span><span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span>
<span class="n">nhfh</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:a</span><span class="p">)</span>
<span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:b</span><span class="p">)</span>
<span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:c</span><span class="p">)</span>
<span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:d</span><span class="p">)</span> <span class="c1"># => "canada"</span>
<span class="c1"># API change</span>
<span class="n">nhfh</span> <span class="o">=</span> <span class="p">{</span><span class="ss">company: </span><span class="p">{</span><span class="ss">person: </span><span class="p">{</span><span class="ss">address: </span><span class="p">{</span><span class="ss">country: </span><span class="s2">"canada"</span><span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span>
<span class="n">nhfh</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:a</span><span class="p">)</span>
<span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:b</span><span class="p">)</span>
<span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:c</span><span class="p">)</span>
<span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:d</span><span class="p">)</span> <span class="c1">#=> KeyError: key not found: :a</span></code></pre></figure>
<p>Notice how this stops from the first accessor that failed. It fails loudly and asks its creator to change the accessors. But I did say this is a silver medal solution. It’s great and it is far better than silent failures, but it is still not the best solution. If this hash is a JSON returned by an outside API, it would be better to create a wrapper class to be consumed by your app</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">CompanyWrapper</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">params</span><span class="p">)</span>
<span class="vi">@company</span> <span class="o">=</span> <span class="n">params</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">address</span>
<span class="n">person</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:address</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">person</span>
<span class="n">company</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:person</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">company</span>
<span class="vi">@company</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>Notice the order of my methods. Define the method below its callee which brings me to my next point</p>
<h2 id="avoid-spaghetti-code">Avoid spaghetti code</h2>
<p>Every class should read like a story where the story that should finish on the first method, and as you go further, the less you should care about the following story. If done right, the methods clear and succinct, ordered with intent to be understood, then the reader would only need to read the first method of the class.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">BadSendEmail</span>
<span class="kp">include</span> <span class="no">Virtus</span><span class="p">.</span><span class="nf">model</span>
<span class="n">attribute</span> <span class="ss">:user_id</span><span class="p">,</span> <span class="no">Integer</span>
<span class="k">def</span> <span class="nf">call</span>
<span class="n">send_email!</span> <span class="o">&&</span> <span class="n">increment_email_service!</span>
<span class="k">end</span>
<span class="kp">private</span>
<span class="k">def</span> <span class="nf">user</span>
<span class="no">User</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">company</span>
<span class="n">user</span><span class="p">.</span><span class="nf">company</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">send_email!</span>
<span class="no">EmailService</span><span class="p">.</span><span class="nf">new_email</span><span class="p">(</span><span class="n">user_email</span><span class="p">).</span><span class="nf">deliver_later</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">user_email</span>
<span class="n">user</span><span class="p">.</span><span class="nf">email</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">increment_email_service!</span>
<span class="n">company</span><span class="p">.</span><span class="nf">sent_emails</span><span class="p">.</span><span class="nf">increment!</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>How was your experience reading and understanding through the code? To be honest, when reading through this code, I’m not sure if it is working since everything is jumbled together. There is no direction on how the class tells the story. Once there is a bug in it, it is harder to find it as well because the code is harder to reason about</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">GoodSendEmail</span>
<span class="kp">include</span> <span class="no">Virtus</span><span class="p">.</span><span class="nf">model</span>
<span class="n">attribute</span> <span class="ss">:user_id</span><span class="p">,</span> <span class="no">Integer</span>
<span class="k">def</span> <span class="nf">call</span>
<span class="n">send_email!</span> <span class="o">&&</span> <span class="n">increment_email_service!</span>
<span class="k">end</span>
<span class="kp">private</span>
<span class="k">def</span> <span class="nf">send_email!</span>
<span class="no">EmailService</span><span class="p">.</span><span class="nf">new_email</span><span class="p">(</span><span class="n">user_email</span><span class="p">).</span><span class="nf">deliver_later</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">increment_email_service!</span>
<span class="n">company</span><span class="p">.</span><span class="nf">sent_emails</span><span class="p">.</span><span class="nf">increment!</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">user_email</span>
<span class="n">user</span><span class="p">.</span><span class="nf">email</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">company</span>
<span class="n">user</span><span class="p">.</span><span class="nf">company</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">user</span>
<span class="no">User</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>Although it is valuable to read the whole service object, you can just stop at the call method to know what the service does. But if you read further, we see the definition of the methods that was called directly above it. The reader would understand more and more of the code as they read a long.</p>
<hr />
<p>As a collaborator, understand that when someone reviews your code, it is not just because it looks better but also because you are not the one full-time maintaining it. The decisions you make towards your solution will be harder to remove once it is implemented. It is much easier to add code than to remove code without breaking any change. But when something breaks, our code should be vigilant. We should be alarmed that a breaking change happened and our errors guide us on to where we should apply our fixes.</p>Kudos to you, person who contributed to a Ruby repository. I am actually thankful that you did not just create an issue but you actually tried to solve the problem by creating a Pull Request. Now my job, is 10x easier since I don’t have to stop the other things that I am trying to do.Starting Startups2017-06-02T00:00:00+00:002017-06-02T00:00:00+00:00http://pauloancheta.com//startups/elixir/phoenix/ruby%20on%20rails/2017/06/02/starting-startups<p>In the past few months, I have been wanting to start a startup. The entrepreneur
in me has been itching to design and build and market a whole new product that
could possibly help a lot of people. Once my idea was set, I was on the hunt for
my new tech stack. And of course I lean on the hottest and latest. I started
dreaming of becoming a hipster programmer who would release a kraken of an app
in the wild and be featured in the blog posts that would talk about how we did
it and how successful our company’s stack is. So I leaned towards Elixir and
Phoenix.</p>
<p>With Elixir and Phoenix, I could lessen the future cost of having a lot of
servers. With it, I don’t have to worry about having multiple connections since
it can handle so much server load. 2M connections all at once! The on small
problem, is I have worked on a few Ruby on Rails apps but none of a Phoenix app.
I have dabbled with it for a while but I cannot say that I am so well versed at
it that I would know what to do whenever I would encounter any problem. Not
saying that I know everything in Rails but there is lesser chance of me not
knowing what I am doing if I use something that I am really familiar with.</p>
<p><img src="/img/posts/phoenixframework-logo.png" alt="phoenix-framework" /></p>
<p>I was up for the challenge though. <code class="language-plaintext highlighter-rouge">mix phoenix.new startup</code>. I typed it with so
much enthusiasm that I would learn so much building something new. In no time, I’ve
been learning about authentication in phoenix, testing and using different hex. I’ve
integrated cloudinary using <a href="https://github.com/smeevil/cloudex"><code class="language-plaintext highlighter-rouge">cloudex</code></a>, sparkpost
using <a href="https://github.com/andrewtimberlake/bamboo_sparkpost"><code class="language-plaintext highlighter-rouge">bamboo_sparkpost</code></a>,
integration testing using <a href="https://github.com/keathley/wallaby"><code class="language-plaintext highlighter-rouge">wallaby</code></a> and
faking my data with <a href="https://github.com/thoughtbot/ex_machina"><code class="language-plaintext highlighter-rouge">ex_machina</code></a>.</p>
<p>While all of them are so interesting and cutting edge and it is so fast, I
found that the pace I’m going at is slower than my usual pace. Primary reason is
I’m using a language that I am not used to while creating something totally new
to me and having so much unknowns. This is the perfect recipe for disaster and success
in learning. The good thing is, all the maintainers of the hex that I have mentioned are
really nice and you can talk to them on slack and they will answer your questions
the best way they can.</p>
<p>But as all tech founders have experienced, there will come a time that you will
need a partner. Every founder needs a co-founder because you can’t do it alone. I guess
especially me. I know that I have a tendency to be consumed by my work and completely
detach myself off the outside world because if I do something not related to
my startup, I will feel guilty and think that the reason I failed is because I
took a 10min power nap after working 12 hours straight without break. Completely
unhealthy and that is the reason I need a co-founder.</p>
<p>At the beginning of my venture, my wife did help me. She told me to get away from
the computer and be with her. Go to church with her. Talk to people and do
fun stuff and realize that without good mental health, you cannot have sustainable
working habits.</p>
<p>Few weeks after creating the phoenix app, an opportunity came up. A friend of mine
is now interested in my project and is interested in becoming a co-founder. I knew
right there that if I take him as a co-founder, I would have to re-write the app
in a language that he also knows and he is familiar to. If I made him learn a
completely new language that he might not even be interested in, our progress will be
slow and we won’t be creating this in the best practice that the language demands.
I needed a co-founder. We should do it again in <a href="http://rubyonrails.org/">Rails</a>.</p>
<p><img src="/img/posts/rails-logo.png" alt="ruby-on-rails" /></p>
<p>Ruby on rails is tried and tested and we both work in a company that works primarily
on rails. It is our full time jobs and we know what we are doing in rails. We
have our preferences in other gems and styles and how we test our code. It boosted our
productivity and now, we are almost ready to launch our private beta.</p>
<p>It would be good to release something in a totally new language and framework with
shiny new tools. There is something sexy in coding in the unfamiliar ground. But it
comes with a cost and I guess I just cowarded to the feat of creating something from
a tool that I am not used to. Maybe next time I would have that energy. But for now,
I will trust my tried and tested for something that I would release to production, but
still have my shiny new tools for the toy apps and experiments that I will (in the
future) do.</p>In the past few months, I have been wanting to start a startup. The entrepreneur in me has been itching to design and build and market a whole new product that could possibly help a lot of people. Once my idea was set, I was on the hunt for my new tech stack. And of course I lean on the hottest and latest. I started dreaming of becoming a hipster programmer who would release a kraken of an app in the wild and be featured in the blog posts that would talk about how we did it and how successful our company’s stack is. So I leaned towards Elixir and Phoenix.Batch Change Files2017-04-26T00:00:00+00:002017-04-26T00:00:00+00:00http://pauloancheta.com//zsh/sed/the%20silver%20searcher/2017/04/26/batch-change-files<p>Renaming strings is a job that we as programmers can never avoid. The only thing we can do is either make it fun or make it fast that we don’t have to think about it anymore. So, I tried to do both. Following what I have learned from the book <a href="https://www.amazon.com/Sed-Awk-Dale-Dougherty/dp/1565922255">Sed & Awk</a>, I wrote my simple script. Just a small note, I am using <a href="https://github.com/ggreer/the_silver_searcher">The Silver Searcher</a> here. And yes, it is faster than <a href="https://beyondgrep.com/">Ack</a>.</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/zsh</span>
<span class="c"># run chmod 777 on this file</span>
<span class="c"># use silver searcher to find all matches and use -l option to print only the filenames</span>
<span class="c"># convert file names into an array so we can loop through it</span>
<span class="nv">files_array</span><span class="o">=(</span><span class="s2">"</span><span class="k">${</span><span class="p">(@f)</span><span class="si">$(</span>ag <span class="nt">-l</span> <span class="s1">'OLD_STRING'</span><span class="si">)</span><span class="k">}</span><span class="s2">"</span><span class="o">)</span>
<span class="k">for </span>file <span class="k">in</span> <span class="nv">$files_array</span>
<span class="k">do</span>
<span class="c"># edit file and substitute OLD_STRING to NEW_STRING</span>
<span class="nb">sed</span> <span class="nt">-i</span> <span class="s1">''</span> <span class="nt">-e</span> <span class="s1">'s/OLD_STRING/NEW_STRING/g'</span> <span class="nv">$file</span>
<span class="k">done</span></code></pre></figure>Renaming strings is a job that we as programmers can never avoid. The only thing we can do is either make it fun or make it fast that we don’t have to think about it anymore. So, I tried to do both. Following what I have learned from the book Sed & Awk, I wrote my simple script. Just a small note, I am using The Silver Searcher here. And yes, it is faster than Ack.Robust JSON API for Mobile Apps Powered by Rails2016-04-22T00:00:00+00:002016-04-22T00:00:00+00:00http://pauloancheta.com//base64-images/jsonapi-resources/rspec_api_documentation/2016/04/22/base-64-images<p>Let’s say we have a <a href="http://rubyonrails.org">Rails</a> app that uploads images using <code class="language-plaintext highlighter-rouge">carrierwave</code>. We want to extend this functionality to let a mobile app upload images as well. The only constants we know are that the photos should be sent to our Rails app through a RESTful JSON API, and that the images are strings encoded in base64.</p>
<iframe src="//giphy.com/embed/TFMoOxjnAAMbm" width="480" height="269" frameborder="0" class="giphy-embed" allowfullscreen=""></iframe>
<p><a href="http://giphy.com/gifs/television-lol-TFMoOxjnAAMbm">via GIPHY</a></p>
<!-- break -->
<h1 id="here-are-the-tools-that-we-are-going-to-use">Here are the tools that we are going to use</h1>
<ul>
<li><a href="https://github.com/lebedev-yury/carrierwave-base64">carrierwave-base64</a> - Upload files encoded as base64 to carrierwave.</li>
<li><a href="https://github.com/cerebris/jsonapi-resources">jsonapi-resources</a> - provides a framework for developing a server that complies with the JSON API specification.</li>
</ul>
<p><em>For The Stretch Goal</em></p>
<ul>
<li><a href="https://github.com/zipmark/rspec_api_documentation">rspec_api_documentation</a> - Generate pretty API docs for your Rails API</li>
<li><a href="https://github.com/modeset/apitome">apitome</a> - Rails viewer for the documentation</li>
</ul>
<h1 id="add-models-to-db-and-mounting-the-uploader">Add Models to DB and mounting the uploader</h1>
<p><em>Implementing carrierwave-base64</em></p>
<p>We need a place to store the images. We could use a generator to create a <code class="language-plaintext highlighter-rouge">Post</code> table that has an <code class="language-plaintext highlighter-rouge">image</code> column which stores strings.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">></span><span class="err">$</span> <span class="n">rails</span> <span class="n">g</span> <span class="n">model</span> <span class="n">post</span> <span class="n">image</span><span class="ss">:string</span>
<span class="o">></span><span class="err">$</span> <span class="n">rake</span> <span class="n">db</span><span class="ss">:migrate</span></code></pre></figure>
<p>With our generated model, we attach a base64 image uploader which will allow us to attach an object in the fields of our database. The code still looks like an ordinary <code class="language-plaintext highlighter-rouge">carrierwave</code> implementation – but with a really small difference. Instead of having <code class="language-plaintext highlighter-rouge">mount_uploader</code> in the model, we would add <code class="language-plaintext highlighter-rouge">mount_base64_uploader</code> instead.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># app/models/post.rb</span>
<span class="k">class</span> <span class="nc">Post</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="n">mount_base64_uploader</span> <span class="ss">:image</span><span class="p">,</span> <span class="no">ImageUploader</span>
<span class="k">end</span>
<span class="c1"># app/uploaders/image_uploader.rb</span>
<span class="k">class</span> <span class="nc">ImageUploader</span> <span class="o"><</span> <span class="no">CarrierWave</span><span class="o">::</span><span class="no">Uploader</span><span class="o">::</span><span class="no">Base</span><span class="p">;</span> <span class="k">end</span></code></pre></figure>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># rails console</span>
<span class="nb">p</span> <span class="o">=</span> <span class="no">Post</span><span class="p">.</span><span class="nf">new</span>
<span class="n">base64_image</span> <span class="o">=</span> <span class="no">Base64</span><span class="p">.</span><span class="nf">encode64</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="n">awesome_picture</span><span class="p">.</span><span class="nf">jpg</span><span class="p">))</span>
<span class="nb">p</span><span class="p">.</span><span class="nf">image</span> <span class="o">=</span> <span class="s2">"data:image/jpg;base64,</span><span class="si">#{</span><span class="n">base64_image</span><span class="si">}</span><span class="s2">"</span>
<span class="nb">p</span><span class="p">.</span><span class="nf">save!</span></code></pre></figure>
<p>Now that can save a base64 image, we now have to create an API endpoint that our mobile app can call so they can post images.</p>
<h1 id="creating-the-json-api-endpoint">Creating the JSON API Endpoint</h1>
<p><strong>implementing jsonapi-resources</strong></p>
<p>Although there is a lot more to explore in <code class="language-plaintext highlighter-rouge">jsonapi-resources</code>, I will only touch on just a few of its really cool features. I believe this gem deserves its own blog post on how much benefit it provides with just a few lines of code.</p>
<p>Now let’s create a <code class="language-plaintext highlighter-rouge">jsonapi-resources</code> controller and resource with generators that the gem provides.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">></span><span class="err">$</span> <span class="n">rails</span> <span class="n">generate</span> <span class="n">jsonapi</span><span class="ss">:resource</span> <span class="n">api</span><span class="o">/</span><span class="n">post</span>
<span class="c1"># => app/resources/api/post_resource.rb</span>
<span class="k">class</span> <span class="nc">Api::PostResource</span> <span class="o"><</span> <span class="no">JSONAPI</span><span class="o">::</span><span class="no">Resource</span>
<span class="n">attribute</span> <span class="ss">:image</span>
<span class="k">end</span>
<span class="c1"># app/controllers/api/application_controller.rb</span>
<span class="k">class</span> <span class="nc">Api::ApplicationController</span> <span class="o"><</span> <span class="no">JSONAPI</span><span class="o">::</span><span class="no">ResourceController</span>
<span class="n">protect_from_forgery</span> <span class="ss">with: :null_session</span>
<span class="k">end</span>
<span class="c1"># app/controllers/api/posts_controller.rb</span>
<span class="k">class</span> <span class="nc">Api::PostsController</span> <span class="o"><</span> <span class="no">Api</span><span class="o">::</span><span class="no">ApplicationController</span><span class="p">;</span> <span class="k">end</span>
<span class="c1"># config/router.rb</span>
<span class="n">namespace</span> <span class="s2">"api"</span> <span class="k">do</span>
<span class="n">jsonapi_resources</span> <span class="ss">:post</span><span class="p">,</span> <span class="ss">only: </span><span class="p">[</span><span class="ss">:create</span><span class="p">]</span>
<span class="k">end</span></code></pre></figure>
<p>Although the <code class="language-plaintext highlighter-rouge">ApplicationController</code> that we have written inherits from the <code class="language-plaintext highlighter-rouge">jsonapi-resources</code> controller, this can also be a normal controller that includes a <code class="language-plaintext highlighter-rouge">ActsAsResourceController</code>.</p>
<p>In the routes, we are using the <code class="language-plaintext highlighter-rouge">jsonapi_resources</code> method. This gives us a lot of useful endpoints. For the sake of this example, let’s just focus on a posting endpoint and add <code class="language-plaintext highlighter-rouge">only: [:create]</code>. Thus giving:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">api_posts</span> <span class="nx">POST</span> <span class="o">/</span><span class="nx">api</span><span class="o">/</span><span class="nx">posts</span><span class="p">(.:</span><span class="nx">format</span><span class="p">)</span> <span class="nx">api</span><span class="o">/</span><span class="nx">posts</span><span class="err">#</span><span class="nx">create</span></code></pre></figure>
<p>This is actually all we need to post a base64 image through an API. From here we can use Postman:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">curl</span> <span class="o">-</span><span class="nx">X</span> <span class="nx">POST</span> <span class="o">-</span><span class="nx">H</span> <span class="dl">"</span><span class="s2">Content-Type: application/vnd.api+json</span><span class="dl">"</span> <span class="o">-</span><span class="nx">H</span> <span class="dl">"</span><span class="s2">Cache-Control: no-cache</span><span class="dl">"</span> <span class="o">-</span><span class="nx">H</span> <span class="dl">"</span><span class="s2">Postman-Token: 233cdeb0-ba65-7bd5-c550-8e8b79e181bb</span><span class="dl">"</span> <span class="o">-</span><span class="nx">d</span> <span class="dl">'</span><span class="s1">{
"data": {
"type": "posts",
"attributes": { "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAMAAACahl6sAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpCQTg1MENEQjEyMjA2ODExQjI2OUUwNTczQjFGQjMxMyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoyQjU5RTEyQkM3NjAxMUU0QTQ1NUJBOTY0QzkzRDVCMiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoyQjU5RTEyQUM3NjAxMUU0QTQ1NUJBOTY0QzkzRDVCMiIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QkI4NTBDREIxMjIwNjgxMUIyNjlFMDU3M0IxRkIzMTMiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QkE4NTBDREIxMjIwNjgxMUIyNjlFMDU3M0IxRkIzMTMiLz4gPC9yZGY6RGVzY3Jp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" }
}
} </span><span class="dl">'</span> <span class="dl">"</span><span class="s2">http://localhost:3000/api/posts</span><span class="dl">"</span></code></pre></figure>
<hr />
<h1 id="stretch-goal-testing--documentation">Stretch Goal: Testing & Documentation</h1>
<p><em>rspec api documentation</em></p>
<p>It is important to test and document API implementations. With <code class="language-plaintext highlighter-rouge">rspec_api_documentation</code>, we can do both at the same time. In my opinion, the best part of using this gem is that it does not generate the documentation for a failed example. It runs all the acceptance test, and if it passes, it generates the documentation. Once the documentation is re-generated, all the documentation is removed and generates a new one. Also, example documentation can be skipped with the <code class="language-plaintext highlighter-rouge">document: false</code> option.</p>
<p>First we need to tell <code class="language-plaintext highlighter-rouge">rspec_api_documentation</code> that we are going to be formatting the body to a <code class="language-plaintext highlighter-rouge">JSON</code> response by adding this helper:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># spec/rails_helper.rb</span>
<span class="c1"># Values listed are the default values</span>
<span class="no">RspecApiDocumentation</span><span class="p">.</span><span class="nf">configure</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
<span class="c1"># Change how the post body is formatted by default, you can still override by `raw_post`</span>
<span class="c1"># Can be :json, :xml, or a proc that will be passed the params</span>
<span class="n">config</span><span class="p">.</span><span class="nf">request_body_formatter</span> <span class="o">=</span> <span class="ss">:json</span>
<span class="n">config</span><span class="p">.</span><span class="nf">format</span> <span class="o">=</span> <span class="ss">:json</span>
<span class="k">end</span></code></pre></figure>
<p>Now that is all set up, we can start writing our test.</p>
<p>To set up our test, we would first have to include <code class="language-plaintext highlighter-rouge">rspec_api_documentation</code> dsl. This gives us wrappers to have headers to our requests and setting HTTP verbs as context. We also use <code class="language-plaintext highlighter-rouge">resource</code> instead of <code class="language-plaintext highlighter-rouge">describe</code> to define what we are testing.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># spec/acceptance/post_spec.rb</span>
<span class="nb">require</span> <span class="s2">"rails_helper"</span>
<span class="nb">require</span> <span class="s2">"rspec_api_documentation/dsl"</span>
<span class="n">resource</span> <span class="s2">"Posts"</span> <span class="k">do</span>
<span class="n">let!</span><span class="p">(</span><span class="ss">:valid_base64_image</span><span class="p">){</span> <span class="no">Base64</span><span class="p">.</span><span class="nf">encode64</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="n">awesome_picture</span><span class="p">.</span><span class="nf">jpg</span><span class="p">))</span> <span class="p">}</span>
<span class="n">let!</span><span class="p">(</span><span class="ss">:request_attributes</span><span class="p">){</span>
<span class="p">{</span><span class="ss">data: </span><span class="p">{</span><span class="ss">type: </span><span class="s2">"posts"</span><span class="p">,</span> <span class="ss">attributes: </span><span class="p">{</span><span class="ss">image: </span><span class="s2">"data:image/png;base64,</span><span class="si">#{</span><span class="n">valid_base64_image</span><span class="si">}</span><span class="s2">"</span><span class="p">}}</span>
<span class="p">}</span>
<span class="n">header</span> <span class="s2">"Accept"</span><span class="p">,</span> <span class="s2">"application/vnd.api+json"</span>
<span class="n">header</span> <span class="s2">"Content-Type"</span><span class="p">,</span> <span class="s2">"application/vnd.api+json"</span>
<span class="k">end</span></code></pre></figure>
<p>Below I have added a method that will be passed in a request method in my “example” (“example” in an acceptance test is analogous to an <code class="language-plaintext highlighter-rouge">it</code> block). A header method takes in 2 arguments: the header field name as a string and the header value.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">resource</span> <span class="s2">"Posts"</span> <span class="k">do</span>
<span class="c1"># ... lines ommitted</span>
<span class="n">post</span> <span class="s2">"/api/posts"</span> <span class="k">do</span>
<span class="n">example</span> <span class="s2">"Post a photo"</span> <span class="k">do</span>
<span class="n">do_request</span><span class="p">(</span><span class="n">request_attributes</span><span class="p">)</span>
<span class="n">expect</span><span class="p">(</span><span class="n">status</span><span class="p">).</span><span class="nf">to</span> <span class="n">eq</span> <span class="mi">201</span>
<span class="n">images</span> <span class="o">=</span> <span class="no">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">response_body</span><span class="p">)</span>
<span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="s2">"data"</span><span class="p">)</span>
<span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="s2">"attributes"</span><span class="p">)</span>
<span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="s2">"image"</span><span class="p">)</span>
<span class="n">expect</span><span class="p">(</span><span class="n">images</span><span class="p">[</span><span class="s2">"image"</span><span class="p">][</span><span class="s2">"url"</span><span class="p">]).</span><span class="nf">to</span> <span class="n">be_present</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>Now, let’s examine what goes in the test. In <code class="language-plaintext highlighter-rouge">rspec</code> we normally use <code class="language-plaintext highlighter-rouge">describe</code> or <code class="language-plaintext highlighter-rouge">context</code>. In an <code class="language-plaintext highlighter-rouge">rspec_api_documentation</code> test, we use the http verb, followed by the path that we want to test. It also takes in a block that contains a <code class="language-plaintext highlighter-rouge">do_request</code> method. This method can take in an argument. In a <code class="language-plaintext highlighter-rouge">GET</code> request, it does not need an argument, but for our case, the <code class="language-plaintext highlighter-rouge">POST</code> request takes in a hash as an argument.</p>
<p>Run the test and generate the docs with:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">></span><span class="err">$</span> <span class="n">rake</span> <span class="n">docs</span><span class="ss">:generate</span>
<span class="c1"># Or jus run the test without generating the documentation</span>
<span class="o">></span><span class="err">$</span> <span class="n">rspec</span> <span class="n">spec</span><span class="o">/</span><span class="n">acceptance</span></code></pre></figure>
<p>The docs are available at <code class="language-plaintext highlighter-rouge">http://localhost:3000/docs/api</code> and with the help of <code class="language-plaintext highlighter-rouge">apitome</code> this would look really cool! In essence, <code class="language-plaintext highlighter-rouge">apitome</code> is a wrapper for <code class="language-plaintext highlighter-rouge">rspec_api_documentation</code> to enhance the generated documentation.</p>
<hr />
<p>Creating an API endpoint is never complete without a good proper documentation. With this API bootstrap combo for Rails, it makes mobile image upload feature easier. See how awesome this combo is with my toy app! Follow this <a href="https://blog-jsonapi.herokuapp.com/">link</a> to go to the website, and this <a href="https://blog-jsonapi.herokuapp.com/docs/api">link</a> to go to the api documentation</p>Let’s say we have a Rails app that uploads images using carrierwave. We want to extend this functionality to let a mobile app upload images as well. The only constants we know are that the photos should be sent to our Rails app through a RESTful JSON API, and that the images are strings encoded in base64.