activerecord-dbt 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +921 -0
  4. data/Rakefile +5 -0
  5. data/lib/active_record/dbt/column/column.rb +84 -0
  6. data/lib/active_record/dbt/column/test.rb +61 -0
  7. data/lib/active_record/dbt/column/testable/accepted_values_testable.rb +59 -0
  8. data/lib/active_record/dbt/column/testable/not_null_testable.rb +23 -0
  9. data/lib/active_record/dbt/column/testable/relationships_testable.rb +49 -0
  10. data/lib/active_record/dbt/column/testable/unique_testable.rb +37 -0
  11. data/lib/active_record/dbt/config.rb +45 -0
  12. data/lib/active_record/dbt/configuration/data_sync.rb +15 -0
  13. data/lib/active_record/dbt/configuration/logger.rb +41 -0
  14. data/lib/active_record/dbt/configuration/parser.rb +15 -0
  15. data/lib/active_record/dbt/configuration/used_dbt_package.rb +25 -0
  16. data/lib/active_record/dbt/dbt_package/dbt_utils/table/testable/unique_combination_of_columns_testable.rb +42 -0
  17. data/lib/active_record/dbt/dbt_package/dbterd/column/testable/relationships_meta_relationship_type.rb +138 -0
  18. data/lib/active_record/dbt/factory/columns_factory.rb +31 -0
  19. data/lib/active_record/dbt/factory/model/staging_factory.rb +22 -0
  20. data/lib/active_record/dbt/factory/source_factory.rb +16 -0
  21. data/lib/active_record/dbt/factory/table_factory.rb +16 -0
  22. data/lib/active_record/dbt/factory/tables_factory.rb +15 -0
  23. data/lib/active_record/dbt/model/staging/base.rb +73 -0
  24. data/lib/active_record/dbt/model/staging/sql.rb +43 -0
  25. data/lib/active_record/dbt/model/staging/yml.rb +108 -0
  26. data/lib/active_record/dbt/railtie.rb +8 -0
  27. data/lib/active_record/dbt/source/yml.rb +37 -0
  28. data/lib/active_record/dbt/table/base.rb +16 -0
  29. data/lib/active_record/dbt/table/test.rb +19 -0
  30. data/lib/active_record/dbt/table/yml.rb +75 -0
  31. data/lib/active_record/dbt/version.rb +7 -0
  32. data/lib/active_record/dbt.rb +17 -0
  33. data/lib/generators/active_record/dbt/config/USAGE +9 -0
  34. data/lib/generators/active_record/dbt/config/config_generator.rb +30 -0
  35. data/lib/generators/active_record/dbt/config/templates/source_config.yml.tt +68 -0
  36. data/lib/generators/active_record/dbt/initializer/USAGE +8 -0
  37. data/lib/generators/active_record/dbt/initializer/initializer_generator.rb +15 -0
  38. data/lib/generators/active_record/dbt/initializer/templates/dbt.rb +10 -0
  39. data/lib/generators/active_record/dbt/source/USAGE +8 -0
  40. data/lib/generators/active_record/dbt/source/source_generator.rb +22 -0
  41. data/lib/generators/active_record/dbt/staging_model/USAGE +9 -0
  42. data/lib/generators/active_record/dbt/staging_model/staging_model_generator.rb +38 -0
  43. data/lib/generators/active_record/dbt/staging_model/templates/staging_model.sql.tt +29 -0
  44. data/lib/tasks/active_record/dbt_tasks.rake +6 -0
  45. metadata +133 -0
data/README.md ADDED
@@ -0,0 +1,921 @@
1
+ # ActiveRecord::Dbt
2
+
3
+ `ActiveRecord::Dbt` generates dbt files from the information of the database connected via ActiveRecord.
4
+
5
+ Currently, it can generate `yaml` files for `sources` and `models` files for `staging`.
6
+
7
+ ## Installation
8
+
9
+ To install `activerecord-dbt`, add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'activerecord-dbt'
13
+ ```
14
+
15
+ Since it is only used in the development environment, it is recommended to add it to the development group:
16
+
17
+ ```ruby
18
+ group :development do
19
+ gem 'activerecord-dbt'
20
+ end
21
+ ```
22
+
23
+ Then run:
24
+
25
+ ```bash
26
+ $ bundle
27
+ ```
28
+
29
+ Alternatively, you can install it manually by running:
30
+
31
+ ```bash
32
+ $ gem install activerecord-dbt
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ ### Configuration
38
+
39
+ #### ActiveRecord::Dbt Configuration
40
+
41
+ Create an initializer file for dbt:
42
+
43
+ ```bash
44
+ $ bin/rails generate active_record:dbt:initializer
45
+ ```
46
+
47
+ This will generate the `config/initializers/dbt.rb` file.
48
+
49
+ Configuration | Description
50
+ --------- | ---------
51
+ config_directory_path | The path to the directory where files generated by `bin/rails generate active_record:dbt:*` are stored. The default is `lib/dbt`.
52
+ export_directory_path | The path to the directory where configuration files are stored. The default is `doc/dbt`.
53
+ data_sync_delayed | Indicates whether there is a data delay. If set to `true`, `severity: warn` is applied to the `relationships` test. The default is `false`.
54
+ logger | The destination for log output. The default is `Logger.new('./log/active_record_dbt.log')`.
55
+ used_dbt_package_names | An array of `dbt` package names to use.
56
+
57
+ Example:
58
+
59
+ Adjust the settings according to your environment.
60
+
61
+ ```ruby
62
+ # frozen_string_literal: true
63
+
64
+ require 'active_record/dbt'
65
+
66
+ ActiveRecord::Dbt.configure do |c|
67
+ c.config_directory_path = 'lib/dbt'
68
+ c.export_directory_path = 'doc/dbt'
69
+ c.data_sync_delayed = false
70
+ c.used_dbt_package_names = [
71
+ 'dbt-labs/dbt_utils',
72
+ 'datnguye/dbterd'
73
+ ]
74
+ end
75
+
76
+ ```
77
+
78
+ #### Create Configuration Files
79
+
80
+ Create configuration files for dbt:
81
+
82
+ ```bash
83
+ $ bin/rails generate active_record:dbt:config
84
+ ```
85
+
86
+ This will create the following files.
87
+
88
+ File | Description
89
+ --------- | ---------
90
+ `#{config_directory_path}/source_config.yml` | Used to generate `#{export_directory_path}/src_#{source_name}.yml`.
91
+ `#{config_directory_path}/staging_model.sql.tt` | Used to generate `#{export_directory_path}/stg_#{source_name}__#{table_name}.sql`.
92
+
93
+ ### Generate dbt Source File
94
+
95
+ #### dbt Source Configuration
96
+
97
+ In the `#{config_directory_path}/source_config.yml` file, describe the properties you want to set for the source.
98
+ You can configure `sources`, `table_overrides`, `defaults`, and `table_descriptions` in this file.
99
+
100
+ The available properties for `sources` and `table_overrides` are detailed in [Source properties | dbt Developer Hub](https://docs.getdbt.com/reference/source-properties).
101
+
102
+ ##### sources
103
+
104
+ Set all properties except for `tables`.
105
+
106
+ Example:
107
+
108
+ ```yml
109
+ sources:
110
+ name: dummy
111
+ meta:
112
+ generated_by: activerecord-dbt
113
+ description: |-
114
+ Write a description of the 'dummy' source.
115
+ You can write multiple lines.
116
+
117
+ ```
118
+
119
+ ##### table_overrides
120
+
121
+ Set all properties for `tables` except for `name` and `description`.
122
+
123
+ Example:
124
+
125
+ ```yml
126
+ table_overrides:
127
+ users:
128
+ loaded_at_field: created_at
129
+ freshness:
130
+ warn_after:
131
+ count: 3
132
+ period: day
133
+ error_after:
134
+ count: 5
135
+ period: day
136
+ columns:
137
+ created_at:
138
+ tests:
139
+ - not_null:
140
+ where: 'id != 1'
141
+
142
+ ```
143
+
144
+ ##### defaults
145
+
146
+ Set default values for the `name` and `description` of `tables`.
147
+
148
+ In `logical_name` and `description` of `table_descriptions`, you can refer to the table name with `{{ table_name }}`.
149
+ In the `description` of `columns`, you can refer to the table name with `{{ table_name }}` and the column name with `{{ column_name }}`.
150
+
151
+ Example:
152
+
153
+ ```yml
154
+ defaults:
155
+ table_descriptions:
156
+ logical_name: Write a logical_name of the '{{ table_name }}' table.
157
+ columns:
158
+ description: Write a description of the '{{ table_name }}.{{ column_name }}' column.
159
+
160
+ ```
161
+
162
+ If nothing is set, it defaults to the following:
163
+
164
+ ```yml
165
+ defaults:
166
+ table_descriptions:
167
+ logical_name: Write a logical_name of the '{{ table_name }}' table.
168
+ columns:
169
+ description: Write a description of the '{{ table_name }}.{{ column_name }}' column.
170
+
171
+ ```
172
+
173
+ ##### table_descriptions
174
+
175
+ Set the `name` and `description` for `tables`.
176
+
177
+ Configuration | Description
178
+ --------- | ---------
179
+ logical_name | A title or one-line description to be output in the dbt `description`.
180
+ description | A detailed description of `logical_name` to be output in the dbt `description`.
181
+
182
+ Example:
183
+
184
+ ```yml
185
+ table_descriptions:
186
+ ar_internal_metadata:
187
+ logical_name: Internal Metadata
188
+ description: |-
189
+ By default Rails will store information about your Rails environment and schema
190
+ in an internal table named `ar_internal_metadata`.
191
+ columns:
192
+ key: Key
193
+ value: Value
194
+ created_at: Created At
195
+ updated_at: Updated At
196
+ schema_migrations:
197
+ logical_name: Schema Migrations
198
+ description: |-
199
+ Rails keeps track of which migrations have been committed to the database and
200
+ stores them in a neighboring table in that same database called `schema_migrations`.
201
+ columns:
202
+ version: The version number of the migration.
203
+
204
+ ```
205
+
206
+ ##### Example:
207
+
208
+ Adjust the settings according to your environment.
209
+
210
+ ```yml
211
+ sources:
212
+ name: dummy
213
+ meta:
214
+ generated_by: activerecord-dbt
215
+ description: |-
216
+ Write a description of the 'dummy' source.
217
+ You can write multiple lines.
218
+
219
+ table_overrides:
220
+ users:
221
+ loaded_at_field: created_at
222
+ freshness:
223
+ warn_after:
224
+ count: 3
225
+ period: day
226
+ error_after:
227
+ count: 5
228
+ period: day
229
+ columns:
230
+ created_at:
231
+ tests:
232
+ - not_null:
233
+ where: 'id != 1'
234
+
235
+ defaults:
236
+ table_descriptions:
237
+ logical_name: Write a logical_name of the '{{ table_name }}' table.
238
+ columns:
239
+ description: Write a description of the '{{ table_name }}.{{ column_name }}' column.
240
+
241
+ table_descriptions:
242
+ ar_internal_metadata:
243
+ logical_name: Internal Metadata
244
+ description: |-
245
+ By default Rails will store information about your Rails environment and schema
246
+ in an internal table named `ar_internal_metadata`.
247
+ columns:
248
+ key: Key
249
+ value: Value
250
+ created_at: Created At
251
+ updated_at: Updated At
252
+ schema_migrations:
253
+ logical_name: Schema Migrations
254
+ description: |-
255
+ Rails keeps track of which migrations have been committed to the database and
256
+ stores them in a neighboring table in that same database called `schema_migrations`.
257
+ columns:
258
+ version: The version number of the migration.
259
+
260
+ ```
261
+
262
+ #### Generate `#{export_directory_path}/src_#{source_name}.yml`
263
+
264
+ Generate a source file for dbt:
265
+
266
+ ```bash
267
+ $ bin/rails generate active_record:dbt:source
268
+ ```
269
+
270
+ Generate `#{export_directory_path}/src_#{source_name}.yml`.
271
+
272
+ ##### Example:
273
+
274
+ > [!NOTE]
275
+ >
276
+ > The output will be as shown below. It is recommended to indent the YAML file with a tool of your choice.
277
+
278
+ ```yaml
279
+ ---
280
+ version: 2
281
+ sources:
282
+ - name: dummy
283
+ meta:
284
+ generated_by: activerecord-dbt
285
+ description: |-
286
+ Write a description of the 'dummy' source.
287
+ You can write multiple lines.
288
+ tables:
289
+ - name: ar_internal_metadata
290
+ description: |-
291
+ # Internal Metadata
292
+ By default Rails will store information about your Rails environment and schema
293
+ in an internal table named `ar_internal_metadata`.
294
+ columns:
295
+ - name: key
296
+ description: Key
297
+ meta:
298
+ column_type: string
299
+ tests:
300
+ - unique
301
+ - not_null
302
+ - name: value
303
+ description: Value
304
+ meta:
305
+ column_type: string
306
+ - name: created_at
307
+ description: Created At
308
+ meta:
309
+ column_type: datetime
310
+ tests:
311
+ - not_null
312
+ - name: updated_at
313
+ description: Updated At
314
+ meta:
315
+ column_type: datetime
316
+ tests:
317
+ - not_null
318
+ - name: companies
319
+ description: Write a logical_name of the 'companies' table.
320
+ columns:
321
+ - name: id
322
+ description: id
323
+ meta:
324
+ column_type: integer
325
+ tests:
326
+ - unique
327
+ - not_null
328
+ - name: name
329
+ description: Write a description of the 'companies.name' column.
330
+ meta:
331
+ column_type: string
332
+ tests:
333
+ - not_null
334
+ - name: establishment_date
335
+ description: Write a description of the 'companies.establishment_date' column.
336
+ meta:
337
+ column_type: string
338
+ - name: average_age
339
+ description: Write a description of the 'companies.average_age' column.
340
+ meta:
341
+ column_type: float
342
+ - name: published
343
+ description: Write a description of the 'companies.published' column.
344
+ meta:
345
+ column_type: boolean
346
+ tests:
347
+ - not_null
348
+ - accepted_values:
349
+ values:
350
+ - true
351
+ - false
352
+ quote: false
353
+ - name: created_at
354
+ description: Created At
355
+ meta:
356
+ column_type: datetime
357
+ tests:
358
+ - not_null
359
+ - name: updated_at
360
+ description: Updated At
361
+ meta:
362
+ column_type: datetime
363
+ tests:
364
+ - not_null
365
+ - name: posts
366
+ description: Post
367
+ columns:
368
+ - name: id
369
+ description: ID
370
+ meta:
371
+ column_type: integer
372
+ tests:
373
+ - unique
374
+ - not_null
375
+ - name: user_id
376
+ description: User
377
+ meta:
378
+ column_type: integer
379
+ tests:
380
+ - not_null
381
+ - relationships:
382
+ to: source('dummy', 'users')
383
+ field: id
384
+ meta:
385
+ relationship_type: many-to-one
386
+ - name: title
387
+ description: Title
388
+ meta:
389
+ column_type: string
390
+ - name: content
391
+ description: Content
392
+ meta:
393
+ column_type: text
394
+ - name: created_at
395
+ description: Post Created At
396
+ meta:
397
+ column_type: datetime
398
+ tests:
399
+ - not_null
400
+ - name: updated_at
401
+ description: Post Updated At
402
+ meta:
403
+ column_type: datetime
404
+ tests:
405
+ - not_null
406
+ - name: status
407
+ description: Write a description of the 'posts.status' column.
408
+ meta:
409
+ column_type: integer
410
+ tests:
411
+ - accepted_values:
412
+ values:
413
+ - 0
414
+ - 1
415
+ - 2
416
+ quote: false
417
+ - name: posts_tags
418
+ description: Write a logical_name of the 'posts_tags' table.
419
+ tests:
420
+ - dbt_utils.unique_combination_of_columns:
421
+ combination_of_columns:
422
+ - post_id
423
+ - tag_id
424
+ columns:
425
+ - name: post_id
426
+ description: post_id
427
+ meta:
428
+ column_type: integer
429
+ tests:
430
+ - not_null
431
+ - relationships:
432
+ to: source('dummy', 'posts')
433
+ field: id
434
+ meta:
435
+ relationship_type: many-to-one
436
+ active_record_dbt_error:
437
+ class: NameError
438
+ message: uninitialized constant PostsTag
439
+ - name: tag_id
440
+ description: tag_id
441
+ meta:
442
+ column_type: integer
443
+ tests:
444
+ - not_null
445
+ - relationships:
446
+ to: source('dummy', 'tags')
447
+ field: id
448
+ meta:
449
+ relationship_type: many-to-one
450
+ active_record_dbt_error:
451
+ class: NameError
452
+ message: uninitialized constant PostsTag
453
+ - name: profiles
454
+ description: Write a logical_name of the 'profiles' table.
455
+ columns:
456
+ - name: id
457
+ description: id
458
+ meta:
459
+ column_type: integer
460
+ tests:
461
+ - unique
462
+ - not_null
463
+ - name: user_id
464
+ description: user_id
465
+ meta:
466
+ column_type: integer
467
+ tests:
468
+ - unique
469
+ - not_null
470
+ - relationships:
471
+ to: source('dummy', 'users')
472
+ field: id
473
+ meta:
474
+ relationship_type: one-to-one
475
+ - name: first_name
476
+ description: Write a description of the 'profiles.first_name' column.
477
+ meta:
478
+ column_type: string
479
+ tests:
480
+ - not_null
481
+ - name: last_name
482
+ description: Write a description of the 'profiles.last_name' column.
483
+ meta:
484
+ column_type: string
485
+ tests:
486
+ - not_null
487
+ - name: created_at
488
+ description: Created At
489
+ meta:
490
+ column_type: datetime
491
+ tests:
492
+ - not_null
493
+ - name: updated_at
494
+ description: Updated At
495
+ meta:
496
+ column_type: datetime
497
+ tests:
498
+ - not_null
499
+ - name: relationships
500
+ description: Write a logical_name of the 'relationships' table.
501
+ tests:
502
+ - dbt_utils.unique_combination_of_columns:
503
+ combination_of_columns:
504
+ - follower_id
505
+ - followed_id
506
+ columns:
507
+ - name: id
508
+ description: id
509
+ meta:
510
+ column_type: integer
511
+ tests:
512
+ - unique
513
+ - not_null
514
+ - name: follower_id
515
+ description: follower_id
516
+ meta:
517
+ column_type: integer
518
+ tests:
519
+ - not_null
520
+ - relationships:
521
+ to: source('dummy', 'users')
522
+ field: id
523
+ meta:
524
+ relationship_type: many-to-one
525
+ - name: followed_id
526
+ description: followed_id
527
+ meta:
528
+ column_type: integer
529
+ tests:
530
+ - not_null
531
+ - relationships:
532
+ to: source('dummy', 'users')
533
+ field: id
534
+ meta:
535
+ relationship_type: many-to-one
536
+ - name: created_at
537
+ description: Created At
538
+ meta:
539
+ column_type: datetime
540
+ tests:
541
+ - not_null
542
+ - name: updated_at
543
+ description: Updated At
544
+ meta:
545
+ column_type: datetime
546
+ tests:
547
+ - not_null
548
+ - name: schema_migrations
549
+ description: |-
550
+ # Schema Migrations
551
+ Rails keeps track of which migrations have been committed to the database and
552
+ stores them in a neighboring table in that same database called `schema_migrations`.
553
+ columns:
554
+ - name: version
555
+ description: The version number of the migration.
556
+ meta:
557
+ column_type: string
558
+ tests:
559
+ - unique
560
+ - not_null
561
+ - name: tags
562
+ description: Write a logical_name of the 'tags' table.
563
+ columns:
564
+ - name: id
565
+ description: id
566
+ meta:
567
+ column_type: integer
568
+ tests:
569
+ - unique
570
+ - not_null
571
+ - name: name
572
+ description: Write a description of the 'tags.name' column.
573
+ meta:
574
+ column_type: string
575
+ tests:
576
+ - unique
577
+ - not_null
578
+ - name: created_at
579
+ description: Created At
580
+ meta:
581
+ column_type: datetime
582
+ tests:
583
+ - not_null
584
+ - name: updated_at
585
+ description: Updated At
586
+ meta:
587
+ column_type: datetime
588
+ tests:
589
+ - not_null
590
+ - name: user_tags
591
+ description: Write a logical_name of the 'user_tags' table.
592
+ tests:
593
+ - dbt_utils.unique_combination_of_columns:
594
+ combination_of_columns:
595
+ - user_id
596
+ - tag_id
597
+ columns:
598
+ - name: id
599
+ description: id
600
+ meta:
601
+ column_type: integer
602
+ tests:
603
+ - unique
604
+ - not_null
605
+ - name: user_id
606
+ description: user_id
607
+ meta:
608
+ column_type: integer
609
+ tests:
610
+ - not_null
611
+ - relationships:
612
+ to: source('dummy', 'users')
613
+ field: id
614
+ meta:
615
+ relationship_type: many-to-one
616
+ - name: tag_id
617
+ description: tag_id
618
+ meta:
619
+ column_type: integer
620
+ tests:
621
+ - not_null
622
+ - relationships:
623
+ to: source('dummy', 'tags')
624
+ field: id
625
+ meta:
626
+ relationship_type: many-to-one
627
+ - name: created_at
628
+ description: Created At
629
+ meta:
630
+ column_type: datetime
631
+ tests:
632
+ - not_null
633
+ - name: updated_at
634
+ description: Updated At
635
+ meta:
636
+ column_type: datetime
637
+ tests:
638
+ - not_null
639
+ - name: users
640
+ description: User
641
+ loaded_at_field: created_at
642
+ freshness:
643
+ warn_after:
644
+ count: 3
645
+ period: day
646
+ error_after:
647
+ count: 5
648
+ period: day
649
+ columns:
650
+ - name: id
651
+ description: ID
652
+ meta:
653
+ column_type: integer
654
+ tests:
655
+ - unique
656
+ - not_null
657
+ - name: created_at
658
+ description: User Created At
659
+ meta:
660
+ column_type: datetime
661
+ tests:
662
+ - not_null:
663
+ where: id != 1
664
+ - name: updated_at
665
+ description: User Updated At
666
+ meta:
667
+ column_type: datetime
668
+ tests:
669
+ - not_null
670
+ - name: company_id
671
+ description: company_id
672
+ meta:
673
+ column_type: integer
674
+ tests:
675
+ - relationships:
676
+ to: source('dummy', 'companies')
677
+ field: id
678
+ meta:
679
+ relationship_type: many-to-one
680
+
681
+ ```
682
+
683
+ ### Generate dbt Staging Files
684
+
685
+ #### dbt Staging Configuration
686
+
687
+ In the `#{config_directory_path}/staging_model.sql.tt` file, write the SQL template for the `staging` model you want to create.
688
+ You can use `sql.source_name`, `sql.table_name`, `sql.select_column_names`, `sql.primary_key_eql_id?`, and `sql.rename_primary_id` within this file.
689
+
690
+ Example:
691
+
692
+ ```sql
693
+ with
694
+
695
+ source as (
696
+
697
+ select * from {{ source('<%= sql.source_name %>', '<%= sql.table_name %>') }}
698
+
699
+ ),
700
+
701
+ renamed as (
702
+
703
+ select
704
+
705
+ <%- sql.select_column_names.each_with_index do |(column_type, columns), column_type_index| -%>
706
+ -- <%= column_type %>
707
+ <%- columns.each_with_index do |column, column_index| -%>
708
+ <%- is_rename_primary_id = sql.primary_key_eql_id? && sql.primary_key?(column.name) -%>
709
+ <%- is_last_column = column_type_index == sql.select_column_names.size - 1 && column_index == columns.size - 1 -%>
710
+ <%= is_rename_primary_id ? "id as #{sql.rename_primary_id}" : column.name %><% unless is_last_column -%>,<%- end %>
711
+ <%- if column_type_index != sql.select_column_names.size - 1 && column_index == columns.size - 1 -%>
712
+
713
+ <%- end -%>
714
+ <%- end -%>
715
+ <%- end -%>
716
+
717
+ from source
718
+
719
+ )
720
+
721
+ select * from renamed
722
+
723
+ ```
724
+
725
+ Different Pattern:
726
+
727
+ ```sql
728
+ #standardSQL
729
+
730
+ with source as (
731
+ select
732
+ <%- if sql.primary_key_eql_id? -%>
733
+ id as <%= sql.rename_primary_id %>
734
+ , * except(id)
735
+ <%- else -%>
736
+ *
737
+ <%- end -%>
738
+ from {{ source('<%= sql.source_name %>', '<%= sql.table_name %>') }}
739
+ )
740
+
741
+ , final as (
742
+ select
743
+ <%- sql.select_column_names.each_with_index do |(column_type, columns), column_type_index| -%>
744
+ -- <%= column_type %>
745
+ <%- columns.each_with_index do |column, column_index| -%>
746
+ <% unless column_type_index == 0 && column_index == 0 -%>, <%- end %><%= (sql.primary_key_eql_id? && sql.primary_key?(column.name) ? sql.rename_primary_id : column.name) %>
747
+ <%- if column_type_index != sql.select_column_names.size - 1 && column_index == columns.size - 1 -%>
748
+
749
+ <%- end -%>
750
+ <%- end -%>
751
+ <%- end -%>
752
+ from source
753
+ )
754
+
755
+ select
756
+ *
757
+ from final
758
+
759
+ ```
760
+
761
+ #### Generate dbt Staging Files
762
+
763
+ Generate staging model files for dbt:
764
+
765
+ ```bash
766
+ $ bin/rails generate active_record:dbt:staging_model TABLE_NAME
767
+ ```
768
+
769
+ Generate staging model files for dbt that reference the specified `TABLE_NAME`.
770
+
771
+ File | Description
772
+ --------- | ---------
773
+ `#{export_directory_path}/stg_#{source_name}__#{table_name}.sql` | Staging model file for dbt.
774
+ `#{export_directory_path}/stg_#{source_name}__#{table_name}.yml` | Staging model documentation file for dbt.
775
+
776
+ Example:
777
+
778
+ ```bash
779
+ $ bin/rails generate active_record:dbt:staging_model profiles
780
+ ```
781
+
782
+ ##### Generate `#{export_directory_path}/stg_#{source_name}__#{table_name}.sql`
783
+
784
+ Example:
785
+
786
+ ```sql
787
+ with
788
+
789
+ source as (
790
+
791
+ select * from {{ source('dummy', 'profiles') }}
792
+
793
+ ),
794
+
795
+ renamed as (
796
+
797
+ select
798
+
799
+ -- ids
800
+ id as profile_id,
801
+ user_id,
802
+
803
+ -- strings
804
+ first_name,
805
+ last_name,
806
+
807
+ -- datetimes
808
+ created_at,
809
+ updated_at
810
+
811
+ from source
812
+
813
+ )
814
+
815
+ select * from renamed
816
+
817
+ ```
818
+
819
+ Different Pattern:
820
+
821
+ ```sql
822
+ #standardSQL
823
+
824
+ with source as (
825
+ select
826
+ id as profile_id
827
+ , * except(id)
828
+ from {{ source('dummy', 'profiles') }}
829
+ )
830
+
831
+ , final as (
832
+ select
833
+ -- ids
834
+ profile_id
835
+ , user_id
836
+
837
+ -- strings
838
+ , first_name
839
+ , last_name
840
+
841
+ -- datetimes
842
+ , created_at
843
+ , updated_at
844
+ from source
845
+ )
846
+
847
+ select
848
+ *
849
+ from final
850
+
851
+ ```
852
+
853
+ ##### Generate `#{export_directory_path}/stg_#{source_name}__#{table_name}.yml`
854
+
855
+ Example:
856
+
857
+ ```yaml
858
+ ---
859
+ version: 2
860
+ models:
861
+ - name: stg_dummy__profiles
862
+ description: Write a logical_name of the 'profiles' table.
863
+ columns:
864
+ - name: profile_id
865
+ description: profile_id
866
+ meta:
867
+ column_type: integer
868
+ tests:
869
+ - unique
870
+ - not_null
871
+ - relationships:
872
+ to: source('dummy', 'profiles')
873
+ field: id
874
+ meta:
875
+ relationship_type: one-to-one
876
+ - name: user_id
877
+ description: user_id
878
+ meta:
879
+ column_type: integer
880
+ tests:
881
+ - unique
882
+ - not_null
883
+ - relationships:
884
+ to: source('dummy', 'users')
885
+ field: id
886
+ meta:
887
+ relationship_type: one-to-one
888
+ - name: first_name
889
+ description: Write a description of the 'profiles.first_name' column.
890
+ meta:
891
+ column_type: string
892
+ tests:
893
+ - not_null
894
+ - name: last_name
895
+ description: Write a description of the 'profiles.last_name' column.
896
+ meta:
897
+ column_type: string
898
+ tests:
899
+ - not_null
900
+ - name: created_at
901
+ description: Created At
902
+ meta:
903
+ column_type: datetime
904
+ tests:
905
+ - not_null
906
+ - name: updated_at
907
+ description: Updated At
908
+ meta:
909
+ column_type: datetime
910
+ tests:
911
+ - not_null
912
+
913
+ ```
914
+
915
+ ## Contributing
916
+
917
+ Contribution directions go here.
918
+
919
+ ## License
920
+
921
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).