dm-constraints 0.9.11 → 0.10.0

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.
@@ -1,6 +1,8 @@
1
- === 0.9.11 / 2009-03-29
1
+ === 0.10.0 / 2009-10-15
2
+
3
+ * Updated to work with dm-core 0.10.0
2
4
 
3
- * 5 major enhancements:
5
+ === 0.9.11 / 2009-03-29
4
6
 
5
7
  * Added :destroy! constraints
6
8
  * Added support for 1:1 constraints
data/Manifest.txt CHANGED
@@ -1,14 +1,12 @@
1
- History.txt
1
+ History.rdoc
2
2
  LICENSE
3
3
  Manifest.txt
4
- README.txt
4
+ README.rdoc
5
5
  Rakefile
6
6
  TODO
7
7
  lib/dm-constraints.rb
8
- lib/dm-constraints/data_objects_adapter.rb
9
8
  lib/dm-constraints/delete_constraint.rb
10
- lib/dm-constraints/mysql_adapter.rb
11
- lib/dm-constraints/postgres_adapter.rb
9
+ lib/dm-constraints/migrations.rb
12
10
  lib/dm-constraints/version.rb
13
11
  spec/integration/constraints_spec.rb
14
12
  spec/spec.opts
File without changes
data/Rakefile CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'pathname'
2
- require 'rubygems'
3
2
 
4
3
  ROOT = Pathname(__FILE__).dirname.expand_path
5
4
  JRUBY = RUBY_PLATFORM =~ /java/
@@ -14,10 +13,10 @@ GEM_NAME = 'dm-constraints'
14
13
  GEM_VERSION = DataMapper::Constraints::VERSION
15
14
  GEM_DEPENDENCIES = [['dm-core', GEM_VERSION]]
16
15
  GEM_CLEAN = %w[ log pkg coverage ]
17
- GEM_EXTRAS = { :has_rdoc => true, :extra_rdoc_files => %w[ README.txt LICENSE TODO History.txt ] }
16
+ GEM_EXTRAS = { :has_rdoc => true, :extra_rdoc_files => %w[ README.rdoc LICENSE TODO History.rdoc ] }
18
17
 
19
18
  PROJECT_NAME = 'datamapper'
20
- PROJECT_URL = "http://github.com/sam/dm-more/tree/master/#{GEM_NAME}"
19
+ PROJECT_URL = "http://github.com/datamapper/dm-more/tree/master/#{GEM_NAME}"
21
20
  PROJECT_DESCRIPTION = PROJECT_SUMMARY = 'DataMapper plugin constraining relationships'
22
21
 
23
22
  [ ROOT, ROOT.parent ].each do |dir|
@@ -1,13 +1,12 @@
1
1
  module DataMapper
2
2
  module Constraints
3
3
  module DeleteConstraint
4
-
5
4
  def self.included(base)
6
5
  base.extend(ClassMethods)
7
6
  end
8
7
 
9
8
  module ClassMethods
10
- DELETE_CONSTRAINT_OPTIONS = [:protect, :destroy, :destroy!, :set_nil, :skip]
9
+ CONSTRAINT_OPTIONS = [ :protect, :destroy, :destroy!, :set_nil, :skip ].to_set.freeze
11
10
 
12
11
  ##
13
12
  # Checks that the constraint type is appropriate to the relationship
@@ -23,26 +22,25 @@ module DataMapper
23
22
  # @return [nil]
24
23
  #
25
24
  # @api semi-public
26
- def check_delete_constraint_type(cardinality, name, options = {})
27
- #Make sure options contains :constraint key, whether nil or not
28
- options[:constraint] ||= nil
29
- constraint_type = options[:constraint]
30
- return if constraint_type.nil?
25
+ def check_delete_constraint_type(cardinality, name, *args)
26
+ options = extract_options(args)
31
27
 
32
- delete_constraint_options = DELETE_CONSTRAINT_OPTIONS.map { |o| ":#{o}" }
33
- if !DELETE_CONSTRAINT_OPTIONS.include?(constraint_type)
34
- raise ArgumentError, ":constraint option must be one of #{delete_constraint_options * ', '}"
35
- end
28
+ return unless options.key?(:constraint)
29
+
30
+ constraint = options[:constraint]
36
31
 
37
- if constraint_type == :set_nil && self.relationships[name].is_a?(DataMapper::Associations::RelationshipChain)
38
- raise ArgumentError, "Constraint type :set_nil is not valid for M:M relationships"
32
+ unless CONSTRAINT_OPTIONS.include?(constraint)
33
+ raise ArgumentError, ":constraint option must be one of #{CONSTRAINT_OPTIONS.to_a.join(', ')}"
39
34
  end
40
35
 
41
- if cardinality == 1 && constraint_type == :destroy!
42
- raise ArgumentError, "Constraint type :destroy! is not valid for 1:1 relationships"
36
+ # XXX: is any constraint valid with a :through relationship?
37
+ if constraint == :set_nil && options.key?(:through)
38
+ raise ArgumentError, 'Constraint type :set_nil is not valid for relationships using :through'
43
39
  end
44
40
  end
45
41
 
42
+ private
43
+
46
44
  ##
47
45
  # Temporarily changes the visibility of a method so a block can be evaluated against it
48
46
  #
@@ -62,38 +60,25 @@ module DataMapper
62
60
  def with_changed_method_visibility(method, from_visibility, to_visibility, &block)
63
61
  send(to_visibility, method)
64
62
  yield
63
+ ensure
65
64
  send(from_visibility, method)
66
65
  end
67
-
68
66
  end
69
67
 
70
68
  ##
71
- # Addes the delete constraint options to a relationship
69
+ # Adds the delete constraint options to a relationship
72
70
  #
73
- # @param params [*ARGS] Arguments passed to Relationship#initialize or RelationshipChain#initialize
74
- #
75
- # @notes This takes *params because it runs before the initializer for Relationships and RelationshipChains
76
- # which have different method signatures
71
+ # @param params [*ARGS] Arguments passed to Relationship#initialize
77
72
  #
78
73
  # @return [nil]
79
74
  #
80
75
  # @api semi-public
81
- def add_delete_constraint_option(*params)
82
- opts = params.last
83
-
84
- if opts.is_a?(Hash)
85
- #if it is a chain, set the constraint on the 1:M near relationship(anonymous)
86
- if self.is_a?(DataMapper::Associations::RelationshipChain)
87
- opts = params.last
88
- near_rel = opts[:parent_model].relationships[opts[:near_relationship_name]]
89
- near_rel.options[:constraint] = opts[:constraint]
90
- near_rel.instance_variable_set "@delete_constraint", opts[:constraint]
91
- end
92
-
93
- @delete_constraint = params.last[:constraint]
94
- end
76
+ def add_constraint_option(name, child_model, parent_model, options = {})
77
+ @constraint = options.fetch(:constraint, :protect) || :skip
95
78
  end
96
79
 
80
+ private
81
+
97
82
  ##
98
83
  # Checks delete constraints prior to destroying a dm resource or collection
99
84
  #
@@ -101,86 +86,32 @@ module DataMapper
101
86
  #
102
87
  # @notes
103
88
  # - It only considers a relationship's constraints if this is the parent model (ie a child shouldn't delete a parent)
104
- # - RelationshipChains are skipped, as they are evaluated by their underlying 1:M relationships
89
+ # - Many to Many Relationships are skipped, as they are evaluated by their underlying 1:M relationships
105
90
  #
106
91
  # @returns [nil]
107
92
  #
108
93
  # @api semi-public
109
94
  def check_delete_constraints
110
- model.relationships.each do |rel_name, rel|
111
- #Only look at relationships where this model is the parent
112
- next if rel.parent_model != model
113
-
114
- #Don't delete across M:M relationships, instead use their anonymous 1:M Relationships
115
- next if rel.is_a?(DataMapper::Associations::RelationshipChain)
116
-
117
- children = self.send(rel_name)
118
- if children.kind_of?(DataMapper::Collection)
119
- check_collection_delete_constraints(rel,children)
120
- elsif children
121
- check_resource_delete_constraints(rel,children)
95
+ relationships.each_value do |relationship|
96
+ next unless relationship.respond_to?(:constraint)
97
+ next unless association = relationship.get(self)
98
+
99
+ delete_allowed = case constraint = relationship.constraint
100
+ when :protect
101
+ Array(association).empty?
102
+ when :destroy, :destroy!
103
+ association.send(constraint)
104
+ when :set_nil
105
+ Array(association).all? do |resource|
106
+ resource.update(relationship.inverse => nil)
107
+ end
108
+ when :skip
109
+ true # do nothing
122
110
  end
123
- end # relationships
124
- end # check_delete_constraints
125
111
 
126
- ##
127
- # Performs the meat of the check_delete_constraints method for a collection of resources
128
- #
129
- # @param rel [DataMapper::Associations::Relationship] relationship being evaluated
130
- #
131
- # @param children [~DataMapper::Collection] child records to constrain
132
- #
133
- # @see #check_delete_constraints
134
- #
135
- # @api semi-public
136
- def check_collection_delete_constraints(rel, children)
137
- case rel.delete_constraint
138
- when nil, :protect
139
- unless children.empty?
140
- DataMapper.logger.error("Could not delete #{self.class} a child #{children.first.class} exists")
141
- throw(:halt,false)
142
- end
143
- when :destroy
144
- children.each{|child| child.destroy}
145
- when :destroy!
146
- children.destroy!
147
- when :set_nil
148
- children.each do |child|
149
- child.class.many_to_one_relationships.each do |mto_rel|
150
- child.send("#{mto_rel.name}=", nil) if child.send(mto_rel.name).eql?(self)
151
- end
152
- end
153
- end
154
- end
155
-
156
- ##
157
- # Performs the meat of check_delete_constraints method for a single resource
158
- #
159
- # @param rel [DataMapper::Associations::Relationship] the relationship to evaluate
160
- #
161
- # @param child [~DataMapper::Model] the model to constrain
162
- #
163
- # @see #check_delete_constraints
164
- #
165
- # @api semi-public
166
- def check_resource_delete_constraints(rel, child)
167
- case rel.delete_constraint
168
- when nil, :protect
169
- unless child.nil?
170
- DataMapper.logger.error("Could not delete #{self.class} a child #{child.class} exists")
171
- throw(:halt,false)
172
- end
173
- when :destroy
174
- child.destroy
175
- when :destroy!
176
- #not supported in dm-master, an exception should have been raised on class load
177
- when :set_nil
178
- child.class.many_to_one_relationships.each do |mto_rel|
179
- child.send("#{mto_rel.name}=", nil) if child.send(mto_rel.name).eql?(self)
180
- end
112
+ throw(:halt, false) unless delete_allowed
181
113
  end
182
114
  end
183
-
184
115
  end # DeleteConstraint
185
116
  end # Constraints
186
117
  end # DataMapper
@@ -0,0 +1,274 @@
1
+ module DataMapper
2
+ module Constraints
3
+ module Migrations
4
+ module SingletonMethods
5
+ def self.included(base)
6
+ # TODO: figure out how to make this work without AMC
7
+ base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
8
+ alias_method :auto_migrate_down_without_constraints!, :auto_migrate_down!
9
+ alias_method :auto_migrate_down!, :auto_migrate_down_with_constraints!
10
+
11
+ alias_method :auto_migrate_up_without_constraints!, :auto_migrate_up!
12
+ alias_method :auto_migrate_up!, :auto_migrate_up_with_constraints!
13
+ RUBY
14
+ end
15
+
16
+ def auto_migrate_down_with_constraints!(repository_name = nil)
17
+ repository_execute(:auto_migrate_down_with_constraints!, repository_name)
18
+ auto_migrate_down_without_constraints!(repository_name)
19
+ end
20
+
21
+ def auto_migrate_up_with_constraints!(repository_name = nil)
22
+ auto_migrate_up_without_constraints!(repository_name)
23
+ repository_execute(:auto_migrate_up_with_constraints!, repository_name)
24
+ end
25
+ end
26
+
27
+ module DataObjectsAdapter
28
+ ##
29
+ # Determine if a constraint exists for a table
30
+ #
31
+ # @param storage_name [Symbol]
32
+ # name of table to check constraint on
33
+ # @param constraint_name [~String]
34
+ # name of constraint to check for
35
+ #
36
+ # @return [Boolean]
37
+ #
38
+ # @api private
39
+ def constraint_exists?(storage_name, constraint_name)
40
+ statement = <<-SQL.compress_lines
41
+ SELECT COUNT(*)
42
+ FROM "information_schema"."table_constraints"
43
+ WHERE "constraint_type" = 'FOREIGN KEY'
44
+ AND "table_schema" = ?
45
+ AND "table_name" = ?
46
+ AND "constraint_name" = ?
47
+ SQL
48
+
49
+ query(statement, schema_name, storage_name, constraint_name).first > 0
50
+ end
51
+
52
+ ##
53
+ # Create the constraint for a relationship
54
+ #
55
+ # @param relationship [Relationship]
56
+ # the relationship to create the constraint for
57
+ #
58
+ # @return [true, false]
59
+ # true if creating the constraints was successful
60
+ #
61
+ # @api semipublic
62
+ def create_relationship_constraint(relationship)
63
+ return false unless valid_relationship_for_constraint?(relationship)
64
+
65
+ source_model = relationship.source_model
66
+ source_table = source_model.storage_name(name)
67
+ source_key = relationship.source_key
68
+
69
+ constraint_name = constraint_name(source_table, relationship.name)
70
+ return false if constraint_exists?(source_table, constraint_name)
71
+
72
+ constraint_type = case relationship.inverse.constraint
73
+ when :protect then 'NO ACTION'
74
+ when :destroy, :destroy! then 'CASCADE'
75
+ when :set_nil then 'SET NULL'
76
+ end
77
+
78
+ return false if constraint_type.nil?
79
+
80
+ storage_name = relationship.source_model.storage_name(name)
81
+ reference_storage_name = relationship.target_model.storage_name(name)
82
+
83
+ foreign_keys = relationship.source_key.map { |p| property_to_column_name(p, false) }
84
+ reference_keys = relationship.target_key.map { |p| property_to_column_name(p, false) }
85
+
86
+ execute(create_constraints_statement(storage_name, constraint_name, constraint_type, foreign_keys, reference_storage_name, reference_keys))
87
+ end
88
+
89
+ ##
90
+ # Remove the constraint for a relationship
91
+ #
92
+ # @param relationship [Relationship]
93
+ # the relationship to remove the constraint for
94
+ #
95
+ # @return [true, false]
96
+ # true if destroying the constraint was successful
97
+ #
98
+ # @api semipublic
99
+ def destroy_relationship_constraint(relationship)
100
+ return false unless valid_relationship_for_constraint?(relationship)
101
+
102
+ source_model = relationship.source_model
103
+ source_table = source_model.storage_name(name)
104
+
105
+ constraint_name = constraint_name(source_table, relationship.name)
106
+ return false unless constraint_exists?(source_table, constraint_name)
107
+
108
+ execute(destroy_constraints_statement(source_table, constraint_name))
109
+ end
110
+
111
+ private
112
+
113
+ ##
114
+ # Check to see if the relationship's constraints can be used
115
+ #
116
+ # Only one-to-one, one-to-many, and many-to-many relationships
117
+ # can be used for constraints. They must also be in the same
118
+ # repository as the adapter is connected to.
119
+ #
120
+ # @param relationship [Relationship]
121
+ # the relationship to check
122
+ #
123
+ # @return [true, false]
124
+ # true if a constraint can be established for relationship
125
+ #
126
+ # @api private
127
+ def valid_relationship_for_constraint?(relationship)
128
+ return false unless relationship.source_repository_name == name || relationship.source_repository_name.nil?
129
+ return false unless relationship.target_repository_name == name || relationship.target_repository_name.nil?
130
+ return false unless relationship.kind_of?(Associations::ManyToOne::Relationship)
131
+ true
132
+ end
133
+
134
+ module SQL
135
+ private
136
+
137
+ ##
138
+ # Generates the SQL statement to create a constraint
139
+ #
140
+ # @param constraint_name [String]
141
+ # name of the foreign key constraint
142
+ # @param constraint_type [String]
143
+ # type of foreign key constraint to add to the table
144
+ # @param storage_name [String]
145
+ # name of table to constrain
146
+ # @param foreign_keys [Array[String]]
147
+ # columns in the table that refer to foreign table
148
+ # @param reference_storage_name [String]
149
+ # table the foreign key refers to
150
+ # @param reference_storage_name [Array[String]]
151
+ # columns the foreign table that are referred to
152
+ #
153
+ # @return [String]
154
+ # SQL DDL Statement to create a constraint
155
+ #
156
+ # @api private
157
+ def create_constraints_statement(storage_name, constraint_name, constraint_type, foreign_keys, reference_storage_name, reference_keys)
158
+ <<-SQL.compress_lines
159
+ ALTER TABLE #{quote_name(storage_name)}
160
+ ADD CONSTRAINT #{quote_name(constraint_name)}
161
+ FOREIGN KEY (#{foreign_keys.join(', ')})
162
+ REFERENCES #{quote_name(reference_storage_name)} (#{reference_keys.join(', ')})
163
+ ON DELETE #{constraint_type}
164
+ ON UPDATE #{constraint_type}
165
+ SQL
166
+ end
167
+
168
+ ##
169
+ # Generates the SQL statement to destroy a constraint
170
+ #
171
+ # @param storage_name [String]
172
+ # name of table to constrain
173
+ # @param constraint_name [String]
174
+ # name of foreign key constraint
175
+ #
176
+ # @return [String]
177
+ # SQL DDL Statement to destroy a constraint
178
+ #
179
+ # @api private
180
+ def destroy_constraints_statement(storage_name, constraint_name)
181
+ <<-SQL.compress_lines
182
+ ALTER TABLE #{quote_name(storage_name)}
183
+ DROP CONSTRAINT #{quote_name(constraint_name)}
184
+ SQL
185
+ end
186
+
187
+ ##
188
+ # generates a unique constraint name given a table and a relationships
189
+ #
190
+ # @param storage_name [String]
191
+ # name of table to constrain
192
+ # @param relationships_name [String]
193
+ # name of the relationship to constrain
194
+ #
195
+ # @return [String]
196
+ # name of the constraint
197
+ #
198
+ # @api private
199
+ def constraint_name(storage_name, relationship_name)
200
+ identifier = "#{storage_name}_#{relationship_name}"[0, self.class::IDENTIFIER_MAX_LENGTH - 3]
201
+ "#{identifier}_fk"
202
+ end
203
+ end
204
+
205
+ include SQL
206
+ end
207
+
208
+ module MysqlAdapter
209
+ module SQL
210
+ private
211
+
212
+ ##
213
+ # MySQL specific query to drop a foreign key
214
+ #
215
+ # @param storage_name [String]
216
+ # name of table to constrain
217
+ # @param constraint_name [String]
218
+ # name of foreign key constraint
219
+ #
220
+ # @return [String]
221
+ # SQL DDL Statement to destroy a constraint
222
+ #
223
+ # @api private
224
+ def destroy_constraints_statement(storage_name, constraint_name)
225
+ <<-SQL.compress_lines
226
+ ALTER TABLE #{quote_name(storage_name)}
227
+ DROP FOREIGN KEY #{quote_name(constraint_name)}
228
+ SQL
229
+ end
230
+ end
231
+
232
+ include SQL
233
+ end
234
+
235
+ module Sqlite3Adapter
236
+ def constraint_exists?(*)
237
+ false
238
+ end
239
+
240
+ def create_relationship_constraint(*)
241
+ false
242
+ end
243
+
244
+ def destroy_relationship_constraint(*)
245
+ false
246
+ end
247
+ end
248
+
249
+ module Model
250
+ def auto_migrate_down_with_constraints!(repository_name = self.repository_name)
251
+ return unless storage_exists?(repository_name)
252
+ return if self.respond_to?(:is_remixable?) && self.is_remixable?
253
+ execute_each_relationship(:destroy_relationship_constraint, repository_name)
254
+ end
255
+
256
+ def auto_migrate_up_with_constraints!(repository_name = self.repository_name)
257
+ return if self.respond_to?(:is_remixable?) && self.is_remixable?
258
+ execute_each_relationship(:create_relationship_constraint, repository_name)
259
+ end
260
+
261
+ private
262
+
263
+ def execute_each_relationship(method, repository_name)
264
+ adapter = DataMapper.repository(repository_name).adapter
265
+ return unless adapter.respond_to?(method)
266
+
267
+ relationships(repository_name).each_value do |relationship|
268
+ adapter.send(method, relationship)
269
+ end
270
+ end
271
+ end
272
+ end
273
+ end
274
+ end
@@ -1,5 +1,5 @@
1
1
  module DataMapper
2
2
  module Constraints
3
- VERSION = '0.9.11'
3
+ VERSION = '0.10.0'.freeze
4
4
  end
5
5
  end
@@ -1,58 +1,42 @@
1
- # Needed to import datamapper and other gems
2
- require 'rubygems'
3
- require 'pathname'
4
-
5
- # Add all external dependencies for the plugin here
6
- gem 'dm-core', '0.9.11'
7
- require 'dm-core'
8
-
9
- # Require plugin-files
10
- require Pathname(__FILE__).dirname.expand_path / 'dm-constraints' / 'data_objects_adapter'
11
- require Pathname(__FILE__).dirname.expand_path / 'dm-constraints' / 'postgres_adapter'
12
- require Pathname(__FILE__).dirname.expand_path / 'dm-constraints' / 'mysql_adapter'
13
- require Pathname(__FILE__).dirname.expand_path / 'dm-constraints' / 'delete_constraint'
1
+ require 'dm-constraints/delete_constraint'
2
+ require 'dm-constraints/migrations'
3
+ require 'dm-constraints/version'
14
4
 
15
5
  module DataMapper
16
6
  module Associations
17
- class RelationshipChain
7
+ class OneToMany::Relationship
18
8
  include Extlib::Hook
19
- include DataMapper::Constraints::DeleteConstraint
9
+ include Constraints::DeleteConstraint
20
10
 
21
- attr_reader :delete_constraint
22
11
  OPTIONS << :constraint
23
12
 
13
+ attr_reader :constraint
14
+
24
15
  # initialize is a private method in Relationship
25
16
  # and private methods can not be "advised" (hooked into)
26
17
  # in extlib.
27
18
  with_changed_method_visibility(:initialize, :private, :public) do
28
- before :initialize, :add_delete_constraint_option
19
+ before :initialize, :add_constraint_option
29
20
  end
30
21
  end
31
- end
32
- end
33
22
 
34
- module DataMapper
35
- module Associations
36
- class Relationship
37
- include Extlib::Hook
38
- include DataMapper::Constraints::DeleteConstraint
23
+ class ManyToMany::Relationship
39
24
 
40
- attr_reader :delete_constraint
41
25
  OPTIONS << :constraint
42
26
 
43
- # initialize is a private method in Relationship
44
- # and private methods can not be "advised" (hooked into)
45
- # in extlib.
46
- with_changed_method_visibility(:initialize, :private, :public) do
47
- before :initialize, :add_delete_constraint_option
27
+ private
28
+
29
+ # TODO: document
30
+ # @api semipublic
31
+ chainable do
32
+ def one_to_many_options
33
+ super.merge(:constraint => @constraint)
34
+ end
48
35
  end
49
36
  end
50
37
  end
51
- end
52
38
 
53
- module DataMapper
54
39
  module Constraints
55
-
56
40
  include DeleteConstraint
57
41
 
58
42
  module ClassMethods
@@ -65,28 +49,22 @@ module DataMapper
65
49
  #
66
50
  def self.included(model)
67
51
  model.extend(ClassMethods)
68
- model.class_eval do
52
+ model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
69
53
  before_class_method :has, :check_delete_constraint_type
70
- if method_defined?(:destroy)
54
+
55
+ if instance_methods.any? { |m| m.to_sym == :destroy }
71
56
  before :destroy, :check_delete_constraints
72
57
  end
73
- end
58
+ RUBY
74
59
  end
75
-
76
- end
77
-
78
- class AutoMigrator
79
- include Extlib::Hook
80
- include DataMapper::Constraints::DataObjectsAdapter::Migration
81
60
  end
82
61
 
83
- module Adapters
84
- if defined?(MysqlAdapter)
85
- MysqlAdapter.send :include, DataMapper::Constraints::MysqlAdapter::SQL
86
- end
87
-
88
- if defined?(PostgresAdapter)
89
- PostgresAdapter.send :include, DataMapper::Constraints::PostgresAdapter::SQL
62
+ module Migrations
63
+ constants.each do |const_name|
64
+ if Constraints::Migrations.const_defined?(const_name)
65
+ mod = const_get(const_name)
66
+ mod.send(:include, Constraints::Migrations.const_get(const_name))
67
+ end
90
68
  end
91
69
  end
92
70
  end