Friday, September 12, 2008
Refactoring: Ruby Edition available on Safari
Wednesday, July 30, 2008
Refactoring: Ruby Edition available on Amazon
It's been about 18 months since my first entry about Refactoring: Ruby Edition. It's been a long journey, but the book should be seeing day light in the next few months.
It's available for pre-order on Amazon now, and it should also be available on Safari in the near future.
Thanks to everyone who helped, and I hope the book is as fun to read as it was to write.
It's available for pre-order on Amazon now, and it should also be available on Safari in the near future.
Thanks to everyone who helped, and I hope the book is as fun to read as it was to write.
Labels: books, refactoring, ruby
Wednesday, March 19, 2008
Ruby: Replace Temp with Chain
You have methods that can be chained for greater maintainability.
becomes
Motivation
Calling methods on different lines technically gets the job done, but at times it makes sense to chain method calls together and provide a more fluent interface. In the above examples, assigning an expectation to a local variable is only necessary so that the arguments and return value can be specified. The solution utilizing Method Chaining removes the need for the local variable. Method Chaining can also improve maintainability by providing an interface that allows you to compose code that reads naturally.
Mechanics
Suppose you were designing a library for creating html elements. This library would likely contain a method that created a select drop down and allowed you to add options to the select. The following code contains the Select class that could enable creating the example html and an example usage of the select class.
The first step in creating a Method Chained solution is to create a method that creates the Select instance and adds an option.
Next, change the method that adds options to return self so that it can be chained.
Finally, rename the add_option method to something that reads more fluently, such as "and".
mock = Mock.new
expectation = mock.expects(:a_method_name)
expectation.with("arguments")
expectation.returns([1, :array])becomes
mock = Mock.new
mock.expects(:a_method_name).with("arguments").returns([1, :array])Motivation
Calling methods on different lines technically gets the job done, but at times it makes sense to chain method calls together and provide a more fluent interface. In the above examples, assigning an expectation to a local variable is only necessary so that the arguments and return value can be specified. The solution utilizing Method Chaining removes the need for the local variable. Method Chaining can also improve maintainability by providing an interface that allows you to compose code that reads naturally.
Mechanics
- Return self from methods you wish to allow chaining from
- Test
- Remove the local variable and chain the method calls
- Test
Suppose you were designing a library for creating html elements. This library would likely contain a method that created a select drop down and allowed you to add options to the select. The following code contains the Select class that could enable creating the example html and an example usage of the select class.
@options ||= []
end
options << arg
end
end
select = Select.new
select.add_option(1999)
select.add_option(2000)
select.add_option(2001)
select.add_option(2002)
select # => #<Select:0x28708 @options=[1999, 2000, 2001, 2002]>
The first step in creating a Method Chained solution is to create a method that creates the Select instance and adds an option.
select = self.new
select.options << option
select
end
# ...
end
select = Select.with_option(1999)
select.add_option(2000)
select.add_option(2001)
select.add_option(2002)
select # => #<Select:0x28488 @options=[1999, 2000, 2001, 2002]>
Next, change the method that adds options to return self so that it can be chained.
# ...
options << arg
self
end
end
select = Select.with_option(1999).add_option(2000).add_option(2001).add_option(2002)
select # => #<Select:0x28578 @options=[1999, 2000, 2001, 2002]>
Finally, rename the add_option method to something that reads more fluently, such as "and".
select = self.new
select.options << option
select
end
@options ||= []
end
options << arg
self
end
end
select = Select.with_option(1999).and(2000).and(2001).and(2002)
select # => #<Select:0x28578 @options=[1999, 2000, 2001, 2002]>Labels: refactoring, ruby
Tuesday, March 18, 2008
Ruby: Isolate Dynamic Receptor
Isolate Dynamic Receptor
A class utilizing method_missing has become painful to alter
Introduce a new class and move the method_missing logic to that class.
Motivation
As I previously mentioned, objects that use method_missing often raise NoMethodError errors unexpectedly, or worse you get no more information than: stack level too deep (SystemStackError).
Despite the added complexity, method_missing is a powerful tool that needs to be used when the interface of a class can not be predetermined. On those occasions I like to use Isolate Dynamic Receptor to limit the behavior of an object that also relies on method_missing.
The ActiveRecord::Base (AR::B) class defines method_missing to handle dynamic find messages. The implementation of method_missing allows you to send find messages that use attributes of a class as limiting conditions for the results that will be returned by the dynamic find messages. For example, given a Person subclass of AR::B that has both a first name and a ssn attribute it's possible to send the messages Person.find_by_first_name, Person.find_by_ssn, and Person.find_by_first_name_and_ssn.
It's possible, but not realistic to dynamically define methods for all possible combinations of the attributes of an AR::B subclass. Instead utilizing method_missing is a good solution; however, by defining method_missing on the AR::B class itself the complexity of the class is increased significantly. AR::B would benefit from a maintainability perspective if instead the dynamic finder logic were defined on a class whose single responsibility was to handle dynamic find messages. For example, the above Person class could support find with the following syntax: Person.find.by_first_name, Person.find.by_ssn, or Person.find.by_first_name_and_ssn
Note: very often it's possible to know all valid method calls ahead of time, in which case I prefer Replace Dynamic Receptor with Dynamically Define Method.
Mechanics
Here's a recorder class that records all calls to method_missing.
The recorder class may need additional behavior such as the ability to play back all the messages on an object and the ability to represent all the calls as strings.
As the behavior of Recorder grows it becomes harder to understand what messages are dynamically handled and what messages are actually explicitly defined. By design the functionality of method_missing should handle any unknown message, but how do you know if you've broken something by adding a explicitly defined method?
The solution to this problem is to introduce an additional class that has the single responsibility of handling the dynamic method calls. In this case we have a class Recorder that handles recording unknown messages as well as playing back the messages or printing them. To reduce complexity we will introduce the MessageCollector class that handles the method_missing calls.
The record method of Recorder will create a new instance of the MessageCollector class and each additional chained call will be recorded. The play back and printing capabilities will remain on the Recorder object.
A class utilizing method_missing has become painful to alter
Introduce a new class and move the method_missing logic to that class.
Motivation
As I previously mentioned, objects that use method_missing often raise NoMethodError errors unexpectedly, or worse you get no more information than: stack level too deep (SystemStackError).
Despite the added complexity, method_missing is a powerful tool that needs to be used when the interface of a class can not be predetermined. On those occasions I like to use Isolate Dynamic Receptor to limit the behavior of an object that also relies on method_missing.
The ActiveRecord::Base (AR::B) class defines method_missing to handle dynamic find messages. The implementation of method_missing allows you to send find messages that use attributes of a class as limiting conditions for the results that will be returned by the dynamic find messages. For example, given a Person subclass of AR::B that has both a first name and a ssn attribute it's possible to send the messages Person.find_by_first_name, Person.find_by_ssn, and Person.find_by_first_name_and_ssn.
It's possible, but not realistic to dynamically define methods for all possible combinations of the attributes of an AR::B subclass. Instead utilizing method_missing is a good solution; however, by defining method_missing on the AR::B class itself the complexity of the class is increased significantly. AR::B would benefit from a maintainability perspective if instead the dynamic finder logic were defined on a class whose single responsibility was to handle dynamic find messages. For example, the above Person class could support find with the following syntax: Person.find.by_first_name, Person.find.by_ssn, or Person.find.by_first_name_and_ssn
Note: very often it's possible to know all valid method calls ahead of time, in which case I prefer Replace Dynamic Receptor with Dynamically Define Method.
Mechanics
- Create a new class whose sole responsibility is to handle the dynamic method calls.
- Copy the logic from method_missing on the original class to the method_missing of the focused class.
- Change all client code that previously called the dynamic methods on the original object.
- Remove the method_missing from the original object.
- Test
Here's a recorder class that records all calls to method_missing.
instance_methods.each do |meth|
undef_method meth unless meth =~ /^(__|inspect)/
end
@messages ||= []
end
messages << [sym, args]
self
end
endThe recorder class may need additional behavior such as the ability to play back all the messages on an object and the ability to represent all the calls as strings.
messages.inject(obj) do |result, message|
result.send message.first, *message.last
end
end
messages.inject([]) do |result, message|
result << "(args: )"
end.join(".")
end
endAs the behavior of Recorder grows it becomes harder to understand what messages are dynamically handled and what messages are actually explicitly defined. By design the functionality of method_missing should handle any unknown message, but how do you know if you've broken something by adding a explicitly defined method?
The solution to this problem is to introduce an additional class that has the single responsibility of handling the dynamic method calls. In this case we have a class Recorder that handles recording unknown messages as well as playing back the messages or printing them. To reduce complexity we will introduce the MessageCollector class that handles the method_missing calls.
instance_methods.each do |meth|
undef_method meth unless meth =~ /^(__|inspect)/
end
@messages ||= []
end
messages << [sym, args]
self
end
endThe record method of Recorder will create a new instance of the MessageCollector class and each additional chained call will be recorded. The play back and printing capabilities will remain on the Recorder object.
@message_collector.messages.inject(obj) do |result, message|
result.send message.first, *message.last
end
end
@message_collector ||= MessageCollector.new
end
@message_collector.messages.inject([]) do |result, message|
result << "(args: )"
end.join(".")
end
endLabels: method_missing, refactoring, ruby
Monday, March 17, 2008
Move eval from Run-time to Parse-time
You need to use eval, but want to limit the number of times eval is necessary.
becomes
Motivation
Mechanics
The following Person class uses eval to define the logic the readers rely upon for returning a default value if no value has previously been set.
The above example executes without issue, but it relies upon eval each time a reader is called. If multiple calls to eval are determined to be problematic the solution is to expand the eval to include defining the method itself.
options.each_pair do |attribute, default_value|
define_method attribute do
eval "@ ||= "
end
end
end
attr_with_default :emails => "[]", :employee_number => "EmployeeNumberGenerator.next"
endbecomes
options.each_pair do |attribute, default_value|
eval "def
@ ||=
end"
end
end
attr_with_default :emails => "[]", :employee_number => "EmployeeNumberGenerator.next"
endMotivation
premature optimization is the root of all evil -- Knuth, DonaldI'll never advocate for premature optimization, but this refactoring can be helpful when you determine that eval is a source of performance pain. The Kernel#eval method can be the right solution in some cases; but it is almost always more expensive (in terms of performance) than it's alternatives. In the cases where eval is necessary, it's often better to move an eval call from run-time to parse-time.
Mechanics
- Expand the scope of the string being eval'd.
- Test
The following Person class uses eval to define the logic the readers rely upon for returning a default value if no value has previously been set.
options.each_pair do |attribute, default_value|
define_method attribute do
eval "@ ||= "
end
end
end
attr_with_default :emails => "[]", :employee_number => "EmployeeNumberGenerator.next"
endThe above example executes without issue, but it relies upon eval each time a reader is called. If multiple calls to eval are determined to be problematic the solution is to expand the eval to include defining the method itself.
options.each_pair do |attribute, default_value|
eval "def
@ ||=
end"
end
end
attr_with_default :emails => "[]", :employee_number => "EmployeeNumberGenerator.next"
endLabels: eval, refactoring, ruby
Thursday, February 28, 2008
Ruby: Replace method_missing with dynamic method definitions
You have methods you want to handle dynamically without the pain of debugging method_missing.
becomes
Motivation
Debugging classes that use method_missing can often be painful. At best you often get a NoMethodError on an object that you didn't expect, and at worst you get stack level too deep (SystemStackError).
There are times that method_missing is required. If the usage of an object is unknown, but must support unexpected method calls you may not be able to avoid the use of method_missing.
However, often you know how an object will be used and using Dynamically Define Method you can achieve the same behavior without relying on method_missing.
Mechanics
Delegation is a common task while developing software. Delegation can be handled explicitly by defining methods yourself or by utilizing something from the Ruby Standard Library such as Forwardable. Using these techniques gives you control over what methods you want to delegate to the subject object; however, sometimes you want to delegate all methods without specifying them. Ruby's Standard Library also provides this capability with the delegate library, but we'll assume we need to implement our own for this example.
The simple way to handle delegation (ignoring the fact that you would want to undefine all the standard methods a class gets by default) is to use method_missing to pass any method calls straight to the subject.
This solution does work, but it can be problematic when mistakes are made. For example, calling a method that does not exist on the subject will result in the subject raising a NoMethodError. Since the method call is being called on the decorator, but the subject is raising the error it may be painful to track down where the problem resides.
The wrong object raising a NoMethodError is significantly better than the dreaded stack level too deep (SystemStackError). This can be caused by something as simple as forgetting to use the subject instance variable and trying to use a non-existent subject method or any misspelled method. When this happens the only feedback you have is that something went wrong, but Ruby isn't sure exactly what it was.
These problems can be avoided entirely by using the available data to dynamically define methods at run time. The following example defines an instance method on the decorator for each public method of the subject.
Using this technique any invalid method calls will be correctly reported as NoMethodErrors on the decorator. Additionally, there's no method_missing definition, which should help avoid the stack level too deep problem entirely.
Example: Using user defined data to define methods
Often you can use the information from a class definition to define methods instead of relying on method_missing. For example, the following code relies on method_missing to determine if any of the attributes are nil.
@subject = subject
end
@subject.send sym, *args, &block
end
endbecomes
subject.public_methods(false).each do |meth|
(class << self; self; end).class_eval do
define_method meth do |*args|
subject.send meth, *args
end
end
end
end
endMotivation
Debugging classes that use method_missing can often be painful. At best you often get a NoMethodError on an object that you didn't expect, and at worst you get stack level too deep (SystemStackError).
There are times that method_missing is required. If the usage of an object is unknown, but must support unexpected method calls you may not be able to avoid the use of method_missing.
However, often you know how an object will be used and using Dynamically Define Method you can achieve the same behavior without relying on method_missing.
Mechanics
- Dynamically define the necessary methods
- Remove method_missing
- Test
Delegation is a common task while developing software. Delegation can be handled explicitly by defining methods yourself or by utilizing something from the Ruby Standard Library such as Forwardable. Using these techniques gives you control over what methods you want to delegate to the subject object; however, sometimes you want to delegate all methods without specifying them. Ruby's Standard Library also provides this capability with the delegate library, but we'll assume we need to implement our own for this example.
The simple way to handle delegation (ignoring the fact that you would want to undefine all the standard methods a class gets by default) is to use method_missing to pass any method calls straight to the subject.
@subject = subject
end
@subject.send sym, *args, &block
end
endThis solution does work, but it can be problematic when mistakes are made. For example, calling a method that does not exist on the subject will result in the subject raising a NoMethodError. Since the method call is being called on the decorator, but the subject is raising the error it may be painful to track down where the problem resides.
The wrong object raising a NoMethodError is significantly better than the dreaded stack level too deep (SystemStackError). This can be caused by something as simple as forgetting to use the subject instance variable and trying to use a non-existent subject method or any misspelled method. When this happens the only feedback you have is that something went wrong, but Ruby isn't sure exactly what it was.
These problems can be avoided entirely by using the available data to dynamically define methods at run time. The following example defines an instance method on the decorator for each public method of the subject.
subject.public_methods(false).each do |meth|
(class << self; self; end).class_eval do
define_method meth do |*args|
subject.send meth, *args
end
end
end
end
endUsing this technique any invalid method calls will be correctly reported as NoMethodErrors on the decorator. Additionally, there's no method_missing definition, which should help avoid the stack level too deep problem entirely.
Example: Using user defined data to define methods
Often you can use the information from a class definition to define methods instead of relying on method_missing. For example, the following code relies on method_missing to determine if any of the attributes are nil.
attr_accessor :name, :age
empty?(sym.to_s.sub