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.
- 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
|