John Keith

Hi, I'm a developer that loves Ruby, Rails, Angular, and iOS.

Defining a Method Accessible From Anywhere in a Rails App

It started as an innocuous attempt to dry up some controller code.

1
2
3
4
5
6
# app/controllers/export_controller.rb

if ENV['TORQUEBOX_APP_TYPE'].present? || ENV['TORQUEBOX_APP_NAME'].present?
  # do some crazy stuff in a background process since this is clearly running in production
else
  # Rails must be running locally, so do it in the foreground

I noticed that the same if statement was used in our config/application.rb to setup the correct logging when Torquebox is running. After searching the rest of the code and seeing the same logic was present in config/environments/development.rb I mistakenly thought, “It can’t be too hard to figure out how to have a method accessible from anywhere in a Rails app.”

How wrong I was!

That said, I came up with two possible solutions. The first was to create a proc, stuff it in a global variable, and then access that from each of the places I needed (the config files and the controller).

1
$torquebox_running = Proc.new { ENV['TORQUEBOX_APP_TYPE'].present? || ENV['TORQUEBOX_APP_NAME'].present? }

While the proc worked, I didn’t think it was the best solution for two structural reasons.

  1. It would have to be defined in application.rb in order to be used in the start up process, but being defined in that file outside of the Application class (so that it could be accessed elsewhere in the app) would be a tad strange.

  2. Usage wise, the syntax of $torquebox_running.call seemed a bit strained to me.

My gut told me that I might be breaking every law in the book by declaring a global proc during the boot-up process of a Rails app. So I decided on a more compartmentalized (though still perhaps not object-oriented) approach to solving the problem.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# lib/services/torquebox_information_service.rb
module TorqueboxInformationService
  extend self

  def env_vars_present?
      ENV['TORQUEBOX_APP_TYPE'].present? || ENV['TORQUEBOX_APP_NAME'].present?
  end

  def process_running?
      # not working, needs refinement
      process_pid = Process.pid
      process_info = `ps -ef | grep #{process_pid}`
      process_info.downcase.include?('torquebox')
  end
end
1
2
# config/application.rb
require File.expand_path('../../lib/services/torquebox_information_service', __FILE__)

By wrapping these environment checking methods inside a module and using the handy extend self, the logic was kept isolated and no awful globals created in the process.

I then required the module in the application.rb above all other requirements and the Application class in order that it would be available in every part of the app (config files, models, controllers, the whole gaggle of them!). So far, this approach is working and allows the logic for checking if Torquebox is running to be stored in a single place.

I still feel like this could be improved, though. Maybe by not using extend self and instead including the module on any of the classes that need access to these methods it would better keep these Torquebox logic confined to only the places in the app that need access?

Ajax Testing Using RSpec, Capybara, and Puffing Billy

Launch Academy has come to an end. It was an incredible 10 weeks, but I’m already itching to jump into a new project while beginning my developer job search.

Ajax testing was one of the areas I only briefly touched on at the end of the course so I wanted to spend some time this week exploring the ins and outs of this crucial aspect of testing. The engineers at Launch said that Ajax and other key Javascript operations of an app should be tested using a JavaScript testing framework. I’m still tempted, however, see benefits to testing interactions that rely on Ajax with integration tests, especially if they are fundamental to a user’s experience on the page. The example below is how I implemented a couple of Ajax tests using the GitHub API and a simple form.

To start with, it is important to note that testing JavaScript requires you to add some extra configuration in your rails_helper and in your specs. Capybara by default uses the Selenium javascript driver to test Javascript on the page. From my initial testing, it seems like Selenium returns consistent results, but takes more time than other methods. Selenium opens a new instance of the browser for each test, which on a big test suite I imagine could lead to considerable delay.

To enable Javascript on a test, simply pass the js: true option to the test. (Or better yet, pass js: true to your entire feature to use Javascript on all the tests in a spec). When you run RSpec, you should see browser windows pop up and replay the actions mapped out in your integration tests.

There are other Javascript drivers, Poltergeist being the second one I implemented. Poltergeist needs a little extra setup to get it running, as it is built upon PhantomJS, which allows it to run your tests without opening new browser windows each time.

First, add Poltergeist to your Gemfile.

1
gem 'poltergeist'

Then, in your rails_helper, specify the default javascript driver to be used in your tests.

1
Capybara.javascript_driver = :poltergeist

Again, make sure either a single test or your entire feature has Javascript enabled by passing the js: true option.

1
2
3
require_relative '../rails_helper'

feature "user enters basic information on homepage", js: true do

Finally, install PhantomJS from the link above. When running RSpec, you should see your test suite function as normal, without browser windows materializing all over the screen.

One major issue that arises with these two approaches is API calls. When using these Javascript drivers on their own, the test suite is still reaching out and making API requests. At first I thought I was safe – I had VCR and WebMock enabled in order to record and replay HTTP interactions. I found out the old fashioned way – by disconnecting from my wi-fi and causing the tests to fail – that these mechanisms were in fact not capturing my Ajax requests.

To grab these Ajax requests, I hunted down a gem called Puffing Billy. Puffing Billy handles the recording of Ajax calls like VCR, allowing you to run the test suite with genuine API requests once and then subsequently replay the recordings made from the first calls. Puffing Billy has a great readme on its GitHub page, but below are the steps I followed to get it working.

First, add Puffing Billy to your Gemfile.

1
gem 'puffing-billy'

Then, require Puffing Billy in your rails_helper.

1
require 'billy/rspec'

Next, set your default Javascript driver in your rails_helper. (Puffing Billy supports Selenium, Poltergeist, and Webkit. See the docs for more details.)

1
Capybara.javascript_driver = :poltergeist_billy

Lastly, configure Puffing Billy to cache Ajax interactions with this configure block in your rails_helper.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Billy.configure do |c|
  c.cache = true
  c.cache_request_headers = false
  c.ignore_params = ["http://www.google-analytics.com/__utm.gif",
                     "https://r.twimg.com/jot",
                     "http://p.twitter.com/t.gif",
                     "http://p.twitter.com/f.gif",
                     "http://www.facebook.com/plugins/like.php",
                     "https://www.facebook.com/dialog/oauth",
                     "http://cdn.api.twitter.com/1/urls/count.json"]
  c.path_blacklist = []
  c.persist_cache = true
  c.ignore_cache_port = true # defaults to true
  c.non_successful_cache_disabled = false
  c.non_successful_error_level = :warn
  c.non_whitelisted_requests_disabled = false
  c.cache_path = 'spec/req_cache/'
end

That should be it! Now you can run your test suite, watch it pass, and see a folder of the requests in req_cache.

Below is an example of a basic feature I wrote for what I believe is the world’s first, only, and hopefully last GitHub dating app. (Not sure I’ll be continuing with that side project…but it was a good example for learning Ajax testing). I’ll also include all the necessary configs you need to get RSpec, Capybara, and Puffing Billy to run these test.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# onboarding page feature

require_relative '../rails_helper'

feature "user enters basic information on homepage", js: true do
  scenario "fills in github username" do
    visit root_path
    fill_in "Your Github username", with: "johnkeith"
    select "Male", from: "gender-select"
    expect(page).to have_xpath "//img[@src=\"https://avatars.githubusercontent.com/u/4976905?\"]"
    expect(page).to have_css "div.has-success"
  end

  scenario "fills in a github username that doesn't exist" do
    visit root_path
    fill_in "Your Github username", with: "notaghuser"
    select "Male", from: "gender-select"
    expect(page).to have_content "Sorry, that is not a Github username."
    expect(page).to have_css "div.has-error"
  end

  scenario "selects own gender" do
    visit root_path
    select "Male", from: "gender-select"
    fill_in "Your Github username", with: "johnkeith"
    expect(page).to have_css "div.has-success"
  end

  scenario "selects preference for matches" do
    visit root_path
    select "Men", from: "match-pref-select"
    fill_in "Your Github username", with: "johnkeith"
    expect(page).to have_css "div.has-success"
  end

  scenario "fills username, selects gender, selects preferences" do
    visit root_path
    fill_in "Your Github username", with: "johnkeith"
    select "Male", from: "gender-select"
    select "Women", from: "match-pref-select"
    expect(page).to have_xpath "//img[@src=\"https://avatars.githubusercontent.com/u/4976905?\"]"
    expect(page).to have_select("gender-select", selected: "Male")
    expect(page).to have_select("match-pref-select", selected: "Women")
  end
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# rails_helper.rb

ENV["RAILS_ENV"] ||= 'test'
require 'spec_helper'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'capybara/rails'
require 'shoulda/matchers'
require 'capybara/poltergeist'
require 'billy/rspec'

Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }

ActiveRecord::Migration.maintain_test_schema!

Capybara.javascript_driver = :poltergeist_billy

Billy.configure do |c|
  c.cache = true
  c.cache_request_headers = false
  c.ignore_params = ["http://www.google-analytics.com/__utm.gif",
                     "https://r.twimg.com/jot",
                     "http://p.twitter.com/t.gif",
                     "http://p.twitter.com/f.gif",
                     "http://www.facebook.com/plugins/like.php",
                     "https://www.facebook.com/dialog/oauth",
                     "http://cdn.api.twitter.com/1/urls/count.json"]
  c.path_blacklist = []
  c.persist_cache = true
  c.ignore_cache_port = true # defaults to true
  c.non_successful_cache_disabled = false
  c.non_successful_error_level = :warn
  c.non_whitelisted_requests_disabled = false
  c.cache_path = 'spec/req_cache/'
end
1
2
3
4
5
6
7
8
9
# spec_helper.rb

require 'webmock/rspec'

WebMock.disable_net_connect!(allow_localhost: true)

RSpec.configure do |config|

end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Gemfile

group :development, :test do
  gem 'dotenv-rails'
  gem 'rspec-rails'
  gem 'capybara'
  gem 'launchy'
  gem 'factory_girl_rails'
  gem 'pry-rails'
  gem 'poltergeist'
  gem 'puffing-billy'
  gem 'webmock'
  gem 'valid_attribute'
  gem 'shoulda-matchers'
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/ home page with form to be tested

.row
  .col-md-3
    .portrait-placeholder
      %img{ src: "", class: "img-responsive img-thumbnail", id: "github-avatar" }
  .col-md-9
    %form{ role: "form", id: "onboard-form" }
      .form-group.form-group-lg#github-username-group
        %input{ type: "text", class: "form-control input-lg", placeholder: "Your Github username", id: "github-username" }
      .form-group.form-group-lg#gender-group
        %select{ class: "form-control input-lg", id: "gender-select" }
          %option{ value: "", "disabled" => "disabled", "selected" => "selected" } Select your gender
          %option{ value: "male", id: "gender-male" } Male
          %option{ value: "female", id: "gender-female" } Female
      .form-group.form-group-lg#match-group
        %select{ class: "form-control input-lg", id: "match-pref-select" }
          %option{ value: "", "disabled" => "disabled", "selected" => "selected" } Select your preference
          %option{ value: "men", id: "match-men" } Men
          %option{ value: "women", id: "match-women" } Women
    .form-errors
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// jquery for form 

$(function(){
  $("#github-username").change(function(){
    var username = $("#github-username").val();
    $.ajax({
      type: "GET",
      url: "https://api.github.com/users/" + username,
      success: function(d) {
        $("#github-avatar").attr("src", d.avatar_url);
        $("#github-username-group").addClass("has-success");
        $("#github-username-group").removeClass("has-error");
        $(".form-errors").text("");

      },
      error: function() {
        $(".form-errors").text("Sorry, that is not a Github username.");
        $("#github-avatar").attr("src", "");
        $("#github-username-group").addClass("has-error");
        $("#github-username-group").removeClass("has-success");
      }
    });
  });
  $("#gender-select").change(function(){
    $("#gender-group").addClass("has-success");
  });
  $("#match-pref-select").change(function(){
    $("#match-group").addClass("has-success");
  });
});

Week Six and Embracing TDD

We have made it to the end of week six. It was a big one for me:

  • Pushed a Javascript game to Heroku
  • Promptly got hacked
  • Started figuring out OmniAuth
  • Finally surmounted my fear/distaste for Test Driven Development.

TDD was one of the big aspects of the move to Rails that I was dreading. During our phase working in Sinatra, we didn’t worry about TDD – it was enough trying to wrap our heads around the basics of HTTP, Postgres, and ActiveRecord. Now that we are building with Rails in ernest, TDD has moved to the foreground of our development process.

Initially, I was very wary of TDD. The Launch instructors did a great job of demonstrating the rationale behind TDD, showing us how TDD can ensure against your changes in one part of an app breaking another part, which was one thing that never quite sunk in for me working through tutorials online. That said, some of my early fumbling through Capybara and Rspec left me hesitant about TDD.

When we started TDD, I shied away from the assignments, focusing on a side project in Javascript. We finally finished that Javascript game on Friday, turning it into a Sinatra app that we were able to put up on Heroku, and this weekend I resolved to put in the time to get comfortable using TDD.

Having spent the past two days building a first Rails app from the ground up with TDD, my perspective has certainly changed. When you strictly adhere to the TDD methodology, it makes the process of building an app (especially a Rails app by hand, without scaffolding, as was the assignment this weekend) much more approachable and logical. My final product hardly had comprehensive test coverage, but now I feel comfortable with using TDD as a guide for building an app piece by piece.

How I’ve Learned in Five Short Weeks at Launch

We are now officially halfway through our ten weeks at Launch. What I’m still marveling at after five weeks is how Launch Academy is transforming me as a learner.

Pre-Launch, back in high school and college, I was a very learn-by-the-book kind of guy. I would read cover to cover any text put in front of me, but asking me to jump into a topic or domain without much preparation beforehand was always a struggle.

One great part about learning web development is that the read first, execute later approach I relied on in the past does not work. I’ve felt myself constantly challenged the past few weeks to dive into projects knowing only enough to get started and then to seek out only the knowledge I need to complete to reach the goal.

Fundamentally, I guess this is what makes project-based learning powerful: the fact that the knowledge you acquire is all acquired in a specific context of doing or creating. Even as I’ve started to head off on tangents of my own, like diving deeply into Javascript prototypes last week, this learning is done to serve a purpose, to bring some piece of functionality into existence.

If you want to take the metaphor perhaps a little too far (which is always a fun exercise), it is almost like the project-based curriculum of Launch is OOP for your brain. Sure, you can gather knowledge together in your mind about a certain object, but these little tarballs of information are worthless until you start defining behaviors and passing messages between them. Knowledge as an object in and of itself does nothing – it is only once it starts to interact with other bits of information that the effect is meaningful.

Week Four and the Challenges of OOP

Week four saw us moving into object oriented programming fully, a move that has certainly come with its share of challenges.

What I’m finding most challenging about OOP is figuring out how to reorient an app once it has gotten off track. A friend and I started working on a game a little over a week ago, back before we were really comfortable with OOP, and this week our lack of discipline and underlying structure has become more and more apparent as our code base has ballooned.

UnoDos

(It’s amazing how quickly the complexity of a game, even a simple one, can get out of hand). Certainly, working on the game has been a great exercise, though, for struggling through the realities of OOP.

We also started moving into ActiveRecord last week, initially building some of the pieces ourselves and then seeing how ActiveRecord makes the process of interacting with a database much more enjoyable. This week we’ll be beginning with Rails, so I’m excited to be getting back into it and diving into the nitty gritty details. Already, with all of our work the past few weeks, I feel much more prepared to figure out how Rails really works than I ever did learning on my own.

Week Three and Designing for Efficiency

Week three of Launch Academy was a short one with the holiday on Monday, but we covered a lot of ground. It was the start of the second module in the curriculum and we dove headfirst into databases this week. The relation database we are using right now is Postgres, which has been a nice change from the static data storage in CSV files we were using in weeks one and two.

We also had a great speaker come this week – Peter Degen-Portnoy. He gave us a brief introduction to object oriented design, which will be the focus of our work this week.

Outside of our core material last week, I spent time wresting with problems of efficiency. I found myself still mulling over mechanics from the 2048 code I wrote in week two, looking for ways to improve the efficiency of the logic used to find random empty spaces on the game board. I knew our initial way was not ideal – it determined random X and Y coordinates on the board and checked to see if that space was empty. Eventually, I ended up benchmarking three different ways of finding a random empty space in a matrix and I believe I found a reasonably efficient way to go about it. (It is the middle method of the three below, which gives consistently better results, often taking half the time of the other two).

My goal is to continue thinking along these lines this week – learning about how to incorporate object oriented design into our apps while keeping an eye out for writing efficient code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
test_board = [["2","4","4","2"],
              ["4","2","2"," "],
              ["2","2","8","2"],
              ["4","3","4","8"]]

def insertRandomly(board)
  two_or_four = rand.round == 0 ? 4 : 2
  insert_successful = false
  while !insert_successful
    random_position_one = rand(board.length)
    random_position_two = rand(board.length)
    if board[random_position_one][random_position_two] == " "
      board[random_position_one][random_position_two] = two_or_four.to_s
      insert_successful = true
    end
  end
end

def insertLessRandomly(board)
  two_or_four = rand.round == 0 ? 4 : 2
  random_direction = rand.round == 0 ? true : false
  insert_successful = false
  while !insert_successful
    random_position_one = rand(board.length)
    empty_space = nil
    empty_space = board[random_position_one].find_index do |item|
      item == " "
    end
    if empty_space != nil
      board[random_position_one][empty_space] = two_or_four.to_s
      insert_successful = true
    end
  end
end

def insertByIndex(board)
  two_or_four = rand.round == 0 ? 4 : 2
  empty_spaces = []
  board.each_with_index do |row, row_index|
    row.each_with_index do |column, col_index|
      empty_spaces << [row_index, col_index] if column == " "
    end
  end
  select_empty = empty_spaces.sample
  board[select_empty[0]][select_empty[1]] = two_or_four
end