 Sunday, February 03, 2008
Mary sent me this editorial from the new york times. UGH! Here's the first paragraph: At a New York or Los Angeles cocktail party, few would dare make a pejorative comment about Barack Obama’s race or Hillary Clinton’s sex. Yet it would be easy to get away with deriding Mike Huckabee’s religious faith. So, the first part of his argument is that Obama's race (what is it, really?) and Hillary Clinton's sex (can I say the same thing?) is exactly the same as Mike Huckabee's choice of invisible friend. Yeah, that makes sense. That's like saying that mocking goth kids is the exact same as making fun of children with down syndrome (note: I'm not condoning the active mocking of this girl more like mocking this girl). He then quickly switches to the standard argument of "look, everyone, we aren't burning people at the stake anymore. Evangelicals do some good." Here's a good quote: Scorning people for their faith is intrinsically repugnant, and in this case it also betrays a profound misunderstanding of how far evangelicals have moved over the last decade. Today, conservative Christian churches do superb work on poverty, AIDS, sex trafficking, climate change, prison abuses, malaria and genocide in Darfur. He starts off, of course, with making a statement as though it is a fact: "Scorning people for their faith is intrinsically repugnant..." Really? Why is this such a self-obvious fact? Explain to me why scorning people because they have a leftover from the invisible friends of childhood is "intrinsically repugnant." I love the line "...how far evangelicals have moved over the last decade." Oh, I'm sorry, I didn't realize they had much such strides in their humanitarianism OVER THE LAST DECADE! He goes on to explain how evangelicals have FINALLY started putting fighting poverty over abortion as their issue of choice. WOW! Thanks, christies! As he says, they used to be mean (Falwell describing AIDS as "God's judgement against promiscuity"), but now, oh man, they are so nice and interested in helping people now! He then goes on to talk about Rick Warren and how fantastic it is that he and his megachurch are finally getting around to helping people. I wonder what percentage of their profits are used in pure humanitarian aid and what percentage are used to further their own aims of poisoning the minds of our youth. Well, the rest of the column is just the same old attempts to give examples of how the christies are doing good and we should be thankful for it. We've heard these arguments before, and they follow the same pattern: give examples of how there are some nice christee organizations, not bothering to mention all the of the negative that is done, all the while ignoring the secular organization doing exactly the same task without the negative overhead of an invisible friend telling you that you have the truth over the person you are trying to help.
 Saturday, February 02, 2008
Well, as I mentioned in part I and part II, I'm building an application track my coupon savings, specifically Entertainment Book coupons, called Coupon Tracker. I've got a couple people signed up, but there is still lots of room for people to try it out. Just go on and go sign up. Today, I got a day to work on it, so I spent the day finishing up my specs for remove, as well as adding some specs for better user feedback if a coupon can't be saved for some reason. I also discovered the form.date_select helper, which creates a nice set of drop-down selections for choosing a date. Before this, the date field was a text box, which makes it way to easy to put in a date wrong. I also added a What's New page, along with some other stuff. I also got to play a bit with some unnecessary AJAX with script.aculo.us to make a title pulsate a bit. Here's the list from the what's new page of what I did today: 2008-02-02 - What's New (this page) added
- Added the ability to delete a coupon. Just click the x next to the coupon in the list
- Put some validation feedback on the coupon to give feedback on why the coupon wasn't saved
- Changed "Date Used" input to an easier input format (drop-down selections)
- Sorted coupons from newest to oldest
- Added header to show how many coupons for how many users we track
I'm learning a lot of rails as I go along, so I'm definitely making this well worth my while. I also recorded a screencast of using Coupon Tracker to see how easy it is right now.
 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
 Wednesday, January 23, 2008
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.
 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 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.
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.
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/
 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.
[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.
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.
 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:
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.
 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!
© Copyright 2008 Corey Haines
Theme design by Bryan Bell
newtelligence dasBlog 2.0.7226.0  | Page rendered at Tuesday, December 02, 2008 7:56:24 AM (Eastern Standard Time, UTC-05:00)
Pick a theme:
|
On this page....
| | Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|
| 27 | 28 | 29 | 30 | 31 | 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 | 1 | | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
Search
Navigation
Categories
Blogroll
Sign In
|