dbee-active_record 2.0.0 → 2.1.0.pre.alpha

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: 13ace02381ea618f552af8f6d4ff3c9c615479c45797ea0bf0e3ce77ce031d6f
4
- data.tar.gz: 14027158ea930bdfce136621d9e49aad1927da1e9fa9a1c6213aae7a6044d067
3
+ metadata.gz: f8de8c388637ba455ffca406ff42751ecfb62f4a40bbcb9f560a085b585568a1
4
+ data.tar.gz: 9723886e37e886d7df7fa4661df4c81d35fe186d08a7d82de66083e08ed44bac
5
5
  SHA512:
6
- metadata.gz: 31dda63e9a89898b387fc071aa3e7dc1b5253ad270de2042cfd2b73e2b53934a36b0d312b29091c1ccf2cc30e81f3dea61259ddc9cf889e6af272b75e75c1a09
7
- data.tar.gz: 8ea4ff00d7cfea26be982681de6cc9e1dbce4113a03d09cd01815ddbd36a9ce49da68298239ef8906dfd3cc69d2e4f9fa66103be2cbda1fbec6fe83868faf7bb
6
+ metadata.gz: be75ae14b3c4b0154a0cc4f63950fa86a5f9f16272cd2d5133a4fc4649e2bcd6f7dc5836cf1706c7a9a6b9dba1f1183783038b013bc9e78f5cc5f25e9c980f78
7
+ data.tar.gz: 7f466202c164f0da7fb936010bc73393a0643682588f14093979fa1e8844f36bd0978020f3f435624d6bea25d95767af35884c6e453a35f41d219dd4331b22d7
@@ -1,6 +1,20 @@
1
- Metrics/LineLength:
1
+ AllCops:
2
+ TargetRubyVersion: 2.5
3
+
4
+ Layout/LineLength:
2
5
  Max: 100
3
6
 
7
+ Lint/RaiseException:
8
+ Enabled: True
9
+
10
+ Lint/StructNewOverride:
11
+ Enabled: True
12
+
13
+ Metrics/AbcSize:
14
+ Max: 16
15
+ Exclude:
16
+ - spec/db_helper.rb
17
+
4
18
  Metrics/BlockLength:
5
19
  ExcludedMethods:
6
20
  - let
@@ -13,18 +27,19 @@ Metrics/BlockLength:
13
27
  - spec/dbee/**/*
14
28
  - dbee-active_record.gemspec
15
29
 
30
+ Metrics/ClassLength:
31
+ Max: 125
32
+
16
33
  Metrics/MethodLength:
17
34
  Max: 25
18
35
  Exclude:
19
36
  - spec/db_helper.rb
20
37
 
21
- AllCops:
22
- TargetRubyVersion: 2.4
38
+ Style/HashEachMethods:
39
+ Enabled: True
23
40
 
24
- Metrics/AbcSize:
25
- Max: 16
26
- Exclude:
27
- - spec/db_helper.rb
41
+ Style/HashTransformKeys:
42
+ Enabled: True
28
43
 
29
- Metrics/ClassLength:
30
- Max: 125
44
+ Style/HashTransformValues:
45
+ Enabled: True
@@ -1 +1 @@
1
- 2.6.3
1
+ 2.6.6
@@ -6,16 +6,12 @@ services:
6
6
  - mysql
7
7
  rvm:
8
8
  # Build on the latest stable of all supported Rubies (https://www.ruby-lang.org/en/downloads/):
9
- - 2.4.6
10
- - 2.5.5
11
- - 2.6.3
9
+ - 2.5.8
10
+ - 2.6.6
11
+ - 2.7.1
12
12
  env:
13
13
  - AR_VERSION=5
14
14
  - AR_VERSION=6
15
- matrix:
16
- exclude:
17
- - rvm: 2.4.6
18
- env: AR_VERSION=6
19
15
  cache: bundler
20
16
  before_script:
21
17
  - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
@@ -1,3 +1,33 @@
1
+ # 2.1.0 (TBD)
2
+
3
+ ### Additions:
4
+
5
+ * Implemented Dbee::Query::Field#aggregator
6
+ * Implemented Dbee::Query::Field#filters
7
+
8
+ ### Changes:
9
+
10
+ * Bumped minimum Ruby version to 2.5
11
+
12
+ # 2.0.4 (February 13th, 2020)
13
+
14
+ * use Arel#in for Equal filters when there is more than one value
15
+ * use Arel#not_in for NotEqual filters when there are is than one value
16
+
17
+ # 2.0.3 (January 7th, 2020)
18
+
19
+ * Added/tested support for Dbee 2.0.3
20
+ * Added support for Ruby 2.6.5
21
+
22
+ # 2.0.2 (November 7th, 2019)
23
+
24
+ * Added/tested support for Dbee 2.0.2
25
+ * Added support for Ruby 2.3.8
26
+
27
+ # 2.0.1 (October 25th, 2019)
28
+
29
+ * Development dependency updates.
30
+
1
31
  # 2.0.0 (September 3rd, 2019)
2
32
 
3
33
  * Only support Dbee version 2.0.0 and above
@@ -15,11 +15,19 @@ Gem::Specification.new do |s|
15
15
  s.email = ['mruggio@bluemarblepayroll.com']
16
16
  s.files = `git ls-files`.split("\n")
17
17
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
- s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
18
+ s.bindir = 'exe'
19
+ s.executables = []
19
20
  s.homepage = 'https://github.com/bluemarblepayroll/dbee-active_record'
20
21
  s.license = 'MIT'
22
+ s.metadata = {
23
+ 'bug_tracker_uri' => 'https://github.com/bluemarblepayroll/dbee-active_record/issues',
24
+ 'changelog_uri' => 'https://github.com/bluemarblepayroll/dbee-active_record/blob/master/CHANGELOG.md',
25
+ 'documentation_uri' => 'https://www.rubydoc.info/gems/dbee-active_record',
26
+ 'homepage_uri' => s.homepage,
27
+ 'source_code_uri' => s.homepage
28
+ }
21
29
 
22
- s.required_ruby_version = '>= 2.4.6'
30
+ s.required_ruby_version = '>= 2.5'
23
31
 
24
32
  ar_version = ENV['AR_VERSION'] || ''
25
33
 
@@ -34,15 +42,15 @@ Gem::Specification.new do |s|
34
42
  end
35
43
 
36
44
  s.add_dependency('activerecord', activerecord_version)
37
- s.add_dependency('dbee', '~>2')
45
+ s.add_dependency('dbee', '=2.1.0.pre.alpha')
38
46
 
39
47
  s.add_development_dependency('guard-rspec', '~>4.7')
40
48
  s.add_development_dependency('mysql2', '~>0.5')
41
49
  s.add_development_dependency('pry', '~>0')
42
- s.add_development_dependency('rake', '~> 12')
50
+ s.add_development_dependency('rake', '~> 13')
43
51
  s.add_development_dependency('rspec', '~> 3.8')
44
- s.add_development_dependency('rubocop', '~>0.74.0')
52
+ s.add_development_dependency('rubocop', '~>0.81.0')
45
53
  s.add_development_dependency('simplecov', '~>0.17.0')
46
- s.add_development_dependency('simplecov-console', '~>0.5.0')
54
+ s.add_development_dependency('simplecov-console', '~>0.7.0')
47
55
  s.add_development_dependency('sqlite3', '~>1')
48
56
  end
File without changes
@@ -7,26 +7,24 @@
7
7
  # LICENSE file in the root directory of this source tree.
8
8
  #
9
9
 
10
- require_relative 'expression_builder/constraint_maker'
11
- require_relative 'expression_builder/order_maker'
12
- require_relative 'expression_builder/select_maker'
13
- require_relative 'expression_builder/where_maker'
10
+ require_relative 'expression_builder/constraint'
11
+ require_relative 'expression_builder/order'
12
+ require_relative 'expression_builder/select'
13
+ require_relative 'expression_builder/where'
14
14
 
15
15
  module Dbee
16
16
  module Providers
17
17
  class ActiveRecordProvider
18
18
  # This class can generate an Arel expression tree.
19
19
  class ExpressionBuilder
20
- extend Forwardable
21
-
22
20
  class MissingConstraintError < StandardError; end
23
21
 
24
- def_delegators :statement, :to_sql
25
-
26
22
  def initialize(model, table_alias_maker, column_alias_maker)
27
23
  @model = model
28
24
  @table_alias_maker = table_alias_maker
29
25
  @column_alias_maker = column_alias_maker
26
+ @requires_group_by = false
27
+ @group_by_columns = []
30
28
 
31
29
  clear
32
30
  end
@@ -49,13 +47,25 @@ module Dbee
49
47
  self
50
48
  end
51
49
 
50
+ def to_sql
51
+ if requires_group_by
52
+ @requires_group_by = false
53
+ statement.group(group_by_columns) unless group_by_columns.empty?
54
+ @group_by_columns = []
55
+ end
56
+
57
+ statement.to_sql
58
+ end
59
+
52
60
  private
53
61
 
54
62
  attr_reader :base_table,
55
63
  :statement,
56
64
  :model,
57
65
  :table_alias_maker,
58
- :column_alias_maker
66
+ :column_alias_maker,
67
+ :requires_group_by,
68
+ :group_by_columns
59
69
 
60
70
  def tables
61
71
  @tables ||= {}
@@ -70,7 +80,7 @@ module Dbee
70
80
 
71
81
  key_path = filter.key_path
72
82
  arel_column = key_paths_to_arel_columns[key_path]
73
- predicate = WhereMaker.instance.make(filter, arel_column)
83
+ predicate = Where.instance.make(filter, arel_column)
74
84
 
75
85
  build(statement.where(predicate))
76
86
 
@@ -82,22 +92,40 @@ module Dbee
82
92
 
83
93
  key_path = sorter.key_path
84
94
  arel_column = key_paths_to_arel_columns[key_path]
85
- predicate = OrderMaker.instance.make(sorter, arel_column)
95
+ predicate = Order.instance.make(sorter, arel_column)
86
96
 
87
97
  build(statement.order(predicate))
88
98
 
89
99
  self
90
100
  end
91
101
 
102
+ def add_filter_key_paths(filters)
103
+ filters.each_with_object({}) do |filter, memo|
104
+ arel_key_column = add_key_path(filter.key_path)
105
+
106
+ memo[arel_key_column] = filter
107
+ end
108
+ end
109
+
92
110
  def add_field(field)
93
- add_key_path(field.key_path)
111
+ arel_value_column = add_key_path(field.key_path)
112
+ arel_key_columns_to_filters = add_filter_key_paths(field.filters)
94
113
 
95
- key_path = field.key_path
96
- arel_column = key_paths_to_arel_columns[key_path]
97
- predicate = SelectMaker.instance.make(field, arel_column, column_alias_maker)
114
+ predicate = Select.instance.make(
115
+ field,
116
+ arel_key_columns_to_filters,
117
+ arel_value_column,
118
+ column_alias_maker
119
+ )
98
120
 
99
121
  build(statement.project(predicate))
100
122
 
123
+ if field.aggregator?
124
+ @requires_group_by = true
125
+ else
126
+ group_by_columns << arel_value_column
127
+ end
128
+
101
129
  self
102
130
  end
103
131
 
@@ -123,7 +151,7 @@ module Dbee
123
151
  def table(name, model, previous_table)
124
152
  table = make_table(model.table, name)
125
153
 
126
- on = ConstraintMaker.instance.make(model.constraints, table, previous_table)
154
+ on = Constraint.instance.make(model.constraints, table, previous_table)
127
155
 
128
156
  raise MissingConstraintError, "for: #{name}" unless on
129
157
 
@@ -142,16 +170,16 @@ module Dbee
142
170
  end
143
171
 
144
172
  def add_key_path(key_path)
145
- return if key_paths_to_arel_columns.key?(key_path)
173
+ return key_paths_to_arel_columns[key_path] if key_paths_to_arel_columns.key?(key_path)
146
174
 
147
175
  ancestors = model.ancestors!(key_path.ancestor_names)
148
176
 
149
177
  table = traverse_ancestors(ancestors)
150
178
 
151
179
  arel_column = table[key_path.column_name]
152
- key_paths_to_arel_columns[key_path] = arel_column
153
180
 
154
- self
181
+ # Note that this returns arel_column
182
+ key_paths_to_arel_columns[key_path] = arel_column
155
183
  end
156
184
 
157
185
  def build(new_expression)
@@ -12,9 +12,19 @@ module Dbee
12
12
  class ActiveRecordProvider
13
13
  class ExpressionBuilder
14
14
  # Can derive constraints for Arel table JOIN statements.
15
- class ConstraintMaker
15
+ class Constraint
16
16
  include Singleton
17
17
 
18
+ def make(constraints, table, previous_table)
19
+ constraints.inject(nil) do |memo, constraint|
20
+ method = CONSTRAINT_RESOLVERS[constraint.class]
21
+
22
+ raise ArgumentError, "constraint unhandled: #{constraint.class.name}" unless method
23
+
24
+ method.call(constraint, memo, table, previous_table)
25
+ end
26
+ end
27
+
18
28
  CONCAT_METHOD = lambda do |on, arel_column, value|
19
29
  on ? on.and(arel_column.eq(value)) : arel_column.eq(value)
20
30
  end
@@ -44,16 +54,6 @@ module Dbee
44
54
  }.freeze
45
55
 
46
56
  private_constant :CONSTRAINT_RESOLVERS
47
-
48
- def make(constraints, table, previous_table)
49
- constraints.inject(nil) do |memo, constraint|
50
- method = CONSTRAINT_RESOLVERS[constraint.class]
51
-
52
- raise ArgumentError, "constraint unhandled: #{constraint.class.name}" unless method
53
-
54
- method.call(constraint, memo, table, previous_table)
55
- end
56
- end
57
57
  end
58
58
  end
59
59
  end
@@ -12,16 +12,9 @@ module Dbee
12
12
  class ActiveRecordProvider
13
13
  class ExpressionBuilder
14
14
  # Derives Arel#order predicates.
15
- class OrderMaker
15
+ class Order
16
16
  include Singleton
17
17
 
18
- SORTER_EVALUATORS = {
19
- Query::Sorters::Ascending => ->(column) { column },
20
- Query::Sorters::Descending => ->(column) { column.desc }
21
- }.freeze
22
-
23
- private_constant :SORTER_EVALUATORS
24
-
25
18
  def make(sorter, arel_column)
26
19
  method = SORTER_EVALUATORS[sorter.class]
27
20
 
@@ -29,6 +22,13 @@ module Dbee
29
22
 
30
23
  method.call(arel_column)
31
24
  end
25
+
26
+ SORTER_EVALUATORS = {
27
+ Query::Sorters::Ascending => ->(column) { column },
28
+ Query::Sorters::Descending => ->(column) { column.desc }
29
+ }.freeze
30
+
31
+ private_constant :SORTER_EVALUATORS
32
32
  end
33
33
  end
34
34
  end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2019-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ module Dbee
11
+ module Providers
12
+ class ActiveRecordProvider
13
+ class ExpressionBuilder
14
+ # Derives Arel#project predicates.
15
+ class Select
16
+ include Singleton
17
+
18
+ def make(field, arel_key_nodes_to_filters, arel_value_node, alias_maker)
19
+ column_alias = quote(alias_maker.make(field.display))
20
+ predicate = expression(field, arel_key_nodes_to_filters, arel_value_node)
21
+ predicate = aggregate(field, predicate)
22
+
23
+ predicate.as(column_alias)
24
+ end
25
+
26
+ private
27
+
28
+ AGGREGRATOR_EVALUATORS = {
29
+ nil => ->(arel_node) { arel_node },
30
+ Query::Field::Aggregator::AVE => ->(node) { Arel::Nodes::Avg.new([node]) },
31
+ Query::Field::Aggregator::COUNT => ->(node) { Arel::Nodes::Count.new([node]) },
32
+ Query::Field::Aggregator::MAX => ->(node) { Arel::Nodes::Max.new([node]) },
33
+ Query::Field::Aggregator::MIN => ->(node) { Arel::Nodes::Min.new([node]) },
34
+ Query::Field::Aggregator::SUM => ->(node) { Arel::Nodes::Sum.new([node]) }
35
+ }.freeze
36
+
37
+ private_constant :AGGREGRATOR_EVALUATORS
38
+
39
+ def quote(value)
40
+ ActiveRecord::Base.connection.quote(value)
41
+ end
42
+
43
+ def aggregate(field, predicate)
44
+ AGGREGRATOR_EVALUATORS[field.aggregator].call(predicate)
45
+ end
46
+
47
+ def expression(field, arel_key_nodes_to_filters, arel_value_node)
48
+ if field.filters?
49
+ case_statement = Arel::Nodes::Case.new
50
+ filter_predicate = make_filter_predicate(arel_key_nodes_to_filters)
51
+
52
+ case_statement.when(filter_predicate).then(arel_value_node)
53
+ else
54
+ arel_value_node
55
+ end
56
+ end
57
+
58
+ def make_filter_predicate(arel_key_nodes_to_filters)
59
+ predicates = arel_key_nodes_to_filters.map do |arel_key_node, filter|
60
+ Where.instance.make(filter, arel_key_node)
61
+ end
62
+
63
+ predicates.inject(predicates.shift) do |memo, predicate|
64
+ memo.and(predicate)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end