phil_columns 0.1.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 +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +257 -0
- data/Rakefile +6 -0
- data/bin/phil_columns +6 -0
- data/lib/phil_columns.rb +27 -0
- data/lib/phil_columns/archivist.rb +36 -0
- data/lib/phil_columns/cli.rb +162 -0
- data/lib/phil_columns/cli/generate.rb +43 -0
- data/lib/phil_columns/cli/list.rb +68 -0
- data/lib/phil_columns/command.rb +14 -0
- data/lib/phil_columns/command/base.rb +64 -0
- data/lib/phil_columns/command/empty.rb +30 -0
- data/lib/phil_columns/command/generate.rb +9 -0
- data/lib/phil_columns/command/generate/seed.rb +25 -0
- data/lib/phil_columns/command/generator.rb +45 -0
- data/lib/phil_columns/command/install.rb +65 -0
- data/lib/phil_columns/command/list.rb +10 -0
- data/lib/phil_columns/command/list/tagged_with.rb +27 -0
- data/lib/phil_columns/command/list/tags.rb +37 -0
- data/lib/phil_columns/command/mulligan.rb +22 -0
- data/lib/phil_columns/command/seed.rb +44 -0
- data/lib/phil_columns/configuration.rb +88 -0
- data/lib/phil_columns/error.rb +5 -0
- data/lib/phil_columns/filter.rb +66 -0
- data/lib/phil_columns/migrator.rb +80 -0
- data/lib/phil_columns/output.rb +29 -0
- data/lib/phil_columns/railtie.rb +18 -0
- data/lib/phil_columns/seed.rb +37 -0
- data/lib/phil_columns/seed_utils.rb +59 -0
- data/lib/phil_columns/seeder.rb +58 -0
- data/lib/phil_columns/version.rb +3 -0
- data/lib/phil_columns/with_backend.rb +21 -0
- data/phil_columns.gemspec +30 -0
- data/spec/spec_helper.rb +2 -0
- data/templates/seed_class.erb +11 -0
- metadata +200 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module PhilColumns
|
4
|
+
module Command
|
5
|
+
class Mulligan < Base
|
6
|
+
|
7
|
+
def execute
|
8
|
+
load_environment
|
9
|
+
|
10
|
+
say( "- DRY RUN -", :yellow ) if dry_run?
|
11
|
+
|
12
|
+
unless dry_run?
|
13
|
+
migrator.mulligan
|
14
|
+
archivist.clear_seeds
|
15
|
+
end
|
16
|
+
|
17
|
+
seeder.execute
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module PhilColumns
|
4
|
+
module Command
|
5
|
+
class Seed < Base
|
6
|
+
|
7
|
+
def execute
|
8
|
+
load_environment
|
9
|
+
|
10
|
+
say( "- DRY RUN -", :yellow ) if config.dry_run
|
11
|
+
say "Seeding #{method_name} to version #{config.version}", :green
|
12
|
+
|
13
|
+
seeds.each do |seed_meta|
|
14
|
+
say ''
|
15
|
+
say "* Executing seed: #{seed_meta.filepath}", :cyan
|
16
|
+
|
17
|
+
instance = seed_meta.klass.new( config )
|
18
|
+
instance.send( method_name )
|
19
|
+
record_seed( seed_meta )
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def record_seed( seed_meta )
|
26
|
+
if method_name == :up
|
27
|
+
archivist.record_seed( seed_meta.timestamp )
|
28
|
+
else
|
29
|
+
archivist.remove_seed( seed_meta.timestamp )
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def method_name
|
34
|
+
return :down if down?
|
35
|
+
:up
|
36
|
+
end
|
37
|
+
|
38
|
+
def down?
|
39
|
+
config.down
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
require 'pathname'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module PhilColumns
|
6
|
+
class Configuration < SimpleDelegator
|
7
|
+
|
8
|
+
TAG_REGEX = /^~@?(\w+)$/
|
9
|
+
|
10
|
+
def initialize( options={} )
|
11
|
+
@_config = Hashie::Mash.new( read_config_file ).
|
12
|
+
merge( options )
|
13
|
+
|
14
|
+
super( _config )
|
15
|
+
end
|
16
|
+
|
17
|
+
def tags
|
18
|
+
(_config[:tags].nil? || _config[:tags].empty?) ?
|
19
|
+
_config.default_tags :
|
20
|
+
_config.tags
|
21
|
+
end
|
22
|
+
|
23
|
+
def partitioned_tags
|
24
|
+
@partitioned_tags ||= tags.partition { |tag| TAG_REGEX.match( tag ) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def exclusion_tags
|
28
|
+
partitioned_tags.first.
|
29
|
+
map { |tag| TAG_REGEX.match( tag )[1] }
|
30
|
+
end
|
31
|
+
|
32
|
+
def inclusion_tags
|
33
|
+
partitioned_tags.last
|
34
|
+
end
|
35
|
+
|
36
|
+
def config_filepath
|
37
|
+
Pathname.new( '.phil_columns' )
|
38
|
+
end
|
39
|
+
|
40
|
+
# Not sure why we need these explicit proxies
|
41
|
+
def down; _config.down; end
|
42
|
+
def dry_run; _config.dry_run; end
|
43
|
+
def operation; _config.operation; end
|
44
|
+
|
45
|
+
def version
|
46
|
+
if _config.down &&
|
47
|
+
_config.version == 'all'
|
48
|
+
'0'
|
49
|
+
else
|
50
|
+
_config.version
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def slice( *keys )
|
55
|
+
Hashie::Mash.new( _config.select { |k,v| keys.map( &:to_s ).include?( k.to_s ) } )
|
56
|
+
end
|
57
|
+
|
58
|
+
def save_to_file
|
59
|
+
File.open( config_filepath, 'w' ) do |f|
|
60
|
+
f.write( YAML::dump( config_as_hash ))
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
protected
|
65
|
+
|
66
|
+
attr_reader :_config
|
67
|
+
|
68
|
+
def read_config_file
|
69
|
+
return {} unless config_filepath.file?
|
70
|
+
YAML::load_file( config_filepath.expand_path )
|
71
|
+
end
|
72
|
+
|
73
|
+
def config_as_hash
|
74
|
+
slice( *keys_to_persist ).to_h
|
75
|
+
end
|
76
|
+
|
77
|
+
def keys_to_persist
|
78
|
+
%w(
|
79
|
+
default_tags
|
80
|
+
env_files
|
81
|
+
schema_load_strategy
|
82
|
+
schema_unload_strategy
|
83
|
+
seeds_path
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module PhilColumns
|
2
|
+
class Filter
|
3
|
+
|
4
|
+
include SeedUtils
|
5
|
+
|
6
|
+
def initialize( config )
|
7
|
+
@config = config
|
8
|
+
@seeds_path = config.seeds_path
|
9
|
+
@tags = config.tags
|
10
|
+
end
|
11
|
+
|
12
|
+
def seeds
|
13
|
+
load_seeds
|
14
|
+
calculate_seed_set
|
15
|
+
end
|
16
|
+
|
17
|
+
def calculate_seed_set_all
|
18
|
+
end
|
19
|
+
|
20
|
+
def calculate_seed_set_any
|
21
|
+
if any_exclusion_tags?
|
22
|
+
raise PhilColumns::Error,
|
23
|
+
"Cannot provide exclusion tags (#{config.exclusion_tags.join( ', ' )}) when operation is any"
|
24
|
+
end
|
25
|
+
|
26
|
+
seeds_for_current_env.tap do |seeds|
|
27
|
+
unless tags.empty?
|
28
|
+
seeds.select! { |seed_meta| (seed_meta.tags & tags).size > 0 }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
attr_reader :config,
|
36
|
+
:seeds_path,
|
37
|
+
:tags
|
38
|
+
|
39
|
+
def calculate_seed_set
|
40
|
+
send( "calculate_seed_set_#{config.operation}" ).tap do |seeds|
|
41
|
+
unless config.version.blank?
|
42
|
+
if config.down
|
43
|
+
seeds.select! { |seed_meta| seed_meta.timestamp > config.version }
|
44
|
+
else
|
45
|
+
seeds.select! { |seed_meta| seed_meta.timestamp <= config.version }
|
46
|
+
seeds.reject! { |seed_meta| seed_already_executed?( seed_meta.timestamp ) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def seed_already_executed?( version )
|
54
|
+
archivist.seed_already_executed?( version )
|
55
|
+
end
|
56
|
+
|
57
|
+
def archivist
|
58
|
+
@archivist ||= PhilColumns::Archivist.new
|
59
|
+
end
|
60
|
+
|
61
|
+
def any_exclusion_tags?
|
62
|
+
config.exclusion_tags.size > 0
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module PhilColumns
|
2
|
+
class Migrator
|
3
|
+
|
4
|
+
include PhilColumns::Output
|
5
|
+
include PhilColumns::WithBackend
|
6
|
+
|
7
|
+
def initialize( config )
|
8
|
+
@backend = PhilColumns::migrator_klass.new
|
9
|
+
@config = config
|
10
|
+
end
|
11
|
+
|
12
|
+
def clear_migrations_table
|
13
|
+
raise( *error ) unless backend_responds?( :clear_migrations_table )
|
14
|
+
backend.send :clear_migrations_table
|
15
|
+
end
|
16
|
+
|
17
|
+
def down( version=0 )
|
18
|
+
raise( *error ) unless backend_responds?( :down )
|
19
|
+
backend.send :down, version
|
20
|
+
end
|
21
|
+
|
22
|
+
def drop_table( table )
|
23
|
+
raise( *error ) unless backend_responds?( :drop_table )
|
24
|
+
backend.send :drop_table, table
|
25
|
+
end
|
26
|
+
|
27
|
+
def drop_tables
|
28
|
+
raise( *error ) unless backend_responds?( :drop_tables )
|
29
|
+
backend.send :drop_tables
|
30
|
+
end
|
31
|
+
|
32
|
+
def latest_version
|
33
|
+
raise( *error ) unless backend_responds?( :latest_version )
|
34
|
+
backend.send :latest_version
|
35
|
+
end
|
36
|
+
|
37
|
+
def load_schema
|
38
|
+
raise( *error ) unless backend_responds?( :load_schema )
|
39
|
+
backend.send :load_schema
|
40
|
+
end
|
41
|
+
|
42
|
+
def mulligan
|
43
|
+
if config.schema_unload_strategy == 'drop'
|
44
|
+
confirm "Dropping all tables ... ", :cyan do
|
45
|
+
drop_tables
|
46
|
+
clear_migrations_table
|
47
|
+
end
|
48
|
+
else
|
49
|
+
confirm "Migrating DB to version 0 ... ", :cyan do
|
50
|
+
down
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if config.schema_load_strategy == 'load'
|
55
|
+
confirm "Loading schema ... ", :cyan do
|
56
|
+
load_schema
|
57
|
+
end
|
58
|
+
else
|
59
|
+
confirm "Migrating DB to latest version ... ", :cyan do
|
60
|
+
up
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def tables
|
66
|
+
raise( *error ) unless backend_responds?( :tables )
|
67
|
+
backend.send :tables
|
68
|
+
end
|
69
|
+
|
70
|
+
def up( version=nil )
|
71
|
+
raise( *error ) unless backend_responds?( :up )
|
72
|
+
backend.send :up, version
|
73
|
+
end
|
74
|
+
|
75
|
+
protected
|
76
|
+
|
77
|
+
attr_reader :config
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module PhilColumns
|
2
|
+
module Output
|
3
|
+
|
4
|
+
def write( msg, color=:white )
|
5
|
+
$stdout.write( Rainbow( msg ).color( color ))
|
6
|
+
end
|
7
|
+
|
8
|
+
def say( msg, color=:white )
|
9
|
+
$stdout.puts( Rainbow( msg ).color( color ))
|
10
|
+
end
|
11
|
+
|
12
|
+
def say_ok
|
13
|
+
say 'OK', :green
|
14
|
+
end
|
15
|
+
|
16
|
+
def say_error
|
17
|
+
say 'ERROR', :red
|
18
|
+
end
|
19
|
+
|
20
|
+
def confirm( msg, color=:white, &block )
|
21
|
+
write msg, color
|
22
|
+
block.call
|
23
|
+
say_ok
|
24
|
+
rescue
|
25
|
+
say_error
|
26
|
+
raise
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'phil_columns'
|
2
|
+
require 'rails'
|
3
|
+
|
4
|
+
module PhilColumns
|
5
|
+
class Railtie < Rails::Railtie
|
6
|
+
|
7
|
+
#initializer 'phil_columns.insert_into_active_record' do |app|
|
8
|
+
#ActiveSupport.on_load :active_record do
|
9
|
+
#ActiveRecord::Base.send :include, Genesis::ActiveRecordExtensions
|
10
|
+
#end
|
11
|
+
#end
|
12
|
+
|
13
|
+
rake_tasks do
|
14
|
+
Dir[File.join(File.dirname(__FILE__),'tasks/*.rake')].each { |f| load f }
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module PhilColumns
|
2
|
+
class Seed
|
3
|
+
|
4
|
+
def self.envs( *envs )
|
5
|
+
@_envs = envs.sort.map( &:to_s )
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.tags( *tags )
|
9
|
+
@_tags = tags.sort.map( &:to_s )
|
10
|
+
end
|
11
|
+
|
12
|
+
def self._envs
|
13
|
+
@_envs || []
|
14
|
+
end
|
15
|
+
|
16
|
+
def self._tags
|
17
|
+
@_tags || []
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize( config )
|
21
|
+
@config = config
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
attr_reader :config
|
27
|
+
|
28
|
+
def dry_run?
|
29
|
+
config.dry_run
|
30
|
+
end
|
31
|
+
|
32
|
+
def protect( &block )
|
33
|
+
block.call unless dry_run?
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module PhilColumns
|
2
|
+
module SeedUtils
|
3
|
+
|
4
|
+
SEED_REGEX = /^\w+\/\w+\/(\d{14})_(.+)\.rb$/
|
5
|
+
|
6
|
+
def load_seeds
|
7
|
+
seed_filepaths.each do |seed_filepath|
|
8
|
+
load seed_filepath
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def map_seeds
|
13
|
+
seed_filepaths.map do |seed_filepath|
|
14
|
+
klass = discover_seed_class( seed_filepath )
|
15
|
+
|
16
|
+
Hashie::Mash.new( envs: klass._envs,
|
17
|
+
filepath: seed_filepath,
|
18
|
+
klass: klass,
|
19
|
+
tags: klass._tags,
|
20
|
+
timestamp: discover_seed_timestamp( seed_filepath ))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def seed_filepaths
|
25
|
+
seeds = Dir.glob( "#{seeds_path}/*" )
|
26
|
+
if config.down
|
27
|
+
seeds.sort { |a,b| b <=> a }
|
28
|
+
else
|
29
|
+
seeds.sort
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def seeds_for_current_env
|
34
|
+
map_seeds.select { |seed_meta| seed_meta.envs.include?( config.env ) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def each_seed_meta_for_current_env( &block )
|
38
|
+
seeds_for_current_env.each do |seed_meta|
|
39
|
+
block.call( seed_meta )
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def seeds_path
|
44
|
+
config.seeds_path
|
45
|
+
end
|
46
|
+
|
47
|
+
def discover_seed_timestamp( filepath )
|
48
|
+
matches = SEED_REGEX.match( filepath )
|
49
|
+
matches[1]
|
50
|
+
end
|
51
|
+
|
52
|
+
def discover_seed_class( filepath )
|
53
|
+
matches = SEED_REGEX.match( filepath )
|
54
|
+
snakecased = matches[2]
|
55
|
+
snakecased.camelize.constantize
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|