-
-
Save Masa331/a8ed8aca0d63c6d7885c706cfe5fe0e5 to your computer and use it in GitHub Desktop.
Was chatting with @mfeathers about retaining Ruby's chained Enumerable style, but finding a way to inject names that reflects the application domain (as opposed to just littering functional operations everywhere, which may be seen as a sort of Primitive Obsession)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# A little toy file demonstrating how to build chainable | |
# data transformations that reveal some amount of intent | |
# through named extracted methods. | |
# | |
# Kudos to @mfeathers for giving me the idea to try this | |
# | |
# Copyright Test Double, LLC, 2016. All Rights Reserved. | |
require_relative "marketing_refinements" | |
class MarketResearch | |
# Vanilla / Anonymous / Primitive approach to chaining | |
def income_by_smoking(data) | |
Hash[ | |
data.reject {|p| p[:income] < 10_000 }. | |
group_by { |p| p[:smoker] }. | |
map { |(is_smoker, people)| | |
[ | |
is_smoker ? :smokers : :non_smokers, | |
people.map {|p| p[:income]}.reduce(:+).to_f / people.size | |
] | |
} | |
] | |
end | |
# Refined approach to tacking named domain abstractions onto Array/Hash | |
using MarketingRefinements | |
def income_by_smoking_fancy(data) | |
data.exclude_incomes_under(10_000). | |
separate_people_by(:smoker). | |
average_income_by_smoking | |
end | |
end | |
DATA = [ | |
{age: 19, smoker: false, income: 10_000, education: :high_school}, | |
{age: 49, smoker: true, income: 120_000, education: :bachelors}, | |
{age: 55, smoker: false, income: 400_000, education: :masters}, | |
{age: 23, smoker: true, income: 10_000, education: :bachelors}, | |
{age: 70, smoker: false, income: 70_000, education: :phd }, | |
{age: 34, smoker: false, income: 90_000, education: :masters}, | |
{age: 90, smoker: true, income: 0, education: :high_school}, | |
] | |
original_result = MarketResearch.new.income_by_smoking(DATA) | |
fancy_result = MarketResearch.new.income_by_smoking_fancy(DATA) | |
puts <<-MSG | |
Original result: #{original_result} | |
Fancy result: #{fancy_result} | |
MSG |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module MarketingRefinements | |
refine Array do | |
# Domain-specific | |
def exclude_incomes_under(min) | |
reject {|p| p[:income] < min } | |
end | |
def separate_people_by(attribute) | |
group_by { |p| p[attribute] } | |
end | |
# General-purpose | |
def average(attr) | |
map {|el| el[attr]}.reduce(:+).to_f / size | |
end | |
end | |
refine Hash do | |
# Domain-specific | |
def average_income_by_smoking | |
Hash[ | |
transform_keys { |is_smoker| | |
is_smoker ? :smokers : :non_smokers | |
}.map {|key, people| | |
[key, people.average(:income)] | |
} | |
] | |
end | |
# General-purpose | |
def transform_keys | |
{}.tap do |result| | |
self.each_key do |key| | |
result[yield(key)] = self[key] | |
end | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment