Last active
August 29, 2015 13:57
-
-
Save andrewtimberlake/9462561 to your computer and use it in GitHub Desktop.
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
# -*- coding: utf-8 -*- | |
require 'fiber' | |
module EachGroup | |
def each_group(*fields, &block) | |
grouper = Grouper.new(*fields, &block) | |
loop_fiber = Fiber.new do | |
each do |result| | |
grouper.process_result(result) | |
end | |
end | |
loop_fiber.resume | |
end | |
class Grouper | |
def initialize(*fields, &block) | |
@current_group = nil | |
@fields = fields | |
@block = block | |
end | |
attr_reader :fields, :block | |
attr_accessor :current_group | |
def process_result(result) | |
group_fiber = get_group_fiber(result) | |
group_fiber.resume(result) if group_fiber.alive? | |
end | |
private | |
def get_group_fiber(result) | |
group_value = fields.map{|f| result.public_send(f) } | |
unless current_group == group_value | |
self.current_group = group_value | |
create_group_fiber(result, group_value) | |
end | |
@group_fiber | |
end | |
def create_group_fiber(result, group_value) | |
@group_fiber = Fiber.new do |first_result| | |
group = Group.new(group_value) | |
block.call(group) | |
end | |
@group_fiber.resume(nil) # Start the fiber and wait for its first yield | |
end | |
end | |
class Group | |
def initialize(value) | |
@value = value | |
end | |
attr_reader :value | |
def each(&block) | |
while result = Fiber.yield | |
block.call(result) | |
end | |
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
# -*- coding: utf-8 -*- | |
require 'spec_helper' | |
require 'ostruct' | |
spec_require 'lib/each_group' | |
describe EachGroup do | |
before(:all) do | |
Array.send(:include, EachGroup) | |
end | |
let(:data) { [ | |
OpenStruct.new(year: 2014, month: 1), | |
OpenStruct.new(year: 2014, month: 11), | |
OpenStruct.new(year: 2013, month: 2), | |
OpenStruct.new(year: 2012, month: 2), | |
] } | |
it "returns the data in groups" do | |
results = [] | |
data.each_group(:year) do |group| | |
results << group.value | |
group.each do |value| | |
results << value | |
end | |
end | |
expect(results).to eq([ | |
[2014], | |
#<OpenStruct year=2014, month=1>, | |
#<OpenStruct year=2014, month=11>, | |
[2013], | |
#<OpenStruct year=2013, month=2>, | |
[2012], | |
#<OpenStruct year=2012, month=2>, | |
]) | |
end | |
it "returns the data in groups without checking the group values" do | |
results = [] | |
data.each_group(:year) do |group| | |
results << group.value | |
end | |
expect(results).to eq([ | |
[2014], | |
[2013], | |
[2012], | |
]) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment