dbee 2.1.0.pre.alpha → 3.1.0

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: acc518f50b6b9e95165347240882ca415194e6b641e68ff292d5b2a6d3937527
4
- data.tar.gz: 954dbcdfac112f2098e7c1828d8c243369092526ee65179c9819ceaafddc70f7
3
+ metadata.gz: 234c40439102547cc3c8b78cbde5bc3755fcf49d8515c81eed7c7942f1c6a526
4
+ data.tar.gz: b9bb06a2e773786bb7e19b541f1dd6f104909baeea93148bbde78f9c5965ced8
5
5
  SHA512:
6
- metadata.gz: 5731c83ec9b0ded7f36c6b07d16597f97337240bd65bbec7dc82a9ca7f4de4da0cac9063a6e9aa9cf8199e6f5c32e7ab0a2904684b64bebde9e996f9bceb8e41
7
- data.tar.gz: c47a038d7fac640a620a13373460bbd8df05a35b209264de1b0fedd727eab072925d0f2cfa82a800ddd2362756f887b0cd227d1e1cd01c98a0334b1c3cd55cd0
6
+ metadata.gz: 3484318a258c8788fa7b08533cb3b27679fc52903e70f2914d4ab8ddd36921fcf628d3f934dbc0704fbf38f1d49c03c087a3c656121d0266083c3c98e41cde75
7
+ data.tar.gz: 53c8425f56c659f222d729c71855d4c1ecaaa3dd90d21ced647152d49c00bdc5ed2bc550bae7c307f098835a13d165c423685033df6ffee62e3867bfc32c0224
@@ -0,0 +1,71 @@
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
+ working_directory: ~/dbee
18
+ steps:
19
+ - checkout
20
+
21
+ # TODO: wrap bundler caching logic into an Orb:
22
+ - when:
23
+ condition: << parameters.use-bundler-cache >>
24
+ steps:
25
+ - restore_cache:
26
+ key: v1.0.0-build-ruby-dependency-cache-{{ checksum "dbee.gemspec" }}-{{ checksum "Gemfile" }}-{{ checksum ".ruby-version" }}
27
+
28
+ - run: bundle install --path vendor/bundle
29
+
30
+ - when:
31
+ condition: << parameters.use-bundler-cache >>
32
+ steps:
33
+ - save_cache:
34
+ key: v1.0.0-build-ruby-dependency-cache-{{ checksum "dbee.gemspec" }}-{{ checksum "Gemfile" }}-{{ checksum ".ruby-version" }}
35
+ paths:
36
+ - vendor/bundle
37
+
38
+ - store_artifacts:
39
+ path: Gemfile.lock
40
+
41
+ - run: bundle exec rubocop
42
+
43
+ - run: COVERAGE=true bundle exec rspec -r rspec_junit_formatter --format progress --format RspecJunitFormatter -o test-results/rspec/results.xml
44
+
45
+ - store_test_results:
46
+ path: test-results
47
+
48
+ - store_artifacts:
49
+ path: coverage
50
+
51
+ - status_to_ms_teams/report:
52
+ webhook_url: $MS_TEAMS_WEBHOOK_URL
53
+
54
+ workflows:
55
+ version: 2.1
56
+ build:
57
+ jobs:
58
+ - build:
59
+ context: org-global
60
+ monthly-gem-dependency-refresh-check:
61
+ triggers:
62
+ - schedule:
63
+ cron: '0 0 1 * *'
64
+ filters:
65
+ branches:
66
+ only:
67
+ - master
68
+ jobs:
69
+ - build:
70
+ context: org-global
71
+ use-bundler-cache: false
data/.gitignore CHANGED
@@ -4,3 +4,4 @@
4
4
  /coverage
5
5
  Gemfile.lock
6
6
  /pkg
7
+ .vscode
data/.rubocop.yml CHANGED
@@ -1,20 +1,15 @@
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: 16
9
+ Max: 20
15
10
 
16
11
  Metrics/BlockLength:
17
- ExcludedMethods:
12
+ IgnoredMethods:
18
13
  - let
19
14
  - it
20
15
  - describe
@@ -22,17 +17,11 @@ Metrics/BlockLength:
22
17
  - specify
23
18
  - define
24
19
 
20
+ Metrics/ParameterLists:
21
+ CountKeywordArgs: false
22
+
25
23
  Metrics/ClassLength:
26
24
  Max: 125
27
25
 
28
26
  Metrics/MethodLength:
29
27
  Max: 25
30
-
31
- Style/HashEachMethods:
32
- Enabled: True
33
-
34
- Style/HashTransformKeys:
35
- Enabled: True
36
-
37
- Style/HashTransformValues:
38
- Enabled: True
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 2.6.6
data/CHANGELOG.md CHANGED
@@ -1,4 +1,24 @@
1
- # 2.1.0 (TBD)
1
+ # 3.1.0 (October 4th, 2021)
2
+
3
+ Additions
4
+
5
+ * Added Query#offset to help with callers needing paging support.
6
+ # 3.0.0 (March 11th, 2021)
7
+
8
+ ### Additions
9
+
10
+ * Support for graph based models. This paves the way for representing more advanced features, such as sub-queries in a more clear way. Since graph based models are similar to DSL models, the hope is that they will be easier to work with and understand.
11
+
12
+ ### Breaking Changes
13
+
14
+ * The `to_model` method on `Dbee::Base` objects has been removed. Use `to_schema` instead.
15
+ * The `ancestors!` method on `Dbee::Model` has been removed. Use `Dbee::Schema#expand_query_path` instead.
16
+
17
+ # 2.1.1 (July 14th, 2020)
18
+
19
+ * Removed guard that ensured a query has at least one field to establish a more rational base-case.
20
+
21
+ # 2.1.0 (July 13th, 2020)
2
22
 
3
23
  ### Additions:
4
24
 
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Dbee
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/dbee.svg)](https://badge.fury.io/rb/dbee) [![Build Status](https://travis-ci.org/bluemarblepayroll/dbee.svg?branch=master)](https://travis-ci.org/bluemarblepayroll/dbee) [![Maintainability](https://api.codeclimate.com/v1/badges/208b36a1d13751687df9/maintainability)](https://codeclimate.com/github/bluemarblepayroll/dbee/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/208b36a1d13751687df9/test_coverage)](https://codeclimate.com/github/bluemarblepayroll/dbee/test_coverage) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
3
+ [![CircleCI](https://circleci.com/bb/bluemarble-ondemand/dbee/tree/master.svg?style=svg&circle-token=7f7afbea94912b2b75a889cae3e2c46a3a869d21)](https://circleci.com/bb/bluemarble-ondemand/dbee/tree/master)
4
4
 
5
5
  Dbee arose out of a need for an ad-hoc reporting solution that included:
6
6
 
@@ -20,12 +20,12 @@ Both of these solutions ended up closely coupling our domain data layer to ad-ho
20
20
 
21
21
  ## Installation
22
22
 
23
- This specific library is the core modeling component of the Dbee framework, but by itself, it not completely usable. You will need to provide a SQL generator which understands how to convert the data and query modeling to actual SQL. This library comes with a stub: Dbee::Providers::NullProvider, while the main reference implementation is split out into its own library: [dbee-active_record](https://github.com/bluemarblepayroll/dbee-active_record). Together these two libraries comprise a complete solution. Refer to the other library for more information on installation.
23
+ This specific library is the core modeling component of the Dbee framework, but by itself, it is not completely usable. You will need to provide a SQL generator which understands how to convert the data and query modeling to actual SQL. This library comes with a stub: Dbee::Providers::NullProvider, while the main reference implementation is split out into its own library: [dbee-active_record](https://github.com/bluemarblepayroll/dbee-active_record). Together these two libraries comprise a complete solution. Refer to the other library for more information on installation.
24
24
 
25
25
  To install through Rubygems:
26
26
 
27
27
  ````
28
- gem install install dbee
28
+ gem install dbee
29
29
  ````
30
30
 
31
31
  You can also add this to your Gemfile:
@@ -189,49 +189,58 @@ The two code-first examples above should be technically equivalent.
189
189
  You can choose to alternatively describe your data model using configuration. The YAML below is equivalent to the Ruby sub-classes above:
190
190
 
191
191
  ````yaml
192
- name: practice
193
- models:
194
- - name: patients
195
- constraints:
196
- - type: reference
197
- name: practice_id
198
- parent: id
199
- models:
200
- - name: notes
201
- constraints:
202
- - type: reference
203
- name: patient_id
204
- parent: id
205
- - name: work_phone_number
206
- table: phones
207
- constraints:
208
- - type: reference
209
- name: patient_id
210
- parent: id
211
- - type: static
212
- name: phone_number_type
213
- value: work
214
- - name: cell_phone_number
215
- table: phones
216
- constraints:
217
- - type: reference
218
- name: patient_id
219
- parent: id
220
- - type: static
221
- name: phone_number_type
222
- value: cell
223
- - name: fax_phone_number
224
- table: phones
225
- constraints:
226
- - type: reference
227
- name: patient_id
228
- parent: id
229
- - type: static
230
- name: phone_number_type
231
- value: fax
192
+ practice:
193
+ table: practices
194
+ relationships:
195
+ patients:
196
+ model: patient
197
+ constraints:
198
+ - type: reference
199
+ name: practice_id
200
+ parent: id
201
+ patient:
202
+ table: patients
203
+ relationships:
204
+ notes:
205
+ model: note
206
+ constraints:
207
+ - type: reference
208
+ name: patient_id
209
+ parent: id
210
+ work_phone_number:
211
+ model: phone_number
212
+ constraints:
213
+ - type: reference
214
+ name: patient_id
215
+ parent: id
216
+ - type: static
217
+ name: phone_number_type
218
+ value: work
219
+ cell_phone_number:
220
+ model: phone_number
221
+ constraints:
222
+ - type: reference
223
+ name: patient_id
224
+ parent: id
225
+ - type: static
226
+ name: phone_number_type
227
+ value: cell
228
+ fax_phone_number:
229
+ model: phone_number
230
+ constraints:
231
+ - type: reference
232
+ name: patient_id
233
+ parent: id
234
+ - type: static
235
+ name: phone_number_type
236
+ value: fax
237
+ note:
238
+ table: notes
239
+ phone_number:
240
+ table: phones
232
241
  ````
233
242
 
234
- It is up to you to determine which modeling technique to use as both are equivalent. Technically speaking, the code-first DSL is nothing more than syntactic sugar on top of Dbee::Model.
243
+ It is up to you to determine which modeling technique to use as both are equivalent. Technically speaking, the code-first DSL is nothing more than syntactic sugar on top of `Dbee::Schema` and `Dbee::Model`. Also note that prior to version three of this project, a more hierarchical tree based model configuration was used. See [Tree Based Model Backward Compatibility](#tree-based-model-backward-compatibility) below for more information on this.
235
244
 
236
245
  #### Table Partitioning
237
246
 
@@ -275,6 +284,7 @@ Cats:
275
284
  The Query API (Dbee::Query) is a simplified and abstract way to model an SQL query. A Query has the following components:
276
285
 
277
286
  * fields (SELECT)
287
+ * from (FROM)
278
288
  * filters (WHERE)
279
289
  * sorters (ORDER BY)
280
290
  * limit (LIMIT/TAKE)
@@ -291,6 +301,7 @@ Get all practices:
291
301
 
292
302
  ````ruby
293
303
  query = {
304
+ from: 'practice',
294
305
  fields: [
295
306
  { key_path: 'id' },
296
307
  { key_path: 'active' },
@@ -299,10 +310,11 @@ query = {
299
310
  }
300
311
  ````
301
312
 
302
- Get all practices, limit to 10, and sort by name (descending) then id (ascending):
313
+ Get all practices, limit to 2, offset by 3, and sort by name (descending) then id (ascending):
303
314
 
304
315
  ````ruby
305
316
  query = {
317
+ from: 'practice',
306
318
  fields: [
307
319
  { key_path: 'id' },
308
320
  { key_path: 'active' },
@@ -312,7 +324,8 @@ query = {
312
324
  { key_path: 'name', direction: :descending },
313
325
  { key_path: 'id' }
314
326
  ],
315
- limit: 10
327
+ limit: 2,
328
+ offset: 3
316
329
  }
317
330
  ````
318
331
 
@@ -320,6 +333,7 @@ Get top 5 active practices and patient whose name start with 'Sm':
320
333
 
321
334
  ````ruby
322
335
  query = {
336
+ from: 'practice',
323
337
  fields: [
324
338
  { key_path: 'name', display: 'Practice Name' },
325
339
  { key_path: 'patients.first', display: 'Patient First Name' },
@@ -338,6 +352,7 @@ Get practice IDs, patient IDs, names, and cell phone numbers that starts with '5
338
352
 
339
353
  ````ruby
340
354
  query = {
355
+ from: 'practice',
341
356
  fields: [
342
357
  { key_path: 'id', display: 'Practice ID #' },
343
358
  { key_path: 'patients.id', display: 'Patient ID #' },
@@ -363,6 +378,22 @@ You execute a Query against a Data Model, using a Provider. The sample provider
363
378
 
364
379
  Here are some sample executions based off the preceding examples:
365
380
 
381
+ ##### Base Case
382
+
383
+ If a query has no fields then it is implied you would like all fields on the root table. For example:
384
+
385
+ ````ruby
386
+ require 'dbee/providers/active_record_provider'
387
+
388
+ class Practice < Dbee::Base; end
389
+
390
+ provider = Dbee::Providers::ActiveRecordProvider.new
391
+ query = { from: 'practice' }
392
+ sql = Dbee.sql(Practice, query, provider)
393
+ ````
394
+
395
+ It equivalent to saying: `SELECT practices.* FROM practices`. This helps to establish a deterministic base-case: it returns the same implicit columns that is independent of sql joins (sorters and/or filters may require sql joins.)
396
+
366
397
  ##### Code-First Execution
367
398
 
368
399
  ````ruby
@@ -373,6 +404,7 @@ class Practice < Dbee::Base; end
373
404
  provider = Dbee::Providers::ActiveRecordProvider.new
374
405
 
375
406
  query = {
407
+ from: 'practice',
376
408
  fields: [
377
409
  { key_path: 'id' },
378
410
  { key_path: 'active' },
@@ -391,10 +423,11 @@ require 'dbee/providers/active_record_provider'
391
423
  provider = Dbee::Providers::ActiveRecordProvider.new
392
424
 
393
425
  model = {
394
- name: :practice
426
+ practice: { table: 'practices' }
395
427
  }
396
428
 
397
429
  query = {
430
+ from: 'practice',
398
431
  fields: [
399
432
  { key_path: 'id' },
400
433
  { key_path: 'active' },
@@ -414,19 +447,24 @@ Fields can be configured to use aggregation by setting its `aggregator` attribut
414
447
  **Data Model**:
415
448
 
416
449
  ````yaml
417
- name: practice
418
- models:
419
- - name: patients
420
- constraints:
421
- - type: reference
422
- name: practice_id
423
- parent: id
450
+ practice:
451
+ table: practices
452
+ relationships:
453
+ patients:
454
+ model: patient
455
+ constraints:
456
+ - type: reference
457
+ name: practice_id
458
+ parent: id
459
+ patient:
460
+ table: patients
424
461
  ````
425
462
 
426
463
  **Query**:
427
464
 
428
465
  ````ruby
429
466
  query = {
467
+ from: 'practice',
430
468
  fields: [
431
469
  {
432
470
  key_path: 'id',
@@ -476,19 +514,21 @@ id | patient_id | key | value
476
514
  **Model Configuration**:
477
515
 
478
516
  ````yaml
479
- name: patients
480
- models:
481
- - name: patient_fields
482
- constraints:
483
- - type: reference
484
- parent: id
485
- name: patient_id
517
+ patients:
518
+ relationships:
519
+ - patient_fields:
520
+ constraints:
521
+ - type: reference
522
+ parent: id
523
+ name: patient_id
524
+ patient_fields:
486
525
  ````
487
526
 
488
527
  **Query**:
489
528
 
490
529
  ````ruby
491
530
  query = {
531
+ from: 'patients',
492
532
  fields: [
493
533
  {
494
534
  key_path: 'id',
@@ -530,7 +570,59 @@ ID # | First Name | Date of Birth | Drivers License #
530
570
  -- | ---------- | ------------- | -----------------
531
571
  1 | frank | 1900-01-01 | ABC123
532
572
 
573
+ ## Tree Based Model Backward Compatibility
574
+
575
+ In version three of this gem, the representation of configuration based models was changed to be more of a graph structure than the previous tree structure. For backwards compatibility, it is still possible to pass this older tree based structure as the first argument `Dbee.sql`. The practices example would be represented this way in the old structure:
576
+
577
+ ````yaml
578
+ # Deprecated tree based model configuration:
579
+ name: practice
580
+ table: practices
581
+ models:
582
+ - name: patients
583
+ constraints:
584
+ - type: reference
585
+ name: practice_id
586
+ parent: id
587
+ models:
588
+ - name: notes
589
+ constraints:
590
+ - type: reference
591
+ name: patient_id
592
+ parent: id
593
+ - name: work_phone_number
594
+ table: phones
595
+ constraints:
596
+ - type: reference
597
+ name: patient_id
598
+ parent: id
599
+ - type: static
600
+ name: phone_number_type
601
+ value: work
602
+ - name: cell_phone_number
603
+ table: phones
604
+ constraints:
605
+ - type: reference
606
+ name: patient_id
607
+ parent: id
608
+ - type: static
609
+ name: phone_number_type
610
+ value: cell
611
+ - name: fax_phone_number
612
+ table: phones
613
+ constraints:
614
+ - type: reference
615
+ name: patient_id
616
+ parent: id
617
+ - type: static
618
+ name: phone_number_type
619
+ value: fax
620
+ ````
533
621
 
622
+ Also note to further maintain backwards compatibility, queries issued against
623
+ tree based models do not need the "from" attribute to be defined. This is
624
+ because the from/starting point of the query can be inferred as the model at
625
+ the root of the tree.
534
626
  ## Contributing
535
627
 
536
628
  ### Development Environment Configuration
@@ -570,7 +662,7 @@ Note: ensure you have proper authorization before trying to publish new versions
570
662
  After code changes have successfully gone through the Pull Request review process then the following steps should be followed for publishing new versions:
571
663
 
572
664
  1. Merge Pull Request into master
573
- 2. Update `lib/dbee/version.rb` using [semantic versioning](https://semver.org/)
665
+ 2. Update `version.rb` using [semantic versioning](https://semver.org/)
574
666
  3. Install dependencies: `bundle`
575
667
  4. Update `CHANGELOG.md` with release notes
576
668
  5. Commit & push master to remote and ensure CI builds master successfully
data/dbee.gemspec CHANGED
@@ -17,15 +17,7 @@ Gem::Specification.new do |s|
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'
21
20
  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
- }
29
21
 
30
22
  s.required_ruby_version = '>= 2.5'
31
23
 
@@ -34,9 +26,13 @@ Gem::Specification.new do |s|
34
26
 
35
27
  s.add_development_dependency('guard-rspec', '~>4.7')
36
28
  s.add_development_dependency('pry', '~>0')
29
+ s.add_development_dependency('pry-byebug')
37
30
  s.add_development_dependency('rake', '~> 13')
38
31
  s.add_development_dependency('rspec')
39
- s.add_development_dependency('rubocop', '~>0.81.0')
40
- s.add_development_dependency('simplecov', '~>0.17.0')
32
+ s.add_development_dependency('rspec_junit_formatter')
33
+ s.add_development_dependency('rubocop', '~> 1')
34
+ s.add_development_dependency('rubocop-rake')
35
+ s.add_development_dependency('rubocop-rspec')
36
+ s.add_development_dependency('simplecov', '~>0.19.0')
41
37
  s.add_development_dependency('simplecov-console', '~>0.7.0')
42
38
  end
data/lib/dbee/base.rb CHANGED
@@ -7,8 +7,8 @@
7
7
  # LICENSE file in the root directory of this source tree.
8
8
  #
9
9
 
10
- require_relative 'dsl/association'
11
10
  require_relative 'dsl/association_builder'
11
+ require_relative 'dsl/association'
12
12
  require_relative 'dsl/methods'
13
13
  require_relative 'dsl/reflectable'
14
14
 
@@ -22,23 +22,9 @@ module Dbee
22
22
  BASE_CLASS_CONSTANT = Dbee::Base
23
23
 
24
24
  class << self
25
- # This method is cycle-resistant due to the fact that it is a requirement to send in a
26
- # key_chain. That means each model produced using to_model is specific to a set of desired
27
- # fields. Basically, you cannot derive a Model from a Base subclass without the context
28
- # of a Query. This is not true for configuration-first Model definitions because, in that
29
- # case, cycles do not exist since the nature of the configuration is flat.
30
- def to_model(key_chain, name = nil, constraints = [], path_parts = [])
31
- derived_name = name.to_s.empty? ? inflected_class_name(self.name) : name.to_s
32
- key = [key_chain, derived_name, constraints, path_parts]
33
-
34
- to_models[key] ||= Model.make(
35
- model_config(
36
- key_chain,
37
- derived_name,
38
- constraints,
39
- path_parts + [name]
40
- )
41
- )
25
+ # Returns the smallest needed Dbee::Schema for the provided key_chain.
26
+ def to_schema(key_chain)
27
+ DslSchemaBuilder.new(self, key_chain).to_schema
42
28
  end
43
29
 
44
30
  def inherited_table_name
@@ -58,43 +44,15 @@ module Dbee
58
44
  end
59
45
  end
60
46
 
61
- private
62
-
63
- def model_config(key_chain, name, constraints, path_parts)
64
- {
65
- constraints: constraints,
66
- models: associations(key_chain, path_parts),
67
- name: name,
68
- partitioners: inherited_partitioners,
69
- table: inherited_table_name
70
- }
71
- end
72
-
73
- def associations(key_chain, path_parts)
74
- inherited_associations.select { |c| key_chain.ancestor_path?(path_parts, c.name) }
75
- .map do |association|
76
- model_constant = association.model_constant
77
-
78
- model_constant.to_model(
79
- key_chain,
80
- association.name,
81
- association.constraints,
82
- path_parts
83
- )
84
- end
47
+ def inflected_class_name
48
+ inflector.underscore(inflector.demodulize(name))
85
49
  end
86
50
 
87
- def to_models
88
- @to_models ||= {}
89
- end
51
+ private
90
52
 
91
53
  def inflected_table_name(name)
92
54
  inflector.pluralize(inflector.underscore(inflector.demodulize(name)))
93
55
  end
94
-
95
- def inflected_class_name(name)
96
- inflector.underscore(inflector.demodulize(name))
97
- end
98
56
  end
99
57
  end
100
58
  end