Mike Vincent

use it or lose it.

Lone Star Ruby Conf 2007

September 9th, 2007

Really glad I was able to attend the Lone Star Ruby Conference. It’s always great to get to meet people I’ve either never met or only in some online fashion. To highlight some of the things I most enjoyed about this particular conference…

  • Only 200 attendees, so it retained that small community feel.
  • I kind of enjoyed the single track style for a change. Sure there might have been less mingling than if we were constantly shuffling from track to track, but I served to help get to know the people you were around much better as well. I’m also pretty certain that I would have missed at least a couple of what turned out to be my favorite talks if I had been given choices.
  • The food was pretty good, pats on the back to those responsible!
  • While I didn’t win anything, the give-a-ways were quite nice.
  • plenty of power outlets, bandwidth was lacking but I’ve yet to see that any different at any conference. DeVue video from Gregg Pollack and Jason Seifer, the guys from Rails Envy. Django vs Rails. To be released Monday, it’s a must see; very funny.
My personally favorite talks were…
  • James Edward Gray II talked about using ruby as glue, which I’ve done my share of. Lots of good tips and a reminder that the best solution to a problem is probably simpler than you think if you look to using the existing tools around you.
  • David Chelimsky’s talk on rspec/bdd with live demonstration. I <3 the live demo, anyone that does something like that well tends to make their talk magnitudes more interesting, even more so with audience participation. Great job, David.
  • However you may feel about the subject matter, HD Moore’s talk about metasploit was pretty interesting. I’ll leave it at that. :)
  • PJ Hyett’s talk was refreshing for the first talk of saturday morning and it was soem practical advice for making your site successful.
  • Patrick Farley gave what I think was ultimately the most rewarding talk for me. He actually went in depth showing and discussing ruby’s internals to illustrate how ruby handles classes, modules, methods, etc. I found it very interesting and now I’m going to have to spend some time looking at the internals too. :) As well as projects like Jruby and rubinius.
  • Must give props to fellow DFW homie and glue of dallas.rb, Adam Keys I’ve long come to the same conclusion about the need for at least 3 people on a team.
  • Chris Wanstrath’s talk was also pretty awesome. He kept a good focus on BDD being about testing (specing, I use them interchangeably, too ;) behavior and not implementation. I thought he did a pretty good job describing what the difference is and why you focus on behavior.
  • Charles Nutter of Jruby fame was interesting too. (bonus points for live demos)
  • Zed.. The final (anti)keynote from Zed Shaw, simulcasted on IRC, was very good and entertaining. I plan to read up more on statistics and R sometime soon.
There seemed to be a (sometimes not so) subtle recurring theme in the talks too.
  • Don’t be an asshole, and we are probably all assholes at some time.
  • We’re all capable people, exercise your capabilities
  • Don’t be sheep

Definitely see myself going to this again next year.

LSRC 2007

September 5th, 2007

Having attended SXSW and RailsConf this year, I'm set to head to Austin tomorrow afternoon for the Lone Star Ruby Conference. Looking forward to meeting up with more folks and seeing/learning some new and interesting things. So be on the lookout for the guy with a non-mac/non-windows notebook, it's probably me.

See you there!

Extending Rspec

August 30th, 2007

UPDATE2: Don't Do this.. Don't try this.. Just move along, nothing to see here. I'll make a follow up post explaining why when I get a chance.

UPDATE: update: r2536 in rspec trunk renamed BehaviorApi to ExampleApi, I updated the code below to reflect that.

Last night a fella in #rspec on freenode was inquiring how to specify an ActiveRecord attribute should be unique. The advice given was to save the object being specified and attempt to create another object, duplicating the attribute in question's value, then specify the new object should not be valid and should contain an error against said attribute.

This is how I do it as well. But, I've often itched to just encapsulate it into a custom matcher so I wasn't repeatidly cluttering my specs with that setup each time I wanted to specify a unique attribute.

So I made a spec/my_matchers.rb file and required it from my spec/spec_helper.rb as well as adding a punch to the behavior api so that it gets included.

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
47
48
49
50
51
52
53
54
55

# my_matchers.rb
module My
  module SpecMatchers    
    class UnsavedRecord < StandardError; end
    class HaveAUnique  #:nodoc:

      def initialize(attribute, opts = {})
        @attribute = attribute
        @options = {:case_sensitive => true}.merge(opts)
      end

      def matches?(obj)
        raise UnsavedRecord, "Can only check uniqueness against saved records, save your object first." if obj.new_record?
        @value = obj.send(@attribute)
        klass = obj.class
        new_obj = klass.new(@attribute => @value)
        new_obj.valid?
        ok = !new_obj.errors[@attribute].nil?
        # check if it should be unique even with different casing
        if ok && !@options[:case_sensitive]
          new_obj = klass.new(@attribute => flip_case(@value))
          new_obj.valid?
          ok = !new_obj.errors[@attribute].nil?
        end
        ok
      end

      def flip_case(string)
        string.size.times do |i|
          string[i] = string[i].chr.send(:upcase) == string[i].chr ? string[i].chr.send(:downcase) : string[i].chr.send(:upcase)
        end
        string
      end

      def failure_message
        msg = "expected #{@attribute} to be unique, but was able to create another object having #{@attribute} => #{@value} without errors\n"
      end

      def negative_failure_message
        failure_message
      end

      def description
        "unique attribute value"
      end

    end

    def have_a_unique(*exp)
      HaveAUnique.new(*exp)
    end

  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

# somewhere in my spec/spec_helper.rb
require File.dirname(__FILE__) + '/my_matchers.rb'

# Open up the behaviors api and stick our custom matchers in
# module Spec::DSL::BehaviourAp
# update: r2536 in rspec trunk renamed BehaviorApi to ExampleApi, so now it's...
module Spec::DSL::ExampleApi
  def before_eval
    module_eval do 
      include My::SpecMatchers
    end
  end
end

# I -think- this is how you'd do it using rspec versions < 1.0.8
# the project I did this within, I'm using trunk. 
# class Spec::DSL::Behaviour
#   def before_eval
#     @eval_module.include My::SpecMatchers
#   end
# end

That's pretty much it.. So now I can write a spec like..

1
2
3
4
5
6
7
8
9
10
11
12
13
14

describe Sompn do
  before do
    @sompn = Sompn.create!(:name => 'foo', :phone => '123-555-1212')
  end

  it "should have a unique case insensitive name" do
    @sompn.should have_a_unique(:name, :case_sensite => false)
  end

  it "should have a unique phone" do
    @sompn.should have_a_unique(:phone)
  end
end

Gives me further ideas to clear up my specs.. Maybe a have_a(attribute, {:required => false}} matcher that simply specifies the presence of an attribute and whether it is actually required to be present. That'd be just as nifty.. let's do it! Open up the my_matchers.rb file and add this after the have_a_unique definition.

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

    # Have a ....
    class HaveA #:nodoc:

      def initialize(attribute, opts = {})
        @attribute = attribute
        @options = {:required => false}.merge(opts)
      end

      def matches?(obj)
        @klass = obj.class
        @exist = obj.respond_to?(@attribute.to_sym)
        if @options[:required]
          obj.send("#{@attribute}=",nil) 
          obj.valid?
          return !obj.errors[@attribute].nil?
        end
        @exist
      end

      def failure_message
        if @options[:required] && @exist
          "expected #{@attribute} to be required but having a nil value is valid"
        else
          "expected #{@klass} object to respond to #{@attribute}"
        end
      end

      def negative_failure_message
        failure_message
      end

      def description
        "unique attribute value"
      end

    end

    def have_a(*exp)
      HaveA.new(*exp)
    end

And now I specify attributes exist, possibly even required like..

1
2
3
4
5
6
7
8
9
10

describe Sompn do
  it "should have a foo, but not require it" do
    @sompn.should have_a(:foo)
  end

  it "should require a bar" do
    @sompn.should have_a(:bar, :required => true)
  end
end
So there we go.. custom matchers for the cleaner, clearer specs.
UpdateOh, I know the site's layout and all is pretty crappy.. It'll be getting some loving soon, I hope, it's been on the todo list for a long time already.

xit for rspec

August 12th, 2007

Update: As of revision 2530 they have added the xit feature to rspec's trunk so the next release will find it baked in.

Edit: So no sooner do I make a post, and there's a new rspec release and I realize the way I thought the new 'pending' feature worked was mistaken. So I fixed the post to reflect what actually works.

Adam Keys asked if there was an 'xit' feature in rspec, citing that you could mark specs as pending or incomplete or some such by prefixing an 'x' to them with test/spec. I told him if he commented out the block in his spec rspec would denote it as incomplete. But It was pretty easy to implement for rspec by dropping this in your spec_helper.rb:
1
2
3
4
5
6
7

# Omit passing the specification's block into Example so it will be marked incomplete/pending
module Spec::DSL::BehaviourEval::ModuleMethods
  def xit(description=:__generate_description, opts={})
    examples << Spec::DSL::Example.new(description, opts)
  end
end
Or, even better, using Rspec's new pending feature:
1
2
3
4
5
6
7
8
9
10
11

# Using Rspec > 1.0.6 'pending' feature, which will raise an exception if the specification block does NOT fail
module Spec::DSL::BehaviourEval::ModuleMethods
  def xit(description=:__generate_description, opts={}, &block)
    examples << Spec::DSL::Example.new(description, opts) do
      pending "temporarily disabled" do
        instance_eval(&block)
      end
    end
  end
end
Quick contrived example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# roman_spec.rb
module Spec::DSL::BehaviourEval::ModuleMethods
  def xit(description=:__generate_description, opts={}, &block)
    examples << Spec::DSL::Example.new(description, opts) do
      pending "temporarily disabled" do
        instance_eval(&block)
      end
    end
  end
end

describe "The letter 'I'" do
  it "should be 'i' in lower case" do
    "I".downcase.should eql("i")
  end

  xit "should respond to :to_roman" do
    "I".should respond_to(:to_roman)
  end
end
Produces the following specdoc output
1
2
3
4
5
6
7
8
9
10
11

The letter 'I'
- should be 'i' in lower case
- should respond to :to_roman (PENDING: temporarily disabled)

Finished in 0.005163 seconds

2 examples, 0 failures, 1 pending

Pending:
The letter 'I' should respond to :to_roman (temporarily disabled)
If we later add the to_roman method to string, eg:
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

# roman_spec.rb
module Spec::DSL::BehaviourEval::ModuleMethods
  def xit(description=:__generate_description, opts={}, &block)
    examples << Spec::DSL::Example.new(description, opts) do
      pending "temporarily disabled" do
        instance_eval(&block)
      end
    end
  end
end

class String
  def to_roman
  end
end

describe "The letter 'I'" do
  it "should be 'i' in lower case" do
    "I".downcase.should eql("i")
  end

  xit "should respond to :to_roman" do
    "I".should respond_to(:to_roman)
  end
end
and rerun the spec, we get..
1
2
3
4
5
6
7
8
9
10
11
12
13

The letter 'I'
- should be 'i' in lower case
- should respond to :to_roman (ERROR - 1)

1)
'The letter 'I' should respond to :to_roman' FIXED
Expected pending 'temporarily disabled' to fail. No Error was raised.
/home/mike/projects/logn/roman_spec.rb:6:in `xit'

Finished in 0.005257 seconds

2 examples, 1 failure

It does have that annoyance that the error response is somewhat ambiguous because it's the xit method that is failing. It would be nice to know the line number, etc for the pending specs.

dallas.rb

December 5th, 2006

I finally made the time to catch a dallas.rb meeting last night. Great people. I'm pretty certain I'll be coming more frequently from now on. Last night's meeting was pretty much an ad hoc series of discussions for a couple of hours ranging from the Comatose micro CMS plugin for rails to The ErLang Movie. Looking forward to next year's meet.
Mike Vincent uses the Shay theme, and is Powered by Mephisto