Brian Takita's blog



RR 0.6.2 Released

edit Posted by Brian Takita on Tuesday October 28, 2008 at 06:07AM

This release features a sexier Double definition block syntax using instance_eval:

mock(User) do
  find(1) {user_1}
  find(2) {user_2}
end

RR also maintains its non-instance_evaled Double definition block syntax, if the block has an arity of 1:

mock(User) do |expect|
  expect.find(1) {user_1}
  expect.find(2) {user_2}
end

The instance_eval syntax is very useful in the context of Double Definition chaining:

mock.proxy(User).find(1) do
  mock.proxy! do
    blogs do
      mock.proxy!.find_by_id(2) do |actual_blog|
        actual_blog.name.should == "My ranty blog"
        actual_blog
      end
    end

    fans do
      mock.proxy!.thank_everybody
    end

    foes do
      mock.proxy!.ask_for_forgiveness
    end
  end
end

user = User.find(1)
user.blogs.find_by_id(2) # My ranty blog
user.fans.thank_everybody
user.foes.ask_for_forgiveness

Of course the previous example is a complicated case of interaction testing, and a simpler state-based and/or hybrid approach may be better, but it demonstrates how using instance_eval can promote readability.

Here is the non-instance_eval solution:

mock.proxy(User).find(1) do
  mock.proxy! do |expect|
    expect.blogs do
      mock.proxy!.find_by_id(2) do |actual_blog|
        actual_blog.name.should == "My ranty blog"
        actual_blog
      end
    end

    expect.fans do
      mock.proxy!.thank_everybody
    end

    expect.foes do
      mock.proxy!.ask_for_forgiveness
    end
  end
end

instance_eval Controversy

Ola Bini warned against overusing instance_eval. While instance_eval adds beauty, it can also make things more difficult to extend and debug.

In RR, the DoubleDefinitionCreatorProxy (the object that is instance_evaled when defining Doubles using blocks) uses the blank slate pattern, so arbitrary method names can be passed in to define the Double. The Blank Slate implies that the DoubleDefinitionCreatorProxy will not be extended with methods. James Earl Gray explains the pattern that I used for RR.

spec/test helper methods will not be usable within the instance_evaled blocks because the intent of the DoubleDefinitionCreatorProxy object is incompatible with the instance_eval with delegation pattern. DoubleDefinitionCreatorProxy already uses method_missing to create DoubleDefinitions. If you wish to use spec/test helper methods, you will need to memoize it to a variable and use lexical scoping.

Now, one may want to define a helper method in the test/spec that is returned when the Double is invoked.

describe User do
  describe "#fans.thank_everybody" do
    it "thanks all of my lovely fans" do
      user = User.new
      memoized_my_lovely_fans = my_lovely_fans
      mock(user) {fans {memoized_my_lovely_fans}}
      memoized_my_lovely_fans.each {|fan| mock(fan).thank_you}

      user.fans.thank_everybody
    end

    def my_lovely_fans
      [Fan.new, Fan.new]
    end
  end
end

While I think this will be a good addition to RR, I recognize that adding the instance_eval has the possibility of making RR less usable. I'll pay close attention to see how this pans out and am willing to remove it if there are major issues.

Effective Markdown Editing with the WMD editor and the Save Text Area Firefox plugin

edit Posted by Brian Takita on Saturday October 18, 2008 at 08:38PM

Anytime I need to edit Markdown, I reach for the WMD editor. Their splitscreen demo is the most effective way to edit Markdown that I have seen.

The left screen is the editor and the right screen is the "real-time" preview of the Markdown. It is nice because I don't have to press a preview button to see rendered Markdown. The Markdown is also rendered as I type so I get instantaneous feedback of my changes.

There is also a Save Text Area Firefox addon, which enables me to save/load the contents of a text area to/from a file on the filesystem. Also the Ctrl+s shortcut saves the file.

So when editing Markdown, I:

  1. Open Firefox and go to http://wmd-editor.com/examples/splitscreen?blank=1
  2. Load or Save a markdown file by right-clicking the editor screen
    1. Going to the Text submenu
    2. Clicking Load or Save As
  3. Edit the file and see the generated output

Of course, its not a text editor replacement, since the possible text manipulation in Firefox is limited, but the feedback that is provided by WMD is very effective to rapid Markdown editing. I hope this sort of UI becomes more common.

Now if only there were a similar Textile editor...

NYC Stand Up 10/15 - 10/16 2008

edit Posted by Brian Takita on Thursday October 16, 2008 at 02:25PM

10/15/2008

Help!

  • Autocompleters - Which one do we use?

    • We are currently using the YUI autocompleter from YUI 2.4.1 which works well. However, we are making significant architectural changes, and the "new" way used the YUI autocomplenet from 2.6.0. We don't want to install YUI just for the autocompleter and we don't want to deal with the version mismatch, so we are going to use a different autocompleter. It seems that there are many autocompleters, so which one do we use?

    • NOTE: We ended up going with the jQuery autocompleter. The autocompleter had issues with noConflict mode and with pressing the backspace key. We fixed them and will post the fixed version to Github soon.

Interesting

  • Postgres replication requires a primary key, and HABTM solutions typically do not use primary keys. Has many through is better anyway.

10/16/2008

Help!

  • CI build is slow and we don't know why

    • Our CI build is failing because of the performance issues. Some strange things we noticed:

    • There are many Sockets in the TIME_WAIT state

    • A possible solution is to run the OSX diskutils on the CI box.

    • Piston Hangs when importing a SVN dependency into a Git repo

Interesting

  • The Heavy team is coming for lunch to discuss process

  • git-svn is difficult to work with

    • Use all Git or all SVN if you can.
  • Polonium

    • Using click_and_wait fixes failures due to timeout on the CI box. It is more performant because click_and_wait is implemented in JS. The standard Polonium wait_for assertions poll into SeleniumRC.

NYC Stand Up 10/14 2008

edit Posted by Brian Takita on Tuesday October 14, 2008 at 02:00PM

A developer accidentally committed to an SVN external. Switching the SVN user to have read-only access to the external.

Casebook's ccrb + Funkytown + JsUnit build opens parallels on development boxes. The issue is probably with the Funkytown configuration.

Adam Keyes is coming in to talk to us during lunch today.

Casebook is moving to Git.

NYC Stand Up 10/13 2008

edit Posted by Brian Takita on Monday October 13, 2008 at 01:50PM

Interesting

"All your brunch is belong to us" - Damon McCormick

NYC Ruby Tomorrow

Erector 0.4.200 was released. New features include:

  • The Widget#join method, which joins an Array of text/widgets, with a separator which is text or a widget. This translates the standard idiom, without needing calls to raw.

    join [ "People", "Projects" ], " | "

  • Added a Pretty Printer which can be enabled by Erector::Doc.prettyprint_default = true (for example in development.rb)

JsUnit runs very slowly on the CI box on OSX and eventually times out, unless we open a VNC window to the machine. We tried upgrading to a more recent JsUnit, but couldn't get that to work at all.

Nanite looks really cool.

RR 0.6.0 Released

edit Posted by Brian Takita on Monday October 13, 2008 at 06:37AM

I'm pleased to announce the 0.6.0 version of RR. The changes include:

  • Declaring Double subject objects without having to pass it in via the mock!, stub!, dont_allow!, instance_of!, and proxy! methods
  • Revised Double chaining API
  • satisfy matcher
  • hash_including matcher

Declaring Double Subjects (The bang methods)

In previous versions of RR, you always needed to pass in the subject of the double. For example:

subject = Object.new
mock(subject).does_something {:and_returns_me}
subject.does_something # :and_returns_me

Now you can have RR automatically create the subject object for you by using the ! method:

subject = mock!.does_something {:and_returns_me}.subject
subject.does_something # :and_returns_me

Now the bang methods by themselves don't really add a whole lot, but when used in the context of Double chaining, they become a powerful addition.

Double Chaining

Nick Kallen presented the use case for Double chaining and contributed a patch for the 0.5.0 release of RR. It has proved useful and is now more fully incorporated into RR. Now you can pass in your subject or use the subject provided by RR by using the ! method. Here are some examples of Double Chaining:

mock(subject).first(1) {mock(Object.new).second(2) {mock(Object.new).third(3) {4}}}
subject.first(1).second(2).third(3) # 4

mock(subject).first(1) {mock!.second(2) {mock!.third(3) {4}}}
subject.first(1).second(2).third(3) # 4

mock(subject).first(1).mock!.second(2).mock!.third(3) {4}
subject.first(1).second(2).third(3) # 4

Of course you have access to the proxy facilities:

mock.proxy(User).find('1').mock.proxy!.children.mock.proxy!.find_all_by_group_id(10)
User.find('1').children.find_all_by_group_id(10) # Makes verifications pass and returns the actual children

You can also do branched Double chaining:

mock(subject).first do
  mock! do |expect|
    expect.branch1.mock!.branch11 {11} # or expect.branch1 {mock!.branch11 {11}}
    expect.branch2.mock!.branch22 {22} # or expect.branch2 {mock!.branch22 {22}}
  end
end
o = subject.first
o.branch1.branch11 # 11
o.branch2.branch22 # 22

Satisfy Matcher

Matthew O'Conner submitted a patch that added the satisfy matcher. This adds the ability to add arbitrary argument expectation matchers.

mock(object).foobar(satisfy {|arg| arg.length == 2})
object.foobar("xy")

Hash Including Matcher

Matthew O'Conner also submitted a patch that added the hash_including matcher. This adds a convenient way to assert that the passed-in hash includes certain key/value pairs.

mock(object).foobar(hash_including(:red => "#FF0000", :blue => "#0000FF"))
object.foobar({:red => "#FF0000", :blue => "#0000FF", :green => "#00FF00"})

Mailing list

RR has a mailing lists at:

Also, RR's rubyforge page is at http://rubyforge.org/projects/double-ruby and of course the github page is at http://github.com/btakita/rr.

Yes, and there is more to come

There are many interesting ideas floating around. Joseph Wilk has been playing around with adding Spies into RR. I'm also thinking about adding Double validation scoping into RR. Also, I'm impressed by Mocha's warning of unused stubs. Josh Susser also proposed having a mode where a warning would occur if a mocked method is not implemented on the subject being mocked.

If you have any feature requests, please send an email to the mailing list or add it to the rubyforge tracker.

Build Your Own Rails Plugin Platform with Desert

edit Posted by Brian Takita on Friday June 20, 2008 at 11:19PM

While it is easy to include plugins in your Rails projects, it isn't easy to extend and customize the plugin for your own application. Desert solves that limitation/complication by making it just as easy to extend or modify a plugin class as it would be with any other class. In this post we will go over how Desert provides an easy way to manage and extend your plugins.

At Pivotal, we offer an integrated platform of Rails plugins named Socialitis.

Socialitis is an internal project that grew out of the observation that many of our start-up clients needed to build the same non-differentiating features; user management, friends/contacts, activity feeds, on-site messaging, etc.

The Socialitis platform is broken up into number of plugins that extend the Rails app in specific ways. These plugins may have dependencies on other plugins.

One of the major design goals of Socialitis is easy, drop-in, integration into existing Rails apps. This means using convention over configuration and removing as much integration responsibility from the user of the plugin as possible.

Another design goal was to provide sensible defaults and make each plugin easy to customize for your app.

We used Desert to achieve these goals, and so can you for your own platform.

The major features that Desert provides are:

  • Defining a Rail's like directory structure into your plugin (models, views, controllers, helpers)
  • Plugin dependencies
  • Seamless overriding of classes and modules defined by parent plugins
  • Plugin migrations
  • Plugin routing

Desert provides a similar feature set to the Radient plugin system and the now defunct Appable Plugins framework.

For a simple example, lets say you have two plugins, name User and Messaging. The User plugin provides basic authentication and login features, and the Messaging plugin allows Users to send Messages to each other. The Message plugin depends on the User plugin.

The directory structure of the full Rails app looks like:

  |-- app
  |   |-- controllers
  |   |   |-- application.rb
  |   |   `-- blogs_controller.rb
  |   |-- helpers
  |   |   |-- application_helper.rb
  |   |   `-- blogs_helper.rb
  |   |-- models
  |   |   `-- user.rb
  |   `-- views
  |       |-- blogs
  |       |-- layouts
  |       |   `-- users.html.erb
  |       `-- users
  |           |-- index.html.erb
  |           `-- show.html.erb
  |-- db
  |   `-- migrate
  |       `-- 001_migrate_users_to_001.rb
  |-- lib
  |   `-- current_user.rb
  |-- spec
  |   |-- controllers
  |   |   `-- blogs_controller_spec.rb
  |   |-- fixtures
  |   |-- models
  |   |-- spec_helper.rb
  |   `-- views
  |       `-- blogs
  `-- vendor
      `-- plugins
          `-- user
              |-- app
              |   |-- controllers
              |   |   |-- logins_controller.rb
              |   |   `-- users_controller.rb
              |   |-- helpers
              |   |   |-- logins_helper.rb
              |   |   `-- users_helper.rb
              |   |-- models
              |   |   |-- login.rb
              |   |   `-- user.rb
              |   `-- views
              |       |-- logins
              |       |   |-- edit.html.erb
              |       |   |-- index.html.erb
              |       |   |-- new.html.erb
              |       |   `-- show.html.erb
              |       `-- users
              |           |-- edit.html.erb
              |           |-- index.html.erb
              |           |-- new.html.erb
              |           `-- show.html.erb
              |-- config
              |   `-- routes.rb
              |-- db
              |   `-- migrate
              |       `-- 001_create_users.rb
              |-- init.rb
              |-- lib
              |   `-- current_user.rb
              |-- spec
              |   |-- controllers
              |   |   `-- user_controller_spec.rb
              |   |-- fixtures
              |   |   `-- users.yml
              |   |-- models
              |   |   `-- user.rb
              |   |-- spec_helper.rb
              |   `-- views
              |       `-- users
              `-- tasks
          `-- message
              |-- app
              |   |-- controllers
              |   |   `-- message_controller.rb
              |   |-- helpers
              |   |   |-- message_helper.rb
              |   |   `-- user_helper.rb
              |   |-- models
              |   |   |-- message.rb
              |   |   `-- user.rb
              |   `-- views
              |       `-- messages
              |           |-- edit.html.erb
              |           |-- index.html.erb
              |           |-- new.html.erb
              |           `-- show.html.erb
              |-- config
              |   `-- routes.rb
              |-- db
              |   `-- migrate
              |       `-- 001_create_messages.rb
              |-- init.rb
              |-- spec
              |   |-- controllers
              |   |   |-- message_controller_spec.rb
              |   |   `-- user_controller_spec.rb
              |   |-- fixtures
              |   |   |-- messages.yml
              |   |   `-- users.yml
              |   |-- models
              |   |   |-- message_spec.rb
              |   |   `-- user_spec.rb
              |   |-- spec_helper.rb
              |   `-- views
              |       `-- messages
              `-- tasks

The User plugin introduces the various User and Login Rails objects. The Message plugin introduces its respective Message objects. Notice that the Message plugin also reopens some of the User objects to insert functionality.

For example, vendor/plugins/users/app/models/user.rb looks something like:

class User < ActiveRecord::Base
  has_many :logins
end

The Message plugin would then reopen User in vendor/plugins/message/app/models/user.rb:

 class User < ActiveRecord::Base
   has_many :messages_received
   has_many :messages_sent
 end

Meanwhile, the main application can also reopen User in app/models/user.rb

 class User < ActiveRecord::Base
   def custom_app_method
     # custom app logic #
   end
 end

Desert allows you to utilize Ruby's ability to repoen classes to layer on functionality in your plugins and application. At Pivotal, we have had success in sharing code across multiple client applications using this technique.

Another thing to note is normally the Message plugin would be loaded before the User plugin. Desert allows you to create plugin dependencies. So in vendor/plugins/message/init.rb:

require_plugin 'user'
require_plugin 'will_paginate'

This means you no longer need to define plugin load order inside of environment.rb. Your plugins can take care of that. Desert works with practically all plugins. That means you can have a plugin dependency on any existing Rails plugin.

To see more examples & documentation, take a look at the Desert project at http://github.com/pivotal/desert.

jQuery & greasemonkey while being considerate to John's bandwidth

edit Posted by Brian Takita on Wednesday February 27, 2008 at 09:21AM

Here is a slight modification to Joan Piedra's jQuery greasemonkey script that loads the jQuery library on any page you visit. This gives you the power of jQuery when using Firebug or Greasemonkey. Here is an example of its usage.

var jQueryDefinition = (function() {/*jQuery library*/});
var GM_JQ = document.createElement('script');

GM_JQ.innerHTML = '(' + jQueryDefinition.toString() + ')();';
GM_JQ.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(GM_JQ);

Here is the full script. Here is the userscrips page.

Make sure that the jquery.user.js script is loaded before any other dependent Greasemonkey scripts.

The difference is you can put the jQuery library into the script itself. This causes the page to load faster and does not use up John Resig's bandwidth. Unfortunately, this also means you will need to manually upgrade your greasemonkey script when the next version of jQuery comes out.

[Update] Edited the Greasemonkey script to make jQuery compatible with other js libraries. [Update] Pointing to userscripts.org

Introducing RR

edit Posted by Brian Takita on Monday December 31, 2007 at 10:30PM

I'm pleased to introduce a new Test Double (or mock) framework named RR, which is short for Double Ruby.

Why a Double framework and not a Mock framework?

A mock is a type of test double. Since RR supports mocks, stubs, and proxies, it makes sense to refer to RR as a double framework. The proxy is a new usage pattern that I will introduce later in this article, and in more detail in future articles.

Unfortunately, the terminology over doubles has been contradictory depending on the framework. RR's terminology tries to be as faithful as possible to Gerald Meszaros' definition of test doubles. You can read more about test doubles in http://xunitpatterns.com/Test2e.html and Martin Fowler's article, http://martinfowler.com/articles/mocksArentStubs.html. Regretfully, this does mean that RR will have slightly different terminology than other double frameworks.

How does RR compare to other Mock frameworks?

Most double frameworks focus mainly on mocks (hence the categorization "mock framework"). RR's focus is on enabling more double test patterns in a terse and readable syntax.

RR also does not have dedicated mock objects. It primarily uses the technique called 'double injection'. Names that other frameworks use are 'stub injection', 'mock object injection', 'partial mocking', or 'stubbing'. The term I'll use for this is a double injection, since one or many doubles are being injected into an object's method.

I'll use trivial Rails examples to highlight the syntactical differences between RR, Mocha, Rspec's mocks, and Flexmock. They may or may not be appropriate situations for mocks. The right situations for mocks is an entirely different discussion.

If there is better way to do any of the examples, please post a comment and I will gladly replace it.

Mocks

Here are the ways to mock the User.find method. The expectation is the User class object will receive a call to #find with the argument '99' once and will return the object represented by the variable user.

RR
mock(User).find('99') { user }
Mocha
User.expects(:find).with('99').returns(user)
spec/mocks
User.should_receive(:find).with('99').and_return(user)
Flexmock
flexstub(User).should_receive(:find).with('99').and_return(user).once

Stubs

Here are the ways to stub the User.find method. When the User class object receives a call to find with the argument '99' it will return user1. When User receives find with any other arg, it returns user2.

RR
stub(User).find('99') { user1 }
stub(User).find { user2 }
Mocha
User.stubs(:find).with(anything).returns(2)
User.stubs(:find).with('99').returns(1)
spec/mocks
users = {
  '99' => user1,
  'default' => user2
}
User.stub!(:find).and_return do |id|
  users[id] || users['default']
end
Flexmock
users = {
  '99' => user1,
  'default' => user2
}
flexstub(User).should_receive(:find).and_return do |id|
  users[id] || users['default']
end

Proxy

A proxy used with a mock or stub causes the real method to be called. Expectations can be placed on the invocation and the return value can be intercepted. The main rationales are test clarity and you can ensure that the methods are being called correctly, even after you refactor your code. I will delve more into proxies and their usage patterns in my next article.

Mock Proxy

The following examples set an expectation that User.find('99') will be called once. The actual user is returned.

RR
mock.proxy(User).find('99')
Mocha

You cannot implement this in Mocha. You can do an approximation in this situation however. This technique is not always the solution you need, though.

user = User.find('99')
User.expects(:find).with('99').returns(user)
spec/mocks
find_method = User.method(:find)
User.should_receive(:find).with('99').and_return(&find_method)
Flexmock
find_method = User.method(:find)
User.should_receive(:find).with('99').and_return(&find_method)

Stub Proxy

The following examples intercept the return value of User.find('99') and stub out valid? to return false.

RR
stub.proxy(User).find('99') do |user|
  stub(user).valid? {false}
  user
end
Mocha

Again, this is an approximation, since you cannot use proxies in Mocha.

user = User.find('99')
user.stubs(:valid?).returns(false)
User.stubs(:find).with('99').returns(user)
spec/mocks
find_method = User.method(:find)
User.stub!(:find).with('99').and_return do |id|
  user = find_method.call(id)
  user.stub!(:valid?).and_return(false)
  user
end
Flexmock
find_method = User.method(:find)
flexstub(User).should_receive(:find).with('99').and_return do |id|
  user = find_method.call(id)
  flexstub(user).should_receive(:valid?).and_return(false)
  user
end

instance_of

instance_of is method sugar than allows you to mock or stub instances of a particular class. The following examples mock instances of User to expect valid? with no arguments to be called once and return false.

RR
mock.instance_of(User).valid? {false}
Mocha
User.any_instance.expects(:valid?).returns(false)
spec/mocks
new_method = User.method(:new)
User.stub!(:new).and_return do |*args|
  user = new_method.call(*args)
  user.should_receive(:valid?).and_return(false)
  user
end
Flexmock
new_method = User.method(:new)
flexstub(User).should_receive(:new).and_return do |*args|
  user = new_method.call(*args)
  flexmock(user).should_receive(:valid?).and_return(false)
  user
end

More to come

This concludes the introduction to RR. RR enables some techniques, like proxying, that will make your tests clearer and less brittle. In the next article I will describe into patterns and techniques that will make mocks a more feasible tool for more situations.

Column Edit Mode in VI

edit Posted by Brian Takita on Thursday December 27, 2007 at 07:50AM

I've found that typing in column mode to be very useful when using editors like IntelliJ or TextMate. VI also has a column edit mode, that is a little tricky to use.

To use it, press:

  • Ctrl + V to go into column mode
  • Select the columns and rows where you want to enter your text
  • Shift + i to go into insert mode in column mode
  • Type in the text you want to enter. Dont be discouraged by the fact that only the first row is changed.
  • Esc to apply your change (or alternately Ctrl+c)

You will now see your changed applied.

Other articles: