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)
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
# -*- 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