Instantly share code, notes, and snippets.

Embed
What would you like to do?
# -*- coding: utf-8 -*-
require 'fiber'
module EachGroup
def each_group(*fields, &block)
loop_fiber = Fiber.new do
current_value = nil
group_fiber = nil
each do |result|
group_value = Array(fields).map{|f| result.public_send(f) }
unless current_value == group_value
current_value = 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
group_fiber.resume(result) if group_fiber.alive? # Send the result to the group fiber
end
group_fiber.resume(nil) if group_fiber && group_fiber.alive? # Final yield to finish off the group's internal loop
end
loop_fiber.resume(nil) # Start the outer loop
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
# -*- 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