activecube 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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