Class: FatCore::Column

Inherits:
Object
  • Object
show all
Defined in:
lib/fat_core/column.rb

Overview

Column objects are just a thin wrapper around an Array to allow columns to be summed and have other operations performed on them, but compacting out nils before proceeding. My original attempt to do this by monkey-patching Array turned out badly. This works much nicer.

Constant Summary collapse

TYPES =
%w(NilClass TrueClass FalseClass Date DateTime Numeric String)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(header:, type: 'NilClass', items: []) ⇒ Column

Returns a new instance of Column.



11
12
13
14
15
16
# File 'lib/fat_core/column.rb', line 11

def initialize(header:, type: 'NilClass', items: [])
  @header = header.as_sym
  @type = type
  raise "Unknown column type '#{type}" unless TYPES.include?(@type.to_s)
  @items = items
end

Instance Attribute Details

#headerObject (readonly)

Returns the value of attribute header.



7
8
9
# File 'lib/fat_core/column.rb', line 7

def header
  @header
end

#itemsObject (readonly)

Returns the value of attribute items.



7
8
9
# File 'lib/fat_core/column.rb', line 7

def items
  @items
end

#typeObject (readonly)

Returns the value of attribute type.



7
8
9
# File 'lib/fat_core/column.rb', line 7

def type
  @type
end

Instance Method Details

#+(other) ⇒ Object

Return a new Column appending the items of other to our items, checking for type compatibility.



40
41
42
43
# File 'lib/fat_core/column.rb', line 40

def +(other)
  raise 'Cannot combine columns with different types' unless type == other.type
  Column.new(header: header, type: type, items: items + other.items)
end

#<<(itm) ⇒ Object



18
19
20
# File 'lib/fat_core/column.rb', line 18

def <<(itm)
  items << convert_to_type(itm)
end

#[](k) ⇒ Object



22
23
24
# File 'lib/fat_core/column.rb', line 22

def [](k)
  items[k]
end

#avgObject



70
71
72
# File 'lib/fat_core/column.rb', line 70

def avg
  sum / items.compact.size.to_d
end

#convert_to_boolean(val) ⇒ Object

Convert the val to a boolean if it looks like one, otherwise return nil. Any boolean or a string of t, f, true, false, y, n, yes, or no, regardless of case is assumed to be a boolean.



141
142
143
144
145
146
147
148
149
150
# File 'lib/fat_core/column.rb', line 141

def convert_to_boolean(val)
  return val if val.is_a?(TrueClass) || val.is_a?(FalseClass)
  val = val.to_s.clean
  return nil if val.blank?
  if val =~ /\Afalse|f|n|no/i
    false
  elsif val =~ /\Atrue|t|y|yes\z/i
    true
  end
end

#convert_to_date_time(val) ⇒ Object

Convert the val to a DateTime if it is either a DateTime, a Date, or a String that can be parsed as a DateTime, otherwise return nil. It only recognizes strings that contain a something like ‘2016-01-14’ or ‘2/12/1985’ within them, otherwise DateTime.parse would treat many bare numbers as dates, such as ‘2841381’, which it would recognize as a valid date, but the user probably does not intend it to be so treated.



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/fat_core/column.rb', line 158

def convert_to_date_time(val)
  return val if val.is_a?(DateTime)
  return val.to_datetime if val.is_a?(Date) && type == 'DateTime'
  return val if val.is_a?(Date)
  begin
    val = val.to_s.clean
    return nil if val.blank?
    return nil unless val =~ %r{\b\d\d\d\d[-/]\d\d?[-/]\d\d?\b}
    val = DateTime.parse(val.to_s.clean)
    val = val.to_date if val.seconds_since_midnight.zero?
    val
  rescue ArgumentError
    return nil
  end
end

#convert_to_numeric(val) ⇒ Object

Convert the val to a Numeric if is already a Numberic or is a String that looks like one. Any Float is promoted to a BigDecimal. Otherwise return nil.



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/fat_core/column.rb', line 177

def convert_to_numeric(val)
  return BigDecimal.new(val, Float::DIG) if val.is_a?(Float)
  return val if val.is_a?(Numeric)
  # Eliminate any commas, $'s, or _'s.
  val = val.to_s.clean.gsub(/[,_$]/, '')
  return nil if val.blank?
  case val
  when /\A(\d+\.\d*)|(\d*\.\d+)\z/
    BigDecimal.new(val.to_s.clean)
  when /\A[\d]+\z/
    val.to_i
  when %r{\A(\d+)\s*[:/]\s*(\d+)\z}
    Rational($1, $2)
  end
end

#convert_to_string(val) ⇒ Object



193
194
195
# File 'lib/fat_core/column.rb', line 193

def convert_to_string(val)
  val.to_s
end

#convert_to_type(val) ⇒ Object

Convert val to the type of key, a ruby class constant, such as Date, Numeric, etc. If type is NilClass, the type is open, and a non-blank val will attempt conversion to one of the allowed types, typing it as a String if no other type is recognized. If the val is blank, and the type is nil, the column type remains open. If the val is nil or a blank and the type is already determined, the val is set to nil, and should be filtered from any column computations. If the val is non-blank and the column type determined, raise an error if the val cannot be converted to the column type. Otherwise, returns the converted val as an object of the correct class.



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/fat_core/column.rb', line 84

def convert_to_type(val)
  case type
  when 'NilClass'
    if val.blank?
      # Leave the type of the column open
      val = nil
    else
      # Only non-blank values are allowed to set the type of the column
      val_class = val.class
      val = convert_to_boolean(val) ||
            convert_to_date_time(val) ||
            convert_to_numeric(val) ||
            convert_to_string(val)
      @type =
        if val.is_a?(Numeric)
          'Numeric'
        else
          val.class.name
        end
      val
    end
  when 'TrueClass', 'FalseClass'
    val_class = val.class
    val = convert_to_boolean(val)
    if val.nil?
      raise "Inconsistent value in a Boolean column #{header} has class #{val_class}"
    end
    val
  when 'DateTime', 'Date'
    val_class = val.class
    val = convert_to_date_time(val)
    unless val
      raise "Inconsistent value in a DateTime column #{key} has class #{val_class}"
    end
    val
  when 'Numeric'
    val_class = val.class
    val = convert_to_numeric(val)
    unless val
      raise "Inconsistent value in a Numeric column #{key} has class #{val_class}"
    end
    val
  when 'String'
    val_class = val.class
    val = convert_to_string(val)
    unless val
      raise "Inconsistent value in a String column #{key} has class #{val_class}"
    end
    val
  else
    raise "Unknown object of class #{type} in Table"
  end
end

#firstObject



45
46
47
# File 'lib/fat_core/column.rb', line 45

def first
  items.compact.first
end

#lastObject



49
50
51
# File 'lib/fat_core/column.rb', line 49

def last
  items.compact.last
end

#last_iObject



34
35
36
# File 'lib/fat_core/column.rb', line 34

def last_i
  size - 1
end

#maxObject



66
67
68
# File 'lib/fat_core/column.rb', line 66

def max
  items.compact.max
end

#minObject



62
63
64
# File 'lib/fat_core/column.rb', line 62

def min
  items.compact.min
end

#rng_sObject

Return a string that of the first and last values.



54
55
56
# File 'lib/fat_core/column.rb', line 54

def rng_s
  "#{first}..#{last}"
end

#sizeObject



30
31
32
# File 'lib/fat_core/column.rb', line 30

def size
  items.size
end

#sumObject



58
59
60
# File 'lib/fat_core/column.rb', line 58

def sum
  items.compact.sum
end

#to_aObject



26
27
28
# File 'lib/fat_core/column.rb', line 26

def to_a
  items
end