# Re: Refactoring (#75)

**URL:** <https://www.ruby-forum.com/t/re-refactoring-75/58328>\
**Category:** Ruby\
**Created:** [April 20, 2006, 5:30pm UTC](https://www.ruby-forum.com/t/re-refactoring-75/58328 "2006-04-20T17:30:06Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![MolitorS\_Stephen\_L](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/e480ec/32.png) [@MolitorS\_Stephen\_L](https://www.ruby-forum.com/u/MolitorS_Stephen_L)\
**Post date:** [April 20, 2006, 5:30pm UTC](https://www.ruby-forum.com/t/re-refactoring-75/58328/1 "2006-04-20T17:30:06Z")

</div>

James,

Great summary. Wish I had participated in the quiz!

I liked Dave B.'s ‘Removed Unused Scope’ example. Would you (or  
someone else) care to give examples of ‘Replace Similar Methods with  
Metaprogrammed Definitions’ and a ‘Convert a Family of Methods to a  
Single method\_missing()’? I’m especially interested in the latter. I  
know how method\_missing works and I’ve seen its use in frameworks (like  
Rails), but I’ve never actually written one myself.

I’m sure there are opportunities to use these Rubyesque type  
refactorings in my code but I tend not to see them due to lack of  
experience. One of the great things about the Refactoring book for me  
was that it gave examples of things that I could easily relate to my own  
application code. It sharpened my nose.

Thanks again for the quiz.

Steve

---

<div class="post-metadata">

**Author:** ![MolitorS\_Stephen\_L](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/e480ec/32.png) [@MolitorS\_Stephen\_L](https://www.ruby-forum.com/u/MolitorS_Stephen_L)\
**Post date:** [April 20, 2006, 5:45pm UTC](https://www.ruby-forum.com/t/re-refactoring-75/58328/2 "2006-04-20T17:45:29Z")

</div>

This is from memory, because I don’t have easy access to the code at the  
moment.  
I built a GUI application that automated various mainframe tasks for  
users over TN3270E.  
The particular mainframe in question had fairly tedious navigation  
steps, and was broken out into separate ‘screens’, each of which had a  
four-character name.

Long story short, I ended up making a method-missing handler for  
screen navigation.

def method\_missing(message, \*args, &block)  
if message =~ /navigate\_to\_(\w{4})/  
x, y = find\_prompt\_coordinates  
screen.put(x,y,$1)  
screen.transmit  
else  
super  
end  
end

Which meant I could then say:  
host.navigate\_to\_abcd

I could just as easily have made “navigate\_to” take a screen name as a  
parameter, but I thought this was easier to read. Maybe it’s not the  
best example.

–Wilson.

---

<div class="post-metadata">

**Author:** ![MolitorS\_Stephen\_L](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/e480ec/32.png) [@MolitorS\_Stephen\_L](https://www.ruby-forum.com/u/MolitorS_Stephen_L)\
**Post date:** [April 20, 2006, 6:06pm UTC](https://www.ruby-forum.com/t/re-refactoring-75/58328/3 "2006-04-20T18:06:57Z")

</div>

On 4/20/06, Molitor, Stephen L [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> James,
> 
> Great summary. Wish I had participated in the quiz!
> 
> I liked Dave B.'s ‘Removed Unused Scope’ example. Would you (or  
> someone else) care to give examples of ‘Replace Similar Methods with  
> Metaprogrammed Definitions’

I’m not sure what James had in mind, but I think this works well as an  
example of post refactoring ‘Replace Similar Methods with  
Metaprogrammed Definitions’:

def add\_test(name, clean\_or\_dirty, number\_of\_errors, definition)  
eval \<\<“END\_OF\_TEST”  
def test\_#{name}  
assert\_class\_is\_#{clean\_or\_dirty} Class.new {#{definition}}  
assert\_number\_of\_errors #{number\_of\_errors}  
end  
END\_OF\_TEST  
end

def dont\_add\_test(\*args)

# not gonna do it

end

class CheckRTest \< Test::Unit::TestCase  
def setup  
@checkr = CheckR.new  
end

def test\_initialize  
assert\_kind\_of CheckR, @checkr  
end

def test\_is\_class\_clean\_exists  
assert\_respond\_to @checkr, “is\_class\_clean?”  
end

def test\_errors\_exists  
assert\_respond\_to @checkr, “errors”  
end

add\_test :clean\_if,  
:clean, 0,  
“def foo; true if true; end”

add\_test :clean\_if\_2,  
:clean, 0,  
“def foo;if true;a=1;puts a;end;end”

add\_test :clean\_while,  
:clean, 0,  
“def foo;while a==2;break;end;end”

add\_test :clean\_assignment\_in\_if\_body,  
:clean, 0,  
“def foo;if a==2;a=3;a;end;end”

add\_test :clean\_return\_value,  
:clean, 0,  
“def foo;1;end”

[thirty more test definitions deleted]

private  
def assert\_class\_is\_clean(klass)  
assert\_equal true, check\_class(klass)  
end

def assert\_class\_is\_dirty(klass)  
assert\_equal false, check\_class(klass)  
end

def assert\_number\_of\_errors(n)  
assert\_equal n, @checkr.errors.length  
end

def check\_class(klass)  
@checkr.is\_class\_clean?(klass)  
end

> designed with the static languages in mind. I’m very intrigued by how a  
> I don’t think we have one. We can #freeze the object, but that won’t
> 
> ```
> def foo
> bar
> 
> ```
> 
> and games. I think Pat hit on a super important topic here and we would  
> all do well to learn more about it.
> 
> Tomorrow, Matthew M. is back with a quiz about txet smnraiclbg…

–  
thanks,  
-pate

---

<div class="post-metadata">

**Author:** ![MolitorS\_Stephen\_L](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/e480ec/32.png) [@MolitorS\_Stephen\_L](https://www.ruby-forum.com/u/MolitorS_Stephen_L)\
**Post date:** [April 20, 2006, 6:16pm UTC](https://www.ruby-forum.com/t/re-refactoring-75/58328/4 "2006-04-20T18:16:08Z")

</div>

On 4/20/06, Molitor, Stephen L [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> Would you (or  
> someone else) care to give examples of ‘Replace Similar Methods with  
> Metaprogrammed Definitions’ and a ‘Convert a Family of Methods to a  
> Single method\_missing()’?

I’ll provide an example:

Let’s say you have a class for managing a list of music. Each item in  
the list is a class which contains a lot of meta-data about the music  
files (artist, title, album, track number, duration, genre, release  
date, etc.) Part of managing this list of music is sorting it by  
various criteria, for display in a media application for example.

There are a lot of different ways to sort based on the various kinds  
of meta-data, and it would be a pain to have to code a special method  
to sort on each type of meta-data:

list.sort\_by\_artist, list.sort\_by\_title, etc.

Of course in this case it might not be too difficult to just do a  
custom sort when needed:

list.sort\_by {|song| song.artist}

But why not make things really handy by adding a method missing?

# Full test code

class Song \< Struct.new(‘Song’, :title, :artist, :album)  
def to\_s  
“#{title} by #{artist} (#{album})”  
end  
end

class MusicList  
attr\_accessor :list

def initialize  
@list = []  
end

def method\_missing(name, _args)  
if name.to\_s =~ /sort\_by\_(._)/  
puts “Sorting by #$1:”  
@list.sort\_by {|song| song.send($1.to\_sym)}  
end  
end  
end

if $0 == **FILE**  
music = MusicList.new  
music.list \<\< Song.new(‘Here I Am’, ‘Al Green’, ‘Greatest Hits’)  
music.list \<\< Song.new(‘Ain't No Sunsine’, ‘Al Green’, ‘Greatest  
Hits’)  
music.list \<\< Song.new(‘Until It Sleeps’, ‘Metallica’, ‘Load’)  
music.list \<\< Song.new(‘Bleeding Me’, ‘Metallica’, ‘Load’)  
music.list \<\< Song.new(‘King Nothing’, ‘Metallica’, ‘Load’)  
music.list \<\< Song.new(‘Shiver’, ‘Maroon 5’, ‘Songs About Jane’)  
music.list \<\< Song.new(‘This Love’, ‘Maroon 5’, ‘Songs About Jane’)  
puts music.sort\_by\_title, ‘----’  
puts music.sort\_by\_artist, ‘----’  
puts music.sort\_by\_album, ‘----’  
end

# End test code

Now things like list.sort\_by\_artist and list.sort\_by\_title will call  
method missing, and any new meta-data can be sorted by without  
changing the list code.

Ryan

---

<div class="post-metadata">

**Author:** ![MolitorS\_Stephen\_L](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/e480ec/32.png) [@MolitorS\_Stephen\_L](https://www.ruby-forum.com/u/MolitorS_Stephen_L)\
**Post date:** [April 20, 2006, 8:04pm UTC](https://www.ruby-forum.com/t/re-refactoring-75/58328/5 "2006-04-20T20:04:18Z")

</div>

On Apr 20, 2006, at 11:04 AM, pat eyler wrote:

> I’m not sure what James had in mind, but I think this works well as an  
> example of post refactoring ‘Replace Similar Methods with  
> Metaprogrammed Definitions’:

[snip example]

Right, or even more trivially:

def a=( value )  
@a = value  
end

def b=( value )  
@b = value  
end

def c=( value )  
@c = value  
end

To:

attr\_writer :a, :b, :c

James Edward G. II

---

<div class="post-metadata">

**Author:** ![MolitorS\_Stephen\_L](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/e480ec/32.png) [@MolitorS\_Stephen\_L](https://www.ruby-forum.com/u/MolitorS_Stephen_L)\
**Post date:** [April 20, 2006, 6:34pm UTC](https://www.ruby-forum.com/t/re-refactoring-75/58328/6 "2006-04-20T18:34:38Z")

</div>

Rather than mucking around with method\_missing, let’s just extend the  
language so method names are regular expressions:

class MusicList  
def /sort\_by\_(.+)/ (\*margs) # “margs” == matches as args  
@list.sort\_by { |song| song.send(margs[0])  
end  
end

Method name resolution might be hell, though…

---

<div class="post-metadata">

**Author:** ![MolitorS\_Stephen\_L](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/e480ec/32.png) [@MolitorS\_Stephen\_L](https://www.ruby-forum.com/u/MolitorS_Stephen_L)\
**Post date:** [April 20, 2006, 9:03pm UTC](https://www.ruby-forum.com/t/re-refactoring-75/58328/7 "2006-04-20T21:03:06Z")

</div>

On 4/20/06, Logan C. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> I guess what I’m saying is I’d rather have explicitly defined  
> generated methods than one method that alters it’s behavior based on  
> its name. Of course these methods would not prevent you from sorting  
> by anythiong else or even still having the method\_missing in there, I  
> just think it looks cleaner, in this case.

That’s what I get for trying to make up an example 😉

Keep in mind this was just an example and may not be the best example.  
Maybe the ActiveRecord find methods which use method\_missing are a  
better example. Of course I’m sure they could be implemented some  
other way as well.

Ryan

---

<div class="post-metadata">

**Author:** ![MolitorS\_Stephen\_L](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/e480ec/32.png) [@MolitorS\_Stephen\_L](https://www.ruby-forum.com/u/MolitorS_Stephen_L)\
**Post date:** [April 20, 2006, 8:53pm UTC](https://www.ruby-forum.com/t/re-refactoring-75/58328/8 "2006-04-20T20:53:51Z")

</div>

On Apr 20, 2006, at 12:33 PM, Matthew M. wrote:

> Method name resolution might be hell, though…  
> I would rather have something like  
> class MusicList  
> sortable\_attr\_accessor :song, :duration # does everything  
> # attr\_accessor used to do,  
> but  
> # also defines #sort\_by\_song,  
> # etc.  
> end

or  
class MusicList  
can\_sort\_by :song, :duration  
end

I guess what I’m saying is I’d rather have explicitly defined  
generated methods than one method that alters it’s behavior based on  
its name. Of course these methods would not prevent you from sorting  
by anythiong else or even still having the method\_missing in there, I  
just think it looks cleaner, in this case.
