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.
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!
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.
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.
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.