incubate!(bang) has officially started back up - check incubatebang.com for details

isotope|eleven


About_adam_gamble

Adam Gamble

developer

Adam has been a professional developer for almost 10 years. He started web development with PHP, and has since moved on to Ruby on Rails and hasn't looked back. He has worked on projects for both the educational and financial fields.

Blog Posts

I've recently tried to start writing my methods in such a way that a user can pass in just about anything they want and get the expected results. I'll go through the normal iterations, and at the end we'll have a method that works how just about anyone could expect.

Consider a project where we need to add tags to an asset model. These models have a join table in between them. Here is our schema:

create_table "asset_join_tags", :force => true do |t|
  t.integer  "asset_id"
  t.integer  "tag_id"
  t.datetime "created_at", :null => false
  t.datetime "updated_at", :null => false
end

create_table "assets", :force => true do |t|
  t.string   "name"
  t.datetime "created_at", :null => false
  t.datetime "updated_at", :null => false
end

create_table "tags", :force => true do |t|
  t.string   "name"
  t.datetime "created_at", :null => false
  t.datetime "updated_at", :null => false
end

So we need an method on the asset model for people to add tags, and not have to know the underlying structure. Lets create that.

class Asset < ActiveRecord::Base
  has_many :asset_join_tags
  has_many :tags, :through => :asset_join_tags

  def add_tag tag
    asset_join_tags.create(:tag_id => tag.id)
  end
end

Excellent, so now from the console we can call @asset.add_tag(Tag.first) and it will tag our asset with the first tag. But wait a minute. What if we're in a controller and get a tag as a param to add? We'd have to load the model first and pass it to this method. Thats no good at all. Lets fix it.

def add_tag tag
  tag = tag.id if tag.is_a?(Tag)
  asset_join_tags.create(:tag_id => tag)
end

Excellent, now our method will accept an id or an actual tag and you get expected results either way. But what if the form submits an array of tag ids? Lets change our method to handle this behavior.

class Asset < ActiveRecord::Base
  has_many :asset_join_tags
  has_many :tags, :through => :asset_join_tags

  def add_tags tags
    tags = [tags] unless tags.is_a?(Array)
    tags.each do |tag|
      tag = tag.id if tag.is_a?(Tag)
      asset_join_tags.create(:tag_id => tag)
    end
  end
  alias :add_tag :add_tags
end

Perfect, now we have one method that can accept 4 different combinations of things and produce the same results. a tag id, a tag, an array of tags, or an array of tag ids.

Consider writing your methods this way so we can follow the principle of least surprise.

About_adam_gamble

So this just happened

by: Adam Gamble

April 27th, 2012 03:47

I wonder if this sent an airbrake error, which created Airbrake inception.

So I needed to test a redirect that only happened for IOS devices. I had no idea how to accomplish this with a specific cucumber scenario. So I did what any good developer would do. I figured it out.

Here is how:

make a new file features/support/custom_user_agents.rb

Capybara.register_driver :iphone do |app|

  require 'selenium/webdriver'
  profile = Selenium::WebDriver::Firefox::Profile.new

  #Change the line below to change the user agent
  profile['general.useragent.override'] = "Mozilla/5.0 (iPhone; U; CPU
  like Mac OS X; en)       AppleWebKit/420+ (KHTML, like Gecko) Version/3.0
  Mobile/1A535b Safari/419.3"

  Capybara::Selenium::Driver.new(app, :profile => profile)
end

Capybara.use_default_driver

then tag your scenario with @iphone and it will automatically use the new selenium driver we just created! Other scenarios will still use the default driver. Hope this helps save someone some time.

Git Bisect is awesome

So I was attempting to debug a weird bug that cropped up in a release branch that hadn't existed before all the original feature branches were merged. There were quite a few feature branches and changes so I was scratching my head trying to figure out what broke it.

Enter Scene: Ben Holley (Isotope11 Developer)

"You should try git bisect! I've never done it but I heard it will help you here"

Turns out its awesome. It will step between a known good commit/branch and a known bad commit/branch and allow you to test your code in between and mark them as good or bad. Eventually, and fairly quickly you will narrow down what commit caused your particular problem. Here is how:

$ git bisect start
$ git bisect good some_good_branch
$ git bisect bad master
> Bisecting: 60 revisions left to test after this (roughly 6 steps)

You will now be in a "bisect" branch that is somewhere between the known good point and the known bad point. At this point you run whatever test you need to and verify if your code works at this point or not.

Lets assume it does not

$ git bisect bad
> Bisecting: 29 revisions left to test after this (roughly 5 steps)

Git has stepped us to a different point in the tree to test here. Notice that it says that there are only 29 revisions left to test. It doesn't just step back one by one it figured out what branches definitely can't have caused the problem based on the tree. So you don't have to step through every single commit. In this case git estimates I'll have roughly 5 steps left.

Basically you will continue through the git bisect good or bad routine until eventually git returns this

> db791e5031d72dea6a6cfb761d349c53277c1a4f is the first bad commit

You now know exactly which commit broke your code.

To return to the branch you were in when you started the git bisect type:

$ git bisect reset

Now use git show to figure out what was in that commit that was so horrible and you're done!

$ git show db791e5031d72dea6a6cfb761d349c53277c1a4f

This saved me hours of debugging time, hopefully it will help someone out there too

CLI Interface to Xrono

Isotope11 has just released a command line interface to the Xrono time tracking app. It allows you to manage tickets and enter time straight from the command line. Its still in the very early stages, but I wanted to get a post out about it early.

You will most likely want to put this in your global gemset if you're using rvm.

rvm use 1.9.2@global
gem install xcli

You can repeat this step for all your rubies that you use.

xcli uses the .xronorc file in your home directory to figure out what xrono to use and what your credentials are. Here is a sample.

---
  url: http://www.xrono.org
  email: dev@xrono.org
  password: '123456'

Once that is in place, you need to specify a git repo url per project in xrono. Xcli currently checks origin's git repo url to relate to a project in xrono. Type git remote -v and copy the git url next to origin. Paste this into the git repo url field on the project edit page in xrono and save.

Your project is now setup to use xcli.

To get a list of commands type

xcli

Tasks:
  xcli clients      # list clients
  xcli enter_time   # enter time to current ticket based on git repo and branch
  xcli new_ticket   # create new ticket based on git repo and branch
  xcli projects     # list projects
  xcli status       # show information about the current user
  xcli ticket       # show current ticket based on git repo and branch

Once you are in a feature branch you will want to check and see if there is a ticket in xrono associated with that branch.

xcli ticket

If no ticket is listed we need to add one

xcli new_ticket --estimated_hours=0 --description="some ticket" --name="some ticket"

Now use xcli ticket to make sure it worked

xcli ticket

To enter time against that ticket

xcli enter_time --hours=5 --message="I did lots of awesome things"