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,104 @@
1
+ module MigrationValidators
2
+ module Adapters
3
+ module Routing
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ ###################################################################
9
+ ## UNIQUENESS
10
+ ###################################################################
11
+ def validate_uniqueness_index validators
12
+ add_index validators.first
13
+ [validators.first]
14
+ end
15
+
16
+ def remove_validate_uniqueness_index validators
17
+ remove_index(validators.first)
18
+ [validators.first]
19
+ end
20
+
21
+ def validate_uniqueness validators
22
+ validate_uniqueness_index validators
23
+ end
24
+
25
+ def remove_validate_uniqueness validators
26
+ remove_validate_uniqueness_index validators
27
+ end
28
+
29
+ private
30
+
31
+ def compose_index_name validator
32
+ if validator.options.blank? || (index_name = validator.options[:index_name]).blank?
33
+ "idx_mgr_validates_#{validator.table_name}_#{validator.column_name}_#{validator.validator_name}"
34
+ else
35
+ index_name
36
+ end
37
+ end
38
+
39
+ def add_index validator
40
+ ::ActiveRecord::Base.connection.add_index validator.table_name,
41
+ validator.column_name,
42
+ :name => compose_index_name(validator),
43
+ :unique => true
44
+ end
45
+
46
+ def remove_index validator
47
+ ::ActiveRecord::Base.connection.remove_index validator.table_name,
48
+ :name => compose_index_name(validator)
49
+ end
50
+
51
+ def execute statements
52
+ statements = [statements] if statements.kind_of?(String)
53
+
54
+ statements.each {|stmt| ::ActiveRecord::Base.connection.execute(stmt) }
55
+ end
56
+
57
+ module ClassMethods
58
+ def routers to = nil
59
+ @routers ||= {}
60
+ end
61
+
62
+
63
+ def router validator_name, container_type, &block
64
+ router = routers["#{validator_name}_#{container_type}"] ||= MigrationValidators::Core::ValidatorRouter.new(containers)
65
+ router.instance_eval(&block) if block
66
+ router
67
+ end
68
+
69
+ def route validator_name, container_type, opts = {}, &block
70
+ router validator_name, container_type, &block
71
+
72
+ default = opts.delete(:default)
73
+ remove = opts.delete(:remove)
74
+ remove = true if remove.nil?
75
+
76
+ if (to = opts[:to])
77
+ to = [to] unless to.kind_of?(Array)
78
+ to.each {|container_name| router(validator_name, container_type).to container_name}
79
+ end
80
+
81
+ define_method :"validate_#{validator_name}_#{container_type}" do |validators|
82
+ execute(self.class.router(validator_name, container_type).add_validators(validators))
83
+ validators
84
+ end
85
+ alias_method(:"validate_#{validator_name}", :"validate_#{validator_name}_#{container_type}") if default
86
+
87
+ if remove
88
+ define_method :"remove_validate_#{validator_name}_#{container_type}" do |validators|
89
+ execute(self.class.router(validator_name, container_type).remove_validators(validators))
90
+ validators
91
+ end
92
+ alias_method(:"remove_validate_#{validator_name}", :"remove_validate_#{validator_name}_#{container_type}") if default
93
+ end
94
+ end
95
+
96
+ def clear_routing
97
+ public_instance_methods.grep(/^remove_validate_/) { |method_name| undef_method method_name }
98
+ public_instance_methods.grep(/^validate_/) { |method_name| undef_method method_name }
99
+ end
100
+ end
101
+
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,53 @@
1
+ module MigrationValidators
2
+ module Adapters
3
+ module Syntax
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def define_base_syntax
10
+ syntax do
11
+ operation(:db_name)
12
+ operation(:db_value)
13
+ operation(:wrap) {|value| "(#{value})"}
14
+ operation(:and) {|stmt, value| "#{stmt} AND #{value}"}
15
+ operation(:or) {|stmt, value| "#{stmt} OR #{value}"}
16
+ operation(:length) {|value| "LENGTH(#{stmt})"}
17
+ operation(:trim) {|value| "TRIM(#{stmt})"}
18
+ operation(:coalesce) {|value| "COALESCE(#{stmt}, '')"}
19
+ operation(:if) {|if_stmt, then_stmt| "IF #{if_stmt} THEN #{then_stmt} ENDIF"}
20
+ operation(:regexp) {|stmt, value| "#{stmt} REGEXP #{value}"}
21
+ operation(:greater_than) {|stmt, value| "#{stmt} > #{value}"}
22
+ operation(:greater_or_equal_to) {|stmt, value| "#{stmt} >= #{value}"}
23
+ operation(:less_than) {|stmt, value| "#{stmt} < #{value}"}
24
+ operation(:less_or_equal_to) {|stmt, value| "#{stmt} <= #{value}"}
25
+ operation(:equal_to) {|stmt, value| "#{stmt} = #{value}"}
26
+ operation(:between) do |stmt, range|
27
+ "#{@stmt} >= #{compile(range.first).db_value} AND #{@stmt} <#{range.exclude_end? ? '' : '='} #{compile(range.last).db_value}"
28
+ end
29
+ operation(:in) do |stmt, array|
30
+ "#{@stmt} IN (#{array.collect{|value| compile(value).db_value}.join(', ')})"
31
+ end
32
+ operation(:not_in) do |stmt, array|
33
+ "#{@stmt} NOT IN (#{array.collect{|value| compile(value).db_value}.join(', ')})"
34
+ end
35
+ operation(:not_null) {|value| "#{value} IS NOT NULL"}
36
+ operation(:null) {|value| "#{value} IS NULL"}
37
+ operation(:not) {|value| "NOT #{value}"}
38
+ operation(:exists) {|value| "EXISTS #{value}"}
39
+ operation(:select) {|value| "SELECT #{value}"}
40
+ operation(:from) {|value| " FROM #{value}"}
41
+ operation(:where) {|value| "WHERE #{value}"}
42
+ end
43
+ end
44
+
45
+ def syntax &block
46
+ @builder ||= MigrationValidators::Core::StatementBuilder.new
47
+ @builder.instance_eval(&block) if block
48
+ @builder
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,131 @@
1
+ module MigrationValidators
2
+ module Adapters
3
+ module ValidatorDefinitions
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def validators
10
+ @validators ||= {}
11
+ end
12
+
13
+ def validator name, opts = {}, &block
14
+ validator = validators[name] ||= MigrationValidators::Core::ValidatorDefinition.new(syntax)
15
+
16
+ validator.post :allow_nil => true do
17
+ self.wrap.or(column.db_name.null)
18
+ end if opts[:allow_nil] || !opts.key?(:allow_nil)
19
+
20
+ validator.post :allow_blank => true do
21
+ self.wrap.or(column.db_name.coalesce.trim.length.equal_to(0))
22
+ end if opts[:allow_blank] || !opts.key?(:allow_blank)
23
+
24
+ validator.instance_eval(&block) if block
25
+ end
26
+
27
+ def define_base_validators
28
+ validator :inclusion do
29
+ property :in do |value|
30
+ if value.kind_of?(Array)
31
+ column.db_name.not_null.and(column.db_name.in(value))
32
+ elsif value.kind_of?(Range)
33
+ column.db_name.not_null.and(db_name.between(value))
34
+ else
35
+ column.db_name.not_null.and(column.db_name.equal_to(compile(value).db_value))
36
+ end
37
+ end
38
+ end
39
+
40
+ validator :exclusion do
41
+ property :in do |value|
42
+ if value.kind_of?(Array)
43
+ column.db_name.not_in(value)
44
+ elsif value.kind_of?(Range)
45
+ column.db_name.between(value).wrap.not
46
+ else
47
+ column.db_name.equal_to(compile(value).db_value).not
48
+ end
49
+ end
50
+ end
51
+
52
+ validator :format do
53
+ property :with do |value|
54
+ column.db_name.not_null.and(column.db_name.regexp(compile(value).db_value))
55
+ end
56
+ end
57
+
58
+ validator :length do
59
+ property :is, :message => :wrong_length do |value|
60
+ column.db_name.coalesce.length.equal_to(compile(value).db_value)
61
+ end
62
+ property :maximum, :message => :too_long do |value|
63
+ column.db_name.coalesce.length.less_or_equal_to(compile(value).db_value)
64
+ end
65
+ property :minimum, :message => :too_short do |value|
66
+ column.db_name.coalesce.length.greater_or_equal_to(compile(value).db_value)
67
+ end
68
+
69
+ property :in do |value|
70
+ case value.class.name
71
+ when "Array" then column.db_name.coalesce.length.in(value)
72
+ when "Range" then column.db_name.coalesce.length.between(value)
73
+ else column.db_name.coalesce.length.equal_to(compile(value).db_value)
74
+ end
75
+ end
76
+
77
+ property :within do |value|
78
+ case value.class.name
79
+ when "Array" then column.db_name.coalesce.length.in(value)
80
+ when "Range" then column.db_name.coalesce.length.between(value)
81
+ else column.db_name.coalesce.length.equal_to(compile(value).db_value)
82
+ end
83
+ end
84
+ end
85
+
86
+ validator :size do
87
+ property :is, :message => :wrong_length do |value|
88
+ column.db_name.coalesce.length.equal_to(compile(value).db_value)
89
+ end
90
+ property :maximum, :message => :too_long do |value|
91
+ column.db_name.coalesce.length.less_or_equal_to(compile(value).db_value)
92
+ end
93
+ property :minimum, :message => :too_short do |value|
94
+ column.db_name.coalesce.length.greater_or_equal_to(compile(value).db_value)
95
+ end
96
+
97
+ property :in do |value|
98
+ case value.class.name
99
+ when "Array" then column.db_name.coalesce.length.in(value)
100
+ when "Range" then column.db_name.coalesce.length.between(value)
101
+ else column.db_name.coalesce.length.equal_to(compile(value).db_value)
102
+ end
103
+ end
104
+
105
+ property :within do |value|
106
+ case value.class.name
107
+ when "Array" then column.db_name.coalesce.length.in(value)
108
+ when "Range" then column.db_name.coalesce.length.between(value)
109
+ else column.db_name.coalesce.length.equal_to(compile(value).db_value)
110
+ end
111
+ end
112
+ end
113
+
114
+ validator :presense do
115
+ property do |value|
116
+ column.db_name.not_null.and(column.db_name.trim.length.greater_than(0))
117
+ end
118
+ end
119
+
120
+ validator :uniqueness do
121
+ property do |value|
122
+ "NOT EXISTS(SELECT #{column}
123
+ FROM #{validator.table_name}
124
+ WHERE (#{column.db_name} = #{column}))"
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,88 @@
1
+ module MigrationValidators
2
+ module Core
3
+ class AdapterWrapper
4
+ class AdapterCaller
5
+ def initialize adapter, action
6
+ @adapter = adapter
7
+ @action = action
8
+ end
9
+
10
+ def process validators
11
+ return if validators.blank?
12
+
13
+ check_validators(validators)
14
+
15
+ validators.group_by{|validator| [validator.table_name, compose_method_name(validator)]}.each do |group_id, group|
16
+
17
+ until group.blank?
18
+ handled_validators = @adapter.send(group_id.last, group)
19
+
20
+ break if handled_validators.blank?
21
+
22
+ group = group - handled_validators
23
+ end
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def compose_method_name validator
30
+ method_suffix = validator.options.blank? || validator.options[:as].blank? ? "" : "_#{validator.options[:as]}"
31
+ :"#{@action}_#{validator.validator_name}#{method_suffix}"
32
+ end
33
+
34
+ def grep_public_instance_methods expr
35
+ @adapter.class.public_instance_methods.collect{|method| method.to_s.match(expr)}.compact.collect{|match| match[1]}
36
+ end
37
+
38
+ def supported_validators
39
+ grep_public_instance_methods(/^#{@action}_([a-z]*)/).uniq
40
+ end
41
+
42
+ def supported_db_forms validator_name
43
+ grep_public_instance_methods(/^#{@action}_#{validator_name}_([a-z]*)/).uniq
44
+ end
45
+
46
+ def default_db_form_supported? validator_name
47
+ @adapter.class.method_defined? :"#{@action}_#{validator_name}"
48
+ end
49
+
50
+ def array2str array
51
+ "[" + array.collect {|elem| "'#{elem}'"}.join(',') + "]"
52
+ end
53
+
54
+ def check_validators validators
55
+ validators.each do |validator|
56
+
57
+ unless supported_validators.include?(validator.validator_name.to_s)
58
+ raise MigrationValidatorsException.new("Adapter '#{@adapter.name}'. 'Action '#{@action}' for '#{validator.validator_name}' is not supported. Available validators: #{array2str(supported_validators)}")
59
+ end
60
+
61
+ if validator.options && (db_form = validator.options[:as])
62
+ unless supported_db_forms(validator.validator_name).include?(db_form.to_s)
63
+ raise MigrationValidatorsException.new("Adapter '#{@adapter.name}'. Action '#{@action}' for db form '#{db_form}' for validator '#{validator.validator_name}' is not supported. Available db forms: #{array2str(supported_db_forms(validator.validator_name))}")
64
+ end
65
+ else
66
+ unless default_db_form_supported?(validator.validator_name.to_s)
67
+ raise MigrationValidatorsException.new("Adapter '#{@adapter.name}'. 'Action '#{@action}' for '#{validator.validator_name}' with default db form is not supported")
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ def initialize adapter
75
+ @creator = AdapterCaller.new adapter, :validate
76
+ @remover = AdapterCaller.new adapter, :remove_validate
77
+ end
78
+
79
+ def create_validators validators
80
+ @creator.process validators
81
+ end
82
+
83
+ def remove_validators validators
84
+ @remover.process validators
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,178 @@
1
+ module MigrationValidators
2
+ module Core
3
+ class DbValidator < ::ActiveRecord::Base
4
+ set_table_name MigrationValidators.migration_validators_table_name
5
+
6
+ validates_presence_of :table_name
7
+ validates_length_of :table_name, :maximum => 255
8
+ validates_length_of :column_name, :maximum => 255
9
+
10
+ validates_presence_of :validator_name
11
+ validates_length_of :validator_name, :maximum => 255
12
+
13
+ validate do |validator|
14
+ unless ::ActiveRecord::Base.connection.table_exists?(validator.table_name)
15
+ validator.errors[:table_name] << "table '#{table_name}' does not exist"
16
+ else
17
+ unless ::ActiveRecord::Base.connection.columns(validator.table_name).any?{|col| col.name == validator.column_name.to_s}
18
+ validator.errors[:column_name] << "column '#{column_name}' does not exist in the table '#{table_name}'"
19
+ end
20
+ end
21
+ end
22
+
23
+ before_save :prepare_options
24
+
25
+ serialize :options, Hash
26
+ serialize :constraints, Array
27
+
28
+ def name
29
+ "#{table_name}_#{column_name}_#{validator_name}"
30
+ end
31
+
32
+
33
+ def error_message
34
+ unless (options.blank? || (message = options[:message]).blank?)
35
+ message
36
+ else
37
+ "#{validator_name} violated for #{table_name} field #{column_name}"
38
+ end
39
+ end
40
+
41
+ def satisfies opts
42
+ return true if opts.blank?
43
+
44
+ opts.collect do |property_name, property_value|
45
+ [property_name, property_value.kind_of?(Array) ? property_value : [property_value]]
46
+ end.all? do |property_name, property_value|
47
+ property_value.include?(options[property_name])
48
+ end
49
+ end
50
+
51
+ def save_to_constraint constraint_name
52
+ self.constraints ||= []
53
+ self.constraints << constraint_name.to_s unless self.constraints.include?(constraint_name.to_s)
54
+ end
55
+
56
+ def remove_from_constraint constraint_name
57
+ self.constraints.delete(constraint_name.to_s) if self.constraints
58
+ end
59
+
60
+ def in_constraint? constraint_name
61
+ self.constraints && self.constraints.include?(constraint_name.to_s)
62
+ end
63
+
64
+ private
65
+ def prepare_options
66
+ options = options.inject({}) do |res, (key, value)|
67
+ res[key.to_s] = value
68
+ res
69
+ end unless options.blank?
70
+
71
+ true
72
+ end
73
+
74
+ class << self
75
+
76
+ def add_column_validator table_name, column_name, validator_name, opts
77
+ remove_validators(:table_name => table_name.to_s,
78
+ :column_name => column_name.to_s,
79
+ :validator_name => validator_name.to_s)
80
+
81
+ add_new_validator(:table_name => table_name.to_s,
82
+ :column_name => column_name.to_s,
83
+ :validator_name => validator_name.to_s,
84
+ :options => opts)
85
+ end
86
+
87
+ def remove_column_validator table_name, column_name, validator_name
88
+ remove_validators :table_name => table_name.to_s, :column_name => column_name.to_s, :validator_name => validator_name.to_s
89
+ end
90
+
91
+ def remove_column_validators table_name, column_name
92
+ remove_validators :table_name => table_name.to_s, :column_name => column_name.to_s
93
+ end
94
+
95
+ def table_validators table_name, opts = {}
96
+ with_options opts do
97
+ DbValidator.find(:all, :conditions => { :table_name => table_name.to_s })
98
+ end
99
+ end
100
+
101
+ def constraint_validators constraint
102
+ (DbValidator.find(:all, :conditions => ["constraints LIKE ?", "%#{constraint}%"]) + validators_to_add - validators_to_remove).select{|validator| validator.in_constraint?(constraint)}
103
+ end
104
+
105
+ def remove_table_validators table_name
106
+ remove_validators :table_name => table_name.to_s
107
+ end
108
+
109
+ def column_validators table_name, column_name, opts = {}
110
+ with_options opts do
111
+ DbValidator.find :all, :conditions => { :table_name => table_name.to_s, :column_name => column_name.to_s }
112
+ end
113
+ end
114
+
115
+ def rename_column table_name, old_column_name, new_column_name
116
+ DbValidator.update_all({:column_name => new_column_name.to_s}, :column_name => old_column_name.to_s)
117
+ end
118
+
119
+ def rename_table old_table_name, new_table_name
120
+ DbValidator.update_all({:table_name => new_table_name.to_s}, :table_name => old_table_name.to_s)
121
+ end
122
+
123
+ def commit adapter = nil
124
+ adapter.remove_validators(validators_to_remove.select{|validator| ::ActiveRecord::Base.connection.table_exists?(validator.table_name)}) if adapter
125
+ validators_to_remove.each(&:destroy)
126
+ validators_to_remove.clear
127
+
128
+ adapter.create_validators(validators_to_add) if adapter
129
+ validators_to_add.each(&:save!)
130
+ validators_to_add.clear
131
+ end
132
+
133
+ def rollback
134
+ validators_to_remove.clear
135
+ validators_to_add.clear
136
+ end
137
+
138
+
139
+ def clear_all
140
+ rollback
141
+ DbValidator.delete_all
142
+ end
143
+
144
+ def validators_to_remove
145
+ @validators_to_remove ||= []
146
+ end
147
+
148
+ def validators_to_add
149
+ @validators_to_add ||= []
150
+ end
151
+
152
+ private
153
+
154
+ def validators_to_remove
155
+ @validators_to_remove ||= []
156
+ end
157
+
158
+ def validators_to_add
159
+ @validators_to_add ||= []
160
+ end
161
+
162
+ def with_options opts, &block
163
+ block.call.select {|validator| validator.satisfies(opts) }
164
+ end
165
+
166
+ def add_new_validator opts
167
+ validators_to_add << new(opts)
168
+ end
169
+
170
+ def remove_validators opts
171
+ DbValidator.find(:all, :conditions => opts).each { |validator| validators_to_remove << validator }
172
+ end
173
+
174
+
175
+ end
176
+ end
177
+ end
178
+ end