mv-core 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/LICENSE.txt +20 -0
  2. data/README.rdoc +88 -0
  3. data/lib/migration_validators/active_record/base.rb +24 -0
  4. data/lib/migration_validators/active_record/connection_adapters/abstract_adapter.rb +36 -0
  5. data/lib/migration_validators/active_record/connection_adapters/native_adapter.rb +118 -0
  6. data/lib/migration_validators/active_record/connection_adapters/table.rb +15 -0
  7. data/lib/migration_validators/active_record/connection_adapters/table_definition.rb +24 -0
  8. data/lib/migration_validators/active_record/migration.rb +29 -0
  9. data/lib/migration_validators/active_record/schema.rb +31 -0
  10. data/lib/migration_validators/active_record/schema_dumper.rb +24 -0
  11. data/lib/migration_validators/adapters/base.rb +15 -0
  12. data/lib/migration_validators/adapters/containers.rb +102 -0
  13. data/lib/migration_validators/adapters/routing.rb +104 -0
  14. data/lib/migration_validators/adapters/syntax.rb +53 -0
  15. data/lib/migration_validators/adapters/validator_definitions.rb +131 -0
  16. data/lib/migration_validators/core/adapter_wrapper.rb +88 -0
  17. data/lib/migration_validators/core/db_validator.rb +178 -0
  18. data/lib/migration_validators/core/statement_builder.rb +60 -0
  19. data/lib/migration_validators/core/validator_container.rb +110 -0
  20. data/lib/migration_validators/core/validator_definition.rb +91 -0
  21. data/lib/migration_validators/core/validator_router.rb +45 -0
  22. data/lib/mv-core.rb +100 -0
  23. data/lib/options.rb +7 -0
  24. data/spec/migration_validators/active_record/connection_adapters/abstract_adapter_spec.rb +440 -0
  25. data/spec/migration_validators/active_record/connection_adapters/table_definition_spec.rb +4 -0
  26. data/spec/migration_validators/active_record/migration.rb +82 -0
  27. data/spec/migration_validators/active_record/schema_dumper_spec.rb +44 -0
  28. data/spec/migration_validators/adapters/base_spec.rb +89 -0
  29. data/spec/migration_validators/core/adapter_wrapper_spec.rb +168 -0
  30. data/spec/migration_validators/core/db_validator_spec.rb +347 -0
  31. data/spec/migration_validators/core/statement_builder_spec.rb +36 -0
  32. data/spec/migration_validators/core/validator_container_spec.rb +121 -0
  33. data/spec/migration_validators/core/validator_definition_spec.rb +131 -0
  34. data/spec/migration_validators/core/validator_router_spec.rb +60 -0
  35. data/spec/mv-core_spec.rb +4 -0
  36. data/spec/spec_helper.rb +15 -0
  37. data/spec/support/factories/db_validator.rb +43 -0
  38. metadata +152 -0
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Valeriy Prokopchuk
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.
@@ -0,0 +1,88 @@
1
+ = Abbreviations
2
+
3
+ MV - Migration Validators Projects. All gems that belongs to that project are prefixed with mv-*
4
+
5
+ = Project goals
6
+
7
+ Main goal of the project is to allow developer to express database constraints in a Ruby like manner.
8
+
9
+ = mv-core
10
+
11
+ mv-core is a set of core classes that are used everywhere across Migration Validators project gems.
12
+
13
+ = Usage
14
+
15
+ Create new table:
16
+
17
+ create_table do |t|
18
+ t.string :str_column, :validates => {:uniqueness => true, :inclusion => {:in => 1..3}}
19
+ t.column :column_name, :integer, :validates => {:exclusion => {:in => [1,2,3]}}
20
+ end
21
+
22
+ Modify existing table:
23
+
24
+ change_table do |t|
25
+ t.change :str_column, :integer, :validates => {:exclusion => {:in => [1,2,3]}}
26
+ t.change_validates :column_name, :inclusion => {:in => 1..3}
27
+ end
28
+
29
+ Update existing table with new validators:
30
+
31
+ validate_column :table_name, :str_column, :exclusion => {:in => [1,2,3]}
32
+
33
+ Remove existing validators:
34
+
35
+ change_table do |t|
36
+ t.change :str_column, :integer, :validates => {:exclusion => false}
37
+ end
38
+ validate_column table_name, :str_column, :exclusion => false
39
+
40
+ Define the way how validator will be created:
41
+
42
+ as trigger:
43
+
44
+ validate_column :table_name, :str_column, :validates => {:uniqueness => true, :as => :trigger }
45
+
46
+ as check constraint:
47
+
48
+ validate_column :table_name, :str_column, :validates => {:uniqueness => true, :as => :check }
49
+
50
+ Define event that will be validates:
51
+
52
+ while new record creation:
53
+
54
+ validate_column :table_name, :str_column, :validates => {:uniqueness => true, :on => :create }
55
+
56
+ or while updating of existing record:
57
+
58
+ validate_column :table_name, :str_column, :validates => {:uniqueness => true, :on => :create }
59
+
60
+ Supported validators and their properties might vary from one db driver to another. See detailed properties description in correspondent driver section.
61
+
62
+ = Drivers
63
+
64
+ Currently there are drivers for MySQL, PostgreSQL and SQLite RDBMS
65
+
66
+ So - see detailed info here:
67
+
68
+ PostgreSQL: https://github.com/vprokopchuk256/mv-postgresql
69
+
70
+ MySQL: https://github.com/vprokopchuk256/mv-mysql
71
+
72
+ SQLite: https://github.com/vprokopchuk256/mv-sqlite
73
+
74
+
75
+ == Contributing to mv-core
76
+
77
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
78
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
79
+ * Fork the project
80
+ * Start a feature/bugfix branch
81
+ * Commit and push until you are happy with your contribution
82
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
83
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
84
+
85
+ == Copyright
86
+
87
+ Copyright (c) 2011 Valeriy Prokopchuk. See LICENSE.txt for
88
+ further details.
@@ -0,0 +1,24 @@
1
+ module MigrationValidators
2
+ module ActiveRecord
3
+ module Base
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ base.class_eval do
7
+ class << self
8
+ alias_method_chain :establish_connection, :validators
9
+ end
10
+ end
11
+ end
12
+
13
+ module ClassMethods
14
+ def establish_connection_with_validators *args
15
+ establish_connection_without_validators *args
16
+
17
+ connection.class.class_eval {
18
+ include MigrationValidators::ActiveRecord::ConnectionAdapters::NativeAdapter
19
+ } unless connection.class.include?(MigrationValidators::ActiveRecord::ConnectionAdapters::NativeAdapter)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,36 @@
1
+ module MigrationValidators
2
+ module ActiveRecord
3
+ module ConnectionAdapters
4
+ module AbstractAdapter
5
+ def self.included(base)
6
+ base.class_eval do
7
+ alias_method_chain :initialize_schema_migrations_table, :validators
8
+ end
9
+ end
10
+
11
+ def initialize_migration_validators_table
12
+ migrations_table = MigrationValidators.migration_validators_table_name
13
+
14
+ unless table_exists?(migrations_table)
15
+ create_table migrations_table do |t|
16
+ t.string :table_name, :null => false, :limit => 255
17
+ t.string :column_name, :null => true, :limit => 255
18
+ t.string :validator_name, :null => false, :limit => 255
19
+ t.text :options
20
+ t.text :constraints
21
+ end
22
+
23
+ add_index migrations_table, :table_name
24
+ add_index migrations_table, [:table_name, :column_name], :name => 'mg_vld_tbl_clm'
25
+ add_index migrations_table, [:table_name, :column_name, :validator_name], :name => 'mg_vld_tbl_clm_vldn', :unique => true
26
+ end
27
+ end
28
+
29
+ def initialize_schema_migrations_table_with_validators
30
+ initialize_schema_migrations_table_without_validators
31
+ initialize_migration_validators_table
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,118 @@
1
+ module MigrationValidators
2
+ module ActiveRecord
3
+ module ConnectionAdapters
4
+ module NativeAdapter
5
+ def self.included(base)
6
+ base.class_eval do
7
+ alias_method_chain :drop_table, :validators
8
+ alias_method_chain :remove_column, :validators
9
+ alias_method_chain :rename_column, :validators
10
+ alias_method_chain :change_column, :validators
11
+ alias_method_chain :add_column, :validators
12
+ alias_method_chain :rename_table, :validators
13
+ alias_method_chain :create_table, :validators
14
+ end
15
+ end
16
+
17
+ def validate_column table_name, column_name, opts
18
+ table_name = table_name || @context_table_name
19
+ raise MigrationValidatorsException.new("at least one column validator should be defined") if opts.blank?
20
+
21
+
22
+ opts.each do |validator_name, validator_options|
23
+ if validator_options.blank?
24
+ raise MigrationValidatorsException.new("use false to remove column validator") unless validator_options == false
25
+
26
+ MigrationValidators::Core::DbValidator.remove_column_validator table_name, column_name, validator_name
27
+ else
28
+ validator_options = {} if validator_options == true
29
+ MigrationValidators::Core::DbValidator.add_column_validator table_name, column_name, validator_name, validator_options
30
+ end
31
+ end
32
+ end
33
+
34
+ def in_context_of_table table_name
35
+ @context_table_name = table_name
36
+ res = yield if block_given?
37
+ @context_table_name = nil
38
+ res
39
+ end
40
+
41
+ def do_internally
42
+ disable_migration_validators
43
+ yield
44
+ enable_migration_validators
45
+ end
46
+
47
+ private
48
+
49
+ def disable_migration_validators
50
+ @migration_validators_call_stack = [0, @migration_validators_call_stack ||= 0].max + 1
51
+ end
52
+
53
+ def enable_migration_validators
54
+ @migration_validators_call_stack = [0, @migration_validators_call_stack ||= 0].max - 1
55
+ end
56
+
57
+ def migration_validators_enabled
58
+ (@migration_validators_call_stack ||= 0) <= 0
59
+ end
60
+
61
+
62
+ def do_enabled
63
+ yield if migration_validators_enabled
64
+ end
65
+
66
+
67
+ def drop_table_with_validators table_name
68
+ do_enabled { MigrationValidators::Core::DbValidator.remove_table_validators table_name }
69
+ do_internally { drop_table_without_validators table_name }
70
+ end
71
+
72
+ def remove_column_with_validators table_name, *column_names
73
+ do_enabled do
74
+ column_names.flatten.each do |column_name|
75
+ MigrationValidators::Core::DbValidator.remove_column_validators table_name, column_name
76
+ end
77
+ end
78
+
79
+ do_internally { remove_column_without_validators table_name, *column_names }
80
+ end
81
+
82
+ def rename_column_with_validators table_name, old_column_name, new_column_name
83
+ do_enabled { MigrationValidators::Core::DbValidator.rename_column table_name, old_column_name, new_column_name }
84
+ do_internally { rename_column_without_validators table_name, old_column_name, new_column_name }
85
+ end
86
+
87
+ def add_column_with_validators table_name, column_name, type, opts
88
+ validates = opts.delete(:validates)
89
+
90
+ do_internally { add_column_without_validators table_name, column_name, type, opts }
91
+
92
+ do_enabled { validate_column(table_name, column_name, validates) } unless validates.blank?
93
+ end
94
+
95
+ def rename_table_with_validators old_table_name, new_table_name
96
+ do_enabled { MigrationValidators::Core::DbValidator.rename_table old_table_name, new_table_name }
97
+ do_internally { rename_table_without_validators old_table_name, new_table_name }
98
+ end
99
+
100
+ def change_column_with_validators table_name, column_name, type, opts
101
+ validates = opts.delete(:validates)
102
+
103
+ do_enabled { MigrationValidators::Core::DbValidator.remove_column_validators table_name, column_name }
104
+
105
+ do_internally { change_column_without_validators table_name, column_name, type, opts }
106
+
107
+ do_enabled { validate_column(table_name, column_name, validates) } unless validates.blank?
108
+ end
109
+
110
+ def create_table_with_validators table_name, *args, &block
111
+ in_context_of_table table_name do
112
+ create_table_without_validators table_name, *args, &block
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,15 @@
1
+ module MigrationValidators
2
+ module ActiveRecord
3
+ module ConnectionAdapters
4
+ module Table
5
+ def self.included(base)
6
+ base.class_eval do
7
+ def change_validates column_name, opts
8
+ ::ActiveRecord::Base.connection.validate_column(@table_name, column_name, opts) unless opts.blank?
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ module MigrationValidators
2
+ module ActiveRecord
3
+ module ConnectionAdapters
4
+ module TableDefinition
5
+ def self.included(base)
6
+ base.class_eval do
7
+ alias_method_chain :column, :validators
8
+
9
+ def change_validates *args
10
+ end
11
+ end
12
+ end
13
+
14
+ def column_with_validators name, type, options = {}
15
+ validates = options.delete(:validates)
16
+
17
+ column_without_validators name, type, options
18
+
19
+ ::ActiveRecord::Base.connection.validate_column(nil, name, validates) unless validates.blank?
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,29 @@
1
+ module MigrationValidators
2
+ module ActiveRecord
3
+ module Migration
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ base.class_eval do
7
+ class << self
8
+ alias_method_chain :migrate, :validators
9
+ end
10
+ end
11
+ end
12
+
13
+ module ClassMethods
14
+ def migrate_with_validators *args
15
+
16
+ connection.class.class_eval {
17
+ include MigrationValidators::ActiveRecord::ConnectionAdapters::NativeAdapter
18
+ } unless connection.class.include?(MigrationValidators::ActiveRecord::ConnectionAdapters::NativeAdapter)
19
+
20
+ connection.initialize_migration_validators_table
21
+
22
+ migrate_without_validators *args
23
+
24
+ MigrationValidators::Core::DbValidator.commit MigrationValidators.validator
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ module MigrationValidators
2
+ module ActiveRecord
3
+ module Schema
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ base.class_eval do
7
+ class << self
8
+ alias_method_chain :define, :validators
9
+ end
10
+ end
11
+ end
12
+
13
+ module ClassMethods
14
+ def define_with_validators *args, &block
15
+
16
+ connection.class.class_eval {
17
+ include MigrationValidators::ActiveRecord::ConnectionAdapters::NativeAdapter
18
+ } unless connection.class.include?(MigrationValidators::ActiveRecord::ConnectionAdapters::NativeAdapter)
19
+
20
+ connection.initialize_migration_validators_table
21
+
22
+ res = define_without_validators *args, &block
23
+
24
+ MigrationValidators::Core::DbValidator.commit MigrationValidators.validator
25
+
26
+ res
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,24 @@
1
+ module MigrationValidators
2
+ module ActiveRecord
3
+ module SchemaDumper
4
+ def self.included(base)
5
+ base.class_eval do
6
+ alias_method_chain :tables, :validators
7
+ end
8
+ end
9
+
10
+ def tables_with_validators(stream)
11
+ tables_without_validators(stream)
12
+
13
+ stream.puts ""
14
+ stream.puts " #Validators"
15
+ MigrationValidators::Core::DbValidator.find(:all, :order => "table_name, column_name").each do |validator|
16
+ stream.puts " validate_column :#{validator.table_name}, :#{validator.column_name}, :#{validator.validator_name} => #{validator.options.inspect}"
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ require File.expand_path(File.dirname(__FILE__)) + '/syntax'
2
+ require File.expand_path(File.dirname(__FILE__)) + '/validator_definitions'
3
+ require File.expand_path(File.dirname(__FILE__)) + '/containers'
4
+ require File.expand_path(File.dirname(__FILE__)) + '/routing'
5
+
6
+ module MigrationValidators
7
+ module Adapters
8
+ class Base
9
+ include MigrationValidators::Adapters::Syntax
10
+ include MigrationValidators::Adapters::ValidatorDefinitions
11
+ include MigrationValidators::Adapters::Containers
12
+ include MigrationValidators::Adapters::Routing
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,102 @@
1
+ module MigrationValidators
2
+ module Adapters
3
+ module Containers
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def containers
10
+ @containers ||= {}
11
+ end
12
+
13
+ def container name, &block
14
+ raise "Container name is not defined" if name.blank?
15
+
16
+ container = containers[name] ||= MigrationValidators::Core::ValidatorContainer.new(name, validators, syntax)
17
+ container.instance_eval(&block) if block
18
+ container
19
+ end
20
+
21
+ def define_base_containers
22
+ container :insert_trigger do
23
+ group do |validator|
24
+ [validator.table_name, (validator.options && validator.options[:insert_trigger_name]) || "trg_mgr_validates_#{validator.table_name}_ins"]
25
+ end
26
+
27
+ constraint_name do |group_name|
28
+ group_name.last
29
+ end
30
+
31
+ operation :create do |stmt, trigger_name, group_name|
32
+ "CREATE TRIGGER #{trigger_name} BEFORE INSERT ON #{group_name.first} FOR EACH ROW
33
+ BEGIN
34
+ #{stmt};
35
+ END;"
36
+ end
37
+
38
+ operation :drop do |stmt, trigger_name, group_name|
39
+ "DROP TRIGGER IF EXISTS #{trigger_name};"
40
+ end
41
+
42
+ operation :join do |stmt, value|
43
+ [stmt, value].delete_if(&:blank?).join(";\n")
44
+ end
45
+
46
+ operation :db_name do |value|
47
+ "NEW.#{value}"
48
+ end
49
+
50
+ end
51
+
52
+ container :update_trigger do
53
+ group do |validator|
54
+ [validator.table_name, (validator.options && validator.options[:update_trigger_name]) || "trg_mgr_validates_#{validator.table_name}_upd"]
55
+ end
56
+
57
+ constraint_name do |group_name|
58
+ group_name.last
59
+ end
60
+
61
+ operation :create do |stmt, trigger_name, group_name|
62
+ "CREATE TRIGGER #{trigger_name} BEFORE UPDATE ON #{group_name.first} FOR EACH ROW
63
+ BEGIN
64
+ #{stmt};
65
+ END;"
66
+ end
67
+
68
+ operation :drop do |stmt, trigger_name, group_name|
69
+ "DROP TRIGGER IF EXISTS #{trigger_name};"
70
+ end
71
+
72
+ operation :join do |stmt, value|
73
+ [stmt, value].delete_if(&:blank?).join(";\n")
74
+ end
75
+
76
+ operation :db_name do |value|
77
+ "NEW.#{value}"
78
+ end
79
+ end
80
+
81
+ container :check do
82
+ group do |validator|
83
+ [validator.table_name, (validator.options && validator.options[:check_name]) || "chk_#{validator.table_name}_#{validator.column_name}"]
84
+ end
85
+
86
+ constraint_name do |group_name|
87
+ group_name.last
88
+ end
89
+
90
+ operation :create do |stmt, check_name, group_name|
91
+ "ALTER TABLE #{group_name.first} ADD CONSTRAINT #{check_name} CHECK(#{stmt});"
92
+ end
93
+
94
+ operation :drop do |stmt, check_name, group_name|
95
+ "ALTER TABLE #{group_name.first} DROP CONSTRAINT #{check_name};"
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end