activecube 0.1.2 → 0.1.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc8d353c9c9d670cda8c4f9de4a3e59994c4f8e6ebd2d25551a5bf712f53db00
4
- data.tar.gz: 55ac707acc8dd783318739e8f4bb424454da0c597a54ab89b09c76a16982ac23
3
+ metadata.gz: ef6448e22d9122edb34207112002802381c6f807a60a73150add0dfba415eff2
4
+ data.tar.gz: f1db99febe43eb9943f96c344bbd77c2c3a42e6619225b9266980b6b2e3c486b
5
5
  SHA512:
6
- metadata.gz: 7652d255be72680539226092333c4c1fa081a0416b4ef50a13de17d40385c45628be5e761d1191b9e7e5ab1b55c26ca613012d9e601fda4c44984249bd7053e2
7
- data.tar.gz: b20ad10bbec9f17cc95a879f84690739fcb3109e451797e954efd720a0de3fbaeac0a38e7b4a0ab1e647d2f3666d0b4b3de7c93d6bd3441897d3a72ab9db03f2
6
+ metadata.gz: c4ea5bf5ac7fc108a1868cdffc4b12a7c916d34b4f93bc9c094f930a405dd27e989e60287d39ebe2ed136add74de86bd0849dcd47122e54489e5a02ddb9276ca
7
+ data.tar.gz: d67e1cd6832b0c4bbc1ce2669ca145359b34e31ba5182392ff25d5b7e13b9bc639ae4822121821739b52bb7c0f2b829d06fb76c51cbd15b346db5f8a1488a26a
data/Gemfile.lock CHANGED
@@ -9,7 +9,7 @@ GIT
9
9
  PATH
10
10
  remote: .
11
11
  specs:
12
- activecube (0.1.2)
12
+ activecube (0.1.3)
13
13
  activerecord (>= 5.2)
14
14
 
15
15
  GEM
data/README.md CHANGED
@@ -132,9 +132,7 @@ The methods used to contruct the query:
132
132
  - **slice** defines which dimensions slices the results
133
133
  - **measure** defines what to measure
134
134
  - **when** defines which selectors to apply
135
- - **desc, asc, take, skip** are for ordering and limiting result set
136
-
137
- (take and skip have aliases: offset and limit).
135
+ - **desc, asc, offset, limit** are for ordering and limiting result set
138
136
 
139
137
  After the query contructed, the following methods can be applied:
140
138
 
@@ -12,7 +12,7 @@ module Activecube::Common
12
12
  end
13
13
  else
14
14
  define_method fname do |model, arel_table, measure, cube_query|
15
- column = arel_table[self.class.column_name.to_sym]
15
+ column = pre_aggregate_value model, arel_table, measure, cube_query
16
16
  measure.selectors.empty? ? column.send(fname) : column.send(fname.to_s+'If', measure.condition_query(model, arel_table, cube_query))
17
17
  end
18
18
  end
@@ -21,5 +21,9 @@ module Activecube::Common
21
21
  end
22
22
 
23
23
 
24
+ def pre_aggregate_value _model, arel_table, _measure, _cube_query
25
+ arel_table[self.class.column_name.to_sym]
26
+ end
27
+
24
28
  end
25
29
  end
@@ -41,7 +41,8 @@ module Activecube
41
41
  end
42
42
 
43
43
  def field *args
44
- (@fields ||= {} )[args.first.to_sym] = Field.new( *args)
44
+ name = args.first.to_sym
45
+ (@fields ||= {} )[name] = Field.build( name, args.second )
45
46
  end
46
47
 
47
48
  end
@@ -2,11 +2,30 @@ module Activecube
2
2
  class Field
3
3
 
4
4
  attr_reader :name, :definition
5
- def initialize *args
6
- @name = args.first
7
- @definition = args.second
5
+
6
+ def self.build name, arg
7
+ if arg.kind_of? String
8
+ Field.new name, arg
9
+ elsif arg.kind_of? Hash
10
+ Field.new name, arg.symbolize_keys
11
+ elsif arg.kind_of?(Class) && arg < Field
12
+ arg.new name
13
+ else
14
+ raise ArgumentError, "Unexpected field #{name} definition with #{arg.class.name}"
15
+ end
16
+ end
17
+
18
+ def initialize name, arg = nil
19
+ @name = name
20
+ @definition = arg
8
21
  end
9
22
 
23
+ def expression _model, _arel_table, _slice, _cube_query
24
+ raise ArgumentError, "String expression expected for #{name} field, instead #{definition.class.name} is found" unless definition.kind_of?(String)
25
+ definition
26
+ end
10
27
 
11
28
  end
29
+
30
+
12
31
  end
@@ -70,7 +70,8 @@ module Activecube::Processor
70
70
  @models = []
71
71
  measure_tables.group_by(&:table).each_pair do |table, list|
72
72
  @models << table.model
73
- reduced = cube_query.reduced list.map(&:measure)
73
+ reduce_options = measure_tables.count==1 ? cube_query.options : []
74
+ reduced = cube_query.reduced list.map(&:measure), reduce_options
74
75
  table_query = table.query reduced
75
76
  composed_query = composed_query ? table.join(cube_query, composed_query, table_query) : table_query
76
77
  end
@@ -49,22 +49,20 @@ module Activecube::Query
49
49
  self
50
50
  end
51
51
 
52
- def skip *args
52
+ def offset *args
53
53
  args.each{|arg|
54
54
  options << Limit.new( arg, :skip)
55
55
  }
56
56
  self
57
57
  end
58
58
 
59
- def take *args
59
+ def limit *args
60
60
  args.each{|arg|
61
61
  options << Limit.new( arg, :take)
62
62
  }
63
63
  self
64
64
  end
65
65
 
66
- alias_method :limit, :take
67
- alias_method :offset, :skip
68
66
 
69
67
  def query
70
68
  composer = Activecube::Processor::Composer.new(self)
@@ -88,7 +86,7 @@ module Activecube::Query
88
86
  (measures.map(&:selectors) + selectors).flatten.map(&:required_column_names).flatten.uniq
89
87
  end
90
88
 
91
- def reduced other_measures
89
+ def reduced other_measures, other_options
92
90
 
93
91
  common_selectors = []
94
92
  other_measures.each_with_index do |m,i|
@@ -115,11 +113,11 @@ module Activecube::Query
115
113
 
116
114
  return self if (reduced_measures == self.measures) && (reduced_selectors == self.selectors)
117
115
 
118
- CubeQuery.new cube, slices, reduced_measures, reduced_selectors
116
+ CubeQuery.new cube, slices, reduced_measures, reduced_selectors, other_options
119
117
  end
120
118
 
121
119
  def join_fields
122
- slices.map{|s| s.dimension_class.identity || s.key }.uniq
120
+ slices.map{|s| s.dimension.class.identity || s.key }.uniq
123
121
  end
124
122
 
125
123
  def orderings
@@ -9,7 +9,8 @@ module Activecube
9
9
  end
10
10
 
11
11
  def append_query _model, _cube_query, _table, query
12
- query.order(::Arel.sql(argument.to_s).send(direction))
12
+ text = argument.to_s.split(',').map{|s| "`#{s}`"}.join(',')
13
+ query.order(::Arel.sql(text).send(direction))
13
14
  end
14
15
 
15
16
  end
@@ -44,7 +44,7 @@ module Activecube::Query
44
44
  define_method(method) do |*args|
45
45
  raise ArgumentError, "Selector for #{method} already set" if operator
46
46
  if ARRAY_OPERATORS.include? method
47
- @operator = Operator.new(method, args)
47
+ @operator = Operator.new(method, args.flatten)
48
48
  elsif method=='between'
49
49
  if args.kind_of?(Range)
50
50
  @operator = Operator.new(method, args)
@@ -1,36 +1,59 @@
1
1
  module Activecube::Query
2
2
  class Slice < Item
3
3
 
4
- attr_reader :field, :modifier
5
- def initialize cube, key, dimension, field = nil, modifier = nil
6
- super cube, key, dimension
7
- @field = field
8
- @modifier = modifier
9
- field_methods! if field.try(:definition).kind_of?(Hash)
4
+ attr_reader :dimension, :parent
5
+ def initialize cube, key, definition, parent = nil
6
+ super cube, key, definition
7
+ @dimension = parent ? parent.dimension : definition
8
+ @parent = parent
9
+
10
+ if parent
11
+ raise "Unexpected class #{definition.class.name}" unless definition.kind_of?(Activecube::Field)
12
+ field_methods! if definition.class < Activecube::Field
13
+ end
14
+
10
15
  end
11
16
 
12
- def [] key
13
- unless (definition.kind_of? Activecube::Dimension) && !self.field && (field = definition.class.fields[key])
14
- raise "Field #{key} is not defined for #{definition.name}"
15
- end
16
- Slice.new cube, key, definition, field
17
+ def required_column_names
18
+ dimension.class.column_names || []
17
19
  end
18
20
 
19
- def alias! new_key
20
- self.class.new cube, new_key, definition, field, modifier
21
+ def [] arg
22
+
23
+ key = arg.to_sym
24
+
25
+ child = if definition.kind_of? Activecube::Dimension
26
+ definition.class.fields[key]
27
+ elsif definition.kind_of?(Activecube::Field) && (hash = definition.definition).kind_of?(Hash)
28
+ hash[key]
29
+ end
30
+
31
+ raise "Field #{key} is not defined for #{definition}" unless child
32
+
33
+ if child.kind_of?(Class) && child <= Activecube::Field
34
+ child = child.new key
35
+ elsif !child.kind_of?(Activecube::Field)
36
+ child = Activecube::Field.new(key, child)
37
+ end
38
+
39
+ Slice.new cube, key, child, self
40
+
21
41
  end
22
42
 
23
- def dimension_class
24
- definition.class
43
+ def alias! new_key
44
+ self.class.new cube, new_key, definition, parent
25
45
  end
26
46
 
27
- def append_query _model, cube_query, table, query
47
+ def append_query model, cube_query, table, query
28
48
 
29
49
  attr_alias = "`#{key.to_s}`"
30
- expr = field ? Arel.sql( modifier || field.definition ) : table[dimension_class.column_name]
50
+ expr = parent ?
51
+ Arel.sql(definition.expression( model, table, self, cube_query) ) :
52
+ table[dimension.class.column_name]
53
+
31
54
  query = query.project(expr.as(attr_alias))
32
55
 
33
- if identity = dimension_class.identity
56
+ if identity = dimension.class.identity
34
57
  query = query.project(table[identity]).group(table[identity])
35
58
  else
36
59
  query = query.group(attr_alias)
@@ -44,23 +67,21 @@ module Activecube::Query
44
67
  end
45
68
 
46
69
  def to_s
47
- "Dimension #{super}"
70
+ parent ? "Dimension #{dimension}[#{super}]" : "Dimension #{super}"
48
71
  end
49
-
50
-
51
- private
52
-
72
+
53
73
  def field_methods!
54
- field.definition.each_pair do |k,v|
55
- if v.kind_of? Proc
56
- define_singleton_method k, ((proc {|x| @modifier = x; self}) << v)
57
- elsif v.kind_of? String
58
- define_singleton_method k do @modifier = v; self end
59
- else
60
- raise "Unexpected type #{k.class.name} for definition of #{name}[#{k}]"
74
+ excluded = [:expression] + self.class.instance_methods(false)
75
+ definition.class.instance_methods(false).each do |name|
76
+ unless excluded.include?(name)
77
+ define_singleton_method name do |*args|
78
+ definition.send name, *args
79
+ self
80
+ end
61
81
  end
62
82
  end
63
83
  end
84
+
64
85
 
65
86
  end
66
87
  end
@@ -5,7 +5,7 @@ module Activecube
5
5
 
6
6
  attr_reader :database, :role
7
7
 
8
- [:slice, :measure, :when, :skip, :take, :desc, :asc].each do |method|
8
+ [:slice, :measure, :when, :desc, :asc, :limit, :offset].each do |method|
9
9
  define_method(method) do |*args|
10
10
  Query::CubeQuery.new(self).send method, *args
11
11
  end
@@ -25,10 +25,9 @@ module Activecube
25
25
 
26
26
  def super_model
27
27
  raise ArgumentError, "No tables specified for cube #{name}" if tables.count==0
28
- return tables.first.model if tables.count==1
29
28
 
30
29
  tables.collect{ |t|
31
- t.model.ancestors.select{|c| c <= ActiveRecord::Base }.reverse
30
+ t.model.ancestors.select{|c| c < ActiveRecord::Base }
32
31
  }.transpose.select{|c|
33
32
  c.uniq.count==1
34
33
  }.last.first
@@ -1,3 +1,3 @@
1
1
  module Activecube
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.3"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activecube
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aleksey Studnev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-02-22 00:00:00.000000000 Z
11
+ date: 2020-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord