fx 0.9.0 → 0.10.1

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +4 -4
  3. data/CHANGELOG.md +43 -1
  4. data/Gemfile +5 -2
  5. data/README.md +16 -0
  6. data/bin/rake +2 -3
  7. data/bin/rspec +13 -3
  8. data/bin/yard +13 -3
  9. data/fx.gemspec +3 -3
  10. data/lib/fx/adapters/postgres/connection.rb +12 -4
  11. data/lib/fx/adapters/postgres/functions.rb +11 -28
  12. data/lib/fx/adapters/postgres/query_executor.rb +34 -0
  13. data/lib/fx/adapters/postgres/triggers.rb +14 -29
  14. data/lib/fx/adapters/postgres.rb +13 -13
  15. data/lib/fx/command_recorder.rb +5 -3
  16. data/lib/fx/configuration.rb +2 -2
  17. data/lib/fx/definition.rb +7 -5
  18. data/lib/fx/schema_dumper.rb +5 -17
  19. data/lib/fx/statements.rb +61 -58
  20. data/lib/fx/version.rb +1 -1
  21. data/lib/fx.rb +1 -1
  22. data/lib/generators/fx/function/function_generator.rb +48 -55
  23. data/lib/generators/fx/function/templates/db/migrate/create_function.erb +1 -1
  24. data/lib/generators/fx/function/templates/db/migrate/update_function.erb +1 -1
  25. data/lib/generators/fx/migration_helper.rb +45 -0
  26. data/lib/generators/fx/name_helper.rb +33 -0
  27. data/lib/generators/fx/trigger/templates/db/migrate/create_trigger.erb +1 -1
  28. data/lib/generators/fx/trigger/templates/db/migrate/update_trigger.erb +1 -1
  29. data/lib/generators/fx/trigger/trigger_generator.rb +43 -66
  30. data/lib/generators/fx/version_helper.rb +55 -0
  31. data/spec/acceptance/user_manages_functions_spec.rb +6 -6
  32. data/spec/acceptance/user_manages_triggers_spec.rb +10 -10
  33. data/spec/acceptance_helper.rb +17 -14
  34. data/spec/dummy/config/application.rb +1 -5
  35. data/spec/features/functions/migrations_spec.rb +4 -4
  36. data/spec/features/functions/revert_spec.rb +4 -4
  37. data/spec/features/triggers/migrations_spec.rb +6 -6
  38. data/spec/features/triggers/revert_spec.rb +8 -8
  39. data/spec/fx/adapters/postgres/functions_spec.rb +12 -5
  40. data/spec/fx/adapters/postgres/query_executor_spec.rb +75 -0
  41. data/spec/fx/adapters/postgres/triggers_spec.rb +14 -7
  42. data/spec/fx/adapters/postgres_spec.rb +62 -20
  43. data/spec/fx/definition_spec.rb +6 -6
  44. data/spec/fx/schema_dumper_spec.rb +132 -14
  45. data/spec/fx_spec.rb +1 -1
  46. data/spec/generators/fx/function/function_generator_spec.rb +10 -10
  47. data/spec/generators/fx/migration_helper_spec.rb +108 -0
  48. data/spec/generators/fx/name_helper_spec.rb +114 -0
  49. data/spec/generators/fx/trigger/trigger_generator_spec.rb +42 -19
  50. data/spec/generators/fx/version_helper_spec.rb +157 -0
  51. data/spec/spec_helper.rb +11 -4
  52. data/spec/support/database_reset.rb +24 -0
  53. data/spec/support/generator_setup.rb +46 -5
  54. metadata +30 -12
@@ -1,39 +1,43 @@
1
1
  require "rails/generators"
2
2
  require "rails/generators/active_record"
3
+ require "generators/fx/version_helper"
4
+ require "generators/fx/migration_helper"
5
+ require "generators/fx/name_helper"
3
6
 
4
7
  module Fx
5
8
  module Generators
6
9
  # @api private
7
10
  class TriggerGenerator < Rails::Generators::NamedBase
8
11
  include Rails::Generators::Migration
12
+
9
13
  source_root File.expand_path("../templates", __FILE__)
10
14
  argument :table_name, type: :hash, required: true
11
15
 
16
+ DEFINITION_PATH = %w[db triggers].freeze
17
+
12
18
  class_option :migration, type: :boolean
13
19
 
14
20
  def create_triggers_directory
15
- unless trigger_definition_path.exist?
16
- empty_directory(trigger_definition_path)
17
- end
21
+ return if trigger_definition_path.exist?
22
+
23
+ empty_directory(trigger_definition_path)
18
24
  end
19
25
 
20
26
  def create_trigger_definition
21
- create_file definition.path
27
+ create_file(definition.path)
22
28
  end
23
29
 
24
30
  def create_migration_file
25
- return if skip_migration_creation?
26
- if updating_existing_trigger?
27
- migration_template(
28
- "db/migrate/update_trigger.erb",
29
- "db/migrate/update_trigger_#{file_name}_to_version_#{version}.rb"
30
- )
31
- else
32
- migration_template(
33
- "db/migrate/create_trigger.erb",
34
- "db/migrate/create_trigger_#{file_name}.rb"
35
- )
36
- end
31
+ return if migration_helper.skip_creation?
32
+
33
+ template_info = migration_helper.migration_template_info(
34
+ object_type: :trigger,
35
+ file_name: file_name,
36
+ updating_existing: version_helper.updating_existing?,
37
+ version: version_helper.current_version
38
+ )
39
+
40
+ migration_template(template_info[:template], template_info[:filename])
37
41
  end
38
42
 
39
43
  def self.next_migration_number(dir)
@@ -42,84 +46,57 @@ module Fx
42
46
 
43
47
  no_tasks do
44
48
  def previous_version
45
- @_previous_version ||= Dir.entries(trigger_definition_path)
46
- .map { |name| version_regex.match(name).try(:[], "version").to_i }
47
- .max
49
+ version_helper.previous_version
48
50
  end
49
51
 
50
52
  def version
51
- @_version ||= previous_version.next
53
+ version_helper.current_version
52
54
  end
53
55
 
54
56
  def migration_class_name
55
- if updating_existing_trigger?
56
- "UpdateTrigger#{class_name}ToVersion#{version}"
57
+ if version_helper.updating_existing?
58
+ migration_helper.update_migration_class_name(
59
+ object_type: :trigger,
60
+ class_name: class_name,
61
+ version: version
62
+ )
57
63
  else
58
64
  super
59
65
  end
60
66
  end
61
67
 
62
- def activerecord_migration_class
63
- if ActiveRecord::Migration.respond_to?(:current_version)
64
- "ActiveRecord::Migration[#{ActiveRecord::Migration.current_version}]"
65
- else
66
- "ActiveRecord::Migration"
67
- end
68
- end
69
-
70
68
  def formatted_name
71
- if singular_name.include?(".")
72
- "\"#{singular_name}\""
73
- else
74
- ":#{singular_name}"
75
- end
69
+ Fx::Generators::NameHelper.format_for_migration(singular_name)
76
70
  end
77
71
 
78
72
  def formatted_table_name
79
- name = table_name["table_name"] || table_name["on"]
80
-
81
- if name.nil?
82
- raise(
83
- ArgumentError,
84
- "Either `table_name:NAME` or `on:NAME` must be specified"
85
- )
86
- end
87
-
88
- if name.include?(".")
89
- "\"#{name}\""
90
- else
91
- ":#{name}"
92
- end
73
+ Fx::Generators::NameHelper.format_table_name_from_hash(table_name)
93
74
  end
94
75
  end
95
76
 
96
77
  private
97
78
 
98
- def version_regex
99
- /\A#{file_name}_v(?<version>\d+)\.sql\z/
100
- end
101
-
102
- def updating_existing_trigger?
103
- previous_version > 0
79
+ def trigger_definition_path
80
+ @_trigger_definition_path ||= Rails.root.join(*DEFINITION_PATH)
104
81
  end
105
82
 
106
- def definition
107
- Fx::Definition.trigger(name: file_name, version: version)
83
+ def version_helper
84
+ @_version_helper ||= Fx::Generators::VersionHelper.new(
85
+ file_name: file_name,
86
+ definition_path: trigger_definition_path
87
+ )
108
88
  end
109
89
 
110
- def trigger_definition_path
111
- @_trigger_definition_path ||= Rails.root.join("db", "triggers")
90
+ def migration_helper
91
+ @_migration_helper ||= Fx::Generators::MigrationHelper.new(options)
112
92
  end
113
93
 
114
- # Skip creating migration file if:
115
- # - migrations option is nil or false
116
- def skip_migration_creation?
117
- !migration
94
+ def definition
95
+ version_helper.definition_for_version(version: version, type: :trigger)
118
96
  end
119
97
 
120
- # True unless explicitly false
121
- def migration
122
- options[:migration] != false
98
+ def updating_existing_trigger?
99
+ version_helper.updating_existing?
123
100
  end
124
101
  end
125
102
  end
@@ -0,0 +1,55 @@
1
+ module Fx
2
+ module Generators
3
+ # @api private
4
+ class VersionHelper
5
+ def initialize(file_name:, definition_path:)
6
+ @file_name = file_name
7
+ @definition_path = definition_path
8
+ end
9
+
10
+ def previous_version
11
+ @previous_version ||= existing_versions.max || 0
12
+ end
13
+
14
+ def current_version
15
+ previous_version.next
16
+ end
17
+
18
+ def updating_existing?
19
+ previous_version > 0
20
+ end
21
+
22
+ def creating_new?
23
+ previous_version == 0
24
+ end
25
+
26
+ def definition_for_version(version:, type:)
27
+ case type
28
+ when :function
29
+ Fx::Definition.function(name: file_name, version: version)
30
+ when :trigger
31
+ Fx::Definition.trigger(name: file_name, version: version)
32
+ else
33
+ raise(
34
+ ArgumentError,
35
+ "Unknown type: #{type}. Must be :function or :trigger"
36
+ )
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ VERSION_PATTERN = /v(\d+)/
43
+ private_constant :VERSION_PATTERN
44
+
45
+ attr_reader :file_name, :definition_path
46
+
47
+ def existing_versions
48
+ Dir
49
+ .glob("#{file_name}_v*.sql", base: definition_path)
50
+ .map { |file| file[VERSION_PATTERN, 1].to_i }
51
+ .compact
52
+ end
53
+ end
54
+ end
55
+ end
@@ -3,14 +3,14 @@ require "acceptance_helper"
3
3
  RSpec.describe "User manages functions" do
4
4
  it "handles simple functions" do
5
5
  successfully "rails generate fx:function test"
6
- write_function_definition "test_v01", <<~EOS
6
+ write_function_definition "test_v01", <<~SQL
7
7
  CREATE OR REPLACE FUNCTION test()
8
8
  RETURNS text AS $$
9
9
  BEGIN
10
10
  RETURN 'test';
11
11
  END;
12
12
  $$ LANGUAGE plpgsql;
13
- EOS
13
+ SQL
14
14
  successfully "rake db:migrate"
15
15
 
16
16
  result = execute("SELECT * FROM test() AS result")
@@ -21,14 +21,14 @@ RSpec.describe "User manages functions" do
21
21
  "db/functions/test_v01.sql",
22
22
  "db/functions/test_v02.sql"
23
23
  )
24
- write_function_definition "test_v02", <<~EOS
24
+ write_function_definition "test_v02", <<~SQL
25
25
  CREATE OR REPLACE FUNCTION test()
26
26
  RETURNS text AS $$
27
27
  BEGIN
28
28
  RETURN 'testest';
29
29
  END;
30
30
  $$ LANGUAGE plpgsql;
31
- EOS
31
+ SQL
32
32
  successfully "rake db:migrate"
33
33
 
34
34
  result = execute("SELECT * FROM test() AS result")
@@ -37,14 +37,14 @@ RSpec.describe "User manages functions" do
37
37
 
38
38
  it "handles functions with arguments" do
39
39
  successfully "rails generate fx:function adder"
40
- write_function_definition "adder_v01", <<~EOS
40
+ write_function_definition "adder_v01", <<~SQL
41
41
  CREATE FUNCTION adder(x int, y int)
42
42
  RETURNS int AS $$
43
43
  BEGIN
44
44
  RETURN $1 + $2;
45
45
  END;
46
46
  $$ LANGUAGE plpgsql;
47
- EOS
47
+ SQL
48
48
  successfully "rake db:migrate"
49
49
 
50
50
  result = execute("SELECT * FROM adder(1, 2) AS result")
@@ -4,7 +4,7 @@ RSpec.describe "User manages triggers" do
4
4
  it "handles simple triggers" do
5
5
  successfully "rails generate model user name:string upper_name:string"
6
6
  successfully "rails generate fx:function uppercase_users_name"
7
- write_function_definition "uppercase_users_name_v01", <<~EOS
7
+ write_function_definition "uppercase_users_name_v01", <<~SQL
8
8
  CREATE OR REPLACE FUNCTION uppercase_users_name()
9
9
  RETURNS trigger AS $$
10
10
  BEGIN
@@ -12,38 +12,38 @@ RSpec.describe "User manages triggers" do
12
12
  RETURN NEW;
13
13
  END;
14
14
  $$ LANGUAGE plpgsql;
15
- EOS
15
+ SQL
16
16
  successfully "rails generate fx:trigger uppercase_users_name table_name:users"
17
- write_trigger_definition "uppercase_users_name_v01", <<~EOS
17
+ write_trigger_definition "uppercase_users_name_v01", <<~SQL
18
18
  CREATE TRIGGER uppercase_users_name
19
19
  BEFORE INSERT ON users
20
20
  FOR EACH ROW
21
21
  EXECUTE FUNCTION uppercase_users_name();
22
- EOS
22
+ SQL
23
23
  successfully "rake db:migrate"
24
24
 
25
- execute <<~EOS
25
+ execute <<~SQL
26
26
  INSERT INTO users
27
27
  (name, created_at, updated_at)
28
28
  VALUES
29
29
  ('Bob', NOW(), NOW());
30
- EOS
30
+ SQL
31
31
  result = execute("SELECT upper_name FROM users WHERE name = 'Bob';")
32
32
  expect(result).to eq("upper_name" => "BOB")
33
33
 
34
34
  successfully "rails generate fx:trigger uppercase_users_name table_name:users"
35
- write_trigger_definition "uppercase_users_name_v02", <<~EOS
35
+ write_trigger_definition "uppercase_users_name_v02", <<~SQL
36
36
  CREATE TRIGGER uppercase_users_name
37
37
  BEFORE UPDATE ON users
38
38
  FOR EACH ROW
39
39
  EXECUTE FUNCTION uppercase_users_name();
40
- EOS
40
+ SQL
41
41
  successfully "rake db:migrate"
42
- execute <<~EOS
42
+ execute <<~SQL
43
43
  UPDATE users
44
44
  SET name = 'Alice'
45
45
  WHERE id = 1;
46
- EOS
46
+ SQL
47
47
 
48
48
  result = execute("SELECT upper_name FROM users WHERE name = 'Alice';")
49
49
  expect(result).to eq("upper_name" => "ALICE")
@@ -5,32 +5,35 @@ ENV["RAILS_ENV"] = "test"
5
5
  RSpec.configure do |config|
6
6
  config.around(:each) do |example|
7
7
  Dir.chdir("spec/dummy") do
8
+ DatabaseReset.call
9
+
8
10
  example.run
11
+
12
+ DatabaseReset.call
9
13
  end
10
14
  end
11
15
 
12
16
  config.before(:suite) do
13
17
  Dir.chdir("spec/dummy") do
14
- system <<~CMD
15
- git init -b master 1>/dev/null &&
16
- git config user.email "fx@example.com"
17
- git config user.name "Fx"
18
- git add -A &&
19
- git commit --no-gpg-sign --message 'initial' 1>/dev/null
20
- CMD
18
+ system [
19
+ "git init -b master 1>/dev/null",
20
+ "git config user.email 'fx@example.com'",
21
+ "git config user.name 'Fx'",
22
+ "git add -A",
23
+ "git commit --no-gpg-sign --message 'initial' 1>/dev/null"
24
+ ].join(" && ")
21
25
  end
22
26
  end
23
27
 
24
28
  config.after(:suite) do
25
29
  Dir.chdir("spec/dummy") do
26
30
  ActiveRecord::Base.connection.disconnect!
27
- system <<~CMD
28
- echo &&
29
- rake db:environment:set db:drop db:create 1>/dev/null &&
30
- git add -A &&
31
- git reset --hard HEAD 1>/dev/null &&
32
- rm -rf .git/ 1>/dev/null
33
- CMD
31
+ DatabaseReset.call
32
+ system [
33
+ "git add -A",
34
+ "git reset --hard HEAD 1>/dev/null",
35
+ "rm -rf .git/ 1>/dev/null"
36
+ ].join(" && ")
34
37
  end
35
38
  end
36
39
 
@@ -12,10 +12,6 @@ module Dummy
12
12
  config.eager_load = false
13
13
  config.active_support.deprecation = :stderr
14
14
 
15
- config.load_defaults 7.0
16
-
17
- if Rails.version >= "8.0"
18
- config.active_support.to_time_preserves_timezone = :zone
19
- end
15
+ config.load_defaults 7.2
20
16
  end
21
17
  end
@@ -2,14 +2,14 @@ require "spec_helper"
2
2
 
3
3
  RSpec.describe "Function migrations", :db do
4
4
  around do |example|
5
- sql_definition = <<~EOS
5
+ sql_definition = <<~SQL
6
6
  CREATE OR REPLACE FUNCTION test()
7
7
  RETURNS text AS $$
8
8
  BEGIN
9
9
  RETURN 'test';
10
10
  END;
11
11
  $$ LANGUAGE plpgsql;
12
- EOS
12
+ SQL
13
13
  with_function_definition(name: :test, sql_definition: sql_definition) do
14
14
  example.run
15
15
  end
@@ -40,14 +40,14 @@ RSpec.describe "Function migrations", :db do
40
40
  it "can run migrations that updates functions" do
41
41
  connection.create_function(:test)
42
42
 
43
- sql_definition = <<~EOS
43
+ sql_definition = <<~SQL
44
44
  CREATE OR REPLACE FUNCTION test()
45
45
  RETURNS text AS $$
46
46
  BEGIN
47
47
  RETURN 'testest';
48
48
  END;
49
49
  $$ LANGUAGE plpgsql;
50
- EOS
50
+ SQL
51
51
  with_function_definition(
52
52
  name: :test,
53
53
  version: 2,
@@ -2,14 +2,14 @@ require "spec_helper"
2
2
 
3
3
  RSpec.describe "Reverting migrations", :db do
4
4
  around do |example|
5
- sql_definition = <<~EOS
5
+ sql_definition = <<~SQL
6
6
  CREATE OR REPLACE FUNCTION test()
7
7
  RETURNS text AS $$
8
8
  BEGIN
9
9
  RETURN 'test';
10
10
  END;
11
11
  $$ LANGUAGE plpgsql;
12
- EOS
12
+ SQL
13
13
  with_function_definition(name: :test, sql_definition: sql_definition) do
14
14
  example.run
15
15
  end
@@ -50,14 +50,14 @@ RSpec.describe "Reverting migrations", :db do
50
50
  it "can run reversible migrations for updating functions" do
51
51
  connection.create_function(:test)
52
52
 
53
- sql_definition = <<~EOS
53
+ sql_definition = <<~SQL
54
54
  CREATE OR REPLACE FUNCTION test()
55
55
  RETURNS text AS $$
56
56
  BEGIN
57
57
  RETURN 'bar';
58
58
  END;
59
59
  $$ LANGUAGE plpgsql;
60
- EOS
60
+ SQL
61
61
  with_function_definition(
62
62
  name: :test,
63
63
  version: 2,
@@ -2,14 +2,14 @@ require "spec_helper"
2
2
 
3
3
  RSpec.describe "Trigger migrations", :db do
4
4
  around do |example|
5
- connection.execute <<~EOS
5
+ connection.execute <<~SQL
6
6
  CREATE TABLE users (
7
7
  id int PRIMARY KEY,
8
8
  name varchar(256),
9
9
  upper_name varchar(256)
10
10
  );
11
- EOS
12
- Fx.database.create_function <<~EOS
11
+ SQL
12
+ Fx.database.create_function <<~SQL
13
13
  CREATE OR REPLACE FUNCTION uppercase_users_name()
14
14
  RETURNS trigger AS $$
15
15
  BEGIN
@@ -17,13 +17,13 @@ RSpec.describe "Trigger migrations", :db do
17
17
  RETURN NEW;
18
18
  END;
19
19
  $$ LANGUAGE plpgsql;
20
- EOS
21
- sql_definition = <<~EOS
20
+ SQL
21
+ sql_definition = <<~SQL
22
22
  CREATE TRIGGER uppercase_users_name
23
23
  BEFORE INSERT ON users
24
24
  FOR EACH ROW
25
25
  EXECUTE FUNCTION uppercase_users_name();
26
- EOS
26
+ SQL
27
27
  with_trigger_definition(
28
28
  name: :uppercase_users_name,
29
29
  sql_definition: sql_definition
@@ -2,14 +2,14 @@ require "spec_helper"
2
2
 
3
3
  RSpec.describe "Reverting migrations", :db do
4
4
  around do |example|
5
- connection.execute <<~EOS
5
+ connection.execute <<~SQL
6
6
  CREATE TABLE users (
7
7
  id int PRIMARY KEY,
8
8
  name varchar(256),
9
9
  upper_name varchar(256)
10
10
  );
11
- EOS
12
- Fx.database.create_function <<~EOS
11
+ SQL
12
+ Fx.database.create_function <<~SQL
13
13
  CREATE OR REPLACE FUNCTION uppercase_users_name()
14
14
  RETURNS trigger AS $$
15
15
  BEGIN
@@ -17,13 +17,13 @@ RSpec.describe "Reverting migrations", :db do
17
17
  RETURN NEW;
18
18
  END;
19
19
  $$ LANGUAGE plpgsql;
20
- EOS
21
- sql_definition = <<~EOS
20
+ SQL
21
+ sql_definition = <<~SQL
22
22
  CREATE TRIGGER uppercase_users_name
23
23
  BEFORE INSERT ON users
24
24
  FOR EACH ROW
25
25
  EXECUTE FUNCTION uppercase_users_name();
26
- EOS
26
+ SQL
27
27
  with_trigger_definition(
28
28
  name: :uppercase_users_name,
29
29
  sql_definition: sql_definition
@@ -67,12 +67,12 @@ RSpec.describe "Reverting migrations", :db do
67
67
  it "can run reversible migrations for updating triggers" do
68
68
  connection.create_trigger(:uppercase_users_name)
69
69
 
70
- sql_definition = <<~EOS
70
+ sql_definition = <<~SQL
71
71
  CREATE TRIGGER uppercase_users_name
72
72
  BEFORE UPDATE ON users
73
73
  FOR EACH ROW
74
74
  EXECUTE FUNCTION uppercase_users_name();
75
- EOS
75
+ SQL
76
76
  with_trigger_definition(
77
77
  name: :uppercase_users_name,
78
78
  sql_definition: sql_definition,
@@ -4,21 +4,21 @@ RSpec.describe Fx::Adapters::Postgres::Functions, :db do
4
4
  describe ".all" do
5
5
  it "returns `Function` objects" do
6
6
  connection = ActiveRecord::Base.connection
7
- connection.execute <<~EOS
7
+ connection.execute <<~SQL
8
8
  CREATE OR REPLACE FUNCTION test()
9
9
  RETURNS text AS $$
10
10
  BEGIN
11
11
  RETURN 'test';
12
12
  END;
13
13
  $$ LANGUAGE plpgsql;
14
- EOS
14
+ SQL
15
15
 
16
- functions = Fx::Adapters::Postgres::Functions.new(connection).all
16
+ functions = Fx::Adapters::Postgres::Functions.all(connection)
17
17
 
18
18
  first = functions.first
19
19
  expect(functions.size).to eq(1)
20
20
  expect(first.name).to eq("test")
21
- expect(first.definition).to eq(<<~EOS)
21
+ expect(first.definition).to eq(<<~SQL)
22
22
  CREATE OR REPLACE FUNCTION public.test()
23
23
  RETURNS text
24
24
  LANGUAGE plpgsql
@@ -27,7 +27,14 @@ RSpec.describe Fx::Adapters::Postgres::Functions, :db do
27
27
  RETURN 'test';
28
28
  END;
29
29
  $function$
30
- EOS
30
+ SQL
31
+
32
+ connection.execute "CREATE SCHEMA IF NOT EXISTS other;"
33
+ connection.execute "SET search_path = 'other';"
34
+
35
+ functions = Fx::Adapters::Postgres::Functions.all(connection)
36
+
37
+ expect(functions).to be_empty
31
38
  end
32
39
  end
33
40
  end
@@ -0,0 +1,75 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe Fx::Adapters::Postgres::QueryExecutor, :db do
4
+ it "executes the query and maps results to objects" do
5
+ connection = ActiveRecord::Base.connection
6
+ query = "SELECT 'Hello World' as message, 'english' as language"
7
+ greeter = Class.new do
8
+ attr_reader :message, :language
9
+
10
+ def initialize(row)
11
+ @message = row.fetch("message")
12
+ @language = row.fetch("language")
13
+ end
14
+ end
15
+
16
+ results = described_class.call(
17
+ connection: connection,
18
+ query: query,
19
+ model_class: greeter
20
+ )
21
+
22
+ expect(results.size).to eq(1)
23
+ expect(results.first).to be_a(greeter)
24
+ expect(results.first.message).to eq("Hello World")
25
+ expect(results.first.language).to eq("english")
26
+ end
27
+
28
+ it "executes query with multiple results" do
29
+ connection = ActiveRecord::Base.connection
30
+ query = <<~SQL
31
+ SELECT 'first' as name
32
+ UNION ALL
33
+ SELECT 'second' as name
34
+ ORDER BY name
35
+ SQL
36
+ simple_name = Class.new do
37
+ attr_reader :name
38
+
39
+ def initialize(row)
40
+ @name = row.fetch("name")
41
+ end
42
+ end
43
+
44
+ results = described_class.call(
45
+ connection: connection,
46
+ query: query,
47
+ model_class: simple_name
48
+ )
49
+
50
+ expect(results.size).to eq(2)
51
+ expect(results).to all(be_a(simple_name))
52
+ expect(results.first.name).to eq("first")
53
+ expect(results.last.name).to eq("second")
54
+ end
55
+
56
+ it "returns an empty array when query returns no results" do
57
+ connection = ActiveRecord::Base.connection
58
+ query = "SELECT 'test' as name WHERE 1 = 0"
59
+ simple_name = Class.new do
60
+ attr_reader :name
61
+
62
+ def initialize(row)
63
+ @name = row.fetch("name")
64
+ end
65
+ end
66
+
67
+ results = described_class.call(
68
+ connection: connection,
69
+ query: query,
70
+ model_class: simple_name
71
+ )
72
+
73
+ expect(results).to eq([])
74
+ end
75
+ end