Class: ActiveWarehouse::Fact

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
lib/active_warehouse/fact.rb

Overview

Facts represent business measures. A row in a fact table corresponds to set of measurements in a particular granularity along with the foreign keys connecting the fact to various dimensions. All measurements in a fact table must be at the same grain.

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.aggregate_fieldsObject

Get a list of all aggregate fields



11
12
13
# File 'lib/active_warehouse/fact.rb', line 11

def aggregate_fields
  @aggregate_fields
end

.calculated_field_optionsObject

Hash of calculated field options, where the key is the field name and the value is the Hash of options for that calculated field.



18
19
20
# File 'lib/active_warehouse/fact.rb', line 18

def calculated_field_options
  @calculated_field_options
end

.calculated_fieldsObject

Get a list of all calculated fields



14
15
16
# File 'lib/active_warehouse/fact.rb', line 14

def calculated_fields
  @calculated_fields
end

.dimension_relationshipsObject

Array of belongs_to Reflection instances that represent the dimensions for this fact.



22
23
24
# File 'lib/active_warehouse/fact.rb', line 22

def dimension_relationships
  @dimension_relationships
end

Class Method Details

.aggregate_field_for_name(name) ⇒ Object

Get the AggregateField instance for the specified name.



174
175
176
# File 'lib/active_warehouse/fact.rb', line 174

def aggregate_field_for_name(name)
  aggregate_fields.find {|f| f.name.to_s == name.to_s}
end

.calculated_field(field, options = {}, &block) ⇒ Object

Define a calculated field

  • field: The field name

  • options: An options hash

This method takes a block which will be passed the current aggregate record.

Example: calculated_field (:gross_margin) { |r| r.gross_profit_dollar_amount / r.sales_dollar_amount}



145
146
147
# File 'lib/active_warehouse/fact.rb', line 145

def calculated_field(field, options={}, &block)
  calculated_fields << CalculatedField.new(self, field, options[:type], options, &block)
end

.calculated_field_for_name(name) ⇒ Object

Get the CalculatedField instance for the specified name



164
165
166
# File 'lib/active_warehouse/fact.rb', line 164

def calculated_field_for_name(name)
  calculated_fields.find {|f| f.name.to_s == name.to_s}
end

.class_for_name(name) ⇒ Object

Get the class for the specified fact name



97
98
99
# File 'lib/active_warehouse/fact.rb', line 97

def class_for_name(name)
  class_name(name).constantize
end

.class_name(name) ⇒ Object

Get the class name for the specified fact name



90
91
92
93
94
# File 'lib/active_warehouse/fact.rb', line 90

def class_name(name)
  fact_name = name.to_s
  fact_name = "#{fact_name}_facts" unless fact_name =~ /_fact[s?]$/
  fact_name.classify
end

.define_aggregate(field, options = {}) ⇒ Object Also known as: aggregate

Define an aggregate. Also aliased from aggregate()

  • field: The field name

  • options: A hash of options for the aggregate



117
118
119
120
121
122
123
124
125
126
# File 'lib/active_warehouse/fact.rb', line 117

def define_aggregate(field, options={})
  if columns_hash[field.to_s].nil?
    raise ArgumentError, "Field #{field} does not exist in table #{table_name}"
  end
  options[:type] ||= :sum
  
  aggregate_field = AggregateField.new(self, columns_hash[field.to_s],
                                       options[:type], options)
  aggregate_fields << aggregate_field
end

.define_prejoin(field) ⇒ Object Also known as: prejoin

Define prejoined fields from a dimension of the fact. Also aliased from prejoin()

  • field: A hash with the key of dimension and an array

of attributes from the dimension as value



133
134
135
# File 'lib/active_warehouse/fact.rb', line 133

def define_prejoin(field)
  prejoined_fields.merge!(field)
end

.dimension(association_id, options = {}) ⇒ Object

Acts as an alias for belongs_to, yet marks this relationship as a dimension. You must call dimension instead of belongs_to. Accepts same options as belongs_to.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/active_warehouse/fact.rb', line 27

def dimension(association_id, options = {})
  options[:class_name] ||= "#{association_id}Dimension".classify
  options[:foreign_key] ||= "#{association_id}_id"
  slowly_changing_over = options.delete(:slowly_changing)
  belongs_to association_id, options
  dimension_relationship = reflections[association_id]
  
  if slowly_changing_over
    if !dimensions.include?(slowly_changing_over)
      raise "No dimension specified with name '#{slowly_changing_over}' in fact '#{self.name}', specify it first with dimension macro"
    end
    dimension_relationship.slowly_changing_over = dimension_relationships[slowly_changing_over]
  end
  
  dimension_relationships[association_id] = dimension_relationship
end

.dimension_class(dimension_name) ⇒ Object

Returns the dimension class, given a dimension name from this fact. Must appear as a registered dimension relationship.



73
74
75
# File 'lib/active_warehouse/fact.rb', line 73

def dimension_class(dimension_name)
  dimension_relationships[dimension_name.to_sym].class_name.constantize
end

.dimensionsObject

Return a list of dimensions for this fact.

Example:

sales_fact

date_id
region_id
sales_amount
number_items_sold

Calling SalesFact.dimensions would return the list: [:date, :region]



67
68
69
# File 'lib/active_warehouse/fact.rb', line 67

def dimensions
  dimension_relationships.collect { |k,v| k }
end

.field_for_name(name) ⇒ Object

Get the field instance for the specified name. Looks in aggregate fields first, then calculated fields



180
181
182
183
184
# File 'lib/active_warehouse/fact.rb', line 180

def field_for_name(name)
  field = aggregate_fields.find {|f| f.name.to_s == name.to_s}
  field = calculated_fields.find {|f| f.name.to_s == name.to_s} unless field
  field
end

.foreign_key_for(dimension_name) ⇒ Object

Return the foreign key that the fact uses to relate back to the specified dimension. This is found using the dimension_relationships hash.



110
111
112
# File 'lib/active_warehouse/fact.rb', line 110

def foreign_key_for(dimension_name)
  dimension_relationships[dimension_name].primary_key_name
end

.has_semiadditive_fact?Boolean

Returns true if this fact has at least one fact that is semiadditive, or false

Returns:

  • (Boolean)


151
152
153
154
155
156
# File 'lib/active_warehouse/fact.rb', line 151

def has_semiadditive_fact?
  aggregate_fields.each do |field|
    return true if field.is_semiadditive?
  end
  return false
end

.last_modifiedObject

Get the time when the fact source file was last modified



78
79
80
# File 'lib/active_warehouse/fact.rb', line 78

def last_modified
  File.new(__FILE__).mtime
end

.populateObject



204
205
206
# File 'lib/active_warehouse/fact.rb', line 204

def populate
  prejoin_fact.populate
end

.prejoin_factObject



200
201
202
# File 'lib/active_warehouse/fact.rb', line 200

def prejoin_fact
  @prejoin_fact ||= ActiveWarehouse::PrejoinFact.new(self)
end

.prejoined_fieldsObject

Get the hash of all prejoined fields



192
193
194
# File 'lib/active_warehouse/fact.rb', line 192

def prejoined_fields
  @prejoined_fields ||= {}
end

.prejoined_table_nameObject

The table name to use for the prejoined fact table



187
188
189
# File 'lib/active_warehouse/fact.rb', line 187

def prejoined_table_name
  "prejoined_#{table_name}"
end

.slowly_changes_over_class(dimension_name) ⇒ Object

returns the Class for the dimension which the specified dimension_name is slowly changing over



52
53
54
# File 'lib/active_warehouse/fact.rb', line 52

def slowly_changes_over_class(dimension_name)
  dimension_class(slowly_changes_over_name(dimension_name))
end

.slowly_changes_over_name(dimension_name) ⇒ Object

returns the dimension name (as specified in the dimension macro) which the specified dimension_name is slowly changing over



46
47
48
# File 'lib/active_warehouse/fact.rb', line 46

def slowly_changes_over_name(dimension_name)
  dimension_relationships[dimension_name].slowly_changing_over.name
end

.table_nameObject

Get the table name. The fact table name is pluralized



83
84
85
86
87
# File 'lib/active_warehouse/fact.rb', line 83

def table_name
  name = self.name.demodulize.underscore.pluralize
  set_table_name(name)
  name
end

.to_fact(fact_name) ⇒ Object

Get the fact class for the specified value. The fact parameter may be a class, String or Symbol.



103
104
105
106
# File 'lib/active_warehouse/fact.rb', line 103

def to_fact(fact_name)
  return fact_name if fact_name.is_a?(Class) and fact_name.superclass == Fact
  return class_for_name(fact_name)
end