veksel 0.2.2 → 0.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: 288e8f30d1d6e5297d1decb5a50cb352c099a8bb8c8081bbb1b16ab834ecfb21
4
- data.tar.gz: 280188595b8536edb7f8677b9cf8dded5f815b8c6552122a09670aadcdc691d6
3
+ metadata.gz: 692cda4c884d09fd6388bc304b72a0c41270942654815bebf08a91025a164e89
4
+ data.tar.gz: 81dabd57bfdf2096d6ab75a5bc870a3e39ff122bc790758551830d11414fbcd4
5
5
  SHA512:
6
- metadata.gz: 45c0de39a5e76b77bd7b14ffdbfa0dfbe209ff87fa0ae42acb2a84c6c44971814eac36026063be82656159ce72cd37fcaf4e0db428f0be1263cb121821aefddb
7
- data.tar.gz: 9b441491aa25606aa1e87080523f8daed8a5550f0f9a4a250d801ddce39b4da02bd4bab716d3b3f603462d9e09c53de1a11f4c954654a3abcb679fa87f71e27a
6
+ metadata.gz: 38a76367d2ab7f8603a646f8236c027acea8f5104a4c342169cbacf9d35a02878b9f477a924afe4f9421dc1a9fe0e1abc89bc3918404296e9f883517fceabeae
7
+ data.tar.gz: f919b8fcbdfa107a15e8d784731570656561a0c843b32d489324166431da4897e10e357b4dac2989910c7f49ea3c060888777c0eabcfd97708206b0be760fb1b
data/bin/veksel CHANGED
@@ -2,8 +2,6 @@
2
2
  require 'veksel/cli'
3
3
 
4
4
  case ARGV[0]
5
- when 'suffix'
6
- Veksel::CLI.suffix
7
5
  when 'fork'
8
6
  Veksel::CLI.fork
9
7
  end
@@ -8,40 +8,13 @@ namespace :veksel do
8
8
 
9
9
  desc "List forked databases"
10
10
  task list: 'db:load_config' do
11
- require 'veksel/commands/clean'
11
+ require 'veksel/commands/list'
12
12
 
13
13
  db = ActiveRecord::Base.configurations.find_db_config('development')
14
- command = Veksel::Commands::Clean.new(db)
15
- databases = command.all_databases
16
- active_branches = command.active_branches
17
-
18
- if databases.empty?
19
- puts "No databases created by Veksel"
20
- next
21
- end
22
-
23
- hash = {}
24
- databases.each do |database|
25
- branch = database.sub(command.prefix, '')
26
- hash[branch] = database
27
- end
28
-
29
- longest_branch_name = hash.keys.max_by(&:length).length
30
- longest_database_name = hash.values.max_by(&:length).length
31
- puts "Databases created by Veksel:"
32
- puts ""
33
- puts "#{'Branch'.ljust(longest_branch_name)} #{'Database'.ljust(longest_database_name)} Active"
34
- inactive_count = 0
35
- hash.each do |branch, database|
36
- # Print a formatted string padded to fit the longest branch name
37
- active = active_branches.include?(branch) ? 'Yes' : 'No'
38
- inactive_count += 1 if active == 'No'
39
- puts "#{branch.ljust(longest_branch_name)} #{database.ljust(longest_database_name)} #{active}"
40
- end
41
-
42
- if inactive_count > 0
43
- puts ""
44
- puts "Clean inactive databases with bin/rails veksel:clean"
14
+ begin
15
+ Veksel::Commands::List.new(db).perform
16
+ rescue Veksel::AdapterNotSupported => e
17
+ $stderr.puts e.message
45
18
  end
46
19
  end
47
20
 
@@ -50,6 +23,10 @@ namespace :veksel do
50
23
  require 'veksel/commands/clean'
51
24
 
52
25
  db = ActiveRecord::Base.configurations.find_db_config('development')
53
- Veksel::Commands::Clean.new(db).perform
26
+ begin
27
+ Veksel::Commands::Clean.new(db).perform
28
+ rescue Veksel::AdapterNotSupported => e
29
+ $stderr.puts "#{e.message} - clean skipped"
30
+ end
54
31
  end
55
32
  end
data/lib/veksel/cli.rb CHANGED
@@ -2,39 +2,44 @@ require 'veksel'
2
2
 
3
3
  module Veksel
4
4
  module CLI
5
- def self.suffix
6
- print Veksel.suffix
7
- end
8
-
9
- def self.fork
10
- return if Veksel.skip_fork?
11
- t1 = Time.now.to_f
12
-
13
- require 'veksel/commands/fork'
14
- require 'psych'
15
- require 'ostruct'
16
- require 'fileutils'
17
-
18
- config = read_config('config/database.yml')[:development]
19
-
20
- target_database = config[:database] + Veksel.suffix
21
- db = OpenStruct.new(configuration_hash: config, database: target_database)
22
- Veksel::Commands::Fork.new(db).perform
5
+ DbConfig = Struct.new('DbConfig', :configuration_hash)
6
+
7
+ class << self
8
+ def fork
9
+ return if Veksel.skip_fork?
10
+ t1 = Time.now.to_f
11
+
12
+ require 'veksel/commands/fork'
13
+ require 'psych'
14
+ require 'fileutils'
15
+
16
+ config = read_config('config/database.yml')[:development]
17
+ if config.key?(:primary)
18
+ config = config[:primary]
19
+ $stderr.puts "Warning: Only your primary database will be forked"
20
+ end
21
+
22
+ target_database = config[:database] + Veksel.suffix
23
+ db = DbConfig.new(config)
24
+ if Veksel::Commands::Fork.new(db).perform
25
+ duration = ((Time.now.to_f - t1) * 1000).to_i
26
+ FileUtils.touch('tmp/restart.txt')
27
+ puts "Forked database in #{duration.to_i}ms"
28
+ end
29
+ rescue AdapterNotSupported => e
30
+ $stderr.puts "#{e.message} - fork skipped"
31
+ end
23
32
 
24
- duration = ((Time.now.to_f - t1) * 1000).to_i
25
- FileUtils.touch('tmp/restart.txt')
26
- puts "Forked database in #{duration.to_i}ms"
27
- end
33
+ def read_config(path)
34
+ raw_config = File.read(path)
28
35
 
29
- private_class_method def self.read_config(path)
30
- raw_config = File.read(path)
36
+ if raw_config.include?('<%=')
37
+ require 'erb'
38
+ raw_config = ERB.new(raw_config).result
39
+ end
31
40
 
32
- if raw_config.include?('<%=')
33
- require 'erb'
34
- raw_config = ERB.new(raw_config).result
41
+ Psych.safe_load(raw_config, aliases: true, symbolize_names: true)
35
42
  end
36
-
37
- Psych.safe_load(raw_config, aliases: true, symbolize_names: true)
38
43
  end
39
44
  end
40
45
  end
@@ -1,33 +1,22 @@
1
- require_relative '../pg_cluster'
2
-
3
1
  module Veksel
4
2
  module Commands
5
3
  class Clean
6
- attr_reader :prefix
7
-
8
4
  def initialize(db, dry_run: false)
9
- @pg_cluster = PgCluster.new(db.configuration_hash)
10
- @prefix = Veksel.prefix(db.configuration_hash[:database])
5
+ @adapter = Veksel.adapter_for(db.configuration_hash)
11
6
  @dry_run = dry_run
12
7
  end
13
8
 
14
9
  def perform
15
- all_databases = @pg_cluster.list_databases(prefix: @prefix)
10
+ all_databases = @adapter.forked_databases
11
+ active_branches = Veksel.active_branches
12
+
16
13
  stale_databases = all_databases.filter do |database|
17
- active_branches.none? { |branch| database.end_with?("_#{branch}") }
14
+ active_branches.none? { |branch| database.branch == branch }
18
15
  end
19
16
  stale_databases.each do |database|
20
- @pg_cluster.drop_database(database, dry_run: @dry_run)
17
+ @adapter.drop_database(database.name, dry_run: @dry_run)
21
18
  end
22
19
  end
23
-
24
- def active_branches
25
- `git for-each-ref 'refs/heads/' --format '%(refname)'`.split("\n").map { |ref| ref.sub('refs/heads/', '') }
26
- end
27
-
28
- def all_databases
29
- @pg_cluster.list_databases(prefix: @prefix)
30
- end
31
20
  end
32
21
  end
33
22
  end
@@ -1,22 +1,23 @@
1
- require_relative '../pg_cluster'
2
-
3
1
  module Veksel
4
2
  module Commands
5
3
  class Fork
6
- attr_reader :pg_cluster, :source_db, :target_db
4
+ attr_reader :adapter, :source_db, :target_db
7
5
 
8
6
  def initialize(db)
9
- @pg_cluster = PgCluster.new(db.configuration_hash)
10
- @source_db = db.database.sub(%r[#{Veksel.suffix}$], '')
11
- @target_db = db.database
7
+ @adapter = Veksel.adapter_for(db.configuration_hash)
8
+ @source_db = adapter.main_database
9
+ @target_db = adapter.db_name_for_suffix(Veksel.suffix)
12
10
  raise "Source and target databases cannot be the same" if source_db == target_db
13
11
  end
14
12
 
15
13
  def perform
16
- return if pg_cluster.target_populated?(target_db)
14
+ return false unless adapter
15
+ return false if adapter.target_populated?(target_db)
16
+
17
+ adapter.kill_connection(source_db)
18
+ adapter.create_database(target_db, template: source_db)
17
19
 
18
- pg_cluster.kill_connection(source_db)
19
- pg_cluster.create_database(target_db, template: source_db)
20
+ true
20
21
  end
21
22
  end
22
23
  end
@@ -0,0 +1,45 @@
1
+ module Veksel
2
+ module Commands
3
+ class List
4
+ attr_reader :adapter
5
+
6
+ def initialize(db)
7
+ @adapter = Veksel.adapter_for(db.configuration_hash)
8
+ end
9
+
10
+ def perform
11
+ databases = adapter.forked_databases
12
+ active_branches = Veksel.active_branches
13
+
14
+ if databases.empty?
15
+ puts "No databases created by Veksel"
16
+ return
17
+ end
18
+
19
+ hash = {}
20
+ databases.each do |database|
21
+ branch = database.branch
22
+ hash[branch] = database.name
23
+ end
24
+
25
+ longest_branch_name = hash.keys.max_by(&:length).length
26
+ longest_database_name = hash.values.max_by(&:length).length
27
+ puts "Databases created by Veksel:"
28
+ puts ""
29
+ puts "#{'Branch'.ljust(longest_branch_name)} #{'Database'.ljust(longest_database_name)} Active"
30
+ inactive_count = 0
31
+ hash.each do |branch, database|
32
+ # Print a formatted string padded to fit the longest branch name
33
+ active = active_branches.include?(branch) ? 'Yes' : 'No'
34
+ inactive_count += 1 if active == 'No'
35
+ puts "#{branch.ljust(longest_branch_name)} #{database.ljust(longest_database_name)} #{active}"
36
+ end
37
+
38
+ if inactive_count > 0
39
+ puts ""
40
+ puts "Clean inactive databases with bin/rails veksel:clean"
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -1,11 +1,28 @@
1
1
  module Veksel
2
2
  class PgCluster
3
+ Database = Struct.new(:name, :branch)
4
+
3
5
  attr_reader :configuration_hash
4
6
 
5
7
  def initialize(configuration_hash)
6
8
  @configuration_hash = configuration_hash
7
9
  end
8
10
 
11
+ def main_database
12
+ configuration_hash[:veksel_main_database] || configuration_hash[:database]
13
+ end
14
+
15
+ def forked_databases
16
+ list_databases(prefix: forked_database_prefix).map do |name|
17
+ Database.new(name, name.sub(forked_database_prefix, ''))
18
+ end
19
+ end
20
+
21
+ def db_name_for_suffix(suffix)
22
+ return main_database if suffix.empty?
23
+ "#{forked_database_prefix}#{suffix}"
24
+ end
25
+
9
26
  def target_populated?(dbname)
10
27
  IO.pipe do |r, w|
11
28
  spawn(pg_env, %[psql -t #{pg_connection_args(dbname)} -c "SELECT 'ok' FROM ar_internal_metadata LIMIT 1;"], out: w, err: '/dev/null')
@@ -42,6 +59,10 @@ module Veksel
42
59
 
43
60
  private
44
61
 
62
+ def forked_database_prefix
63
+ "#{main_database}_"
64
+ end
65
+
45
66
  def pg_connection_args(dbname)
46
67
  "-d #{dbname} --no-password"
47
68
  end
@@ -11,14 +11,16 @@ module Veksel
11
11
  ActiveRecord::DatabaseConfigurations.register_db_config_handler do |env_name, name, url, config|
12
12
  next if url.present?
13
13
  next unless env_name == 'development' || env_name == 'test'
14
+ veksel_adapter = Veksel.adapter_for(config, exception: false)
15
+ next unless veksel_adapter
14
16
 
15
- if PgCluster.new(config).target_populated?("#{config[:database]}#{Veksel.suffix}")
16
- ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, name, config.merge({
17
- database: "#{config[:database]}#{Veksel.suffix}"
18
- }))
19
- else
20
- ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, name, config)
21
- end
17
+ database_name = veksel_adapter.db_name_for_suffix(Veksel.suffix)
18
+ next unless veksel_adapter.target_populated?(database_name)
19
+
20
+ ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, name, config.merge({
21
+ database: database_name,
22
+ veksel_main_database: config[:database],
23
+ }))
22
24
  end
23
25
  end
24
26
  end
data/lib/veksel/suffix.rb CHANGED
@@ -1,17 +1,18 @@
1
1
  module Veksel
2
- PROTECTED_BRANCHES = %w[master main HEAD].freeze
3
-
4
2
  class Suffix
3
+ PROTECTED_BRANCHES = %w[master main].freeze
4
+
5
5
  def initialize(branch_name)
6
6
  @branch_name = branch_name
7
7
  end
8
8
 
9
9
  def to_s
10
10
  case @branch_name
11
+ when 'HEAD'
11
12
  when *PROTECTED_BRANCHES
12
13
  ""
13
14
  else
14
- "_#{@branch_name}"
15
+ @branch_name
15
16
  end
16
17
  end
17
18
  end
@@ -1,3 +1,3 @@
1
1
  module Veksel
2
- VERSION = "0.2.2"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/veksel.rb CHANGED
@@ -1,19 +1,30 @@
1
- if defined?(Rails::VERSION)
2
- if Rails::VERSION::MAJOR < 6
3
- raise "Veksel requires Rails 6 or later"
4
- end
5
- end
6
-
7
1
  require "veksel/version"
8
2
  require "veksel/railtie" if defined?(Rails::Railtie)
9
3
  require "veksel/suffix"
10
4
 
11
5
  module Veksel
6
+ class AdapterNotSupported < StandardError; end
7
+
12
8
  class << self
9
+ def adapter_for(config, exception: true)
10
+ case config[:adapter]
11
+ when 'postgresql'
12
+ require_relative './veksel/pg_cluster'
13
+ Veksel::PgCluster.new(config)
14
+ else
15
+ return unless exception
16
+ raise AdapterNotSupported, "Veksel does not yet support #{config[:adapter]}"
17
+ end
18
+ end
19
+
13
20
  def current_branch
14
21
  `git rev-parse --abbrev-ref HEAD`.strip
15
22
  end
16
23
 
24
+ def active_branches
25
+ `git for-each-ref 'refs/heads/' --format '%(refname)'`.split("\n").map { |ref| ref.sub('refs/heads/', '') }
26
+ end
27
+
17
28
  def skip_fork?
18
29
  suffix.to_s.strip.empty?
19
30
  end
@@ -21,9 +32,5 @@ module Veksel
21
32
  def suffix
22
33
  Suffix.new(current_branch).to_s
23
34
  end
24
-
25
- def prefix(dbname)
26
- dbname.sub(%r[#{Veksel.suffix}$], '_')
27
- end
28
35
  end
29
36
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: veksel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Theodor Tonum
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-12 00:00:00.000000000 Z
11
+ date: 2024-09-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -44,20 +44,6 @@ dependencies:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
46
  version: '0'
47
- - !ruby/object:Gem::Dependency
48
- name: pg
49
- requirement: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - ">="
52
- - !ruby/object:Gem::Version
53
- version: '0'
54
- type: :runtime
55
- prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - ">="
59
- - !ruby/object:Gem::Version
60
- version: '0'
61
47
  description: Seperate databases for every branch in your development environment
62
48
  email:
63
49
  - theodor@tonum.no
@@ -75,6 +61,7 @@ files:
75
61
  - lib/veksel/cli.rb
76
62
  - lib/veksel/commands/clean.rb
77
63
  - lib/veksel/commands/fork.rb
64
+ - lib/veksel/commands/list.rb
78
65
  - lib/veksel/pg_cluster.rb
79
66
  - lib/veksel/railtie.rb
80
67
  - lib/veksel/suffix.rb
@@ -102,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
89
  - !ruby/object:Gem::Version
103
90
  version: '0'
104
91
  requirements: []
105
- rubygems_version: 3.4.19
92
+ rubygems_version: 3.5.16
106
93
  signing_key:
107
94
  specification_version: 4
108
95
  summary: Veksel keeps seperate databases for every branch in your development environment.