adsl 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -20
- data/README.md +14 -21
- data/bin/adsl-verify +8 -8
- data/lib/adsl.rb +3 -0
- data/lib/adsl/adsl.rb +3 -0
- data/lib/adsl/ds/data_store_spec.rb +339 -0
- data/lib/adsl/extract/instrumenter.rb +206 -0
- data/lib/adsl/extract/meta.rb +33 -0
- data/lib/adsl/extract/rails/action_block_builder.rb +233 -0
- data/lib/adsl/extract/rails/action_instrumenter.rb +400 -0
- data/lib/adsl/extract/rails/action_runner.rb +57 -0
- data/lib/adsl/extract/rails/active_record_metaclass_generator.rb +555 -0
- data/lib/adsl/extract/rails/callback_chain_simulator.rb +135 -0
- data/lib/adsl/extract/rails/invariant_extractor.rb +48 -0
- data/lib/adsl/extract/rails/invariant_instrumenter.rb +70 -0
- data/lib/adsl/extract/rails/other_meta.rb +57 -0
- data/lib/adsl/extract/rails/rails_extractor.rb +211 -0
- data/lib/adsl/extract/rails/rails_instrumentation_test_case.rb +34 -0
- data/lib/adsl/extract/rails/rails_special_gem_instrumentation.rb +120 -0
- data/lib/adsl/extract/rails/rails_test_helper.rb +140 -0
- data/lib/adsl/extract/sexp_utils.rb +54 -0
- data/lib/adsl/fol/first_order_logic.rb +261 -0
- data/lib/adsl/parser/adsl_parser.racc +159 -0
- data/lib/{parser → adsl/parser}/adsl_parser.rex +4 -4
- data/lib/{parser → adsl/parser}/adsl_parser.rex.rb +6 -6
- data/lib/adsl/parser/adsl_parser.tab.rb +1031 -0
- data/lib/adsl/parser/ast_nodes.rb +1410 -0
- data/lib/adsl/railtie.rb +67 -0
- data/lib/adsl/spass/bin.rb +230 -0
- data/lib/{spass → adsl/spass}/ruby_extensions.rb +0 -0
- data/lib/adsl/spass/spass_ds_extensions.rb +931 -0
- data/lib/adsl/spass/spass_translator.rb +393 -0
- data/lib/adsl/spass/util.rb +13 -0
- data/lib/adsl/util/csv_hash_formatter.rb +94 -0
- data/lib/adsl/util/general.rb +228 -0
- data/lib/adsl/util/test_helper.rb +71 -0
- data/lib/adsl/verification/formula_generators.rb +231 -0
- data/lib/adsl/verification/instrumentation_filter.rb +50 -0
- data/lib/adsl/verification/invariant.rb +19 -0
- data/lib/adsl/verification/rails_verification.rb +33 -0
- data/lib/adsl/verification/utils.rb +20 -0
- data/lib/adsl/verification/verification_case.rb +13 -0
- data/test/integration/rails/rails_branch_verification_test.rb +112 -0
- data/test/integration/rails/rails_verification_test.rb +253 -0
- data/test/integration/spass/basic_translation_test.rb +563 -0
- data/test/integration/spass/control_flow_translation_test.rb +421 -0
- data/test/unit/adsl/ds/data_store_spec_test.rb +54 -0
- data/test/unit/adsl/extract/instrumenter_test.rb +103 -0
- data/test/unit/adsl/extract/meta_test.rb +142 -0
- data/test/unit/adsl/extract/rails/action_block_builder_test.rb +178 -0
- data/test/unit/adsl/extract/rails/action_instrumenter_test.rb +68 -0
- data/test/unit/adsl/extract/rails/active_record_metaclass_generator_test.rb +336 -0
- data/test/unit/adsl/extract/rails/callback_chain_simulator_test.rb +76 -0
- data/test/unit/adsl/extract/rails/invariant_extractor_test.rb +92 -0
- data/test/unit/adsl/extract/rails/rails_extractor_test.rb +1380 -0
- data/test/unit/adsl/extract/rails/rails_test_helper_test.rb +25 -0
- data/test/unit/adsl/extract/sexp_utils_test.rb +100 -0
- data/test/unit/adsl/fol/first_order_logic_test.rb +227 -0
- data/test/unit/adsl/parser/action_parser_test.rb +1040 -0
- data/test/unit/adsl/parser/ast_nodes_test.rb +359 -0
- data/test/unit/adsl/parser/class_parser_test.rb +288 -0
- data/test/unit/adsl/parser/general_parser_test.rb +67 -0
- data/test/unit/adsl/parser/invariant_parser_test.rb +432 -0
- data/test/unit/adsl/parser/parser_util_test.rb +126 -0
- data/test/unit/adsl/spass/bin_test.rb +65 -0
- data/test/unit/adsl/spass/ruby_extensions_test.rb +39 -0
- data/test/unit/adsl/spass/spass_ds_extensions_test.rb +7 -0
- data/test/unit/adsl/spass/spass_translator_test.rb +342 -0
- data/test/unit/adsl/util/csv_hash_formatter_test.rb +68 -0
- data/test/unit/adsl/util/general_test.rb +303 -0
- data/test/unit/adsl/util/test_helper_test.rb +120 -0
- data/test/unit/adsl/verification/formula_generators_test.rb +200 -0
- data/test/unit/adsl/verification/instrumentation_filter_test.rb +39 -0
- data/test/unit/adsl/verification/utils_test.rb +39 -0
- data/test/unit/adsl/verification/verification_case_test.rb +8 -0
- metadata +229 -29
- data/lib/ds/data_store_spec.rb +0 -292
- data/lib/fol/first_order_logic.rb +0 -260
- data/lib/parser/adsl_ast.rb +0 -779
- data/lib/parser/adsl_parser.racc +0 -151
- data/lib/parser/adsl_parser.tab.rb +0 -976
- data/lib/parser/dsdl_parser.rex.rb +0 -196
- data/lib/parser/dsdl_parser.tab.rb +0 -976
- data/lib/spass/bin.rb +0 -164
- data/lib/spass/spass_ds_extensions.rb +0 -870
- data/lib/spass/spass_translator.rb +0 -388
- data/lib/spass/util.rb +0 -11
- data/lib/util/csv_hash_formatter.rb +0 -47
- data/lib/util/test_helper.rb +0 -33
- 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
|