Created
November 30, 2014 06:51
Revisions
-
Jeanine Adkisson created this gist
Nov 30, 2014 .There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,100 @@ class Variant class Caser attr_reader :value def initialize(variant) @variant = variant @invoked = false end def try_case(tag, &b) if @variant.tag == tag @invoked = true @value = yield(*@variant.values) end end def invoked? @invoked end end def self.spec @spec ||= {} end def self.caser @caser ||= Class.new(Caser) end attr_reader :values def initialize(*values) @values = values end def self.variant(tag, *names) parent_class = self klass = spec[tag] = Class.new(self) klass.class_eval do names.each_with_index do |name, i| define_method(name) { @values[i] } end define_method(:type) { parent_class } define_method(:tag) { tag } end caser.class_eval do define_method(tag) { |&b| try_case(tag, &b) } end (class << self; self; end).class_eval do define_method(tag) { |*values| klass.new(*values) } end end def inspect "#<#{type}.#{tag}(#{values.map(&:inspect).join(', ')})>" end def cases(cases={}, &b) if block_given? caser = self.class.caser.new(self) yield caser if caser.invoked? caser.value else non_exhaustive_cases! end else selection = cases[tag] || cases[:else] || non_exhaustive_cases! selection.call(*values) end end end class Order < Variant variant :delivery, :address variant :digital, :email variant :pickup, :store_id end # n constructors # Order.delivery('123 Main St') # Order.digital('customer@example.com') # Order.pickup(456) # # order.cases( # delivery: ->(address) { "delivering to #{address}" }, # digital: ->(email) { "emailing to #{email}" }, # pickup: ->(store_id) { # "picking up from #{Store.find(store_id).address}" # }, # ) # # order.cases do |c| # c.delivery { |address| "delivering to #{address}" } # c.digital { |email| "emailing to #{email}" } # c.pickup do |store_id| # "picking up from #{Store.find(store_id).address}" # end # end