adsl 0.0.3 → 0.1.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.
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