Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

wip

  • Loading branch information...
commit 6e77942ee5782907cab1a2aa4ce5cf24ebd29e08 1 parent edfd899
@adamhunter authored
View
1  Rakefile
@@ -1,4 +1,5 @@
#!/usr/bin/env rake
+
begin
require 'bundler/setup'
rescue LoadError
View
0  dossier_test
No changes.
View
1  lib/dossier.rb
@@ -41,5 +41,6 @@ class ExecuteError < StandardError; end
require "dossier/report"
require "dossier/responder"
require "dossier/result"
+require "dossier/segmenter"
require "dossier/stream_csv"
require "dossier/xls"
View
12 lib/dossier/report.rb
@@ -8,9 +8,17 @@ class Report
attr_reader :options
attr_accessor :parent
+ def self.inherited(base)
+ base.const_set(:Segmenter, Class.new(Dossier::Segmenter))
+ end
+
def self.filename
"#{report_name.parameterize}-report_#{Time.now.strftime('%m-%d-%Y_%H-%M-%S')}"
end
+
+ def self.segmenter
+ const_get(:Segmenter)
+ end
def initialize(options = {})
@options = options.dup.with_indifferent_access
@@ -39,6 +47,10 @@ def run
tap { execute }
end
+ def segmenter
+ @segmenter ||= self.class.segmenter.new(self)
+ end
+
def formatter
Dossier::Formatter
end
View
119 lib/dossier/segmenter.rb
@@ -0,0 +1,119 @@
+module Dossier
+ class Segmenter
+ attr_accessor :report
+
+ delegate :segments, to: "self.class"
+
+ def self.segments
+ @segments ||= SegmentChain.new
+ end
+
+ def self.segment(group_by, options = {}, &block)
+ segments << SegmentDefinition.new(self, group_by, options.fetch(:display_name, group_by))
+ instance_eval(&block) if block_given?
+ end
+
+ def initialize(report)
+ self.report = report
+ segment_definitions.first.connect(self)
+ end
+
+ def data
+ @data ||= report.results.rows.inject(default_data) { |acc, row|
+ acc.tap do |hash|
+ end
+ }
+ end
+
+ def default_data
+ segment_definitions.map(&:group_by).reduce({}) do |acc, group_by|
+ acc.tap { |hash| hash[group_by] = {} }
+ end
+ end
+
+ def segment_definitions
+ self.class.segments
+ end
+
+ def segments
+ @segments ||= segment_definitions.map { |definition| definition.segment_class.new(self, definition) }
+ end
+ end
+
+ class SegmentChain
+ include Enumerable
+
+ def initialize
+ @segment_chain = []
+ end
+
+ def at(index)
+ segment_chain.at(index)
+ end
+ alias :[] :at
+
+ def <<(segment)
+ segment_chain.last.connect(segment) if segment_chain.any?
+ segment_chain << segment
+ end
+
+ def each
+ segment_chain.each { |segment| yield segment }
+ end
+
+ private
+ attr_reader :segment_chain
+ end
+
+ class SegmentDefinition
+ attr_accessor :segmenter, :group_by, :display_name, :parent, :child
+
+ def initialize(segmenter, group_by, display_name)
+ self.segmenter = segmenter
+ self.group_by = group_by
+ self.display_name = display_name
+ define_segment_class
+ end
+
+ def connect(segment)
+ self.parent = segment
+ segment.extend proxy_module
+ end
+
+ def proxy_module
+ segment = self
+ Module.new do
+ define_method(segment.link_method) do
+ end
+ end
+ end
+
+ def link_method
+ group_by.to_s.pluralize
+ end
+
+ def segment_class_name
+ group_by.to_s.classify
+ end
+
+ def segment_class
+ segmenter.const_get(segment_class_name)
+ end
+
+ private
+
+ def define_segment_class
+ segmenter.const_set(segment_class_name, Class.new(Segment))
+ end
+ end
+
+ class Segment
+ attr_accessor :segmenter, :report, :definition
+
+ def initialize(segmenter, definition)
+ self.segmenter = segmenter
+ self.report = segmenter.report
+ self.definition = definition
+ end
+ end
+end
View
10 spec/dossier/report_spec.rb
@@ -94,4 +94,14 @@ def sql; ''; end
end
end
+ describe "segmenters" do
+ it "creates a segment subclass under its own namespace when inherited" do
+ expect(EmployeeReport::Segmenter.ancestors).to include(Dossier::Segmenter)
+ end
+
+ it "has a segmenter" do
+ expect(report.segmenter).to be_a TestReport::Segmenter
+ end
+ end
+
end
View
76 spec/dossier/segmenter_spec.rb
@@ -0,0 +1,76 @@
+require 'spec_helper'
+
+describe Dossier::Segmenter do
+
+ let(:segmenter_class) {
+ Class.new(Dossier::Segmenter) {
+ segment :s1
+ segment :s2, display_name: :s2_name do
+ segment :s3, display_name: ->(row) { "#{row[:s1]} - #{row[:s3]}" }
+ end
+ }
+ }
+ let(:report) { Class.new(Dossier::Report).new }
+ let(:segmenter) { segmenter_class.new(report) }
+
+ describe "report data" do
+ it "stores the report rows in a hash organized by segment" do
+ expect(segmenter.data).to eq({s1: {s2: {s3: []}}})
+ end
+ end
+
+ describe "segments" do
+ let(:segmenter) { segmenter_class }
+
+ it "keeps an array of segments" do
+ expect(segmenter.segments.count).to eq 3
+ end
+
+ it "keeps the segments in the order defined" do
+ expect(segmenter.segments[1].group_by).to eq :s2
+ end
+
+ it "allows nesting segment definations" do
+ expect(segmenter.segments[2].group_by).to eq :s3
+ end
+
+ describe "display_name" do
+ it "allows passing a symbol" do
+ expect(segmenter.segments[1].display_name).to eq :s2_name
+ end
+
+ it "allows passing a proc" do
+ expect(segmenter.segments[2].display_name).to be_a(Proc)
+ end
+ end
+ end
+
+ describe "segment chain" do
+ describe "base" do
+ it "takes the report upon instantiation" do
+ expect(segmenter.report).to eq report
+ end
+
+ it "has access to its classes segments" do
+ expect(segmenter.segments).to eq segmenter.class.segments
+ end
+
+ it "will have an s1s" do
+ expect(segmenter).to respond_to(:s1s)
+ end
+ end
+
+ describe "s1s" do
+ it "will have an s2s" do
+ expect(segmenter.s1s.first).to respond_to(:s2s)
+ end
+ end
+
+ describe "s2s" do
+ it "will have an s3s" do
+ expect(segmenter.s2s.first).to respond_to(:s3s)
+ end
+ end
+ end
+
+end
Please sign in to comment.
Something went wrong with that request. Please try again.