veksel 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5ea3fcb9a16045af635f30b41e951e002e82d3f9de54c2b015f183069bf96a27
4
- data.tar.gz: db4756858dac59fe8e26b67c9d0498aa5bf34eae6c88b4cadc61b1cd326b4c4d
3
+ metadata.gz: 692cda4c884d09fd6388bc304b72a0c41270942654815bebf08a91025a164e89
4
+ data.tar.gz: 81dabd57bfdf2096d6ab75a5bc870a3e39ff122bc790758551830d11414fbcd4
5
5
  SHA512:
6
- metadata.gz: 2b10cd547c563757d88b93e8293b1e3243d1d962708729c6faf1b90e16b8b75de89b52dff3e74d45b9a36ff470fbc6ddb20fdd922fae2c20aa23f9dfaac526e7
7
- data.tar.gz: 23ddfb9527f9ebbee6f6e91bc01553261f0985170ac27aeef8d9717c6d307fd111c5d00b620d04a7043092a7d8f0ff7c2c2be67450003f69286b034a09b3467b
6
+ metadata.gz: 38a76367d2ab7f8603a646f8236c027acea8f5104a4c342169cbacf9d35a02878b9f477a924afe4f9421dc1a9fe0e1abc89bc3918404296e9f883517fceabeae
7
+ data.tar.gz: f919b8fcbdfa107a15e8d784731570656561a0c843b32d489324166431da4897e10e357b4dac2989910c7f49ea3c060888777c0eabcfd97708206b0be760fb1b
data/README.md CHANGED
@@ -63,13 +63,14 @@ $ gem install veksel
63
63
  - Explicit/optional branching
64
64
  - Other database drivers
65
65
 
66
- ## Publishing
66
+ ## Release process
67
67
 
68
- Veksel is published and distributed on [rubygems](https://rubygems.org).
68
+ ```shell
69
+ # Bump version number in lib/veksel/version.rb
69
70
 
70
- To publish a new version:
71
+ # Update lockfile
72
+ bundle install
71
73
 
72
- ```shell
73
74
  # Update changelog, review manually before committing
74
75
  conventional-changelog -p conventionalcommits -i CHANGELOG.md -s
75
76
  git commit -m 'chore(release): x.y.z'
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,27 +2,44 @@ require 'veksel'
2
2
 
3
3
  module Veksel
4
4
  module CLI
5
- def self.suffix
6
- print Veksel.suffix
7
- end
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
8
21
 
9
- def self.fork
10
- return if Veksel.skip_fork?
11
- t1 = Time.now.to_f
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
12
32
 
13
- require 'veksel/commands/fork'
14
- require 'psych'
15
- require 'ostruct'
16
- require 'fileutils'
33
+ def read_config(path)
34
+ raw_config = File.read(path)
17
35
 
18
- config = Psych.safe_load(File.read('config/database.yml'), aliases: true)['development'].transform_keys(&:to_sym)
19
- target_database = config[:database] + Veksel.suffix
20
- db = OpenStruct.new(configuration_hash: config, database: target_database)
21
- Veksel::Commands::Fork.new(db).perform
36
+ if raw_config.include?('<%=')
37
+ require 'erb'
38
+ raw_config = ERB.new(raw_config).result
39
+ end
22
40
 
23
- duration = ((Time.now.to_f - t1) * 1000).to_i
24
- FileUtils.touch('tmp/restart.txt')
25
- puts "Forked database in #{duration.to_i}ms"
41
+ Psych.safe_load(raw_config, aliases: true, symbolize_names: true)
42
+ end
26
43
  end
27
44
  end
28
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.1"
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.1
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-10 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.