mainej-activewarehouse 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/activewarehouse/README +99 -0
  2. data/activewarehouse/Rakefile +165 -0
  3. data/activewarehouse/TODO +4 -0
  4. data/activewarehouse/db/migrations/001_create_table_reports.rb +28 -0
  5. data/activewarehouse/doc/references.txt +4 -0
  6. data/activewarehouse/generators/bridge/USAGE +1 -0
  7. data/activewarehouse/generators/bridge/bridge_generator.rb +46 -0
  8. data/activewarehouse/generators/bridge/templates/fixture.yml +5 -0
  9. data/activewarehouse/generators/bridge/templates/migration.rb +27 -0
  10. data/activewarehouse/generators/bridge/templates/model.rb +3 -0
  11. data/activewarehouse/generators/bridge/templates/unit_test.rb +8 -0
  12. data/activewarehouse/generators/cube/USAGE +1 -0
  13. data/activewarehouse/generators/cube/cube_generator.rb +28 -0
  14. data/activewarehouse/generators/cube/templates/model.rb +3 -0
  15. data/activewarehouse/generators/cube/templates/unit_test.rb +8 -0
  16. data/activewarehouse/generators/date_dimension/USAGE +1 -0
  17. data/activewarehouse/generators/date_dimension/date_dimension_generator.rb +16 -0
  18. data/activewarehouse/generators/date_dimension/templates/fixture.yml +5 -0
  19. data/activewarehouse/generators/date_dimension/templates/migration.rb +31 -0
  20. data/activewarehouse/generators/date_dimension/templates/model.rb +3 -0
  21. data/activewarehouse/generators/date_dimension/templates/unit_test.rb +8 -0
  22. data/activewarehouse/generators/dimension/USAGE +1 -0
  23. data/activewarehouse/generators/dimension/dimension_generator.rb +46 -0
  24. data/activewarehouse/generators/dimension/templates/fixture.yml +5 -0
  25. data/activewarehouse/generators/dimension/templates/migration.rb +11 -0
  26. data/activewarehouse/generators/dimension/templates/model.rb +3 -0
  27. data/activewarehouse/generators/dimension/templates/unit_test.rb +8 -0
  28. data/activewarehouse/generators/dimension_view/USAGE +1 -0
  29. data/activewarehouse/generators/dimension_view/dimension_view_generator.rb +62 -0
  30. data/activewarehouse/generators/dimension_view/templates/migration.rb +17 -0
  31. data/activewarehouse/generators/dimension_view/templates/model.rb +3 -0
  32. data/activewarehouse/generators/dimension_view/templates/unit_test.rb +10 -0
  33. data/activewarehouse/generators/fact/USAGE +1 -0
  34. data/activewarehouse/generators/fact/fact_generator.rb +46 -0
  35. data/activewarehouse/generators/fact/templates/fixture.yml +5 -0
  36. data/activewarehouse/generators/fact/templates/migration.rb +13 -0
  37. data/activewarehouse/generators/fact/templates/model.rb +3 -0
  38. data/activewarehouse/generators/fact/templates/unit_test.rb +10 -0
  39. data/activewarehouse/generators/time_dimension/USAGE +1 -0
  40. data/activewarehouse/generators/time_dimension/templates/fixture.yml +5 -0
  41. data/activewarehouse/generators/time_dimension/templates/migration.rb +12 -0
  42. data/activewarehouse/generators/time_dimension/templates/model.rb +3 -0
  43. data/activewarehouse/generators/time_dimension/templates/unit_test.rb +8 -0
  44. data/activewarehouse/generators/time_dimension/time_dimension_generator.rb +14 -0
  45. data/activewarehouse/init.rb +1 -0
  46. data/activewarehouse/install.rb +5 -0
  47. data/activewarehouse/lib/active_warehouse.rb +91 -0
  48. data/activewarehouse/lib/active_warehouse/aggregate.rb +75 -0
  49. data/activewarehouse/lib/active_warehouse/aggregate/dwarf_aggregate.rb +369 -0
  50. data/activewarehouse/lib/active_warehouse/aggregate/dwarf_common.rb +44 -0
  51. data/activewarehouse/lib/active_warehouse/aggregate/dwarf_printer.rb +34 -0
  52. data/activewarehouse/lib/active_warehouse/aggregate/no_aggregate.rb +212 -0
  53. data/activewarehouse/lib/active_warehouse/aggregate/pid_aggregate.rb +29 -0
  54. data/activewarehouse/lib/active_warehouse/aggregate_field.rb +59 -0
  55. data/activewarehouse/lib/active_warehouse/bridge.rb +19 -0
  56. data/activewarehouse/lib/active_warehouse/bridge/hierarchy_bridge.rb +46 -0
  57. data/activewarehouse/lib/active_warehouse/builder.rb +3 -0
  58. data/activewarehouse/lib/active_warehouse/builder/date_dimension_builder.rb +91 -0
  59. data/activewarehouse/lib/active_warehouse/builder/generator/generator.rb +13 -0
  60. data/activewarehouse/lib/active_warehouse/builder/generator/name_generator.rb +20 -0
  61. data/activewarehouse/lib/active_warehouse/builder/generator/paragraph_generator.rb +11 -0
  62. data/activewarehouse/lib/active_warehouse/builder/random_data_builder.rb +239 -0
  63. data/activewarehouse/lib/active_warehouse/builder/test_data_builder.rb +54 -0
  64. data/activewarehouse/lib/active_warehouse/calculated_field.rb +27 -0
  65. data/activewarehouse/lib/active_warehouse/compat/compat.rb +49 -0
  66. data/activewarehouse/lib/active_warehouse/core_ext.rb +1 -0
  67. data/activewarehouse/lib/active_warehouse/core_ext/time.rb +5 -0
  68. data/activewarehouse/lib/active_warehouse/core_ext/time/calculations.rb +40 -0
  69. data/activewarehouse/lib/active_warehouse/cube.rb +235 -0
  70. data/activewarehouse/lib/active_warehouse/cube_query_result.rb +69 -0
  71. data/activewarehouse/lib/active_warehouse/dimension.rb +329 -0
  72. data/activewarehouse/lib/active_warehouse/dimension/date_dimension.rb +15 -0
  73. data/activewarehouse/lib/active_warehouse/dimension/dimension_reflection.rb +21 -0
  74. data/activewarehouse/lib/active_warehouse/dimension/dimension_view.rb +27 -0
  75. data/activewarehouse/lib/active_warehouse/dimension/hierarchical_dimension.rb +99 -0
  76. data/activewarehouse/lib/active_warehouse/dimension/slowly_changing_dimension.rb +147 -0
  77. data/activewarehouse/lib/active_warehouse/fact.rb +239 -0
  78. data/activewarehouse/lib/active_warehouse/field.rb +74 -0
  79. data/activewarehouse/lib/active_warehouse/migrations.rb +64 -0
  80. data/activewarehouse/lib/active_warehouse/ordered_hash.rb +34 -0
  81. data/activewarehouse/lib/active_warehouse/prejoin_fact.rb +97 -0
  82. data/activewarehouse/lib/active_warehouse/report.rb +7 -0
  83. data/activewarehouse/lib/active_warehouse/report/abstract_report.rb +149 -0
  84. data/activewarehouse/lib/active_warehouse/report/chart_report.rb +9 -0
  85. data/activewarehouse/lib/active_warehouse/report/data_cell.rb +21 -0
  86. data/activewarehouse/lib/active_warehouse/report/data_column.rb +19 -0
  87. data/activewarehouse/lib/active_warehouse/report/data_row.rb +15 -0
  88. data/activewarehouse/lib/active_warehouse/report/dimension.rb +58 -0
  89. data/activewarehouse/lib/active_warehouse/report/table_report.rb +38 -0
  90. data/activewarehouse/lib/active_warehouse/version.rb +9 -0
  91. data/activewarehouse/lib/active_warehouse/view.rb +9 -0
  92. data/activewarehouse/lib/active_warehouse/view/crumb.rb +64 -0
  93. data/activewarehouse/lib/active_warehouse/view/report_helper.rb +98 -0
  94. data/activewarehouse/lib/active_warehouse/view/table_view.rb +134 -0
  95. data/activewarehouse/lib/active_warehouse/view/yui_adapter.rb +68 -0
  96. data/activewarehouse/tasks/active_warehouse_tasks.rake +122 -0
  97. metadata +237 -0
@@ -0,0 +1,74 @@
1
+ module ActiveWarehouse #:nodoc:
2
+ # Encapsulates a field.
3
+ class Field
4
+ # The owning class which is either a Fact or Dimension
5
+ attr_reader :owning_class
6
+
7
+ # The field name
8
+ attr_reader :name
9
+
10
+ # The field type
11
+ attr_reader :type
12
+
13
+ attr_accessor :limit
14
+ attr_accessor :scale
15
+ attr_accessor :precision
16
+
17
+ # A Hash of options for the field
18
+ attr_reader :field_options
19
+
20
+ # +owning_class+ is the class of the table, either Fact or Dimension, that
21
+ # this field is found in. Must somehow subclass ActiveRecord::Base
22
+ # +name+ is the name of this field.
23
+ # +field_options+ is a hash of raw options from the original definition.
24
+ # Options can include :label => a column alias or label for this field,
25
+ # :table_alias for a table alias (useful for building queries)
26
+ def initialize(owning_class, name, type, field_options = {})
27
+ @owning_class = owning_class
28
+ @name = name
29
+ @type = type
30
+ @field_options = field_options
31
+ @label = field_options[:label]
32
+ @table_alias = field_options[:table_alias]
33
+ end
34
+
35
+ # returns the :label set in field_options, or from_table_name+'_'+name.
36
+ # Unless you have table_alias specified, then label will return table_alias+'_'+name.
37
+ # The default label can exceed database limits, so use :label to override.
38
+ def label
39
+ @label ? @label : "#{table_alias || from_table_name}_#{name}"
40
+ end
41
+
42
+ # returns name of this field, matches name of the column
43
+ def name
44
+ @name
45
+ end
46
+
47
+ # returns rails specific column type, e.g. :float or :string
48
+ def column_type
49
+ @type
50
+ end
51
+
52
+ # convert the label into something we can use in a table.
53
+ # i.e., 'Sum of Transactions' becomes 'sum_of_transactions'
54
+ def label_for_table
55
+ label.gsub(/ /, '_').downcase
56
+ end
57
+
58
+ # returns the table name that has this fact column
59
+ def from_table_name
60
+ owning_class.table_name
61
+ end
62
+
63
+ # returns a table alias or if none set just the table name
64
+ def table_alias
65
+ @table_alias || from_table_name
66
+ end
67
+
68
+ # Get a display string for the field. Delegates to label.
69
+ def to_s
70
+ label
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,64 @@
1
+ module ActiveWarehouse #:nodoc:
2
+ # Responsible for migrating ActiveWarehouse tables which are automatically created.
3
+ class Migrator < ActiveRecord::Migrator
4
+ class << self
5
+ def schema_info_table_name #:nodoc:
6
+ ActiveRecord::Base.table_name_prefix + 'activewarehouse_schema_info' + ActiveRecord::Base.table_name_suffix
7
+ end
8
+
9
+ def current_version #:nodoc:
10
+ result = ActiveRecord::Base.connection.select_one("SELECT version FROM #{schema_info_table_name}")
11
+ if result
12
+ result['version'].to_i
13
+ else
14
+ # There probably isn't an entry for this plugin in the migration info table.
15
+ # We need to create that entry, and set the version to 0
16
+ ActiveRecord::Base.connection.execute("INSERT INTO #{schema_info_table_name} (version) VALUES (0)")
17
+ 0
18
+ end
19
+ end
20
+ end
21
+
22
+ def set_schema_version(version)
23
+ ActiveRecord::Base.connection.update("UPDATE #{self.class.schema_info_table_name} SET version = #{down? ? version.to_i - 1 : version.to_i}")
24
+ end
25
+ end
26
+ end
27
+
28
+ module ActiveRecord #:nodoc:
29
+ module ConnectionAdapters #:nodoc:
30
+ module SchemaStatements #:nodoc:
31
+ def initialize_schema_information_with_activewarehouse
32
+ initialize_schema_information_without_activewarehouse
33
+
34
+ begin
35
+ execute "CREATE TABLE #{ActiveWarehouse::Migrator.schema_info_table_name} (version #{type_to_sql(:integer)})"
36
+ rescue ActiveRecord::StatementInvalid
37
+ # Schema has been initialized
38
+ end
39
+ end
40
+ alias_method_chain :initialize_schema_information, :activewarehouse
41
+
42
+ def dump_schema_information_with_activewarehouse
43
+ schema_information = []
44
+
45
+ dump = dump_schema_information_without_activewarehouse
46
+ schema_information << dump if dump
47
+
48
+ begin
49
+ plugins = ActiveRecord::Base.connection.select_all("SELECT * FROM #{ActiveWarehouse::Migrator.schema_info_table_name}")
50
+ plugins.each do |plugin|
51
+ if (version = plugin['version'].to_i) > 0
52
+ schema_information << "INSERT INTO #{ActiveWarehouse::Migrator.schema_info_table_name} (version) VALUES (#{version})"
53
+ end
54
+ end
55
+ rescue ActiveRecord::StatementInvalid
56
+ # No Schema Info
57
+ end
58
+
59
+ schema_information.join("\n")
60
+ end
61
+ alias_method_chain :dump_schema_information, :activewarehouse
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,34 @@
1
+ module ActiveWarehouse
2
+ # Simple ordered hash implementation
3
+ class OrderedHash < Hash
4
+ alias_method :store, :[]=
5
+ alias_method :each_pair, :each
6
+
7
+ def initialize
8
+ @keys = []
9
+ end
10
+
11
+ def []=(key, val)
12
+ @keys << key
13
+ super
14
+ end
15
+
16
+ def delete(key)
17
+ @keys.delete(key)
18
+ super
19
+ end
20
+
21
+ def each
22
+ @keys.each { |k| yield k, self[k] }
23
+ end
24
+
25
+ def each_key
26
+ @keys.each { |k| yield k }
27
+ end
28
+
29
+ def each_value
30
+ @keys.each { |k| yield self[k] }
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,97 @@
1
+ module ActiveWarehouse #:nodoc:
2
+ # Class that supports prejoining a fact table with dimensions. This is useful if you need
3
+ # to list facts along with some or all of their detail information.
4
+ class PrejoinFact
5
+ # The fact class that this engine instance is connected to
6
+ attr_accessor :fact_class
7
+
8
+ delegate :prejoined_table_name,
9
+ :connection,
10
+ :prejoined_fields,
11
+ :dimension_relationships,
12
+ :dimension_class,
13
+ :table_name,
14
+ :columns, :to => :fact_class
15
+
16
+ # Initialize the engine instance
17
+ def initialize(fact_class)
18
+ @fact_class = fact_class
19
+ end
20
+
21
+ # Populate the prejoined fact table.
22
+ def populate(options={})
23
+ populate_prejoined_fact_table(options)
24
+ end
25
+
26
+ protected
27
+ # Drop the storage table
28
+ def drop_prejoin_fact_table
29
+ connection.drop_table(prejoined_table_name) if connection.tables.include?(prejoined_table_name)
30
+ end
31
+
32
+ # Get foreign key names that are excluded.
33
+ def excluded_foreign_key_names
34
+ excluded_dimension_relations = prejoined_fields.keys.collect {|k| dimension_relationships[k]}
35
+ excluded_dimension_relations.collect {|r| r.primary_key_name}
36
+ end
37
+
38
+ # Construct the prejoined fact table.
39
+ def create_prejoined_fact_table(options={})
40
+ connection.transaction {
41
+ drop_prejoin_fact_table
42
+
43
+ connection.create_table(prejoined_table_name, :id => false) do |t|
44
+ # get all columns except the foreign_key columns for prejoined dimensions
45
+ columns.each do |c|
46
+ t.column(c.name, c.type) unless excluded_foreign_key_names.include?(c.name)
47
+ end
48
+ #prejoined_columns
49
+ prejoined_fields.each_pair do |key, value|
50
+ dclass = dimension_class(key)
51
+ dclass.columns.each do |c|
52
+ t.column(c.name, c.type) if value.include?(c.name.to_sym)
53
+ end
54
+ end
55
+ end
56
+ }
57
+ end
58
+
59
+ # Populate the prejoined fact table.
60
+ def populate_prejoined_fact_table(options={})
61
+ fact_columns_string = columns.collect {|c|
62
+ "#{table_name}." + c.name unless excluded_foreign_key_names.include?(c.name)
63
+ }.compact.join(",\n")
64
+
65
+ prejoined_columns = []
66
+
67
+ tables_and_joins = "#{table_name}"
68
+
69
+ prejoined_fields.each_pair do |key, value|
70
+ dimension = dimension_class(key)
71
+ tables_and_joins += "\nJOIN #{dimension.table_name} as #{key}"
72
+ tables_and_joins += "\n ON #{table_name}.#{dimension_relationships[key].primary_key_name} = "
73
+ tables_and_joins += "#{key}.#{dimension.primary_key}"
74
+ prejoined_columns << value.collect {|v| "#{key}." + v.to_s}
75
+ end
76
+
77
+ if connection.support_select_into_table?
78
+ drop_prejoin_fact_table
79
+ sql = <<-SQL
80
+ SELECT #{fact_columns_string},
81
+ #{prejoined_columns.join(",\n")}
82
+ FROM #{tables_and_joins}
83
+ SQL
84
+ sql = connection.add_select_into_table(prejoined_table_name, sql)
85
+ else
86
+ create_prejoined_fact_table(options)
87
+ sql = <<-SQL
88
+ INSERT INTO #{prejoined_table_name}
89
+ SELECT #{fact_columns_string},
90
+ #{prejoined_columns.join(",\n")}
91
+ FROM #{tables_and_joins}
92
+ SQL
93
+ end
94
+ connection.transaction { connection.execute(sql) }
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,7 @@
1
+ require 'active_warehouse/report/abstract_report'
2
+ require 'active_warehouse/report/table_report'
3
+ require 'active_warehouse/report/chart_report'
4
+ require 'active_warehouse/report/dimension'
5
+ require 'active_warehouse/report/data_column'
6
+ require 'active_warehouse/report/data_row'
7
+ require 'active_warehouse/report/data_cell'
@@ -0,0 +1,149 @@
1
+ module ActiveWarehouse #:nodoc:
2
+ module Report #:nodoc:
3
+ # Base module for reports.
4
+ module AbstractReport
5
+
6
+ # Array of parameters which will be passed
7
+ attr_accessor :pass_params
8
+
9
+ # A Hash of level names mapped to a method that is used to filter the
10
+ # available column values
11
+ attr_accessor :column_filters
12
+
13
+ # A Hash of level names mapped to a method that is used to filter the
14
+ # available row values
15
+ attr_accessor :row_filters
16
+
17
+ # An optional conditions String
18
+ attr_accessor :conditions
19
+
20
+ # Set the cube name
21
+ def cube_name=(name)
22
+ write_attribute(:cube_name, name)
23
+ @cube = nil
24
+ end
25
+
26
+ # Get the current cube instance
27
+ def cube
28
+ if @cube.nil?
29
+ cube_class = ActiveWarehouse::Cube.class_name(self.cube_name).constantize
30
+ @cube = cube_class.new
31
+ end
32
+ @cube
33
+ end
34
+
35
+ # Get the fact class
36
+ def fact_class
37
+ cube.class.fact_class
38
+ end
39
+
40
+ # Get the column dimension class
41
+ def column_dimension_class
42
+ @column_dimension_class ||= fact_class.dimension_class(self.column_dimension_name)
43
+ end
44
+
45
+ # Get the column hierarchy. Uses the first hierarchy in the column
46
+ # dimension if not specified
47
+ def column_hierarchy
48
+ ch = read_attribute(:column_hierarchy)
49
+ if ch.nil? || ch == 'NULL'
50
+ column_dimension_class.hierarchies.first
51
+ else
52
+ ch
53
+ end
54
+ end
55
+
56
+ # Get the column prefix. Returns 'c' if not specified.
57
+ def column_param_prefix
58
+ read_attribute(:column_param_prefix) || 'c'
59
+ end
60
+
61
+ # Get the row dimension class
62
+ def row_dimension_class
63
+ @row_dimension_class ||= fact_class.dimension_class(self.row_dimension_name)
64
+ end
65
+
66
+ # Get the row hierarchy. Uses the first hierarchy in the row dimension if
67
+ # not specified
68
+ def row_hierarchy
69
+ read_attribute(:row_hierarchy) || row_dimension_class.hierarchies.first
70
+ end
71
+
72
+ # Get the row parameter prefix. Returns 'r' if not specified.
73
+ def row_param_prefix
74
+ read_attribute(:row_param_prefix) || 'r'
75
+ end
76
+
77
+ # Get the list of displayed fact attributes. If this value is not
78
+ # specified then all aggregate and calculated fields will be displayed
79
+ def fact_attributes
80
+ return read_attribute(:fact_attributes) if read_attribute(:fact_attributes)
81
+ fa = []
82
+ fact_class.aggregate_fields.each { |field| fa << field }
83
+ fact_class.calculated_fields.each { |field| fa << field }
84
+ fa
85
+ end
86
+
87
+ def column_filters
88
+ @column_filters ||= {}
89
+ end
90
+
91
+ def row_filters
92
+ @row_filters ||= {}
93
+ end
94
+
95
+ def pass_params
96
+ @pass_params ||= []
97
+ end
98
+
99
+ protected
100
+ # Callback which is invoked on each object returned from a call to the object's find method.
101
+ def after_find
102
+ from_storage
103
+ end
104
+
105
+ # Converts values for all columns which can store symbol values into strings. This is used to store
106
+ # the data in the database as a string rather than a YAML representation
107
+ def to_storage
108
+ symbol_attributes.each do |name|
109
+ self[name] = self[name].to_s if self[name]
110
+ end
111
+ list_attributes.each do |name|
112
+ self[name] = self[name].join(',') if self[name]
113
+ end
114
+ symbolized_list_attributes.each do |name|
115
+ self[name] = self[name].join(',') if self[name]
116
+ end
117
+ end
118
+
119
+ # Converts values for all columns which store strings in the database to symbols.
120
+ def from_storage
121
+ symbol_attributes.each do |name|
122
+ self[name] = self[name].to_sym if self[name]
123
+ end
124
+ list_attributes.each do |name|
125
+ self[name] = self[name].split(/,/) if self[name]
126
+ end
127
+ symbolized_list_attributes.each do |name|
128
+ self[name] = self[name].split(/,/).collect { |v| v.to_sym } if self[name]
129
+ end
130
+ end
131
+
132
+ # Attributes which should contain a symbol
133
+ def symbol_attributes
134
+ %w(cube_name column_dimension_name column_hierarchy row_dimension_name row_hierarchy)
135
+ end
136
+
137
+ # Attributes which should contain a list of strings
138
+ def list_attributes
139
+ %w(column_constraints row_constraints)
140
+ end
141
+
142
+ # Attributes which should contain a list of symbols
143
+ def symbolized_list_attributes
144
+ %w(fact_attributes)
145
+ end
146
+
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,9 @@
1
+ module ActiveWarehouse #:nodoc:
2
+ module Report #:nodoc:
3
+ class ChartReport < ActiveRecord::Base #:nodoc:
4
+ include AbstractReport
5
+ before_save :to_storage
6
+ after_save :from_storage
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ module ActiveWarehouse
2
+ module Report
3
+
4
+ class DataCell
5
+
6
+ attr_accessor :column_dimension_value, :row_dimension_value, :fact_attribute, :raw_value, :value
7
+
8
+ def initialize(column_dimension_value, row_dimension_value, fact_attribute, raw_value, value)
9
+ @column_dimension_value = column_dimension_value
10
+ @row_dimension_value = row_dimension_value
11
+ @fact_attribute = fact_attribute
12
+ @raw_value = raw_value
13
+ @value = value
14
+ end
15
+
16
+ def key
17
+ "#{column_dimension_value}_#{fact_attribute.label}".gsub(' ', '_').downcase
18
+ end
19
+ end
20
+ end
21
+ end