dbee 2.0.3 → 2.1.0.pre.alpha
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/.rubocop.yml +22 -7
- data/.ruby-version +1 -1
- data/.travis.yml +3 -4
- data/CHANGELOG.md +11 -0
- data/README.md +123 -0
- data/dbee.gemspec +13 -5
- data/exe/.gitkeep +0 -0
- data/lib/dbee/query.rb +1 -3
- data/lib/dbee/query/field.rb +36 -5
- data/lib/dbee/version.rb +1 -1
- data/spec/dbee/query/field_spec.rb +54 -6
- metadata +20 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: acc518f50b6b9e95165347240882ca415194e6b641e68ff292d5b2a6d3937527
|
4
|
+
data.tar.gz: 954dbcdfac112f2098e7c1828d8c243369092526ee65179c9819ceaafddc70f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5731c83ec9b0ded7f36c6b07d16597f97337240bd65bbec7dc82a9ca7f4de4da0cac9063a6e9aa9cf8199e6f5c32e7ab0a2904684b64bebde9e996f9bceb8e41
|
7
|
+
data.tar.gz: c47a038d7fac640a620a13373460bbd8df05a35b209264de1b0fedd727eab072925d0f2cfa82a800ddd2362756f887b0cd227d1e1cd01c98a0334b1c3cd55cd0
|
data/.rubocop.yml
CHANGED
@@ -1,6 +1,18 @@
|
|
1
|
-
|
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
|
+
|
4
16
|
Metrics/BlockLength:
|
5
17
|
ExcludedMethods:
|
6
18
|
- let
|
@@ -10,14 +22,17 @@ Metrics/BlockLength:
|
|
10
22
|
- specify
|
11
23
|
- define
|
12
24
|
|
25
|
+
Metrics/ClassLength:
|
26
|
+
Max: 125
|
27
|
+
|
13
28
|
Metrics/MethodLength:
|
14
29
|
Max: 25
|
15
30
|
|
16
|
-
|
17
|
-
|
31
|
+
Style/HashEachMethods:
|
32
|
+
Enabled: True
|
18
33
|
|
19
|
-
|
20
|
-
|
34
|
+
Style/HashTransformKeys:
|
35
|
+
Enabled: True
|
21
36
|
|
22
|
-
|
23
|
-
|
37
|
+
Style/HashTransformValues:
|
38
|
+
Enabled: True
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.6.
|
1
|
+
2.6.6
|
data/.travis.yml
CHANGED
@@ -4,10 +4,9 @@ env:
|
|
4
4
|
language: ruby
|
5
5
|
rvm:
|
6
6
|
# Build on the latest stable of all supported Rubies (https://www.ruby-lang.org/en/downloads/):
|
7
|
-
- 2.
|
8
|
-
- 2.
|
9
|
-
- 2.
|
10
|
-
- 2.6.5
|
7
|
+
- 2.5.8
|
8
|
+
- 2.6.6
|
9
|
+
- 2.7.1
|
11
10
|
cache: bundler
|
12
11
|
before_script:
|
13
12
|
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
# 2.1.0 (TBD)
|
2
|
+
|
3
|
+
### Additions:
|
4
|
+
|
5
|
+
* Added Dbee::Query::Field#aggregator (such as ave, min, max, sum, etc.)
|
6
|
+
* Added Dbee::Query::Field#filters (allows for doing select column filtering)
|
7
|
+
|
8
|
+
### Changes:
|
9
|
+
|
10
|
+
* Bumped minimum Ruby version to 2.5
|
11
|
+
|
1
12
|
# 2.0.3 (January 7th, 2020)
|
2
13
|
|
3
14
|
### Fixes:
|
data/README.md
CHANGED
@@ -407,6 +407,129 @@ sql = Dbee.sql(model, query, provider)
|
|
407
407
|
|
408
408
|
The above examples showed how to use a plugin provider, see the plugin provider's documentation for more information about its options and use.
|
409
409
|
|
410
|
+
#### Aggregation
|
411
|
+
|
412
|
+
Fields can be configured to use aggregation by setting its `aggregator` attribute. For example, say we wanted to count the number of patients per practice:
|
413
|
+
|
414
|
+
**Data Model**:
|
415
|
+
|
416
|
+
````yaml
|
417
|
+
name: practice
|
418
|
+
models:
|
419
|
+
- name: patients
|
420
|
+
constraints:
|
421
|
+
- type: reference
|
422
|
+
name: practice_id
|
423
|
+
parent: id
|
424
|
+
````
|
425
|
+
|
426
|
+
**Query**:
|
427
|
+
|
428
|
+
````ruby
|
429
|
+
query = {
|
430
|
+
fields: [
|
431
|
+
{
|
432
|
+
key_path: 'id',
|
433
|
+
display: 'Practice ID #',
|
434
|
+
},
|
435
|
+
{
|
436
|
+
key_path: 'name',
|
437
|
+
display: 'Practice Name',
|
438
|
+
},
|
439
|
+
{
|
440
|
+
key_path: 'patients.id',
|
441
|
+
display: 'Total Patients',
|
442
|
+
aggregator: :count
|
443
|
+
},
|
444
|
+
]
|
445
|
+
````
|
446
|
+
|
447
|
+
An example of a materialized result would be something akin to:
|
448
|
+
|
449
|
+
Practice ID # | Practice Name | Total Patients
|
450
|
+
------------- | --------------- | --------------
|
451
|
+
1 | Families Choice | 293
|
452
|
+
2 | Awesome Choice | 2305
|
453
|
+
3 | Best Value | 1200
|
454
|
+
|
455
|
+
A complete list of aggregator values can be found by inspecting the `Dbee::Query::Field::Aggregator` constant.
|
456
|
+
|
457
|
+
#### Field/Column Level Filtering & Pivoting
|
458
|
+
|
459
|
+
Fields can also have filters which provide post-filtering (on the select-level instead of at query-level.) This can be used in conjunction with aggregate functions to provide pivoting. For example:
|
460
|
+
|
461
|
+
**Data/Schema Example**:
|
462
|
+
|
463
|
+
patients:
|
464
|
+
|
465
|
+
id | first | last
|
466
|
+
-- | ----- | -----
|
467
|
+
1 | frank | rizzo
|
468
|
+
|
469
|
+
patient_fields:
|
470
|
+
|
471
|
+
id | patient_id | key | value
|
472
|
+
-- | ---------- | --------------- | -----
|
473
|
+
1 | 1 | dob | 1900-01-01
|
474
|
+
2 | 1 | drivers_license | ABC123
|
475
|
+
|
476
|
+
**Model Configuration**:
|
477
|
+
|
478
|
+
````yaml
|
479
|
+
name: patients
|
480
|
+
models:
|
481
|
+
- name: patient_fields
|
482
|
+
constraints:
|
483
|
+
- type: reference
|
484
|
+
parent: id
|
485
|
+
name: patient_id
|
486
|
+
````
|
487
|
+
|
488
|
+
**Query**:
|
489
|
+
|
490
|
+
````ruby
|
491
|
+
query = {
|
492
|
+
fields: [
|
493
|
+
{
|
494
|
+
key_path: 'id',
|
495
|
+
display: 'ID #'
|
496
|
+
},
|
497
|
+
{
|
498
|
+
key_path: 'first',
|
499
|
+
display: 'First Name'
|
500
|
+
},
|
501
|
+
{
|
502
|
+
aggregator: :max,
|
503
|
+
key_path: 'patient_fields.value',
|
504
|
+
display: 'Date of Birth',
|
505
|
+
filters: [
|
506
|
+
{
|
507
|
+
key_path: 'patient_fields.key',
|
508
|
+
value: 'dob'
|
509
|
+
}
|
510
|
+
]
|
511
|
+
},
|
512
|
+
{
|
513
|
+
aggregator: :max,
|
514
|
+
key_path: 'patient_fields.value',
|
515
|
+
display: 'Drivers License #',
|
516
|
+
filters: [
|
517
|
+
{
|
518
|
+
key_path: 'patient_fields.key',
|
519
|
+
value: 'drivers_license'
|
520
|
+
}
|
521
|
+
]
|
522
|
+
}
|
523
|
+
}
|
524
|
+
}
|
525
|
+
````
|
526
|
+
|
527
|
+
Executing the query above against the data and model would yield:
|
528
|
+
|
529
|
+
ID # | First Name | Date of Birth | Drivers License #
|
530
|
+
-- | ---------- | ------------- | -----------------
|
531
|
+
1 | frank | 1900-01-01 | ABC123
|
532
|
+
|
410
533
|
|
411
534
|
## Contributing
|
412
535
|
|
data/dbee.gemspec
CHANGED
@@ -15,20 +15,28 @@ 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.
|
18
|
+
s.bindir = 'exe'
|
19
|
+
s.executables = []
|
19
20
|
s.homepage = 'https://github.com/bluemarblepayroll/dbee'
|
20
21
|
s.license = 'MIT'
|
22
|
+
s.metadata = {
|
23
|
+
'bug_tracker_uri' => 'https://github.com/bluemarblepayroll/dbee/issues',
|
24
|
+
'changelog_uri' => 'https://github.com/bluemarblepayroll/dbee/blob/master/CHANGELOG.md',
|
25
|
+
'documentation_uri' => 'https://www.rubydoc.info/gems/dbee',
|
26
|
+
'homepage_uri' => s.homepage,
|
27
|
+
'source_code_uri' => s.homepage
|
28
|
+
}
|
21
29
|
|
22
|
-
s.required_ruby_version = '>= 2.
|
30
|
+
s.required_ruby_version = '>= 2.5'
|
23
31
|
|
24
|
-
s.add_dependency('acts_as_hashable', '~>1', '>=1.
|
32
|
+
s.add_dependency('acts_as_hashable', '~>1', '>=1.2.0')
|
25
33
|
s.add_dependency('dry-inflector', '~>0')
|
26
34
|
|
27
35
|
s.add_development_dependency('guard-rspec', '~>4.7')
|
28
36
|
s.add_development_dependency('pry', '~>0')
|
29
37
|
s.add_development_dependency('rake', '~> 13')
|
30
38
|
s.add_development_dependency('rspec')
|
31
|
-
s.add_development_dependency('rubocop', '~>0.
|
39
|
+
s.add_development_dependency('rubocop', '~>0.81.0')
|
32
40
|
s.add_development_dependency('simplecov', '~>0.17.0')
|
33
|
-
s.add_development_dependency('simplecov-console', '~>0.
|
41
|
+
s.add_development_dependency('simplecov-console', '~>0.7.0')
|
34
42
|
end
|
data/exe/.gitkeep
ADDED
File without changes
|
data/lib/dbee/query.rb
CHANGED
@@ -25,10 +25,8 @@ module Dbee
|
|
25
25
|
|
26
26
|
attr_reader :fields, :filters, :limit, :sorters
|
27
27
|
|
28
|
-
def_delegator :fields,
|
29
|
-
|
28
|
+
def_delegator :fields, :sort, :sorted_fields
|
30
29
|
def_delegator :filters, :sort, :sorted_filters
|
31
|
-
|
32
30
|
def_delegator :sorters, :sort, :sorted_sorters
|
33
31
|
|
34
32
|
def initialize(fields:, filters: [], limit: nil, sorters: [])
|
data/lib/dbee/query/field.rb
CHANGED
@@ -15,24 +15,55 @@ module Dbee
|
|
15
15
|
class Field
|
16
16
|
acts_as_hashable
|
17
17
|
|
18
|
-
|
18
|
+
module Aggregator
|
19
|
+
AVE = :ave
|
20
|
+
COUNT = :count
|
21
|
+
MAX = :max
|
22
|
+
MIN = :min
|
23
|
+
SUM = :sum
|
24
|
+
end
|
25
|
+
include Aggregator
|
26
|
+
|
27
|
+
attr_reader :aggregator, :display, :filters, :key_path
|
19
28
|
|
20
|
-
def initialize(
|
29
|
+
def initialize(
|
30
|
+
aggregator: nil,
|
31
|
+
display: nil,
|
32
|
+
filters: [],
|
33
|
+
key_path:
|
34
|
+
)
|
21
35
|
raise ArgumentError, 'key_path is required' if key_path.to_s.empty?
|
22
36
|
|
23
|
-
@
|
24
|
-
@display
|
37
|
+
@aggregator = aggregator ? Aggregator.const_get(aggregator.to_s.upcase.to_sym) : nil
|
38
|
+
@display = (display.to_s.empty? ? key_path : display).to_s
|
39
|
+
@filters = Filters.array(filters).uniq
|
40
|
+
@key_path = KeyPath.get(key_path)
|
25
41
|
|
26
42
|
freeze
|
27
43
|
end
|
28
44
|
|
45
|
+
def filters?
|
46
|
+
filters.any?
|
47
|
+
end
|
48
|
+
|
49
|
+
def aggregator?
|
50
|
+
!aggregator.nil?
|
51
|
+
end
|
52
|
+
|
29
53
|
def hash
|
30
|
-
|
54
|
+
[
|
55
|
+
aggregator,
|
56
|
+
display,
|
57
|
+
filters,
|
58
|
+
key_path
|
59
|
+
].hash
|
31
60
|
end
|
32
61
|
|
33
62
|
def ==(other)
|
34
63
|
other.instance_of?(self.class) &&
|
64
|
+
other.aggregator == aggregator &&
|
35
65
|
other.key_path == key_path &&
|
66
|
+
other.filters == filters &&
|
36
67
|
other.display == display
|
37
68
|
end
|
38
69
|
alias eql? ==
|
data/lib/dbee/version.rb
CHANGED
@@ -10,6 +10,31 @@
|
|
10
10
|
require 'spec_helper'
|
11
11
|
|
12
12
|
describe Dbee::Query::Field do
|
13
|
+
let(:config) do
|
14
|
+
{
|
15
|
+
display: 'd',
|
16
|
+
key_path: 'a.b.c'
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:config_with_aggregation_and_filters) do
|
21
|
+
config.merge(
|
22
|
+
aggregator: :ave,
|
23
|
+
filters: [
|
24
|
+
{
|
25
|
+
key_path: 'a.b',
|
26
|
+
value: 'something'
|
27
|
+
}
|
28
|
+
]
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
subject { described_class.new(config_with_aggregation_and_filters) }
|
33
|
+
|
34
|
+
let(:subject_without_aggregation_and_filters) do
|
35
|
+
described_class.new(config)
|
36
|
+
end
|
37
|
+
|
13
38
|
it 'should act as hashable' do
|
14
39
|
expect(described_class).to respond_to(:make)
|
15
40
|
expect(described_class).to respond_to(:array)
|
@@ -24,16 +49,19 @@ describe Dbee::Query::Field do
|
|
24
49
|
end
|
25
50
|
|
26
51
|
context 'equality' do
|
27
|
-
|
52
|
+
specify '#hash produces same output as [aggregator, key_path, and display]' do
|
53
|
+
expected = [
|
54
|
+
subject.aggregator,
|
55
|
+
subject.display,
|
56
|
+
subject.filters,
|
57
|
+
subject.key_path
|
58
|
+
].hash
|
28
59
|
|
29
|
-
|
30
|
-
|
31
|
-
specify '#hash produces same output as concatenated string hash of key_path and display' do
|
32
|
-
expect(subject.hash).to eq("#{config[:key_path]}#{config[:display]}".hash)
|
60
|
+
expect(subject.hash).to eq(expected)
|
33
61
|
end
|
34
62
|
|
35
63
|
specify '#== and #eql? compare attributes' do
|
36
|
-
object2 = described_class.new(
|
64
|
+
object2 = described_class.new(config_with_aggregation_and_filters)
|
37
65
|
|
38
66
|
expect(subject).to eq(object2)
|
39
67
|
expect(subject).to eql(object2)
|
@@ -44,4 +72,24 @@ describe Dbee::Query::Field do
|
|
44
72
|
expect(subject).not_to eq(nil)
|
45
73
|
end
|
46
74
|
end
|
75
|
+
|
76
|
+
describe '#aggregator?' do
|
77
|
+
it 'returns true if not nil' do
|
78
|
+
expect(subject.aggregator?).to be true
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'returns false if nil' do
|
82
|
+
expect(subject_without_aggregation_and_filters.aggregator?).to be false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#filters?' do
|
87
|
+
it 'returns true if at least one filter' do
|
88
|
+
expect(subject.filters?).to be true
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'returns false if nil' do
|
92
|
+
expect(subject_without_aggregation_and_filters.filters?).to be false
|
93
|
+
end
|
94
|
+
end
|
47
95
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dbee
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.1.0.pre.alpha
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Ruggio
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-07-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: acts_as_hashable
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
version: '1'
|
20
20
|
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 1.
|
22
|
+
version: 1.2.0
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,7 +29,7 @@ dependencies:
|
|
29
29
|
version: '1'
|
30
30
|
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 1.
|
32
|
+
version: 1.2.0
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: dry-inflector
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -106,14 +106,14 @@ dependencies:
|
|
106
106
|
requirements:
|
107
107
|
- - "~>"
|
108
108
|
- !ruby/object:Gem::Version
|
109
|
-
version: 0.
|
109
|
+
version: 0.81.0
|
110
110
|
type: :development
|
111
111
|
prerelease: false
|
112
112
|
version_requirements: !ruby/object:Gem::Requirement
|
113
113
|
requirements:
|
114
114
|
- - "~>"
|
115
115
|
- !ruby/object:Gem::Version
|
116
|
-
version: 0.
|
116
|
+
version: 0.81.0
|
117
117
|
- !ruby/object:Gem::Dependency
|
118
118
|
name: simplecov
|
119
119
|
requirement: !ruby/object:Gem::Requirement
|
@@ -134,22 +134,21 @@ dependencies:
|
|
134
134
|
requirements:
|
135
135
|
- - "~>"
|
136
136
|
- !ruby/object:Gem::Version
|
137
|
-
version: 0.
|
137
|
+
version: 0.7.0
|
138
138
|
type: :development
|
139
139
|
prerelease: false
|
140
140
|
version_requirements: !ruby/object:Gem::Requirement
|
141
141
|
requirements:
|
142
142
|
- - "~>"
|
143
143
|
- !ruby/object:Gem::Version
|
144
|
-
version: 0.
|
144
|
+
version: 0.7.0
|
145
145
|
description: " Dbee provides a simple-to-use data modeling and query API. The
|
146
146
|
query API can produce SQL using other ORMs, such as Arel/ActiveRecord. The targeted
|
147
147
|
use-case for Dbee is ad-hoc reporting, so the total SQL feature-set that Dbee supports
|
148
148
|
is rather limited.\n"
|
149
149
|
email:
|
150
150
|
- mruggio@bluemarblepayroll.com
|
151
|
-
executables:
|
152
|
-
- console
|
151
|
+
executables: []
|
153
152
|
extensions: []
|
154
153
|
extra_rdoc_files: []
|
155
154
|
files:
|
@@ -167,6 +166,7 @@ files:
|
|
167
166
|
- Rakefile
|
168
167
|
- bin/console
|
169
168
|
- dbee.gemspec
|
169
|
+
- exe/.gitkeep
|
170
170
|
- lib/dbee.rb
|
171
171
|
- lib/dbee/base.rb
|
172
172
|
- lib/dbee/constant_resolver.rb
|
@@ -227,7 +227,12 @@ files:
|
|
227
227
|
homepage: https://github.com/bluemarblepayroll/dbee
|
228
228
|
licenses:
|
229
229
|
- MIT
|
230
|
-
metadata:
|
230
|
+
metadata:
|
231
|
+
bug_tracker_uri: https://github.com/bluemarblepayroll/dbee/issues
|
232
|
+
changelog_uri: https://github.com/bluemarblepayroll/dbee/blob/master/CHANGELOG.md
|
233
|
+
documentation_uri: https://www.rubydoc.info/gems/dbee
|
234
|
+
homepage_uri: https://github.com/bluemarblepayroll/dbee
|
235
|
+
source_code_uri: https://github.com/bluemarblepayroll/dbee
|
231
236
|
post_install_message:
|
232
237
|
rdoc_options: []
|
233
238
|
require_paths:
|
@@ -236,12 +241,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
236
241
|
requirements:
|
237
242
|
- - ">="
|
238
243
|
- !ruby/object:Gem::Version
|
239
|
-
version: 2.
|
244
|
+
version: '2.5'
|
240
245
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
241
246
|
requirements:
|
242
|
-
- - "
|
247
|
+
- - ">"
|
243
248
|
- !ruby/object:Gem::Version
|
244
|
-
version:
|
249
|
+
version: 1.3.1
|
245
250
|
requirements: []
|
246
251
|
rubygems_version: 3.0.3
|
247
252
|
signing_key:
|