adsl 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -20
  3. data/README.md +14 -21
  4. data/bin/adsl-verify +8 -8
  5. data/lib/adsl.rb +3 -0
  6. data/lib/adsl/adsl.rb +3 -0
  7. data/lib/adsl/ds/data_store_spec.rb +339 -0
  8. data/lib/adsl/extract/instrumenter.rb +206 -0
  9. data/lib/adsl/extract/meta.rb +33 -0
  10. data/lib/adsl/extract/rails/action_block_builder.rb +233 -0
  11. data/lib/adsl/extract/rails/action_instrumenter.rb +400 -0
  12. data/lib/adsl/extract/rails/action_runner.rb +57 -0
  13. data/lib/adsl/extract/rails/active_record_metaclass_generator.rb +555 -0
  14. data/lib/adsl/extract/rails/callback_chain_simulator.rb +135 -0
  15. data/lib/adsl/extract/rails/invariant_extractor.rb +48 -0
  16. data/lib/adsl/extract/rails/invariant_instrumenter.rb +70 -0
  17. data/lib/adsl/extract/rails/other_meta.rb +57 -0
  18. data/lib/adsl/extract/rails/rails_extractor.rb +211 -0
  19. data/lib/adsl/extract/rails/rails_instrumentation_test_case.rb +34 -0
  20. data/lib/adsl/extract/rails/rails_special_gem_instrumentation.rb +120 -0
  21. data/lib/adsl/extract/rails/rails_test_helper.rb +140 -0
  22. data/lib/adsl/extract/sexp_utils.rb +54 -0
  23. data/lib/adsl/fol/first_order_logic.rb +261 -0
  24. data/lib/adsl/parser/adsl_parser.racc +159 -0
  25. data/lib/{parser → adsl/parser}/adsl_parser.rex +4 -4
  26. data/lib/{parser → adsl/parser}/adsl_parser.rex.rb +6 -6
  27. data/lib/adsl/parser/adsl_parser.tab.rb +1031 -0
  28. data/lib/adsl/parser/ast_nodes.rb +1410 -0
  29. data/lib/adsl/railtie.rb +67 -0
  30. data/lib/adsl/spass/bin.rb +230 -0
  31. data/lib/{spass → adsl/spass}/ruby_extensions.rb +0 -0
  32. data/lib/adsl/spass/spass_ds_extensions.rb +931 -0
  33. data/lib/adsl/spass/spass_translator.rb +393 -0
  34. data/lib/adsl/spass/util.rb +13 -0
  35. data/lib/adsl/util/csv_hash_formatter.rb +94 -0
  36. data/lib/adsl/util/general.rb +228 -0
  37. data/lib/adsl/util/test_helper.rb +71 -0
  38. data/lib/adsl/verification/formula_generators.rb +231 -0
  39. data/lib/adsl/verification/instrumentation_filter.rb +50 -0
  40. data/lib/adsl/verification/invariant.rb +19 -0
  41. data/lib/adsl/verification/rails_verification.rb +33 -0
  42. data/lib/adsl/verification/utils.rb +20 -0
  43. data/lib/adsl/verification/verification_case.rb +13 -0
  44. data/test/integration/rails/rails_branch_verification_test.rb +112 -0
  45. data/test/integration/rails/rails_verification_test.rb +253 -0
  46. data/test/integration/spass/basic_translation_test.rb +563 -0
  47. data/test/integration/spass/control_flow_translation_test.rb +421 -0
  48. data/test/unit/adsl/ds/data_store_spec_test.rb +54 -0
  49. data/test/unit/adsl/extract/instrumenter_test.rb +103 -0
  50. data/test/unit/adsl/extract/meta_test.rb +142 -0
  51. data/test/unit/adsl/extract/rails/action_block_builder_test.rb +178 -0
  52. data/test/unit/adsl/extract/rails/action_instrumenter_test.rb +68 -0
  53. data/test/unit/adsl/extract/rails/active_record_metaclass_generator_test.rb +336 -0
  54. data/test/unit/adsl/extract/rails/callback_chain_simulator_test.rb +76 -0
  55. data/test/unit/adsl/extract/rails/invariant_extractor_test.rb +92 -0
  56. data/test/unit/adsl/extract/rails/rails_extractor_test.rb +1380 -0
  57. data/test/unit/adsl/extract/rails/rails_test_helper_test.rb +25 -0
  58. data/test/unit/adsl/extract/sexp_utils_test.rb +100 -0
  59. data/test/unit/adsl/fol/first_order_logic_test.rb +227 -0
  60. data/test/unit/adsl/parser/action_parser_test.rb +1040 -0
  61. data/test/unit/adsl/parser/ast_nodes_test.rb +359 -0
  62. data/test/unit/adsl/parser/class_parser_test.rb +288 -0
  63. data/test/unit/adsl/parser/general_parser_test.rb +67 -0
  64. data/test/unit/adsl/parser/invariant_parser_test.rb +432 -0
  65. data/test/unit/adsl/parser/parser_util_test.rb +126 -0
  66. data/test/unit/adsl/spass/bin_test.rb +65 -0
  67. data/test/unit/adsl/spass/ruby_extensions_test.rb +39 -0
  68. data/test/unit/adsl/spass/spass_ds_extensions_test.rb +7 -0
  69. data/test/unit/adsl/spass/spass_translator_test.rb +342 -0
  70. data/test/unit/adsl/util/csv_hash_formatter_test.rb +68 -0
  71. data/test/unit/adsl/util/general_test.rb +303 -0
  72. data/test/unit/adsl/util/test_helper_test.rb +120 -0
  73. data/test/unit/adsl/verification/formula_generators_test.rb +200 -0
  74. data/test/unit/adsl/verification/instrumentation_filter_test.rb +39 -0
  75. data/test/unit/adsl/verification/utils_test.rb +39 -0
  76. data/test/unit/adsl/verification/verification_case_test.rb +8 -0
  77. metadata +229 -29
  78. data/lib/ds/data_store_spec.rb +0 -292
  79. data/lib/fol/first_order_logic.rb +0 -260
  80. data/lib/parser/adsl_ast.rb +0 -779
  81. data/lib/parser/adsl_parser.racc +0 -151
  82. data/lib/parser/adsl_parser.tab.rb +0 -976
  83. data/lib/parser/dsdl_parser.rex.rb +0 -196
  84. data/lib/parser/dsdl_parser.tab.rb +0 -976
  85. data/lib/spass/bin.rb +0 -164
  86. data/lib/spass/spass_ds_extensions.rb +0 -870
  87. data/lib/spass/spass_translator.rb +0 -388
  88. data/lib/spass/util.rb +0 -11
  89. data/lib/util/csv_hash_formatter.rb +0 -47
  90. data/lib/util/test_helper.rb +0 -33
  91. data/lib/util/util.rb +0 -114
@@ -0,0 +1,555 @@
1
+ require 'active_record'
2
+ require 'active_support'
3
+ require 'adsl/parser/ast_nodes'
4
+ require 'adsl/extract/rails/other_meta'
5
+ require 'adsl/util/test_helper'
6
+
7
+ module ADSL
8
+ module Extract
9
+ module Rails
10
+
11
+ class ActiveRecordMetaclassGenerator
12
+ include ADSL::Parser
13
+
14
+ def initialize(ar_class)
15
+ @ar_class = ar_class
16
+ end
17
+
18
+ def parent_classname
19
+ if @ar_class.superclass == ActiveRecord::Base
20
+ nil
21
+ else
22
+ ASTIdent.new :text => ActiveRecordMetaclassGenerator.adsl_ast_class_name(@ar_class.superclass)
23
+ end
24
+ end
25
+
26
+ def self.adsl_ast_class_name(klass)
27
+ klass.name.sub('::', '_')
28
+ end
29
+
30
+ def self.remove_by_from_method(method)
31
+ method.to_s.match(/^([^_]+)_.*/)[1].to_sym if /^[^_]+_by_.*$/ =~ method.to_s
32
+ end
33
+
34
+ def reflection_to_adsl_ast(reflection)
35
+ assoc_name = reflection.name.to_s
36
+ target_class = reflection.class_name
37
+ cardinality = reflection.collection? ? [0, 1.0/0.0] : [0, 1]
38
+ inverse_of = case reflection.macro
39
+ when :belongs_to; nil
40
+ when :has_one, :has_many
41
+ inverse_of_col_name = reflection.has_inverse? ? reflection.inverse_of : reflection.foreign_key
42
+ candidates = target_class.constantize.reflections.values.select do |r|
43
+ r.macro == :belongs_to && r.foreign_key.to_sym == inverse_of_col_name.to_sym
44
+ end
45
+ if candidates.empty?
46
+ # just dont treat it as an inverse
47
+ nil
48
+ else
49
+ raise "Multiple opposite relations found for #{reflection}: #{canditates}" if candidates.length > 1
50
+ candidates.first.name.to_s
51
+ end
52
+ when :has_and_belongs_to_many
53
+ join_table = reflection.options[:join_table]
54
+ candidates = target_class.constantize.reflections.values.select do |r|
55
+ r.macro == :has_and_belongs_to_many && r.options[:join_table] == join_table
56
+ end
57
+ raise "No inverse relations found for #{reflection}" if candidates.empty?
58
+ raise "Multiple opposite relations found for #{reflection}: #{canditates}" if candidates.length > 1
59
+ foreign_name = candidates.first.name.to_s
60
+ assoc_name < foreign_name ? nil : foreign_name
61
+ else
62
+ raise "Unknown association macro `#{reflection.macro}' on #{reflection}"
63
+ end
64
+
65
+ ASTRelation.new(
66
+ :cardinality => cardinality,
67
+ :to_class_name => ASTIdent.new(:text => target_class.sub('::', '_')),
68
+ :name => ASTIdent.new(:text => assoc_name.to_s),
69
+ :inverse_of_name => (inverse_of.nil? ? nil : ASTIdent.new(:text => inverse_of.sub('::', '_')))
70
+ )
71
+ end
72
+
73
+ def reflections(options = {})
74
+ # true => include only
75
+ # false => exclude
76
+ # nil => ignore filter
77
+ options = {
78
+ :this_class => true,
79
+ :polymorphic => false,
80
+ :through => nil,
81
+ }.merge options
82
+
83
+ refs = @ar_class.reflections.values.dup
84
+
85
+ case options[:this_class]
86
+ when true; refs.select!{ |ref| ref.active_record == @ar_class }
87
+ when false; refs.select!{ |ref| ref.active_record != @ar_class}
88
+ end
89
+
90
+ case options[:polymorphic]
91
+ when true; refs.select!{ |ref| ref.options[:as] or ref.options[:polymorphic] }
92
+ when false; refs.select!{ |ref| !ref.options[:as] and !ref.options[:polymorphic] }
93
+ end
94
+
95
+ case options[:through]
96
+ when true; refs.select!{ |ref| ref.through_reflection }
97
+ when false; refs.select!{ |ref| ref.through_reflection.nil? }
98
+ end
99
+
100
+ refs
101
+ end
102
+
103
+ def create_destroys(new_class)
104
+ refls = reflections :this_class => nil
105
+ new_class.send :define_method, :destroy do |*args|
106
+ stmts = []
107
+ object = if self.adsl_ast.objset_has_side_effects?
108
+ var_name = ASTIdent.new(:text => "__delete_#{ self.class.adsl_ast_class_name }_temp_var")
109
+ stmts << ASTAssignment.new(:var_name => var_name.dup, :objset => self.adsl_ast)
110
+ self.class.new :adsl_ast => ASTVariable.new(:var_name => var_name.dup)
111
+ else
112
+ self
113
+ end
114
+
115
+ refls.each do |refl|
116
+ next unless [:delete, :delete_all, :destroy, :destroy_all].include? refl.options[:dependent]
117
+
118
+ if refl.options[:dependent] == :destroy or refl.options[:dependent] == :destroy_all
119
+ if refl.through_reflection.nil?
120
+ stmts += object.send(refl.name).destroy
121
+ else
122
+ stmts += object.send(refl.through_reflection.name).destroy
123
+ end
124
+ else
125
+ if refl.through_reflection.nil?
126
+ stmts += object.send(refl.name).delete
127
+ else
128
+ stmts += object.send(refl.through_reflection.name).delete
129
+ end
130
+ end
131
+ end
132
+
133
+ stmts << ASTDeleteObj.new(:objset => object.adsl_ast)
134
+
135
+ stmts
136
+ end
137
+ new_class.send(:define_method, :destroy! ){ |*args| destroy *args }
138
+ new_class.send(:define_method, :destroy_all ){ |*args| destroy *args }
139
+
140
+ new_class.send :define_method, :delete do |*args|
141
+ [ASTDeleteObj.new(:objset => adsl_ast)]
142
+ end
143
+ new_class.send(:define_method, :delete! ){ |*args| delete *args }
144
+ new_class.send(:define_method, :delete_all){ |*args| delete *args }
145
+ new_class.send(:define_method, :clear ){ |*args| delete *args }
146
+ end
147
+
148
+ def generate_class
149
+ @ar_class.class_exec do
150
+
151
+ include ADSL::Parser
152
+
153
+ attr_accessor :adsl_ast
154
+ attr_accessible :adsl_ast
155
+
156
+ def initialize(attributes = {}, options = {})
157
+ attributes = {} if attributes.is_a?(MetaUnknown)
158
+ super({
159
+ :adsl_ast => ASTCreateObjset.new(:class_name => ASTIdent.new(:text => self.class.adsl_ast_class_name))
160
+ }.merge(attributes), options)
161
+ end
162
+
163
+ # no-ops
164
+ def save(*args); end
165
+ def save!(*args); end
166
+ def reorder(*args); self; end
167
+ def order(*args); self; end
168
+ def reorder(*args); self; end
169
+ def includes(*args); self; end
170
+ def all(*args); self; end
171
+ def scope_for_create; self; end
172
+ def id; self; end # used to allow foreign key assignment
173
+
174
+ def count_by_group(*args); MetaUnknown.new; end
175
+ def size; MetaUnknown.new; end
176
+ def length; MetaUnknown.new; end
177
+ def count; MetaUnknown.new; end
178
+ def map; MetaUnknown.new; end
179
+ def valid?(*args); MetaUnknown.new; end
180
+
181
+ def hash
182
+ @adsl_ast.hash
183
+ end
184
+
185
+ def take(*params)
186
+ self.class.new :adsl_ast => ASTOneOf.new(:objset => self.adsl_ast)
187
+ end
188
+ alias_method :take!, :take
189
+ alias_method :first, :take
190
+ alias_method :last, :take
191
+ alias_method :find, :take
192
+
193
+ def where(*args)
194
+ self.class.new :adsl_ast => ASTSubset.new(:objset => self.adsl_ast)
195
+ end
196
+ alias_method :only, :where
197
+ alias_method :except, :where
198
+ alias_method :my, :where
199
+ alias_method :paginate, :where # will_paginate
200
+ def merge(other)
201
+ if other.adsl_ast.is_a? ASTAllOf
202
+ self
203
+ elsif self.adsl_ast.is_a? ASTAllOf
204
+ other
205
+ elsif other.is_a? ActiveRecord::Base
206
+ # the scope is on the right hand side; so we can just replace all AllOfs in the right
207
+ # with the left hand side
208
+ self.class.new(:adsl_ast => other.adsl_ast.block_replace{ |node|
209
+ self.adsl_ast.dup if node.is_a? ASTAllOf
210
+ })
211
+ else
212
+ self
213
+ end
214
+ end
215
+
216
+ def apply_finder_options(options)
217
+ options.include?(:conditions) ? self.class.new(:adsl_ast => ASTSubset.new(:objset => self.adsl_ast)) : self
218
+ end
219
+
220
+ def empty?
221
+ ASTEmpty.new :objset => self.adsl_ast
222
+ end
223
+
224
+ def +(other)
225
+ self.class.new :adsl_ast => ASTUnion.new(:objsets => [self.adsl_ast, other.adsl_ast])
226
+ end
227
+
228
+ def each(&block)
229
+ instrumenter = ::ADSL::Extract::Instrumenter.get_instance
230
+ var_name = ASTIdent.new(:text => block.parameters.first[1].to_s)
231
+ var = self.class.new(:adsl_ast => ADSL::Parser::ASTVariable.new(:var_name => var_name))
232
+
233
+ substmts = instrumenter.abb.in_stmt_frame var, &block
234
+
235
+ ASTForEach.new(
236
+ :objset => self.adsl_ast,
237
+ :var_name => var_name.dup,
238
+ :block => ASTBlock.new(:statements => substmts)
239
+ )
240
+ end
241
+
242
+ def include?(other)
243
+ other = other.adsl_ast if other.respond_to? :adsl_ast
244
+ if other.is_a? ASTNode and other.class.is_objset?
245
+ ASTIn.new :objset1 => other, :objset2 => self.adsl_ast
246
+ else
247
+ super
248
+ end
249
+ end
250
+ def <=(other); other.include? self; end
251
+ alias_method :>=, :include?
252
+
253
+ def ==(other)
254
+ other = other.adsl_ast if other.respond_to? :adsl_ast
255
+ if other.is_a? ASTNode and other.class.is_objset?
256
+ ASTEqual.new :objsets => [self.adsl_ast, other]
257
+ else
258
+ super
259
+ end
260
+ end
261
+
262
+ def !=(other)
263
+ other = other.adsl_ast if other.respond_to? :adsl_ast
264
+ if other.is_a? ASTNode and other.class.is_objset?
265
+ ASTNot.new(:subformula => ASTEqual.new(:objsets => [self.adsl_ast, other]))
266
+ else
267
+ super
268
+ end
269
+ end
270
+
271
+ def method_missing(method, *args, &block)
272
+ if without_by = ActiveRecordMetaclassGenerator.remove_by_from_method(method)
273
+ self.send(without_by)
274
+ # maybe this is a scope invocation?
275
+ elsif self.class.respond_to? method
276
+ begin
277
+ prev_scoped = self.class.scoped
278
+ self.class.scoped = self
279
+ return self.class.send method, *args, &block
280
+ ensure
281
+ self.class.scoped = prev_scoped
282
+ end
283
+ else
284
+ super
285
+ end
286
+ end
287
+
288
+ def respond_to?(method, include_all = false)
289
+ # maybe this is a scope invocation? hard to say
290
+ super || ActiveRecordMetaclassGenerator.remove_by_from_method(method) || self.class.respond_to?(method)
291
+ end
292
+
293
+ # note that this build method does not apply to objsets
294
+ # acquired using :through associations
295
+ def build(*params)
296
+ return self unless self.adsl_ast.is_a?(ASTDereference)
297
+ self.class.new(:adsl_ast => ASTDereferenceCreate.new(
298
+ :objset => self.adsl_ast.objset,
299
+ :rel_name => self.adsl_ast.rel_name
300
+ ))
301
+ end
302
+ alias_method :create, :build
303
+ alias_method :create!, :build
304
+
305
+ # note that this method does not apply to objsets
306
+ # acquired using :through associations
307
+ def <<(param)
308
+ return self unless self.adsl_ast.is_a?(ASTDereference)
309
+ return super unless param.respond_to? :adsl_ast
310
+ raise "Invalid type added on dereference: #{param.class.name} to #{self.class.name}" unless param.class <= self.class
311
+ ASTCreateTup.new(
312
+ :objset1 => self.adsl_ast.objset.dup,
313
+ :rel_name => self.adsl_ast.rel_name.dup,
314
+ :objset2 => param.adsl_ast.dup
315
+ )
316
+ end
317
+ alias_method :add, :<<
318
+
319
+
320
+ class << self
321
+ include ADSL::Parser
322
+
323
+ def ar_class
324
+ superclass
325
+ end
326
+
327
+ def adsl_ast_class_name
328
+ ActiveRecordMetaclassGenerator.adsl_ast_class_name(self)
329
+ end
330
+
331
+ def all(*params)
332
+ self.new :adsl_ast => ASTAllOf.new(:class_name => ASTIdent.new(:text => adsl_ast_class_name))
333
+ end
334
+ def scope_for_create; self; end
335
+ def scope_attributes?; false; end
336
+ def scoped; @scoped || all; end
337
+ def scoped=(scoped); @scoped = scoped; end
338
+ alias_method :order, :all
339
+
340
+ # calculations
341
+ def calculate(*args); ::ADSL::Extract::Rails::MetaUnknown.new; end
342
+ alias_method :count, :calculate
343
+ alias_method :average, :calculate
344
+ alias_method :minimum, :calculate
345
+ alias_method :maximum, :calculate
346
+ alias_method :sum, :calculate
347
+ alias_method :pluck, :calculate
348
+
349
+ def find(*args)
350
+ self.all.take
351
+ end
352
+
353
+ def where(*args)
354
+ self.all.where
355
+ end
356
+ alias_method :only, :where
357
+ alias_method :except, :where
358
+ alias_method :my, :where
359
+
360
+ def build(*args)
361
+ new(*args)
362
+ end
363
+
364
+ def method_missing(method, *args, &block)
365
+ if method.to_s =~ /^find_.*$/
366
+ self.find
367
+ else
368
+ super
369
+ end
370
+ end
371
+
372
+ def respond_to?(method, include_all = false)
373
+ return true if method.to_s =~ /^find_.*$/
374
+ super
375
+ end
376
+ end
377
+ end
378
+
379
+ create_destroys @ar_class
380
+
381
+ @ar_class.send :default_scope, @ar_class.all
382
+
383
+ reflections(:polymorphic => false, :through => false).each do |assoc|
384
+ @ar_class.new.replace_method assoc.name do
385
+ self_adsl_ast = self.adsl_ast
386
+ target_class = assoc.class_name.constantize
387
+ result = target_class.new :adsl_ast => ASTDereference.new(
388
+ :objset => self_adsl_ast.dup,
389
+ :rel_name => ASTIdent.new(:text => assoc.name.to_s)
390
+ )
391
+
392
+ if assoc.macro == :has_many
393
+ result.singleton_class.send :define_method, :delete do |*args|
394
+ # has_many association.delete(ids)
395
+ object = if args.empty?
396
+ self
397
+ elsif args.length == 1
398
+ self.find
399
+ else
400
+ self.where
401
+ end
402
+ if [:delete, :delete_all].include? assoc.options[:dependent]
403
+ object == self ? super() : object.delete
404
+ elsif [:destroy, :destroy_all].include? assoc.options[:dependent]
405
+ object.destroy
406
+ else
407
+ [ASTDeleteTup.new(
408
+ :objset1 => self_adsl_ast.dup,
409
+ :rel_name => ASTIdent.new(:text => assoc.name.to_s),
410
+ :objset2 => object.adsl_ast
411
+ )]
412
+ end
413
+ end
414
+ elsif assoc.macro == :has_and_belongs_to_many
415
+ result.singleton_class.send :define_method, :delete do |*args|
416
+ object = if args.empty?
417
+ self
418
+ elsif args.length == 1
419
+ self.find
420
+ else
421
+ self.where
422
+ end
423
+ [ASTDeleteTup.new(
424
+ :objset1 => self_adsl_ast.dup,
425
+ :rel_name => ASTIdent.new(:text => assoc.name.to_s),
426
+ :objset2 => object.adsl_ast
427
+ )]
428
+ end
429
+ end
430
+
431
+ result
432
+ end
433
+
434
+ @ar_class.new.replace_method "#{assoc.name}=" do |other|
435
+ ASTSetTup.new(
436
+ :objset1 => self.adsl_ast,
437
+ :rel_name => ASTIdent.new(:text => assoc.name.to_s),
438
+ :objset2 => other.adsl_ast
439
+ )
440
+ end
441
+
442
+ if assoc.macro == :belongs_to
443
+ @ar_class.class_eval <<-ruby
444
+ alias_method :#{assoc.foreign_key}, :#{assoc.name}
445
+ alias_method :#{assoc.foreign_key}=, :#{assoc.name}=
446
+ ruby
447
+ end
448
+ end
449
+ reflections(:polymorphic => false, :through => true).each do |assoc|
450
+ @ar_class.new.replace_method assoc.name do
451
+ through_assoc = assoc.through_reflection
452
+ source_assoc = assoc.source_reflection
453
+
454
+ first_step = self.send through_assoc.name
455
+ result = first_step.send source_assoc.name
456
+
457
+ result.singleton_class.class_exec do
458
+ def build(*args)
459
+ # does not support composite :through associations
460
+ self.class.new(:adsl_ast => ASTDereferenceCreate.new(
461
+ :rel_name => self.adsl_ast.rel_name,
462
+ :objset => ASTDereferenceCreate.new(
463
+ :objset => self.adsl_ast.objset.objset,
464
+ :rel_name => self.adsl_ast.objset.rel_name
465
+ )
466
+ ))
467
+ end
468
+
469
+ def <<(param)
470
+ target = param.adsl_ast
471
+ intermed_deref = self.adsl_ast
472
+ source_deref = self.adsl_ast.objset
473
+ ASTCreateTup.new(
474
+ :objset1 => ASTDereferenceCreate.new(
475
+ :objset => source_deref.objset,
476
+ :rel_name => source_deref.rel_name,
477
+ ),
478
+ :rel_name => intermed_deref.rel_name,
479
+ :objset2 => target.dup
480
+ )
481
+ end
482
+ alias_method :add, :<<
483
+ end
484
+ result
485
+ end
486
+
487
+ @ar_class.new.replace_method "#{assoc.name}=" do |other|
488
+ # delete the join objects originating from this, not invoking callbacks
489
+ # create new ones
490
+ # connect this with all the join objects, and each join object with a corresponding other
491
+ through_assoc = assoc.through_reflection
492
+ source_assoc = assoc.source_reflection
493
+ join_class_name = through_assoc.class_name.constantize.adsl_ast_class_name
494
+ origin_name = ASTIdent.new :text => "#{self.class.name.underscore}__#{through_assoc.name}__origin"
495
+ target_name = ASTIdent.new :text => "#{self.class.name.underscore}__#{through_assoc.name}__target"
496
+ iter_name = ASTIdent.new :text => "#{self.class.name.underscore}__#{through_assoc.name}__iterator"
497
+ join_name = ASTIdent.new :text => "#{self.class.name.underscore}__#{through_assoc.name}__join_object"
498
+ [
499
+ ASTAssignment.new(:var_name => origin_name.dup, :objset => self.adsl_ast),
500
+ ASTAssignment.new(:var_name => target_name.dup, :objset => other.adsl_ast),
501
+ ASTDeleteObj.new(:objset => ASTDereference.new(
502
+ :objset => ASTVariable.new(:var_name => origin_name.dup),
503
+ :rel_name => ASTIdent.new(:text => through_assoc.name.to_s)
504
+ )),
505
+ ASTForEach.new(
506
+ :var_name => iter_name,
507
+ :objset => ASTVariable.new(:var_name => target_name.dup),
508
+ :block => ASTBlock.new(:statements => [
509
+ ASTAssignment.new(
510
+ :var_name => join_name,
511
+ :objset => ASTCreateObjset.new(:class_name => ASTIdent.new(:text => join_class_name))
512
+ ),
513
+ ASTCreateTup.new(
514
+ :objset1 => ASTVariable.new(:var_name => origin_name.dup),
515
+ :rel_name => ASTIdent.new(:text => through_assoc.name.to_s),
516
+ :objset2 => ASTVariable.new(:var_name => join_name.dup)
517
+ ),
518
+ ASTCreateTup.new(
519
+ :objset1 => ASTVariable.new(:var_name => join_name.dup),
520
+ :rel_name => ASTIdent.new(:text => source_assoc.name.to_s),
521
+ :objset2 => ASTVariable.new(:var_name => iter_name.dup)
522
+ )
523
+ ])
524
+ )
525
+ ]
526
+ end
527
+ end
528
+
529
+
530
+ reflections(:polymorphic => true).each do |assoc|
531
+ @ar_class.new.replace_method assoc.name do
532
+ ADSL::Extract::Rails::MetaUnknown.new
533
+ end
534
+ end
535
+
536
+ adsl_ast_parent_name = parent_classname
537
+
538
+ adsl_ast_relations = reflections(:polymorphic => false, :through => false).map{ |ref| reflection_to_adsl_ast ref }
539
+
540
+ @ar_class.singleton_class.send :define_method, :adsl_ast do
541
+ ASTClass.new(
542
+ :name => ASTIdent.new(:text => adsl_ast_class_name),
543
+ :parent_name => adsl_ast_parent_name,
544
+ :relations => adsl_ast_relations
545
+ )
546
+ end
547
+
548
+ @ar_class
549
+ end
550
+
551
+ end
552
+
553
+ end
554
+ end
555
+ end