dynamic_migrations 3.2.2 → 3.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e8f9a1a4cf3316579443b0fed7898a42ab410ba114d194c5ffb874a5ca5027d9
4
- data.tar.gz: 03651b7187cef799edee4d64e9c7533f0c6fb9ce0f9193b16e721c13e508373b
3
+ metadata.gz: d3402241c1537169c438a53ccadd5bc53c048479d75164afe5ec7659bc8f65c3
4
+ data.tar.gz: 0dd0e31a82d757ee980b12c51f8baff81cc9a698308be775a2112bc89fa75fc3
5
5
  SHA512:
6
- metadata.gz: cfdbf88d363b8280f18d1aa63cc7ad9d917de7d5c0582ae43a02ac9341af4c00918f6cd5382c935c2a1a1026873afdd98e2aed42397a722b8660fb876133408a
7
- data.tar.gz: 35451e4c7cfbcfa0a7dec0133b726d005c32ff4c1d7d2792737f24b4b7c2da98b0ba50250f30876f99743d16b911201243d0a0252f162794ec6a08e1955cc634
6
+ metadata.gz: 27fd59935f7d85a81fde4ce74899490917396d8598be9f3a23c8fb5b59a49f9049c85a703ec50d3e9b4040db6ce0acb72ecfa579eb2304bf7d8a79a17ebbccda
7
+ data.tar.gz: 043f54479dfbe7078755a4a258f7ff8a7d0d6fc12208e6849f5a452b473fc28fd0f04c612b9af959f0c69d1b230107a90409b122dcf0e92f33cc32ea1067f716
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.3.0](https://github.com/craigulliott/dynamic_migrations/compare/v3.2.2...v3.3.0) (2023-08-18)
4
+
5
+
6
+ ### Features
7
+
8
+ * adding parameters to triggers ([0d48466](https://github.com/craigulliott/dynamic_migrations/commit/0d484664dd344c34804580fb939a2807339a0552))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * only accepting action_order for loaded triggers, and calculating its value dynamically for configured triggers ([0d48466](https://github.com/craigulliott/dynamic_migrations/commit/0d484664dd344c34804580fb939a2807339a0552))
14
+ * providing full function definition within specs (to conform with updated pg_spec_helper gem) ([0d48466](https://github.com/craigulliott/dynamic_migrations/commit/0d484664dd344c34804580fb939a2807339a0552))
15
+ * removing unused action_statement from triggers ([0d48466](https://github.com/craigulliott/dynamic_migrations/commit/0d484664dd344c34804580fb939a2807339a0552))
16
+
3
17
  ## [3.2.2](https://github.com/craigulliott/dynamic_migrations/compare/v3.2.1...v3.2.2) (2023-08-17)
4
18
 
5
19
 
@@ -18,17 +18,10 @@ module DynamicMigrations
18
18
  fn_sql = block.call.strip
19
19
  end
20
20
 
21
- # ensure that the function ends with a semicolon
22
- unless fn_sql.end_with? ";"
23
- fn_sql << ";"
24
- end
25
-
26
21
  # schema_name was not provided to this method, it comes from the migration class
27
22
  execute <<~SQL
28
23
  CREATE FUNCTION #{schema_name}.#{function_name}() returns trigger language plpgsql AS
29
- $$BEGIN #{fn_sql}
30
- RETURN NEW;
31
- END$$;
24
+ $$#{fn_sql}$$;
32
25
  SQL
33
26
 
34
27
  if comment.is_a? String
@@ -46,11 +39,6 @@ module DynamicMigrations
46
39
  fn_sql = block.call.strip
47
40
  end
48
41
 
49
- # ensure that the function ends with a semicolon
50
- unless fn_sql.end_with? ";"
51
- fn_sql << ";"
52
- end
53
-
54
42
  # schema_name was not provided to this method, it comes from the migration class
55
43
  # assert it already exists
56
44
  exists_result = execute <<~SQL
@@ -72,9 +60,7 @@ module DynamicMigrations
72
60
  # create or replace will update the function
73
61
  execute <<~SQL
74
62
  CREATE OR REPLACE FUNCTION #{schema_name}.#{function_name}() returns trigger language plpgsql AS
75
- $$BEGIN #{fn_sql}
76
- RETURN NEW;
77
- END$$;
63
+ $$#{fn_sql}$$;
78
64
  SQL
79
65
 
80
66
  if comment.is_a? String
@@ -16,7 +16,7 @@ module DynamicMigrations
16
16
  end
17
17
 
18
18
  # create a postgres trigger
19
- def add_trigger table_name, name:, action_timing:, event_manipulation:, action_orientation:, function_schema_name:, function_name:, action_condition: nil, action_reference_old_table: nil, action_reference_new_table: nil, comment: nil
19
+ def add_trigger table_name, name:, action_timing:, event_manipulation:, action_orientation:, function_schema_name:, function_name:, parameters: nil, action_condition: nil, action_reference_old_table: nil, action_reference_new_table: nil, comment: nil
20
20
  unless [:insert, :delete, :update].include? event_manipulation
21
21
  raise UnexpectedEventManipulationError, event_manipulation
22
22
  end
@@ -53,7 +53,7 @@ module DynamicMigrations
53
53
  #{timing_sql} ON #{schema_name}.#{table_name} #{temp_tables_sql}
54
54
  FOR EACH #{action_orientation}
55
55
  #{condition_sql}
56
- EXECUTE FUNCTION #{function_schema_name}.#{function_name}();
56
+ EXECUTE FUNCTION #{function_schema_name}.#{function_name}(#{parameters});
57
57
  SQL
58
58
 
59
59
  if comment.is_a? String
@@ -20,10 +20,6 @@ module DynamicMigrations
20
20
  optional_options_syntax = (options_syntax == "") ? "" : ", #{options_syntax}"
21
21
 
22
22
  fn_sql = function.definition.strip
23
- # ensure that the function ends with a semicolon
24
- unless fn_sql.end_with? ";"
25
- fn_sql << ";"
26
- end
27
23
 
28
24
  add_fragment schema: function.schema,
29
25
  table: function.triggers.first&.table,
@@ -33,7 +29,7 @@ module DynamicMigrations
33
29
  migration: comment_sql + <<~RUBY
34
30
  create_function :#{function.name}#{optional_options_syntax} do
35
31
  <<~SQL
36
- #{indent fn_sql}
32
+ #{indent fn_sql, 2}
37
33
  SQL
38
34
  end
39
35
  RUBY
@@ -41,10 +37,6 @@ module DynamicMigrations
41
37
 
42
38
  def update_function function, code_comment = nil
43
39
  fn_sql = function.definition.strip
44
- # ensure that the function ends with a semicolon
45
- unless fn_sql.end_with? ";"
46
- fn_sql << ";"
47
- end
48
40
 
49
41
  add_fragment schema: function.schema,
50
42
  table: function.triggers.first&.table,
@@ -54,7 +46,7 @@ module DynamicMigrations
54
46
  migration: <<~RUBY
55
47
  update_function :#{function.name} do
56
48
  <<~SQL
57
- #{indent fn_sql}
49
+ #{indent fn_sql, 2}
58
50
  SQL
59
51
  end
60
52
  RUBY
@@ -28,6 +28,10 @@ module DynamicMigrations
28
28
  options[:action_condition] = ":#{trigger.action_condition}"
29
29
  end
30
30
 
31
+ unless trigger.parameters.nil?
32
+ options[:parameters] = "\"#{trigger.parameters}\""
33
+ end
34
+
31
35
  unless trigger.action_reference_old_table.nil?
32
36
  options[:action_reference_old_table] = ":#{trigger.action_reference_old_table}"
33
37
  end
@@ -216,8 +216,9 @@ module DynamicMigrations
216
216
  fragment
217
217
  end
218
218
 
219
- def indent multi_line_string
220
- multi_line_string.gsub("\n", "\n ")
219
+ def indent multi_line_string, levels = 1
220
+ spaces = " " * levels
221
+ multi_line_string.gsub("\n", "\n#{spaces}")
221
222
  end
222
223
 
223
224
  def strip_empty_lines multi_line_string
@@ -198,7 +198,7 @@ module DynamicMigrations
198
198
  :event_manipulation,
199
199
  :action_order,
200
200
  :action_condition,
201
- :action_statement,
201
+ :parameters,
202
202
  :action_orientation,
203
203
  :action_reference_old_table,
204
204
  :action_reference_new_table,
@@ -92,7 +92,7 @@ module DynamicMigrations
92
92
  table.add_trigger trigger_name, action_timing: trigger_definition[:action_timing],
93
93
  event_manipulation: trigger_definition[:event_manipulation],
94
94
  action_order: trigger_definition[:action_order],
95
- action_statement: trigger_definition[:action_statement],
95
+ parameters: trigger_definition[:parameters],
96
96
  action_orientation: trigger_definition[:action_orientation],
97
97
  function: function,
98
98
  action_condition: trigger_definition[:action_condition],
@@ -31,10 +31,10 @@ module DynamicMigrations
31
31
  raise ExpectedSymbolError, name unless name.is_a? Symbol
32
32
  @name = name
33
33
 
34
- unless definition.is_a?(String) && definition.strip != ""
35
- raise ExpectedDefinitionError, definition
34
+ unless definition.is_a?(String) && definition.strip != "" && definition.strip.end_with?("END;")
35
+ raise ExpectedDefinitionError, "Definition must be a string, and end with `END;`. Definition provided:\n#{definition}"
36
36
  end
37
- @definition = definition
37
+ @definition = definition.strip
38
38
 
39
39
  unless description.nil?
40
40
  raise ExpectedStringError, description unless description.is_a? String
@@ -14,10 +14,7 @@ module DynamicMigrations
14
14
  class UnexpectedEventManipulationError < StandardError
15
15
  end
16
16
 
17
- class UnexpectedActionOrderError < StandardError
18
- end
19
-
20
- class UnexpectedActionStatementError < StandardError
17
+ class UnexpectedParametersError < StandardError
21
18
  end
22
19
 
23
20
  class UnexpectedActionOrientationError < StandardError
@@ -35,13 +32,15 @@ module DynamicMigrations
35
32
  class ExpectedFunctionError < StandardError
36
33
  end
37
34
 
35
+ class UnexpectedActionOrderError < StandardError
36
+ end
37
+
38
38
  attr_reader :table
39
39
  attr_reader :name
40
40
  attr_reader :event_manipulation
41
41
  attr_reader :action_timing
42
- attr_reader :action_order
43
42
  attr_reader :action_condition
44
- attr_reader :action_statement
43
+ attr_reader :parameters
45
44
  attr_reader :action_orientation
46
45
  attr_reader :function
47
46
  attr_reader :action_reference_old_table
@@ -49,7 +48,7 @@ module DynamicMigrations
49
48
  attr_reader :description
50
49
 
51
50
  # initialize a new object to represent a validation in a postgres table
52
- def initialize source, table, name, action_timing:, event_manipulation:, action_order:, action_statement:, action_orientation:, function:, action_condition: nil, action_reference_old_table: nil, action_reference_new_table: nil, description: nil
51
+ def initialize source, table, name, action_timing:, event_manipulation:, parameters:, action_orientation:, function:, action_order: nil, action_condition: nil, action_reference_old_table: nil, action_reference_new_table: nil, description: nil
53
52
  super source
54
53
 
55
54
  unless table.is_a? Table
@@ -72,20 +71,27 @@ module DynamicMigrations
72
71
  end
73
72
  @event_manipulation = event_manipulation
74
73
 
75
- unless action_order.is_a?(Integer) && action_order >= 1
76
- raise UnexpectedActionOrderError, action_order
74
+ if from_configuration?
75
+ unless action_order.nil?
76
+ raise UnexpectedActionOrderError, "Unexpected `action_order` argument. Action order is calculated dynamically for configured triggers."
77
+ end
78
+
79
+ else
80
+ unless action_order.is_a?(Integer) && action_order >= 1
81
+ raise UnexpectedActionOrderError, "Missing valid `action_order` argument. Action order must be provided for triggers loaded from the database."
82
+ end
83
+ @action_order = action_order
77
84
  end
78
- @action_order = action_order
79
85
 
80
86
  unless action_condition.nil? || action_condition.is_a?(String)
81
87
  raise ExpectedStringError, action_condition
82
88
  end
83
89
  @action_condition = action_condition
84
90
 
85
- unless action_statement.is_a?(String) && action_statement[/\AEXECUTE FUNCTION [a-z]+(_[a-z]+)*\.[a-z]+(_[a-z]+)*\(\)\z/]
86
- raise UnexpectedActionStatementError, "unexpected action statement `#{action_statement}`, currently only `EXECUTE FUNCTION function_name()` statements are supported"
91
+ unless parameters.nil? || (parameters.is_a?(String) && parameters[/\A'[\w\d_ -]+'(, ?'[\w\d_ -]+')*\z/])
92
+ raise UnexpectedParametersError, "unexpected parameters `#{parameters}`, currently only a comma seeparated list of strings is supported"
87
93
  end
88
- @action_statement = action_statement
94
+ @parameters = parameters
89
95
 
90
96
  unless [:row, :statement].include? action_orientation
91
97
  raise UnexpectedActionOrientationError, action_orientation
@@ -120,6 +126,42 @@ module DynamicMigrations
120
126
  end
121
127
  end
122
128
 
129
+ def action_order
130
+ # if the source is the database, then return the locally stored
131
+ # representation of the action order
132
+ if from_database?
133
+ action_order = @action_order
134
+ if action_order.nil?
135
+ raise "Missing valid action_order. This should be impossible."
136
+ end
137
+ action_order
138
+
139
+ # otherwise return the dynamically calculated action order, this is calculated
140
+ # by returning this triggers index in the list of alphabetically sorted triggers
141
+ # for this triggers table
142
+ else
143
+ pos = @table.triggers.sort_by(&:name).index(self)
144
+ if pos.nil?
145
+ raise "Trigger not found in table triggers list. This should be impossible."
146
+ end
147
+ pos + 1
148
+ end
149
+ end
150
+
151
+ def action_condition= new_action_condition
152
+ unless new_action_condition.nil? || new_action_condition.is_a?(String)
153
+ raise ExpectedStringError, new_action_condition
154
+ end
155
+ @action_condition = new_action_condition
156
+ end
157
+
158
+ def parameters= new_parameters
159
+ unless new_parameters.nil? || new_parameters.is_a?(String)
160
+ raise ExpectedStringError, new_parameters
161
+ end
162
+ @parameters = new_parameters
163
+ end
164
+
123
165
  # return true if this has a description, otherwise false
124
166
  def has_description?
125
167
  !@description.nil?
@@ -131,7 +173,7 @@ module DynamicMigrations
131
173
  :action_timing,
132
174
  :action_order,
133
175
  :action_condition,
134
- :action_statement,
176
+ :parameters,
135
177
  :action_orientation,
136
178
  :action_reference_old_table,
137
179
  :action_reference_new_table
@@ -38,13 +38,13 @@ module DynamicMigrations
38
38
  end
39
39
 
40
40
  # adds a new trigger to this table, and returns it
41
- def add_trigger name, action_timing:, event_manipulation:, action_order:, action_statement:, action_orientation:, function:, action_condition: nil, action_reference_old_table: nil, action_reference_new_table: nil, description: nil
41
+ def add_trigger name, action_timing:, event_manipulation:, parameters:, action_orientation:, function:, action_order: nil, action_condition: nil, action_reference_old_table: nil, action_reference_new_table: nil, description: nil
42
42
  if has_trigger? name
43
43
  raise(TriggerAlreadyExistsError, "Trigger #{name} already exists")
44
44
  end
45
45
  included_target = self
46
46
  if included_target.is_a? Table
47
- new_trigger = @triggers[name] = Trigger.new source, included_target, name, action_timing: action_timing, event_manipulation: event_manipulation, action_order: action_order, action_statement: action_statement, action_orientation: action_orientation, function: function, action_condition: action_condition, action_reference_old_table: action_reference_old_table, action_reference_new_table: action_reference_new_table, description: description
47
+ new_trigger = @triggers[name] = Trigger.new source, included_target, name, action_timing: action_timing, event_manipulation: event_manipulation, action_order: action_order, parameters: parameters, action_orientation: action_orientation, function: function, action_condition: action_condition, action_reference_old_table: action_reference_old_table, action_reference_new_table: action_reference_new_table, description: description
48
48
  else
49
49
  raise ModuleIncludedIntoUnexpectedTargetError, included_target
50
50
  end
@@ -35,22 +35,14 @@ module DynamicMigrations
35
35
  '.{35,} WHEN ((.+)) EXECUTE FUNCTION'
36
36
  )
37
37
  ) [1] ELSE NULL END AS action_condition,
38
- p_n.nspname AS function_schema,
39
- p.proname AS function_name,
40
- p.prosrc AS function_definition,
41
38
  SUBSTRING(
42
39
  pg_get_triggerdef(t.oid)
43
40
  FROM
44
- POSITION(
45
- ('EXECUTE FUNCTION') IN (
46
- SUBSTRING(
47
- pg_get_triggerdef(t.oid)
48
- FROM
49
- 48
50
- )
51
- )
52
- ) + 47
53
- ) AS action_statement,
41
+ '\\(([\d\w_''-]+)\\)$'
42
+ ) AS parameters,
43
+ p_n.nspname AS function_schema,
44
+ p.proname AS function_name,
45
+ p.prosrc AS function_definition,
54
46
  CASE t.tgtype & 1 WHEN 1 THEN 'row' ELSE 'statement' END AS action_orientation,
55
47
  CASE t.tgtype & 66 WHEN 2 THEN 'before' WHEN 64 THEN 'instead_of' ELSE 'after' END AS action_timing,
56
48
  t.tgoldtable AS action_reference_old_table,
@@ -112,7 +104,7 @@ module DynamicMigrations
112
104
  function_schema: row["function_schema"].to_sym,
113
105
  function_name: row["function_name"].to_sym,
114
106
  function_definition: row["function_definition"],
115
- action_statement: row["action_statement"],
107
+ parameters: row["parameters"],
116
108
  action_orientation: row["action_orientation"].to_sym,
117
109
  action_timing: row["action_timing"].to_sym,
118
110
  # `action_reference_old_table` and `action_reference_new_table` can be null
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DynamicMigrations
4
- VERSION = "3.2.2"
4
+ VERSION = "3.3.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynamic_migrations
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.2
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Craig Ulliott
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-17 00:00:00.000000000 Z
11
+ date: 2023-08-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg