Postgres Window Functions

Posted 17 days back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home

We recently ran into a case where a join was getting out of hand and we were struggling with how to rein in the size of it. Then we found window functions. Window functions (Oracle calls them analytic functions) are a part of the SQL standard and this post will explore how to use them in Postgres. Let’s see how they work and what kind of problems they can help us solve.

Our problem

Let’s say we have a blogging platform where we have posts that have many comments. Here’s our setup:

CREATE TABLE posts (
  id integer PRIMARY KEY,
  body varchar,
  created_at timestamp DEFAULT current_timestamp
);

CREATE TABLE comments (
 id INTEGER PRIMARY KEY,
 post_id integer NOT NULL,
 body varchar,
 created_at timestamp DEFAULT current_timestamp
);

/* make two posts */
INSERT INTO posts VALUES (1, 'foo');
INSERT INTO posts VALUES (2, 'bar');

/* make 4 comments for the first post */
INSERT INTO comments VALUES (1, 1, 'foo old');
INSERT INTO comments VALUES (2, 1, 'foo new');
INSERT INTO comments VALUES (3, 1, 'foo newer');
INSERT INTO comments VALUES (4, 1, 'foo newest');

/* make 4 comments for the second post */
INSERT INTO comments VALUES (5, 2, 'bar old');
INSERT INTO comments VALUES (6, 2, 'bar new');
INSERT INTO comments VALUES (7, 2, 'bar newer');
INSERT INTO comments VALUES (8, 2, 'bar newest');

Here’s a SQL Fiddle with our example if you’d like to query the data.

Our Goal

We want to get each post’s three most recent comments. We could start by writing a query such as the one below to pull all the posts and comments. We could then filter this result in whatever application language we’re using (Ruby, PHP, Go, whatever) on top of our database:

SELECT posts.id AS post_id, comments.id AS comment_ids, comments.body AS body
FROM posts LEFT OUTER JOIN comments ON posts.id = comments.post_id;

resulting in:

| POST_ID | COMMENT_IDS |       BODY |
|---------|-------------|------------|
|       1 |           1 |    foo old |
|       1 |           2 |    foo new |
|       1 |           3 |  foo newer |
|       1 |           4 | foo newest |
|       2 |           5 |    bar old |
|       2 |           6 |    bar new |
|       2 |           7 |  bar newer |
|       2 |           8 | bar newest |

This solution may work for awhile but what happens when we have 10’s of thousands of posts that each have 10’s of thousands of comments (it’s a really popular blog)? That resulting join table just got really big.

We want to see just these results without requiring any further filtering:

| POST_ID | COMMENT_ID |       BODY |
|---------|------------|------------|
|       1 |          4 | foo newest |
|       1 |          3 |  foo newer |
|       1 |          2 |    foo new |
|       2 |          8 | bar newest |
|       2 |          7 |  bar newer |
|       2 |          6 |    bar new |

To get this subset we need to some how rank or order the comments for each post and then limit the set. How do we go about this more targeted task in SQL? Enter window functions.

What are window functions?

Window functions are a tool to perform advanced sorting and limiting on a subset of a joined table of data (hence, the name window). We’ll look at one particular function, dense_rank(), but all built-in (sum, for example) and user-defined aggregate functions can act as window functions by calling the OVER keyword. Some other popular functions include row_number(), rank(), and percent_rank(). A complete list of available window functions can be found here.

Back to our problem

How can we use this new knowledge to write our query? We want the three most recent comments so we know we’ll need to do some sort of sorting by created_at and then limit the number of results.

Here’s how we can get the ranking information we need using dense_rank():

SELECT posts.id AS post_id, comments.id AS comment_id, comments.body AS body,
  dense_rank() OVER (
    PARTITION BY post_id
    ORDER BY comments.created_at DESC
  ) AS comment_rank
FROM posts LEFT OUTER JOIN comments ON posts.id = comments.post_id;

Resulting in:

| POST_ID | COMMENT_ID |       BODY | COMMENT_RANK |
|---------|------------|------------|--------------|
|       1 |          4 | foo newest |            1 |
|       1 |          3 |  foo newer |            2 |
|       1 |          2 |    foo new |            3 |
|       1 |          1 |    foo old |            4 |
|       2 |          8 | bar newest |            1 |
|       2 |          7 |  bar newer |            2 |
|       2 |          6 |    bar new |            3 |
|       2 |          5 |    bar old |            4 |

We have the same query from before but we’ve added an additional field in our SELECT statement:

dense_rank() OVER (
  PARTITION BY post_id
  ORDER BY comments.created_at DESC
) AS comment_rank

OVER is the keyword that triggers the use of the window function. Inside the OVER statement we’re saying:

  • rank the comments by created_at (ORDER BY comments.created_at) and
  • scope the ranking to each post (PARTITION BY post_id)

This result is still returning the full set but now we have the added comment_rank field showing us the comments for each post in order of age. Given this ranking, now we need to fetch just those comments that have a comment_rank less than 4 (since we want only the three most recent comments). We can achieve this goal by using our new ranking query as a sub-select statement like so:

SELECT comment_id, post_id, body FROM (
  SELECT posts.id AS post_id, comments.id AS comment_id, comments.body AS body,
    dense_rank() OVER (
      PARTITION BY post_id
      ORDER BY comments.created_at DESC
    ) AS comment_rank
  FROM posts LEFT OUTER JOIN comments ON posts.id = comments.post_id
) AS ranked_comments
WHERE comment_rank < 4;

resulting in:

| POST_ID | COMMENT_ID |       BODY |
|---------|------------|------------|
|       1 |          4 | foo newest |
|       1 |          3 |  foo newer |
|       1 |          2 |    foo new |
|       2 |          8 | bar newest |
|       2 |          7 |  bar newer |
|       2 |          6 |    bar new |

And that’s our answer. Our query has become pretty hard to read though.

Refactoring using Common Table Expressions

We can gain back some of the readability by using a Common Table Expression (or CTE). Our query would become:

WITH ranked_comments AS (
  SELECT posts.id AS post_id, comments.id AS comment_id, comments.body AS body,
    dense_rank() OVER (
      PARTITION BY post_id
      ORDER BY comments.created_at DESC
    ) AS comment_rank
  FROM posts LEFT OUTER JOIN comments ON posts.id = comments.post_id
)

SELECT
  post_id,
  comment_id,
  body
FROM ranked_comments
WHERE comment_rank < 4;

Window functions introduce some additional syntax but are a really powerful tool when we need more complex sorting or limiting behavior.

Want to learn more?

For some more help on the syntax and options for using these functions check out these docs or this guide. There are some gotchas around dealing with NULL values and how ordering works so be sure to read through this section and test your code. Happy querying.

Using Gestalt Principles for Natural Interactions

Posted 18 days back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home

Gestalt is a term used in psychology which expresses the idea that the whole of something is more important to our understanding than the individual parts. The Gestalt principles describe the way our mind interprets visual elements.

The principles I find most helpful day-to-day are:

  • Similarity
  • Enclosure
  • Continuation
  • Closure
  • Proximity
  • Figure-Ground

We’ll look at a few examples of each principle and break down how it informs the way you interact with an interface.

Similarity

Perceiving objects that are similar to be part of a group or pattern.

You can see similarity being used in Van Gogh’s “Starry Night”. We are able to distinguish the stars from the night sky because of two contrasting attributes:

  • The circular orbs that we perceive to be stars are all the same color, yellow.
  • The direction of the brush strokes making up the stars are all moving in the same, circular direction.

Van Gough Starry Night

This tells us that all of the elements with those two attributes are the same. It also tells us that they are separate from the night sky.

Let’s take a look at an example of similarity used in an interface. In this example from Tumblr, we perceive the links, represented by icons, to each act as a link to create a blog post.

Tumblr Post Icons

What are the similarities?

  • Each option is represented by an icon with text beneath.
  • Each icon and text beneath the icon are the same size.
  • The icons are evenly distributed in the space, each given equal treatment.

What does it tell us about the process of creating a blog post?

  • As a user, we know that any one of these icons represent a means to a similar end – creating a new blog post.
  • We know exactly where to go, or what UI elements to look for when we need to create a new blog post.

Enclosure

Things that appear to have a boundary around them are perceived to be grouped, and therefore related.

This example of a Facebook post has 3 instances of enclosure that afford the clarity of this interface.

Facebook Post

The first enclosure is the post as a whole (highlighted in the screenshot below). Each Facebook post is enclosed in a rectangle with a white background, thin gray border that distinguishes it from the light gray background.

Facebook Post

The second enclosure is the representation of the link within the post. The photo followed by the title and description of the link appear to be grouped together, and therefore related. I now know the organization and separation of information.

Facebook Post

The third enclosure is the area at the bottom of the post. Everything related to social interactions is enclosed inside of the light-blue background. As a user, this allows me to know exactly how and where to interact with this post.

Facebook Post

These enclosures provide affordances that allow me to group and interpret the information accurately.

Continuation

The eye creates momentum as it is compelled to move through one object and continue to another.

Here’s a screenshot of Google Maps walking directions. Rather than a series of blue dots, we perceive this as a single line.

Google maps walking directions

We also understand we are to physically walk in the direction of this “line”. Nothing in the interface explicitly tells us that the dotted line indicates direction. A small icon of a person walking and the blue dots create the idea of momentum and direction.

Another common application of continuation is the timeline of a media player.

Rdio audio player

This line represents the duration of the track. As the track plays, the color of the line changes.

The second color is perceived as a second line. As that line grows, we perceive the passage of time. We don’t expect the second line to continue past the end point of the first line.

This gives us the understanding that when the second line reaches the end of the first line, the track has played to completion. You don’t imagine that the second line could extend past the first.

The interface does not need to offer hints in the form of visual “nouns” (e.g. an arrow indicating duration or time) because the visual “verbs” (e.g. the animation/interaction when the track is playing) teaches users very quickly when the track begins and to anticipate when it finishes.

Closure

When an object is incomplete, but enough of the object is indicated, the mind perceives the object to be whole by mentally filling the information.

Take a look at the Notifications icon in Twitter’s interface. When you have a notification, a number enclosed in a square is placed over the icon.

Twitter Notification Icon with Notification

There is enough of the bell visible for our mind to still read this icon as the bell.

Let’s look at another example of closure being used to complete an interaction. In the Urban Outfitters online store, notice what happens when an item is added to my “shopping cart”.

Urban Outfitters Online Shopping Example

In this interaction, once the “Add to Bag” button is clicked, a few things happen:

  • The text inside of the button changes to “Added!”.
  • A number appears next to the shopping cart icon in the navigation.
  • A modal slides down from the shopping cart icon which confirms, again, the item has been added to my shopping cart.

The fact that the item has been added to our shopping cart is implied through the interface. We didn’t go to my shopping cart page to see the items in it. We also did not need to use a drag and drop interaction (which is often more work for the user) to create this reassurance. We receive enough visual feedback in the interface to assume that the item has been added.

All of this information is understood without actually having to go to the shopping cart page.

Proximity (or Grouping)

When elements are close together, we perceive them to be part of a group.

Let’s take a look at the layout of Twitter’s profile information: Twitter example

The avatar, cover photo, display name, and user name are placed close together. Because they are close together spatially, we read this information as a group, and thus, being related.

The stats associated with the Twitter account are located a few pixels below the grouping of personal information.

Twitter example

The pink line in this screenshot highlights the negative space separating the two groupings and creates the boundary separating their proximities.

We can look at another example from Twitter that utilizes the same principle:

Twitter example

The elements that allow you to interact with this tweet are close together and are located farther down, vertically, than the rest of the content and elements in the enclosure.

Twitter example

The highlighted areas expose the groupings created by the layout. You can see how the proximity of a number to their respective interaction icons indicates the relationship between the number and the icon.

A note about white space

You may hear designers say things like, “we need more white-space” from time-to-time. White space is a synonymous term for “negative space”.

In many cases (not all), white space is used as a sort of enclosure (reference the enclosure principle above). The negative space acts as an invisible border. By doing this, it actually defines a region of proximity, thereby adding meaning to something that might otherwise look too “busy” or cluttered to make sense of.

The really cool thing is that this meaning is created without having to add lines, colors, or other visual elements. The areas where visual elements are absent (the white or negative space) actually creates just as much meaning than the presence of visual elements (the positive space).

This is the same idea as grouping digits of a phone number. It’s easier to read and remember if the numbers are grouped. The grouping is visualized by adding negative space between the numbers.

E.g. 555-555-5555 vs 5555555555

Figure Ground

Perceiving certain objects as being in the foreground and other objects as being in the background.

A common example of figure-ground is the interaction of opening up a modal.

New York Times Modal

In the New York Times example above, the figure-ground relationship is manipulated by:

  • a white, transparent background that softens the appearance of the original content you were focused on.
  • a border and subtle drop shadow around the box containing the log in fields.

The figure-ground relationship allows us to understand this interaction. You perceive the modal to be in the foreground and the New York Times home page to be in the background. This tells us that we have not left the page we were on because we can still see it “beneath” the transparent white background. However, the context has changed, as it now appears to have moved to the background, and new elements are in the foreground.

A note about minimal styles

Notice the minimal visual styles applied to the interface in our New York Times example. The border around the login modal is about 1 pixel wide, the drop shadow has a very limited spread and a light color so as not to create too much contrast.

When creating something to be minimal, (which is different from Minimalism), we want to know: what is the least amount of detail that can be added to create the necessary impact?

Deiter Rams' final principle for good design states that design “is as little design as possible”. Understanding how Gestalt principles are are applied allows us to create the essential meaning in our products without excess design, styles, or steps.

Takeaway

An interface should be more than a collection of isolated interactions. Our minds want to perceive that smaller interactions are related to each other and work together to complete a larger task.

If we’re not able to perceive this, the disconnect leaves room for confusion. People need to see that everything is somehow integrated into the larger goal at hand.

You can use these principles to build a more intuitive interface, identify problems, and find solutions in an existing interface.

Episode #495 - September 12th, 2014

Posted 20 days back at Ruby5

@rubystrings, Global Day of Coderetreat 2014, Asynchronous Integration Tests With Capybara, nice rake tasks, and module prepend in rails 5

Listen to this episode on Ruby5

Sponsored by NewRelic

Andrew Sharpe wrote about the Total Cost of Hosting Ownership with cloud hosting
NewRelic

@RubyStrings

Did you know that there are more than 100 different ways to create strings in #Ruby?
@RubyStrings

Coderetreat 2014

Global Day of Coderetreat 2014
Coderetreat 2014

Write Reliable, Asynchronous Integration Tests With Capybara

The asynchrounus nature of javascript can be a little difficult to test with Capybara
Write Reliable, Asynchronous Integration Tests With Capybara

Nice Rake Tasks

Maciej Mensfeld was having that issue with rake tasks slowing down his site and wrote a post about a powerful tool he used to solve the problem
Nice Rake Tasks

Module#prepend wiht Rails 5

Rails 5, Module#prepend, and the End of `alias_method_chain`
Module#prepend wiht Rails 5

How to Find Missing or Unused I18n Translations

Posted 21 days back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home

While I18n is a great tool for quick copy changes, translation files can quickly become large and multiply, leaving you with the feeling that there are missing or unused translations hiding under your nose. Enter i18n-tasks, which provides - you guessed it - I18n tasks. Among these tasks are:

% i18n-tasks unused

and

% i18n-tasks missing

Want the test suite to fail when there are missing or unused tasks? Add i18n-tasks to your Gemfile:

group :test do
  gem "i18n-tasks"
end
% bundle

And run the following:

% cp $(i18n-tasks gem-path)/templates/rspec/i18n_spec.rb spec/

You now have a test in your spec directory that will fail when there are missing or unused I18n translations!

Bonus: Use thoughtbot’s I18n-tasks config file template to set up your config/i18n-tasks.yml file. This will ensure that I18n-tasks doesn’t return false positives or false negatives.

What’s next?

If you found this useful, you might also enjoy:

Say Hello to thoughtbot at Denver Startup Week

Posted 21 days back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home

Whoot! Denver Startup Week is next week and we are super excited to attend many of the wonderful sessions that are planned for the week! There is an amazing lineup of design and development related sessions that our folks are going to be heading to as well as a few events we’ll be hosting ourselves.

We’ll be around at various events and should be fairly recognizable in our red jackets and t-shirts. Please say hi, we’d love to get to know you and find out what you’re up to in the Denver startup scene!

We’ll be at the following events and hope to see you there!

Tuesday - 1pm to 2:30pm - Discovering Compelling Solutions Through Storytelling & Playtime - Denver City Center - Speaker Anthony Navarre

The best solutions to complex and highly technical problems often seem counterintuitive and unexpected, but in hindsight seem so obvious and simple. Finding elegant solutions, for many, can seem unobtainable. In this interactive session, Anthony will explore some practical ways that your teams can work more creatively together to arrive at more compelling solutions through storytelling and playtime — no prior genius required.

To sign up please visit: DSW Schedule - Anthony’s Talk

Tuesday - 5:30pm to 7:30pm - Pins and Cues and You! - Lucky Strike Denver - hosted by thoughtbot

Please join thoughtbot (and friends) at our Denver Startup Week happy hour at the VIP and billiard sections at Lucky Strike. We’d love to get to know and hang out with as many of you as possible. We’ll have food, drinks, bowling, pool and plenty of room for folks to mingle and generally have fun. Form teams if you like - “Founders vs Developers, iOS vs Android, Sass vs Less” whatever … let’s just have fun!

To sign up please visit: DSW Schedule - Happy Hour Event

Wednesday - 3:00pm - 6:30pm - Startup Crawl - 1600 Champa Street Suite 340 - hosted by thoughtbot

We’ll be participating in the Startup Crawl happening on Wednesday evening. We plan to have some programming play with Spheros, Corn Hole, beer/wine/non-alcoholic beverages and snacks.

For more information please visit: Built in Colorado - DSW Startup Crawl

Thursday & Friday - 10:00am to 11:40am - Office Hours with Desi - Basecamp (1515 Arapahoe)

I will be at Basecamp to answer your questions about product development. I have 14 years experience building products and have lots of knowledge to share. Feel free to bring your questions/concerns and let me help you figure out next steps.

To sign up for Thursday’s session, please visit: DSW - Thursday Office hours with Desi

To sign up for Friday’s session, please visit: DSW - Friday Office hours with Desi

Friday - Various times - Design and Development Office Hours - Basecamp (1515 Arapahoe)

Several of our team members will be around Basecamp to help you with any design/usability or coding related issues. We’ll be tweeting our location from our @thoughtbot account so be sure to follow us and check for our locations on Friday.

Write Reliable, Asynchronous Integration Tests With Capybara

Posted 23 days back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home

We’ve come to rely on integration tests as part of a balanced testing approach. They support outside-in development, they catch regressions, and they bridge the gap between Ruby and JavaScript unit tests.

However, integration tests involving Ruby and JavaScript are fraught with danger. Developers frequently complain of tests which fail erratically. Debugging these tests can be somewhat of a mystery: you see records inserted into your test database during logs, but they somehow don’t show up on the page. These annoyances drive some developers to abandon integration tests entirely.

Why are integration tests with JavaScript so much harder?

rack-test

First, a little background.

Integration tests in Rails applications work by simulating a user’s experience through the HTML interface. You load up your Rails application, and your integration test harness (such as Capybara) simulates a user clicking around on the site.

Without JavaScript, Capybara uses rack-test as a driver by default and looks something like this:

  • You trigger user action by invoking one of Capybara’s DSL methods, such as visit.
  • Capybara tells its driver (rack-test) to load the requested URL.
  • rack-test uses the URL to generate a fake Rack request and passes it directly to the in-memory instance of your Rails application.
  • The Rack response is parsed by rack-test and saved as the current page.

Other actions like click_link work similarly, and rack-test will do things like save cookies, follow redirects, and post forms to make it feel like a real browser. This simulated browser can do many of the things a real browser can, but it’s missing one killer feature: JavaScript.

Selenium, capybara-webkit, and Poltergeist

JavaScript drivers work a little differently. Because browser tools like WebKit are difficult to load inside of a Ruby process, these drivers boot up an external process which can interact with the browser engine. Because the external process doesn’t have access to the in-memory instance of your Rails application, they must make actual HTTP requests.

Interactions performed using Capybara’s JavaScript drivers look something like this:

  • You trigger user action by invoking one of Capybara’s DSL methods, such as visit.
  • Capybara tells its driver (such as capybara-webkit) to load the requested URL.
  • The driver starts its external process to hold the browser engine.
  • In order to serve actual HTTP requests, Capybara boots a new instance of your Rails application in a background thread.
  • The driver’s browser process uses the URL to create a real HTTP request which is passed to your application server, such as Thin.
  • Thin translates the HTTP request into a Rack request, which is passed to your Rails application in the background thread.
  • Thin also translates your Rack response into a real HTTP response, which is accepted by the browser process.

There’s a lot of extra machinery in here to make this work: process forking, HTTP requests, and background threads. However, there’s really only one bump which frequently affects users: that pesky background thread.

If requests are served in a background thread, that means that your tests keep running while your application responds to simulated interactions. This provides for an endless number of race conditions, where your tests look for elements on the page which have not appeared yet.

Capybara to the Rescue

Much of Capybara’s source code is dedicated to battling this asynchronous problem. Capybara is smart enough to understand that a page may not have loaded by the time you try to interact with it. A typical interaction might look like this:

Test Thread Application Thread
Your test invokes visit. Waiting for a request.
Capybara tests the driver to load the page. Waiting for a request.
The driver performs a request. Waiting for a request.
Your test invokes click_link. Your application receives the request.
Capybara looks for the link on the page, but it isn’t there. Your application sends a response.
Capybara tries to find the element again, but it’s not there. The driver receives the response.
Capybara successfully finds the element from the response. Waiting for a request.

As you can see, Capybara handles this interaction gracefully, even though the test starts looking for a link to click on before the page has finished loading.

However, if Capybara handles these asynchronous issues for you, why is it so easy to write flapping tests with Capybara, where sometimes the tests pass and sometimes they fail?

There are a few tricks to properly using the Capybara API so as to minimize the number of possible race conditions.

Find the first matching element

Bad:

first(".active").click

If there isn’t an .active element on the page yet, first will return nil and the click will fail.

Good:

# If you want to make sure there's exactly one
find(".active").click

# If you just want the first element
find(".active", match: :first).click

Capybara will wait for the element to appear before trying to click. Note that match: :first is more brittle, because it will silently click on a different element if you introduce new elements which match.

Interact with all matching elements

Bad:

all(".active").each(&:click)

If there are no matching elements yet, an empty array will be returned, and no elements will be affected.

Good:

find(".active", match: :first)
all(".active").each(&:click)

Capybara will wait for the first matching element before trying to click on the rest.

Note: there is usually a better way to test things than iterating over matching elements, but that is beyond the scope of this post. Think carefully before using all.

Directly interacting with JavaScript

Bad:

execute_script("$('.active').focus()")

JavaScript expressions may be evaluated before the action is complete, and the wrong element or no element may be affected.

Good:

find(".active")
execute_script("$('.active').focus()")

Capybara will wait until a matching element is on the page, and then dispatch a JavaScript command which interacts with it.

Note: execute_script should only be used as a last resort when running into driver limitations or other issues which make it impossible to use other Capybara methods.

Checking a field’s value

Bad:

expect(find_field("Username").value).to eq("Joe")

Capybara will wait for the matching element and then immediately return its value. If the value changes from a page load or Ajax request, it will be too late.

Good:

expect(page).to have_field("Username", with: "Joe")

Capybara will wait for a matching element and then wait until its value matches, up to two seconds.

Checking an element’s attribute

Bad:

expect(find(".user")["data-name"]).to eq("Joe")

Capybara will wait for the matching element and then immediately return the requested attribute.

Good:

expect(page).to have_css(".user[data-name='Joe']")

Capybara will wait for the element to appear and have the correct attribute.

Looking for matching CSS

Bad:

it "doesn't have an active class name" do
  expect(has_active_class).to be_false
end

def has_active_class
  has_css?(".active")
end

Capybara will immediately return true if the element hasn’t been removed from the page yet, causing the test to fail. It will also wait two seconds before returning false, meaning the test will be slow when it passes.

Good:

it "doesn't have an active class name" do
  expect(page).not_to have_active_class
end

def have_active_class
  have_css(".active")
end

Capybara will wait up to two seconds for the element to disappear before failing, and will pass immediately when the element isn’t on the page as expected.

Summary

When interacting with the page, use action methods like click_on instead of finder methods like find whenever possible. Capybara knows the most about what you’re doing with those methods, and can more intelligently handle odd edge cases.

When verifying that elements are on the page as expected, use RSpec matchers like have_css instead of node methods like text whenever possible. Capybara can wait for the result you want with a matcher, but doesn’t know what text you’re expecting when you invoke methods on a node.

Episode #494 - September 9th, 2014

Posted 23 days back at Ruby5

This episode covers RSpec 3.1, unifying multiple analytics services with Rack::Tracker, new features in Rails 4.2, the Fearless Rails Deployment book, a spike for thoughts about Rack 2.0 with The_Metal and RubyConf Portugal.

Listen to this episode on Ruby5

Sponsored by Top Ruby Jobs

If you're looking for a top Ruby job or for top Ruby talent, then you should check out Top Ruby Jobs. Top Ruby Jobs is a website dedicated to the best jobs available in the Ruby community.
Top Ruby Jobs

RSpec 3.1

RSpec 3.1 has been released. Myron Marston wrote up another thorough blog post with the notable changes in this release.
RSpec 3.1

Rack::Tracker

Rack::Tracker is a Rack middleware that can be hooked up to multiple analytics services and exposes them in a unified fashion. It comes with built­-in support for Google Analytics and Facebook, but you can also add your own.
Rack::Tracker

New in Rails 4.2

Rashmi Yadav wrote about some smaller and lesser known features coming up in Rails 4.2.
New in Rails 4.2

Fearless Rails Deployment

Zach Campbell has released the final version of his "Fearless Rails Deployment" book, with everything you need to know about deploying Rails applications. The book is available for $39.99 with a money-back guarantee if you're not 100% satisfied.
Fearless Rails Deployment

the_metal

the_metal is a project from Aaron "Tenderlove" Patterson which is a spike for thoughts about Rack 2.0. Some of its design goals are listed on the README. So if you care about Rack, you should probably look into the_metal.
the_metal

RubyConf Portugal

RubyConf Portugal will be taking place October 13th and 14th in Braga. Speakers include Terrence Lee, Gau­tam Rege, Steve Klabnik, Katrina Owen and Erik Michaels­Ober, just to name a few. Use discount code Ruby5<3PT for a 10% discount.
RubyConf Portugal

Pluralizing I18n Translations in Your Rails Application

Posted 24 days back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home

Say we have some I18n text that tells users how many notifications they have. One option for dealing with a singular vs plural situation would look like this:

# config/locales/en.yml

en:
  single_notification: You have 1 notification
  other_notification_count: You have %{count} notifications
<%# app/views/notifications/index.html.erb %>

<% if current_user.notifications.count == 1 %>
  <%= t("single_notification") %>
<% else %>
  <%= t("other_notification_count", count: current_user.notifications.count) %>
<% end %>

Kind of ugly, right? Luckily, Rails provides a simple way to deal with pluralization in translations. Let’s try it out:

# config/locales/en.yml

en:
  notification:
    one: You have 1 notification
    other: You have %{count} notifications
<%# app/views/notifications/index.html.erb %>

<%= t("notification", count: current_user.notifications.count) %>

Same result, and no conditionals in our views. Awesome.

Make sure you are not using methods like “1.day.ago” when dealing with a Date column

Posted 24 days back at Ruby Fleebie

TL;DR: Read the title ;)

I spent too many hours debugging a feature spec in an application. Turns out I was simply not paying attention to what I was doing!

In a fixture file, I was setting a field the following way:

some_date: <%=1.day.ago.to_s :db%>

I didn’t pay attention to the fact that “some_date” was a Date and not a DateTime. This single line was responsible for some intermittent failures in my test suite.

The technical explanation
The problem is that methods like “days.ago” return ActiveSupport:TimeWithZone objects which contain, well, the time portion along with the current timezone info. Then there is the to_s(:db) part which convert the resulting DateTime in UTC for storage in the DB. It means that if I call “1.day.ago.to_s :db” on september 7th at 8:00 PM (local timezone, which is UTC -4 for me), the result will be “2014-09-07 00:00:00″ UTC time. And since I was storing this in a Date column, the time portion would simply gets discarded… so I ended up with a date of September 7th instead of the expected September 6th in the DB.

Solution
Of course this problem was very easy to fix once I understood what the problem was. I simple changed the fixture file so that it looks like:

some_date: <%=1.day.ago.to_date.to_s :db%>

This works as well:

some_date: <%=Date.yesterday.to_s :db%>

Hoping this will save others some painful debugging!

Aspen Ghost

Posted 26 days back at Mike Clark

Aspen Ghost

The great gray owl haunts the aspen forest, ever watchful of careless prey.

Episode #493 - September 5th, 2014

Posted 27 days back at Ruby5

Reading Rails talks TimeWithZone, descriptive_statistics, new gems in Rails 4.2, Paperdragon, and using Ruby's English operators all in this episode of the Ruby5 podcast!

Listen to this episode on Ruby5

Sponsored by New Relic

New Relic is _the_ all-in-one web performance analytics product. It lets you manage and monitor web application performance, from the browser down to the line of code. With Real User Monitoring, New Relic users can see browser response times by geographical location of the user, or by browser type.
This episode is sponsored by New Relic

Reading Rails - TimeWithZone

Baffled by time zones in Rails? This blog post will dive deep into the implementation and give you insight into what's going on in ActiveSupport.
Reading Rails - TimeWithZone

descriptive_statistics

Want to cool statistical methods on enumerables? The descriptive_statistics gem has just what you've been looking for.
descriptive_statistics

ActiveJob and GlobalID

Learn about some of the new goodies in Rails 4.2 with this blog post from Mikamai.
ActiveJob and GlobalID

Paperdragon

The Paperdragon gem puts a layer on top of Dragonfly to integrate more nicely with Ruby objects.
Paperdragon

Ruby's English Operators

Avdi explains Ruby's `and` and `or` operators in this free episode of RubyTapas.
Ruby's English Operators

An Introduction to WebGL

Posted 28 days back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home

On a recent project we had to do a lot of work with WebGL. The most difficult and frustrating thing about the project was the lack of good resources on working with WebGL, especially for non-trivial projects. What I did find was usually focused on just having code that puts pixels on the screen, but not how it worked or why. If you’re interested in learning WebGL, these posts will take you from zero to a working 3D application, with an emphasis on how the various concepts work and why.

What is WebGL?

WebGL is a thin wrapper around OpenGL ES 2.0 that is exposed through a JavaScript API. OpenGL is a low level library for drawing 2D graphics. This was a major misconception for me. I always thought that it was used to produce 3D. Instead, our job is to do the math to convert 3D coordinates into a 2D image. What OpenGL provides for us is the ability to push some data to the GPU, and execute specialized code that we write on the GPU rather than the CPU. This code is called a shader, and we write it in a language called GLSL.

To get started, we need to understand a few core concepts.

  • Clip Space: This will be the coordinate system we use in our final output. It is represented as a number between -1 and 1, regardless of the size of the canvas. This is how the GPU sees things.
  • Pixel Space: This is how we commonly think about graphics, where X is a number between 0 and the width of the canvas, and Y is a number between 0 and the height of the canvas.
  • Vertex Shader: This is the function which is responsible for converting our inputs into coordinates in clip space to draw on the screen.
  • Fragment Shader: This is the function which is responsible for determining the color of each pixel we told the GPU to draw in the vertex shader.

Boilerplate

We need to write a bit of boilerplate to get everything wired up to start drawing on the screen. The first thing we’ll need is a canvas tag.

<canvas width="600" height="600">
</canvas>

In our JavaScript code, we need to find the canvas, and use it to get an instance of WebGLRenderingContext. This is the object that contains all of the OpenGL methods we are going to use. The documentation for WebGL is generally quite lacking, but every method and constant maps to an equivalent method in the C API. The function glVertexAttrib1f in C would be gl.vertexAttrib1f in WebGL, assuming the variable gl is your WebGLRenderingContext. The constant GL_STATIC_DRAW in C would be gl.STATIC_DRAW in WebGL.

main = ->
  canvas = document.getElementByTagName("canvas")
  gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl")

The next thing we’re going to need is an instance of WebGLProgram. This is an object that will hold information about which shaders we’re using, and what data we’ve passed into it. As we initialize the program, we are going to want to compile and link our shaders. We’re going to need the source code of the shaders as a string. I prefer to write them in separate files, in order to get syntax highlighting and other file type specific helpers from my editor. In a Rails app, we can just spit out the files into the page server side.

module ApplicationHelper
  def shaders
    shaders = {}
    Dir.chdir(Rails.root.join("app", "assets", "shaders")) do
      Dir["**"].each do |path|
        shaders[path] = open(path).read
      end
    end
    shaders
  end
end
<% # app/assets/layouts/application.html.erb %>
<script>
  window.shaders = #{shaders.to_json.html_safe}
</script>

Compiling the Shaders

Now in our JavaScript, we can create a few helper functions to create a program, compile our shaders, and link them together. To compile the shaders, we need to do three things:

  1. Get the source of the shader
  2. Determine if it’s a vertex or fragment shader
  3. Call the appropriate methods on our WebGlRenderingContext

Once we’ve got both of our shaders, we can create the program, and link up the shaders. Let’s create an object that wraps up this process for us. You can find a gist here.

class WebGLCompiler
  constructor: (@gl, @shaders) ->

  createProgramWithShaders: (vertexShaderName, fragmentShaderName) ->
    vertexShader = @_createShader(vertexShaderName)
    fragmentShader = @_createShader(fragmentShaderName)
    @_createProgram(vertexShader, fragmentShader)

  _createShader: (shaderName) ->
    shaderSource = @shaders["#{shaderName}.glsl"]
    unless shaderSource
      throw "Unknown shader: #{shaderName}"

    @_compileShader(shaderSource, @_typeForShader(shaderName))

  _typeForShader: (name) ->
    if name.indexOf("vertex") != -1
      @gl.VERTEX_SHADER
    else if name.indexOf("fragment") != -1
      @gl.FRAGMENT_SHADER
    else
      throw "Unknown shader type for #{name}"

  _compileShader: (shaderSource, shaderType) ->
    shader = @gl.createShader(shaderType)
    @gl.shaderSource(shader, shaderSource)
    @gl.compileShader(shader)

    unless @gl.getShaderParameter(shader, @gl.COMPILE_STATUS)
      error = @gl.getShaderInfoLog(shader)
      console.error(error)
      throw "Could not compile shader. Error: #{error}"

    shader

  _createProgram: (vertexShader, fragmentShader) ->
    program = @gl.createProgram()
    @gl.attachShader(program, vertexShader)
    @gl.attachShader(program, fragmentShader)
    @gl.linkProgram(program)

    unless @gl.getProgramParameter(program, @gl.LINK_STATUS)
      error = @gl.getProgramInfoLog(program)
      console.error(error)
      throw "Program failed to link. Error: #{error}"

    program

We’re going to call createProgramWithShaders, giving it the name of the files to use for the vertex and fragment shaders. We assume that all vertex shaders are going to have the word “vertex” in the name, and that fragment shaders will have the word “fragment”. After compiling each shader, we attempt to compile it and check for errors. Finally, we attach the shaders to our program, and try to link the shaders. If all of this succeeded, the result will be an instance of WebGLProgram

main = ->
  canvas = document.getElementsByTagName("canvas")[0]
  gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl")
  compiler = new WebGLCompiler(gl, window.shaders)
  program = compiler.createProgramWithShaders("main_vertex", "main_fragment")

Now we can start writing actual code! We’ll start by writing the simplest possible vertex shader. It will do nothing but return the input unchanged.

attribute vec2 vertexCoord;

void main() {
  gl_Position = vec4(vertexCoord, 0.0, 1.0);
}

An attribute is the primary input to the vertex shader. We’re going to give it an array of values. OpenGL will loop over them, and call this function once per element. The function doesn’t actually return anything. Instead, we set a local variable called gl_Position. That variable expects a vec4, which means it has an x, y, z, and w, rather than a vec2, which just has x and y. z works like the z-index property in CSS. w is a value that every other axis will be divided by. We’ll set it to 1.0 for now, so nothing is affected.

Once the vertex shader has set enough points to draw a triangle, the fragment shader will be called once per pixel in that triangle. For now, we’ll just always return blue.

void main() {
  gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
}

Sending Data to the GPU

The last step is to wire up our program to our rendering context, pass in the data, and draw a triangle. First we’ll make sure our screen is in a consistent state.

gl.clearColor(1.0, 1.0, 1.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)

clearColor tells the GPU what color to use for pixels where we don’t draw anything. We’ve set it to white. Then, we tell it to reset the canvas so nothing has been drawn. The next then we need to do is give our program some data. In order to do this, we’ll need to create a buffer. A buffer is essentially an address in memory where we can shove an arbitrary number of bits.

gl.useProgram(program)
buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(
  gl.ARRAY_BUFFER
  new Float32Array([
    0.0, 0.8
    -0.8, -0.8
    0.8, -0.8
  ])
  gl.STATIC_DRAW
)

OpenGL is highly stateful. When we call bufferData, we never specify which buffer is being used. Instead, it works with the last buffer we passed to bindBuffer. gl.ARRAY_BUFFER tells OpenGL that the contents of this buffer are going to be used for an attribute. gl.STATIC_DRAW is a performance hint that says this data is going to be used often, but won’t change much.

Now that we’ve put the data in memory, we need to tell OpenGL which attribute to use it for, and how it should interpret that data. Right now it just sees it as a bunch of bits.

vertexCoord = gl.getAttribLocation(program, "vertexCoord")

gl.enableVertexAttribArray(vertexCoord)
gl.vertexAttribPointer(vertexCoord, 2, gl.FLOAT, false, 0, 0)

The first thing we need to do is get the location of the attribute in our program. This is going to be a numeric index, based on the order that we use it in our program. In this case, it’ll be 0. Next we call enableVertexAttribArray, which takes the location of an attribute, and tells us that we want to use the data that we’re going to populate it with. I’ll admit, I don’t know why you would have an attribute present in your application, but not enable it. Finally, vertexAttribPointer will populate the attribute with the currently bound buffer, and tell it how to interpret the data. This is what each of the arguments mean:

gl.vertexAttribPointer(
  # Which attribute to use
  vertexCoord

  # The number of floats to use for each element. Since it's a vec2, every
  # 2 floats is a single vector.
  2

  # The type to read the data as
  gl.FLOAT

  # Whether the data should be normalized, or used as is
  false

  # The number of floats to skip in between loops
  0

  # The index to start from
  0
)

Finally, we need to tell it that we’ve finished giving it all of the data it needs, and we’re ready to draw something to the screen.

gl.drawArrays(gl.TRIANGLES, 0, 3)

drawArrays means that we want to loop through the attribute data, in the order that it was given. The first argument is the method we should use for drawing. TRIANGLES means that it should use every three points as a surface. It would take 6 points to draw two triangles. There are other options, such as TRIANGLE_STRIP, which would only take 4 points to draw 2 triangles. There’s also POINTS or LINES, which completely change how a single triangle is drawn. The second argument is which element in the array we should start from. The final argument is the number of points we’re going to draw. The end result, is a simple triangle. All of the code used for this sample is available here.

blue triangle

Go interfaces communicate intent

Posted 29 days back at techno weenie - Home

Interfaces are one of my favorite features of Go. When used properly in arguments, they tell you what a function is going to do with your object.

// from io
func Copy(dst Writer, src Reader) (written int64, err error)

Right away, you know Copy() is going to call dst.Write() and src.Read().

Interfaces in return types tell you what you can and should do with the object.

// from os/exec
func (c *Cmd) StdoutPipe() (io.ReadCloser, error) {

It's unclear what type of object StdoutPipe() is returning, but I do know that I can read it. Since it also implements io.Closer, I know that I should probably close it somewhere.

This brings up a good rule of thumb when designing Go APIs. Prefer an io.Reader over an io.ReadCloser for arguments. Let the calling code handle its own resource cleanup. Simple enough. So what breaks this rule? Oh, my dumb passthrough package.

Here's the intended way to use it:

func main() {
  fakeResWriter := pseudoCodeForExample()
  res, _ := http.Get("SOMETHING")
  passthrough.Pass(res, fakeResWriter, 200)
}

However, on a first glance without any knowledge of how the passthrough package works, you may be inclined to close the body manually.

func main() {
  fakeResWriter := pseudoCodeForExample()
  res, _ := http.Get("SOMETHING")
  // hopefully you're not ignoring this possible error :)

  // close body manually
  defer res.Body.Close()

  // passthrough also closes it???
  passthrough.Pass(res, fakeResWriter, 200)
}

Now, you're closing the Body twice. That's not great.

Resource management is very important, so we commonly review code to ensure everything is handled properly. Helper functions that try to do too much like passthrough have caused us enough issues that I've rethought how I design Go packages. Don't get in the way of idiomatic Go code.

Back to Basics: SOLID

Posted 30 days back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home

SOLID is an acronym created by Bob Martin and Michael Feathers that refers to five fundamental principles that help engineers write maintainable code. We like to think of these principles as the foundational elements we use when evaluating the health of my codebase and architectural approach. The principles that make up the acronym are as follows:

Let’s take a closer look at each of these principles with some examples.

Single Responsibility Principle

The Single Responsibility Principle is the most abstract of the bunch. It helps keep classes and methods small and maintainable. In addition to keeping classes small and focused it also makes them easier to understand.

While we all agree that focusing on a single responsibility is important, it’s difficult to determine what a class’s responsibility is. Generally it is said that anything that gives a class a reason to change can be viewed as a responsibility. By change I am talking about structural changes to the class itself (as in modifying the text in the class’s file) not the object’s in-memory state. Let’s look at an example of some code that isn’t following the principle:

class DealProcessor
  def initialize(deals)
    @deals = deals
  end

  def process
    @deals.each do |deal|
      Commission.create(deal: deal, amount: calculate_commission)
      mark_deal_processed
    end
  end

  private

  def mark_deal_processed
    @deal.processed = true
    @deal.save!
  end

  def calculate_commission
    @deal.dollar_amount * 0.05
  end
end

In the above class we have a single command interface that processes commission payments for deals. At first glance the class seems simple enough, but let’s look at reasons we might want to change this class. Any change in how we calculate commissions would require a change to this class. We could introduce new commission rules or strategies that would cause our calculate_commission method to change. For instance, we might want vary the percentage based on deal amount. Any change in the steps required to mark a deal as processed in the mark_deal_processed method would result in a change in the file as well. An example of this might be adding support for sending an email summary of a specific person’s commissions after marking a deal processed. The fact that we can identify multiple reasons to change signals a violation of the Single Responsibility Principle.

We can do a quick refactor and get our code in compliance with the Single Responsibility Principle. Let’s take a look:

class DealProcessor
  def initialize(deals)
    @deals = deals
  end

  def process
    @deals.each do |deal|
      mark_deal_processed
      CommissionCalculator.new.create_commission(deal)
    end
  end

  private

  def mark_deal_processed
    @deal.processed = true
    @deal.save!
  end
end

class CommissionCalculator
  def create_commission(deal)
    Commission.create(deal: deal, amount: deal.dollar_amount * 0.05)
  end
end

We now have two smaller classes that handle the two specific tasks. We have our processor that is responsible for processing and our calculator that computes the amount and creates any data associated with our new commission.

Open/Closed Principle

The Open/Closed Principle states that classes or methods should be open for extension, but closed for modification. This tells us we should strive for modular designs that make it possible for us to change the behavior of the system without making modifications to the classes themselves. This is generally achieved through the use of patterns such as the strategy pattern. Let’s look at an example of some code that is violating the Open/Closed Principle:

class UsageFileParser
  def initialize(client, usage_file)
    @client = client
    @usage_file = usage_file
  end

  def parse
    case @client.usage_file_format
      when :xml
        parse_xml
      when :csv
        parse_csv
    end

    @client.last_parse = Time.now
    @client.save!
  end

  private

  def parse_xml
    # parse xml
  end

  def parse_csv
    # parse csv
  end
end

In the above example we can see that we’ll have to modify our file parser anytime we add a client that reports usage information to us in a different file format. This violates the Open/Closed Principle. Let’s take a look at how we might modify this code to make it open to extension:

class UsageFileParser
  def initialize(client, parser)
    @client = client
    @parser = parser
  end

  def parse(usage_file)
    parser.parse(usage_file)
    @client.last_parse = Time.now
    @client.save!
  end
end

class XmlParser
  def parse(usage_file)
    # parse xml
  end
end

class CsvParser
  def parse(usage_file)
    # parse csv
  end
end

With this refactor we’ve made it possible to add new parsers without changing any code. Any additional behavior will only require the addition of a new handler. This makes our UsageFileParser reusable and in many cases will keep us in compliance with the Single Responsibility Principle as well by encouraging us to create smaller more focused classes.

Liskov Substitution Principle

Liskov’s principle tends to be the most difficult to understand. The principle states that you should be able to replace any instances of a parent class with an instance of one of it’s children without creating any unexpected or incorrect behaviors.

Let’s look at a example of a Liskov violation. Let’s look at a simple example of a Liskov violation. We’ll start with the classic example of the relationship between a rectangle and a square.. Let’s take a look:

class Rectangle
  def set_height(height)
    @height = height
  end

  def set_width(width)
    @width = width
  end
end

class Square < Rectangle
  def set_height(height)
    super(height)
    @width = height
  end

  def set_width(width)
    super(width)
    @height = width
  end
end

For our Square class to make sense we need to modify both height and width when we call either set_height or set_width. This is the classic example of a Liskov violation. The modification of the other instance method is an unexpected consequence. If we were taking advantage of polymorphism and iterating over a collection of Rectangle objects one of which happened to be a Square, calling either method will result in a surprise. An engineer writing code with an instance of the Rectangle class in mind would never expect calling set_height to modify the width of the object.

Another common instance of a Liskov violation is raising an exception for an overridden method in a child class. It’s also not uncommon to see methods overridden with modified method signatures causing branching on type in classes that depend on objects of the parent’s type. All of these either lead to unstable code or unnecessary and ugly branching.

Interface Segregation Principle

This principle is less relevant in dynamic languages. Since duck typing languages don’t require that types be specified in our code this principle can’t be violated.

That doesn’t mean we shouldn’t take a look at a potential violation in case we’re working with another language. The principle states that a client should not be forced to depend on methods that it does not use.

Let’s take a closer look at what this means in Swift. In Swift we can use protocols to define types that will require concrete classes to conform to the structures they outline. This makes it possible to create classes and methods that require only the minimum API.

In this example we’ll create two classes Test and User. We’ll also reference a Question class I will omit since it will not be necessary for the sake of this example. Our user will take tests. And tests can be scored and taken. Let’s take a look:

class Test {
  leg questions: [Question]
  init(testQuestions: [Question]) {
    questions = testQuestions
  }

  func answerQuestion(questionNumber: Int, choice: Int) {
    questions[questionNumber].answer(choice)
  }

  func gradeQuestion(questionNumber: Int, correct: Bool) {
    question[questionNumber].grade(correct)
  }
}

class User {
  func takeTest(test: Test) {
    for question in test.questions {
      test.answerQuestion(question.number, arc4random(4))
    }
  }
}

Our User would not get a very good grade because they’re randomly choosing test answers, but we also have a violation of the Interface Segregation Principle. Our takeTest requires we provide an argument of type Test. The Test type has two methods one of which our takeTest method depends, but the takeTest method does not care about gradeQuestion method. Let’s take advantage of Swift’s protocols to fix this and get back on the right side of our Interface Segregation Principle.

protocol QuestionAnswering {
  var questions: [Question] { get }
  func answerQuestion(questionNumber: Int, choice: Int)
}

class Test: QuestionAnswering {
  let questions: [Question]
  init(testQuestions: [Question]) {
    self.questions = testQuestions
  }

  func answerQuestion(questionNumber: Int, choice: Int) {
    questions[questionNumber].answer(choice)
  }

  func gradeQuestion(questionNumber: Int, correct: Bool) {
    question[questionNumber].grade(correct)
  }
}

class User {
  func takeTest(test: QuestionAnswering) {
    for question in test.questions {
      test.answerQuestion(question.number, arc4random(4))
    }
  }
}

Our takeTest method now requires a QuestionAnswering type. This is an improvement because we can now use this same logic for any type that conforms to this protocol. Perhaps down the road we would like to add a Quiz type, or even different types of tests. With our new implementation we could easily reuse this code.

Dependency Inversion Principle

The Dependency Inversion Principle has to do with high level (think business logic) objects not depending on low-level (think database querying and IO) implementation details. This can be achieved with duck typing and the Dependency Inversion Principle. Often this pattern is used to achieve the open closed principle we discussed above. In fact, we can even reuse that same example as a demonstration of this principle. Let’s take a look:

class UsageFileParser
  def initialize(client, parser)
    @client = client
    @parser = parser
  end

  def parse(usage_file)
    parser.parse(usage_file)
    @client.last_parse = Time.now
    @client.save!
  end
end

class XmlParser
  def parse(usage_file)
    # parse xml
  end
end

class CsvParser
  def parse(usage_file)
    # parse csv
  end
end

As you can see, our high level object, the file parser, does not depend directly on an implementation of a lower level object, XML and CSV parsers. The only thing that is required for an object to be used by our high level class is that it respond to the parse message. This decouples our high level functionality from low level implementation details and allows us to easily modify what those low level implementation details are. Having to write a separate usage file parser per file type would require lots of unnecessary duplication.

What’s next?

If you found this useful, learn more by watching the SOLID videos on The Weekly Iteration:

Considering dropping support for Rails 1.0-2.2

Posted 30 days back at Phusion Corporate Blog

We work very hard to maintain backward compatibility in Phusion Passenger. Even the latest version still supports Ruby 1.8.5 and Rails 1.0. We’ve finally reached a point where we believe dropping support for Rails 1.0-2.2 will benefit the quality of our codebase. Is there anybody here who would object to us dropping support for Rails 1.0-2.2? If so, please let us know by posting a comment. Rails 2.3 will still be supported.

The post Considering dropping support for Rails 1.0-2.2 appeared first on Phusion Corporate Blog.