Created
December 8, 2015 19:41
-
-
Save eljojo/3682cdbde11f49d6a545 to your computer and use it in GitHub Desktop.
some playing with functional programming and ruby
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 HashManipulation is a helper object. | |
# The idea is to aid when modifying hashes. | |
# changes = HashManipulation.new | |
# changes.key { |key| key.upcase } | |
# changes.value { |value| value.length } | |
# changes.("children" => [1, 2, 3]) # {"CHILDREN" => 3} | |
module Reports | |
class HashManipulation | |
def initialize | |
@key = -> (obj) { obj } | |
@value = -> (obj) { obj } | |
end | |
def key(&block) | |
@key = block | |
self | |
end | |
def value(&block) | |
@value = block | |
self | |
end | |
def call(obj) | |
obj.map do |key, value| | |
[@key.call(key), @value.call(value)] | |
end.to_h | |
end | |
def self.value(&block) | |
self.new.value(&block) | |
end | |
def self.key(&block) | |
self.new.key(&block) | |
end | |
end | |
end |
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 Pipe is intended to be simple way of doing function composition (?) | |
module Reports | |
class Pipe | |
def initialize(procs = []) | |
@procs = procs | |
end | |
delegate :<<, to: :@procs | |
def call(obj) | |
@procs.inject(obj) { |memo, func| func.(memo) } | |
end | |
end | |
end |
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 Reports | |
class RepeatSwitches | |
def initialize | |
@contract_data = ContractDatum.all.ordered.includes(:user) | |
end | |
def report | |
group_by_first_or_second_switch = procerize(:group_by) do |contract| | |
first_switch?(contract) ? "First Switch" : "Repeat Switch" | |
end | |
get_counts = HashManipulation.value {|values| values.length } | |
get_switches_count = HashManipulation.value do |contracts| | |
get_counts.(group_by_first_or_second_switch.(contracts)) | |
end | |
add_totals = HashManipulation.value do |hash| | |
hash.dup.tap do |res| | |
res["Total"] = hash.values.inject(:+) | |
end | |
end | |
pipe = Pipe.new | |
pipe << get_switches_count | |
pipe << add_totals | |
final_function = group_by_month(pipe) | |
final_function.(@contract_data) | |
end | |
def group_by_month(action) | |
group_by_month = procerize(:group_by) do |contract_datum| | |
ordered_at = contract_datum.ordered_at | |
[ordered_at.year, ordered_at.mon] | |
end | |
sort_by_first = -> (hash) { hash.sort_by(&:first).to_h } | |
humanize_month = HashManipulation.key{|(year, month)| "#{year}/#{month}" } | |
pipe = Pipe.new | |
pipe << group_by_month | |
pipe << action | |
pipe << sort_by_first | |
pipe << humanize_month | |
pipe | |
end | |
private | |
def first_switch?(contract_datum) | |
ordered_at = ContractDatum.arel_table[:ordered_at] | |
user_contracts = contract_datum.user.contract_data.ordered | |
user_contracts.where(ordered_at.lt(contract_datum.ordered_at)).empty? | |
end | |
def procerize(method, &block) | |
-> (obj) { obj.send(method, &block) } | |
end | |
end | |
end |
@plexus hey this is super cool! Thanks a lot for your help!
I think I finally found a good example (for myself) so I can practice some functional programming.
thanks for the motivation as well :)
@plexus was faster and way more insightfull than I would have been 👍 .
@bitboxer the intention still counts :)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Good job! I tend to stick functional helper functions in a module that extends itself, so I can call them as "static" methods, or in places where I use them a lot I can include the module
I've generalized your "procerize" a bit here, I've used something similar in some of my code but never could find a good name for it. It's basically "Symbol#to_proc, but also bind variables or a block". I've had it called
send_with_args
or (I kid you not)σ
, but I admit neither of those really stuck :)One interesting observation is that both your HashManipulations and your Pipe are mutable. Why not do something like this?
That way you can do
and you can reuse "partial" pipes
In any case I think it's very cool you're trying these kind of things out. I'll start working on the Happy Lambda again at the end of the year (two more weeks left at my current job), so it's good to get some mental stimulation :D