martyr 0.1.74.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.tags +868 -0
  7. data/.travis.yml +3 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +265 -0
  11. data/Rakefile +1 -0
  12. data/TODO.txt +54 -0
  13. data/bin/console +62 -0
  14. data/bin/setup +7 -0
  15. data/lib/martyr/base_cube.rb +73 -0
  16. data/lib/martyr/cube.rb +134 -0
  17. data/lib/martyr/dimension_reference.rb +26 -0
  18. data/lib/martyr/errors.rb +20 -0
  19. data/lib/martyr/helpers/delegators.rb +17 -0
  20. data/lib/martyr/helpers/intervals.rb +222 -0
  21. data/lib/martyr/helpers/metric_id_standardizer.rb +47 -0
  22. data/lib/martyr/helpers/registrable.rb +15 -0
  23. data/lib/martyr/helpers/sorter.rb +79 -0
  24. data/lib/martyr/helpers/translations.rb +34 -0
  25. data/lib/martyr/level_concern/has_level_collection.rb +11 -0
  26. data/lib/martyr/level_concern/level.rb +45 -0
  27. data/lib/martyr/level_concern/level_collection.rb +60 -0
  28. data/lib/martyr/level_concern/level_comparator.rb +45 -0
  29. data/lib/martyr/level_concern/level_definitions_by_dimension.rb +24 -0
  30. data/lib/martyr/runtime/data_set/coordinates.rb +108 -0
  31. data/lib/martyr/runtime/data_set/element.rb +66 -0
  32. data/lib/martyr/runtime/data_set/element_common.rb +51 -0
  33. data/lib/martyr/runtime/data_set/element_locator.rb +143 -0
  34. data/lib/martyr/runtime/data_set/fact.rb +83 -0
  35. data/lib/martyr/runtime/data_set/fact_indexer.rb +72 -0
  36. data/lib/martyr/runtime/data_set/future_fact_value.rb +58 -0
  37. data/lib/martyr/runtime/data_set/future_metric.rb +40 -0
  38. data/lib/martyr/runtime/data_set/virtual_element.rb +131 -0
  39. data/lib/martyr/runtime/data_set/virtual_elements_builder.rb +202 -0
  40. data/lib/martyr/runtime/dimension_scopes/base_level_scope.rb +20 -0
  41. data/lib/martyr/runtime/dimension_scopes/degenerate_level_scope.rb +76 -0
  42. data/lib/martyr/runtime/dimension_scopes/dimension_scope_collection.rb +78 -0
  43. data/lib/martyr/runtime/dimension_scopes/level_scope_collection.rb +20 -0
  44. data/lib/martyr/runtime/dimension_scopes/query_level_scope.rb +223 -0
  45. data/lib/martyr/runtime/fact_scopes/base_fact_scope.rb +62 -0
  46. data/lib/martyr/runtime/fact_scopes/fact_scope_collection.rb +127 -0
  47. data/lib/martyr/runtime/fact_scopes/main_fact_scope.rb +7 -0
  48. data/lib/martyr/runtime/fact_scopes/null_scope.rb +7 -0
  49. data/lib/martyr/runtime/fact_scopes/sub_fact_scope.rb +16 -0
  50. data/lib/martyr/runtime/fact_scopes/wrapped_fact_scope.rb +11 -0
  51. data/lib/martyr/runtime/pivot/pivot_axis.rb +67 -0
  52. data/lib/martyr/runtime/pivot/pivot_cell.rb +54 -0
  53. data/lib/martyr/runtime/pivot/pivot_grain_element.rb +22 -0
  54. data/lib/martyr/runtime/pivot/pivot_row.rb +49 -0
  55. data/lib/martyr/runtime/pivot/pivot_table.rb +109 -0
  56. data/lib/martyr/runtime/pivot/pivot_table_builder.rb +125 -0
  57. data/lib/martyr/runtime/query/metric_dependency_resolver.rb +149 -0
  58. data/lib/martyr/runtime/query/query_context.rb +246 -0
  59. data/lib/martyr/runtime/query/query_context_builder.rb +215 -0
  60. data/lib/martyr/runtime/scope_operators/base_operator.rb +113 -0
  61. data/lib/martyr/runtime/scope_operators/group_operator.rb +18 -0
  62. data/lib/martyr/runtime/scope_operators/select_operator_for_dimension.rb +24 -0
  63. data/lib/martyr/runtime/scope_operators/select_operator_for_metric.rb +35 -0
  64. data/lib/martyr/runtime/scope_operators/where_operator_for_dimension.rb +43 -0
  65. data/lib/martyr/runtime/scope_operators/where_operator_for_metric.rb +25 -0
  66. data/lib/martyr/runtime/slices/data_slices/data_slice.rb +70 -0
  67. data/lib/martyr/runtime/slices/data_slices/metric_data_slice.rb +54 -0
  68. data/lib/martyr/runtime/slices/data_slices/plain_dimension_data_slice.rb +109 -0
  69. data/lib/martyr/runtime/slices/data_slices/time_dimension_data_slice.rb +9 -0
  70. data/lib/martyr/runtime/slices/has_scoped_levels.rb +29 -0
  71. data/lib/martyr/runtime/slices/memory_slices/TO_DELETE.md +188 -0
  72. data/lib/martyr/runtime/slices/memory_slices/memory_slice.rb +84 -0
  73. data/lib/martyr/runtime/slices/memory_slices/metric_memory_slice.rb +59 -0
  74. data/lib/martyr/runtime/slices/memory_slices/plain_dimension_memory_slice.rb +48 -0
  75. data/lib/martyr/runtime/slices/scopeable_slice_data.rb +73 -0
  76. data/lib/martyr/runtime/slices/slice_definitions/base_slice_definition.rb +30 -0
  77. data/lib/martyr/runtime/slices/slice_definitions/metric_slice_definition.rb +120 -0
  78. data/lib/martyr/runtime/slices/slice_definitions/plain_dimension_level_slice_definition.rb +26 -0
  79. data/lib/martyr/runtime/sub_cubes/fact_filler_strategies.rb +61 -0
  80. data/lib/martyr/runtime/sub_cubes/query_metrics.rb +56 -0
  81. data/lib/martyr/runtime/sub_cubes/sub_cube.rb +134 -0
  82. data/lib/martyr/runtime/sub_cubes/sub_cube_grain.rb +117 -0
  83. data/lib/martyr/schema/dimension_associations/dimension_association_collection.rb +33 -0
  84. data/lib/martyr/schema/dimension_associations/level_association.rb +37 -0
  85. data/lib/martyr/schema/dimension_associations/level_association_collection.rb +18 -0
  86. data/lib/martyr/schema/dimensions/dimension_definition_collection.rb +39 -0
  87. data/lib/martyr/schema/dimensions/plain_dimension_definition.rb +39 -0
  88. data/lib/martyr/schema/dimensions/time_dimension_definition.rb +24 -0
  89. data/lib/martyr/schema/facts/base_fact_definition.rb +22 -0
  90. data/lib/martyr/schema/facts/fact_definition_collection.rb +44 -0
  91. data/lib/martyr/schema/facts/main_fact_definition.rb +45 -0
  92. data/lib/martyr/schema/facts/sub_fact_definition.rb +44 -0
  93. data/lib/martyr/schema/metrics/base_metric.rb +77 -0
  94. data/lib/martyr/schema/metrics/built_in_metric.rb +38 -0
  95. data/lib/martyr/schema/metrics/count_distinct_metric.rb +172 -0
  96. data/lib/martyr/schema/metrics/custom_metric.rb +26 -0
  97. data/lib/martyr/schema/metrics/custom_rollup.rb +31 -0
  98. data/lib/martyr/schema/metrics/dependency_inferrer.rb +150 -0
  99. data/lib/martyr/schema/metrics/metric_definition_collection.rb +94 -0
  100. data/lib/martyr/schema/named_scopes/named_scope.rb +19 -0
  101. data/lib/martyr/schema/named_scopes/named_scope_collection.rb +42 -0
  102. data/lib/martyr/schema/plain_dimension_levels/base_level_definition.rb +39 -0
  103. data/lib/martyr/schema/plain_dimension_levels/degenerate_level_definition.rb +75 -0
  104. data/lib/martyr/schema/plain_dimension_levels/level_definition_collection.rb +15 -0
  105. data/lib/martyr/schema/plain_dimension_levels/query_level_definition.rb +99 -0
  106. data/lib/martyr/version.rb +3 -0
  107. data/lib/martyr/virtual_cube.rb +74 -0
  108. data/lib/martyr.rb +55 -0
  109. data/martyr.gemspec +41 -0
  110. metadata +296 -0
@@ -0,0 +1,94 @@
1
+ module Martyr
2
+ module Schema
3
+ class MetricDefinitionCollection < HashWithIndifferentAccess
4
+ include Martyr::Registrable
5
+ include Martyr::Translations
6
+
7
+ attr_reader :cube, :dependency_inferrer, :standardizer
8
+ delegate :cube_name, to: :cube
9
+
10
+ alias_method :find_metric, :find_or_error
11
+
12
+ def initialize(cube)
13
+ super()
14
+ @cube = cube
15
+ @dependency_inferrer = cube.metric_dependency_inferrer
16
+ @standardizer = Martyr::MetricIdStandardizer.new(cube_name)
17
+ end
18
+
19
+ def supports_metric?(metric_name)
20
+ has_key? second_element_from_id(metric_name, fallback: true)
21
+ end
22
+
23
+ # @see register_built_in_metric
24
+ def has_sum_metric(*args)
25
+ register_built_in_metric(:sum, *args)
26
+ end
27
+
28
+ # @see register_built_in_metric
29
+ def has_min_metric(*args)
30
+ register_built_in_metric(:min, *args)
31
+ end
32
+
33
+ # @see register_built_in_metric
34
+ def has_max_metric(*args)
35
+ register_built_in_metric(:max, *args)
36
+ end
37
+
38
+ # @param level [String] The level ID on which to perform the distinct count. The level must be connected to the
39
+ # fact with has_dimension_level.
40
+ # @option null_unless [String] an SQL fragment that creates a helper field on which the COUNT DISTINCT occurs.
41
+ # Example:
42
+ # 1. has_count_distinct_metric 'customer_count', level: 'customers.name'
43
+ # Count distinct occurs on `customers.name`
44
+ #
45
+ # 2. has_count_distinct_metric 'customer_with_property_count', level: 'customers.name', null_unless: 'customers.property'
46
+ # A helper field is created on which the COUNT DISTINCT occurs:
47
+ # CASE WHEN customers.property THEN customers.id ELSE NULL END AS customer_with_property_count_helper
48
+ #
49
+ # Since COUNT DISTINCT ignores null values, this can be an effective way to create boolean values
50
+ #
51
+ def has_count_distinct_metric(name, level:, null_unless: nil, fact_alias: name, typecast: :to_i,
52
+ sort: Sorter.identity, fact_grain: [], sub_query: [], sub_queries: [])
53
+
54
+ level_association = cube.dimension_associations.find_level_association(level)
55
+ register CountDistinctMetric.new cube_name: cube_name, name: name, fact_alias: fact_alias, typecast: typecast,
56
+ sort: sort, level: level_association, null_unless: null_unless, fact_grain: Array.wrap(fact_grain),
57
+ sub_queries: Array.wrap(sub_queries) + Array.wrap(sub_query)
58
+ end
59
+
60
+ def has_custom_metric(name, block, rollup: :sum, sort: Sorter.identity, depends_on: [], fact_grain: [])
61
+ inferrer = dependency_inferrer.infer_from_block(depends_on:
62
+ standardizer.standardize(depends_on), fact_grain: fact_grain, &block)
63
+
64
+ register CustomMetric.new cube_name: cube_name, name: name, block: block, rollup_function: rollup, sort: sort,
65
+ depends_on: inferrer.depends_on, fact_grain: inferrer.fact_grain
66
+ end
67
+
68
+ def has_custom_rollup(name, block, sort: Sorter.identity, depends_on: [], fact_grain: [])
69
+ inferrer = dependency_inferrer.infer_from_block(depends_on:
70
+ standardizer.standardize(depends_on), fact_grain: fact_grain, &block)
71
+
72
+ register CustomRollup.new cube_name: cube_name, name: name, block: block, sort: sort,
73
+ depends_on: inferrer.depends_on, fact_grain: inferrer.fact_grain
74
+ end
75
+
76
+ private
77
+
78
+ def register(metric)
79
+ super
80
+ dependency_inferrer.add_metric(metric)
81
+ metric
82
+ end
83
+
84
+ def register_built_in_metric(rollup_function, name, statement, fact_alias: name, typecast: :to_i,
85
+ sort: Sorter.identity, fact_grain: [], sub_query: [], sub_queries: [])
86
+
87
+ register BuiltInMetric.new cube_name: cube_name, name: name, statement: statement, fact_alias: fact_alias,
88
+ rollup_function: rollup_function, typecast: typecast, sort: sort, fact_grain: Array.wrap(fact_grain),
89
+ sub_queries: Array.wrap(sub_queries) + Array.wrap(sub_query)
90
+ end
91
+
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,19 @@
1
+ module Martyr
2
+ module Schema
3
+ class NamedScope
4
+ include ActiveModel::Model
5
+
6
+ attr_reader :name, :proc
7
+
8
+ def initialize(name, proc)
9
+ @name = name.to_s
10
+ @proc = proc.to_proc
11
+ end
12
+
13
+ def run(query_context_builder, *args)
14
+ query_context_builder.instance_exec(*args, &@proc)
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,42 @@
1
+ module Martyr
2
+ module Schema
3
+ class NamedScopeCollection < HashWithIndifferentAccess
4
+ include Martyr::Registrable
5
+
6
+ # = DSL
7
+
8
+ def scope(name, proc)
9
+ named_scope = NamedScope.new(name, proc)
10
+ register(named_scope)
11
+ add_to_cube_helper_module(named_scope)
12
+ add_to_query_helper_module(named_scope)
13
+ end
14
+
15
+ def cube_helper_module
16
+ @cube_helper_module ||= Module.new
17
+ end
18
+
19
+ def query_helper_module
20
+ @query_helper_module ||= Module.new
21
+ end
22
+
23
+ private
24
+
25
+ # Delegates all named scopes to #new_query_context_builder
26
+ def add_to_cube_helper_module(named_scope)
27
+ cube_helper_module.module_eval do
28
+ delegate named_scope.name, to: :new_query_context_builder
29
+ end
30
+ end
31
+
32
+ def add_to_query_helper_module(named_scope)
33
+ query_helper_module.module_eval do
34
+ define_method(named_scope.name) do |*args|
35
+ named_scope.run(self, *args)
36
+ end
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,39 @@
1
+ module Martyr
2
+ module Schema
3
+ class BaseLevelDefinition
4
+ include ActiveModel::Model
5
+ include Martyr::Level
6
+
7
+ # @attribute fact_key [String] the field in the fact where the attribute resides. E.g.:
8
+ # degenerate_level :country, fact_key: 'invoices.country'
9
+ #
10
+ # @attribute fact_alias [String] the alias to give in the `AS` part of the SQL fact statement.
11
+ #
12
+ # @attribute sort [Proc] optional lambda function for sorting.
13
+ # For query levels it accepts the record:
14
+ # ->(record) { record.custom_sort_order }
15
+ #
16
+ # For degenerates it accepts the value:
17
+ # ->(value) { value[1..2] }
18
+ #
19
+ attr_accessor :name, :fact_key, :fact_alias, :sort
20
+
21
+ delegate :dimension_name, :dimension_definition, to: :collection
22
+ alias_method :slice_id, :dimension_name
23
+ delegate :build_data_slice, :build_memory_slice, to: :dimension_definition
24
+
25
+ # This allows to ask any Martyr::Level for #level_definition
26
+ def level_definition
27
+ self
28
+ end
29
+
30
+ def dimension_definition
31
+ collection.dimension
32
+ end
33
+
34
+ def supported?
35
+ false
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,75 @@
1
+ module Martyr
2
+ module Schema
3
+ class DegenerateLevelDefinition < BaseLevelDefinition
4
+
5
+ # @attribute query_level_key [String] the field in the query level where the degenerate attribute resides. E.g.:
6
+ # degenerate_level :country, query_level_key: 'billing_country'
7
+ #
8
+ # @attribute query_level_with_finder [Proc] the block to run on the query level's ActiveRecord scope. The block
9
+ # receives two arguments - `scope` and `values`. E.g.:
10
+ # ->(scope, values) { scope.where('billing_country' => values) }
11
+ #
12
+ attr_accessor :query_level_key, :query_level_with_finder, :value_method
13
+ attr_reader :loaded
14
+
15
+ # @param collection [DimensionDefinitionCollection]
16
+ # @param name [String, Symbol]
17
+ def initialize(collection, name, **options)
18
+ @collection = collection
19
+ hash = {name: name.to_s,
20
+ query_level_key: options[:query_level_key] || name,
21
+ value_method: options[:value_method] || "#{dimension_name}_#{name}",
22
+ fact_key: options[:fact_key] || "#{dimension_name}_#{name}",
23
+ fact_alias: options[:fact_alias] || "#{dimension_name}_#{name}",
24
+ sort: options[:sort] || Sorter.identity }
25
+
26
+ hash.merge! label_expression: options[:label_expression] if options[:label_expression]
27
+
28
+ super hash
29
+
30
+ @query_level_with_finder = options[:query_level_with_finder] || default_query_level_with_finder
31
+ @loaded = false
32
+ end
33
+
34
+ def label_key
35
+ nil
36
+ end
37
+
38
+ def label_expression
39
+ nil
40
+ end
41
+
42
+ def query?
43
+ false
44
+ end
45
+
46
+ def degenerate?
47
+ true
48
+ end
49
+
50
+ def build(collection)
51
+ Runtime::DegenerateLevelScope.new(collection, self)
52
+ end
53
+
54
+ # @param mod [Module]
55
+ def register_element_helper_methods(mod)
56
+ level_id = id
57
+ level_definition = self
58
+ mod.module_eval do
59
+ define_method(level_definition.value_method) { fetch(level_id) }
60
+ end
61
+ end
62
+
63
+ def helper_methods
64
+ [value_method]
65
+ end
66
+
67
+ private
68
+
69
+ def default_query_level_with_finder
70
+ ->(scope, values){ scope.where query_level_key => values }
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,15 @@
1
+ module Martyr
2
+ module Schema
3
+ class LevelDefinitionCollection < HashWithIndifferentAccess
4
+ include Martyr::LevelCollection
5
+
6
+ def degenerate_level(*args)
7
+ register DegenerateLevelDefinition.new(self, *args)
8
+ end
9
+
10
+ def query_level(*args)
11
+ register QueryLevelDefinition.new(self, *args)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,99 @@
1
+ module Martyr
2
+ module Schema
3
+ class QueryLevelDefinition < BaseLevelDefinition
4
+
5
+ LABEL_EXPRESSION_ALIAS = 'martyr_label_expression'
6
+
7
+ attr_accessor :scope, :primary_key, :label_key, :label_expression, :id_method, :record_method, :value_method,
8
+ :parent_association_name
9
+
10
+ # @param collection [DimensionDefinitionCollection]
11
+ # @param name [String, Symbol]
12
+ # @param scope [Proc]
13
+ # @option primary_key [String]
14
+ # @option label_key [String]
15
+ # @option fact_key [String]
16
+ # @option fact_alias [String]
17
+ def initialize(collection, name, scope = nil, **options)
18
+ @collection = collection
19
+ @scope = scope || default_scope
20
+ super name: name.to_s,
21
+ primary_key: options[:primary_key] || 'id',
22
+ label_key: options[:label_key] || name.to_s,
23
+ label_expression: options[:label_expression],
24
+ id_method: options[:id_method] || "#{dimension_name}_#{name}_id",
25
+ record_method: options[:record_method] || "#{dimension_name}_#{name}_record",
26
+ value_method: options[:value_method] || "#{dimension_name}_#{name}",
27
+ fact_key: options[:fact_key] || "#{dimension_name}_#{name}_id",
28
+ fact_alias: options[:fact_alias] || "#{dimension_name}_#{name}_id",
29
+ parent_association_name: options[:parent_association_name]
30
+
31
+ self.sort = options[:sort] || Sorter.default_for_query(label_field)
32
+ add_label_expression_to_scope
33
+ end
34
+
35
+ def query?
36
+ true
37
+ end
38
+
39
+ def degenerate?
40
+ false
41
+ end
42
+
43
+ def parent_association_name_with_default
44
+ (parent_association_name || level_above.try(:name)).to_s.presence
45
+ end
46
+
47
+ def label_field
48
+ label_expression ? LABEL_EXPRESSION_ALIAS : label_key
49
+ end
50
+
51
+ def build(collection)
52
+ Runtime::QueryLevelScope.new(collection, self)
53
+ end
54
+
55
+ # @param record [ActiveRecord::Base]
56
+ def record_value(record)
57
+ record.try(label_field)
58
+ end
59
+
60
+ # @param mod [Module]
61
+ def register_element_helper_methods(mod)
62
+ level_id = id
63
+ level_definition = self
64
+ mod.module_eval do
65
+ define_method(level_definition.id_method) { key_for(level_id) }
66
+ define_method(level_definition.record_method) { record_for(level_id) }
67
+ define_method(level_definition.value_method) { fetch(level_id) }
68
+ end
69
+ end
70
+
71
+ def helper_methods
72
+ [id_method, record_method, value_method]
73
+ end
74
+
75
+ private
76
+
77
+ # @return [Proc] a lambda object representing running #all on the guessed-class
78
+ def default_scope
79
+ begin
80
+ klass = dimension_name.classify.constantize
81
+ ->{ klass.all }
82
+ rescue => e
83
+ raise Schema::Error.new(e)
84
+ end
85
+ end
86
+
87
+ def add_label_expression_to_scope
88
+ return unless label_expression
89
+ original_scope = @scope.call
90
+ if original_scope.select_values.present?
91
+ @scope = -> { original_scope.select("#{label_expression} AS #{LABEL_EXPRESSION_ALIAS}") }
92
+ else
93
+ @scope = -> { original_scope.select("#{original_scope.klass.table_name}.*", "#{label_expression} AS #{LABEL_EXPRESSION_ALIAS}") }
94
+ end
95
+ end
96
+
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,3 @@
1
+ module Martyr
2
+ VERSION = "0.1.74.pre"
3
+ end
@@ -0,0 +1,74 @@
1
+ module Martyr
2
+ class VirtualCube < BaseCube
3
+
4
+ def self.contained_cube_classes
5
+ @contained_cube_classes ||= []
6
+ end
7
+
8
+ def self.metric_definitions
9
+ @metric_definitions ||= Schema::MetricDefinitionCollection.new(self)
10
+ end
11
+
12
+ # = DSL
13
+
14
+ def self.use_cube(class_name)
15
+ contained_cube_classes << class_name.constantize
16
+ end
17
+
18
+ def self.dimension_definitions
19
+ merge_from_cubes(Schema::DimensionDefinitionCollection.new, &:dimension_definitions)
20
+ end
21
+
22
+ def self.supported_dimension_definitions
23
+ merge_from_cubes(Schema::DimensionDefinitionCollection.new, &:supported_dimension_definitions)
24
+ end
25
+
26
+ class << self
27
+ delegate :find_metric, :has_custom_rollup, to: :metric_definitions
28
+ delegate :select, :slice, :granulate, :pivot, to: :new_query_context_builder
29
+ alias_method :all, :new_query_context_builder
30
+ alias_method :metrics, :metric_definitions
31
+ end
32
+
33
+ # @param mergeable [#merge!]
34
+ def self.merge_from_cubes(mergeable)
35
+ contained_cube_classes.inject(mergeable) do |merged_object, contained_cube|
36
+ merged_object.merge! yield(contained_cube)
37
+ end
38
+ end
39
+
40
+ def self.virtual?
41
+ true
42
+ end
43
+
44
+ # @override
45
+ def self.find_metric_id(metric_id)
46
+ cube_name, metric_name = id_components(metric_id)
47
+ if cube_name == self.cube_name
48
+ find_metric(metric_name)
49
+ else
50
+ find_cube(cube_name).find_metric(metric_name)
51
+ end
52
+ end
53
+
54
+ def self.find_cube(cube_name)
55
+ contained_cube_classes.find{ |cube| cube.cube_name == cube_name } ||
56
+ raise(Schema::Error.new "Could not find `#{cube_name}`")
57
+ end
58
+
59
+ # @return [Schema::DependencyInferrer]
60
+ def self.metric_dependency_inferrer
61
+ return @metric_dependency_inferrer if @metric_dependency_inferrer
62
+ inferrer = Schema::DependencyInferrer.new
63
+ contained_cube_classes.each do |contained_cube|
64
+ inferrer.add_cube_levels(contained_cube)
65
+ end
66
+ @metric_dependency_inferrer = inferrer
67
+ end
68
+
69
+ def self.standardizer
70
+ @standardizer ||= Martyr::MetricIdStandardizer.new(cube_name, raise_if_not_ok: true)
71
+ end
72
+
73
+ end
74
+ end
data/lib/martyr.rb ADDED
@@ -0,0 +1,55 @@
1
+ require 'martyr/version'
2
+
3
+ require 'csv'
4
+ require 'active_support'
5
+ require 'active_support/core_ext'
6
+ require 'active_model'
7
+ require 'active_record'
8
+
9
+ require 'martyr/helpers/translations'
10
+ require 'martyr/runtime/slices/has_scoped_levels'
11
+ require 'martyr/runtime/data_set/element_common'
12
+
13
+ Dir.glob(File.expand_path '../martyr/helpers/*.rb', __FILE__).each{|x| require File.expand_path(x).split('.rb').first}
14
+ Dir.glob(File.expand_path '../martyr/level_concern/*.rb', __FILE__).each{|x| require File.expand_path(x).split('.rb').first}
15
+ Dir.glob(File.expand_path '../martyr/runtime/scope_operators/*.rb', __FILE__).each{|x| require File.expand_path(x).split('.rb').first}
16
+ Dir.glob(File.expand_path '../martyr/**/*.rb', __FILE__).each{|x| require File.expand_path(x).split('.rb').first}
17
+
18
+ # require 'martyr/base'
19
+ # require 'martyr/errors'
20
+ #
21
+ # require 'martyr/schema/helpers/registrable'
22
+ # require 'martyr/schema/helpers/has_scope'
23
+ #
24
+ # require 'martyr/schema/dimensions/dimension_definition'
25
+ # require 'martyr/schema/dimensions/degenerate_dimension'
26
+ # require 'martyr/schema/dimensions/query_dimension'
27
+ # require 'martyr/schema/dimensions/time_dimension'
28
+ # require 'martyr/schema/dimensions/dimension_definition_collection'
29
+ # require 'martyr/schema/dimensions/shared_dimension_wrapper'
30
+ # require 'martyr/schema/dimensions/level_collection'
31
+ # require 'martyr/schema/dimensions/level'
32
+ #
33
+ # require 'martyr/schema/facts/fact_definition_collection'
34
+ # require 'martyr/schema/facts/main_fact_scope'
35
+ # require 'martyr/schema/facts/sub_fact_scope'
36
+ #
37
+ # require 'martyr/schema/metrics/built_in_metric'
38
+ # require 'martyr/schema/metrics/custom_metric'
39
+ # require 'martyr/schema/metrics/metric_definition_collection'
40
+ #
41
+ # require 'martyr/schema/rollups/custom_rollup'
42
+ # require 'martyr/schema/rollups/rollup_definition_collection'
43
+ #
44
+ # require 'martyr/runtime/query/query_context'
45
+ #
46
+ # require 'martyr/runtime/slices/base_dimension_slice'
47
+ # require 'martyr/runtime/slices/compound_slice'
48
+ # require 'martyr/runtime/slices/degenerate_dimension_slice'
49
+ # require 'martyr/runtime/slices/metric_slice'
50
+ # require 'martyr/runtime/slices/query_dimension_slice'
51
+ # require 'martyr/runtime/slices/time_dimension_slice'
52
+
53
+ module Martyr
54
+ # Your code goes here...
55
+ end
data/martyr.gemspec ADDED
@@ -0,0 +1,41 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'martyr/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "martyr"
8
+ spec.version = Martyr::VERSION
9
+ spec.authors = ["Amit Aharoni"]
10
+ spec.email = ["amit.sites@gmail.com"]
11
+
12
+ spec.summary = %q{Add data mart and pivoting functionality to Active Record models}
13
+ spec.description = %q{A multi-dimensional semantic layer on top of ActiveRecord that allows running pivot table queries and rendering them as CSV, HTML, or KickChart-ready hashes. Supports time dimensions, cohort analysis, custom rollups, and drilling through to the underlying ActiveRecord objects.}
14
+ spec.homepage = "https://github.com/vaharoni/martyr"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
+ # delete this section to allow pushing this gem to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_development_dependency "bundler", "~> 1.9"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency "rspec", "~> 3.2"
33
+ spec.add_development_dependency "sqlite3", "~> 1.3"
34
+ spec.add_development_dependency "activerecord", "~> 4.2"
35
+ spec.add_development_dependency "chinook_database", "~> 0.1"
36
+ spec.add_development_dependency "pry", "~> 0.10"
37
+ spec.add_development_dependency "pry-byebug", "~> 3.3"
38
+
39
+ spec.add_runtime_dependency "activesupport", "~> 4.2"
40
+ spec.add_runtime_dependency "activemodel", "~> 4.2"
41
+ end