using metaprogramming to keep it DRY