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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +3 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +257 -0
  10. data/Rakefile +6 -0
  11. data/bin/phil_columns +6 -0
  12. data/lib/phil_columns.rb +27 -0
  13. data/lib/phil_columns/archivist.rb +36 -0
  14. data/lib/phil_columns/cli.rb +162 -0
  15. data/lib/phil_columns/cli/generate.rb +43 -0
  16. data/lib/phil_columns/cli/list.rb +68 -0
  17. data/lib/phil_columns/command.rb +14 -0
  18. data/lib/phil_columns/command/base.rb +64 -0
  19. data/lib/phil_columns/command/empty.rb +30 -0
  20. data/lib/phil_columns/command/generate.rb +9 -0
  21. data/lib/phil_columns/command/generate/seed.rb +25 -0
  22. data/lib/phil_columns/command/generator.rb +45 -0
  23. data/lib/phil_columns/command/install.rb +65 -0
  24. data/lib/phil_columns/command/list.rb +10 -0
  25. data/lib/phil_columns/command/list/tagged_with.rb +27 -0
  26. data/lib/phil_columns/command/list/tags.rb +37 -0
  27. data/lib/phil_columns/command/mulligan.rb +22 -0
  28. data/lib/phil_columns/command/seed.rb +44 -0
  29. data/lib/phil_columns/configuration.rb +88 -0
  30. data/lib/phil_columns/error.rb +5 -0
  31. data/lib/phil_columns/filter.rb +66 -0
  32. data/lib/phil_columns/migrator.rb +80 -0
  33. data/lib/phil_columns/output.rb +29 -0
  34. data/lib/phil_columns/railtie.rb +18 -0
  35. data/lib/phil_columns/seed.rb +37 -0
  36. data/lib/phil_columns/seed_utils.rb +59 -0
  37. data/lib/phil_columns/seeder.rb +58 -0
  38. data/lib/phil_columns/version.rb +3 -0
  39. data/lib/phil_columns/with_backend.rb +21 -0
  40. data/phil_columns.gemspec +30 -0
  41. data/spec/spec_helper.rb +2 -0
  42. data/templates/seed_class.erb +11 -0
  43. 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,5 @@
1
+ module PhilColumns
2
+ class Error < StandardError
3
+
4
+ end
5
+ 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