sequelizer 0.1.6 → 0.2.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/.beads/.gitignore +54 -0
  3. data/.beads/.jsonl.lock +0 -0
  4. data/.beads/.migration-hint-ts +1 -0
  5. data/.beads/README.md +81 -0
  6. data/.beads/config.yaml +42 -0
  7. data/.beads/issues.jsonl +20 -0
  8. data/.beads/metadata.json +7 -0
  9. data/.coderabbit.yaml +94 -0
  10. data/.github/dependabot.yml +18 -0
  11. data/.github/workflows/dependabot-auto-merge.yml +36 -0
  12. data/.github/workflows/test.yml +44 -9
  13. data/AGENTS.md +126 -0
  14. data/CHANGELOG.md +17 -0
  15. data/CLAUDE.md +11 -0
  16. data/Gemfile +1 -1
  17. data/Gemfile.lock +84 -53
  18. data/config/platforms/base.csv +5 -0
  19. data/config/platforms/rdbms/athena.csv +4 -0
  20. data/config/platforms/rdbms/postgres.csv +3 -0
  21. data/config/platforms/rdbms/snowflake.csv +1 -0
  22. data/config/platforms/rdbms/spark.csv +3 -0
  23. data/lib/sequel/extensions/cold_col.rb +2 -2
  24. data/lib/sequel/extensions/funky.rb +136 -0
  25. data/lib/sequel/extensions/make_readyable.rb +0 -2
  26. data/lib/sequel/extensions/platform.rb +301 -0
  27. data/lib/sequelizer/options.rb +5 -1
  28. data/lib/sequelizer/version.rb +1 -1
  29. data/lib/sequelizer/yaml_config.rb +0 -1
  30. data/sequelizer.gemspec +10 -7
  31. data/test/lib/sequel/extensions/test_cold_col.rb +2 -2
  32. data/test/lib/sequel/extensions/test_make_readyable.rb +0 -1
  33. data/test/lib/sequel/extensions/test_platform.rb +222 -0
  34. data/test/lib/sequelizer/test_connection_maker.rb +2 -2
  35. data/test/lib/sequelizer/test_gemfile_modifier.rb +2 -2
  36. data/test/lib/sequelizer/test_options.rb +28 -0
  37. data/test/test_helper.rb +1 -0
  38. metadata +97 -19
  39. data/.claude/settings.local.json +0 -64
@@ -0,0 +1,301 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # The platform extension provides a unified interface for platform-specific
5
+ # database behavior. It uses KVCSV configuration files to define capabilities
6
+ # and preferences, with Ruby classes only for function translations.
7
+ #
8
+ # DB.extension :platform
9
+ # DB.platform.supports?(:cte) # => true (from config)
10
+ # DB.platform.prefers?(:cte) # => true (from config)
11
+ # DB.platform[:interim_schema] # => nil (from config)
12
+ # DB.platform.date_diff(from, to) # => Sequel expression (from code)
13
+ #
14
+ # Configuration is loaded from CSV files in config/platforms/:
15
+ # - base.csv: conservative defaults
16
+ # - rdbms/<adapter>.csv: RDBMS-specific overrides
17
+ # - Additional configs can be stacked via platform_configs option
18
+ #
19
+ # Related module: Sequel::Platform
20
+
21
+ require 'kvcsv'
22
+
23
+ module Sequel
24
+
25
+ # The Platform module provides database platform abstraction through
26
+ # configuration-driven capabilities and code-driven function translations.
27
+ module Platform
28
+
29
+ # Base platform class with KVCSV config loading and default implementations.
30
+ # Subclasses override function translation methods for platform-specific SQL.
31
+ class Base
32
+
33
+ attr_reader :db, :config
34
+
35
+ # Initialize platform with database connection and config paths.
36
+ #
37
+ # @param db [Sequel::Database] The database connection
38
+ # @param config_paths [Array<String>] Paths to CSV config files (stacked in order)
39
+ def initialize(db, *config_paths)
40
+ @db = db
41
+ @config = config_paths.empty? ? {} : KVCSV::Settings.new(*config_paths)
42
+ end
43
+
44
+ # Check if the platform supports a feature.
45
+ #
46
+ # @param feature [Symbol] Feature name (e.g., :cte, :temp_tables)
47
+ # @return [Boolean] true if supported
48
+ #
49
+ # @example
50
+ # platform.supports?(:cte) # checks supports_cte in config
51
+ # platform.supports?(:temp_tables) # checks supports_temp_tables in config
52
+ def supports?(feature)
53
+ config[:"supports_#{feature}"] || false
54
+ end
55
+
56
+ # Check if the platform prefers a feature (may support but not prefer).
57
+ #
58
+ # @param feature [Symbol] Feature name (e.g., :cte, :parquet)
59
+ # @return [Boolean] true if preferred
60
+ #
61
+ # @example
62
+ # platform.prefers?(:cte) # Spark supports CTEs but doesn't prefer them
63
+ # platform.prefers?(:parquet) # Spark prefers parquet format
64
+ def prefers?(feature)
65
+ config[:"prefers_#{feature}"] || false
66
+ end
67
+
68
+ # Access arbitrary config values.
69
+ #
70
+ # @param key [Symbol] Config key
71
+ # @return [Object] Config value or nil
72
+ #
73
+ # @example
74
+ # platform[:interim_schema] # => "scratch"
75
+ # platform[:schema_switching_method] # => "use"
76
+ def [](key)
77
+ config[key]
78
+ end
79
+
80
+ # Fetch config value with default.
81
+ #
82
+ # @param key [Symbol] Config key
83
+ # @param default [Object] Default value if key not found
84
+ # @return [Object] Config value or default
85
+ def fetch(key, default = nil)
86
+ config.respond_to?(:fetch) ? config.fetch(key, default) : (config[key] || default)
87
+ end
88
+
89
+ # ---- Function translations (override in subclasses) ----
90
+
91
+ # Calculate date difference between two dates.
92
+ #
93
+ # @param from [Symbol, Sequel::SQL::Expression] Start date
94
+ # @param to [Symbol, Sequel::SQL::Expression] End date
95
+ # @return [Sequel::SQL::Expression] Date difference expression
96
+ def date_diff(from, to)
97
+ Sequel.function(:datediff, from, to)
98
+ end
99
+
100
+ # Cast expression to date type.
101
+ #
102
+ # @param expr [Object] Expression to cast
103
+ # @return [Sequel::SQL::Cast] Cast expression
104
+ def cast_date(expr)
105
+ Sequel.cast(expr, Date)
106
+ end
107
+
108
+ # Parse string to date with format.
109
+ #
110
+ # @param value [Object] String value to parse
111
+ # @param format [String] Date format string
112
+ # @return [Sequel::SQL::Expression] Parsed date expression
113
+ def str_to_date(value, format)
114
+ Sequel.function(:to_date, value, format)
115
+ end
116
+
117
+ # Calculate days between two dates.
118
+ #
119
+ # @param from [Symbol, Sequel::SQL::Expression] Start date
120
+ # @param to [Symbol, Sequel::SQL::Expression] End date
121
+ # @return [Sequel::SQL::Expression] Days between expression
122
+ def days_between(from, to)
123
+ date_diff(from, to)
124
+ end
125
+
126
+ end
127
+
128
+ # PostgreSQL platform with Postgres-specific function translations.
129
+ class Postgres < Base
130
+
131
+ def date_diff(from, to)
132
+ # Postgres uses date subtraction
133
+ Sequel.lit('(? - ?)', to, from)
134
+ end
135
+
136
+ def days_between(from, to)
137
+ # Postgres date subtraction returns integer days
138
+ Sequel.lit('(? - ?)', to, from)
139
+ end
140
+
141
+ end
142
+
143
+ # Spark platform with Spark SQL-specific function translations.
144
+ class Spark < Base
145
+
146
+ def date_diff(from, to)
147
+ # Spark datediff has reversed argument order (end, start)
148
+ Sequel.function(:datediff, to, from)
149
+ end
150
+
151
+ def str_to_date(value, format)
152
+ Sequel.function(:to_date, Sequel.cast_string(value), format)
153
+ end
154
+
155
+ end
156
+
157
+ # Snowflake platform with Snowflake-specific function translations.
158
+ class Snowflake < Base
159
+
160
+ def date_diff(from, to)
161
+ # Snowflake requires unit parameter
162
+ Sequel.function(:datediff, 'day', from, to)
163
+ end
164
+
165
+ def days_between(from, to)
166
+ Sequel.function(:datediff, 'day', from, to)
167
+ end
168
+
169
+ end
170
+
171
+ # Athena platform (Presto/Trino based) with Athena-specific function translations.
172
+ class Athena < Base
173
+
174
+ def date_diff(from, to)
175
+ # Athena/Presto uses date_diff with unit
176
+ Sequel.function(:date_diff, 'day', from, to)
177
+ end
178
+
179
+ def days_between(from, to)
180
+ Sequel.function(:date_diff, 'day', from, to)
181
+ end
182
+
183
+ end
184
+
185
+ # Map adapter schemes to platform classes
186
+ PLATFORM_CLASSES = {
187
+ postgres: Postgres,
188
+ postgresql: Postgres,
189
+ spark: Spark,
190
+ athena: Athena,
191
+ presto: Athena,
192
+ trino: Athena,
193
+ snowflake: Snowflake,
194
+ }.freeze
195
+
196
+ # Map adapter schemes to config file names
197
+ ADAPTER_CONFIG_NAMES = {
198
+ postgres: 'postgres',
199
+ postgresql: 'postgres',
200
+ spark: 'spark',
201
+ athena: 'athena',
202
+ presto: 'athena',
203
+ trino: 'athena',
204
+ snowflake: 'snowflake',
205
+ }.freeze
206
+
207
+ class << self
208
+
209
+ # Find the config directory, searching gem paths
210
+ def config_dir
211
+ @config_dir ||= find_config_dir
212
+ end
213
+
214
+ # Allow overriding config dir for testing
215
+ attr_writer :config_dir
216
+
217
+ private
218
+
219
+ def find_config_dir
220
+ # Check relative to this file (gem's config)
221
+ gem_config = File.expand_path('../../../config/platforms', __dir__)
222
+ return gem_config if File.directory?(gem_config)
223
+
224
+ # Fallback to working directory
225
+ local_config = File.join(Dir.pwd, 'config/platforms')
226
+ return local_config if File.directory?(local_config)
227
+
228
+ nil
229
+ end
230
+
231
+ end
232
+
233
+ # Build config paths for the given adapter
234
+ #
235
+ # @param adapter_scheme [Symbol] Database adapter scheme
236
+ # @param extra_configs [Array<String>] Additional config paths to stack
237
+ # @return [Array<String>] Ordered config paths
238
+ def self.config_paths_for(adapter_scheme, extra_configs = [])
239
+ paths = []
240
+
241
+ if config_dir
242
+ base_config = File.join(config_dir, 'base.csv')
243
+ paths << base_config if File.exist?(base_config)
244
+
245
+ adapter_name = ADAPTER_CONFIG_NAMES[adapter_scheme]
246
+ if adapter_name
247
+ rdbms_config = File.join(config_dir, 'rdbms', "#{adapter_name}.csv")
248
+ paths << rdbms_config if File.exist?(rdbms_config)
249
+ end
250
+ end
251
+
252
+ paths.concat(extra_configs.select { |p| File.exist?(p) })
253
+ paths
254
+ end
255
+
256
+ # Build platform instance for database
257
+ #
258
+ # @param db [Sequel::Database] Database connection
259
+ # @param extra_configs [Array<String>] Additional config paths
260
+ # @return [Platform::Base] Platform instance
261
+ def self.build_platform(db, extra_configs = [])
262
+ adapter = effective_adapter(db)
263
+ platform_class = PLATFORM_CLASSES.fetch(adapter, Base)
264
+ config_paths = config_paths_for(adapter, extra_configs)
265
+ platform_class.new(db, *config_paths)
266
+ end
267
+
268
+ # Detect the effective adapter for platform selection.
269
+ # For mock databases, use database_type or host option; otherwise use adapter_scheme.
270
+ #
271
+ # @param db [Sequel::Database] Database connection
272
+ # @return [Symbol] Effective adapter type
273
+ def self.effective_adapter(db)
274
+ return db.adapter_scheme unless db.adapter_scheme == :mock
275
+
276
+ # Mock databases: try database_type first (set for known types like postgres, spark)
277
+ db_type = db.database_type
278
+ return db_type if db_type && db_type != :mock
279
+
280
+ # Fall back to host option (for unknown types like snowflake, athena)
281
+ db.opts[:host]
282
+ end
283
+
284
+ # Extension hook - called when extension is loaded
285
+ def self.extended(db)
286
+ extra_configs = db.opts[:platform_configs] || []
287
+ db.instance_variable_set(:@platform, build_platform(db, extra_configs))
288
+ end
289
+
290
+ # Access the platform instance
291
+ #
292
+ # @return [Platform::Base] Platform instance for this database
293
+ def platform
294
+ @platform
295
+ end
296
+
297
+ end
298
+
299
+ Database.register_extension(:platform, Platform)
300
+
301
+ end
@@ -1,3 +1,4 @@
1
+ require 'uri'
1
2
  require_relative 'yaml_config'
2
3
  require_relative 'env_config'
3
4
  require_relative 'options_hash'
@@ -98,7 +99,10 @@ module Sequelizer
98
99
  opts = OptionsHash.new(passed_options || {}).to_hash
99
100
  sequelizer_options = db_config(opts).merge(opts)
100
101
 
101
- if sequelizer_options[:adapter] =~ /^postgres/
102
+ adapter = sequelizer_options[:adapter]
103
+ adapter ||= URI.parse(sequelizer_options[:url].to_s).scheme if sequelizer_options[:url]
104
+
105
+ if adapter =~ /^postgres/
102
106
  sequelizer_options[:adapter] = 'postgres'
103
107
  paths = %w[search_path schema_search_path schema].map { |key| sequelizer_options.delete(key) }.compact
104
108
 
@@ -1,6 +1,6 @@
1
1
  module Sequelizer
2
2
 
3
3
  # Version for the gem
4
- VERSION = '0.1.6'.freeze
4
+ VERSION = '0.2.0'.freeze
5
5
 
6
6
  end
@@ -1,5 +1,4 @@
1
1
  require 'psych'
2
- require 'pathname'
3
2
  require 'erb'
4
3
 
5
4
  module Sequelizer
data/sequelizer.gemspec CHANGED
@@ -17,18 +17,21 @@ Gem::Specification.new do |spec|
17
17
  spec.require_paths = ['lib']
18
18
  spec.required_ruby_version = '>= 3.2.0'
19
19
 
20
- spec.add_development_dependency 'bundler', '~> 2.0'
20
+ spec.add_development_dependency 'bundler', '>= 2.0'
21
21
  spec.add_development_dependency 'guard', '~> 2.0'
22
- spec.add_development_dependency 'guard-minitest', '~> 2.3'
23
- spec.add_development_dependency 'minitest', '~> 5.3'
22
+ spec.add_development_dependency 'guard-minitest', '~> 3.0'
23
+ spec.add_development_dependency 'minitest', '~> 6.0'
24
+ spec.add_development_dependency 'minitest-mock', '~> 5.27'
24
25
  spec.add_development_dependency 'pg', '~> 1.0'
25
- spec.add_development_dependency 'rake', '~> 12.0'
26
+ spec.add_development_dependency 'rake', '~> 13.3'
26
27
  spec.add_development_dependency 'rubocop', '~> 1.0'
27
28
  spec.add_development_dependency 'rubocop-minitest', '~> 0.25'
28
29
  spec.add_development_dependency 'simplecov', '~> 0.22'
29
- spec.add_dependency 'activesupport', '~> 7.0'
30
- spec.add_dependency 'dotenv', '~> 2.1'
31
- spec.add_dependency 'hashie', '~> 3.2'
30
+ spec.add_dependency 'activesupport', '>= 7', '< 9'
31
+ spec.add_dependency 'dotenv', '>= 2.1', '< 4.0'
32
+ spec.add_dependency 'hashie', '>= 3.2', '< 6.0'
33
+ spec.add_dependency 'kvcsv', '~> 0.1'
34
+ spec.add_dependency 'pp'
32
35
  spec.add_dependency 'sequel', '~> 5.93'
33
36
  spec.add_dependency 'thor', '~> 1.0'
34
37
  spec.metadata['rubygems_mfa_required'] = 'true'
@@ -19,8 +19,8 @@ describe Sequel::ColdColDatabase do
19
19
  end
20
20
 
21
21
  # Add with method to mock database to support CTEs
22
- def @db.with(*args)
23
- dataset.with(*args)
22
+ def @db.with(*)
23
+ dataset.with(*)
24
24
  end
25
25
 
26
26
  @db
@@ -1,7 +1,6 @@
1
1
  require_relative '../../../test_helper'
2
2
  require 'fileutils'
3
3
  require 'tmpdir'
4
- require 'pathname'
5
4
  require 'sequel'
6
5
  require 'sequel/extensions/make_readyable'
7
6
 
@@ -0,0 +1,222 @@
1
+ require_relative '../../../test_helper'
2
+ require 'sequel'
3
+ require 'sequel/extensions/platform'
4
+
5
+ class TestPlatform < Minitest::Test
6
+
7
+ def setup
8
+ # Point to the gem's config directory
9
+ @original_config_dir = Sequel::Platform.config_dir
10
+ Sequel::Platform.config_dir = File.expand_path('../../../../config/platforms', __dir__)
11
+ end
12
+
13
+ def teardown
14
+ Sequel::Platform.config_dir = @original_config_dir
15
+ end
16
+
17
+ # --- Extension Registration ---
18
+
19
+ def test_should_register_extension
20
+ db = Sequel.mock(host: :postgres)
21
+
22
+ assert_respond_to db, :extension
23
+
24
+ db.extension :platform
25
+
26
+ assert_respond_to db, :platform
27
+ end
28
+
29
+ def test_platform_returns_platform_instance
30
+ db = Sequel.mock(host: :postgres)
31
+ db.extension :platform
32
+
33
+ assert_kind_of Sequel::Platform::Base, db.platform
34
+ end
35
+
36
+ # --- Platform Class Selection ---
37
+
38
+ def test_selects_postgres_platform_for_postgres_adapter
39
+ db = Sequel.mock(host: :postgres)
40
+ db.extension :platform
41
+
42
+ assert_kind_of Sequel::Platform::Postgres, db.platform
43
+ end
44
+
45
+ def test_selects_spark_platform_for_spark_adapter
46
+ db = Sequel.mock(host: :spark)
47
+ db.extension :platform
48
+
49
+ assert_kind_of Sequel::Platform::Spark, db.platform
50
+ end
51
+
52
+ def test_selects_snowflake_platform_for_snowflake_adapter
53
+ db = Sequel.mock(host: :snowflake)
54
+ db.extension :platform
55
+
56
+ assert_kind_of Sequel::Platform::Snowflake, db.platform
57
+ end
58
+
59
+ def test_selects_athena_platform_for_athena_adapter
60
+ db = Sequel.mock(host: :athena)
61
+ db.extension :platform
62
+
63
+ assert_kind_of Sequel::Platform::Athena, db.platform
64
+ end
65
+
66
+ def test_selects_athena_platform_for_presto_adapter
67
+ db = Sequel.mock(host: :presto)
68
+ db.extension :platform
69
+
70
+ assert_kind_of Sequel::Platform::Athena, db.platform
71
+ end
72
+
73
+ def test_selects_base_platform_for_unknown_adapter
74
+ db = Sequel.mock(host: :sqlite)
75
+ db.extension :platform
76
+
77
+ assert_instance_of Sequel::Platform::Base, db.platform
78
+ end
79
+
80
+ # --- Config Loading ---
81
+
82
+ def test_supports_returns_boolean_from_config
83
+ db = Sequel.mock(host: :postgres)
84
+ db.extension :platform
85
+
86
+ assert db.platform.supports?(:cte)
87
+ assert db.platform.supports?(:cte_recursive)
88
+ end
89
+
90
+ def test_supports_returns_false_for_unsupported_feature
91
+ db = Sequel.mock(host: :athena)
92
+ db.extension :platform
93
+
94
+ refute db.platform.supports?(:temp_tables)
95
+ refute db.platform.supports?(:cte_recursive)
96
+ end
97
+
98
+ def test_prefers_returns_boolean_from_config
99
+ db = Sequel.mock(host: :postgres)
100
+ db.extension :platform
101
+
102
+ assert db.platform.prefers?(:cte)
103
+ end
104
+
105
+ def test_prefers_returns_false_when_not_preferred
106
+ db = Sequel.mock(host: :spark)
107
+ db.extension :platform
108
+
109
+ refute db.platform.prefers?(:cte)
110
+ assert db.platform.prefers?(:parquet)
111
+ end
112
+
113
+ def test_bracket_access_returns_config_values
114
+ db = Sequel.mock(host: :postgres)
115
+ db.extension :platform
116
+
117
+ assert_equal 'search_path', db.platform[:schema_switching_method]
118
+ end
119
+
120
+ def test_config_stacking_overrides_base
121
+ db = Sequel.mock(host: :athena)
122
+ db.extension :platform
123
+
124
+ # Athena overrides base supports_temp_tables from true to false
125
+ refute db.platform.supports?(:temp_tables)
126
+ # Athena overrides drop_table_needs_unquoted from false to true
127
+ assert db.platform[:drop_table_needs_unquoted]
128
+ end
129
+
130
+ # --- Function Translations ---
131
+
132
+ def test_postgres_date_diff_uses_subtraction
133
+ db = Sequel.mock(host: :postgres)
134
+ db.extension :platform
135
+
136
+ expr = db.platform.date_diff(:start_date, :end_date)
137
+
138
+ # Should produce subtraction syntax - use dataset.literal to get SQL
139
+ sql = db.literal(expr)
140
+
141
+ assert_includes sql, '-'
142
+ end
143
+
144
+ def test_spark_date_diff_uses_datediff_function
145
+ db = Sequel.mock(host: :spark)
146
+ db.extension :platform
147
+
148
+ expr = db.platform.date_diff(:start_date, :end_date)
149
+
150
+ # Should use datediff function with reversed args
151
+ sql = db.literal(expr)
152
+
153
+ assert_includes sql.downcase, 'datediff'
154
+ end
155
+
156
+ def test_snowflake_date_diff_includes_day_unit
157
+ db = Sequel.mock(host: :snowflake)
158
+ db.extension :platform
159
+
160
+ expr = db.platform.date_diff(:start_date, :end_date)
161
+
162
+ sql = db.literal(expr)
163
+
164
+ assert_includes sql.downcase, 'datediff'
165
+ assert_includes sql, 'day'
166
+ end
167
+
168
+ def test_athena_date_diff_uses_date_diff_function
169
+ db = Sequel.mock(host: :athena)
170
+ db.extension :platform
171
+
172
+ expr = db.platform.date_diff(:start_date, :end_date)
173
+
174
+ sql = db.literal(expr)
175
+
176
+ assert_includes sql.downcase, 'date_diff'
177
+ assert_includes sql, 'day'
178
+ end
179
+
180
+ def test_cast_date_returns_cast_expression
181
+ db = Sequel.mock(host: :postgres)
182
+ db.extension :platform
183
+
184
+ expr = db.platform.cast_date(:some_column)
185
+
186
+ assert_kind_of Sequel::SQL::Cast, expr
187
+ end
188
+
189
+ # --- Edge Cases ---
190
+
191
+ def test_supports_unknown_feature_returns_false
192
+ db = Sequel.mock(host: :postgres)
193
+ db.extension :platform
194
+
195
+ refute db.platform.supports?(:nonexistent_feature)
196
+ end
197
+
198
+ def test_prefers_unknown_feature_returns_false
199
+ db = Sequel.mock(host: :postgres)
200
+ db.extension :platform
201
+
202
+ refute db.platform.prefers?(:nonexistent_feature)
203
+ end
204
+
205
+ def test_bracket_access_unknown_key_returns_nil
206
+ db = Sequel.mock(host: :postgres)
207
+ db.extension :platform
208
+
209
+ assert_nil db.platform[:nonexistent_key]
210
+ end
211
+
212
+ def test_fetch_with_default
213
+ db = Sequel.mock(host: :postgres)
214
+ db.extension :platform
215
+
216
+ # Known key
217
+ assert_equal 'search_path', db.platform.fetch(:schema_switching_method, 'default')
218
+ # Unknown key
219
+ assert_equal 'default', db.platform.fetch(:unknown_key, 'default')
220
+ end
221
+
222
+ end
@@ -15,10 +15,10 @@ class TestConnectionMaker < Minitest::Test
15
15
 
16
16
  def with_ignored_yaml_config(opts = {}); end
17
17
 
18
- def with_yaml_config(options = {}, &block)
18
+ def with_yaml_config(options = {}, &)
19
19
  yaml_config = Sequelizer::YamlConfig.new
20
20
  yaml_config.stub(:options, options) do
21
- Sequelizer::YamlConfig.stub :new, yaml_config, &block
21
+ Sequelizer::YamlConfig.stub(:new, yaml_config, &)
22
22
  end
23
23
  end
24
24
 
@@ -79,9 +79,9 @@ class TestGemfileModifier < Minitest::Test
79
79
  pn_mock.expect(:exist?, true)
80
80
  end
81
81
 
82
- def stub_modifying_methods(obj, &block)
82
+ def stub_modifying_methods(obj, &)
83
83
  obj.stub(:system, nil) do
84
- obj.stub(:puts, nil, &block)
84
+ obj.stub(:puts, nil, &)
85
85
  end
86
86
  end
87
87
 
@@ -72,6 +72,34 @@ class TestOptions < Minitest::Test
72
72
  assert_equal(['SELECT * FROM "table"'], db.sqls)
73
73
  end
74
74
 
75
+ def test_url_based_connection_processes_search_path
76
+ options = Sequelizer::Options.new(
77
+ Sequelizer::OptionsHash.new(url: 'postgres://localhost/mydb', search_path: 'my_schema'),
78
+ )
79
+
80
+ assert_equal('my_schema', options.search_path)
81
+ assert_instance_of(Proc, options.to_hash[:after_connect])
82
+ end
83
+
84
+ def test_url_based_connection_processes_schema_as_search_path
85
+ options = Sequelizer::Options.new(
86
+ Sequelizer::OptionsHash.new(url: 'postgresql://localhost/mydb', schema: 'my_schema'),
87
+ )
88
+
89
+ assert_equal('my_schema', options.search_path)
90
+ end
91
+
92
+ def test_url_based_connection_skips_search_path_processing_for_non_postgres
93
+ options = Sequelizer::Options.new(
94
+ Sequelizer::OptionsHash.new(url: 'mysql2://localhost/mydb', search_path: 'my_schema'),
95
+ )
96
+
97
+ # search_path key remains but is NOT processed (no schema creation callback)
98
+ assert_equal('my_schema', options.search_path)
99
+ # adapter should not be normalized to 'postgres'
100
+ assert_nil(options.adapter)
101
+ end
102
+
75
103
  def test_handles_extensions_passed_in
76
104
  options = Sequelizer::Options.new(extension_example_one: 1, extension_example_two: 1, not_an_extension_example: 1)
77
105
 
data/test/test_helper.rb CHANGED
@@ -23,6 +23,7 @@ SimpleCov.start do
23
23
  end
24
24
 
25
25
  require 'minitest/autorun'
26
+ require 'minitest/mock'
26
27
  require 'sequel'
27
28
 
28
29
  module Minitest