tag:www.elabs.se,2005:/blogThe Elabs Blog2016-02-14T08:22:37Ztag:www.elabs.se,2005:Post/782016-02-13T17:31:14Z2016-02-14T08:22:37ZA Love Letter<p>It’s Valentine’s day.</p>
<p>Love is a rare thing to find. In this line of work we’re always talking about passion, about magic, about how users "love" this thing or the other. But truth be told, it’s not very common. We’ll always shoot for excellence and we’ll always try to achieve that special sparkle—the one that can only come from little decisions made right, the one that makes products come alive. That’s why you hire us, right? That’s what we do.</p>
<p>Love, though?</p>
<p>The stars have to align. We’re not even sure all products or companies are made to be loved. Some are made to be indispensable, some are made to be intensely functional, and some try to help you by staying out of your way. And that’s fine. We all want different things, and everyone finds their own road to happiness. But you know what? When love <em>does</em> strike, you kind of want to shout it from the rooftops. And… we have something to tell you.</p>
<p>Ours, as a company, is not the romantic kind of love. It’s more of a two-hearts-beating-as-one sort of affair, maybe described as the way you look at a friend after a few years and you realize that it’s them, it was them all along, and you never saw it coming.</p>
<p>It struck us right in the face. We used to be Elabs and <a href="http://www.varvet.com/">Varvet</a>. But we’re one and the same now and it’s Valentine’s day, a day for celebrating love and sipping pink champagne, and the path before us looks strewn with rose petals. Join us in walking it.</p>
<p>All this is to say: we’ve merged with Varvet. There used to be two separate companies, awesome in their own way and doing almost the same thing. From now on there’s just one company, called <a href="http://www.varvet.com/">Varvet</a>; twice as strong and at least twice as clever. We’ll still be building superlative digital experiences, there’s just more of us now. Feel free to <a href="/contact">contact us</a>!</p>
CJ Kihlbomtag:www.elabs.se,2005:Post/772015-12-30T16:56:34Z2015-12-30T18:32:07ZThe Year 2015: A Summary<p>I missed my <a href="/tag/recap">annual recap</a> last year, mostly because I was tired after a very stressful year, but it’s time to pick up the tradition again. Here are some of the highlights from 2015.</p>
<h2>Client projects</h2>
<p>We had another year of great client collaborations. During the first half of the year we worked on several interesting projects together with <a href="http://www.purpose.com">Purpose</a>, including a social media monitoring dashboard for the climate change awareness campaign <a href="http://www.weareherenow.com">Here Now</a>, the <a href="https://march.globalcitizen.org">One Second March campaign</a>, and other work for non-profits like the <a href="http://www.gatesfoundation.org">Bill & Melinda Gates Foundation</a> and <a href="https://www.oxfam.org">Oxfam</a>. We also helped our friends at <a href="http://oktavilla.se">Oktavilla</a> with some work on <a href="https://unicef.se">UNICEF.se</a>.</p>
<p>We were also fortunate to work with great companies like <a href="http://www.telenorconnexion.com">Telenor Connexion</a> with their Internet-of-Things (IoT) platform, and <a href="https://www.blueboxcloud.com">Blue Box</a> with their private cloud solution. Both of those projects are continuing next year too.</p>
<p>We continued working with some of our exciting startup clients, like <a href="http://naturkartan.se">Naturkartan</a> and <a href="https://pinracer.se">Pinracer</a>. We also worked with <a href="http://www.footballaddicts.com">FootballAddicts</a> on our first real Elixir project. We hope to do more Elixir in 2016, so <a href="/contact">get in touch</a> if you need any Elixir help.</p>
<p><img src="http://elabs.se.s3.amazonaws.com/uploads/picture/file/115/normal_naturkartan.png" alt="Naturkartan"></p>
<p>Towards the end of the year we got into online newspaper design with <a href="http://mitti.se/kungsholmen/">Mitt i</a>, something we're also hoping to do more of in 2016.</p>
<h2>ProjectPuzzle</h2>
<p>In between client projects we finally found time to launch a brand new version of our <a href="https://www.projectpuzzle.com">team scheduling SaaS product ProjectPuzzle</a>. We’ve continued to improve it throughout the year, and we have much more planned. For me personally, it is completely indispensable to running the Elabs consulting business. It let’s me see our utilization at a glance, and keep track of who’s working on what project and when. And with the improvements we’re making, pretty soon it will be a full solution to managing a client services business.</p>
<p><img src="http://elabs.se.s3.amazonaws.com/uploads/picture/file/116/normal_projectpuzzle.png" alt="ProjectPuzzle"></p>
<h2>Flip</h2>
<p>During Lab Days and downtime between client projects Anders and Robin created our first own iOS app: <a href="http://www.flipreversi.com">Flip, a beautiful version of the classic board game Reversi</a>. Flip is free on the App Store, give it a try!</p>
<p><img src="http://elabs.se.s3.amazonaws.com/uploads/picture/file/117/normal_flip.png" alt="Flip Reversi game"></p>
<h2>Journeyman tour visit</h2>
<p>In June we had a visit from <a href="https://twitter.com/DevLCSC">Lennart</a> who spent a week with us as part of his journeyman tour. Lennart is always a vitamin injection, and we really enjoyed having him with us. We finished the week with a Lab Day where we did some Elixir and Phoenix mob programming. Great fun!</p>
<p>Lennart wrote a blog post about his <a href="http://codecoupled.org/2015/07/09/journeyman-elabs/">journeyman visit with Elabs</a>, you should read it.</p>
<p><img src="http://elabs.se.s3.amazonaws.com/uploads/picture/file/118/normal_lennart.jpg" alt="Lennart's visit"></p>
<h2>Nordic Ruby and True North reunion</h2>
<p>After running conferences for 5 years straight we decided to take a break during 2015 to catch our breath a bit. We couldn’t stay away from our regular venue <a href="http://yasuragi.se/en">Yasuragi</a> completely though, so in early July we held at Nordic Ruby and True North reunion where we invited past attendees to come hang out with us in the serene Japanese spa in the Stockholm archipelago. To our delight, we had quite a few attendees come from as far away as the US to spend the weekend with us!</p>
<p><img src="http://elabs.se.s3.amazonaws.com/uploads/picture/file/119/normal_yasuragi.jpg" alt="Reunion at Yasuragi"></p>
<p>While it was nice to take a break and focus on our core business, we really miss organizing a conference. There’s a good chance that we’ll do one in 2016. <a href="https://twitter.com/elabs">Follow @elabs on Twitter</a> for updates!</p>
<h2>Team changes</h2>
<p>The biggest changes this year were that we said goodbye to two of our team members.</p>
<p>Johannes—one of our designers—left in July after 5 great years with us to work on a new product idea he had.</p>
<p><img src="http://elabs.se.s3.amazonaws.com/uploads/picture/file/120/normal_johannes.jpg" alt="Johannes"></p>
<p>Kim, who joined us as a developer 4 years ago, left right before Christmas to move back to Stockholm where his family lives.</p>
<p><img src="http://elabs.se.s3.amazonaws.com/uploads/picture/file/121/normal_kim.jpg" alt="Kim"></p>
<p>We’ll miss them both and wish them the very best of luck!</p>
<p>It wasn’t all goodbyes though! In November we welcomed <strong>Max</strong>, our second apprentice. Max has been doing great so far, and we’re excited to have him on the team. <a href="/blog/73-the-apprentice">He introduced himself</a> here on the blog a few weeks ago.</p>
<h2>What’s next?</h2>
<p>As I’ve <a href="https://twitter.com/cjse/status/679229605761433600">hinted at on Twitter</a>, we have some very exciting things in the works for next year! I won’t give it away quite yet, but you should <a href="https://twitter.com/elabs">follow us on Twitter</a> if you want to be among the first to know.</p>
<p>I can’t wait for 2016 to begin!</p>
<p><strong>Happy New Year!</strong></p>
<p><em>/ CJ</em></p>
CJ Kihlbomtag:www.elabs.se,2005:Post/762015-12-08T15:28:38Z2015-12-08T16:45:09ZA Christmas tree formatter for RSpec!<p>It's December, a time of <a href="https://en.wikipedia.org/wiki/Mulled_wine#Nordic_gl.C3.B6gg">glögg</a> and gingerbread cookies with blue cheese… and Christmas trees!</p>
<p>Me and Anders figured we'd bring some Christmas spirit into our test suite, so we created an RSpec formatter very much like the regular progress formatter with green dots… except it's a tree!</p>
<p>Source code is on GitHub, v0.1, and a nasty piece of code, but it's December so it's very much time to ship it! It's named <a href="https://github.com/elabs/christmas_tree_formatter">christmas_tree_formatter</a>.</p>
<p>Here's a GIF of it in action:</p>
<p><img src="http://i.imgur.com/02yvhuy.gif" alt="Imgur"></p>
Kim Burgestrandtag:www.elabs.se,2005:Post/732015-12-02T15:19:00Z2015-12-02T15:21:22ZThe Apprentice<p>I am not the new apprentice of Sir Alan Sugar, as in the TV series The Apprentice. But I am the new apprentice of Elabs, and for that I am very grateful! The first week is soon completed and wow, I already love this place. A spectacular office with awesome people. Everyone here has been treating me so nicely. Except that they all seem to totally crush me in ping-pong. Maybe that is how it’s supposed to be though for a rookie.</p>
<p>Anyhow, my name is Max Perzon and I am a 24 year old web developer student from the school <a href="https://www.facebook.com/Yrgo-Reklam-Marknadsf%C3%B6ring-788473864580360/">YRGO</a> here in Gothenburg. I am spending the last six months of my education out in the “field” as an apprentice, and that is why I am here at Elabs. What excites me the most about being here is the opportunity I get to be around people way above my skill level for six months. Exposing my ignorance and learning as much as I can by osmosis. Seeing how they attack obstacles and solve problems together. There is no doubt in my mind that I will learn a lot from this experience.</p>
<p>I live together with my girlfriend Åsa in a small apartment here in Gothenburg. She’s from northern Sweden, Östersund. So we like to ski quite a lot together. Other big interests of mine are reading books, running and traveling. I also like dogs, they are awesome.</p>
<p>To round this up, I just want to say thank you to everyone in the team here for welcoming me in such a nice way. It means a lot. I’m looking forward to continuing to get to know each and everyone of them better as time passes. And also, hopefully “steal” some of the secret weapons in their coding arsenal.</p>
<p>Over and out.</p>
<p><em>— Max Perzon</em></p>
Max Perzontag:www.elabs.se,2005:Post/722015-10-30T09:43:15Z2015-10-30T13:29:48ZRetrieving the last N ordered records with ActiveRecord<p>Here's a database of chat messages.</p>
<pre><code>| id | created_at |
|——————|————————————|
| 1 | 13:20 |
| 2 | 13:21 |
| 3 | 13:22 |
| … | …………… |
| 121 | 14:27 |
| 122 | 14:29 |
| 123 | 14:32 |
|——————|————————————|
</code></pre>
<p>So, we're building a chat, and what we'd like to see is the 10 most recent messages, with the oldest of the bunch at the top, and the newest message at the bottom, something like: <code>[<Message 113>, <Message 114>, <Message 115>, …, <Message 123>]</code>.</p>
<p>At first, you might think <em>"I'll just sort all messages by created_at in ascending order, and take the last 10"</em>. OK, here's what that looks like.</p>
<pre><code>Message.order(created_at: :asc).last(10) # => [<Message 113>, <Message 114>, …, <Message 123>]
</code></pre>
<p>Looks good? Yes? No. Have a look at the SQL:</p>
<pre><code> SELECT "messages".* FROM "messages" ORDER BY "messages"."created_at" ASC
| id | created_at |
|——————|————————————|
-> | 1 | 13:20 |
-> | 2 | 13:21 |
-> | 3 | 13:22 |
-> | … | …………… |
-> | 121 | 14:27 |
-> | 122 | 14:29 |
-> | 123 | 14:32 |
|——————|————————————|
</code></pre>
<p>What, no mention of <strong>10</strong> in our SQL query?! <code>.last</code> is not so clever. We end up loading <strong>all messages in our database to Ruby</strong>, maybe a few hundred thousand, and then we throw away all messages except for the last 10, what a waste.</p>
<h3>Okay, how about using <code>OFFSET</code>? Let's try.</h3>
<pre><code>Message.order(created_at: :asc).offset(Message.count - 10).limit(10) # => [<Message 113>, <Message 114>, …, <Message 123>]
</code></pre>
<p>Looks good? Yes? No. Let's look at the SQL:</p>
<pre><code> SELECT "messages".* FROM "messages" ORDER BY "messages"."created_at" ASC OFFSET 112 LIMIT 10
| id | created_at |
|——————|————————————|
SKIP | 1 | 13:20 |
SKIP | 2 | 13:21 |
SKIP | … | …………… |
-> | 113 | 14:13 |
-> | … | …………… |
-> | 123 | 14:32 |
|——————|————————————|
</code></pre>
<p>A few important notes about this.</p>
<ol>
<li><code>OFFSET</code> in SQL must be a positive number so we can't simply use <code>-10</code> as our offset.</li>
<li>ActiveRecord will try to coerce our offset to an integer using <code>#to_i</code>, so we can't pass a subquery as our offset, which leaves us with precomputed positive numbers only.</li>
<li>What is our offset? It must be calculated ahead of time using <code>Message.count</code>, this is slow, and prone to race conditions if we get more messages in between our <code>count</code> and <code>select</code>.</li>
</ol>
<p>Even if we disregard all the above points, have a look at the <a href="http://www.postgresql.org/docs/9.1/static/queries-limit.html">documentation for LIMIT/OFFSET in PostgreSQL</a>: <em>"The rows skipped by an OFFSET clause still have to be computed inside the server; therefore a large OFFSET might be inefficient."</em></p>
<p>Oh, OK, so offset is off.</p>
<h3>One more try. Let's sort this out with a new mindset. We change the order of our SQL, and reverse the thing in Ruby. That must work!</h3>
<pre><code>Message.order(created_at: :asc).reverse_order.limit(10).reverse # => [<Message 113>, <Message 114>, …, <Message 123>]
</code></pre>
<p>Looks good? Yes? Let's look at the SQL.</p>
<pre><code> SELECT "messages".* FROM "messages" ORDER BY "messages"."created_at" DESC LIMIT 10
| id | created_at |
|——————|————————————|
-> | 123 | 14:32 |
-> | … | …………… |
-> | 113 | 14:13 |
|——————|————————————|
</code></pre>
<p>Well, this is actually quite OK. We only retrieve 10 records from the database, which is what we want. Sure, the order of the messages is wrong which is kind of sad, but we can fix that later. Let's make a scope of this and call it a good day!</p>
<pre><code>class << Message
def in_order
order(created_at: :asc)
end
def recent(n)
in_order.reverse_order.limit(n).reverse
end
end
</code></pre>
<p>There's a few downsides with this, can you spot it?</p>
<ol>
<li>We have to reverse it in Ruby.</li>
<li>The return value is an Array, and not an ActiveRecord::Relation, since we force it with <code>reverse</code>.</li>
<li>We can't <a href="http://api.rubyonrails.org/classes/ActiveRecord/SpawnMethods.html#method-i-merge">merge</a> an Array with other scopes.</li>
<li>We can't chain additional SQL conditions to the end of our array to further filter the 10 results, e.g. <code>Message.recent(5).where(…)</code>.</li>
</ol>
<h3>We can still do better!</h3>
<p>What if I told you there is a way to reverse the result in SQL, and that there's also an OK way to do so with ActiveRecord? I'm sure you'd believe me after all of this, anything else would be cruel.</p>
<pre><code>class Message
class << self
def in_order
order(created_at: :asc)
end
def recent(n)
in_order.endmost(n)
end
def endmost(n)
all.only(:order).from(all.reverse_order.limit(n), table_name)
end
end
end
</code></pre>
<p>Looks good? Yes! Let's use this and have a look at the SQL, <code>Message.recent(10)</code>:</p>
<pre><code>SELECT "messages".* FROM (
SELECT "messages".* FROM "messages" ORDER BY "messages"."created_at" DESC LIMIT 10
) messages ORDER BY "messages"."created_at" ASC
| id | created_at |
|——————|————————————|
-> | 113 | 14:13 |
-> | … | …………… |
-> | 123 | 14:32 |
|——————|————————————|
</code></pre>
<p>This is <strong>exactly</strong> what we want, and it has none of the downsides of reversing the results in Ruby. The final <code>.recent</code> method is our final implementation, and it works as expected.</p>
<p>The keen eye will notice that I extracted part of the logic into a general-purpose <code>.endmost</code> method. <code>.endmost</code> is what you want when you call <code>.last</code>: the last N records in the result set <em>without having to retrieve all records from the database</em>, and it works with any ordering. You can impose filtering before, <code>Message.where(…).in_order.endmost(10)</code>, and afterwards to filter your final results as well <code>Message.in_order.endmost(10).where(…)</code>.</p>
<p>Thanks for reading! I hope you found it as useful as I did!</p>
Kim Burgestrand