phil_columns 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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