Friday, July 31, 2015

String conditionals on ActiveRecord callbacks

I’ve got a model for push notifications that uses Parse.com, and it sends the data to Parse on a callback:

before_create :send_to_parse

The problem is, I have a whole bunch of specs that create push notifications, and I don’t want any sent to Parse during testing. Now, I don’t have the Parse credentials accessible within the test environment, but that throws an error when it tries to send to Parse. So, what I really want is to just send to Parse when we’re not testing.

Turns out this is dead simple, albeit a little strange at first:

before_create :send_to_parse, unless: "Rails.env.test?"

I’ve used conditionals with symbolized method names before, but the string bit threw me off. However, it’s simple valid Ruby code, you can put it in a string and it will be evaluated as such.

This is straight from the docs, and I thought it was super cool.

Wednesday, July 22, 2015

Check if array contains any element of another array

I wanted to check if there were common elements in two separate arrays. Seems simple enough, but I was trying to find a way to do this without looping over both arrays in some kind each jungle:

wizard_of_oz_characters = ['tin man', 'scarecrow', 'lion']
batman_villains = ['scarecrow', 'joker', 'bane']
wizard_of_oz_characters.each do |character|
  batman_villains.each do |villain|
    return false if character == villain
  end
end

I was able to pare it down to something simpler:

wizard_of_oz_characters.any? {|character| batman_villains.include?(character) }

…but at it’s core, it’s still looping through and checking each record in sequence. Not what I was after.

Then I found a StackOverflow answer which had exactly what I wanted:

(wizard_of_oz_characters & batman_villains).empty?

This returns the intersection of the two arrays (['scarecrow']), then asks if it is empty. Perfect! And the syntax is kinda fun too.

But what is it actually doing? One of the comments on the SO answer said this is faster because it’s linear rather than quadratic, and although it has been a few years since high school Geometry, I still know quadratic is much more intensive than linear. However, I wanted to find out more.

Turns out, the & operator is treating the Arrays like Sets, an entirely different data type in Ruby. From the Ruby docs themselves, a Set is a “hybrid of Array’s intuitive inter-operation facilities and Hash’s fast lookup.”

So you get the syntax of an Array with the speed of a Hash. And you get this fun and terse syntax. I’m on board with that.

Thursday, July 16, 2015

Quick and easy HTTP headers with HTTParty

UPDATE: I changed the gem. It now follows redirects and maintains the HEAD request across all redirects.

I have a method that checks URLs submitted by the user to ensure they are valid. I don’t want to scrape the page to make sure any specific text is on there because the URLs can go almost anywhere, so I basically only care if the URL returns an HTTP 200 status.

I’m using the excellent HTTParty Ruby gem to make my external requests, and it offers a handy .head method that only returns the website headers for the request:

HTTPart.head(external_url)

However, I found out that if the requested site redirects, then subsequent requests get changed to GET, which isn’t ideal since the GET request will download the entire page’s content rather than just the headers, and is understandably slower.

Thankfully, there are a couple options that can be passed to the .head method which takes care of this:



HTTParty.head(external_url_with_redirects,
              maintain_method_across_redirects: true,
              follow_redirects: true)

Now, I get just the headers, and since all I care about is whether it was a successful request, I’m able to call .success? in my method and use the return value.

There are a few external HTTP gems out there - Net:HTTP (of which HTTParty is a wrapper), RestClient, etc - but HTTParty is my favorite so far, and easiest to work with. Check it out if you haven’t already.

Tuesday, July 7, 2015

ActiveRecord querying on Postgres array column

On a Rails project I’m working on, we have a model that has a db column of array datatype. This is one of those Postgres special datatypes that isn’t present on many other SQL databases, and there’s even some contention about whether it should be an option at all.

At any rate, this is deeply ingrained in our project currently, so I don’t want to go reinventing the wheel at this point and convert this particular column to a separate model and create another has_many/belongs_to relationship. All I want is a way to find all of the objects in this model that have an empty array in that column.

Turns out there’s no built in ActiveRecord finder for this, but the raw SQL isn’t too terrible:

Model.where("NULL = ALL(array_column_name)")

Hat Tip: https://coderwall.com/p/sud9ja/rails-4-the-postgresql-array-data-type

Wednesday, July 1, 2015

Unique objects from a three way join

I have a model that is essentially a three-way join of three models. Let’s call this model Widget. It has a user_id, account_id, and place_id. I want to make sure each of those widget objects is unique in some way - in other words, I don’t want any widget object with the same user_id, account_id, and place_id.

I should probably validate for uniqueness, but only within the confines of what is in the other columns. Here’s where uniqueness scope comes in handy:

validates_uniqueness_of :user_id, scope: [:account_id, :place_id]

Now, if we try to create a Widget with the same user_id, account_id, AND place_id as one already in the system, ActiveRecord will yell at us. But if any of those values are different from what’s already in the db, we’re good to go.

If this was going to be user-facing, I’d probably also want to add a custom error message to this, otherwise it would say something like ‘user_id has been taken’, which isn’t ideal. However, these Widget objects will be created in the background, not explicitly by users, so we shouldn’t ever see these error messages.

Now let’s say we want to control for what might already be in the db. Suppose that we didn’t find out this was an issue until a bunch of people already created duplicate Widgets. Sure, we could create a rake task to comb the db and delete the duplicates, and that would probably be a good idea at some point if we truly don’t need them. But what if for some reason we wanted duplicates to save to the db, but on certain pages only wanted to display unique Widget objects?

Running Widget.all.uniq doesn’t do anything for us, because each widget object has a unique id/primary key. So, assuming we don’t need the primary key on the page we’re displaying these widgets (and since this is essentially just a three-way join with no other attributes, I think that’s a fair assumption), we can pull all other attributes besides the primary id for all the widget objects using ActiveRecord’s select:

Widget.select(:user_id, :account_id, :place_id)

This will give us all of the widget objects in the system, but with nil values for any attributes we didn’t specify - namely, the id field. Now, since we don’t have the unique id to deal with, we can easily filter duplicates using uniq:

Widget.select(:user_id, :account_id, :place_id).uniq

Just to make it cleaner, I would probably make that a scope on Widget so I could give it a name and have it make more sense for other coders on the project.

class Widget < ActiveRecord::Base
  scope :unique, -> { select(:user_id, :account_id, :place_id).uniq }
  # other stuff in Widget class
end

Widget.unique

Bingo bango bongo.

Wednesday, June 24, 2015

Take control of your rspec test suite, Part 1

Let me tell you a little story about how I took our test suite from 25 minutes run time to just under 3 minutes while still testing all the same functionality.

A large application can generate a very unwieldy test suite. On one project I worked on, the test suite took 45 minutes to run on our CI, and when I tried to run the whole thing locally, I stopped it at the 1 1/2 hour mark when it looked like it still had a ways to go.

I can live with a suite like that for awhile, because I just run the tests that are specific to the work I’m doing. However, there are times when I’m working on something that could potentially affect several different places in the app, and I may not know exactly where those places are, so I have to run the whole suite. Waiting 45 minutes to be able to finish my feature is NOT cool, especially since that will likely have to happened a few times to ensure all tests are fixed.

On that project, I was limited with what I could do on the test suite due to budget constraints. However, on another project I worked on, our test suite took 25 minutes to run, but the project manager saw the financial benefit of trimming this number down and so let me spend some time fixing our tests.

I’ll be writing a few different posts on this topic. Step 1 is the easiest part: gathering data. Often, only a handful of tests take the longest to run, so we want to find out which of our tests are “losers” and try to refactor them into “winners.”

Thankfully, rspec gives us a command to do just that:

rspec --profile
# or rspec -p

This will run the entire suite, then list out the 10 examples that took the longest to run. If you want more than 10, you can specify the number you want as an argument, i.e. rspec -p 25.

You can also use this when running on certain test groups. So, if you wanted to find out the 17 slowest feature specs:

rspec -p 17 spec/features

Now you know the worst offenders. Stay tuned for part 2 where we delve into how to fix them up.

Thursday, June 18, 2015

Hack your Rspec setup using tags

I ran into an issue in rspec the other day where I had a before block to setup about 10 different tests, but I needed to write one more that was similar enough that it belonged in the same context but it required slightly different setup so the before block I already defined.

What to do? I could break out the existing tests into a nested context block and use the setup there, then do another nexted context block for my new test, but I really didn’t want to do that because all these tests should belong in the same context. I just wanted to skip the before block on one specific test.

Rspec tags to the rescue!

In this case, my new test was a JS test and all others were not (which is partly why the setup was different. So, the test was already tagged js: true, and in mybefore` block, I modified it like so:

before(:each) do |test|
  # Run this only on tests NOT tagged with js: true
  if test.example.metadata[:js] == nil
    # Existing setup code
  end
end

Works like a charm, and I still have all my related tests within the same context like I wanted.

Hat Tip: http://stackoverflow.com/questions/27864080/how-can-i-skip-some-setup-for-specific-rspec-tags