block composition?

A

aa

Hi,

I'm implementing a plain simple search functionality over an array of
objects. One of the attributes that those objects expose is a creation
date (a Time object). The search functionality should work in 3 ways:
- starting from a date
- up to a date
- within a certain range

In the first implementation I had the same code repeated in different
search methods:

def search_from(start)
@list.select {|s| s.creation >= start}
end
def search_to(_end)
@list.select {|s| s.creation <= _end}
end
def search(start, _end)
@list.select {|s| s.creation >= start && s.creation <= _end}
end

So I wanted to remove the duplication and I thought about defining
search_from and search_to in terms of search. I tried using some sort
of block composition by means of lambda functions, but I didn't really
like it:

def search_from(start)
search(start, nil)
end
def search_to(_end)
search(nil, _end)
end
def search(start, _end)
upper_limit = if start.nil?
lambda {|s| true}
else
lambda {|s| s.creation >= start}
end
lower_limit = if _end.nil?
lambda {|s| true}
else
lambda {|s| s.creation <= _end}
end
@list.select {|s| upper_limit.call(s) && lower_limit.call(s)}
end

In the end I've decided to override <=> on Time so that if you compare
with nil it returns 0 but:
- it's really ugly
- as a side effect Time.mktime(2007) == nil evaluates to true (while
of course Time.mktime(2007).nil? is false)
- your code may break in unexpected ways

Does anybody know a better approach towards block composition?
Thanks! AA

PS: here is the current code with the ugly, horrible, too bad Time.<=>
override.

class Time
alias_method :comp, "<=>".intern
def <=>(other)
if other.nil?
0
else
comp(other)
end
end
end

class Stuff
attr_accessor :name
attr_accessor :creation
def initialize(name, creation)
@name = name
@creation = creation
end
end

class Repository
def initialize(list)
@list = list
end
def search_from(start)
search(start, nil)
end
def search_to(_end)
search(nil, _end)
end
def search(start, _end)
@list.select {|s| s.creation >= start && s.creation <= _end}
end
end

require 'test/unit'

class TestRepository < Test::Unit::TestCase
def setup
@jan = Stuff.new("jan", Time.mktime(2007, 1))
@feb = Stuff.new("feb", Time.mktime(2007, 2))
@rep = Repository.new([@jan, @feb])
end
def test_search_returns_values_starting_from_creation_date
assert_equal([], @rep.search_from(Time.mktime(2008)))
assert_equal([@feb], @rep.search_from(Time.mktime(2007, 1, 15)))
assert_equal([@jan, @feb], @rep.search_from(Time.mktime(2006,
12)))
end
def test_search_returns_values_up_to_creation_date
assert_equal([], @rep.search_to(Time.mktime(2006)))
assert_equal([@jan], @rep.search_to(Time.mktime(2007, 1, 15)))
assert_equal([@jan, @feb], @rep.search_to(Time.mktime(2008)))
end
def test_search_returns_values_within_a_range_of_creation_dates
assert_equal([], @rep.search(Time.mktime(2005),
Time.mktime(2006)))
assert_equal([@jan], @rep.search(Time.mktime(2006),
Time.mktime(2007, 1, 15)))
assert_equal([@jan, @feb], @rep.search(Time.mktime(2006),
Time.mktime(2008)))
end
end
 
A

ara.t.howard

I'm implementing a plain simple search functionality over an array of
objects. One of the attributes that those objects expose is a creation
date (a Time object). The search functionality should work in 3 ways:
- starting from a date
- up to a date
- within a certain range

In the first implementation I had the same code repeated in different
search methods:

def search_from(start)
@list.select {|s| s.creation >= start}
end
def search_to(_end)
@list.select {|s| s.creation <= _end}
end
def search(start, _end)
@list.select {|s| s.creation >= start && s.creation <= _end}
end

why not something simple like

def search &filter
@list.select &filter
end

def search_from time
search{|s| s.creation and s.creation >= time}
end

def search_in time_range
search{|s| s.creation and time_range === s.creation}
end

def search_until time
search{|s| s.creation and s.creation <= time}
end

??

a @ http://codeforpeople.com/
 
A

aa

why not something simple like
[good stuff snipped]

Well, because:
- I haven't thought about using a Time range
- it didn't occur to me that passing the filter to the iterator was
the best idea to decouple iteration with selection criteria
Quite neat!

Nevertheless, I'm still interested to know if anybody has ever
composed different blocks together within the same iteration (and
how).

Thanks a lot.
AA
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,072
Latest member
trafficcone

Latest Threads

Top