babik 0.1.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.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +16 -0
  3. data/README.md +718 -0
  4. data/Rakefile +18 -0
  5. data/lib/babik.rb +122 -0
  6. data/lib/babik/database.rb +16 -0
  7. data/lib/babik/queryset.rb +154 -0
  8. data/lib/babik/queryset/components/aggregation.rb +172 -0
  9. data/lib/babik/queryset/components/limit.rb +22 -0
  10. data/lib/babik/queryset/components/order.rb +161 -0
  11. data/lib/babik/queryset/components/projection.rb +118 -0
  12. data/lib/babik/queryset/components/select_related.rb +78 -0
  13. data/lib/babik/queryset/components/sql_renderer.rb +99 -0
  14. data/lib/babik/queryset/components/where.rb +43 -0
  15. data/lib/babik/queryset/lib/association/foreign_association_chain.rb +97 -0
  16. data/lib/babik/queryset/lib/association/select_related_association_chain.rb +32 -0
  17. data/lib/babik/queryset/lib/condition.rb +103 -0
  18. data/lib/babik/queryset/lib/field.rb +34 -0
  19. data/lib/babik/queryset/lib/join/association_joiner.rb +39 -0
  20. data/lib/babik/queryset/lib/join/join.rb +86 -0
  21. data/lib/babik/queryset/lib/selection/config.rb +19 -0
  22. data/lib/babik/queryset/lib/selection/foreign_selection.rb +39 -0
  23. data/lib/babik/queryset/lib/selection/local_selection.rb +40 -0
  24. data/lib/babik/queryset/lib/selection/operation/base.rb +126 -0
  25. data/lib/babik/queryset/lib/selection/operation/date.rb +178 -0
  26. data/lib/babik/queryset/lib/selection/operation/operations.rb +201 -0
  27. data/lib/babik/queryset/lib/selection/operation/regex.rb +58 -0
  28. data/lib/babik/queryset/lib/selection/path/foreign_path.rb +50 -0
  29. data/lib/babik/queryset/lib/selection/path/local_path.rb +44 -0
  30. data/lib/babik/queryset/lib/selection/path/path.rb +23 -0
  31. data/lib/babik/queryset/lib/selection/select_related_selection.rb +38 -0
  32. data/lib/babik/queryset/lib/selection/selection.rb +19 -0
  33. data/lib/babik/queryset/lib/update/assignment.rb +108 -0
  34. data/lib/babik/queryset/mixins/aggregatable.rb +17 -0
  35. data/lib/babik/queryset/mixins/bounded.rb +38 -0
  36. data/lib/babik/queryset/mixins/clonable.rb +52 -0
  37. data/lib/babik/queryset/mixins/countable.rb +44 -0
  38. data/lib/babik/queryset/mixins/deletable.rb +13 -0
  39. data/lib/babik/queryset/mixins/distinguishable.rb +27 -0
  40. data/lib/babik/queryset/mixins/filterable.rb +51 -0
  41. data/lib/babik/queryset/mixins/limitable.rb +88 -0
  42. data/lib/babik/queryset/mixins/lockable.rb +31 -0
  43. data/lib/babik/queryset/mixins/none.rb +16 -0
  44. data/lib/babik/queryset/mixins/projectable.rb +34 -0
  45. data/lib/babik/queryset/mixins/related_selector.rb +28 -0
  46. data/lib/babik/queryset/mixins/set_operations.rb +32 -0
  47. data/lib/babik/queryset/mixins/sortable.rb +49 -0
  48. data/lib/babik/queryset/mixins/sql_renderizable.rb +17 -0
  49. data/lib/babik/queryset/mixins/updatable.rb +14 -0
  50. data/lib/babik/queryset/templates/default/delete/main.sql.erb +14 -0
  51. data/lib/babik/queryset/templates/default/select/components/aggregation.sql.erb +5 -0
  52. data/lib/babik/queryset/templates/default/select/components/from.sql.erb +16 -0
  53. data/lib/babik/queryset/templates/default/select/components/from_set.sql.erb +3 -0
  54. data/lib/babik/queryset/templates/default/select/components/from_table.sql.erb +2 -0
  55. data/lib/babik/queryset/templates/default/select/components/limit.sql.erb +10 -0
  56. data/lib/babik/queryset/templates/default/select/components/order_by.sql.erb +9 -0
  57. data/lib/babik/queryset/templates/default/select/components/projection.sql.erb +7 -0
  58. data/lib/babik/queryset/templates/default/select/components/select_related.sql.erb +26 -0
  59. data/lib/babik/queryset/templates/default/select/components/where.sql.erb +39 -0
  60. data/lib/babik/queryset/templates/default/select/main.sql.erb +42 -0
  61. data/lib/babik/queryset/templates/default/update/main.sql.erb +15 -0
  62. data/lib/babik/queryset/templates/mssql/select/components/limit.sql.erb +8 -0
  63. data/lib/babik/queryset/templates/mssql/select/components/order_by.sql.erb +21 -0
  64. data/lib/babik/queryset/templates/mysql2/delete/main.sql.erb +15 -0
  65. data/lib/babik/queryset/templates/mysql2/update/main.sql.erb +18 -0
  66. data/lib/babik/queryset/templates/sqlite3/select/components/from_set.sql.erb +5 -0
  67. data/test/config/db/schema.rb +83 -0
  68. data/test/config/models/bad_post.rb +5 -0
  69. data/test/config/models/bad_tag.rb +5 -0
  70. data/test/config/models/category.rb +4 -0
  71. data/test/config/models/geozone.rb +6 -0
  72. data/test/config/models/group.rb +5 -0
  73. data/test/config/models/group_user.rb +5 -0
  74. data/test/config/models/post.rb +24 -0
  75. data/test/config/models/post_tag.rb +5 -0
  76. data/test/config/models/tag.rb +5 -0
  77. data/test/config/models/user.rb +6 -0
  78. data/test/delete/delete_test.rb +60 -0
  79. data/test/delete/foreign_conditions_delete_test.rb +57 -0
  80. data/test/delete/local_conditions_delete_test.rb +20 -0
  81. data/test/enable_coverage.rb +17 -0
  82. data/test/lib/selection/operation/log/test-queries.log +1 -0
  83. data/test/lib/selection/operation/test_date.rb +131 -0
  84. data/test/lib/selection/operation/test_regex.rb +55 -0
  85. data/test/other/clone_test.rb +129 -0
  86. data/test/other/escape_test.rb +21 -0
  87. data/test/other/inverse_of_required_test.rb +33 -0
  88. data/test/select/aggregate_test.rb +151 -0
  89. data/test/select/bounds_test.rb +46 -0
  90. data/test/select/count_test.rb +147 -0
  91. data/test/select/distinct_test.rb +38 -0
  92. data/test/select/exclude_test.rb +72 -0
  93. data/test/select/filter_from_object_test.rb +125 -0
  94. data/test/select/filter_test.rb +207 -0
  95. data/test/select/for_update_test.rb +19 -0
  96. data/test/select/foreign_selection_test.rb +60 -0
  97. data/test/select/get_test.rb +40 -0
  98. data/test/select/limit_test.rb +109 -0
  99. data/test/select/local_selection_test.rb +24 -0
  100. data/test/select/lookup_test.rb +208 -0
  101. data/test/select/none_test.rb +40 -0
  102. data/test/select/order_test.rb +165 -0
  103. data/test/select/project_test.rb +107 -0
  104. data/test/select/select_related_test.rb +124 -0
  105. data/test/select/subquery_test.rb +50 -0
  106. data/test/set_operations/basic_usage_test.rb +121 -0
  107. data/test/test_helper.rb +55 -0
  108. data/test/update/update_test.rb +93 -0
  109. metadata +278 -0
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Babik
4
+ # QuerySet module
5
+ module QuerySet
6
+ # Manages the limit of the QuerySet
7
+ class Limit
8
+ attr_reader :size, :offset
9
+
10
+ # Construct a limit for QuerySet
11
+ # @param size [Integer] Size to be selected
12
+ # @param offset [Integer] Offset from the selection will begin. By default is 0.
13
+ def initialize(size, offset = 0)
14
+ @size = size.to_i
15
+ @offset = offset.to_i
16
+ end
17
+
18
+ end
19
+ end
20
+ end
21
+
22
+
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'babik/queryset/lib/selection/selection'
4
+
5
+ module Babik
6
+ # QuerySet module
7
+ module QuerySet
8
+ # Manages the order of the QuerySet
9
+ class Order
10
+
11
+ attr_reader :order_fields
12
+
13
+ # Construct the order manager
14
+ # @param model [ActiveRecord::Base] base model.
15
+ # @param ordering [Array, String, Hash] ordering that will be applied to the QuerySet.
16
+ # Each item of the array represents an order:
17
+ # - field1: field1 ASC
18
+ # - -field1: field1 DESC
19
+ # - [field1, :ASC]: field1 ASC
20
+ # - [field1, :DESC]: field1 DESC
21
+ # - {field1, :ASC}: field1 ASC
22
+ # - {field1, :DESC}: field1 DESC
23
+ # @raise [RuntimeError] Invalid type of order
24
+ def initialize(model, *ordering)
25
+ @model = model
26
+ # Convert the types of each order field
27
+ order_as_array_or_pairs = ordering.map do |order|
28
+ if [Hash, String, Symbol].include?(order.class)
29
+ self.send("_order_from_#{order.class.to_s.downcase}", order)
30
+ elsif order.class == Array
31
+ order
32
+ else
33
+ raise "Invalid type of order: #{order}"
34
+ end
35
+ end
36
+ _initialize_field_orders(order_as_array_or_pairs)
37
+ end
38
+
39
+ # Get order from string
40
+ # @param order [String] The string of the form 'field1'
41
+ # @api private
42
+ # @return [Array] Conversion of order as string to array.
43
+ def _order_from_string(order)
44
+ return [order, :ASC] if order[0] != '-'
45
+ [order[1..-1], :DESC]
46
+ end
47
+
48
+ # Get order from symbol
49
+ # @param order [Symbol] The symbol of the form :field1
50
+ # @api private
51
+ # @return [Array] Conversion of order as symbol to array.
52
+ def _order_from_symbol(order)
53
+ _order_from_string(order.to_s)
54
+ end
55
+
56
+ # Get order from a hash
57
+ # @param order [Hash] The string of the form <field>: <ORD> (where <ORD> is :ASC or :DESC)
58
+ # @return [Array] Conversion of order as hash to array.
59
+ def _order_from_hash(order)
60
+ raise "More than one key found in order by for class #{self.class}" if order.keys.length > 1
61
+ order_field = order.keys[0]
62
+ order_value = order[order_field]
63
+ [order_field, order_value]
64
+ end
65
+
66
+ # Initialize the order paths
67
+ # @api private
68
+ # @return [Array] Conversion of order as hash to array.
69
+ def _initialize_field_orders(order)
70
+ # Check
71
+ @order_fields = []
72
+ order.each_with_index do |order_field_direction, _order_field_index|
73
+ order_field_path = order_field_direction[0]
74
+ order_direction = order_field_direction[1]
75
+ @order_fields << OrderField.new(@model, order_field_path, order_direction)
76
+ end
77
+ end
78
+
79
+ # Return an direction inversion of this order
80
+ # e.g.
81
+ # User, first_name, ASC => invert => User, first_name, DESC
82
+ # @return [Array<OrderField>] Inverted order.
83
+ def invert
84
+ @order_fields.map(&:invert)
85
+ end
86
+
87
+ # Invert actual order direction
88
+ def invert!
89
+ @order_fields = self.invert
90
+ end
91
+
92
+ # Return the left joins this order include, grouped by alias
93
+ # @return [Hash] Hash with the key equal to alias and the value equals to a Join.
94
+ def left_joins_by_alias
95
+ left_joins_by_alias = {}
96
+ @order_fields.each do |order_field|
97
+ left_joins_by_alias.merge!(order_field.left_joins_by_alias)
98
+ end
99
+ left_joins_by_alias
100
+ end
101
+
102
+ # Return sql of the fields to order.
103
+ # Does not include ORDER BY.
104
+ # @return [SQL] SQL code for fields to order.
105
+ def sql
106
+ @order_fields.map(&:sql).join(', ')
107
+ end
108
+ end
109
+
110
+ # Each one of the fields that appear in the order statement
111
+ class OrderField
112
+ attr_reader :selection, :direction, :model
113
+
114
+ delegate :left_joins_by_alias, to: :selection
115
+
116
+ # Construct the OrderField
117
+ # @param model [ActiveRecord::Base] base model.
118
+ # @param field_path [String, Symbol, Selection] field path. If local, it will be one of the attributes,
119
+ # otherwise will be an association path.
120
+ # @param direction [String, Symbol] :ASC or :DESC (a string will be converted to symbol).
121
+ def initialize(model, field_path, direction)
122
+ direction_sym = direction.to_sym
123
+ unless %i[ASC DESC].include?(direction_sym)
124
+ raise "Invalid order type #{direction} in #{field_path}: Expecting :ASC or :DESC"
125
+ end
126
+ @model = model
127
+ if [String, Symbol].include?(field_path.class)
128
+ @selection = Babik::Selection::Path::Factory.build(@model, field_path)
129
+ elsif [Babik::Selection::Path::LocalPath, Babik::Selection::Path::ForeignPath].include?(field_path.class)
130
+ @selection = field_path
131
+ else
132
+ raise "field_path of class #{field_path.class} not valid. A Symbol/String/Babik::Selection::Base expected"
133
+ end
134
+ @direction = direction_sym
135
+ end
136
+
137
+ # Return a new OrderField with the direction inverted
138
+ # @return [OrderField] Order field with inverted direction.
139
+ def invert
140
+ inverted_direction = if @direction.to_sym == :ASC
141
+ :DESC
142
+ else
143
+ :ASC
144
+ end
145
+ OrderField.new(@model, @selection, inverted_direction)
146
+ end
147
+
148
+ # Return sql of the field to order.
149
+ # i.e. something like this:
150
+ # <table_alias>.<field> ASC
151
+ # <table_alias>.<field> DESC
152
+ # e.g.
153
+ # users_0.first_name ASC
154
+ # posts_0.title DESC
155
+ # @return [SQL] SQL code for field to order.
156
+ def sql
157
+ "#{@selection.target_alias}.#{@selection.selected_field} #{@direction}"
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'babik/queryset/lib/selection/path/path'
4
+
5
+ module Babik
6
+ # QuerySet module
7
+ module QuerySet
8
+
9
+ # Manages the projection of a SELECT QuerySet
10
+ class Projection
11
+
12
+ # Constructs a projection
13
+ # @param model [ActiveRecord::Base] model the projection is based on.
14
+ # @param fields [Array] array of fields that will be projected.
15
+ def initialize(model, fields)
16
+ @fields = []
17
+ @fields_hash = {}
18
+ fields.each do |field|
19
+ new_field = ProjectedField.new(model, field)
20
+ @fields << new_field
21
+ @fields_hash[new_field.alias.to_sym] = new_field
22
+ end
23
+ end
24
+
25
+ def apply_transforms(result_set)
26
+ result_set.map do |record|
27
+ record.symbolize_keys!
28
+ transformed_record = {}
29
+ record.each do |field, value|
30
+ transform = @fields_hash[field].transform
31
+ transformed_record[field] = if transform
32
+ transform.call(value)
33
+ else
34
+ value
35
+ end
36
+ end
37
+ transformed_record
38
+ end
39
+ end
40
+
41
+ # Return sql of the fields to project.
42
+ # Does not include SELECT.
43
+ # @return [SQL] SQL code for fields to select in SELECT.
44
+ def sql
45
+ @fields.map(&:sql).join(', ')
46
+ end
47
+ end
48
+
49
+ # Each one of the fields that will be returned by SELECT clause
50
+ class ProjectedField
51
+ attr_reader :model, :alias, :transform, :selection
52
+
53
+ # Construct a projected field from a model and its field.
54
+ # @param model [ActiveRecord::Base] model whose field will be returned in the SELECT query.
55
+ # @param field [Array, String]
56
+ # if Array, it must be [field_name, alias, transform] where
57
+ # - field_name is the name of the field (the column name). It is mandatory and must be the first
58
+ # item of the array.
59
+ # - alias if present, it will be used to name the field instead of its name.
60
+ # - transform, if present, a lambda function with the transformation each value of that column it must suffer.
61
+ # e.g.:
62
+ # [:created_at, :birth_date]
63
+ # [:stars, ->(stars) { [stars, 5].min } ]
64
+ # Otherwise, a field of the local table or foreign tables.
65
+ #
66
+ def initialize(model, field)
67
+ @model = model
68
+ method_name = "initialize_from_#{field.class.to_s.downcase}"
69
+ unless self.respond_to?(method_name)
70
+ raise "No other parameter type is permitted in #{self.class}.new than Array, String and Symbol."
71
+ end
72
+ self.send(method_name, field)
73
+ @selection = Babik::Selection::Path::Factory.build(model, @name)
74
+ end
75
+
76
+ # Initialize from Array
77
+ def initialize_from_array(field)
78
+ @name = field[0]
79
+ @alias = @name
80
+ [1, 2].each do |field_index|
81
+ next unless field[field_index]
82
+ field_i = field[field_index]
83
+ if [Symbol, String].include?(field_i.class)
84
+ @alias = field_i
85
+ elsif field_i.class == Proc
86
+ @transform = field_i
87
+ else
88
+ raise "#{self.class}.new only accepts String/Symbol or Proc. Passed a #{field_i.class}."
89
+ end
90
+ end
91
+ end
92
+
93
+ # Initialize from String
94
+ def initialize_from_string(field)
95
+ @name = field.to_sym
96
+ @alias = field.to_sym
97
+ @transform = nil
98
+ end
99
+
100
+ # Initialize from Symbol
101
+ def initialize_from_symbol(field)
102
+ initialize_from_string(field)
103
+ end
104
+
105
+ # Return sql of the field to project.
106
+ # i.e. something like this:
107
+ # <table_alias>.<field>
108
+ # <table_alias>.<field> AS <field_alias>
109
+ # e.g.
110
+ # users_0.first_name
111
+ # posts_0.title AS post_title
112
+ # @return [SQL] SQL code for field to appear in SELECT.
113
+ def sql
114
+ "#{@selection.target_alias}.#{@selection.selected_field} AS #{@alias}"
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'babik/queryset/lib/selection/select_related_selection'
4
+
5
+ module Babik
6
+ # QuerySet module
7
+ module QuerySet
8
+ # Delegate object that must deals with all the select_related particularities.
9
+ class SelectRelated
10
+
11
+ attr_reader :model, :associations
12
+
13
+ # Creates a new SelectRelated
14
+ def initialize(model, selection_paths)
15
+ @model = model
16
+ @associations = []
17
+ selection_paths = [selection_paths] if selection_paths.class != Array
18
+ selection_paths.each do |selection_path|
19
+ @associations << Babik::Selection::SelectRelatedSelection.new(@model, selection_path)
20
+ end
21
+ end
22
+
23
+ # Return the joins that are needed according to the associated path
24
+ # @return [Hash{table_alias: String}] Left joins by table alias.
25
+ def left_joins_by_alias
26
+ left_joins_by_alias = {}
27
+ @associations.each do |association|
28
+ left_joins_by_alias.merge!(association.left_joins_by_alias)
29
+ end
30
+ left_joins_by_alias
31
+ end
32
+
33
+ # Return the next object and its related objects
34
+ # Requires a result set of the query that selects all object attributes and the rest
35
+ # of attributes of the associated objects.
36
+ # @param result_set [ResultSet] Result with the query that loads all the required objects
37
+ # (main and related ones).
38
+ # @return [ActiveRecord::Base, Hash{selection_path: ActiveRecord::Base}]
39
+ # Return and object with its associated objects.
40
+ def all_with_related(result_set)
41
+ result_set.map do |record|
42
+ object = instantiate_model_object(record)
43
+ associated_objects = @associations.map do |association|
44
+ [association.selection_path, instantiate_associated_object(record, association)]
45
+ end
46
+ [object, associated_objects.to_h.symbolize_keys]
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ # Construct a model object
53
+ def instantiate_model_object(record)
54
+ object = @model.new
55
+ object.assign_attributes(record.select { |attribute| @model.column_names.include?(attribute) })
56
+ object
57
+ end
58
+
59
+ # Construct an associated object
60
+ def instantiate_associated_object(record, association)
61
+ target_model = association.target_model
62
+ target_object = target_model.new
63
+
64
+ # First, get the attributes that have the desired prefix (the association path)
65
+ target_attributes_with_prefix = record.select { |attr, value| attr.start_with?("#{association.id}__") }
66
+
67
+ # Second, convert it to a hash
68
+ target_attributes = (target_attributes_with_prefix.map do |attribute, value|
69
+ [attribute.split('__')[1], value]
70
+ end).to_h
71
+
72
+ # Last, assign it to the associated object
73
+ target_object.assign_attributes(target_attributes)
74
+ target_object
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb'
4
+ require 'babik/database'
5
+
6
+ module Babik
7
+ module QuerySet
8
+ # SQL renderer
9
+ class SQLRenderer
10
+
11
+ attr_reader :queryset
12
+
13
+ # Where the SQL templates are
14
+ TEMPLATE_PATH = "#{__dir__}/../templates"
15
+
16
+ # Construct a new SQL rendered for a QuerySet
17
+ # @param queryset [QuerySet] QuerySet to be rendered.
18
+ def initialize(queryset)
19
+ @queryset = queryset
20
+ end
21
+
22
+ # Render the SELECT statement
23
+ # @return [String] SQL SELECT statement for this QuerySet.
24
+ def select
25
+ _render('select/main.sql.erb')
26
+ end
27
+
28
+ # Render the UPDATE statement
29
+ # @param update_command [Hash{field: value}] Runs the update query.
30
+ # @return [String] SQL UPDATE statement for this QuerySet.
31
+ def update(update_command)
32
+ @queryset.project!(['id'])
33
+ sql = _render('update/main.sql.erb', {update_command: update_command})
34
+ @queryset.unproject!
35
+ sql
36
+ end
37
+
38
+ # Render the DELETE statement
39
+ # @return [String] SQL DELETE statement for this QuerySet.
40
+ def delete
41
+ @queryset.project(['id'])
42
+ sql = _render('delete/main.sql.erb')
43
+ @queryset.unproject
44
+ sql
45
+ end
46
+
47
+ # Return the SQL representation of all joins of the QuerySet
48
+ # @return [String] A String with all LEFT JOIN statements required for this QuerySet.
49
+ def left_joins
50
+ # Join all left joins and return a string with the SQL code
51
+ @queryset.left_joins_by_alias.values.map(&:sql).join("\n")
52
+ end
53
+
54
+ private
55
+
56
+ # Render a file in a path
57
+ # @api private
58
+ # @param template_path [String] Relative (to {SQLRenderer::TEMPLATE_PATH}) path of the template file.
59
+ # @param extra_replacements [Hash] Hash with the replacements. By default is an empty hash.
60
+ # @return [String] Rendered SQL with QuerySet replacements completed
61
+ def _render(template_path, extra_replacements = {})
62
+ render = lambda do |partial_template_path, replacements|
63
+ replacements[:render] = render
64
+ _base_render(partial_template_path, **replacements)
65
+ end
66
+ replacements = extra_replacements.clone
67
+ replacements[:queryset] = @queryset
68
+ replacements[:render] = render
69
+ _base_render(template_path, **replacements)
70
+ end
71
+
72
+ # Render a file
73
+ # It first search in the dbms_adapter directory and if the file exists, uses that as template.
74
+ # Otherwise, load the one placed in the default directory.
75
+ # @api private
76
+ # @param template_path [String] Relative (to {SQLRenderer::TEMPLATE_PATH}) path of the template file.
77
+ # @param replacements [Hash] Hash with the replacements.
78
+ # @return [String] Rendered SQL with QuerySet replacements completed
79
+ def _base_render(template_path, replacements)
80
+ dbms_adapter = _dbms_adapter
81
+ dbms_adapter_template_path = "#{TEMPLATE_PATH}/#{dbms_adapter}/#{template_path}"
82
+ template_path = if File.exist?(dbms_adapter_template_path)
83
+ dbms_adapter_template_path
84
+ else
85
+ "#{TEMPLATE_PATH}/default/#{template_path}"
86
+ end
87
+ template_content = File.read(template_path)
88
+ ::ERB.new(template_content).result_with_hash(**replacements).gsub(/[ ]+/, ' ').gsub(/[\n ]{2,}/, "\n")
89
+ end
90
+
91
+ # Return the DBMS adapter.
92
+ # @return [String] DBMS adapter (sqlite3, postgre, mysql, mariadb, oracle or mssql).
93
+ def _dbms_adapter
94
+ Babik::Database.config[:adapter]
95
+ end
96
+
97
+ end
98
+ end
99
+ end