martyr 0.1.74.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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