Class: CountStruct

Inherits:
Object
  • Object
show all
Defined in:
lib/count_struct.rb,
lib/count_struct/no_total.rb

Overview

The Struct-like metaclass for counting.

count = CountStruct.new(:cats, :dogs).new
>> count.cats
=> 0
>> count.cats += 1
>> count.dogs += 3
>> count.to_h
=> {:cats=>1, :dogs=>3, :total=>4}
>> count.dogs = 0
>> count.to_h
=> {:cats=>1, :dogs=>0, :total=>1}

Using in a class:

class AnimalReport
  Count = CountStruct.new(:cats, :dogs)      # Use a regular constant for the class. DO NOT use `class Count < ...` due to superclass mismatch error.

  def add_cat(cat)
    count.cats += 1
    ...
  end

  def add_dog(dog)
    count.dogs += 1
    ...
  end

  def count
    @count ||= Count.new
  end
end

Defined Under Namespace

Classes: NoTotal

Class Method Summary collapse

Class Method Details

.new(*keys) ⇒ Object

Raises:

  • (ArgumentError)

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/count_struct.rb', line 37

def self.new(*keys)
  raise ArgumentError, "At least one key is required" if keys.empty?
  raise ArgumentError, "Key 'total' is not allowed" if keys.map(&:to_s).include? "total"

  klass = Class.new

  klass.class_exec(keys) do |keys|
    keys.each do |k|
      eval %{
        def #{k}
          @#{k} ||= 0
        end

        def #{k}=(value)
          @#{k} = value
        end
      }
    end

    eval "def clear; " + keys.map {|k| "remove_instance_variable :@#{k}"}.join("; ") + "; end"

    # Build something like: `"#<count cats=#{send(:cats)} dogs=#{send(:dogs)} | total=#{total}>"`.
    eval 'def inspect; "#<count ' + keys.map {|k| "#{k}=\#{send(:#{k})}"}.join(" ") + ' | total=#{total}>"; end'

    # Build something like: `{cats: send(:cats), dogs: send(:dogs), total: send(:total), ...}`.
    eval "def to_h; {" + (keys + [:total]).map {|k| "#{k}: send(:#{k})"}.join(", ") + "}; end"

    def to_s
      inspect
    end

    # Build something like: `send(:cats) + send(:dogs)`.
    eval "def total; " + keys.map {|k| "send(:#{k})"}.join(" + ") + "; end"
  end # class_exec

  klass
end