sequelizer 0.1.4 → 0.1.6

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/.claude/settings.local.json +64 -0
  3. data/.devcontainer/.p10k.zsh +1713 -0
  4. data/.devcontainer/.zshrc +29 -0
  5. data/.devcontainer/Dockerfile +137 -0
  6. data/.devcontainer/copy-claude-credentials.sh +32 -0
  7. data/.devcontainer/devcontainer.json +102 -0
  8. data/.devcontainer/init-firewall.sh +123 -0
  9. data/.devcontainer/setup-credentials.sh +95 -0
  10. data/.github/workflows/test.yml +1 -1
  11. data/.gitignore +6 -1
  12. data/.overcommit.yml +73 -0
  13. data/.rubocop.yml +167 -0
  14. data/CHANGELOG.md +24 -0
  15. data/CLAUDE.md +219 -0
  16. data/Gemfile +6 -2
  17. data/Gemfile.lock +158 -0
  18. data/Guardfile +1 -1
  19. data/Rakefile +28 -3
  20. data/lib/sequel/extensions/cold_col.rb +436 -0
  21. data/lib/sequel/extensions/db_opts.rb +65 -4
  22. data/lib/sequel/extensions/make_readyable.rb +148 -30
  23. data/lib/sequel/extensions/more_sql.rb +76 -0
  24. data/lib/sequel/extensions/settable.rb +64 -0
  25. data/lib/sequel/extensions/sql_recorder.rb +85 -0
  26. data/lib/sequel/extensions/unionize.rb +169 -0
  27. data/lib/sequel/extensions/usable.rb +30 -1
  28. data/lib/sequelizer/cli.rb +61 -18
  29. data/lib/sequelizer/connection_maker.rb +54 -72
  30. data/lib/sequelizer/env_config.rb +6 -6
  31. data/lib/sequelizer/gemfile_modifier.rb +23 -21
  32. data/lib/sequelizer/monkey_patches/database_in_after_connect.rb +7 -5
  33. data/lib/sequelizer/options.rb +97 -18
  34. data/lib/sequelizer/options_hash.rb +2 -0
  35. data/lib/sequelizer/version.rb +3 -1
  36. data/lib/sequelizer/yaml_config.rb +9 -3
  37. data/lib/sequelizer.rb +65 -9
  38. data/sequelizer.gemspec +12 -7
  39. data/test/lib/sequel/extensions/test_cold_col.rb +251 -0
  40. data/test/lib/sequel/extensions/test_db_opts.rb +10 -8
  41. data/test/lib/sequel/extensions/test_make_readyable.rb +199 -28
  42. data/test/lib/sequel/extensions/test_more_sql.rb +132 -0
  43. data/test/lib/sequel/extensions/test_settable.rb +109 -0
  44. data/test/lib/sequel/extensions/test_sql_recorder.rb +231 -0
  45. data/test/lib/sequel/extensions/test_unionize.rb +76 -0
  46. data/test/lib/sequel/extensions/test_usable.rb +5 -2
  47. data/test/lib/sequelizer/test_connection_maker.rb +21 -17
  48. data/test/lib/sequelizer/test_env_config.rb +5 -2
  49. data/test/lib/sequelizer/test_gemfile_modifier.rb +7 -6
  50. data/test/lib/sequelizer/test_options.rb +14 -9
  51. data/test/lib/sequelizer/test_yaml_config.rb +13 -12
  52. data/test/test_helper.rb +36 -8
  53. metadata +107 -28
  54. data/lib/sequel/extensions/sqls.rb +0 -31
@@ -1,15 +1,44 @@
1
1
  module Sequel
2
+
3
+ # = Usable
4
+ #
5
+ # Sequel extension that provides a convenient +use+ method for switching
6
+ # the current database/schema context. This is particularly useful for
7
+ # databases that support the USE statement like MySQL, SQL Server, and
8
+ # some big data engines.
9
+ #
10
+ # @example
11
+ # db.extension :usable
12
+ # db.use(:my_schema)
13
+ # # Executes: USE `my_schema`
2
14
  module Usable
15
+
16
+ # Switches to the specified database or schema.
17
+ #
18
+ # Executes a USE statement to change the current database context.
19
+ # The schema name is properly quoted using the database's identifier
20
+ # quoting rules.
21
+ #
22
+ # @param schema_name [Symbol, String] the name of the schema/database to use
23
+ # @example
24
+ # db.use(:production_db)
25
+ # db.use('test_schema')
3
26
  def use(schema_name)
4
27
  run(use_sql(schema_name))
5
28
  end
6
29
 
7
30
  private
8
31
 
32
+ # Generates the USE SQL statement for the given schema name.
33
+ #
34
+ # @param schema_name [Symbol, String] the schema name to use
35
+ # @return [String] the USE SQL statement
9
36
  def use_sql(schema_name)
10
- "USE #{quote_identifier(schema_name)}"
37
+ "USE #{literal(schema_name)}"
11
38
  end
39
+
12
40
  end
13
41
 
14
42
  Database.register_extension(:usable, Usable)
43
+
15
44
  end
@@ -3,39 +3,75 @@ require 'pp'
3
3
  require_relative 'gemfile_modifier'
4
4
 
5
5
  module Sequelizer
6
+ # = CLI
7
+ #
8
+ # Command line interface for Sequelizer gem using Thor.
9
+ # Provides commands for:
10
+ #
11
+ # * Updating Gemfile with database adapters
12
+ # * Initializing .env files with database configuration
13
+ # * Displaying current configuration
14
+ #
15
+ # @example
16
+ # sequelizer update_gemfile
17
+ # sequelizer init_env --adapter postgres --host localhost
18
+ # sequelizer config
6
19
  class CLI < Thor
7
- desc 'update_gemfile', 'adds or replaces a line in your Gemfile to include the correct database adapter to work with Sequel'
20
+
21
+ desc 'update_gemfile',
22
+ 'adds or replaces a line in your Gemfile to include the correct database adapter to work with Sequel'
8
23
  option 'dry-run', type: :boolean, desc: 'Only prints out what it would do, but makes no changes'
9
24
  option 'skip-bundle', type: :boolean, desc: "Don't run `bundle install` after modifying Gemfile"
25
+ # Updates the Gemfile to include the appropriate database adapter gem.
26
+ #
27
+ # This command analyzes your current database configuration and adds or updates
28
+ # the corresponding database adapter gem in your Gemfile. It supports various
29
+ # adapters including PostgreSQL, MySQL, SQLite, and JDBC-based adapters.
30
+ #
31
+ # @option options [Boolean] dry-run Only prints what would be done without making changes
32
+ # @option options [Boolean] skip-bundle Skip running `bundle install` after modification
10
33
  def update_gemfile
11
34
  GemfileModifier.new(options).modify
12
35
  end
13
36
 
14
37
  desc 'init_env', 'creates a .env file with the parameters listed'
15
38
  option :adapter,
16
- aliases: :a,
17
- desc: 'adapter for database'
39
+ aliases: :a,
40
+ desc: 'adapter for database'
18
41
  option :host,
19
- aliases: :h,
20
- banner: 'localhost',
21
- desc: 'host for database'
42
+ aliases: :h,
43
+ banner: 'localhost',
44
+ desc: 'host for database'
22
45
  option :username,
23
- aliases: :u,
24
- desc: 'username for database'
46
+ aliases: :u,
47
+ desc: 'username for database'
25
48
  option :password,
26
- aliases: :P,
27
- desc: 'password for database'
49
+ aliases: :P,
50
+ desc: 'password for database'
28
51
  option :port,
29
- aliases: :p,
30
- type: :numeric,
31
- banner: '5432',
32
- desc: 'port for database'
52
+ aliases: :p,
53
+ type: :numeric,
54
+ banner: '5432',
55
+ desc: 'port for database'
33
56
  option :database,
34
- aliases: :d,
35
- desc: 'database for database'
57
+ aliases: :d,
58
+ desc: 'database for database'
36
59
  option :search_path,
37
- aliases: :s,
38
- desc: 'schema for database (PostgreSQL only)'
60
+ aliases: :s,
61
+ desc: 'schema for database (PostgreSQL only)'
62
+ # Creates a .env file with database configuration parameters.
63
+ #
64
+ # This command generates a .env file with SEQUELIZER_* environment variables
65
+ # based on the provided options. It will not overwrite an existing .env file.
66
+ #
67
+ # @option options [String] :adapter Database adapter (e.g., 'postgres', 'mysql2')
68
+ # @option options [String] :host Database host (default: 'localhost')
69
+ # @option options [String] :username Database username
70
+ # @option options [String] :password Database password
71
+ # @option options [Integer] :port Database port (default: 5432 for PostgreSQL)
72
+ # @option options [String] :database Database name
73
+ # @option options [String] :search_path PostgreSQL schema search path
74
+ # @raise [SystemExit] if .env file already exists
39
75
  def init_env
40
76
  if File.exist?('.env')
41
77
  puts ".env already exists! I'm too cowardly to overwrite it!"
@@ -49,6 +85,11 @@ module Sequelizer
49
85
  end
50
86
 
51
87
  desc 'config', 'prints out the connection parameters'
88
+ # Displays the current database configuration and extensions.
89
+ #
90
+ # This command shows the resolved configuration options that would be used
91
+ # for database connections, including all merged sources and any Sequel
92
+ # extensions that would be loaded.
52
93
  def config
53
94
  opts = Options.new
54
95
  pp opts.to_hash
@@ -56,10 +97,12 @@ module Sequelizer
56
97
  end
57
98
 
58
99
  private
100
+
59
101
  def make_env(options)
60
102
  options.map do |key, value|
61
103
  "SEQUELIZER_#{key.upcase}=#{value}"
62
104
  end.join("\n")
63
105
  end
106
+
64
107
  end
65
108
  end
@@ -1,97 +1,79 @@
1
1
  require 'sequel'
2
- require 'cgi'
3
2
  require_relative 'options'
4
3
 
5
4
  module Sequelizer
6
- # Class that handles loading/interpretting the database options and
7
- # creates the Sequel connection
5
+ # = ConnectionMaker
6
+ #
7
+ # Class that handles loading/interpreting the database options and
8
+ # creates the Sequel connection. This class is responsible for:
9
+ #
10
+ # * Loading configuration from multiple sources
11
+ # * Creating standard Sequel database connections
12
+ #
13
+ # @example Basic usage
14
+ # maker = ConnectionMaker.new(adapter: 'postgres', host: 'localhost')
15
+ # db = maker.connection
8
16
  class ConnectionMaker
9
- # The options for Sequel.connect
10
- attr :options
11
17
 
12
- # Accepts an optional set of database options
18
+ # @!attribute [r] options
19
+ # @return [Options] the database connection options
20
+ attr_reader :options
21
+
22
+ # Creates a new ConnectionMaker instance.
13
23
  #
14
- # If no options are provided, attempts to read options from
15
- # config/database.yml
24
+ # If no options are provided, attempts to read options from multiple sources
25
+ # in order of precedence:
26
+ # 1. .env file
27
+ # 2. Environment variables
28
+ # 3. config/database.yml
29
+ # 4. ~/.config/sequelizer/database.yml
16
30
  #
17
- # If config/database.yml doesn't exist, Dotenv is used to try to load a
18
- # .env file, then uses any SEQUELIZER_* environment variables as
19
- # database options
31
+ # @param options [Hash, nil] database connection options
32
+ # @option options [String] :adapter database adapter (e.g., 'postgres', 'mysql2')
33
+ # @option options [String] :host database host
34
+ # @option options [Integer] :port database port
35
+ # @option options [String] :database database name
36
+ # @option options [String] :username database username
37
+ # @option options [String] :password database password
38
+ # @option options [String] :search_path PostgreSQL schema search path
20
39
  def initialize(options = nil)
21
40
  @options = Options.new(options)
22
41
  end
23
42
 
24
- # Returns a Sequel connection to the database
43
+ # Returns a Sequel connection to the database.
44
+ #
45
+ # This method creates a standard Sequel database connection
46
+ # using the configured options.
47
+ #
48
+ # @return [Sequel::Database] configured database connection
49
+ # @raise [Sequel::Error] if connection fails
50
+ #
51
+ # @example
52
+ # connection = maker.connection
53
+ # users = connection[:users].all
25
54
  def connection
26
55
  opts = options.to_hash
27
56
  extensions = options.extensions
28
57
 
29
- conn = if url = (opts.delete(:uri) || opts.delete(:url))
30
- Sequel.connect(url, opts)
31
- else
32
- # Kerberos related options
33
- realm = opts[:realm]
34
- host_fqdn = opts[:host_fqdn] || opts[:host]
35
- principal = opts[:principal]
36
-
37
- adapter = opts[:adapter]
38
- if adapter =~ /\Ajdbc_/
39
- user = opts[:user]
40
- password = opts[:password]
41
- end
42
-
43
- case opts[:adapter] && opts[:adapter].to_sym
44
- when :jdbc_hive2
45
- opts[:adapter] = :jdbc
46
- auth = if realm
47
- ";principal=#{e principal}/#{e host_fqdn}@#{e realm}"
48
- elsif user
49
- ";user=#{e user};password=#{e password}"
50
- else
51
- ';auth=noSasl'
52
- end
53
- opts[:uri] = "jdbc:hive2://#{e opts[:host]}:#{opts.fetch(:port, 21050).to_i}/#{e(opts[:database] || 'default')}#{auth}"
54
- when :jdbc_impala
55
- opts[:adapter] = :jdbc
56
- auth = if realm
57
- ";AuthMech=1;KrbServiceName=#{e principal};KrbAuthType=2;KrbHostFQDN=#{e host_fqdn};KrbRealm=#{e realm}"
58
- elsif user
59
- if password
60
- ";AuthMech=3;UID=#{e user};PWD=#{e password}"
61
- else
62
- ";AuthMech=2;UID=#{e user}"
63
- end
64
- end
65
- opts[:uri] = "jdbc:impala://#{e opts[:host]}:#{opts.fetch(:port, 21050).to_i}/#{e(opts[:database] || 'default')}#{auth}"
66
- when :jdbc_postgres
67
- opts[:adapter] = :jdbc
68
- auth = "?user=#{user}#{"&password=#{password}" if password}" if user
69
- opts[:uri] = "jdbc:postgresql://#{e opts[:host]}:#{opts.fetch(:port, 5432).to_i}/#{e(opts[:database])}#{auth}"
70
- when :impala
71
- opts[:database] ||= 'default'
72
- opts[:port] ||= 21000
73
- if principal
74
- # realm doesn't seem to be used?
75
- opts[:transport] = :sasl
76
- opts[:sasl_params] = {
77
- mechanism: "GSSAPI",
78
- remote_host: host_fqdn,
79
- remote_principal: principal
80
- }
81
- end
82
- end
83
-
84
- Sequel.connect(opts)
85
- end
58
+ conn = create_sequel_connection(opts)
86
59
  conn.extension(*extensions)
87
60
  conn
88
61
  end
89
62
 
90
63
  private
91
64
 
92
- def e(v)
93
- CGI.escape(v.to_s)
65
+ def create_sequel_connection(opts)
66
+ if (url = opts.delete(:uri) || opts.delete(:url))
67
+ Sequel.connect(url, opts)
68
+ else
69
+ configure_adapter_specific_options(opts)
70
+ Sequel.connect(opts)
71
+ end
94
72
  end
73
+
74
+ def configure_adapter_specific_options(opts)
75
+ # No adapter-specific configuration needed currently
76
+ end
77
+
95
78
  end
96
79
  end
97
-
@@ -4,25 +4,25 @@ module Sequelizer
4
4
  # Creates a set of database configuration options from environment
5
5
  # variables
6
6
  class EnvConfig
7
+
7
8
  # Any environment variables in the .env file are loaded and then
8
9
  # any environment variable starting with SEQUELIZER_ will be used
9
10
  # as an option for the database
10
11
  def options
11
12
  Dotenv.load
12
13
 
13
- seq_config = ENV.keys.select { |key| key =~ /^SEQUELIZER_/ }.inject({}) do |config, key|
14
+ seq_config = ENV.keys.grep(/^SEQUELIZER_/).each_with_object({}) do |key, config|
14
15
  new_key = key.gsub(/^SEQUELIZER_/, '').downcase
15
- config[new_key] = ENV[key]
16
- config
16
+ config[new_key] = ENV.fetch(key, nil)
17
17
  end
18
18
 
19
- db_config = ENV.keys.select { |key| key =~ /_DB_OPT_/ }.inject({}) do |config, key|
19
+ db_config = ENV.keys.grep(/_DB_OPT_/).each_with_object({}) do |key, config|
20
20
  new_key = key.downcase
21
- config[new_key] = ENV[key]
22
- config
21
+ config[new_key] = ENV.fetch(key, nil)
23
22
  end
24
23
 
25
24
  db_config.merge(seq_config)
26
25
  end
26
+
27
27
  end
28
28
  end
@@ -2,7 +2,8 @@ require_relative 'options'
2
2
 
3
3
  module Sequelizer
4
4
  class GemfileModifier
5
- attr :options
5
+
6
+ attr_reader :options
6
7
 
7
8
  def initialize(options = {})
8
9
  @options = options
@@ -14,14 +15,14 @@ module Sequelizer
14
15
  modify_gemfile
15
16
  run_bundle unless options['skip-bundle']
16
17
  else
17
- puts "Gemfile needs no modification"
18
+ puts 'Gemfile needs no modification'
18
19
  end
19
20
  end
20
21
 
21
22
  private
22
23
 
23
24
  def modify_gemfile
24
- puts %Q|Adding "#{gem_line}" to Gemfile|
25
+ puts %(Adding "#{gem_line}" to Gemfile)
25
26
  return if options['dry-run']
26
27
 
27
28
  File.write(gemfile, modified_lines.join("\n"))
@@ -30,21 +31,21 @@ module Sequelizer
30
31
  def proper_gem
31
32
  opts = Options.new
32
33
  @proper_gem ||= case opts.adapter
33
- when 'postgres'
34
- 'pg'
35
- when 'sqlite'
36
- 'sqlite3'
37
- when 'mysql'
38
- 'mysql2'
39
- when 'tinytds'
40
- 'tiny_tds'
41
- when 'oracle'
42
- 'ruby-oci8'
43
- when nil
44
- raise "No database adapter defined in your Sequelizer configuration"
45
- else
46
- raise "Don't know which database gem to use with adapter: #{opts.adapter}"
47
- end
34
+ when 'postgres'
35
+ 'pg'
36
+ when 'sqlite'
37
+ 'sqlite3'
38
+ when 'mysql'
39
+ 'mysql2'
40
+ when 'tinytds'
41
+ 'tiny_tds'
42
+ when 'oracle'
43
+ 'ruby-oci8'
44
+ when nil
45
+ raise 'No database adapter defined in your Sequelizer configuration'
46
+ else
47
+ raise "Don't know which database gem to use with adapter: #{opts.adapter}"
48
+ end
48
49
  end
49
50
 
50
51
  def gem_line
@@ -68,22 +69,23 @@ module Sequelizer
68
69
  end
69
70
 
70
71
  def modified_lines
71
- gemfile_lines.select { |l| l !~ Regexp.new(gem_line_comment) } + [full_gem_line]
72
+ gemfile_lines.grep_v(Regexp.new(gem_line_comment)) + [full_gem_line]
72
73
  end
73
74
 
74
75
  def check_for_gemfile
75
76
  return if gemfile.exist?
77
+
76
78
  raise "Could not find Gemfile in current directory: #{Pathname.pwd}"
77
79
  end
78
80
 
79
81
  def run_bundle
80
- puts "Running `bundle install` to update dependencies"
82
+ puts 'Running `bundle install` to update dependencies'
81
83
  system('bundle install')
82
84
  end
83
85
 
84
86
  def gemfile
85
87
  @gemfile ||= Pathname.new('Gemfile')
86
88
  end
89
+
87
90
  end
88
91
  end
89
-
@@ -1,11 +1,12 @@
1
1
  module Sequel
2
2
  class ConnectionPool
3
+
3
4
  # Return a new connection by calling the connection proc with the given server name,
4
5
  # and checking for connection errors.
5
6
  def make_new(server)
6
7
  begin
7
8
  conn = @db.connect(server)
8
- if ac = @after_connect
9
+ if (ac = @after_connect)
9
10
  case ac.arity
10
11
  when 3
11
12
  ac.call(conn, server, @db)
@@ -15,12 +16,13 @@ module Sequel
15
16
  ac.call(conn)
16
17
  end
17
18
  end
18
- rescue Exception=>exception
19
- raise Sequel.convert_exception_class(exception, Sequel::DatabaseConnectionError)
19
+ rescue StandardError => e
20
+ raise Sequel.convert_exception_class(e, Sequel::DatabaseConnectionError)
20
21
  end
21
- raise(Sequel::DatabaseConnectionError, "Connection parameters not valid") unless conn
22
+ raise(Sequel::DatabaseConnectionError, 'Connection parameters not valid') unless conn
23
+
22
24
  conn
23
25
  end
26
+
24
27
  end
25
28
  end
26
-
@@ -3,18 +3,61 @@ require_relative 'env_config'
3
3
  require_relative 'options_hash'
4
4
 
5
5
  module Sequelizer
6
+ # = Options
7
+ #
8
+ # Manages database connection options from multiple configuration sources.
9
+ # This class is responsible for:
10
+ #
11
+ # * Loading configuration from various sources (YAML files, environment variables, .env files)
12
+ # * Applying precedence rules for configuration sources
13
+ # * Processing adapter-specific options (especially PostgreSQL schema handling)
14
+ # * Managing Sequel extensions
15
+ # * Setting up after_connect callbacks
16
+ #
17
+ # == Configuration Sources (in order of precedence)
18
+ #
19
+ # 1. Passed options (highest priority)
20
+ # 2. .env file
21
+ # 3. Environment variables
22
+ # 4. config/database.yml
23
+ # 5. ~/.config/sequelizer/database.yml (lowest priority)
24
+ #
25
+ # @example Basic usage
26
+ # options = Options.new(adapter: 'postgres', host: 'localhost')
27
+ # hash = options.to_hash
28
+ #
29
+ # @example Loading from environment
30
+ # ENV['SEQUELIZER_ADAPTER'] = 'postgres'
31
+ # options = Options.new
32
+ # puts options.adapter # => 'postgres'
6
33
  class Options
7
- attr :extensions
34
+
35
+ # @!attribute [r] extensions
36
+ # @return [Array<Symbol>] list of Sequel extensions to load
37
+ attr_reader :extensions
38
+
39
+ # Creates a new Options instance, processing configuration from multiple sources.
40
+ #
41
+ # @param options [Hash, String, nil] database connection options or connection URL
42
+ # If a Hash is provided, it will be merged with configuration from other sources.
43
+ # If a String is provided, it's treated as a database URL and returned as-is.
44
+ # If nil, configuration is loaded entirely from external sources.
8
45
  def initialize(options = nil)
9
46
  opts = fix_options(options)
10
47
  @options, @extensions = filter_extensions(opts)
11
48
  end
12
49
 
50
+ # Returns the processed options as a hash suitable for Sequel.connect.
51
+ #
52
+ # @return [Hash] the database connection options
13
53
  def to_hash
14
54
  @options
15
55
  end
16
56
 
17
- %w(adapter database username password search_path).each do |name|
57
+ # Define accessor methods for common database options
58
+ %w[adapter database username password search_path].each do |name|
59
+ # @!method #{name}
60
+ # @return [String, nil] the #{name} option value
18
61
  define_method(name) do
19
62
  @options[name]
20
63
  end
@@ -22,9 +65,14 @@ module Sequelizer
22
65
 
23
66
  private
24
67
 
68
+ # Creates an after_connect callback proc that handles custom callbacks
69
+ # and applies database-specific options.
70
+ #
71
+ # @param opts [Hash] options hash that may contain an :after_connect callback
72
+ # @return [Proc] callback to be executed after database connection
25
73
  def make_ac(opts)
26
- Proc.new do |conn, server, db|
27
- if ac = opts[:after_connect]
74
+ proc do |conn, server, db|
75
+ if (ac = opts[:after_connect])
28
76
  ac.arity == 2 ? ac.call(conn, server) : ac.call(conn)
29
77
  end
30
78
  db.extension :db_opts
@@ -32,19 +80,27 @@ module Sequelizer
32
80
  end
33
81
  end
34
82
 
83
+ # Processes and fixes the passed options, handling various input types
84
+ # and applying adapter-specific transformations.
85
+ #
35
86
  # If passed a hash, scans hash for certain options and sets up hash
36
- # to be fed to Sequel.connect
87
+ # to be fed to Sequel.connect. Handles PostgreSQL schema setup and
88
+ # timeout conversion.
89
+ #
90
+ # If passed anything else (like a string that represents a database URL),
91
+ # the value is returned without modification.
37
92
  #
38
- # If fed anything, like a string that represents the URL for a DB,
39
- # the string is returned without modification
93
+ # @param passed_options [Hash, String, nil] the options to process
94
+ # @return [Hash, String] processed options or original string
40
95
  def fix_options(passed_options)
41
96
  return passed_options unless passed_options.nil? || passed_options.is_a?(Hash)
97
+
42
98
  opts = OptionsHash.new(passed_options || {}).to_hash
43
99
  sequelizer_options = db_config(opts).merge(opts)
44
100
 
45
101
  if sequelizer_options[:adapter] =~ /^postgres/
46
102
  sequelizer_options[:adapter] = 'postgres'
47
- paths = %w(search_path schema_search_path schema).map { |key| sequelizer_options.delete(key) }.compact
103
+ paths = %w[search_path schema_search_path schema].map { |key| sequelizer_options.delete(key) }.compact
48
104
 
49
105
  unless paths.empty?
50
106
  sequelizer_options[:search_path] = paths.first
@@ -62,10 +118,14 @@ module Sequelizer
62
118
  sequelizer_options.merge(after_connect: make_ac(sequelizer_options))
63
119
  end
64
120
 
65
- # Grabs the database options from
66
- # - ~/.config/sequelizer.yml if it exists
67
- # - config/database.yml if it exists
68
- # - environment variables (also reads from .env)
121
+ # Loads database configuration from external sources in order of precedence.
122
+ # Sources checked (in order):
123
+ # - ~/.config/sequelizer.yml (if it exists and not ignored)
124
+ # - config/database.yml (if it exists and not ignored)
125
+ # - environment variables (including .env file if not ignored)
126
+ #
127
+ # @param opts [Hash] base options that may contain ignore flags
128
+ # @return [OptionsHash] merged configuration from all sources
69
129
  def db_config(opts)
70
130
  @db_config ||= begin
71
131
  opts = OptionsHash.new(opts)
@@ -77,14 +137,19 @@ module Sequelizer
77
137
  end
78
138
 
79
139
  # Returns a proc that should be executed after Sequel connects to the
80
- # datebase.
140
+ # database.
141
+ #
142
+ # For PostgreSQL connections with a search_path defined, this proc will:
143
+ # 1. Create each schema in the search path if it doesn't exist
144
+ # 2. Set the search_path for the connection
81
145
  #
82
- # Right now, the only thing that happens is if we're connecting to
83
- # PostgreSQL and the schema_search_path is defined, each schema
84
- # is created if it doesn't exist, then the search_path is set for
85
- # the connection.
146
+ # @param search_path [String] comma-separated list of PostgreSQL schemas
147
+ # @return [Proc] callback to execute after connection
148
+ # @example
149
+ # callback = after_connect('public,app_schema')
150
+ # # When called, creates schemas and sets search_path
86
151
  def after_connect(search_path)
87
- Proc.new do |conn|
152
+ proc do |conn|
88
153
  search_path.split(',').map(&:strip).each do |schema|
89
154
  conn.execute("CREATE SCHEMA IF NOT EXISTS #{schema}")
90
155
  end
@@ -92,6 +157,19 @@ module Sequelizer
92
157
  end
93
158
  end
94
159
 
160
+ # Extracts Sequel extension configuration from options.
161
+ #
162
+ # Looks for keys starting with 'extension_' and converts them to
163
+ # extension names. The extension keys are removed from the options
164
+ # hash and returned separately.
165
+ #
166
+ # @param options [Hash] options hash that may contain extension keys
167
+ # @return [Array<Hash, Array>] tuple of [filtered_options, extensions]
168
+ # @example
169
+ # opts = { adapter: 'postgres', extension_pg_json: true, extension_pg_array: true }
170
+ # filtered_opts, exts = filter_extensions(opts)
171
+ # # filtered_opts => { adapter: 'postgres' }
172
+ # # exts => [:pg_json, :pg_array]
95
173
  def filter_extensions(options)
96
174
  extension_regexp = /^extension_/
97
175
  extension_keys = options.keys.select { |k| k.to_s =~ extension_regexp }
@@ -101,5 +179,6 @@ module Sequelizer
101
179
  end
102
180
  [options, extensions]
103
181
  end
182
+
104
183
  end
105
184
  end
@@ -1,7 +1,9 @@
1
1
  require 'hashie'
2
2
  module Sequelizer
3
3
  class OptionsHash < Hash
4
+
4
5
  include Hashie::Extensions::IndifferentAccess
5
6
  include Hashie::Extensions::MergeInitializer
7
+
6
8
  end
7
9
  end
@@ -1,4 +1,6 @@
1
1
  module Sequelizer
2
+
2
3
  # Version for the gem
3
- VERSION = "0.1.4"
4
+ VERSION = '0.1.6'.freeze
5
+
4
6
  end