dbee-active_record 2.1.0.pre.alpha.1 → 2.3.0
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 +4 -4
- data/.circleci/config.yml +84 -0
- data/.rubocop.yml +4 -18
- data/.tool-versions +1 -0
- data/CHANGELOG.md +19 -1
- data/Guardfile +2 -1
- data/README.md +4 -4
- data/dbee-active_record.gemspec +11 -13
- data/lib/dbee/providers/active_record_provider/expression_builder.rb +81 -59
- data/lib/dbee/providers/active_record_provider/maker.rb +37 -0
- data/lib/dbee/providers/active_record_provider/{expression_builder → makers}/constraint.rb +1 -1
- data/lib/dbee/providers/active_record_provider/{expression_builder → makers}/order.rb +1 -1
- data/lib/dbee/providers/active_record_provider/{expression_builder → makers}/select.rb +13 -3
- data/lib/dbee/providers/active_record_provider/{expression_builder → makers}/where.rb +27 -7
- data/lib/dbee/providers/active_record_provider/version.rb +1 -1
- data/lib/dbee/providers/active_record_provider.rb +3 -3
- data/spec/db_helper.rb +7 -3
- data/spec/dbee/providers/active_record_provider/expression_builder_spec.rb +90 -0
- data/spec/dbee/providers/active_record_provider/makers/where_spec.rb +260 -0
- data/spec/dbee/providers/active_record_provider_spec.rb +15 -18
- data/spec/fixtures/active_record_snapshots/five_table_query.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/multiple_same_table_query_with_static_constraints.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/one_table_empty_query.yaml +11 -0
- data/spec/fixtures/active_record_snapshots/one_table_query.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/one_table_query_with_ascending_sort.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/one_table_query_with_descending_sort.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/one_table_query_with_filters.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/one_table_query_with_limit.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/one_table_query_with_multiple_sorts.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/partitioner_example_1_query.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/partitioner_example_2_query.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/reverse_polymorphic_query.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/two_table_query.yaml +6 -0
- data/spec/fixtures/active_record_snapshots/two_table_query_with_aggregation.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/two_table_query_with_pivoting.yaml +1 -0
- data/spec/fixtures/models.yaml +110 -102
- data/spec/spec_helper.rb +13 -2
- metadata +108 -27
- data/.travis.yml +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b68d2e6c79fa4d3bb1ff26726983750cc112f60af4355eee8795cd748d9be9c
|
4
|
+
data.tar.gz: 5e0a15f3f85f465d7bc6fc79a9c74c6847e72be6a5a4ac25ec626f54bec985c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23bb62872a7c2412f0e8745b392b93fbd0c5bc1c7cfaefdc6503f9add73566b774378a69efe82eea53ee9f20c461585d163b96cc715541133b825f4906f87b89
|
7
|
+
data.tar.gz: cff34e878e7abb09865d88893af53e4d578077742ed4768fcee6c612c546e44a5a4f340ea2f9f6051a60ce2a62d484fce411c15487a69da00ad04fa5cf8a512e
|
@@ -0,0 +1,84 @@
|
|
1
|
+
version: 2.1
|
2
|
+
|
3
|
+
orbs:
|
4
|
+
status_to_ms_teams: bluemarblepayroll/status_to_ms_teams_pure_bash@1.0.0
|
5
|
+
|
6
|
+
jobs:
|
7
|
+
build:
|
8
|
+
parameters:
|
9
|
+
use-bundler-cache:
|
10
|
+
type: boolean
|
11
|
+
default: true
|
12
|
+
|
13
|
+
docker:
|
14
|
+
- image: circleci/ruby:2.6.6-buster
|
15
|
+
environment:
|
16
|
+
FORBID_FOCUSED_SPECS: 1
|
17
|
+
AR_VERSION: 5
|
18
|
+
- image: mysql@sha256:f3515b6a6502d872d5a37db78e4d225c0fcbf8da65d1faf8ce4609c92e2cbaf0
|
19
|
+
environment:
|
20
|
+
MYSQL_DATABASE: dbee_test
|
21
|
+
MYSQL_ALLOW_EMPTY_PASSWORD: 1
|
22
|
+
MYSQL_USER: root
|
23
|
+
command:
|
24
|
+
mysqld --sql_mode=TRADITIONAL,NO_AUTO_VALUE_ON_ZERO
|
25
|
+
working_directory: ~/dbee-active_record
|
26
|
+
steps:
|
27
|
+
- checkout
|
28
|
+
- run: sudo apt-get --allow-releaseinfo-change update
|
29
|
+
- run: sudo apt-get update && sudo apt-get install -y git curl libmcrypt-dev default-mysql-client
|
30
|
+
- run: dockerize -wait tcp://localhost:3306 -timeout 1m
|
31
|
+
- run: cp spec/config/database.yaml.ci spec/config/database.yaml
|
32
|
+
- run: mysql -h 127.0.0.1 -u root -e 'CREATE DATABASE IF NOT EXISTS dbee_test;'
|
33
|
+
|
34
|
+
# TODO: wrap bundler caching logic into an Orb:
|
35
|
+
- when:
|
36
|
+
condition: << parameters.use-bundler-cache >>
|
37
|
+
steps:
|
38
|
+
- restore_cache:
|
39
|
+
key: v1.0.0-build-ruby-dependency-cache-{{ checksum "dbee-active_record.gemspec" }}-{{ checksum "Gemfile" }}-{{ checksum ".ruby-version" }}
|
40
|
+
|
41
|
+
- run: bundle install --path vendor/bundle
|
42
|
+
|
43
|
+
- when:
|
44
|
+
condition: << parameters.use-bundler-cache >>
|
45
|
+
steps:
|
46
|
+
- save_cache:
|
47
|
+
key: v1.0.0-build-ruby-dependency-cache-{{ checksum "dbee-active_record.gemspec" }}-{{ checksum "Gemfile" }}-{{ checksum ".ruby-version" }}
|
48
|
+
paths:
|
49
|
+
- vendor/bundle
|
50
|
+
|
51
|
+
- store_artifacts:
|
52
|
+
path: Gemfile.lock
|
53
|
+
|
54
|
+
- run: bundle exec rubocop
|
55
|
+
|
56
|
+
- run: COVERAGE=true bundle exec rspec -r rspec_junit_formatter --format progress --format RspecJunitFormatter -o test-results/rspec/results.xml
|
57
|
+
|
58
|
+
- store_test_results:
|
59
|
+
path: test-results
|
60
|
+
|
61
|
+
- store_artifacts:
|
62
|
+
path: coverage
|
63
|
+
|
64
|
+
- status_to_ms_teams/report:
|
65
|
+
webhook_url: $MS_TEAMS_WEBHOOK_URL
|
66
|
+
|
67
|
+
workflows:
|
68
|
+
version: 2.1
|
69
|
+
build:
|
70
|
+
jobs:
|
71
|
+
- build:
|
72
|
+
context: org-global
|
73
|
+
monthly-gem-dependency-refresh-check:
|
74
|
+
triggers:
|
75
|
+
- schedule:
|
76
|
+
cron: '0 0 1 * *'
|
77
|
+
filters:
|
78
|
+
branches:
|
79
|
+
only:
|
80
|
+
- master
|
81
|
+
jobs:
|
82
|
+
- build:
|
83
|
+
context: org-global
|
84
|
+
use-bundler-cache: false
|
data/.rubocop.yml
CHANGED
@@ -1,22 +1,17 @@
|
|
1
1
|
AllCops:
|
2
2
|
TargetRubyVersion: 2.5
|
3
|
+
NewCops: enable
|
3
4
|
|
4
5
|
Layout/LineLength:
|
5
6
|
Max: 100
|
6
7
|
|
7
|
-
Lint/RaiseException:
|
8
|
-
Enabled: True
|
9
|
-
|
10
|
-
Lint/StructNewOverride:
|
11
|
-
Enabled: True
|
12
|
-
|
13
8
|
Metrics/AbcSize:
|
14
|
-
Max:
|
9
|
+
Max: 20
|
15
10
|
Exclude:
|
16
11
|
- spec/db_helper.rb
|
17
12
|
|
18
13
|
Metrics/BlockLength:
|
19
|
-
|
14
|
+
IgnoredMethods:
|
20
15
|
- let
|
21
16
|
- it
|
22
17
|
- describe
|
@@ -28,18 +23,9 @@ Metrics/BlockLength:
|
|
28
23
|
- dbee-active_record.gemspec
|
29
24
|
|
30
25
|
Metrics/ClassLength:
|
31
|
-
Max:
|
26
|
+
Max: 150
|
32
27
|
|
33
28
|
Metrics/MethodLength:
|
34
29
|
Max: 25
|
35
30
|
Exclude:
|
36
31
|
- spec/db_helper.rb
|
37
|
-
|
38
|
-
Style/HashEachMethods:
|
39
|
-
Enabled: True
|
40
|
-
|
41
|
-
Style/HashTransformKeys:
|
42
|
-
Enabled: True
|
43
|
-
|
44
|
-
Style/HashTransformValues:
|
45
|
-
Enabled: True
|
data/.tool-versions
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby 2.6.6
|
data/CHANGELOG.md
CHANGED
@@ -1,9 +1,27 @@
|
|
1
|
-
# 2.
|
1
|
+
# 2.3.0 (October 4th, 2021)
|
2
|
+
|
3
|
+
### Additions:
|
4
|
+
|
5
|
+
* Support for Dbee::Query#offset.
|
6
|
+
# 2.2.0 (March 11th, 2021)
|
7
|
+
|
8
|
+
### Additions:
|
9
|
+
|
10
|
+
* Support for graph based models.
|
11
|
+
|
12
|
+
# 2.1.2 (October 15th, 2020)
|
13
|
+
|
14
|
+
* Improved test coverage for Where maker
|
15
|
+
* Fixed bug in Where maker which resulted in only IS NULL predicates being generated, even in cases
|
16
|
+
of when IS NOT NULL intended.
|
17
|
+
|
18
|
+
# 2.1.1 (July 15th, 2020)
|
2
19
|
|
3
20
|
### Additions:
|
4
21
|
|
5
22
|
* Implemented Dbee::Query::Field#aggregator
|
6
23
|
* Implemented Dbee::Query::Field#filters
|
24
|
+
* Implemented base case when a Dbee::Query contains no fields
|
7
25
|
|
8
26
|
### Changes:
|
9
27
|
|
data/Guardfile
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
command = 'DISABLE_SIMPLECOV=true bundle exec rspec --format=documentation --order=defined'
|
4
|
+
guard :rspec, cmd: command do
|
4
5
|
require 'guard/rspec/dsl'
|
5
6
|
dsl = Guard::RSpec::Dsl.new(self)
|
6
7
|
|
data/README.md
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
# Dbee Active Record Provider
|
2
2
|
|
3
|
-
[](https://circleci.com/bb/bluemarble-ondemand/dbee-active_record/tree/master)
|
4
4
|
|
5
5
|
Dbee does not ship with a SQL generator by default. This library plugs into Dbee to provide SQL generation via ActiveRecord. Technically speaking: this library does not use ActiveRecord for anything except connection information. All actual SQL generation is performed using Arel. There is no actual coupling of your domain ActiveRecord subclasses to Dbee.
|
6
6
|
|
7
|
-
This library is a plugin for
|
7
|
+
This library is a plugin for Dbee. The Dbee repositories README file contains information about how to use the Data Model and Query API's.
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
|
11
11
|
To install through Rubygems:
|
12
12
|
|
13
13
|
````
|
14
|
-
gem install
|
14
|
+
gem install dbee-active_record
|
15
15
|
````
|
16
16
|
|
17
17
|
You can also add this to your Gemfile:
|
@@ -76,7 +76,7 @@ Note: ensure you have proper authorization before trying to publish new versions
|
|
76
76
|
After code changes have successfully gone through the Pull Request review process then the following steps should be followed for publishing new versions:
|
77
77
|
|
78
78
|
1. Merge Pull Request into master
|
79
|
-
2. Update `
|
79
|
+
2. Update `version.rb` using [semantic versioning](https://semver.org/)
|
80
80
|
3. Install dependencies: `bundle`
|
81
81
|
4. Update `CHANGELOG.md` with release notes
|
82
82
|
5. Commit & push master to remote and ensure CI builds master successfully
|
data/dbee-active_record.gemspec
CHANGED
@@ -11,21 +11,13 @@ Gem::Specification.new do |s|
|
|
11
11
|
By default Dbee ships with no underlying SQL generator. This library will plug in ActiveRecord into Dbee and Dbee will use it for SQL generation.
|
12
12
|
DESCRIPTION
|
13
13
|
|
14
|
-
s.authors = ['Matthew Ruggio']
|
15
|
-
s.email = ['mruggio@bluemarblepayroll.com']
|
14
|
+
s.authors = ['Matthew Ruggio', 'Craig Kattner']
|
15
|
+
s.email = ['mruggio@bluemarblepayroll.com', 'ckattner@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
18
|
s.bindir = 'exe'
|
19
19
|
s.executables = []
|
20
|
-
s.homepage = 'https://github.com/bluemarblepayroll/dbee-active_record'
|
21
20
|
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
|
-
}
|
29
21
|
|
30
22
|
s.required_ruby_version = '>= 2.5'
|
31
23
|
|
@@ -42,15 +34,21 @@ Gem::Specification.new do |s|
|
|
42
34
|
end
|
43
35
|
|
44
36
|
s.add_dependency('activerecord', activerecord_version)
|
45
|
-
s.add_dependency('dbee', '
|
37
|
+
s.add_dependency('dbee', '~>3', '>=3.1.0')
|
46
38
|
|
47
39
|
s.add_development_dependency('guard-rspec', '~>4.7')
|
48
40
|
s.add_development_dependency('mysql2', '~>0.5')
|
49
41
|
s.add_development_dependency('pry', '~>0')
|
42
|
+
s.add_development_dependency('pry-byebug')
|
50
43
|
s.add_development_dependency('rake', '~> 13')
|
51
44
|
s.add_development_dependency('rspec', '~> 3.8')
|
52
|
-
s.add_development_dependency('
|
53
|
-
s.add_development_dependency('
|
45
|
+
s.add_development_dependency('rspec_junit_formatter')
|
46
|
+
s.add_development_dependency('rubocop', '~> 1')
|
47
|
+
s.add_development_dependency('rubocop-rake')
|
48
|
+
s.add_development_dependency('rubocop-rspec')
|
49
|
+
s.add_development_dependency('simplecov', '~>0.19.0')
|
54
50
|
s.add_development_dependency('simplecov-console', '~>0.7.0')
|
55
51
|
s.add_development_dependency('sqlite3', '~>1')
|
52
|
+
# Helpful to spot differences in longer SQL queries:
|
53
|
+
s.add_development_dependency('super_diff', '~>0.6')
|
56
54
|
end
|
@@ -7,52 +7,28 @@
|
|
7
7
|
# LICENSE file in the root directory of this source tree.
|
8
8
|
#
|
9
9
|
|
10
|
-
require_relative '
|
11
|
-
require_relative 'expression_builder/order'
|
12
|
-
require_relative 'expression_builder/select'
|
13
|
-
require_relative 'expression_builder/where'
|
10
|
+
require_relative 'maker'
|
14
11
|
|
15
12
|
module Dbee
|
16
13
|
module Providers
|
17
14
|
class ActiveRecordProvider
|
18
|
-
# This class can generate an Arel expression tree
|
19
|
-
|
15
|
+
# This class can generate an Arel expression tree given a Dbee::Schema
|
16
|
+
# and Dbee::Query.
|
17
|
+
class ExpressionBuilder < Maker # :nodoc: all
|
20
18
|
class MissingConstraintError < StandardError; end
|
21
19
|
|
22
|
-
def initialize(
|
23
|
-
|
24
|
-
@table_alias_maker = table_alias_maker
|
25
|
-
@column_alias_maker = column_alias_maker
|
26
|
-
@requires_group_by = false
|
27
|
-
@group_by_columns = []
|
20
|
+
def initialize(schema, table_alias_maker, column_alias_maker)
|
21
|
+
super(column_alias_maker)
|
28
22
|
|
29
|
-
|
23
|
+
@schema = schema
|
24
|
+
@table_alias_maker = table_alias_maker
|
30
25
|
end
|
31
26
|
|
32
|
-
def
|
33
|
-
|
27
|
+
def to_sql(query)
|
28
|
+
reset_query_state
|
29
|
+
build_query(query)
|
34
30
|
|
35
|
-
|
36
|
-
|
37
|
-
add_partitioners(base_table, model.partitioners)
|
38
|
-
end
|
39
|
-
|
40
|
-
def add(query)
|
41
|
-
query.fields.each { |field| add_field(field) }
|
42
|
-
query.sorters.each { |sorter| add_sorter(sorter) }
|
43
|
-
query.filters.each { |filter| add_filter(filter) }
|
44
|
-
|
45
|
-
add_limit(query.limit)
|
46
|
-
|
47
|
-
self
|
48
|
-
end
|
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
|
31
|
+
return statement.project(select_maker.star(base_table)).to_sql if select_all
|
56
32
|
|
57
33
|
statement.to_sql
|
58
34
|
end
|
@@ -60,19 +36,47 @@ module Dbee
|
|
60
36
|
private
|
61
37
|
|
62
38
|
attr_reader :base_table,
|
39
|
+
:key_paths_to_arel_columns,
|
40
|
+
:from_model,
|
63
41
|
:statement,
|
64
|
-
:model,
|
65
42
|
:table_alias_maker,
|
66
|
-
:column_alias_maker,
|
67
43
|
:requires_group_by,
|
68
|
-
:group_by_columns
|
44
|
+
:group_by_columns,
|
45
|
+
:schema,
|
46
|
+
:select_all,
|
47
|
+
:tables
|
48
|
+
|
49
|
+
def reset_query_state
|
50
|
+
@base_table = nil
|
51
|
+
@key_paths_to_arel_columns = {}
|
52
|
+
@from_model = nil
|
53
|
+
@group_by_columns = []
|
54
|
+
@requires_group_by = false
|
55
|
+
@select_all = true
|
56
|
+
@tables = {}
|
57
|
+
end
|
58
|
+
|
59
|
+
def build_query(query)
|
60
|
+
establish_query_base(query)
|
61
|
+
process_fields_sorters_and_filters(query)
|
62
|
+
|
63
|
+
add_partitioners(base_table, from_model.partitioners)
|
64
|
+
add_limit(query.limit)
|
65
|
+
add_offset(query.offset)
|
66
|
+
|
67
|
+
statement.group(group_by_columns) if requires_group_by && !group_by_columns.empty?
|
68
|
+
end
|
69
69
|
|
70
|
-
def
|
71
|
-
@
|
70
|
+
def establish_query_base(query)
|
71
|
+
@from_model = schema.model_for_name!(query.from)
|
72
|
+
@base_table = make_table(from_model.table, @from_model.name)
|
73
|
+
build(base_table)
|
72
74
|
end
|
73
75
|
|
74
|
-
def
|
75
|
-
|
76
|
+
def process_fields_sorters_and_filters(query)
|
77
|
+
query.fields.each { |field| add_field(field) }
|
78
|
+
query.sorters.each { |sorter| add_sorter(sorter) }
|
79
|
+
query.filters.each { |filter| add_filter(filter) }
|
76
80
|
end
|
77
81
|
|
78
82
|
def add_filter(filter)
|
@@ -80,7 +84,7 @@ module Dbee
|
|
80
84
|
|
81
85
|
key_path = filter.key_path
|
82
86
|
arel_column = key_paths_to_arel_columns[key_path]
|
83
|
-
predicate =
|
87
|
+
predicate = where_maker.make(filter, arel_column)
|
84
88
|
|
85
89
|
build(statement.where(predicate))
|
86
90
|
|
@@ -92,7 +96,7 @@ module Dbee
|
|
92
96
|
|
93
97
|
key_path = sorter.key_path
|
94
98
|
arel_column = key_paths_to_arel_columns[key_path]
|
95
|
-
predicate =
|
99
|
+
predicate = order_maker.make(sorter, arel_column)
|
96
100
|
|
97
101
|
build(statement.order(predicate))
|
98
102
|
|
@@ -108,14 +112,14 @@ module Dbee
|
|
108
112
|
end
|
109
113
|
|
110
114
|
def add_field(field)
|
115
|
+
@select_all = false
|
111
116
|
arel_value_column = add_key_path(field.key_path)
|
112
117
|
arel_key_columns_to_filters = add_filter_key_paths(field.filters)
|
113
118
|
|
114
|
-
predicate =
|
119
|
+
predicate = select_maker.make(
|
115
120
|
field,
|
116
121
|
arel_key_columns_to_filters,
|
117
|
-
arel_value_column
|
118
|
-
column_alias_maker
|
122
|
+
arel_value_column
|
119
123
|
)
|
120
124
|
|
121
125
|
build(statement.project(predicate))
|
@@ -137,6 +141,14 @@ module Dbee
|
|
137
141
|
self
|
138
142
|
end
|
139
143
|
|
144
|
+
def add_offset(offset)
|
145
|
+
offset = offset ? offset.to_i : nil
|
146
|
+
|
147
|
+
build(statement.skip(offset))
|
148
|
+
|
149
|
+
self
|
150
|
+
end
|
151
|
+
|
140
152
|
def add_partitioners(table, partitioners)
|
141
153
|
partitioners.each do |partitioner|
|
142
154
|
arel_column = table[partitioner.name]
|
@@ -148,33 +160,43 @@ module Dbee
|
|
148
160
|
self
|
149
161
|
end
|
150
162
|
|
151
|
-
def table(
|
152
|
-
table = make_table(model.table,
|
163
|
+
def table(ancestor_names, relationship, model, previous_table)
|
164
|
+
table = make_table(model.table, ancestor_names)
|
153
165
|
|
154
|
-
on =
|
166
|
+
on = constraint_maker.make(relationship.constraints, table, previous_table)
|
155
167
|
|
156
|
-
raise MissingConstraintError, "for: #{
|
168
|
+
raise MissingConstraintError, "for: #{ancestor_names}" unless on
|
157
169
|
|
158
170
|
build(statement.join(table, ::Arel::Nodes::OuterJoin))
|
159
171
|
build(statement.on(on))
|
160
172
|
|
161
173
|
add_partitioners(table, model.partitioners)
|
162
174
|
|
163
|
-
tables[
|
175
|
+
tables[ancestor_names] = table
|
164
176
|
end
|
165
177
|
|
166
|
-
|
167
|
-
|
168
|
-
|
178
|
+
# Travel the query path returning the table at the end of the path.
|
179
|
+
#
|
180
|
+
# Side effect: intermediate tables are created along the way and are
|
181
|
+
# added to the "tables" hash keyed by path.
|
182
|
+
def traverse_query_path(expanded_query_path)
|
183
|
+
visited_path = []
|
184
|
+
|
185
|
+
expanded_query_path.inject(base_table) do |prev_model, (relationship, next_model)|
|
186
|
+
visited_path += [relationship.name]
|
187
|
+
if tables.key?(visited_path)
|
188
|
+
tables[visited_path]
|
189
|
+
else
|
190
|
+
table(visited_path, relationship, next_model, prev_model)
|
191
|
+
end
|
169
192
|
end
|
170
193
|
end
|
171
194
|
|
172
195
|
def add_key_path(key_path)
|
173
196
|
return key_paths_to_arel_columns[key_path] if key_paths_to_arel_columns.key?(key_path)
|
174
197
|
|
175
|
-
|
176
|
-
|
177
|
-
table = traverse_ancestors(ancestors)
|
198
|
+
expanded_query_path = schema.expand_query_path(from_model, key_path)
|
199
|
+
table = traverse_query_path(expanded_query_path)
|
178
200
|
|
179
201
|
arel_column = table[key_path.column_name]
|
180
202
|
|
@@ -0,0 +1,37 @@
|
|
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
|
+
require_relative 'makers/constraint'
|
11
|
+
require_relative 'makers/order'
|
12
|
+
require_relative 'makers/select'
|
13
|
+
require_relative 'makers/where'
|
14
|
+
|
15
|
+
module Dbee
|
16
|
+
module Providers
|
17
|
+
class ActiveRecordProvider
|
18
|
+
# This class composes all the maker instances into one for use together.
|
19
|
+
class Maker # :nodoc: all
|
20
|
+
def initialize(column_alias_maker)
|
21
|
+
@column_alias_maker = column_alias_maker
|
22
|
+
@constraint_maker = Makers::Constraint.instance
|
23
|
+
@order_maker = Makers::Order.instance
|
24
|
+
@select_maker = Makers::Select.new(column_alias_maker)
|
25
|
+
@where_maker = Makers::Where.instance
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :constraint_maker,
|
31
|
+
:order_maker,
|
32
|
+
:select_maker,
|
33
|
+
:where_maker
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -10,12 +10,22 @@
|
|
10
10
|
module Dbee
|
11
11
|
module Providers
|
12
12
|
class ActiveRecordProvider
|
13
|
-
|
13
|
+
module Makers # :nodoc: all
|
14
14
|
# Derives Arel#project predicates.
|
15
15
|
class Select
|
16
|
-
|
16
|
+
attr_reader :alias_maker
|
17
17
|
|
18
|
-
def
|
18
|
+
def initialize(alias_maker)
|
19
|
+
@alias_maker = alias_maker
|
20
|
+
|
21
|
+
freeze
|
22
|
+
end
|
23
|
+
|
24
|
+
def star(arel_table)
|
25
|
+
arel_table[Arel.star]
|
26
|
+
end
|
27
|
+
|
28
|
+
def make(field, arel_key_nodes_to_filters, arel_value_node)
|
19
29
|
column_alias = quote(alias_maker.make(field.display))
|
20
30
|
predicate = expression(field, arel_key_nodes_to_filters, arel_value_node)
|
21
31
|
predicate = aggregate(field, predicate)
|
@@ -10,20 +10,26 @@
|
|
10
10
|
module Dbee
|
11
11
|
module Providers
|
12
12
|
class ActiveRecordProvider
|
13
|
-
|
13
|
+
module Makers # :nodoc: all
|
14
14
|
# Derives Arel#where predicates.
|
15
15
|
class Where
|
16
16
|
include Singleton
|
17
17
|
|
18
18
|
def make(filter, arel_column)
|
19
|
-
# If the filter has a value of nil, then simply return an IS NULL predicate
|
20
|
-
return make_is_null_predicate(arel_column)
|
19
|
+
# If the filter has a value of nil, then simply return an IS (NOT) NULL predicate
|
20
|
+
return make_is_null_predicate(arel_column, filter.class) if filter.value.nil?
|
21
21
|
|
22
22
|
values = Array(filter.value).flatten
|
23
23
|
|
24
24
|
# This logic helps ensure that if a null exists that it translates to an IS NULL
|
25
25
|
# predicate and does not get put into an in or not_in clause.
|
26
|
-
predicates =
|
26
|
+
predicates =
|
27
|
+
if values.include?(nil)
|
28
|
+
[make_is_null_predicate(arel_column, filter.class)]
|
29
|
+
else
|
30
|
+
[]
|
31
|
+
end
|
32
|
+
|
27
33
|
predicates += make_predicates(filter, arel_column, values - [nil])
|
28
34
|
|
29
35
|
# Chain all predicates together
|
@@ -47,7 +53,20 @@ module Dbee
|
|
47
53
|
Query::Filters::StartsWith => ->(node, val) { node.matches("#{val}%") }
|
48
54
|
}.freeze
|
49
55
|
|
50
|
-
|
56
|
+
NULL_PREDICATE_MAP = {
|
57
|
+
Query::Filters::Contains => Query::Filters::Equals,
|
58
|
+
Query::Filters::Equals => Query::Filters::Equals,
|
59
|
+
Query::Filters::GreaterThan => Query::Filters::Equals,
|
60
|
+
Query::Filters::GreaterThanOrEqualTo => Query::Filters::Equals,
|
61
|
+
Query::Filters::LessThan => Query::Filters::Equals,
|
62
|
+
Query::Filters::LessThanOrEqualTo => Query::Filters::Equals,
|
63
|
+
Query::Filters::NotContain => Query::Filters::NotEquals,
|
64
|
+
Query::Filters::NotEquals => Query::Filters::NotEquals,
|
65
|
+
Query::Filters::NotStartWith => Query::Filters::NotEquals,
|
66
|
+
Query::Filters::StartsWith => Query::Filters::Equals
|
67
|
+
}.freeze
|
68
|
+
|
69
|
+
private_constant :FILTER_EVALUATORS, :NULL_PREDICATE_MAP
|
51
70
|
|
52
71
|
def make_predicates(filter, arel_column, values)
|
53
72
|
if use_in?(filter, values)
|
@@ -81,8 +100,9 @@ module Dbee
|
|
81
100
|
method.call(arel_column, value)
|
82
101
|
end
|
83
102
|
|
84
|
-
def make_is_null_predicate(arel_column)
|
85
|
-
|
103
|
+
def make_is_null_predicate(arel_column, requested_filter_class)
|
104
|
+
actual_filter_class = NULL_PREDICATE_MAP[requested_filter_class]
|
105
|
+
make_predicate(arel_column, actual_filter_class, nil)
|
86
106
|
end
|
87
107
|
end
|
88
108
|
end
|
@@ -35,12 +35,12 @@ module Dbee
|
|
35
35
|
@column_alias_maker = alias_maker(column_prefix)
|
36
36
|
end
|
37
37
|
|
38
|
-
def sql(
|
38
|
+
def sql(schema, query)
|
39
39
|
ExpressionBuilder.new(
|
40
|
-
|
40
|
+
schema,
|
41
41
|
table_alias_maker,
|
42
42
|
column_alias_maker
|
43
|
-
).
|
43
|
+
).to_sql(query)
|
44
44
|
end
|
45
45
|
|
46
46
|
private
|