schema_migration_monitor 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use ruby-1.9.2-p290@schema_migration_monitor
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in schema_migration_monitor.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Vita Rara (dba Enable Labs)
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # SchemaMigrationMonitor
2
+
3
+ This gem is used to warn developers of pending schema migrations. Each time your development or test environment is loaded, a migration check will occur. If a pending migration is found, the following example output will be in sent to your console output:
4
+
5
+
6
+ ********************************************************
7
+ ** The following migration(s) need to be run: **
8
+ ** - ./db/migrate/20120627151108_create_examples.rb **
9
+ ********************************************************
10
+
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ gem 'schema_migration_monitor', :group => [:development, :test]
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install schema_migration_monitor
25
+
26
+ ## Usage
27
+
28
+ Require the gem!
29
+
30
+ ## Custom Migration Path
31
+
32
+ The gem uses the path './db/migrate' by default to find your migrations. If your project is using a custom path for migrations you must put a '.schema_migration_monitor.yml' in your project's root path. A sample '.schema_migration_monitor.yml' file would be written as follows:
33
+
34
+ migration_path: './custom_migration_path'
35
+
36
+ ## Contributing
37
+
38
+ 1. Fork it
39
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
40
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
41
+ 4. Push to the branch (`git push origin my-new-feature`)
42
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,25 @@
1
+ require 'pathname'
2
+
3
+ class MigrationPathService
4
+
5
+ DEFAULT_MIGRATION_PATH = './db/migrate'
6
+ USER_CONFIG_PATH = '.schema_migration_monitor.yml'
7
+
8
+ class MigrationPathNotFoundError < StandardError; end
9
+
10
+ class << self
11
+ def execute
12
+ return get_user_config_path if Pathname.new(USER_CONFIG_PATH).exist?
13
+ return DEFAULT_MIGRATION_PATH if Pathname.new(DEFAULT_MIGRATION_PATH).exist?
14
+ raise MigrationPathNotFoundError
15
+ end
16
+
17
+ def get_user_config_path
18
+ YAML::load(user_config_file)['migration_path']
19
+ end
20
+
21
+ def user_config_file
22
+ File.open(USER_CONFIG_PATH)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,83 @@
1
+ require 'active_record'
2
+ require File.dirname(__FILE__) + '/migration_path_service'
3
+
4
+ module ActiveRecord
5
+ module ConnectionAdapters
6
+ class ConnectionHandler
7
+
8
+ alias_method :establish_connection_without_monitor, :establish_connection
9
+
10
+ def establish_connection(name, spec)
11
+ result = establish_connection_without_monitor(name,spec)
12
+ monitor_migrations
13
+ result
14
+ end
15
+
16
+ def monitor_migrations
17
+ SchemaMigrationMonitor::Monitor.new.execute
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ module SchemaMigrationMonitor
24
+ class Monitor
25
+
26
+ def initialize(output_stream = $stdout)
27
+ @migration_path = MigrationPathService.execute
28
+ @output_stream = output_stream
29
+ end
30
+
31
+ def execute
32
+ @pending_migrations = get_pending_migrations
33
+ return if @pending_migrations.empty?
34
+ output_report
35
+ end
36
+
37
+ def output_report
38
+ write_boxed_output(report_text.split("\n"))
39
+ end
40
+
41
+ def report_text
42
+ res = "The following migration(s) need to be run:"
43
+ res << @pending_migrations.map { |migration| "\n - #{migration.filename}" }.join
44
+ end
45
+
46
+ def write_boxed_output(text_lines)
47
+ longest_line = get_longest_line(text_lines)
48
+
49
+ wrap_output_in_red do
50
+ @output_stream.write("*" * (longest_line + 6) + "\n")
51
+
52
+ text_lines.each do |line|
53
+ @output_stream.write "** #{line}#{ " " * (longest_line - line.to_s.length) } **\n"
54
+ end
55
+
56
+ @output_stream.write("*" * (longest_line + 6) + "\n")
57
+ end
58
+ end
59
+
60
+ def wrap_output_in_red
61
+ @output_stream.write("\e[1;31m\n")
62
+ yield if block_given?
63
+ @output_stream.write("\n\e[0m")
64
+ end
65
+
66
+ def get_longest_line(text_lines)
67
+ text_lines.inject(0) do |memo, arg|
68
+ line_length = arg.to_s.length
69
+ memo = line_length if line_length > memo
70
+ memo
71
+ end
72
+ end
73
+
74
+ def get_pending_migrations
75
+ migrator.pending_migrations
76
+ end
77
+
78
+ def migrator
79
+ @migrator ||= ActiveRecord::Migrator.new(:up, @migration_path)
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,3 @@
1
+ module SchemaMigrationMonitor
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,2 @@
1
+ require 'schema_migration_monitor/version'
2
+ require 'schema_migration_monitor/monitor'
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/schema_migration_monitor/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Enable Labs"]
6
+ gem.email = ["info@enablelabs.com"]
7
+ gem.description = %q{Make sure the test environment schema is up to date.}
8
+ gem.summary = %q{Make sure the test environment schema is up to date.}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "schema_migration_monitor"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = SchemaMigrationMonitor::VERSION
17
+
18
+ gem.add_development_dependency "rspec"
19
+ gem.add_development_dependency "mocha"
20
+ gem.add_development_dependency 'sqlite3'
21
+
22
+ gem.add_dependency "activerecord"
23
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+ require 'pathname'
3
+
4
+ describe MigrationPathService do
5
+ describe ".execute" do
6
+ describe "when no .schema_migration_monitor config exists" do
7
+ before(:each) do
8
+ Pathname.expects(:new).with(MigrationPathService::USER_CONFIG_PATH).returns( mock(exist?:false) )
9
+ end
10
+
11
+ it "should verify a ./db/migrate directory exists" do
12
+ Pathname.expects(:new).with('./db/migrate').returns( mock(exist?:true) )
13
+ MigrationPathService.execute
14
+ end
15
+
16
+ describe "when a ./db/migrate directory exists" do
17
+ it "should return the string './db/migrate'" do
18
+ Pathname.expects(:new).with('./db/migrate').returns( mock(exist?:true) )
19
+ MigrationPathService.execute.should == './db/migrate'
20
+ end
21
+ end
22
+
23
+ describe "when a ./db/migrate directory does not exist" do
24
+ it "should raise an exception" do
25
+ Pathname.expects(:new).with('./db/migrate').returns( mock(exist?:false) )
26
+
27
+ expect do
28
+ MigrationPathService.execute
29
+ end.should raise_error(MigrationPathService::MigrationPathNotFoundError)
30
+ end
31
+ end
32
+ end
33
+
34
+ describe "when .schema_migration_monitor config exists" do
35
+ let(:user_config_file_contents) do
36
+ contents=<<-EOF
37
+ migration_path: ./custom/migrate
38
+ EOF
39
+ end
40
+
41
+ before(:each) do
42
+ Pathname.expects(:new).with(MigrationPathService::USER_CONFIG_PATH).returns( mock(exist?:true) )
43
+ MigrationPathService.expects(:user_config_file).returns(user_config_file_contents)
44
+ end
45
+
46
+ it "should return the path contained in the .schema_migration_monitor config" do
47
+ MigrationPathService.execute.should == './custom/migrate'
48
+ end
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe SchemaMigrationMonitor::Monitor do
4
+ before(:each) do
5
+ MigrationPathService.stubs(:execute).returns(MigrationPathService::DEFAULT_MIGRATION_PATH)
6
+ end
7
+
8
+ describe "when ActiveRecord establishes a connection" do
9
+ it "should also execute the schema migration monitor" do
10
+ SchemaMigrationMonitor::Monitor.expects(:new).returns(mock(execute: nil))
11
+ ActiveRecord::Base.establish_connection({:adapter => 'sqlite3', database: ":memory:"})
12
+ end
13
+ end
14
+
15
+ describe "when there are no pending migrations" do
16
+ let!(:mock_migrator) do
17
+ mock('migrator').tap do |migrator|
18
+ migrator.expects(:pending_migrations).returns([])
19
+ end
20
+ end
21
+
22
+ before(:each) do
23
+ SchemaMigrationMonitor::Monitor.any_instance.expects(:migrator).returns(mock_migrator)
24
+ end
25
+
26
+ it "should do nothing" do
27
+ output_stream = mock('output_stream')
28
+ output_stream.expects(:write).never
29
+ SchemaMigrationMonitor::Monitor.new(output_stream).execute
30
+ end
31
+ end
32
+
33
+ describe "when there are pending migrations" do
34
+ let(:migrations) { [mock('migration1', filename:'20120101000000_fake_migration')] }
35
+
36
+ let!(:mock_migrator) do
37
+ mock('migrator').tap do |migrator|
38
+ migrator.expects(:pending_migrations).returns(migrations)
39
+ end
40
+ end
41
+
42
+ before(:each) do
43
+ SchemaMigrationMonitor::Monitor.any_instance.expects(:migrator).returns(mock_migrator)
44
+ end
45
+
46
+ it "should print a message to stdout" do
47
+ read_io, write_io = IO.pipe
48
+ SchemaMigrationMonitor::Monitor.new(write_io).execute
49
+ write_io.close_write
50
+ read_io.read.should match(/The following migration\(s\) need to be run/)
51
+ end
52
+ end
53
+
54
+ end
@@ -0,0 +1,7 @@
1
+ Dir['./lib/**/*.rb'].map { |f| require f }
2
+
3
+ require 'mocha'
4
+
5
+ RSpec.configure do |config|
6
+ config.mock_with :mocha
7
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: schema_migration_monitor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Enable Labs
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-27 00:00:00.000000000 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ requirement: &2165800900 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: *2165800900
26
+ - !ruby/object:Gem::Dependency
27
+ name: mocha
28
+ requirement: &2165800480 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: *2165800480
37
+ - !ruby/object:Gem::Dependency
38
+ name: sqlite3
39
+ requirement: &2165800060 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *2165800060
48
+ - !ruby/object:Gem::Dependency
49
+ name: activerecord
50
+ requirement: &2165799640 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :runtime
57
+ prerelease: false
58
+ version_requirements: *2165799640
59
+ description: Make sure the test environment schema is up to date.
60
+ email:
61
+ - info@enablelabs.com
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - .gitignore
67
+ - .rvmrc
68
+ - Gemfile
69
+ - LICENSE
70
+ - README.md
71
+ - Rakefile
72
+ - lib/schema_migration_monitor.rb
73
+ - lib/schema_migration_monitor/migration_path_service.rb
74
+ - lib/schema_migration_monitor/monitor.rb
75
+ - lib/schema_migration_monitor/version.rb
76
+ - schema_migration_monitor.gemspec
77
+ - spec/migration_path_service_spec.rb
78
+ - spec/monitor_spec.rb
79
+ - spec/spec_helper.rb
80
+ has_rdoc: true
81
+ homepage: ''
82
+ licenses: []
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 1.6.2
102
+ signing_key:
103
+ specification_version: 3
104
+ summary: Make sure the test environment schema is up to date.
105
+ test_files:
106
+ - spec/migration_path_service_spec.rb
107
+ - spec/monitor_spec.rb
108
+ - spec/spec_helper.rb