Date Scope Library

The include_date_scopes gem is live and ready for consumption.  I ripped this out of my company’s codebase since its something that lots of people could use.  My original inspiration for it was when I found myself trying to correct four different variations of the between date scope in our codebase.  I moved all of our date scopes into a single file and included them wherever we needed date related scopes.

Since then it has evolved quite a bit.  You can now do date scopes on columns other than `created_at` and you can prepend a name to the date scopes.

Moving this off to its own gem involved several challenges including how to setup a sample ActiveModel to use in specs and just how to setup the structure of the files and folders in a gem.  I’ll detail each of these hurdles in separate posts.

When a day is not a day

It may sound weird, but days are not all made the same.  No, I’m not talking qualitatively, but in quantitative terms.  Whenever we have a daylight savings time switch, days can be either 23 or 25 hours long.

A quick example of this in Rails using Timecop:

Timecop.freeze Time.local 2014,3,9,9 # Go to 9AM 3/9/14 just after DST
24.hours.ago # => 8AM the previous day
1.day.ago # => 9AM the previous day

When you ask Rails for 1.day.ago, it is smart enough to know that you really mean the same time yesterday.  If you try to be smart yourself, Rails won’t be there to help you.

If you are experiencing weird results in your application around DST, try doing a code search for 24.hours and see what turns up.  You may be making the false assumption that a day is always 24 hours long.

How to build a portfolio

I seem to answer this question a lot, so I’m just going to write it all down. I’ve been trying to find junior engineers for my company and one of the things I like to see is a small portfolio on GitHub. This begs the question: how do a I make a portfolio? I’m not expecting much, just a few quick projects so I can see your style. Hell, it doesn’t even have to be Ruby. If you want to do something in AppleScript to automate your computer, go for it!

Your first Ruby script

Your first Ruby project, whether you’re an advanced programmer or a beginner shouldn’t necessarily be in Rails.  Just do something simple.  A good example of this is playing around with RSS stuff.  Ruby has RSS parsing baked right in to make this really straightforward.  You could do something that takes in your favorite news feed and outputs all of the titles or greps out all of the links.  The possibilities are endless.

You could also do something like parsing out a document to find the most common words.

While these two examples have no practical value, they will serve as a good exercise so you can try out Ruby without worrying about Gems or outside code.

Tutorials

If you’ve ever learned a new language or framework, you’ve probably started with a tutorial.  One of the best Rails tutorials has to be the one by Mike Hartl.  It takes you step-by-step through building a Rails application from the ground up and gives you a good basis for branching out from there.  Don’t be shy about putting this tutorial code in your portfolio.  We’ve all had to start somewhere and some of the best out there are those willing to show a little humility and talk about where they started.

Dotfiles

Assuming you’re programming on Unix, you likely have a number of configuration files in your home folder.  Put these in GitHub.  I’ve tried doing this a couple different ways and the easiest way I found was to fork someone else’s dotfiles repo.  I personally took this one:

https://github.com/r00k/dotfiles

Putting your dotfiles up for everyone to see is a really good way to share simple hacks that you’ve done to make your life easier.  I personally love editing my dotfiles and adding in new shortcuts.

Non-code stuff

I was recently chatting with a prospective applicant and he mentioned that he’s been busy storyboarding out an app he wants to do for a game so he doesn’t have any code yet.  I was kind of amazed that he hadn’t yet put this up on GitHub.  Git makes for a great word editor since you can see your changes and allow for quick collaboration.  This may not be part of a formal portfolio, but it really helps to see how someone thinks through a problem.

Ensuring a return

This one tripped me up for a few minutes today so I thought I’d write something up on it.

Given this code, what do you think it would return:

def foo
  "Hello, world."
ensure
  "Goodbye, cruel world."
end

puts foo

Would it be greeting me or say goodbye? It would go with “Hello”. This is because the ensure block doesn’t normally return anything. It is just used after the method returns to wrap up anything that definitely needs to happen.

How would I force it to return something in ensure? You would need to use ‘return’ explicitly like so:

def foo
  "Hello, world."
ensure
  return "Goodbye, cruel world."
end

Now the method will say goodbye like you expect.

DRY Scopes

One of the things you hear over and over again in the Ruby community is DRY,DRY,DRY (don’t-repeat-yourself, for the uninitiated). A couple months ago I noticed that I was making the same changes over and over again in a weird part of the codebase. It happened to be in our scopes. We had a smattering of different scopes dealing with dates and we had several different variations on the simple :between scope. We also had different selections of the dates scopes in different models. Some had the :after scope and some didn’t, etc.

So what’s a good programmer to do? DRY it up!

I first moved all of the scopes into a module and just included that, and lo-and-behold the specs still passed. Problem was though that I couldn’t do this for all of the models since some relied on a column other than created_at. So instead of just creating a separate module, I just made the current one a little more dynamic.

Here are the first two methods that start the magic:

def include_date_scopes
  include_date_scopes_for :created_at
end

def include_date_scopes_for(column, prepend_name = false)
  return unless self.table_exists?
  if self.columns_hash[column.to_s].type == :datetime
    define_timestamp_scopes_for column, prepend_name
  elsif self.columns_hash[column.to_s].type == :date
    define_date_scopes_for column, prepend_name
  end
end

The first method is just there to make it easy to include the scopes for a default created_at column. The second one looks at the table to make sure it exists (it gets tripped up initially if the database isn’t loaded). Then it looks to see if the column is either date or datetime/timestamp type to define the scopes appropriately.

def define_timestamp_scopes_for(column_name, prepend_name = false)
  prefix = prepend_name ? "#{column_name}_" : ""

  define_singleton_method :"#{prefix}before" do |time| 
    where{__send__(column_name).lt (time.is_a?(Date) ? time.to_time : time)}
  end
end

The next method actually defines the scopes. In this case they are more method than scope, but Rails will still treat them the same. This is just one of them, but you can use this as a template. And for the curious, that is not normal scope style. I am using the Squeel gem to make it much easier to read.

Now all I need to do is delete the scopes from a model and add this:

include_date_scopes

In my code I have it defining 16 different timestamp scopes and 12 different date scopes. You can really get creative with it too. If you have other common queries you run in different models, try and group them together.

Negative Security Testing

Part of my day job is to help with software security at my company. Part of this involves reviewing our controllers to make sure that people who shouldn’t be able to access them can’t get in. I’ve created both Cucumber specs and RSpec examples to do this. So far I prefer the RSpec method since it allows me to do a little more introspection.

A little bit of a background on our setup. Like most Rails shops, we use CanCan for our authorization and Devise for the authentication. With CanCan we define roles for the users such that the roles are granted access to certain areas of the system.

When I first wrote up the blacklist test in Cucumber, I had to manually specify which roles weren’t allowed and which actions to test in each controller. Obviously, this would only do real testing up until a new role or action was added. When I switched to RSpec I was able to just give a whitelist of what roles are allowed and then get the available actions through introspection. See my code below:

class SecurityHelper
  def self.action_names(controller, opts = {})
    actions = controller.class.public_instance_methods(false).reject{|a| a.to_s.starts_with? '_'}
    actions - Array(opts[:except])
  end
 
  def self.create_insecure_user(opts = {})
    roles = User.available_roles - Array(opts[:allowed_roles]).map(&:to_s)
    FactoryGirl.create :user, roles: roles
  end
end
 
shared_examples "a secure controller" do |opts = {}|
  let(:actions) { SecurityHelper.action_names(controller, opts) }
  let(:insecure_user) { SecurityHelper.create_insecure_user(opts) }
 
  context "without authenticated user" do
    it "doesn't allow access without signing in" do
      sign_out :user
      actions.each do |action|
        get action.to_sym, id: ''
        response.should redirect_to new_user_session_url
      end
    end
 
    it "denies access to non-allowed roles" do
      actions.each do |action|
        sign_in insecure_user
        get action.to_sym, id: ''
        response.should redirect_to '/'
      end
    end
  end
end
 
 
#############################
## SUPPORT CONTROLLER HELP
#############################
 
shared_examples "a secure support controller" do |controller, opts = {}|
  include_examples "a secure controller", {allowed_roles: [:support]}.merge(opts)
end

I included an example of how to set it up for what I call the support controller.  All you need to do is give it the roles that are allowed and then it creates a user with every role except those.  Then in your controller spec, add this line:

it_behaves_like "a secure support controller"

Voilà! Now you know if anyone ever breaks the security in your controller.

As for the positive side of making sure that a good user can still get in, I just rely on the regular specs since any good test suite will hit every action at least once.

Time Traveling Servers

Ever wonder how you could start your server in a different time, like say 2 days ago.  The QA team was struggling with this for a while until my coworker came up with this ingenious way of using Timecop. In your environment.rb file in rails, add in this:

days = ENV['TIMECOP_DAYS'].nil? ? 0 : ENV['TIMECOP_DAYS'].to_i
minutes = ENV['TIMECOP_MINUTES'].nil? ? 0 : ENV['TIMECOP_MINUTES'].to_i
Timecop.travel Time.now + days.days
Timecop.travel Time.now + minutes.minutes

With that in your environment file, you can start rails server like this:

TIMECOP_DAYS=-1 rails server

Now for all intents and purposes it looks like you are exactly 24 hours ago.