Hamcrest Out Of Test Code!
It's been a while since I read some interesting posts showing creative uses of Hamcrest library out of test code. Since then I've been proscrastinating to implement my own version, trying strongly typed java delegates.
Thankfully this week I came across a nice API called hamcrest-collections. It uses Hamcrest to implement features such as select, reject, map, reduce and zip familiar from languages like Ruby and Python.
Selectors
Selectors can be used to select or reject items that matches a given Matcher, from any iterable object. It reminds me the Specification Pattern from Domain-Driven Design, which is also used for querying objects that satisfies defined specifications.
public static final Person john = new Person("John", 28);
public static final Person nicole = new Person("Nicole", 12);
public static final Person ryan = new Person("Ryan", 23);
public static final Person nathan = new Person("Nathan", 18);
public static final List list() {
return Arrays.asList(john, nicole, ryan, nathan);
}
The code below selects from the list of users defined above, the ones that are under twenty.
@Test
public void should_select_only_people_under_twenty_years_old() {
List users = Person.list();
Iterable underTwentyList = select(users, underAge(20));
assertThat(underTwentyList, hasItems(nicole, nathan));
assertThat(underTwentyList, not(hasItems(john, ryan)));
}
The code below rejects all the users that are under twenty.
@Test
public void should_reject_every_people_under_twenty_years_old() {
List users = Person.list();
Iterable aboveTwentyList = reject(users, underAge(20));
assertThat(aboveTwentyList, hasItems(john, ryan));
assertThat(aboveTwentyList, not(hasItems(nicole, nathan)));
}
Map and Reduce
Map is used to apply a function onto each item in any iterable object, whereas Reduce combines all these elements, applying a Reducer implementation. In our example, we map the timesTwo function, that doubles each element in the list, and then we reduce it by adding up all of them.
@Test
public void should_double_each_number_in_the_list_then_sum_all_of_them() {
List numbers = Arrays.asList(1, 2, 3);
MultiplyBy timesTwo = new MultiplyBy(2);
Iterable result = map(numbers, timesTwo);
assertThat(result, hasItems(2, 4, 6));
Integer sum = reduce(result, new Sum());
assertThat(sum, equalTo(12));
}
public class MultiplyBy implements Function{ private Integer factor; public MultiplyBy(Integer factor) { this.factor = factor; } public Integer apply(Integer number) { return (int)number * factor; } }
public class Sum implements Reducer{ public Integer apply(Integer first, Integer second) { return first + second; } }
Despite the bias created by some developers, that Hamcrest should not be used anywhere else but test code, specially after JUnit has defined it as its new matcher library, just ignore it and add these features to your runtime library, so that you can let your creativity drive you when developing. Get rid of "for" loops from your life!
Value Objects & RoR
Currently I'm working on my personal project, aiming to increase my Ruby On Rails skills. It's quite a simple system for my dad's company and one of the features is to register his clients. I started it out writing the Specs and as I'm new to the Ruby world, doubts started to pop inside my head. I was wondering how to use Value Objects ( Eric Evans definition, please! ) integrated with RoR, something I can do with Java as shown below:
public class Phone() {
private int code;
private int number;
....
}
public class Client() {
private String name;
private Phone home;
private Phone mobile;
....
}
Chatting with Carlos Villela, he advised me to take a look at composed_of feature, that it would help me get the solution for my problem. The final result is shown below. I'm definitely not used to the Ruby syntax yet, for me it's still a bit confusing.
class Phone
attr_reader :code, :number
def initialize(code, number)
@code, @number = code, number
end
def ==(other)
code==other.code && number==other.number
end
end
class Client < ActiveRecord::Base
composed_of :home, :class_name => "Phone",
:mapping => [ %w(home_code code), %w(home_number number) ]
composed_of :mobile, :class_name => "Phone",
:mapping => [ %w(mob_code code), %w(mob_number number) ]
end