dbee 2.0.3 → 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: a84b1a2e86f04be67850fa1bfe79aa9a52d1548db6ba8c76df551b5257a00bd8
4
- data.tar.gz: 2c7d2d5b847e805f46c94019c72b6d7c79990ebd2d4b7d8a3d940278b267a904
3
+ metadata.gz: acc518f50b6b9e95165347240882ca415194e6b641e68ff292d5b2a6d3937527
4
+ data.tar.gz: 954dbcdfac112f2098e7c1828d8c243369092526ee65179c9819ceaafddc70f7
5
5
  SHA512:
6
- metadata.gz: 83f756643490546309610118e1ff5efb501c3db51c0322e5a2bb7a0fe2bc96b5164362d9531e6ff11371fee8a6096ba38c04f5c269bf590670fa76c03d62bb45
7
- data.tar.gz: 449063144c4ba5babd0358e7d8851686d4cf7520d2aef2a9444175e616cc9a9a01fe8c3596c941e24ce4dafa5c7e4aaa0fe8d8ac378953f61d97e2acbc4a36b8
6
+ metadata.gz: 5731c83ec9b0ded7f36c6b07d16597f97337240bd65bbec7dc82a9ca7f4de4da0cac9063a6e9aa9cf8199e6f5c32e7ab0a2904684b64bebde9e996f9bceb8e41
7
+ data.tar.gz: c47a038d7fac640a620a13373460bbd8df05a35b209264de1b0fedd727eab072925d0f2cfa82a800ddd2362756f887b0cd227d1e1cd01c98a0334b1c3cd55cd0
@@ -1,6 +1,18 @@
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
+
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
- AllCops:
17
- TargetRubyVersion: 2.3
31
+ Style/HashEachMethods:
32
+ Enabled: True
18
33
 
19
- Metrics/AbcSize:
20
- Max: 16
34
+ Style/HashTransformKeys:
35
+ Enabled: True
21
36
 
22
- Metrics/ClassLength:
23
- Max: 125
37
+ Style/HashTransformValues:
38
+ Enabled: True
@@ -1 +1 @@
1
- 2.6.5
1
+ 2.6.6
@@ -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.3.8
8
- - 2.4.6
9
- - 2.5.5
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
@@ -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
 
@@ -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.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'
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.3.8'
30
+ s.required_ruby_version = '>= 2.5'
23
31
 
24
- s.add_dependency('acts_as_hashable', '~>1', '>=1.1.0')
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.79.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.6.0')
41
+ s.add_development_dependency('simplecov-console', '~>0.7.0')
34
42
  end
File without changes
@@ -25,10 +25,8 @@ module Dbee
25
25
 
26
26
  attr_reader :fields, :filters, :limit, :sorters
27
27
 
28
- def_delegator :fields, :sort, :sorted_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: [])
@@ -15,24 +15,55 @@ module Dbee
15
15
  class Field
16
16
  acts_as_hashable
17
17
 
18
- attr_reader :key_path, :display
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(key_path:, display: nil)
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
- @key_path = KeyPath.get(key_path)
24
- @display = (display.to_s.empty? ? key_path : display).to_s
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
- "#{key_path}#{display}".hash
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? ==
@@ -8,5 +8,5 @@
8
8
  #
9
9
 
10
10
  module Dbee
11
- VERSION = '2.0.3'
11
+ VERSION = '2.1.0-alpha'
12
12
  end
@@ -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
- let(:config) { { key_path: 'a.b.c', display: 'd' } }
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
- subject { described_class.new(config) }
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(config)
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.3
4
+ version: 2.1.0.pre.alpha
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Ruggio
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
- date: 2020-01-07 00:00:00.000000000 Z
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.1.0
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.1.0
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.79.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.79.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.6.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.6.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.3.8
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: '0'
249
+ version: 1.3.1
245
250
  requirements: []
246
251
  rubygems_version: 3.0.3
247
252
  signing_key: