Saturday, January 26, 2008

Every year, Mary and I get an entertainment book for christmas. A few years ago, I decided to track how much I saved. Being a programmer, I decided to over complicate it, so, instead of using Excel, I wrote a javascript-based solution for a blogger-based blog. Basically, each blog entry was call to a javascript function that saved the data. The blog template had a little snippet of javascript that would sum up the savings for each blog entry's javascript call. It worked great. I think I saved around $150.

[Update: I just loaded up the old one on blogger, and it says that I saved $125.40, not quite $150. Also, if you are interested, you can look at the javascript in the page to see how I did it: nothing too impressive, though, especially with how mature the javascript implementations are these days.]

One thing I also noticed was that I was driven to use the entertainment book more. The next year, I didn't track the savings, and we hardly used it at all. I think that seeing a running total made me strive to take the total higher.

As I mentioned in part I, I am creating an application for this as a learning experience in Rails. Well, after working off and on for the last couple weeks, I have a working version of Coupon Tracker, a multi-user coupon tracking website that allows you to enter coupons with a description and tags. You have your own personal savings list, along with a tag cloud for them. The tags can be used to categorize them however you want. For example, why not have a tags Restaurants, Shopping, Entertainment, etc. Maybe you also want a set of tags that describe how likely you would go back. The sky is the limit.

If you would like to use it, please go to Coupon Tracker and register as a new user. Send me an email (when you register, the page has my email address) with your user name, and I will validate you as a new user. After that, just come back, login and start entering your savings. I would appreciate any and all feedback, as well, considering possible features.

One thing that I would like to add is a pledging piece: users can pledge to donate a certain percentage of their savings to charity. The site would track how much the user has pledged and how much the user has contributed.

It is still in its infancy, feature-wise, but I am constantly adding new features to get the site completely usable. Currently, the feature set includes the following:

  • Multi-user
  • Enter new coupon
  • Support for tags
  • List entered coupons and total savings

The list of features currently being prioritized for addition:

  • Delete a coupon
  • Edit a coupon
  • Filter list by tags
  • Custom sorting of coupon list
  • Export to CSV
  • Integration with charitable donation sites / paypal to donate part of your savings
Saturday, January 26, 2008 7:39:18 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]
 Wednesday, January 23, 2008
Wednesday, January 23, 2008 10:30:01 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]

I got an account with Heroku to host the rails app that I'm working on (a coupon tracker), and it is awesome! I have been developing on my local, then uploading the files to their site when I have my specs passing and my brief manual tests pass.

There was on gotcha, though. I recently added user authentication, which meant I had a bunch of files in different directories that needed to be uploaded. I could have re-imported my whole application, but that flushes your database, as it treats it like the initial setting up of the application. This sucks, as I'm using the app to actually track my real savings with the Entertainment Book.

I contacted their support about this. My suggestion was to allow me to upload a tarball into a root somewhere and have it expand it with overwrite. I got a reply back from James Lindenbaum there pretty promptly. He agreed that importing the application wouldn't work exactly for me, but he suggested the following:

- in the rake window, run "db:data:dump", this will create a db/data.yml file with your data in it
- download db/data.yml
- import your new code
- upload data.yml to db/
- in the rake window, run "db:data:load"

Well, I just did it, and it worked great! This is also a great way to just do a simple refresh, in case I've missed some stuff.

So, all-in-all, I'm very happy with Heroku, especially with their support.

Wednesday, January 23, 2008 10:06:10 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]
 Sunday, January 20, 2008

If you are going to use inject to sum up an attribute of an object (such as amount), make sure you give an initial value. Don't do this:

@total_savings = @coupons.inject { |sum, coupon| sum + coupon.amount }

because you will get an error:

undefined method `+' for #<Coupon:0x4398ee8>

It is trying to pass in the first coupon object as the initial sum parameter to your block. Instead, do this:

@total_savings = @coupons.inject(0) { |sum, coupon| sum + coupon.amount }

Then, it passes 0 as the first value of sum.

Obvious? Once you see it, it sure is, but this confused me for a bit. I hope it helps someone else in the future (probably me).

Sunday, January 20, 2008 10:40:47 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]
 Sunday, January 13, 2008

Well, the holidays filled up time that I would have spent working on my rspec blogging. I haven't forgotten, though. I'm going to be continuing the rspec-lunking series, as well as my quest to read lots and lots of Ruby code. Along the way, though, I'm writing a small rails app to keep track of my entertainment book savings. I did it in javascript and blogger a few years ago, and I got a lot of value out of it: I was able to track how much I had saved; I was stimulated to try new places; and, more importantly, I felt the positive feedback of how much I was savings caused me to use it more. So, I figured I'd start work on this application. I got an account on Heroku, so I have a place to upload my rails apps. I'm not 100% sure what level I'll go blogging it, but I'll definitely track my experiences in one form or another.

The general idea for the application is to keep track of coupon usages. I would also like to have a rating engine of some sort in it, so I can give a small review and a rate to restaurants/services I discover. Eventually, it would be cool if I could make it multi-user application.

Along with that, I want to track the amount saved each time, as well as a running total. Eventually, categories would be cool, so I could get more fine-grained subtotals.

Where to start? Well, I guess at the beginning.

Sunday, January 13, 2008 2:23:42 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]

This really is a great list.

10 - You vigorously deny the existence of thousands of
gods claimed by other religions, but feel outraged
when someone denies the existence of yours.


9 - You feel insulted and "dehumanized" when
scientists say that people evolved from other life
forms, but you have no problem with the Biblical claim
that we were created from dirt.

8 - You laugh at polytheists, but you have no problem
believing in a Triune God.

7 - Your face turns purple when you hear of the
"atrocities" attributed to Allah, but you don't even
flinch when hearing about how God/Jehovah slaughtered
all the babies of Egypt in "Exodus" and ordered the
elimination of entire ethnic groups in "Joshua"
including women, children, and trees!

6 - You laugh at Hindu beliefs that deify humans, and
Greek claims about gods sleeping with women, but you
have no problem believing that the Holy Spirit
impregnated Mary, who then gave birth to a man-god who
got killed, came back to life and then ascended into
the sky.

5 - You are willing to spend your life looking for
little loopholes in the scientifically established age
of Earth (few billion years), but you find nothing
wrong with believing dates recorded by Bronze Age
tribesmen sitting in their tents and guessing that
Earth is a few generations old.

4 - You believe that the entire population of this
planet with the exception of those who share your
beliefs -- though excluding those in all rival sects -
will spend Eternity in an infinite Hell of Suffering.
And yet consider your religion the most "tolerant" and
"loving."

3 - While modern science, history, geology, biology,
and physics have failed to convince you otherwise,
some idiot rolling around on the floor speaking in
"tongues" may be all the evidence you need to "prove"
Christianity.

2 - You define 0.01% as a "high success rate" when it
comes to answered prayers.  You consider that to be
evidence that prayer works.  And you think that the
remaining 99.99% FAILURE was simply the will of God.

1 - You actually know a lot less than many atheists
and agnostics do about the Bible, Christianity, and
church history - but still call yourself a Christian.

Sunday, January 13, 2008 10:38:03 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]

I ran into Russ the last day of codemash (I'll be posting my ravings about codemash soon), and I mentioned that I would put a link to the cleveland ruby user group. Here's the link: http://ruby.meetup.com/119/

Sunday, January 13, 2008 10:28:15 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]
 Thursday, December 27, 2007

[Note: Due to length, this post covers only the beginning of the run method, up to getting the examples_to_run. It will be continued in future posts.]

Here we are at run. We aren't going to look through the runners themselves; you'll have to trust me that I looked at ExampleGroupRunner, and it calls the run method on the ExampleGroup. :) Let's get down and dirty with ExampleGroup now. Naturally, the actual run method is in module ExampleGroupMethods. Here's run:

     # File lib/spec/example/example_group_methods.rb, line 110
110:       def run
111:         examples = examples_to_run
112:         return true if examples.empty?
113:         reporter.add_example_group(self)
114:         return dry_run(examples) if dry_run?
115: 
116:         plugin_mock_framework
117:         define_methods_from_predicate_matchers
118: 
119:         success, before_all_instance_variables = run_before_all
120:         success, after_all_instance_variables  = execute_examples(success, before_all_instance_variables, examples)
121:         success                                = run_after_all(success, after_all_instance_variables)
122:       end
examples_to_run isn't in the rdoc, but it is in the sourcecode for ExampleGroupMethods. Here it is:

def examples_to_run
  all_examples = examples
  return all_examples unless specified_examples?
  all_examples.reject do |example|
    matcher = ExampleMatcher.new(description.to_s, example.description)
    !matcher.matches?(specified_examples)
  end
end

Wow, I don't know about you, but I like the rdoc format much better (I copied this out of aptana). I wonder if there is an option on rdoc to include private methods. I could regenerate the rdocs to include them. Let me take a look. Sure enough, it does have a -all option. I wonder what would be the best way to generate the docs. Well, I went into the lib\spec folder and executed the following line:

rdoc -S -N -a

It sure did a lot. Let's go see. Sweet. It included the private methods. So, I now have a rdoc that contains what I need.

Here's a better view of examples_to_run

     # File example/example_group_methods.rb, line 304
304:       def examples_to_run
305:         all_examples = examples
306:         return all_examples unless specified_examples?
307:         all_examples.reject do |example|
308:           matcher = ExampleMatcher.new(description.to_s, example.description)
309:           !matcher.matches?(specified_examples)
310:         end
311:       end

I do notice that rdoc still doesn't include have everything, as it doesn't have the examples method. Looking in Aptana, we see the following in example_group_methods:

def examples #:nodoc:
  examples = example_objects.dup
  add_method_examples(examples)
  rspec_options.reverse ? examples.reverse : examples
end

Ah, that #:nodoc: might be a good reason it doesn't show up in the rdoc. :) I can't seem to find an option in rdoc to override this, but I'm not going to look too hard.

The reason this takes a dup of example_objects is that the rest of method changes the list, adding method examples, and, if the reverse flag is set, the examples are run in reverse order. This is done via an intrusive update to the list, so we want to make a copy before we do something like that. Let's look at add_method_examples

     # File example/example_group_methods.rb, line 394
394:       def add_method_examples(examples)
395:         instance_methods.sort.each do |method_name|
396:           if example_method?(method_name)
397:             examples << new(method_name) do
398:               __send__(method_name)
399:             end
400:           end
401:         end
402:       end

Wow! This sorts the instance methods on the example group and loops them. If the method is an example method, it adds it to the list of examples. Wow, this is interesting code:

397:             examples << new(method_name) do
398:               __send__(method_name)
399:             end

So, this will create a new ExampleGroup with the method_name as the description and __send__(method_name) as the implementation. Wow! So, my understanding of this is that the method will be executed.

example_method? delegates to should_method?

     # File example/example_group_methods.rb, line 408
408:       def should_method?(method_name)
409:         !(method_name =~ /^should(_not)?$/) &&
410:         method_name =~ /^should/ && (
411:           instance_method(method_name).arity == 0 ||
412:           instance_method(method_name).arity == -1
413:         )
414:       end

At first, this looks like matcher methods to me, but it seems like these are actually being added to the list of examples to run, so they can't be that. I guess you can write methods that start with should(_not) and they will get run. It also makes sure that you don't take parameters. Let's dissect that regular expression to figure out the exact requirements:

!(a) && b && (c || d)

a = method should be either should or should_not with a ? on the end.

b = method name should start with should

c = 0 parameters

d = variable number of parameters (.arity == -1)

So, basically, it says that the method should either be a checker (should(_not)?) or a method that starts with should but has either no parameters or a variable number of parameters. Why the variable number of parameters? You can pass no parameters to those, as well. If we are going to run a method as an example, you don't want to have to provide a parameter. This could be a possible opening in the future to create parameterized tests.

Whew, we made it pretty far down a rabbit-hole, but let's get back to where we were when we started collecting up the examples:

     # File example/example_group_methods.rb, line 304
304:       def examples_to_run
305:         all_examples = examples
306:         return all_examples unless specified_examples?
307:         all_examples.reject do |example|
308:           matcher = ExampleMatcher.new(description.to_s, example.description)
309:           !matcher.matches?(specified_examples)
310:         end
311:       end

So, we've now got the all_examples list properly initialized, we return it unless specified_examples? Well, let's go take a look at this:

     # File example/example_group_methods.rb, line 313
313:       def specified_examples?
314:         specified_examples && !specified_examples.empty?
315:       end
     # File example/example_group_methods.rb, line 317
317:       def specified_examples
318:         rspec_options.examples
319:       end

So, if we have any examples off rspec_options and it isn't empty, then we have specified_examples. Where did this come from?

PS C:\ruby\lib\ruby\gems\1.8\gems\rspec-1.1.1> dir -Recurse | Select-String "rspec_options.examples"

lib\spec.rb:15:      @run || rspec_options.examples_run?
lib\spec\example\example_group_methods.rb:318:        rspec_options.examples

That seems very strange. Now, it is wrapped in specified_examples, so let's look for uses of that:

PS C:\ruby\lib\ruby\gems\1.8\gems\rspec-1.1.1\lib\spec> dir -Recurse -Include *.rb | select-string "specified_examples"

example\example_group_methods.rb:306:        return all_examples unless specified_examples?
example\example_group_methods.rb:309:          !matcher.matches?(specified_examples)
example\example_group_methods.rb:313:      def specified_examples?
example\example_group_methods.rb:314:        specified_examples && !specified_examples.empty?
example\example_group_methods.rb:317:      def specified_examples
example\example_matcher.rb:9:      def matches?(specified_examples)
example\example_matcher.rb:10:        specified_examples.each do |specified_example|

Ah, example matcher uses them. However, I don't see anyone actually adding to them.

Let's stop and think for a bit. The examples are off the rspec_options object. Let's search for uses of rspec_options with any sort of example stuff:

PS C:\ruby\lib\ruby\gems\1.8\gems\rspec-1.1.1\lib\spec> dir -Recurse -Include *.rb | select-string -Pattern "rspec_options\..*example.*"

example\example_group_methods.rb:160:        rspec_options.reverse ? examples.reverse : examples
example\example_group_methods.rb:240:        rspec_options.add_example_group self
example\example_group_methods.rb:244:        rspec_options.remove_example_group self
example\example_group_methods.rb:262:          rspec_options.reporter.example_started(example)
example\example_group_methods.rb:263:          rspec_options.reporter.example_finished(example)
example\example_group_methods.rb:318:        rspec_options.examples
runner\command_line.rb:19:            return $rspec_options.run_examples
runner\heckle_runner.rb:66:        success = @rspec_options.run_examples

Look at add_example_group and remove_example_group. This is in example_group_methods, which looks like it might be what we are interested in.

    # File runner/options.rb, line 71
71:       def add_example_group(example_group)
72:         @example_groups << example_group
73:       end

Okay, this starts to make sense. Let's go look in this for the examples property. Ah, it is a simple one created with attr_accessor.

For our purposes, example_group_methods.register is it. Now, who calls register. This seems like we could go back to previous parts of this serious to see it. I remember there being a register_behaviour in the old code, so it seems like we would have something similar here. Running a select for ".register" returns only a pointer to the comment about using register on the ExampleGroupFactory (example_group_factory.rb:13). Strange. So, my initial searching proves fruitless: it appears at this moment that nobody calls register on the ExampleGroup.

Looking in the specs for RSpec, I find these two specs:

describe '#register' do
        it "should add ExampleGroup to set of ExampleGroups to be run" do
          example_group.register
          options.example_groups.should include(example_group)
        end
      end

      describe '#unregister' do
        before do
          example_group.register
          options.example_groups.should include(example_group)
        end

        it "should remove ExampleGroup from set of ExampleGroups to be run" do
          example_group.unregister
          options.example_groups.should_not include(example_group)
        end
      end
end

Strange. Going to the root of rspec and doing a search for .register returns no uses of calling .register on an example_group except for the test. Could this be vestigial?

So, here's the point. If nothing ever gets added to example_groups, then the code that uses it is never called. So, anyone using specified_examples is going to always get false. The example_group_runner loops over them, but nobody adds. Very, very, very strange.

The important thing for this series (looping back around) is that we were inspecting the following method:

     # File example/example_group_methods.rb, line 304
304:       def examples_to_run
305:         all_examples = examples
306:         return all_examples unless specified_examples?
307:         all_examples.reject do |example|
308:           matcher = ExampleMatcher.new(description.to_s, example.description)
309:           !matcher.matches?(specified_examples)
310:         end
311:       end

So, if specified_examples? always returns false, then we will always just return all_examples. This excludes the loop that is rejecting examples. That looks strange anyway. We'll postpone the look at that.

Now, this post is getting really long, and we are only halfway through the primary method that we are investigating here:

     # File example/example_group_methods.rb, line 110
110:       def run
111:         examples = examples_to_run
112:         return true if examples.empty?
113:         reporter.add_example_group(self)
114:         return dry_run(examples) if dry_run?
115: 
116:         plugin_mock_framework
117:         define_methods_from_predicate_matchers
118: 
119:         success, before_all_instance_variables = run_before_all
120:         success, after_all_instance_variables  = execute_examples(success, before_all_instance_variables, examples)
121:         success                                = run_after_all(success, after_all_instance_variables)
122:       end

We still need to look at dry_run and then the rest of the setup before we even get to executing the samples. I'll continue in the next post.

Thursday, December 27, 2007 2:21:21 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]

[Note: Due to length, this post covers only the beginning of the run method, up to getting the examples_to_run. It will be continued in future posts.]

Here we are at run. We aren't going to look through the runners themselves; you'll have to trust me that I looked at ExampleGroupRunner, and it calls the run method on the ExampleGroup. :) Let's get down and dirty with ExampleGroup now. Naturally, the actual run method is in module ExampleGroupMethods. Here's run:

     # File lib/spec/example/example_group_methods.rb, line 110
110:       def run
111:         examples = examples_to_run
112:         return true if examples.empty?
113:         reporter.add_example_group(self)
114:         return dry_run(examples) if dry_run?
115: 
116:         plugin_mock_framework
117:         define_methods_from_predicate_matchers
118: 
119:         success, before_all_instance_variables = run_before_all
120:         success, after_all_instance_variables  = execute_examples(success, before_all_instance_variables, examples)
121:         success                                = run_after_all(success, after_all_instance_variables)
122:       end
examples_to_run isn't in the rdoc, but it is in the sourcecode for ExampleGroupMethods. Here it is:

def examples_to_run
  all_examples = examples
  return all_examples unless specified_examples?
  all_examples.reject do |example|
    matcher = ExampleMatcher.new(description.to_s, example.description)
    !matcher.matches?(specified_examples)
  end
end

Wow, I don't know about you, but I like the rdoc format much better (I copied this out of aptana). I wonder if there is an option on rdoc to include private methods. I could regenerate the rdocs to include them. Let me take a look. Sure enough, it does have a -all option. I wonder what would be the best way to generate the docs. Well, I went into the lib\spec folder and executed the following line:

rdoc -S -N -a

It sure did a lot. Let's go see. Sweet. It included the private methods. So, I now have a rdoc that contains what I need.

Here's a better view of examples_to_run

     # File example/example_group_methods.rb, line 304
304:       def examples_to_run
305:         all_examples = examples
306:         return all_examples unless specified_examples?
307:         all_examples.reject do |example|
308:           matcher = ExampleMatcher.new(description.to_s, example.description)
309:           !matcher.matches?(specified_examples)
310:         end
311:       end

I do notice that rdoc still doesn't include have everything, as it doesn't have the examples method. Looking in Aptana, we see the following in example_group_methods:

def examples #:nodoc:
  examples = example_objects.dup
  add_method_examples(examples)
  rspec_options.reverse ? examples.reverse : examples
end

Ah, that #:nodoc: might be a good reason it doesn't show up in the rdoc. :) I can't seem to find an option in rdoc to override this, but I'm not going to look too hard.

The reason this takes a dup of example_objects is that the rest of method changes the list, adding method examples, and, if the reverse flag is set, the examples are run in reverse order. This is done via an intrusive update to the list, so we want to make a copy before we do something like that. Let's look at add_method_examples

     # File example/example_group_methods.rb, line 394
394:       def add_method_examples(examples)
395:         instance_methods.sort.each do |method_name|
396:           if example_method?(method_name)
397:             examples << new(method_name) do
398:               __send__(method_name)
399:             end
400:           end
401:         end
402:       end

Wow! This sorts the instance methods on the example group and loops them. If the method is an example method, it adds it to the list of examples. Wow, this is interesting code:

397:             examples << new(method_name) do
398:               __send__(method_name)
399:             end

So, this will create a new ExampleGroup with the method_name as the description and __send__(method_name) as the implementation. Wow! So, my understanding of this is that the method will be executed.

example_method? delegates to should_method?

     # File example/example_group_methods.rb, line 408
408:       def should_method?(method_name)
409:         !(method_name =~ /^should(_not)?$/) &&
410:         method_name =~ /^should/ && (
411:           instance_method(method_name).arity == 0 ||
412:           instance_method(method_name).arity == -1
413:         )
414:       end

At first, this looks like matcher methods to me, but it seems like these are actually being added to the list of examples to run, so they can't be that. I guess you can write methods that start with should(_not) and they will get run. It also makes sure that you don't take parameters. Let's dissect that regular expression to figure out the exact requirements:

!(a) && b && (c || d)

a = method should be either should or should_not with a ? on the end.

b = method name should start with should

c = 0 parameters

d = variable number of parameters (.arity == -1)

So, basically, it says that the method should either be a checker (should(_not)?) or a method that starts with should but has either no parameters or a variable number of parameters. Why the variable number of parameters? You can pass no parameters to those, as well. If we are going to run a method as an example, you don't want to have to provide a parameter. This could be a possible opening in the future to create parameterized tests.

Whew, we made it pretty far down a rabbit-hole, but let's get back to where we were when we started collecting up the examples:

     # File example/example_group_methods.rb, line 304
304:       def examples_to_run
305:         all_examples = examples
306:         return all_examples unless specified_examples?
307:         all_examples.reject do |example|
308:           matcher = ExampleMatcher.new(description.to_s, example.description)
309:           !matcher.matches?(specified_examples)
310:         end
311:       end

So, we've now got the all_examples list properly initialized, we return it unless specified_examples? Well, let's go take a look at this:

     # File example/example_group_methods.rb, line 313
313:       def specified_examples?
314:         specified_examples && !specified_examples.empty?
315:       end
     # File example/example_group_methods.rb, line 317
317:       def specified_examples
318:         rspec_options.examples
319:       end

So, if we have any examples off rspec_options and it isn't empty, then we have specified_examples. Where did this come from?

PS C:\ruby\lib\ruby\gems\1.8\gems\rspec-1.1.1> dir -Recurse | Select-String "rspec_options.examples"

lib\spec.rb:15:      @run || rspec_options.examples_run?
lib\spec\example\example_group_methods.rb:318:        rspec_options.examples

That seems very strange. Now, it is wrapped in specified_examples, so let's look for uses of that:

PS C:\ruby\lib\ruby\gems\1.8\gems\rspec-1.1.1\lib\spec> dir -Recurse -Include *.rb | select-string "specified_examples"

example\example_group_methods.rb:306:        return all_examples unless specified_examples?
example\example_group_methods.rb:309:          !matcher.matches?(specified_examples)
example\example_group_methods.rb:313:      def specified_examples?
example\example_group_methods.rb:314:        specified_examples && !specified_examples.empty?
example\example_group_methods.rb:317:      def specified_examples
example\example_matcher.rb:9:      def matches?(specified_examples)
example\example_matcher.rb:10:        specified_examples.each do |specified_example|

Ah, example matcher uses them. However, I don't see anyone actually adding to them.

Let's stop and think for a bit. The examples are off the rspec_options object. Let's search for uses of rspec_options with any sort of example stuff:

PS C:\ruby\lib\ruby\gems\1.8\gems\rspec-1.1.1\lib\spec> dir -Recurse -Include *.rb | select-string -Pattern "rspec_options\..*example.*"

example\example_group_methods.rb:160:        rspec_options.reverse ? examples.reverse : examples
example\example_group_methods.rb:240:        rspec_options.add_example_group self
example\example_group_methods.rb:244:        rspec_options.remove_example_group self
example\example_group_methods.rb:262:          rspec_options.reporter.example_started(example)
example\example_group_methods.rb:263:          rspec_options.reporter.example_finished(example)
example\example_group_methods.rb:318:        rspec_options.examples
runner\command_line.rb:19:            return $rspec_options.run_examples
runner\heckle_runner.rb:66:        success = @rspec_options.run_examples

Look at add_example_group and remove_example_group. This is in example_group_methods, which looks like it might be what we are interested in.

    # File runner/options.rb, line 71
71:       def add_example_group(example_group)
72:         @example_groups << example_group
73:       end

Okay, this starts to make sense. Let's go look in this for the examples property. Ah, it is a simple one created with attr_accessor.

For our purposes, example_group_methods.register is it. Now, who calls register. This seems like we could go back to previous parts of this serious to see it. I remember there being a register_behaviour in the old code, so it seems like we would have something similar here. Running a select for ".register" returns only a pointer to the comment about using register on the ExampleGroupFactory (example_group_factory.rb:13). Strange. So, my initial searching proves fruitless: it appears at this moment that nobody calls register on the ExampleGroup.

Looking in the specs for RSpec, I find these two specs:

describe '#register' do
        it "should add ExampleGroup to set of ExampleGroups to be run" do
          example_group.register
          options.example_groups.should include(example_group)
        end
      end

      describe '#unregister' do
        before do
          example_group.register
          options.example_groups.should include(example_group)
        end

        it "should remove ExampleGroup from set of ExampleGroups to be run" do
          example_group.unregister
          options.example_groups.should_not include(example_group)
        end
      end
end

Strange. Going to the root of rspec and doing a search for .register returns no uses of calling .register on an example_group except for the test. Could this be vestigial?

So, here's the point. If nothing ever gets added to example_groups, then the code that uses it is never called. So, anyone using specified_examples is going to always get false. The example_group_runner loops over them, but nobody adds. Very, very, very strange.

The important thing for this series (looping back around) is that we were inspecting the following method:

     # File example/example_group_methods.rb, line 304
304:       def examples_to_run
305:         all_examples = examples
306:         return all_examples unless specified_examples?
307:         all_examples.reject do |example|
308:           matcher = ExampleMatcher.new(description.to_s, example.description)
309:           !matcher.matches?(specified_examples)
310:         end
311:       end

So, if specified_examples? always returns false, then we will always just return all_examples. This excludes the loop that is rejecting examples. That looks strange anyway. We'll postpone the look at that.

Now, this post is getting really long, and we are only halfway through the primary method that we are investigating here:

     # File example/example_group_methods.rb, line 110
110:       def run
111:         examples = examples_to_run
112:         return true if examples.empty?
113:         reporter.add_example_group(self)
114:         return dry_run(examples) if dry_run?
115: 
116:         plugin_mock_framework
117:         define_methods_from_predicate_matchers
118: 
119:         success, before_all_instance_variables = run_before_all
120:         success, after_all_instance_variables  = execute_examples(success, before_all_instance_variables, examples)
121:         success                                = run_after_all(success, after_all_instance_variables)
122:       end

We still need to look at dry_run and then the rest of the setup before we even get to executing the samples. I'll continue in the next post.

Thursday, December 27, 2007 2:21:08 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]

Hmm... That title looks awfully unimpressive: it. However, that simplicity is one of the beauties of Ruby: how the language let you write such cool stuff like:

it "should do blah blah blah" do
    do_it
    check_it
end

So, without further ado, let's jump in.

Looking at the rspec rdocs (making sure that I'm looking at the 1.1.1 docs), it creates a Spec::Example::Example and adds it to the current example group (the one creates by the describe method). Here's the code:

     # File lib/spec/example/example_group_methods.rb, line 97
 97:       def it(description=nil, &implementation)
 98:         e = new(description, &implementation)
 99:         example_objects << e
100:         e
101:       end

That new call is interesting to me. We are currently in example_group_methods inside of an ExampleGroup. If we look at initialize on ExampleGroup, it just sets the @_defined_description and @_implementation.

In SharedExampleGroup, it also calls describe and the like.

I just noticed this:

class SharedExampleGroup < Module

class ExampleGroup

Why is SharedExampleGroup < Module? I guess I assumed that SharedExampleGroup < ExampleGroup, but that isn't the case. I can see that Ruby doesn't enforce any sort of type hierarchy, since the types are so amorphous, but it makes sense from a design perspective. Unless, of course, I'm completely misunderstanding the relationship.

[NOTE: I posted a message to the rspec-users on this (I probably should have sent it to rspec-dev). I will post any information I found out as an update to this entry.]

In any case, the it method creates a new example group, adds it to the example_objects array (why isn't it @example_objects?). Oh, I see:

def example_objects
        @example_objects ||= []
end

Interesting. Lazy initialization of an instance variable. I will have to keep this pattern in mind, especially as I continue to look through the code here. For those who don't know, the ||= operator is shorthand for a = a || b. So, if a is nil, then the value of b is used, otherwise a is returned. Couple that with the fact that the return value of an assignment expression is the value assigned, we get a great pattern:

def return_and_assign
     a ||= b
end

If a is not nil, then it is assigned to itself and returned. If a is nil, then it gets the value of b and is returned. Cool. Put this into a method to retrieve an instance variable and you get lazy initialization.

In any case, this ends the overview of it. Basically, it creates a new Example, adds it to the example_objects instance variable and then returns the just created Example.

This ends the initial setup of your specs, getting ready for a runner to run them. Because I am impatient, we are going to skip the code for the runner and start investigating what happens in the code block that you pass to it: that is, your actual spec. This will lead us into matchers, which is where the action is.

Thursday, December 27, 2007 11:50:36 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]
 Sunday, December 23, 2007

Well, I know a lot of other people are using it, so I'm going to try twitter. For me, the idea, I guess, is to provide a means for quick updates that don't warrant a full blog post. Until I get around to redo my blog layout, here's the feed:

Sunday, December 23, 2007 4:28:19 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]

So, I've decided to go all-in and learn to use Aptana instead of making my judgement based on trying to use it without actually knowing anything about it. :)

However, following the tutorials on the aptana wiki, I went to create a new rails project, and it makes me choose my location. If I use the default location, then it appends my project name to it, but, if I choose my own, I have to duplicate the project name in both spots. UGH! And, I can't seem to figure out how to change the default location from the place in My Documents and put it to c:\ruby\rails_apps. I think this should be somewhere in the preferences for RadRails, but I don't see it. This should be obvious, I would think!


Well, I did a quick search in the configuration directory, and I find the following line in both the config.ini and config_vista.ini:

osgi.instance.area.default=@user.home/My Documents/Aptana Studio

So, it appears that I can change it in one of those spots.

I tried in config_vista.ini, and it didn't do it. Let's try config.ini.

Doesn't seem to have worked there, either.


Not only can't I figure out how to set the default location, but the new rails project doesn't seem to want to build a skeleton. I had hoped that, if I checked the option to create the rails skeleton, it would create the rails skeleton. It didn't. I'm going to try it also with setting the path to rails, even though it said I shouldn't have to.

Nope, that didn't help.

Sunday, December 23, 2007 11:27:32 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]
 Saturday, December 22, 2007

I've been having a hell of a time with my internet connection. I have Vista, and, yes, I do like it, but I've noticed that my internet connection has been very very very very slow for the past couple months. In fact, it seems like it has been getting worse and worse. I start out really fast, then watch it drop to single-digit k's. UGH! I've done my google-ing for "slow internet on vista" and "internet connection slowing down on vista" and found some stuff (disable autotuning on the tcp interface in netsh (didn't work for me)). Nothing worked. Well, I went into my wireless card configuration and fiddled a bit, but nothing seemed to work. Then, I noticed "WZC IBSS Channel Number" in the properties for the card. It was set on 11. Okay,  sounds fine, let me go look at my router: 6!!! I switched it to 11, and my connection is back up at the expected speed.

So, this post is really just to put this information up on the web in case someone else has this problem and stumbles upon this post. HEY, GOOGLE! INDEX AWAY!

Saturday, December 22, 2007 10:04:08 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]

As I mentioned last time, I went through the whole discussion on describe in version 1.0.8 of RSpec and 1.1.0 changed stuff. So, as promised, I'm going to spend some time outlining the differences.

The first part is that describe is no longer in kernel.rb, but is now in lib/spec/extensions/main.rb and is in the module Spec::Extensions::Main. Other than that, the describe method is pretty much the same:

    # File lib/spec/extensions/main.rb, line 22
22:       def describe(*args, &block)
23:         raise ArgumentError if args.empty?
24:         raise ArgumentError unless block
25:         args << {} unless Hash === args.last
26:         args.last[:spec_path] = caller(0)[1]
27:         Spec::Example::ExampleGroupFactory.create_example_group(*args, &block)
28:       end

except that it is using ExampleGroupFactory instead of BehaviourFactory. Also, it is interesting to note that we are not making a call to register_behaviour. This definitely makes it interesting to me, as I'm curious what create_example_group returns and how it runs. Let's go look!

    # File lib/spec/example/example_group_factory.rb, line 37
37:         def create_example_group(*args, &block)
38:           opts = Hash === args.last ? args.last : {}
39:           if opts[:shared]
40:             SharedExampleGroup.new(*args, &block)
41:           else
42:             superclass = determine_superclass(opts)
43:             superclass.describe(*args, &block)
44:           end
45:         end

Interesting. There's the same pattern again for defaulting to an empty hash. The rest is different than it was in BehaviourFactory.

SIDENOTE: I am very glad that they moved to ExampleGroup rather than Behaviour, as I got tired of having to correct my spelling of Behaviorour

Interesting, it only relies on opts[:shared] to create a SharedExampleGroup. I guess it creates a normal ExampleGroup in the next stuff. Here's SharedExampleGroup.new:

    # File lib/spec/example/shared_example_group.rb, line 39
39:       def initialize(*args, &example_group_block)
40:         describe(*args)
41:         @example_group_block = example_group_block
42:         self.class.add_shared_example_group(self)
43:       end

Interesting. It calls describe! Then, it caches the example_group_block and calls add_shared_example_group(self) on its class. I'm guessing this adds a class-level array so other example groups can find it by name. Looking into it, though, it has something strange:

   # File lib/spec/example/shared_example_group.rb, line 5
5:         def add_shared_example_group(new_example_group)
6:           guard_against_redefining_existing_example_group(new_example_group)
7:           shared_example_groups << new_example_group
8:         end

guard_against_redefining_existing_example_group(new_example_group) isn't defined anywhere obvious to me. It isn't on SharedExampleGroup or ExampleGroup, and I don't see it is ExampleGroupMethods, either! Where is it? Oh, wait, it isn't in the rdoc, but let's search:

PS C:\ruby\lib\ruby\gems\1.8\gems\rspec-1.1.0> dir -Recurse -Include *.rb | select-string "guard_against"

lib\spec\example\shared_example_group.rb:6:          guard_against_redefining_existing_example_group(new_example_group)
lib\spec\example\shared_example_group.rb:24:        def guard_against_redefining_existing_example_group(new_example_group)

Let's go load it up in Aptana. It is a private method:

private
def guard_against_redefining_existing_example_group(new_example_group)
  existing_example_group = find_shared_example_group(new_example_group.description)
  return unless existing_example_group
  return if new_example_group.equal?(existing_example_group)
  return if spec_path(new_example_group) == spec_path(existing_example_group)
  raise ArgumentError.new("Shared Example '#{existing_example_group.description}' already exists")
end

def spec_path(example_group)
  File.expand_path(example_group.spec_path)
end

First to note, though, is that this leads me to believe that private methods are not included in rdoc.

This isn't that exciting of a method, just doing what you expect it to do.

That is all fine and dandy, but notice that SharedExampleGroup.initialize() calls describe!

40: describe(*args)

Let's go find that. I'm going to guess that it is in ExampleGroupMethods. Sure enough.

According to the docs, this method

Makes the describe/it syntax available from a class. For example:

  class StackSpec < Spec::ExampleGroup
    describe Stack, "with no elements"

    before
      @stack = Stack.new
    end

    it "should raise on pop" do
      lambda{ @stack.pop }.should raise_error
    end
  end

This looks okay, but I am not totally sure right now why you would want to do this, but I'm not so interested right now to go looking. Let's look at the code:

    # File lib/spec/example/example_group_methods.rb, line 35
35:       def describe(*args, &example_group_block)
36:         if example_group_block
37:           self.subclass("Subclass") do
38:             describe(*args)
39:             module_eval(&example_group_block)
40:           end
41:         else
42:           set_description(*args)
43:           before_eval
44:           self
45:         end
46:       end

Notice here that it accepts a code block, but we didn't send it one. I'm guessing that will cause the if to false. Let's go into irb and see:

irb(main):024:0> def passme(*args, &block)
irb(main):025:1> if block
irb(main):026:2> true
irb(main):027:2> else
irb(main):028:2* false
irb(main):029:2> end
irb(main):030:1> end
=> nil
irb(main):031:0> passme
=> false
irb(main):032:0> passme do
irb(main):033:1* puts "hello"
irb(main):034:1> end
=> true
irb(main):035:0>

Sure enough, if you don't pass in block, the if will evaluate to true. Cool.

This means that we are currently in our else clause, which calls set_description, before_eval and returns self. Let's go look at before_eval...WHA? WHA? WHA?

def before_eval
end

Yoinks! That doesn't look like it does much. It sure isn't what I expected. Just looking at it, I would expect it to run the code block passed in to the before method. Hey, let's go look at it and see what is going on:

Alias for append_before

AHA! That looks like it will do what we want. So, you get the before_eval when the interpreter gets to the before call. I will spare you the code that follows before_eval, let's just say that it creates a code block and adds it to an array: either before_each_parts, before_all_parts, after_each_parts or after_all_parts. I will leave it as an exercise to the reader to guess what those are for.

Other than returning self, that is apparently the end of the rspec-1.1.0 changes to the describe method. Luckily, I hadn't made it too far down into the rabbit hole with 1.0.8 before I caught myself.

The cool thing about this is that it shows the creation of the ExampleGroup that is returned from the describe method. This makes me think that whatever runner there is simple calls a run, or execute, or the equivalent on the Example Group to actually run the tests.

Having gone through the first level in rspec, we are now at a slight decision point: should I go up (investigate the runner) or go down (start investigating the actual text of the test, itself). My gut tells me that the really interesting stuff lies farther inside, so I'm going to be head down the hole and look at it next. This will eventually lead us to matchers and some of the neater stuff in ruby, such as how I can write something like:

result.should == 5

Saturday, December 22, 2007 8:55:37 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]