thinair

Boulder, Colorado

elevation 5400 feet

your guide: Eric Dobbs

interactive turtle graphics in a web browser

Monday 24 January 2011 at 00:54

This one feels like one of those small-step-giant-leap things. I've written a simple interactive shell to play with turtle graphics in the browser. What's more, it works from my phone!

source code is here: https://github.com/dobbs/turtle

This makes me happy!

Commands include:

penup

pendown

move pixels

turn degrees

clear

Occupy Wall Street and Tea Party and Arab Spring: evidence of shifting power

Friday 7 October 2011 at 16:53

Concentrations of Power. We take these truths to be self-evident: imbalance of power invites abuse of power, and power has tipped wildly out of balance. The evidence of abuse is widespread and mounting.

Of the People, By the People, and For the People. Abe Lincoln in the Gettysburg Address gave us this myth that our government was about the People. The actual institutions have always been a government of the elite. The Declaration of Independence opens with "We the People" but the Constitution divides power between State and Federal governments. The People are an afterthought squeezed into the Bill of Rights.

The S&P and Fortune 500s. The world's largest corporations have grown into stateless governments unto themselves. We the People have no representation in these corporations. Our only influence is how we work or how we spend. As wealth has concentrated into the hands off the few, the influence of the People over multi-national corporations has dwindled.

New Tools of Communication. We no longer need long chains of command and hierarchical organizations to organize and implement ambitious projects. We no longer need the talking heads in the media or political polls to tell us what the People think. We the People can speak for ourselves.

Emergence. We no longer need the singular vision of individual charismatic leaders to focus our collective action. The new tools of communication empower even very small groups to effect significant changes and for one small group to adopt and adapt the successes and failures of others.

We witness a shift of power. Power never shifts gracefully.

We the People are finally catching on that we can govern ourselves instead of ceeding power to Representatives and we can collectively relaim power ceeded to multi-national coroprations.

This is gonna be an interesting ride.

Testing cookies in Capybara, Rack::Test, RSpec, and Rails: debugging other people's code (1 of 2)

Sunday 11 December 2011 at 23:39

This morning Sarah and the kids made gingerbread cookies, thoroughly destroying all illusions of joyous family fun for the holidays. Speaking of cookies failing to deliver promises, I blew the entire day Friday figuring out a failing capybara test around browser cookies. Not a good weekend for cookies.

It's a Rails app which depends entirely on a legacy authentication and authorization system. The simplest thing that could possibly work is use cookies set by the existing authentication system. Should be simple.

describe "ServiceRequestController" do
  it "redirects when credentials are missing" do
    get new_service_request_path
    response.status.should == 302
  end
end

Test is red.

class ServiceRequestController < ApplicationController
  def new
    redirect_to cookies_path
  end
end

Test is green.

describe "ServiceRequestController" do
  ...
  it "renders the form when credentials are found" do
    page.driver.browser.set_cookie 'username=jhendrix'
    get new_service_request_path
    response.status.should == 200
  end    
end

Test is red.

class ServiceRequestController < ApplicationController
  def new
    user = LegacyUser.find_by_username(request.cookies['username'])
    if !user
      redirect_to cookies_path
    end
  end
end

Test is red. I found a work-around just before lunch, but spent the rest of the day trying to understand why that doesn't work.

tl;dr

If you're here just for the work-around when testing with cookies in Capybara and RSpec, here's my first work around (but the better way to test cookies in Capybara, Rack::Test and RSpec is in part 2 ):

describe "ServiceRequestController" do
  ...
  it "renders the form when credentials are found" do
    page.driver.browser.set_cookie 'username=jhendrix'
    page.driver.get new_service_request_path
    response.status.should == 200
  end    
end

Use the page.driver when you call get.

Debugging other people's code: aka. code caving in a maze of twisty passages

What follows are some of the most important programming skills I learned working with Rob and Paul at bivio: how to find your way around other people's code. I'll start with the failing test.

describe "ServiceRequestController" do
  ...
  it "renders form when credentials are found" do
    page.driver.browser.set_cookie 'username=jhendrix'
    get new_service_request_path
    response.status.should == 200
  end    
end

First question: Am I calling set_cookie correctly? There's a profoundly important tone in this question. Start with a beginner's mind. Don't guess. Don't be clever. Just read and follow the code. This is also one of the hardest things to do. As programmers we're all pretty intoxicated with our own cleverness. Let go and use The Source.

In RubyMine you can Command-Click on set_cookie or type Command-B. That pulls up a list of likely candidates. Without RubyMine you need some unix fu. Two of the most important, general purpose, and language independent programming tools ever are find and grep. Get to know them. They are your best friends.

find ${GEM_PATH//:/ /} -type f -exec grep -nH -e 'def set_cookie' {} /dev/null \;

Either way you search, the results are similar (though not identical). These are cleaned up from the results via find and grep:


action_dispatch/http/response.rb:158:                   def set_cookie(key, value)
action_dispatch/middleware/session/cookie_store.rb:65:  def set_cookie(env, session_id, cookie)
rack/response.rb:58:                                    def set_cookie(key, value)
rack/session/abstract/id.rb:320:                        def set_cookie(env, headers, cookie)
rack/session/cookie.rb:121:                             def set_cookie(env, headers, cookie)
rack/mock_session.rb:23:                                def set_cookie(cookie, uri = nil)

The hits in action_dispatch, rack/response and rack/session are probably all related to the real cookie code. That stuff has to be working correctly or no Rails app would be able to save cookies. It almost has to be something in rack/mock_session.

There's a certain bit of irony here. I didn't trust these search results. I was Being Clever. I took a longer path and ended up in rack/mock_session anyway. The longer path did give me the confidence of a second opinion. Trusting The Source would have produced my answer more quickly. Then again, I might have found the answer and still not trusted it. I'll share the longer path I took which is pretty much what I could learn by stepping through with a debugger.

In this call, page.driver.browser.set_code where does page come from? I know it's from Capybara, but where exactly? It'll be in here somewhere: https://github.com/jnicklas/capybara/tree/master/lib/capybara.

cucumber.rb	
dsl.rb
rails.rb	
rspec.rb	
selector.rb	
server.rb	
session.rb	
version.rb
Looking over that list dsl.rb looked most promising. And sure enough in Capybara::DSL I find

def page
  Capybara.current_session
end

current_session is defined earlier in the file:

def current_session
  session_pool["#{current_driver}:#{session_name}:#{app.object_id}"] ||= Capybara::Session.new(current_driver, app)
end

Now I know that page is a Capybara::Session using current_driver. What's current_driver? It's also defined in dsl.rb:

def current_driver
  @current_driver || default_driver
end

And default_driver?

def default_driver
  @default_driver || :rack_test
end

So page is a Capybara::Session initalized with :rack_test. The relevant code:

attr_reader :mode, :app

def initialize(mode, app=nil)
  @mode = mode
  @app = app
end

def driver
  @driver ||= begin
    unless Capybara.drivers.has_key?(mode)
      other_drivers = Capybara.drivers.keys.map { |key| key.inspect }
      raise Capybara::DriverNotFoundError, "no driver called #{mode.inspect} was found, available drivers: #{other_drivers.join(', ')}"
    end
    Capybara.drivers[mode].call(app)
  end
end

Remind me again what it was I was searching for? I've bounced around the code enough to be lost in "a maze of twisty little passages, all alike." Oh yeah: page.driver.browser.set_cookie. After all this caving, I'm finally through page and now looking for driver. The good news is, it's right here: Capybara.drivers[:rack_test].call(app). But where do I find Capybara.drivers? Well given the name, let's look first at the top level capybara module: Capybara.

def drivers
  @drivers ||= {}
end

Searching through that file, this is the only line that refers to @drivers. What about references to drivers itself? Here's the only one I could find:

def register_driver(name, &block)
  drivers[name] = block
end

And register_driver?

Capybara.register_driver :rack_test do |app|
  Capybara::RackTest::Driver.new(app)
end

Now I know the driver is a Capybara::RackTest::Driver and the top of that file invites the next question: what's the browser?

  attr_reader :app, :options

def initialize(app, options={})
  raise ArgumentError, "rack-test requires a rack application, but none was given" unless app
  @app = app
  @options = DEFAULT_OPTIONS.merge(options)
end

def browser
  @browser ||= Capybara::RackTest::Browser.new(self)
end

Are we there yet? What was I looking for again? Oh, right. It's set_cookie. But there's no sign of that method in Capybara::RackTest::Browser. But there is an important bit that's easy to miss right up top:

class Capybara::RackTest::Browser
  include ::Rack::Test::Methods

Let's take a peek in Rack::Test::Methods. set_cookie is delegated to the current_session.

METHODS = [
  :request,
  :get,
  :post,
  :put,
  :delete,
  :options,
  :head,
  :follow_redirect!,
  :header,
  :set_cookie,
  :clear_cookies,
  :authorize,
  :basic_authorize,
  :digest_authorize,
  :last_response,
  :last_request
]

def_delegators :current_session, *METHODS

current_session points to rack_test_session:


def current_session # :nodoc:
  rack_test_session(_current_session_names.last)
end

which points to build_rack_test_session:


def rack_test_session(name = :default) # :nodoc:
  return build_rack_test_session(name) unless name

  @_rack_test_sessions ||= {}
  @_rack_test_sessions[name] ||= build_rack_test_session(name)
end

which creates a Rack::Test::Session with a rack_mock_session:


def build_rack_test_session(name) # :nodoc:
  Rack::Test::Session.new(rack_mock_session(name))
end

which points to build_rack_mock_session:


def rack_mock_session(name = :default) # :nodoc:
  return build_rack_mock_session unless name

  @_rack_mock_sessions ||= {}
  @_rack_mock_sessions[name] ||= build_rack_mock_session
end

"Which will bring us back to" Rack::MockSession.


def build_rack_mock_session # :nodoc:
  Rack::MockSession.new(app)
end

And inside Rack::MockSession is where the test calls set_cookie.

def set_cookie(cookie, uri = nil)
  cookie_jar.merge(cookie, uri)
end

And what, finally, is cookie_jar?

def cookie_jar
  @cookie_jar ||= Rack::Test::CookieJar.new([], @default_host)
end

It's probably a newly minted Rack::Test::CookieJar. Now wouldn't it have been a lot easier if I'd just trusted the search results in the first place? find and grep are your best friends. You can trust them to save you a lot of time.

Testing cookies in Capybara, Rack::Test, RSpec, and Rails: debugging other people's code (2 of 2)

Sunday 13 December 2011 at 23:10

A quick refresher, mainly for those who may land here first instead of on part 1. I spent all of last Friday figuring out a failing capybara test around browser cookies. First, the tests:

describe "ServiceRequestController", type: :request do
  it "redirects when credentials are missing" do
    get new_service_request_path
    response.status.should == 302
  end

  it "renders the form when credentials are found" do
    page.driver.browser.set_cookie 'username=jhendrix'
    get new_service_request_path
    response.status.should == 200
  end    
end

Second, the controller:

class ServiceRequestController < ApplicationController
  def new
    user = LegacyUser.find_by_username(request.cookies['username'])
    if !user
      redirect_to cookies_path
    end
  end
end

First test passes, second test fails. The previous post traced through line one of my failing spec: page.driver.browser.set_cookie 'username=jhendrix'.

tl;dr

If you're here just for the work-around when testing with cookies in Capybara with the Rack::Test driver and RSpec, here's the best solution I've found so far to make it work. Nevermind trying to set cookies via capybara. Just pass in the HTTP_COOKIE header with the call to get.

describe "ServiceRequestController", type: :request do
  ...
  it "renders the form when credentials are found" do
    get new_service_request_path, {}, 'HTTP_COOKIE' => 'username=jhendrix'
    response.status.should == 200
  end    
end

More tricks for debugging other people's code: aka. code caving in a maze of twisty passages

I still haven't answered my first question: "am I setting cookies correctly?" As you can guess from the tl;dr, the short answer is "No". But life is a journey, not a destination. I'll start again with the first line: page.driver.browser.set_cookie 'username=jhendrix' The first place to look is at set_cookie and see if I'm actually setting one. I'll start with the most important programming power tools find and grep and I'll add in another one: intentionally raising an exception.

find ${GEM_PATH//:/ /} -type f -exec grep -nH -e 'def set_cookie' {} /dev/null \;

Here's the results of that search, cleaned up for legibility and brevity


action_dispatch/http/response.rb:158:                   def set_cookie(key, value)
action_dispatch/middleware/session/cookie_store.rb:65:  def set_cookie(env, session_id, cookie)
rack/response.rb:58:                                    def set_cookie(key, value)
rack/session/abstract/id.rb:320:                        def set_cookie(env, headers, cookie)
rack/session/cookie.rb:121:                             def set_cookie(env, headers, cookie)
rack/mock_session.rb:23:                                def set_cookie(cookie, uri = nil)

As I mentioned in part 1, the hits in action_dispatch, rack/response and rack/session are probably all related to the real cookie code. That stuff has to be working correctly or no Rails app would be able to save cookies. It almost has to be something in rack/mock_session. But this time around I'll take one extra moment test my hypothesis. Look at the source for that method in Rack::MockSession :

def set_cookie(cookie, uri = nil)
  cookie_jar.merge(cookie, uri)
end

Where's cookie_jar come from?

def cookie_jar
  @cookie_jar ||= Rack::Test::CookieJar.new([], @default_host)
end

I'll take drastic action to confirm this is the code in question:

def cookie_jar
  @cookie_jar ||= Rack::Test::CookieJar.new([], @default_host)
  raise @cookiejar.inspect
end

Running the specs... sure enough they fail with a RuntimeError, for example:

1) new service requests redirects when credentials are missing
   Failure/Error: get new_service_request_path
   RuntimeError:
     #<Rack::Test::CookieJar:0x00000103d592c0 @default_host="www.example.com", @cookies=[]>
   # ./spec/requests/service_requests_controller_spec.rb:5:in `block (2 levels) in <top (required)>'

Now I know I'm going in the right direction. Is the cookie is getting set?

def set_cookie(cookie, uri = nil)
  puts "Rack::MockSession.set_cookie(#{cookie})"
  cookie_jar.merge(cookie, uri)
  raise @cookiejar.inspect
end

...

def cookie_jar
  @cookie_jar ||= Rack::Test::CookieJar.new([], @default_host)
end

.Rack::MockSession.set_cookie('username=jhendrix')
F

Failures:

  1) new service requests renders the form when credentials are found
     Failure/Error: page.driver.browser.set_cookie "username=jhendrix"
     RuntimeError:
       #<Rack::Test::CookieJar:0x000001015ee0f0 @default_host="www.example.com", @cookies=[#<Rack::Test::Cookie:0x000001015ee050 @default_host="www.example.com", @name_value_raw="username=jhendrix", @name="username", @value="jhendrix", @options={"domain"=>"www.example.com", "path"=>""}>]>
     # ./spec/requests/service_requests_controller_spec.rb:10:in `block (2 levels) in <top (required)>'

set_cookie works: the cookie I've set in the spec shows up in the CookieJar. Why is the spec failing? I'll change the code to print out the cookie_jar instead of raising an exception and keep searching.

def set_cookie(cookie, uri = nil)
  result = cookie_jar.merge(cookie, uri)
  puts "set_cookie('#{cookie}')\n#{@cookiejar.inspect}"
  result
end

Line 2 of the failing spec

get new_service_request_path

My trusty friends find and grep will turn up a long list. get is a really popular method name for objects of every shape and size.

find ${GEM_PATH//:/ /} -type f -exec grep -nH -e 'def get(' {} /dev/null \;

I'll draw your attention to the punctuation... I'm searching for get( and not just get so the results don't include false leads like get_coffee. Cleaned up results:

action_controller/test_case.rb:364:                   def get(action, parameters = nil, session = nil, flash = nil)
action_dispatch/routing/mapper.rb:476:                def get(*args, &block)
action_dispatch/routing/route_set.rb:106:             def get(name)
action_dispatch/testing/integration.rb:32:            def get(path, parameters = nil, headers = nil)
action_view/flows.rb:12:                              def get(key)
action_view/flows.rb:46:                              def get(key)
active_model/errors.rb:93:                            def get(key)
active_record/identity_map.rb:77:                     def get(klass, primary_key)
active_resource/connection.rb:79:                     def get(path, headers = {})
active_resource/custom_methods.rb:56:                 def get(custom_method_name, options = {})
active_resource/custom_methods.rb:89:                 def get(method_name, options = {})
bundler/vendor/thor/actions/file_manipulation.rb:72:  def get(source, *args, &block)
capybara/rack_test/driver.rb:75:                      def get(*args, &block)
ffi/struct.rb:40:                                     def get(ptr)
ffi/struct.rb:51:                                     def get(ptr)
ffi/struct.rb:66:                                     def get(ptr)
rack/mock.rb:56:                                      def get(uri, opts={})
rack/cache/appengine.rb:27:                           def get(key)
rack-cache-1.1/test/entitystore_test.rb:248:          def get(key); self[key]; end;
rack-cache-1.1/test/metastore_test.rb:318:            def get(key); self[key]; end;
rack-cache-1.1/test/spec_setup.rb:169:                def get(stem, env={}, &b)
rack/test.rb:55:                                      def get(uri, params = {}, env = {}, &block)
rake-0.9.2.2/doc/proto_rake.rdoc:70:                  def get(task_name)
rspec/matchers/operator_matcher.rb:15:                def get(klass, operator)
sass/util/subset_map.rb:73:                           def get(set)
selenium/webdriver/common/driver.rb:105:              def get(url)
selenium/webdriver/remote/bridge.rb:98:               def get(url)
selenium/webdriver/support/event_firing_bridge.rb:20: def get(url)
thor-0.14.6/lib/thor/actions/file_manipulation.rb:72: def get(source, *args, &block)

I could use the same trick by methodically editing every one of these results and raise exceptions in each and then run the specs again. But that would be tedious and I have another trick to share. It's particularly useful for any Domain Specific Language. I'll just instrument the spec itself to report which get to investigate.

it "renders the form when credentials are found" do
  page.driver.browser.set_cookie "username=jhendrix"
  puts "#{method(:get)}"
  get new_service_request_path
  response.status.should == 200
end

Running the spec...

#<Method: RSpec::Core::ExampleGroup::Nested_1(ActionDispatch::Integration::Runner)#get>

Connecting the dots between the earlier result action_dispatch/testing/integration.rb and ActionDispatch::Integration::Runner

It's not quite as easy to spot, but notice that get is forwarded to integration_session

%w(get post put head delete cookies assigns
   xml_http_request xhr get_via_redirect post_via_redirect).each do |method|
  define_method(method) do |*args|
    reset! unless integration_session
    # reset the html_document variable, but only for new get/post calls
    @html_document = nil unless method.in?(["cookies", "assigns"])
    integration_session.__send__(method, *args).tap do
      copy_session_variables!
    end
  end
end

Integration::Session is defined earlier in the same file:

class Session
  DEFAULT_HOST = "www.example.com"

  include Test::Unit::Assertions
  include TestProcess, RequestHelpers, Assertions

There's no sign of any method named get in Integration::Session, but looking through the included modules, I found it in RequestHelpers

def get(path, parameters = nil, headers = nil)
  process :get, path, parameters, headers
end

which calls process which after a bunch of setup creates a new Rack::Test::Session

def process(method, path, parameters = nil, rack_env = nil)
  ...
  session = Rack::Test::Session.new(_mock_session)

which calls _mock_session

def _mock_session
  @_mock_session ||= Rack::MockSession.new(@app, host)
end

That code looks familiar. Both the first and second lines of the spec ask Rack::MockSession for their cookies. Let's instrument that class a little further.

def cookie_jar
  @cookie_jar ||= Rack::Test::CookieJar.new([], @default_host)
  puts "    Rack::MockSession.cookie_jar #{@cookie_jar}"
  @cookie_jar
end

I also need to know when get is called. Back to ActionDispatch::Integration::RequestHelpers

def get(path, parameters = nil, headers = nil)
  puts "ActionDispatch::Integration::RequestHelpers.get #{path}"
  process :get, path, parameters, headers
end

Run the failing spec.

Rack::MockSession.cookie_jar #<Rack::Test::CookieJar:0x00000103458068>
Rack::MockSession.cookie_jar #<Rack::Test::CookieJar:0x00000103458068>
Rack::MockSession.set_cookie('username=jhendrix') #<Rack::Test::CookieJar:0x00000103458068 @default_host="www.example.com", @cookies=[#<Rack::Test::Cookie:0x00000103457e88 @default_host="www.example.com", @name_value_raw="username=jhendrix", @name="username", @value="jhendrix", @options={"domain"=>"www.example.com", "path"=>""}>]>
#<Method: RSpec::Core::ExampleGroup::Nested_1(ActionDispatch::Integration::Runner)#get>
ActionDispatch::Integration::RequestHelpers.get /service_requests/new
Rack::MockSession.cookie_jar #<Rack::Test::CookieJar:0x00000102dce008>
Rack::MockSession.cookie_jar #<Rack::Test::CookieJar:0x00000102dce008>

The source of the problem is revealed in these results but it takes careful reading to spot it. Inside MockSession, cookie_jar is called twice. Then comes the output from the call to set_cookie. Line 4 is from the spec where I identified which get was called. Line 5 is the debug output I just added. And notice that cookie_jar is again called twice within the call to process.

All reasonable enough. Where's the bug?

Look very closely at the CookieJars in use. It'll help if I put them right next to each other:

Rack::MockSession.cookie_jar #<Rack::Test::CookieJar:0x00000103458068>
Rack::MockSession.cookie_jar #<Rack::Test::CookieJar:0x00000102dce008>

Those may be the same class but they are different instances. The call to get reads its cookies from a different CookieJar than where they are set in the call to set_cookie. In fact, those two calls even have different MockSessions. Removing all the previous debug output and adding this:

def initialize(app, default_host = Rack::Test::DEFAULT_HOST)
  puts "#{self}"
  @app = app
  @after_request = []
  @default_host = default_host
  @last_request = nil
  @last_response = nil
end

gives the following output when I run the spec again.

#<Rack::MockSession:0x00000102e69ad0>
#<Rack::MockSession:0x00000102d8e9d0>

My first work-around was this:

it "renders the form when credentials are found" do
  page.driver.browser.set_cookie 'username=jhendrix'
  page.driver.get new_service_request_path
  response.status.should == 200
end

That does get those two lines of code using the same MockSession which also makes the test pass:

#<Rack::MockSession:0x000001036e9db8>
.

Finished in 8.14 seconds
1 example, 0 failures

But in the end, that smells wrong to me. And having just looked at ActionDispatch::Integration::RequestHelpers.get I had a new idea for providing cookies:

it "renders the form when credentials are found" do
  get new_service_request_path, {}, 'HTTP_COOKIE' => 'username=jhendrix'
  response.status.should == 200
end

That test passes.

recap of debugging tricks

Beginner's Mind is really important. Don't be clever. Just carefully follow the code. Take notes along the way so you know the landmarks and can find your way back.

find and grep to search in $GEM_PATH for method definitions.

Raise exceptions in possible matches to confirm you're looking in the right place. Replace the exceptions with puts statements when you need to let the software proceed.

Ruby's built-in introspection, especially Object#method can help identify methods in DSLs where you may not have an easy handle on the object.

You have to read carefully. Read the code carefully and read debug output and stack traces carefully. Even when you narrow it down to the same class in similar contexts, you may be looking at different instances of the class.

recap of this specific "bug"

It is reasonable to think that calling set_cookie should work for the next get or post call. It doesn't. page.driver.browser.set_cookie creates one instance of Rack::MockSession and get creates another. They're not the same session.

On the other hand, maybe it's not a bug in the code but in the documentation. If everyone knows to just pass in HTTP_COOKIE with the request, then there's no need for a pull request at all. I think that's where I've landed.

If for some reason you feel motivated to fix the interaction around set_cookie, there's some kind of coordination needed between Capybara's Rack::Test driver and the Rack::Test::Methods so they can share the same Rack::MockSession.

Turtle Geometry exercises

Thursday 22 December 2011 at 17:05

Exercises and examples from chapter one of Turtle Geometry by Abelson and diSessa implemented in javascript. Kinda blown away by this book: only on chapter one and this page already offers a pretty fun playground for exploring geometry and rudimentary graphics programming.

Click some of the links below for examples and inspiration. You can also type commands into the form below the turtle's sandbox. Commands you type will be saved in the history below the sandbox.

Pen controls include penup, pendown, pensize pixels, and pencolor color. Any CSS color name or hex value will work for color: Wikipedia: Web Colors

Have a look at the source code for these exercises if you like: tg.js