db_nazi 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ /Gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ bundler_args: --without dev
3
+ rvm:
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - jruby-19mode
7
+ - rbx-19mode
8
+ - ruby-head
9
+ - jruby-head
data/CHANGELOG ADDED
@@ -0,0 +1,3 @@
1
+ == 0.0.1 2012-06-23
2
+
3
+ * Hi.
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) George Ogata
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,87 @@
1
+ ## DB Nazi
2
+
3
+ Encourages good DB practices in Active Record migrations.
4
+
5
+ ## What?
6
+
7
+ Active Record makes schema changes wonderfully easy, but due to some unfortunate
8
+ defaults, it can be easy to forget to adhere to some basic database best
9
+ practices, such as restricting columns to be non-nullable, or setting meaningful
10
+ varchar limits. DB Nazi forces you to be explicit about these things so you
11
+ can't simply forget.
12
+
13
+ It might take another 2 seconds to type `null: true`, but it may save you hours
14
+ resolving integrity issues down the line!
15
+
16
+ ### Nullability
17
+
18
+ If `DBNazi.require_nullability` is set to `true` (the default), you must specify
19
+ a `:null` option for all columns.
20
+
21
+ add_column :users, :awesome, :boolean # raises DBNazi::NullabilityRequired
22
+ add_column :users, :awesome, :boolean, null: true # ok
23
+
24
+ ### Varchar limits
25
+
26
+ If `DBNazi.require_varchar_limits` is set to `true` (the default), you must
27
+ specify a `:limit` option for all `:string` columns.
28
+
29
+ add_column :users, :name, :string # raises DBNazi::VarcharLimitRequired
30
+ add_column :users, :name, :string, limit: 100 # ok
31
+
32
+ ### Index uniqueness
33
+
34
+ If `DBNazi.require_index_uniqueness` is set the `true` (the default), you must
35
+ specify a `:unique` option for all indexes.
36
+
37
+ add_index :users, :email # raises DBNazi::IndexUniquenessRequired
38
+ add_index :users, :email, unique: false # ok
39
+
40
+ ## Usage
41
+
42
+ Since this tool is about enforcing developer discipline, I suggest including
43
+ this only in the `:development` group in your `Gemfile`.
44
+
45
+ group :development do
46
+ gem 'db_nazi'
47
+ end
48
+
49
+ If you have an established project, you probably don't want to lay the hard line
50
+ on all your existing migrations. You can do this by specifying a minimum
51
+ migration version in `config/environments/development.rb`:
52
+
53
+ DBNazi.from_version = 20120623000000
54
+
55
+ This means "only be a nazi from migration 20120623000000 onwards."
56
+
57
+ If you're using a migration written by a 3rd party, such as a generator you're
58
+ using, I recommend editing it to conform to the rules above. After all, perhaps
59
+ the 3rd party forgot a 'NOT NULL' or two.
60
+
61
+ If you're *really* sure you want to subvert DB Nazi for whatever reason, you may
62
+ do so like this:
63
+
64
+ class BeAJerk < ActiveRecord::Migration
65
+ no_nazi
66
+ ...
67
+ end
68
+
69
+ Or just for a given block like this:
70
+
71
+ DBNazi.disable do
72
+ ...
73
+ end
74
+
75
+ But no soup for you!
76
+
77
+ ## Contributing
78
+
79
+ * [Bug reports](https://github.com/oggy/db_nazi/issues)
80
+ * [Source](https://github.com/oggy/db_nazi)
81
+ * Patches: Fork on Github, send pull request.
82
+ * Include tests where practical.
83
+ * Leave the version alone, or bump it in a separate commit.
84
+
85
+ ## Copyright
86
+
87
+ Copyright (c) George Ogata. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'ritual'
2
+
3
+ task :ci do
4
+ sh 'bundle exec testrb test/unit'
5
+ sh 'bundle exec testrb test/integration'
6
+ end
data/db_nazi.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.unshift File.expand_path('lib', File.dirname(__FILE__))
3
+ require 'db_nazi/version'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = 'db_nazi'
7
+ gem.version = DBNazi::VERSION
8
+ gem.authors = ['George Ogata']
9
+ gem.email = ['george.ogata@gmail.com']
10
+ gem.license = 'MIT'
11
+ gem.description = ""
12
+ gem.summary = "Encourage good DB practices in ActiveRecord migrations."
13
+ gem.homepage = ""
14
+
15
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ gem.files = `git ls-files`.split("\n")
17
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+
19
+ gem.add_runtime_dependency 'activerecord', '~> 3.2.6'
20
+ gem.add_development_dependency 'ritual', '~> 0.4.1'
21
+ gem.add_development_dependency 'temporaries', '~> 0.2.0'
22
+ gem.add_development_dependency 'looksee', '~> 0.2.0'
23
+ gem.add_development_dependency 'debugger', '~> 1.1.3'
24
+ gem.add_development_dependency 'sqlite3', '~> 1.3.6'
25
+ end
@@ -0,0 +1,31 @@
1
+ module DBNazi
2
+ module AbstractAdapter
3
+ def add_column(table_name, column_name, type, options = {})
4
+ if DBNazi.enabled?(:require_nullability)
5
+ options.key?(:null) or
6
+ raise NullabilityRequired, "[db_nazi] :null parameter required"
7
+ end
8
+ if DBNazi.enabled?(:require_varchar_limits)
9
+ # AR calls #to_sym on type, so do the same here.
10
+ type.to_sym == :string && !options.key?(:limit) and
11
+ raise VarcharLimitRequired, "[db_nazi] string column requires :limit parameter"
12
+ end
13
+ super
14
+ end
15
+
16
+ def add_index(table_name, column_name, options = {})
17
+ if DBNazi.enabled?(:require_index_uniqueness)
18
+ options.key?(:unique) or
19
+ raise IndexUniquenessRequired, "[db_nazi] :unique parameter required"
20
+ end
21
+ end
22
+
23
+ def create_table(name, *)
24
+ if name.to_s == ActiveRecord::Migrator.schema_migrations_table_name.to_s
25
+ DBNazi.disable { super }
26
+ else
27
+ super
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,21 @@
1
+ module DBNazi
2
+ module Migration
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ def nazi_disabled?
8
+ self.class.nazi_disabled?
9
+ end
10
+
11
+ module ClassMethods
12
+ def no_nazi
13
+ @nazi_disabled = true
14
+ end
15
+
16
+ def nazi_disabled?
17
+ @nazi_disabled
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ module DBNazi
2
+ module MigrationProxy
3
+ def self.included(base)
4
+ base.class_eval do
5
+ alias migrate_without_db_nazi migrate
6
+ alias migrate migrate_with_db_nazi
7
+ end
8
+ end
9
+
10
+ def migrate_with_db_nazi(direction)
11
+ action = DBNazi.enabled_for_migration?(migration, version) ? :enable : :disable
12
+ DBNazi.send(action) do
13
+ migrate_without_db_nazi(direction)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ module DBNazi
2
+ module TableDefinition
3
+ def self.included(base)
4
+ base.class_eval do
5
+ alias column_without_db_nazi column
6
+ alias column column_with_db_nazi
7
+ end
8
+ end
9
+
10
+ def column_with_db_nazi(name, type, options = {})
11
+ if DBNazi.enabled?(:require_nullability) && type != :primary_key
12
+ options.key?(:null) or
13
+ raise NullabilityRequired, "[db_nazi] :null parameter required"
14
+ end
15
+ if DBNazi.enabled?(:require_varchar_limits)
16
+ # AR calls #to_sym on type, so do the same here.
17
+ type.to_sym == :string && !options.key?(:limit) and
18
+ raise VarcharLimitRequired, "[db_nazi] string column requires :limit parameter"
19
+ end
20
+ column_without_db_nazi(name, type, options)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,11 @@
1
+ module DBNazi
2
+ VERSION = [0, 0, 1]
3
+
4
+ class << VERSION
5
+ include Comparable
6
+
7
+ def to_s
8
+ join('.')
9
+ end
10
+ end
11
+ end
data/lib/db_nazi.rb ADDED
@@ -0,0 +1,66 @@
1
+ module DBNazi
2
+ autoload :AbstractAdapter, 'db_nazi/abstract_adapter'
3
+ autoload :Migration, 'db_nazi/migration'
4
+ autoload :MigrationProxy, 'db_nazi/migration_proxy'
5
+ autoload :TableDefinition, 'db_nazi/table_definition'
6
+ autoload :VERSION, 'db_nazi/version'
7
+
8
+ ArgumentError = Class.new(::ArgumentError)
9
+ NullabilityRequired = Class.new(ArgumentError)
10
+ VarcharLimitRequired = Class.new(ArgumentError)
11
+ IndexUniquenessRequired = Class.new(ArgumentError)
12
+
13
+ class << self
14
+ attr_accessor :enabled
15
+ attr_accessor :from_version
16
+ attr_accessor :require_nullability
17
+ attr_accessor :require_varchar_limits
18
+ attr_accessor :require_index_uniqueness
19
+
20
+ def enabled?(setting)
21
+ @enabled && send(setting)
22
+ end
23
+
24
+ def enable
25
+ original_enabled = @enabled
26
+ @enabled = true
27
+ begin
28
+ yield
29
+ ensure
30
+ @enabled = original_enabled
31
+ end
32
+ end
33
+
34
+ def disable
35
+ original_enabled = @enabled
36
+ @enabled = false
37
+ begin
38
+ yield
39
+ ensure
40
+ @enabled = original_enabled
41
+ end
42
+ end
43
+
44
+ def enabled_for_migration?(migration, version)
45
+ return false if !@enabled
46
+ return false if migration.nazi_disabled?
47
+ return false if DBNazi.from_version && DBNazi.from_version > version
48
+ true
49
+ end
50
+
51
+ def reset
52
+ self.enabled = true
53
+ self.from_version = nil
54
+ self.require_nullability = true
55
+ self.require_varchar_limits = true
56
+ self.require_index_uniqueness = true
57
+ end
58
+ end
59
+
60
+ reset
61
+ end
62
+
63
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.__send__ :include, DBNazi::AbstractAdapter
64
+ ActiveRecord::ConnectionAdapters::TableDefinition.__send__ :include, DBNazi::TableDefinition
65
+ ActiveRecord::Migration.__send__ :include, DBNazi::Migration
66
+ ActiveRecord::MigrationProxy.__send__ :include, DBNazi::MigrationProxy
data/test/database.yml ADDED
@@ -0,0 +1,2 @@
1
+ sqlite3:
2
+ database: ':memory:'
@@ -0,0 +1,76 @@
1
+ require_relative '../test_helper'
2
+
3
+ describe DBNazi do
4
+ use_database
5
+ use_temporary_directory "#{ROOT}/test/tmp"
6
+
7
+ # Migrations use Kernel.puts. Lame.
8
+ out = Class.new { def write(*) end; def flush(*) end }.new
9
+ use_global_value :stdout, out
10
+
11
+ before do
12
+ DBNazi.reset
13
+ @original_pwd = Dir.pwd
14
+ Dir.chdir "#{ROOT}/test/tmp"
15
+ end
16
+
17
+ after do
18
+ Dir.chdir @original_pwd
19
+ end
20
+
21
+ before do
22
+ DBNazi.reset
23
+ end
24
+
25
+ it "raises an error when there is a careless migration" do
26
+ create_bad_migration(1)
27
+ run_migrations
28
+ errors.size.must_equal 1
29
+ end
30
+
31
+ it "only applies nazism to selected migrations" do
32
+ create_bad_migration(1)
33
+ create_good_migration(2)
34
+ DBNazi.from_version = 2
35
+ run_migrations
36
+ errors.size.must_equal 0
37
+ end
38
+
39
+ def create_bad_migration(version)
40
+ create_migration(version, :bad)
41
+ end
42
+
43
+ def create_good_migration(version)
44
+ create_migration(version, :good)
45
+ end
46
+
47
+ def create_migration(version, good_or_bad)
48
+ write_file "#{version}_test_migration_#{version}.rb", <<-EOS
49
+ |class TestMigration#{version} < ActiveRecord::Migration
50
+ | def up
51
+ | create_table 'table_#{version}' do |t|
52
+ | t.boolean :test_column #{good_or_bad == :good ? ', null: true' : ''}
53
+ | end
54
+ | end
55
+ |end
56
+ EOS
57
+ end
58
+
59
+ def run_migrations
60
+ ActiveRecord::Migrator.migrate('.', nil)
61
+ rescue StandardError => e
62
+ if e.message.include?('[db_nazi]')
63
+ errors << e
64
+ else
65
+ raise
66
+ end
67
+ end
68
+
69
+ def errors
70
+ @errors ||= []
71
+ end
72
+
73
+ def write_file(name, content)
74
+ open(name, 'w') { |f| f.print content.gsub(/^ *\|/, '') }
75
+ end
76
+ end
@@ -0,0 +1,46 @@
1
+ $:.unshift File.expand_path('../lib', File.dirname(__FILE__))
2
+ ROOT = File.expand_path('..', File.dirname(__FILE__))
3
+
4
+ require 'minitest/spec'
5
+ require 'yaml'
6
+ require 'active_record'
7
+ require 'temporaries'
8
+ require 'debugger'
9
+ require 'db_nazi'
10
+
11
+ ADAPTER = ENV['DBNAZI_ADAPTER'] || 'sqlite3'
12
+ CONNECTION = YAML.load_file("#{ROOT}/test/database.yml")[ADAPTER].merge(adapter: ADAPTER)
13
+ ActiveRecord::Base.establish_connection(CONNECTION)
14
+
15
+ MiniTest::Spec.class_eval do
16
+ def recreate_database
17
+ drop_database
18
+ case ADAPTER
19
+ when 'sqlite3'
20
+ ActiveRecord::Base.establish_connection(CONNECTION)
21
+ when 'mysql2', 'postgresql'
22
+ ActiveRecord::Base.connection.create_database 'db_nazi_test'
23
+ else
24
+ raise "can't create database for #{ADAPTER}"
25
+ end
26
+ end
27
+
28
+ def drop_database
29
+ case ADAPTER
30
+ when 'sqlite3'
31
+ when 'mysql2', 'postgresql'
32
+ ActiveRecord::Base.connection.drop_database 'db_nazi_test'
33
+ else
34
+ raise "can't drop database for #{ADAPTER}"
35
+ end
36
+ end
37
+
38
+ def connection
39
+ ActiveRecord::Base.connection
40
+ end
41
+
42
+ def self.use_database
43
+ before { recreate_database }
44
+ after { drop_database }
45
+ end
46
+ end
@@ -0,0 +1,99 @@
1
+ require_relative '../../test_helper'
2
+
3
+ describe DBNazi::AbstractAdapter do
4
+ use_database
5
+
6
+ before do
7
+ DBNazi.reset
8
+ connection.create_table 'test_table'
9
+ end
10
+
11
+ describe "nullability" do
12
+ describe "when it is required" do
13
+ use_attribute_value DBNazi, :require_nullability, true
14
+
15
+ it "raises a DBNazi::NullabilityRequired if :null is not specified when adding a column" do
16
+ lambda do
17
+ connection.add_column 'test_table', 'test_column', :boolean
18
+ end.must_raise(DBNazi::NullabilityRequired)
19
+ end
20
+
21
+ it "does not raise a DBNazi::NullabilityRequired if :null is true when adding a column" do
22
+ connection.add_column 'test_table', 'test_column', :boolean, null: true
23
+ end
24
+
25
+ it "does not raise a DBNazi::NullabilityRequired if :null is false when adding a column" do
26
+ connection.add_column 'test_table', 'test_column', :boolean, null: false, default: false
27
+ end
28
+ end
29
+
30
+ describe "when it is not required" do
31
+ use_attribute_value DBNazi, :require_nullability, false
32
+
33
+ it "does not raise a DBNazi::NullabilityRequired if :null is not specified when adding a column" do
34
+ connection.add_column 'test_table', 'test_column', :boolean
35
+ end
36
+ end
37
+ end
38
+
39
+ describe "varchar limits" do
40
+ describe "when they are required" do
41
+ use_attribute_value DBNazi, :require_varchar_limits, true
42
+
43
+ it "raises a DBNazi::VarcharLimitRequired if :limit is not specified for a :string column" do
44
+ lambda do
45
+ connection.add_column 'test_table', 'test_column', :string, null: true
46
+ end.must_raise(DBNazi::VarcharLimitRequired)
47
+ end
48
+
49
+ it "does not raise a DBNazi::VarcharLimitRequired if :limit is specified for a :string column" do
50
+ connection.add_column 'test_table', 'test_column', :string, limit: 255, null: true
51
+ end
52
+ end
53
+
54
+ describe "when they are not required" do
55
+ use_attribute_value DBNazi, :require_varchar_limits, false
56
+
57
+ it "does not raise a DBNazi::VarcharLimitRequired if :limit is not specified for a :string column" do
58
+ connection.add_column 'test_table', 'test_column', :string, null: true
59
+ end
60
+ end
61
+ end
62
+
63
+ describe "index uniqueness" do
64
+ before do
65
+ connection.add_column 'test_table', 'test_column', :boolean, null: true
66
+ end
67
+
68
+ describe "when it is required" do
69
+ use_attribute_value DBNazi, :require_index_uniqueness, true
70
+
71
+ it "raises a DBNazi::IndexUniquenessRequired if :unique is not specified for an index" do
72
+ lambda do
73
+ connection.add_index 'test_table', 'test_column'
74
+ end.must_raise(DBNazi::IndexUniquenessRequired)
75
+ end
76
+
77
+ it "does not raise a DBNazi::IndexUniquenessRequired if :unique is true for an index" do
78
+ connection.add_index 'test_table', 'test_column', unique: true
79
+ end
80
+
81
+ it "does not raise a DBNazi::IndexUniquenessRequired if :unique is false for an index" do
82
+ connection.add_index 'test_table', 'test_column', unique: false
83
+ end
84
+ end
85
+
86
+ describe "when it is not required" do
87
+ use_attribute_value DBNazi, :require_index_uniqueness, false
88
+
89
+ it "does not raise a DBNazi::IndexUniquenessRequired if :unique is not specified for an index" do
90
+ connection.add_index 'test_table', 'test_column'
91
+ end
92
+ end
93
+ end
94
+
95
+ it "does not prevent construction of the schema migrations table" do
96
+ # AR doesn't specify a varchar limit for the version column. Lame.
97
+ connection.initialize_schema_migrations_table
98
+ end
99
+ end
@@ -0,0 +1,10 @@
1
+ require_relative '../../test_helper'
2
+
3
+ describe DBNazi::Migration do
4
+ describe "#nazi_disabled?" do
5
+ it "is true if no_nazi was called on the migration class" do
6
+ klass = Class.new(ActiveRecord::Migration) { no_nazi }
7
+ klass.new.nazi_disabled?.must_equal true
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,68 @@
1
+ require_relative '../../test_helper'
2
+
3
+ describe DBNazi::MigrationProxy do
4
+ use_database
5
+ use_temporary_directory "#{ROOT}/test/tmp"
6
+
7
+ # Migrations use Kernel.puts. Lame.
8
+ out = Class.new { def write(*) end; def flush(*) end }.new
9
+ use_global_value :stdout, out
10
+
11
+ before do
12
+ @original_pwd = Dir.pwd
13
+ Dir.chdir "#{ROOT}/test/tmp"
14
+ end
15
+
16
+ after do
17
+ Dir.chdir @original_pwd
18
+ end
19
+
20
+ before do
21
+ @migration_proxy = ActiveRecord::MigrationProxy.new("test_migration", 10, "test_migration.rb", nil)
22
+ @migration_class = Class.new(ActiveRecord::Migration) do
23
+ attr_reader :enabled
24
+
25
+ def up
26
+ @enabled = DBNazi.enabled
27
+ end
28
+ end
29
+ migration = @migration = @migration_class.new
30
+ @migration_proxy.singleton_class.send(:define_method, :migration) { migration }
31
+ end
32
+
33
+ describe "#migrate" do
34
+ it "disables DBNazi when no_nazi is used" do
35
+ @migration_class.no_nazi
36
+ @migration_proxy.migrate(:up)
37
+ @migration.enabled.must_equal false
38
+ end
39
+
40
+ describe "when DBNazi.from_version is set" do
41
+ it "disables DBNazi if this migration comes before the minimum version" do
42
+ with_attribute_value DBNazi, :from_version, 11 do
43
+ @migration_proxy.migrate(:up)
44
+ @migration.enabled.must_equal false
45
+ end
46
+ end
47
+
48
+ it "does not disable DBNazi if this migration is the minimum version" do
49
+ with_attribute_value DBNazi, :from_version, 10 do
50
+ @migration_proxy.migrate(:up)
51
+ @migration.enabled.must_equal true
52
+ end
53
+ end
54
+
55
+ it "does not disable DBNazi if this migration comes after the minimum version" do
56
+ with_attribute_value DBNazi, :from_version, 9 do
57
+ @migration_proxy.migrate(:up)
58
+ @migration.enabled.must_equal true
59
+ end
60
+ end
61
+ end
62
+
63
+ it "enables DBNazi otherwise" do
64
+ @migration_proxy.migrate(:up)
65
+ @migration.enabled.must_equal true
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,75 @@
1
+ require_relative '../../test_helper'
2
+
3
+ describe DBNazi::TableDefinition do
4
+ use_database
5
+
6
+ before do
7
+ DBNazi.reset
8
+ end
9
+
10
+ describe "nullability" do
11
+ describe "when it is required" do
12
+ use_attribute_value DBNazi, :require_nullability, true
13
+
14
+ it "raises a DBNazi::NullabilityRequired if :null is not specified when adding a column" do
15
+ connection.create_table 'test_table' do |t|
16
+ lambda do
17
+ t.column 'test_column', :boolean
18
+ end.must_raise(DBNazi::NullabilityRequired)
19
+ end
20
+ end
21
+
22
+ it "does not raise a DBNazi::NullabilityRequired if :null is true when adding a column" do
23
+ connection.create_table 'test_table' do |t|
24
+ t.column 'test_column', :boolean, null: true
25
+ end
26
+ end
27
+
28
+ it "does not raise a DBNazi::NullabilityRequired if :null is false when adding a column" do
29
+ connection.create_table 'test_table' do |t|
30
+ t.column 'test_column', :boolean, null: false, default: false
31
+ end
32
+ end
33
+ end
34
+
35
+ describe "when it is not required" do
36
+ use_attribute_value DBNazi, :require_nullability, false
37
+
38
+ it "does not raise a DBNazi::NullabilityRequired if :null is not specified when adding a column" do
39
+ connection.create_table 'test_table' do |t|
40
+ t.column 'test_column', :boolean
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ describe "varchar limits" do
47
+ describe "when they are required" do
48
+ use_attribute_value DBNazi, :require_varchar_limits, true
49
+
50
+ it "raises a DBNazi::VarcharLimitRequired if :limit is not specified for a :string column" do
51
+ connection.create_table 'test_table' do |t|
52
+ lambda do
53
+ t.column 'test_column', :string, null: true
54
+ end.must_raise(DBNazi::VarcharLimitRequired)
55
+ end
56
+ end
57
+
58
+ it "does not raise a DBNazi::VarcharLimitRequired if :limit is specified for a :string column" do
59
+ connection.create_table 'test_table' do |t|
60
+ t.column 'test_column', :string, limit: 255, null: true
61
+ end
62
+ end
63
+ end
64
+
65
+ describe "when they are not required" do
66
+ use_attribute_value DBNazi, :require_varchar_limits, false
67
+
68
+ it "does not raise a DBNazi::VarcharLimitRequired if :limit is not specified for a :string column" do
69
+ connection.create_table 'test_table' do |t|
70
+ t.column 'test_column', :string, null: true
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,51 @@
1
+ require_relative '../test_helper'
2
+
3
+ describe DBNazi do
4
+ before do
5
+ DBNazi.reset
6
+ end
7
+
8
+ describe ".enable" do
9
+ it "should turn on all feature flags for the duration of the block" do
10
+ DBNazi.enabled = false
11
+ DBNazi.enable do
12
+ DBNazi.enabled?(:require_nullability).must_equal true
13
+ DBNazi.enabled?(:require_varchar_limits).must_equal true
14
+ DBNazi.enabled?(:require_index_uniqueness).must_equal true
15
+ end
16
+ DBNazi.enabled?(:require_nullability).must_equal false
17
+ DBNazi.enabled?(:require_varchar_limits).must_equal false
18
+ DBNazi.enabled?(:require_index_uniqueness).must_equal false
19
+ end
20
+
21
+ it "should restore a flag to true if it was already enabled" do
22
+ DBNazi.enabled = true
23
+ DBNazi.enable do
24
+ DBNazi.enabled?(:require_nullability).must_equal true
25
+ end
26
+ DBNazi.enabled?(:require_nullability).must_equal true
27
+ end
28
+ end
29
+
30
+ describe ".disable" do
31
+ it "should turn off all feature flags for the duration of the block" do
32
+ DBNazi.enabled = true
33
+ DBNazi.disable do
34
+ DBNazi.enabled?(:require_nullability).must_equal false
35
+ DBNazi.enabled?(:require_varchar_limits).must_equal false
36
+ DBNazi.enabled?(:require_index_uniqueness).must_equal false
37
+ end
38
+ DBNazi.enabled?(:require_nullability).must_equal true
39
+ DBNazi.enabled?(:require_varchar_limits).must_equal true
40
+ DBNazi.enabled?(:require_index_uniqueness).must_equal true
41
+ end
42
+
43
+ it "should restore a flag to false if it was already disabled" do
44
+ DBNazi.enabled = false
45
+ DBNazi.disable do
46
+ DBNazi.enabled?(:require_nullability).must_equal false
47
+ end
48
+ DBNazi.enabled?(:require_nullability).must_equal false
49
+ end
50
+ end
51
+ end
metadata ADDED
@@ -0,0 +1,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: db_nazi
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - George Ogata
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 3.2.6
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 3.2.6
30
+ - !ruby/object:Gem::Dependency
31
+ name: ritual
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.4.1
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.4.1
46
+ - !ruby/object:Gem::Dependency
47
+ name: temporaries
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 0.2.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 0.2.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: looksee
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 0.2.0
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 0.2.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: debugger
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 1.1.3
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 1.1.3
94
+ - !ruby/object:Gem::Dependency
95
+ name: sqlite3
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: 1.3.6
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: 1.3.6
110
+ description: ''
111
+ email:
112
+ - george.ogata@gmail.com
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - .gitignore
118
+ - .travis.yml
119
+ - CHANGELOG
120
+ - Gemfile
121
+ - LICENSE
122
+ - README.markdown
123
+ - Rakefile
124
+ - db_nazi.gemspec
125
+ - lib/db_nazi.rb
126
+ - lib/db_nazi/abstract_adapter.rb
127
+ - lib/db_nazi/migration.rb
128
+ - lib/db_nazi/migration_proxy.rb
129
+ - lib/db_nazi/table_definition.rb
130
+ - lib/db_nazi/version.rb
131
+ - test/database.yml
132
+ - test/integration/test_integration.rb
133
+ - test/test_helper.rb
134
+ - test/unit/db_nazi/test_abstract_adapter.rb
135
+ - test/unit/db_nazi/test_migration.rb
136
+ - test/unit/db_nazi/test_migration_proxy.rb
137
+ - test/unit/db_nazi/test_table_definition.rb
138
+ - test/unit/test_db_nazi.rb
139
+ homepage: ''
140
+ licenses:
141
+ - MIT
142
+ post_install_message:
143
+ rdoc_options: []
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ none: false
148
+ requirements:
149
+ - - ! '>='
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ segments:
153
+ - 0
154
+ hash: 2002143158366431765
155
+ required_rubygems_version: !ruby/object:Gem::Requirement
156
+ none: false
157
+ requirements:
158
+ - - ! '>='
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ segments:
162
+ - 0
163
+ hash: 2002143158366431765
164
+ requirements: []
165
+ rubyforge_project:
166
+ rubygems_version: 1.8.24
167
+ signing_key:
168
+ specification_version: 3
169
+ summary: Encourage good DB practices in ActiveRecord migrations.
170
+ test_files:
171
+ - test/database.yml
172
+ - test/integration/test_integration.rb
173
+ - test/test_helper.rb
174
+ - test/unit/db_nazi/test_abstract_adapter.rb
175
+ - test/unit/db_nazi/test_migration.rb
176
+ - test/unit/db_nazi/test_migration_proxy.rb
177
+ - test/unit/db_nazi/test_table_definition.rb
178
+ - test/unit/test_db_nazi.rb