Friday, August 31, 2007
Ruby: Adding a "not" method for readability
The other day I was working with an if statement that looked similar to the snippet below.
I spent enough time in C, C#, etc to be able to parse the if statement fairly easily; however, I thought it would improve readability if I could write the following snippet instead.
Adding a "not" method to Object turned out to be fairly easy. I started with the following tests (block syntax provided by dust).
The implementation of the "not" method is similar to the implementation of the "as" method provided by facets (an explanation of the "as" implementation can be found in a previous entry).
The Object.not method returns an instance of Object::Not. The Object::Not instance is initialized with the subject (the instance that was sent the "not" message). The Object::Not instances are basically proxies that send all calls back to the original instance. The Object::Not class privatizes almost all of it's methods so that most method calls will be handled by method_missing. The method_missing implementation simply forwards on any method call to the subject and negates the result.
if !response.incomplete? && !response.invalid? && response.total > 0
...
endI spent enough time in C, C#, etc to be able to parse the if statement fairly easily; however, I thought it would improve readability if I could write the following snippet instead.
if response.not.incomplete? && response.not.invalid? && response.total > 0
...
endAdding a "not" method to Object turned out to be fairly easy. I started with the following tests (block syntax provided by dust).
unit_tests do
test "not negates true to false" do
assert_equal false, nil.not.nil?
end
test "not negates false to true" do
assert_equal true, Object.new.not.nil?
end
endThe implementation of the "not" method is similar to the implementation of the "as" method provided by facets (an explanation of the "as" implementation can be found in a previous entry).
define_method :not do
Not.new(self)
end
private *instance_methods.select {|m| m !~ /(^__|^\W|^binding$)/ }
@subject = subject
end
!@subject.send(sym,*args,&blk)
end
end
endThe Object.not method returns an instance of Object::Not. The Object::Not instance is initialized with the subject (the instance that was sent the "not" message). The Object::Not instances are basically proxies that send all calls back to the original instance. The Object::Not class privatizes almost all of it's methods so that most method calls will be handled by method_missing. The method_missing implementation simply forwards on any method call to the subject and negates the result.
Sunday, August 26, 2007
Ruby: Calling methods of a specific ancestor
Following the recent post, State pattern using Modules and Facets, Aman King asked:
Let's look at an example of a classes' ancestors.
The ancestors collection includes the class itself [FamilyMember], all included modules [Lynn, James, Kernel], and the superclass [Object]. The order of the ancestors collection is also important. The order of the ancestors is the order that the methods will be looked up when the object receives a message. Therefore, any message that is sent to a FamilyMember instance will first look in the methods of FamilyMember, then in Lynn, then James, etc.
If Lynn and James were to define a method, both of those methods would live on the proxies themselves, not on the FamilyMember class. Since the methods live on the proxies, including more modules will not overwrite a previous method definition; however, the last included module to define a method will be the first consulted when that message is sent. The module that was included last (and defines the method) will execute and return, and any other definitions of that method (found on other ancestors) will not be executed.
So, given the above, how does Kernel.as allow me to call the methods of James even though Lynn clearly has precedence? Let's look at the implementation:
For performance reasons (I assume), Kernel.as stores the As instance in a hash; however, for the purposes of our example the only thing worth noting is that Kernel.as returns an instance of the As class initialized with
The above method_missing definition is what allows you to call a method on any ancestor. Let's start with an example and then walk through the method_missing definition to see how it works.
In the above example the member instance receives the message
what happens when two modules are included in a class[?] ... [will included modules overwrite] any methods that were included already by an earlier included module[?] (for the full comment please see the referenced post)In Aman's comment he also points out that the Programming Ruby provides the following information.
If a module is included within a class definition, the module's constants, class variables, and instance methods are effectively bundled into an anonymous (and inaccessible) superclass for that class. In particular, objects of the class will respond to messages sent to the module's instance methods.Part of the answer to Aman's question is in the statement from Programming Ruby. The way I think of it, each class can have zero or one superclass; however, each class may also have zero or many ancestors that are proxies to modules. You could simplify the previous statement and think of the modules themselves being an ancestor, but it can be important to note the difference because a change to a module will be reflected by all classes that include that module (even classes that included the module before the new behavior was added to the module).
Let's look at an example of a classes' ancestors.
end
end
include James
include Lynn
end
FamilyMember.ancestors # => [FamilyMember, Lynn, James, Object, Kernel]
FamilyMember.superclass # => ObjectThe ancestors collection includes the class itself [FamilyMember], all included modules [Lynn, James, Kernel], and the superclass [Object]. The order of the ancestors collection is also important. The order of the ancestors is the order that the methods will be looked up when the object receives a message. Therefore, any message that is sent to a FamilyMember instance will first look in the methods of FamilyMember, then in Lynn, then James, etc.
If Lynn and James were to define a method, both of those methods would live on the proxies themselves, not on the FamilyMember class. Since the methods live on the proxies, including more modules will not overwrite a previous method definition; however, the last included module to define a method will be the first consulted when that message is sent. The module that was included last (and defines the method) will execute and return, and any other definitions of that method (found on other ancestors) will not be executed.
"James"
end
end
"Lynn"
end
end
include James
include Lynn
end
FamilyMember.ancestors # => [FamilyMember, Lynn, James, Object, Kernel]
FamilyMember.new.name # => "Lynn"So, given the above, how does Kernel.as allow me to call the methods of James even though Lynn clearly has precedence? Let's look at the implementation:
# comments removed for the example, check out the source of Ruby Facets to see the comments
@__as ||= {}
unless r = @__as[ancestor]
r = (@__as[ancestor] = As.new(self, ancestor))
end
r.instance_eval(&blk) if block_given?
r
end
end
#:nodoc:
private *instance_methods.select {|m| m !~ /(^__|^\W|^binding$)/ }
@subject = subject
@ancestor = ancestor
end
@ancestor.instance_method(sym).bind(@subject).call(*args,&blk)
end
endFor performance reasons (I assume), Kernel.as stores the As instance in a hash; however, for the purposes of our example the only thing worth noting is that Kernel.as returns an instance of the As class initialized with
self and the ancestor. Generally, the As instance is returned and a method is immediately called on the As instance. If the As instance doesn't respond to the message it is sent, the method_missing method is invoked.
@ancestor.instance_method(sym).bind(@subject).call(*args,&blk)
endThe above method_missing definition is what allows you to call a method on any ancestor. Let's start with an example and then walk through the method_missing definition to see how it works.
"James"
end
end
"Lynn"
end
end
include James
include Lynn
end
FamilyMember.ancestors # => [FamilyMember, Lynn, James, Object, Kernel]
member = FamilyMember.new
member.name # => "Lynn"
member.as(James).name # => "James"In the above example the member instance receives the message
as which returns an instance of As initialized with the member instance and the module James (as the ancestor). Following the return of the As instance, it receives the name message. Since the As instance doesn't define name, method_missing is called passing in :name as the first argument (sym). Within method_missing, the ancestor (James) receives the message instance_method with the sym (:name) as the argument. The instance_method method will return the unbound method name from the ancestor (James). Next, method_missing binds the unbound method (name) to the subject (the member instance) and sends the call message (with arguments, which are empty in our example). When the unbound method executes bound to the subject it can access any state or behavior of the subject. In our example, the method merely returns "James"; however, the example from State pattern using Modules and Facets verifies that a method from the subject may be called from the unbound method when it is bound to the subject.
Friday, August 24, 2007
Importance of Time
Time is one of the most valuable resources.
Because I believe the above thesis, I value Agile because it strives to provide constant feedback. When given feedback, I can adjust for success (which saves me time), or fail faster (which also saves me time).
Because I believe the above thesis, I value Agile because it strives to provide constant feedback. When given feedback, I can adjust for success (which saves me time), or fail faster (which also saves me time).
Mac OS-X: Disk Inventory X
I recently ran out of space on my MacBook Pro. Luckily, Pat Sarnacke pointed me at Disk Inventory X.
Disk Inventory X is a disk usage utility for Mac OS X 10.3 (and later). It shows the sizes of files and folders in a special graphical way called "treemaps".Disk Inventory X was exactly what I needed, and I highly recommend it if you are wondering where your space is going.
If you've ever wondered where all your disk space has gone, Disk Inventory X will help you to answer this question.
Labels: disk inventory x, mac os-x
Thursday, August 23, 2007
Ruby: State pattern using Modules and Facets
While browsing the docs for Facets I noticed the Kernel.as method. Then, a day later I needed a solution that added behavior using the state pattern; however, it was more elegant for our solution if the state instances could call methods on the class containing the state object. Our first solution was to delegate to the containing class via the method_missing method of the state instances. While this did work, debugging became problematic when things went wrong.
I did a quick spike to see if Kernel.as could provide a solution.
There's a few interesting things about this solution. I can change the behavior at runtime by changing the person attribute. Also, the behavior is actually on the class instead of living on an independent state object that delegates back to the class. Lastly, each method is protected so they must be accessed via the delegations, and an error will occur if they are accessed directly.
I did a quick spike to see if Kernel.as could provide a solution.
protected
"James "
end
end
protected
"Lynn "
end
end
attr_accessor :person
include James
include Lynn
as(person).name
end
"Holbrook"
end
end
member = FamilyMember.new
member.person = James
member.name # => "James Holbrook"
member.person = Lynn
member.name # => "Lynn Holbrook"
member.first_name rescue "first_name is not defined" # => "first_name is not defined"
There's a few interesting things about this solution. I can change the behavior at runtime by changing the person attribute. Also, the behavior is actually on the class instead of living on an independent state object that delegates back to the class. Lastly, each method is protected so they must be accessed via the delegations, and an error will occur if they are accessed directly.
Labels: facets, ruby, state pattern
Ruby: Facets
The first time I stumbled upon Facets was on Mauricio Fernandez's hiki. I (stupidly) mentally bookmarked it, but never bothered to make it back.
Fortunately, I recently got an IM from Pat Farley letting me know that I was missing out.
Fortunately, I recently got an IM from Pat Farley letting me know that I was missing out.
Ruby Facets is the single largest collection of core extension methods and standard library additions available for the Ruby programming language. --facets.rubyforge.orgIf you haven't looked at Facets, you are missing out. I suggest getting familiar with it by browsing the API one class at a time.
Saturday, August 18, 2007
Ruby: initialize_with
In April I wrote about assigning instance variables in a constructor. The code I provided with that entry supports initializing your classes positionally or with a hash.
Since then, I've been thinking that a nice alternative might be a constructor that takes one argument without a name, but any additional arguments must be named (via a hash). Yes, I basically stole this concept from Smalltalk.
Below is a solution to making the above code execute as expected.
One other small change that you might notice is that I've made all the arguments required. That's a preference that I might blog about later, but I think it should be easy enough to remove the raises if you want your options arguments to be optional.
For those interested, here's the code with tests.
Since then, I've been thinking that a nice alternative might be a constructor that takes one argument without a name, but any additional arguments must be named (via a hash). Yes, I basically stole this concept from Smalltalk.
initialize_with :employee_id, :first_name, :last_name
attr_reader :employee_id, :first_name, :last_name
end
initialize_with :amount
attr_reader :amount
endBelow is a solution to making the above code execute as expected.
first_arg = args.shift
define_method :initialize do |*arg|
instance_variable_set "@", arg.shift
required_args = args.inject(first_arg.to_s) {|result, attribute| result << ", " }
raise ArgumentError.new("initialize requires ") if args.any? && arg.empty?
args.each do |attribute|
raise ArgumentError.new("initialize requires ") unless arg.first.has_key?(attribute)
instance_variable_set "@", arg.first[attribute]
end
end
end
endOne other small change that you might notice is that I've made all the arguments required. That's a preference that I might blog about later, but I think it should be easy enough to remove the raises if you want your options arguments to be optional.
For those interested, here's the code with tests.
first_arg = args.shift
define_method :initialize do |*arg|
instance_variable_set "@", arg.shift
required_args = args.inject(first_arg.to_s) {|result, attribute| result << ", " }
raise ArgumentError.new("initialize requires ") if args.any? && arg.empty?
args.each do |attribute|
raise ArgumentError.new("initialize requires ") unless arg.first.has_key?(attribute)
instance_variable_set "@", arg.first[attribute]
end
end
end
end
initialize_with :employee_id, :first_name, :last_name
attr_reader :employee_id, :first_name, :last_name
end
initialize_with :amount
attr_reader :amount
end
unit_tests do
test "verify Person requires all options" do
assert_raises ArgumentError do
Person.new(2, :first_name => 'mike')
end
end
test "verify Person requires first arg" do
assert_raises ArgumentError do
Person.new(:first_name => 'mike', :last_name => 'ward')
end
end
test "verify Person employee_id" do
mike = Person.new(2, :first_name => 'mike', :last_name => 'ward')
assert_equal 2, mike.employee_id
end
test "verify Person first_name" do
mike = Person.new(2, :first_name => 'mike', :last_name => 'ward')
assert_equal 'mike', mike.first_name
end
test "verify Person last_name" do
mike = Person.new(2, :first_name => 'mike', :last_name => 'ward')
assert_equal 'ward', mike.last_name
end
test "verify Money amount" do
money = Money.new(10)
assert_equal 10, money.amount
end
endLabels: initialize_with, initializer, ruby
Thursday, August 16, 2007
Blogs I Read
These are the blogs that I read, in no particular order.
Don't see yours on there, but think it should be? let me know: ruby at [my domain] . com
- PragMactic OS-Xer (http://pragmactic-osxer.blogspot.com/)
- Ralph Johnson - Blog (http://www.cincomsmalltalk.com/userblogs/ralph/blogView)
- Virtual Surreality (http://jagraham.blogspot.com/index.html)
- Cuberick (http://www.cuberick.com)
- SmallDoses (http://bellubbi.com/wordpress)
- FoemBlog (http://blog.foemmel.com)
- Thoughts On ... (http://www.williamcaputo.com/)
- Meme Agora (http://memeagora.blogspot.com/)
- Michael Feathers' Weblog (http://www.artima.com/weblogs/index.jsp?blogger=mfeathers)
- HREF Considered Harmful (http://smallthought.com/avi)
- James Newkirk (http://jamesnewkirk.typepad.com/posts/)
- Joel on Software (http://www.joelonsoftware.com)
- David Chelimsky (http://davidchelimsky.blogspot.com/index.html)
- Martin Fowler's Bliki (http://martinfowler.com/bliki)
- magpiebrain (http://www.magpiebrain.com)
- geek + red sox = peter ryan (http://www.peterryan.net)
- Dan Manges (http://www.dcmanges.com/blog)
- Beech Bonanza (http://beechbonanza.blogspot.com/)
- The Rails Way - all (http://www.therailsway.com/)
- Ruby Inside (http://www.rubyinside.com)
- STEPHEN CHU (http://www.stephenchu.com/)
- Ryan's Scraps (http://ryandaigle.com/)
- Mistaeks I Hav Made (http://nat.truemesh.com/)
- Practical Ruby (http://practicalruby.blogspot.com/)
- Riding Rails (http://weblog.rubyonrails.com/)
- Gluttonous (http://glu.ttono.us/)
- Mock Turtle Soup (http://stevef.truemesh.com/)
- Cody Fauser (http://selenium.jadedpixel.com:10100/)
- Aslak Hellesøy (http://blog.aslakhellesoy.com/)
- Dr Nic on Ruby on Rails (http://drnicwilliams.com)
- has_many :through (http://blog.hasmanythrough.com/)
- Ola Bini on Java, Lisp, Ruby and AI (http://ola-bini.blogspot.com/)
- RailsExpress.blog (https://railsexpress.de/blog)
- Err the Blog (http://errtheblog.com/)
- Loud Thinking (http://www.loudthinking.com/)
- PragDave (http://pragdave.pragprog.com/pragdave/)
- Too-biased - Home (http://blog.leetsoft.com/)
- David Chelimsky (http://blog.davidchelimsky.net/)
- ChadFowler.com - Home (http://www.chadfowler.com/)
- the { buckblogs :here } - Home (http://weblog.jamisbuck.org/)
- My hovercraft is full of eels (http://www.redhillconsulting.com.au/blogs/simon/)
- Floehopper (http://blog.floehopper.org/)
- split-s (http://split-s.blogspot.com/)
- Relevance (http://relevancellc.com/blog)
- Obie Fernandez (http://www.jroller.com/obie/)
- Schrade.Blog (http://kurt.karmalab.org)
- Nuby on Rails (http://nubyonrails.com)
- 14to9.org - article (http://14to9.org/)
- Giles Bowkett (http://gilesbowkett.blogspot.com/)
- defmacro.org (http://www.defmacro.org/)
- Process, People, and Pods (http://processpeoplepods.blogspot.com/)
Don't see yours on there, but think it should be? let me know: ruby at [my domain] . com
Labels: blog
RubyGems: Dust
I've often shown examples similar to the following snippet.
Forgetting the body of the test, the interesting thing about the snippet is that it's a grouping of tests that contains a single test. I prefer this syntax to defining a class that inherits from Test::Unit::TestCase and then defining tests by creating methods that start with "test_". All of the projects that I'm a part of use this syntax and I've yet to hear anyone say that they prefer the classic Test::Unit syntax.
Adding this to your project just got a bit easier. I extracted the core methods and created a new gem: Dust.
Dust gives you unit_tests, functional_tests, and test methods that can be used for defining tests. Dust also includes additional methods that can help enforce design decisions. For example, if you subscribe to the theory that setup and helper methods should be removed if possible, the Test::Unit::TestCase.disallow_setup! and Test::Unit::TestCase.disallow_helpers! should interest you.
functional_tests do
test "given no name, when validated, then error is in the objects error collection" do
instance = Klass.new
instance.valid?
assert_equal "can't be empty", instance.errors.on(:name)
end
endForgetting the body of the test, the interesting thing about the snippet is that it's a grouping of tests that contains a single test. I prefer this syntax to defining a class that inherits from Test::Unit::TestCase and then defining tests by creating methods that start with "test_". All of the projects that I'm a part of use this syntax and I've yet to hear anyone say that they prefer the classic Test::Unit syntax.
Adding this to your project just got a bit easier. I extracted the core methods and created a new gem: Dust.
Dust gives you unit_tests, functional_tests, and test methods that can be used for defining tests. Dust also includes additional methods that can help enforce design decisions. For example, if you subscribe to the theory that setup and helper methods should be removed if possible, the Test::Unit::TestCase.disallow_setup! and Test::Unit::TestCase.disallow_helpers! should interest you.
Thursday, August 09, 2007
Eagerly Initialized Attribute
Initialize an attribute at construction time instead of on the first access.
==>
Motivation
The motivation for converting attributes to be eagerly initialized is for code readability purposes. Lazily initialized attributes change their value upon access. Lazily initialized attributes can be problematic to debug because their values change upon access. Eagerly Initialized Attributes initialize their attributes in the constructor of the class. This leads to encapsulating all initialization logic in the constructor and consistent results when querying the value of the instance variable.
Discussion
I prefer Lazily Initialized Attributes, but Martin prefers Eagerly Initialized Attributes. I opened up the discussion to the reviewers of Refactoring, Ruby Edition and my current ThoughtWorks team, but in the end it was split 50/50 on preference. Based on that fact, I told Martin I didn't think it was a good candidate for Refactoring, Ruby Edition. Not surprisingly, he had a better solution: Provide examples of both refactoring to Eagerly Initialized Attribute and refactoring to Lazily Initialized Attribute.
Martin and I agree that this isn't something worth being religious about. Additionally, we both think it's valuable for a team to standardize on Lazily or Eagerly Initialized Attributes.
Mechanics
The code below is an Employee class with both the email and voice_mail attributes lazily initialized.
Moving to an Eagerly Initialized Attribute generally means moving the initialization logic from the getter methods into the constructor.
@emails ||= []
end
end==>
@emails = []
end
endMotivation
The motivation for converting attributes to be eagerly initialized is for code readability purposes. Lazily initialized attributes change their value upon access. Lazily initialized attributes can be problematic to debug because their values change upon access. Eagerly Initialized Attributes initialize their attributes in the constructor of the class. This leads to encapsulating all initialization logic in the constructor and consistent results when querying the value of the instance variable.
Discussion
I prefer Lazily Initialized Attributes, but Martin prefers Eagerly Initialized Attributes. I opened up the discussion to the reviewers of Refactoring, Ruby Edition and my current ThoughtWorks team, but in the end it was split 50/50 on preference. Based on that fact, I told Martin I didn't think it was a good candidate for Refactoring, Ruby Edition. Not surprisingly, he had a better solution: Provide examples of both refactoring to Eagerly Initialized Attribute and refactoring to Lazily Initialized Attribute.
Martin and I agree that this isn't something worth being religious about. Additionally, we both think it's valuable for a team to standardize on Lazily or Eagerly Initialized Attributes.
Mechanics
- Move the initialization logic to the constructor.
- Test
The code below is an Employee class with both the email and voice_mail attributes lazily initialized.
@emails ||= []
end
@voice_mails ||= []
end
endMoving to an Eagerly Initialized Attribute generally means moving the initialization logic from the getter methods into the constructor.
attr_reader :emails, :voice_mails
@emails = []
@voice_mails = []
end
endLabels: refactoring, ruby
Monday, August 06, 2007
Ruby: Lazily Initialized Attributes
Update: This is probably going to be included in Refactoring, Ruby Edition so I took another pass through it following all of your comments. Thanks for the feedback.
Initialize an attribute on access instead of at construction time.
==>
Motivation
The motivation for converting attributes to be lazily initialized is for code readability purposes. While the above example is trivial, when the Employee class has multiple attributes that need to be initialized the constructor will need to contain all the initialization logic. Classes that initialize instance variables in the constructor need to worry about both attributes and instance variables.The procedural behavior of initializing each attribute in a constructor is unnecessary and less maintainable than a class that deals exclusivey with attributes. Lazily Initialized Attributes can encapsulate all their initialization logic within the methods themselves.
Mechanics
The code below is an Employee class with the email attribute initialized in the constructor.
Moving to a Lazily Initialized Attribute generally means moving the initialization logic to the getter method and initializing on the first access.
Example using instance_variable_defined?
Using ||= for Lazily Initialized Attributes is a common idiom; however, this idiom falls down when nil or false are valid values for the attribute.
In the above example it's not practical to use an ||= operator for a Lazily Initialized Attribute because the find_by_boss_id might return nil. In the case where nil is returned, each time the assistant attribute is accessed another database trip will occur. A superior solution is to use code similar to the example below that utilizes the instance_variable_defined? method that was introduced in ruby 1.8.6.
Initialize an attribute on access instead of at construction time.
@emails = []
end
end==>
@emails ||= []
end
endMotivation
The motivation for converting attributes to be lazily initialized is for code readability purposes. While the above example is trivial, when the Employee class has multiple attributes that need to be initialized the constructor will need to contain all the initialization logic. Classes that initialize instance variables in the constructor need to worry about both attributes and instance variables.The procedural behavior of initializing each attribute in a constructor is unnecessary and less maintainable than a class that deals exclusivey with attributes. Lazily Initialized Attributes can encapsulate all their initialization logic within the methods themselves.
Mechanics
- Move the initialization logic to the attribute getter.
- Test
The code below is an Employee class with the email attribute initialized in the constructor.
attr_reader :emails, :voice_mails
@emails = []
@voice_mails = []
end
endMoving to a Lazily Initialized Attribute generally means moving the initialization logic to the getter method and initializing on the first access.
@emails ||= []
end
@voice_mails ||= []
end
endExample using instance_variable_defined?
Using ||= for Lazily Initialized Attributes is a common idiom; however, this idiom falls down when nil or false are valid values for the attribute.
@assistant = Employee.find_by_boss_id(self.id)
endIn the above example it's not practical to use an ||= operator for a Lazily Initialized Attribute because the find_by_boss_id might return nil. In the case where nil is returned, each time the assistant attribute is accessed another database trip will occur. A superior solution is to use code similar to the example below that utilizes the instance_variable_defined? method that was introduced in ruby 1.8.6.
@assistant = Employee.find_by_boss_id(self.id) unless instance_variable_defined? :@assistant
@assistant
endLabels: refactoring, ruby
Sunday, August 05, 2007
Ruby: operator precedence of && and = (which also applies to || or)
Recently a bug popped up on my project surrounding code similar to the following sample.
The problem with the above code is that it evaluates as if it were the snippet below.
It should be obvious that setting the total to true or false is not the intended behavior.
The problem with the original snippet is the usage of the 'and' operator. The Ruby operator precedence table shows that '=' has as higher precedence than the 'and' operator.
The solution is to use the '&&' operator instead of the 'and' operator. The precedence table shows that the '&&' operator has a higher precedence than the '=' operator. Therefore, the following two lines of code are basically the same.
As the title says, the same rules apply to the precedence of '||' and 'or'.
total = shopping_cart.empty? and shopping_cart.totalThe problem with the above code is that it evaluates as if it were the snippet below.
(total = shopping_cart.empty?) and shopping_cart.totalIt should be obvious that setting the total to true or false is not the intended behavior.
The problem with the original snippet is the usage of the 'and' operator. The Ruby operator precedence table shows that '=' has as higher precedence than the 'and' operator.
The solution is to use the '&&' operator instead of the 'and' operator. The precedence table shows that the '&&' operator has a higher precedence than the '=' operator. Therefore, the following two lines of code are basically the same.
total = shopping_cart.empty? && shopping_cart.total
total = (shopping_cart.empty? && shopping_cart.total)As the title says, the same rules apply to the precedence of '||' and 'or'.
Labels: and, operator precedence, or, ruby
Thursday, August 02, 2007
Spellchecking Pair
Pair programming is a controversial topic. The majority of the projects I've been involved with always have at least one skeptic on the team. The skeptics always cause me to take another look at the practice and see if the specific project provides a new point of view to the discussion.
On my last project I learned that large gaps in talent can turn a teammate into nothing more than a spellchecker. This generally seemed to happen when one member of a pair was significantly more skilled than the other. In the case of an experience gap the more skilled pair can slow down and mentor the junior pair member; however, sometimes a story needs to be completed as soon as possible. In the cases where time is very valuable it seems to make sense to rotate in a more experienced teammate or to split the pair until a more experienced teammate can join the pair.
While I generally prefer pairing, if a teammate is providing nothing more than spelling suggestions it is probably not the best use of his time. Furthermore, when an experienced teammate is required to slow down to explain things, then the pair is not closing the card as quickly as possible.
On my last project I learned that large gaps in talent can turn a teammate into nothing more than a spellchecker. This generally seemed to happen when one member of a pair was significantly more skilled than the other. In the case of an experience gap the more skilled pair can slow down and mentor the junior pair member; however, sometimes a story needs to be completed as soon as possible. In the cases where time is very valuable it seems to make sense to rotate in a more experienced teammate or to split the pair until a more experienced teammate can join the pair.
While I generally prefer pairing, if a teammate is providing nothing more than spelling suggestions it is probably not the best use of his time. Furthermore, when an experienced teammate is required to slow down to explain things, then the pair is not closing the card as quickly as possible.
Labels: agile, pair programming


