rdl 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +7 -6
- data/CHANGES.md +29 -0
- data/README.md +94 -26
- data/lib/rdl/boot.rb +82 -41
- data/lib/rdl/boot_rails.rb +5 -0
- data/lib/rdl/config.rb +9 -1
- data/lib/rdl/query.rb +2 -2
- data/lib/rdl/typecheck.rb +972 -225
- data/lib/rdl/types/annotated_arg.rb +8 -0
- data/lib/rdl/types/ast_node.rb +73 -0
- data/lib/rdl/types/bot.rb +8 -0
- data/lib/rdl/types/bound_arg.rb +63 -0
- data/lib/rdl/types/computed.rb +48 -0
- data/lib/rdl/types/dependent_arg.rb +9 -0
- data/lib/rdl/types/dynamic.rb +61 -0
- data/lib/rdl/types/finite_hash.rb +54 -9
- data/lib/rdl/types/generic.rb +33 -0
- data/lib/rdl/types/intersection.rb +8 -0
- data/lib/rdl/types/lexer.rex +6 -1
- data/lib/rdl/types/lexer.rex.rb +13 -1
- data/lib/rdl/types/method.rb +14 -0
- data/lib/rdl/types/nominal.rb +8 -0
- data/lib/rdl/types/non_null.rb +8 -0
- data/lib/rdl/types/optional.rb +8 -0
- data/lib/rdl/types/parser.racc +31 -5
- data/lib/rdl/types/parser.tab.rb +540 -302
- data/lib/rdl/types/rdl_types.rb +45 -0
- data/lib/rdl/types/singleton.rb +14 -1
- data/lib/rdl/types/string.rb +104 -0
- data/lib/rdl/types/structural.rb +8 -0
- data/lib/rdl/types/top.rb +8 -0
- data/lib/rdl/types/tuple.rb +32 -8
- data/lib/rdl/types/type.rb +54 -11
- data/lib/rdl/types/union.rb +41 -2
- data/lib/rdl/types/var.rb +10 -0
- data/lib/rdl/types/vararg.rb +8 -0
- data/lib/rdl/util.rb +13 -10
- data/lib/rdl/wrap.rb +271 -27
- data/lib/rdl_disable.rb +16 -2
- data/lib/types/active_record.rb +1 -0
- data/lib/types/core/array.rb +442 -23
- data/lib/types/core/basic_object.rb +3 -3
- data/lib/types/core/bigdecimal.rb +5 -0
- data/lib/types/core/class.rb +2 -0
- data/lib/types/core/dir.rb +3 -3
- data/lib/types/core/enumerable.rb +4 -4
- data/lib/types/core/enumerator.rb +1 -1
- data/lib/types/core/file.rb +4 -4
- data/lib/types/core/float.rb +203 -0
- data/lib/types/core/hash.rb +390 -15
- data/lib/types/core/integer.rb +223 -10
- data/lib/types/core/io.rb +2 -2
- data/lib/types/core/kernel.rb +8 -5
- data/lib/types/core/marshal.rb +3 -0
- data/lib/types/core/module.rb +3 -3
- data/lib/types/core/numeric.rb +0 -2
- data/lib/types/core/object.rb +5 -5
- data/lib/types/core/pathname.rb +2 -2
- data/lib/types/core/process.rb +1 -3
- data/lib/types/core/range.rb +1 -1
- data/lib/types/core/regexp.rb +2 -2
- data/lib/types/core/set.rb +1 -1
- data/lib/types/core/string.rb +408 -16
- data/lib/types/core/symbol.rb +3 -3
- data/lib/types/core/time.rb +1 -1
- data/lib/types/core/uri.rb +13 -13
- data/lib/types/rails/_helpers.rb +7 -1
- data/lib/types/rails/action_controller/mime_responds.rb +2 -0
- data/lib/types/rails/active_record/associations.rb +42 -30
- data/lib/types/rails/active_record/comp_types.rb +637 -0
- data/lib/types/rails/active_record/finder_methods.rb +1 -1
- data/lib/types/rails/active_record/model_schema.rb +28 -16
- data/lib/types/rails/active_record/relation.rb +5 -3
- data/lib/types/rails/active_record/sql-strings.rb +166 -0
- data/lib/types/rails/string.rb +1 -1
- data/lib/types/sequel.rb +1 -0
- data/lib/types/sequel/comp_types.rb +581 -0
- data/rdl.gemspec +5 -4
- data/test/test_alias.rb +4 -0
- data/test/test_array_types.rb +244 -0
- data/test/test_bound_types.rb +80 -0
- data/test/test_contract.rb +4 -0
- data/test/test_dsl.rb +5 -0
- data/test/test_dyn_comptype_checks.rb +206 -0
- data/test/test_generic.rb +21 -20
- data/test/test_hash_types.rb +322 -0
- data/test/test_intersection.rb +1 -0
- data/test/test_le.rb +29 -4
- data/test/test_member.rb +3 -1
- data/test/test_parser.rb +5 -0
- data/test/test_query.rb +1 -0
- data/test/test_rdl.rb +63 -28
- data/test/test_rdl_type.rb +4 -0
- data/test/test_string_types.rb +102 -0
- data/test/test_type_contract.rb +59 -37
- data/test/test_typecheck.rb +480 -75
- data/test/test_types.rb +17 -0
- data/test/test_wrap.rb +5 -0
- metadata +35 -5
- data/lib/types/rails/active_record/schema_types.rb +0 -51
data/lib/types/core/symbol.rb
CHANGED
@@ -2,7 +2,7 @@ RDL.nowrap :Symbol
|
|
2
2
|
|
3
3
|
RDL.type :Symbol, 'self.all_symbols', '() -> Array<Symbol>'
|
4
4
|
RDL.type :Symbol, :<=>, '(Symbol other) -> Integer or nil'
|
5
|
-
RDL.type :Symbol, :==, '(%any obj) -> %bool'
|
5
|
+
RDL.type :Symbol, :==, '(%any obj) -> %bool', effect: [:+, :+]
|
6
6
|
RDL.type :Symbol, :=~, '(%any obj) -> Integer or nil'
|
7
7
|
RDL.type :Symbol, :[], '(Integer idx) -> String'
|
8
8
|
RDL.type :Symbol, :[], '(Integer b, Integer n) -> String'
|
@@ -22,6 +22,6 @@ RDL.rdl_alias :Symbol, :size, :length
|
|
22
22
|
RDL.rdl_alias :Symbol, :slice, :[]
|
23
23
|
RDL.type :Symbol, :swapcase, '() -> Symbol'
|
24
24
|
RDL.type :Symbol, :to_proc, '() -> Proc' # TODO proc
|
25
|
-
RDL.
|
26
|
-
RDL.
|
25
|
+
RDL.type :Symbol, :to_s, "() -> String", effect: [:+, :+]
|
26
|
+
RDL.type :Symbol, :to_sym, "() -> self", effect: [:+, :+]
|
27
27
|
RDL.type :Symbol, :upcase, '() -> Symbol'
|
data/lib/types/core/time.rb
CHANGED
@@ -39,7 +39,7 @@ RDL.type :Time, :monday?, '() -> %bool'
|
|
39
39
|
RDL.rdl_alias :Time, :month, :mon
|
40
40
|
RDL.type :Time, :nsec, '() -> Integer'
|
41
41
|
RDL.type :Time, :round, '(Integer) -> Time'
|
42
|
-
RDL.type :Time, :saturday
|
42
|
+
RDL.type :Time, :saturday?, '() -> %bool'
|
43
43
|
RDL.type :Time, :sec, '() -> Integer'
|
44
44
|
RDL.type :Time, :strftime, '(String) -> String'
|
45
45
|
RDL.type :Time, :subsec, '() -> Numeric'
|
data/lib/types/core/uri.rb
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
RDL.nowrap :URI
|
2
2
|
|
3
|
-
RDL.type :URI, :decode_www_form, '(String, ?Encoding, ?String separator, %bool use_charset, %bool isindex) -> Array<[String,String]>'
|
4
|
-
RDL.type :URI, :decode_www_form_component, '(String, ?Encoding) -> Array<[String,String]>'
|
3
|
+
RDL.type :URI, :'self.decode_www_form', '(String, ?Encoding, ?String separator, %bool use_charset, %bool isindex) -> Array<[String,String]>'
|
4
|
+
RDL.type :URI, :'self.decode_www_form_component', '(String, ?Encoding) -> Array<[String,String]>'
|
5
5
|
# RDL.type :URI, :encode_www_form, '(Array<Array<String>>, ?) -> String' #Doublesplat
|
6
6
|
# RDL.type :URI, :encode_www_form_component, '(String, ?) -> String'
|
7
|
-
RDL.type :URI, :extract, '(String, ?Array) { (*%any) -> %any} -> Array<String>'
|
8
|
-
RDL.type :URI, :join, '(*String) -> URI::HTTP'
|
9
|
-
RDL.type :URI, :parse, '(String) -> URI::HTTP'
|
10
|
-
RDL.type :URI, :regexp, '(?Array schemes) -> Array<String>' #Assume schemes are strings
|
11
|
-
RDL.type :URI, :scheme_list, '() -> Hash<String,Class>'
|
12
|
-
RDL.type :URI, :split, '(String) -> Array<String or nil>'
|
7
|
+
RDL.type :URI, :'self.extract', '(String, ?Array) { (*%any) -> %any} -> Array<String>'
|
8
|
+
RDL.type :URI, :'self.join', '(*String) -> URI::HTTP'
|
9
|
+
RDL.type :URI, :'self.parse', '(String) -> URI::HTTP'
|
10
|
+
RDL.type :URI, :'self.regexp', '(?Array schemes) -> Array<String>' #Assume schemes are strings
|
11
|
+
RDL.type :URI, :'self.scheme_list', '() -> Hash<String,Class>'
|
12
|
+
RDL.type :URI, :'self.split', '(String) -> Array<String or nil>'
|
13
13
|
|
14
|
-
RDL.type :URI, :escape, '(String, *Regexp) -> String'
|
15
|
-
RDL.type :URI, :escape, '(String, *String) -> String'
|
16
|
-
RDL.type :URI, :unescape, '(*String) -> String'
|
17
|
-
RDL.rdl_alias :URI, :encode, :escape
|
18
|
-
RDL.rdl_alias :URI, :decode, :unescape
|
14
|
+
RDL.type :URI, :'self.escape', '(String, *Regexp) -> String'
|
15
|
+
RDL.type :URI, :'self.escape', '(String, *String) -> String'
|
16
|
+
RDL.type :URI, :'self.unescape', '(*String) -> String'
|
17
|
+
RDL.rdl_alias :URI, :'self.encode', :'self.escape'
|
18
|
+
RDL.rdl_alias :URI, :'self.decode', :'self.unescape'
|
data/lib/types/rails/_helpers.rb
CHANGED
@@ -12,7 +12,7 @@ class RDL::Rails
|
|
12
12
|
when :string, :text, :binary
|
13
13
|
return 'String'
|
14
14
|
when :integer
|
15
|
-
return '
|
15
|
+
return 'Integer'
|
16
16
|
when :float
|
17
17
|
return 'Float'
|
18
18
|
when :decimal
|
@@ -25,6 +25,12 @@ class RDL::Rails
|
|
25
25
|
return 'Time'
|
26
26
|
when :datetime
|
27
27
|
return 'DateTime'
|
28
|
+
when :json
|
29
|
+
return 'Json'
|
30
|
+
when :inet
|
31
|
+
return 'Inet'
|
32
|
+
when :tsvector
|
33
|
+
return 'tsvector'
|
28
34
|
else
|
29
35
|
raise RuntimeError, "Unrecoganized column type #{rails_type}"
|
30
36
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
RDL.nowrap :'ActiveRecord::Associations::CollectionProxy'
|
2
|
+
RDL.nowrap :'ActiveRecord::Associations::ClassMethods'
|
2
3
|
|
3
4
|
RDL.type_params :'ActiveRecord::Associations::CollectionProxy', [:t], :all?
|
4
5
|
|
@@ -60,6 +61,28 @@ RDL.type :'ActiveRecord::Associations::CollectionProxy', :second_to_last, '(Inte
|
|
60
61
|
RDL.type :'ActiveRecord::Associations::CollectionProxy', :last, '() -> t or nil'
|
61
62
|
RDL.type :'ActiveRecord::Associations::CollectionProxy', :last, '(Integer) -> ActiveRecord::Associations::CollectionProxy<t>'
|
62
63
|
|
64
|
+
RDL.type :'ActiveRecord::Associations::CollectionProxy', :where, '(String, *%any) -> ActiveRecord::Associations::CollectionProxy<t>'
|
65
|
+
RDL.type :'ActiveRecord::Associations::CollectionProxy', :where, '(**%any) -> ActiveRecord::Associations::CollectionProxy<t>'
|
66
|
+
RDL.type :'ActiveRecord::Associations::CollectionProxy', :group, '(Symbol) -> ActiveRecord::Associations::CollectionProxy<t>'
|
67
|
+
RDL.type :'ActiveRecord::Associations::CollectionProxy', :order, '(Symbol) -> ActiveRecord::Associations::CollectionProxy<t>'
|
68
|
+
|
69
|
+
|
70
|
+
# Remaining methods are from CollectionProxy
|
71
|
+
# TODO give these precise types for this particular model
|
72
|
+
# collection<<(object, ...)
|
73
|
+
# collection.delete(object, ...)
|
74
|
+
# collection.destroy(object, ...)
|
75
|
+
# collection.clear
|
76
|
+
# collection.empty?
|
77
|
+
# collection.size
|
78
|
+
# collection.find(...)
|
79
|
+
# collection.where(...)
|
80
|
+
# collection.exists?(...)
|
81
|
+
# collection.build(attributes = {})
|
82
|
+
# collection.create(attributes = {})
|
83
|
+
# collection.create!(attributes = {})
|
84
|
+
|
85
|
+
|
63
86
|
module ActiveRecord::Associations::ClassMethods
|
64
87
|
|
65
88
|
# TODO: Check presence of methods required by, e.g., foreign_key, primary_key, etc.
|
@@ -118,8 +141,6 @@ module ActiveRecord::Associations::ClassMethods
|
|
118
141
|
assoc_type = '%any' # type is data-driven, can't determine statically
|
119
142
|
elsif class_name
|
120
143
|
assoc_type = class_name.to_s.classify
|
121
|
-
elsif anonymous_class # not sure this has anonymou_class
|
122
|
-
assoc_type = anonymous_class.to_s.classify
|
123
144
|
else
|
124
145
|
assoc_type = name.to_s.classify # camelize?
|
125
146
|
end
|
@@ -138,6 +159,7 @@ module ActiveRecord::Associations::ClassMethods
|
|
138
159
|
rdl_type :'ActiveRecord::Associations::ClassMethods', :has_many,
|
139
160
|
'(%symstr name, ?{ (?ActiveRecord::Base) -> %any } scope, class_name: ?%symstr,' +
|
140
161
|
'foreign_key: ?%symstr, foreign_type: ?%symstr, primary_key: ?%symstr,' +
|
162
|
+
'join_table: ?%symstr,'+
|
141
163
|
'dependent: ?(:destroy or :delete_all or :nullify or :restrict_with_exception or :restrict_with_error),' +
|
142
164
|
'counter_cache: ?(%bool or %symstr), as: ?%symstr, through: ?%symstr, source: ?%symstr,' +
|
143
165
|
'source_type: ?%symstr, validate: ?%bool, inverse_of: ?%symstr, extend: ?(Module or Array<Module>))' +
|
@@ -146,7 +168,7 @@ module ActiveRecord::Associations::ClassMethods
|
|
146
168
|
rdl_pre :'ActiveRecord::Associations::ClassMethods', :has_many do
|
147
169
|
|name, scope=nil, class_name: nil, foreign_key: nil, foreign_type: nil, primary_key: nil,
|
148
170
|
dependent: nil, counter_cache: nil, as: nil, through: nil, source: nil, source_type: nil,
|
149
|
-
validate: nil, inverse_of: nil, extend: nil|
|
171
|
+
validate: nil, inverse_of: nil, extend: nil, join_table: nil|
|
150
172
|
|
151
173
|
if class_name
|
152
174
|
collect_type = class_name.to_s.classify
|
@@ -155,12 +177,14 @@ module ActiveRecord::Associations::ClassMethods
|
|
155
177
|
end
|
156
178
|
rdl_type name, "() -> ActiveRecord::Associations::CollectionProxy<#{collect_type}>"
|
157
179
|
rdl_type "#{name}=", "(Array<t>) -> ActiveRecord::Associations::CollectionProxy<#{collect_type}>" # TODO not sure of type
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
180
|
+
if primary_key # not every model has a primary key
|
181
|
+
RDL.at(:model) {
|
182
|
+
# primary_key is not available when has_many is first called!
|
183
|
+
id_type = RDL::Rails.column_to_rdl(collect_type.constantize.columns_hash[primary_key].type)
|
184
|
+
rdl_type "#{name.to_s.singularize}_ids", "() -> Array<#{id_type}>"
|
185
|
+
rdl_type "#{name.to_s.singularize}_ids=", "() -> Array<#{id_type}>"
|
186
|
+
}
|
187
|
+
end
|
164
188
|
true
|
165
189
|
end
|
166
190
|
|
@@ -181,27 +205,15 @@ module ActiveRecord::Associations::ClassMethods
|
|
181
205
|
end
|
182
206
|
rdl_type name, "() -> ActiveRecord::Associations::CollectionProxy<#{collect_type}>"
|
183
207
|
rdl_type "#{name}=", "(Array<t>) -> ActiveRecord::Associations::CollectionProxy<#{collect_type}>" # TODO not sure of type
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
# collection<<(object, ...)
|
194
|
-
# collection.delete(object, ...)
|
195
|
-
# collection.destroy(object, ...)
|
196
|
-
# collection.clear
|
197
|
-
# collection.empty?
|
198
|
-
# collection.size
|
199
|
-
# collection.find(...)
|
200
|
-
# collection.where(...)
|
201
|
-
# collection.exists?(...)
|
202
|
-
# collection.build(attributes = {})
|
203
|
-
# collection.create(attributes = {})
|
204
|
-
# collection.create!(attributes = {})
|
208
|
+
if primary_key # not every model has a primary key
|
209
|
+
RDL.at(:model) {
|
210
|
+
# primary_key is not available when has_and_belongs_to_many is first called!
|
211
|
+
id_type = RDL::Rails.column_to_rdl(collect_type.constantize.columns_hash[primary_key].type)
|
212
|
+
rdl_type "#{name.to_s.singularize}_ids", "() -> Array<#{id_type}>"
|
213
|
+
rdl_type "#{name.to_s.singularize}_ids=", "() -> Array<#{id_type}>"
|
214
|
+
}
|
215
|
+
end
|
216
|
+
|
205
217
|
true
|
206
218
|
end
|
207
219
|
|
@@ -0,0 +1,637 @@
|
|
1
|
+
require_relative "#{File.dirname(__FILE__)}/sql-strings.rb"
|
2
|
+
|
3
|
+
class ActiveRecord::Base
|
4
|
+
extend RDL::Annotate
|
5
|
+
|
6
|
+
type Object, :try, "(Symbol) -> Object", wrap: false
|
7
|
+
type Object, :present?, "() -> %bool", wrap: false
|
8
|
+
type :initialize, '(``DBType.rec_to_schema_type(trec, true)``) -> self', wrap: false
|
9
|
+
type 'self.create', '(``DBType.rec_to_schema_type(trec, true)``) -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
10
|
+
type 'self.create!', '(``DBType.rec_to_schema_type(trec, true)``) -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
11
|
+
type :initialize, '() -> self', wrap: false
|
12
|
+
type 'self.create', '() -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
13
|
+
type 'self.create!', '() -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
14
|
+
type :attribute_names, "() -> Array<String>", wrap: false
|
15
|
+
type :to_json, "(?{ only: Array<String> }) -> String", wrap: false
|
16
|
+
type :update_column, '(``uc_first_arg(trec)``, ``uc_second_arg(trec, targs)``) -> %bool', wrap: false
|
17
|
+
type :[], '(Symbol) -> ``access_output(trec, targs)``', wrap: false
|
18
|
+
type :save!, '(?{ validate: %bool }) -> %bool', wrap: false
|
19
|
+
type 'self.transaction', '() {() -> u} -> u', wrap: false
|
20
|
+
|
21
|
+
type RDL::Globals, 'self.ar_db_schema', "() -> Hash<%any, RDL::Type::GenericType>", wrap: false, effect: [:+, :+]
|
22
|
+
type String, :singularize, "() -> String", wrap: false, effect: [:+, :+]
|
23
|
+
type String, :camelize, "() -> String", wrap: false, effect: [:+, :+]
|
24
|
+
type String, :pluralize, "() -> String", wrap: false, effect: [:+, :+]
|
25
|
+
type String, :underscore, "() -> String", wrap: false, effect: [:+, :+]
|
26
|
+
|
27
|
+
def self.access_output(trec, targs)
|
28
|
+
case trec
|
29
|
+
when RDL::Type::NominalType
|
30
|
+
tname = trec.name.to_sym
|
31
|
+
tschema = RDL.type_cast(RDL::Globals.ar_db_schema[tname].params[0], "RDL::Type::FiniteHashType", force: true).elts
|
32
|
+
raise "Schema not found." unless tschema
|
33
|
+
arg = targs[0]
|
34
|
+
case arg
|
35
|
+
when RDL::Type::SingletonType
|
36
|
+
col = arg.val
|
37
|
+
ret = tschema[col]
|
38
|
+
ret = RDL::Globals.types[:nil] unless ret
|
39
|
+
return ret
|
40
|
+
else
|
41
|
+
raise "TODO"
|
42
|
+
end
|
43
|
+
else
|
44
|
+
raise 'unexpected type'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
RDL.type ActiveRecord::Base, 'self.access_output', "(RDL::Type::Type, Array<RDL::Type::Type>) -> RDL::Type::Type", wrap: false, typecheck: :type_code, effect: [:+, :+]
|
48
|
+
|
49
|
+
def self.uc_first_arg(trec)
|
50
|
+
case trec
|
51
|
+
when RDL::Type::NominalType
|
52
|
+
tname = trec.name.to_sym
|
53
|
+
tschema = RDL.type_cast(RDL::Globals.ar_db_schema[tname].params[0], "RDL::Type::FiniteHashType", force: true).elts
|
54
|
+
raise "Schema not found." unless tschema
|
55
|
+
typs = RDL.type_cast(tschema.keys, "Array<Symbol>", force: true).reject { |k| k == :__associations}.map { |k| RDL::Type::SingletonType.new(k) }
|
56
|
+
return RDL::Type::UnionType.new(*RDL.type_cast(typs, "Array<RDL::Type::Type>"))
|
57
|
+
else
|
58
|
+
raise "unexpected type"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
RDL.type ActiveRecord::Base, 'self.uc_first_arg', "(RDL::Type::Type) -> RDL::Type::UnionType", wrap: false, typecheck: :type_code, effect: [:+, :+]
|
62
|
+
|
63
|
+
def self.uc_second_arg(trec, targs)
|
64
|
+
case trec
|
65
|
+
when RDL::Type::NominalType
|
66
|
+
tname = trec.name.to_sym
|
67
|
+
tschema = RDL.type_cast(RDL::Globals.ar_db_schema[tname].params[0], "RDL::Type::FiniteHashType", force: true).elts
|
68
|
+
raise "Schema not found." unless tschema
|
69
|
+
raise "Unexpected first arg type." unless targs[0].is_a?(RDL::Type::SingletonType) && RDL.type_cast(targs[0], "RDL::Type::SingletonType<Object>").val.is_a?(Symbol)
|
70
|
+
return tschema[RDL.type_cast(targs[0], "RDL::Type::SingletonType<Symbol>").val]
|
71
|
+
else
|
72
|
+
raise "unexpected type"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
RDL.type ActiveRecord::Base, 'self.uc_second_arg', "(RDL::Type::Type, Array<RDL::Type::Type>) -> RDL::Type::Type", wrap: false, typecheck: :type_code, effect: [:+, :+]
|
76
|
+
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
module ActiveRecord::AutosaveAssociation
|
81
|
+
extend RDL::Annotate
|
82
|
+
type :reload, "() -> %any", wrap: false
|
83
|
+
end
|
84
|
+
|
85
|
+
module ActiveRecord::Transactions
|
86
|
+
extend RDL::Annotate
|
87
|
+
type :destroy, '() -> self', wrap: false
|
88
|
+
type :save, '(?{ validate: %bool }) -> %bool', wrap: false
|
89
|
+
end
|
90
|
+
|
91
|
+
module ActiveRecord::Suppressor
|
92
|
+
extend RDL::Annotate
|
93
|
+
|
94
|
+
type :save!, '() -> %bool', wrap: false
|
95
|
+
end
|
96
|
+
|
97
|
+
module ActiveRecord::Core::ClassMethods
|
98
|
+
extend RDL::Annotate
|
99
|
+
## Types from this module are used when receiver is ActiveRecord::Base
|
100
|
+
|
101
|
+
type :find, '(Integer or String) -> ``DBType.find_output_type(trec, targs)``', wrap: false
|
102
|
+
type :find, '(Array<Integer>) -> ``DBType.find_output_type(trec, targs)``', wrap: false
|
103
|
+
type :find, '(Integer, Integer, *Integer) -> ``DBType.find_output_type(trec, targs)``', wrap: false
|
104
|
+
type :find_by, '(``DBType.find_input_type(trec, targs)``) -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
105
|
+
## TODO: find_by's with conditions given as string
|
106
|
+
end
|
107
|
+
|
108
|
+
module ActiveRecord::FinderMethods
|
109
|
+
extend RDL::Annotate
|
110
|
+
## Types from this module are used when receiver is ActiveRecord_Relation
|
111
|
+
|
112
|
+
type :find, '(Integer or String) -> ``DBType.find_output_type(trec, targs)``', wrap: false
|
113
|
+
type :find, '(Array<Integer>) -> ``DBType.find_output_type(trec, targs)``', wrap: false
|
114
|
+
type :find, '(Integer, Integer, *Integer) -> ``DBType.find_output_type(trec, targs)``', wrap: false
|
115
|
+
type :find_by, '(``DBType.find_input_type(trec, targs)``) -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
116
|
+
type :first, '() -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
117
|
+
type :first!, '() -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
118
|
+
type :first, '(Integer) -> ``DBType.rec_to_array(trec)``', wrap: false
|
119
|
+
type :last, '() -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
120
|
+
type :last!, '() -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
121
|
+
type :last, '(Integer) -> ``DBType.rec_to_array(trec)``', wrap: false
|
122
|
+
type :take, '() -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
123
|
+
type :take!, '() -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
124
|
+
type :take, '(Integer) -> ``DBType.rec_to_array(trec)``', wrap: false
|
125
|
+
type :exists?, '(``DBType.exists_input_type(trec, targs)``) -> %bool', wrap: false
|
126
|
+
end
|
127
|
+
|
128
|
+
module ActiveRecord::Querying
|
129
|
+
extend RDL::Annotate
|
130
|
+
## Types from this module are used when receiver is ActiveRecord::Base
|
131
|
+
|
132
|
+
type :first, '() -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
133
|
+
type :first!, '() -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
134
|
+
type :first, '(Integer) -> ``DBType.rec_to_array(trec)``', wrap: false
|
135
|
+
type :last, '() -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
136
|
+
type :last!, '() -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
137
|
+
type :last, '(Integer) -> ``DBType.rec_to_array(trec)``', wrap: false
|
138
|
+
type :take, '() -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
139
|
+
type :take!, '() -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
140
|
+
type :take, '(Integer) -> ``DBType.rec_to_array(trec)``', wrap: false
|
141
|
+
type :exists?, '(``DBType.exists_input_type(trec, targs)``) -> %bool', wrap: false
|
142
|
+
|
143
|
+
type :where, '(``DBType.where_input_type(trec, targs)``) -> ``RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord_Relation), DBType.rec_to_nominal(trec))``', wrap: false
|
144
|
+
type :where, '(String, *%any) -> ``RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord_Relation), DBType.rec_to_nominal(trec))``', wrap: false
|
145
|
+
type :where, '() -> ``RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord::QueryMethods::WhereChain), DBType.rec_to_nominal(trec))``', wrap: false
|
146
|
+
|
147
|
+
|
148
|
+
type :joins, '(``DBType.joins_one_input_type(trec, targs)``) -> ``DBType.joins_output(trec, targs)``', wrap: false
|
149
|
+
type :joins, '(``DBType.joins_multi_input_type(trec, targs)``, %any, *%any) -> ``DBType.joins_output(trec, targs)``', wrap: false
|
150
|
+
type :group, '(*Symbol or String) -> ``RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord_Relation), DBType.rec_to_nominal(trec))``', wrap: false
|
151
|
+
type :select, '(Symbol or String or Array<String>, *Symbol or String or Array<String>) -> ``RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord_Relation), DBType.rec_to_nominal(trec))``', wrap: false
|
152
|
+
type :select, '() { (self) -> %bool } -> ``RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord_Relation), DBType.rec_to_nominal(trec))``', wrap: false
|
153
|
+
type :order, '(%any) -> ``RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord_Relation), DBType.rec_to_nominal(trec))``', wrap: false
|
154
|
+
type :includes, '(``DBType.joins_one_input_type(trec, targs)``) -> ``DBType.joins_output(trec, targs)``', wrap: false
|
155
|
+
type :includes, '(``DBType.joins_multi_input_type(trec, targs)``, %any, *%any) -> ``DBType.joins_output(trec, targs)``', wrap: false
|
156
|
+
type :limit, '(Integer) -> ``RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord_Relation), DBType.rec_to_nominal(trec))``', wrap: false
|
157
|
+
type :count, '() -> Integer', wrap: false
|
158
|
+
type :count, '(``DBType.count_input(trec, targs)``) -> Integer', wrap: false
|
159
|
+
type :sum, '(``DBType.count_input(trec, targs)``) -> Integer', wrap: false
|
160
|
+
type :destroy_all, '() -> ``DBType.rec_to_array(trec)``', wrap: false
|
161
|
+
type :delete_all, '() -> Integer', wrap: false
|
162
|
+
type :references, '(Symbol, *Symbol) -> ``RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord_Relation), DBType.rec_to_nominal(trec))``', wrap: false
|
163
|
+
end
|
164
|
+
|
165
|
+
module ActiveRecord::Relation::QueryMethods
|
166
|
+
extend RDL::Annotate
|
167
|
+
## Types from this module are used when receiver is ActiveRecord_relation
|
168
|
+
|
169
|
+
type :where, '(``DBType.where_input_type(trec, targs)``) -> ``RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord_Relation), RDL.type_cast(trec, "RDL::Type::GenericType", force: true).params[0])``', wrap: false
|
170
|
+
type :where, '(String, *%any) -> ``RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord_Relation), RDL.type_cast(trec, "RDL::Type::GenericType", force: true).params[0])``', wrap: false
|
171
|
+
#type :where, '(String, *String) -> ``RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord_Relation), trec.params[0])``', wrap: false
|
172
|
+
type :where, '() -> ``DBType.where_noarg_output_type(trec)``', wrap: false
|
173
|
+
|
174
|
+
type :joins, '(``DBType.joins_one_input_type(trec, targs)``) -> ``DBType.joins_output(trec, targs)``', wrap: false
|
175
|
+
type :joins, '(``DBType.joins_multi_input_type(trec, targs)``, %any, *%any) -> ``DBType.joins_output(trec, targs)``', wrap: false
|
176
|
+
type :group, '(*Symbol or String) -> ``RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord_Relation), RDL.type_cast(trec, "RDL::Type::GenericType", force: true).params[0])``', wrap: false
|
177
|
+
type :select, '(Symbol or String or Array<String>, *Symbol or String or Array<String>) -> ``RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord_Relation), RDL.type_cast(trec, "RDL::Type::GenericType", force: true).params[0])``', wrap: false
|
178
|
+
type :select, '() { (``RDL.type_cast(trec, "RDL::Type::GenericType", force: true).params[0]``) -> %bool } -> ``RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord_Relation), RDL.type_cast(trec, "RDL::Type::GenericType", force: true).params[0])``', wrap: false
|
179
|
+
type :order, '(%any) -> ``RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord_Relation), RDL.type_cast(trec, "RDL::Type::GenericType", force: true).params[0])``', wrap: false
|
180
|
+
type :includes, '(``DBType.joins_one_input_type(trec, targs)``) -> ``DBType.joins_output(trec, targs)``', wrap: false
|
181
|
+
type :includes, '(``DBType.joins_multi_input_type(trec, targs)``, %any, *%any) -> ``DBType.joins_output(trec, targs)``', wrap: false
|
182
|
+
type :limit, '(Integer) -> ``trec``', wrap: false
|
183
|
+
type :references, '(Symbol, *Symbol) -> self', wrap: false
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
class ActiveRecord::QueryMethods::WhereChain
|
188
|
+
extend RDL::Annotate
|
189
|
+
type_params [:t], :dummy
|
190
|
+
|
191
|
+
type :not, '(``DBType.not_input_type(trec, targs)``) -> ``RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord_Relation), RDL.type_cast(trec, "RDL::Type::GenericType", force: true).params[0])``', wrap: false
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
module ActiveRecord::Delegation
|
196
|
+
extend RDL::Annotate
|
197
|
+
|
198
|
+
type :+, '(%any) -> ``DBType.plus_output_type(trec, targs)``', wrap: false
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
class JoinTable
|
203
|
+
extend RDL::Annotate
|
204
|
+
type_params [:orig, :joined], :dummy
|
205
|
+
## type param :orig will be nominal type of base table in join
|
206
|
+
## type param :joined will be a union type of all joined tables, or just a nominal type if there's only one
|
207
|
+
|
208
|
+
## this class is meant to only be the type parameter of ActiveRecord_Relation or WhereChain, expressing multiple joined tables instead of just a single table
|
209
|
+
end
|
210
|
+
|
211
|
+
|
212
|
+
|
213
|
+
module ActiveRecord::Scoping::Named::ClassMethods
|
214
|
+
extend RDL::Annotate
|
215
|
+
type :all, '() -> ``RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord_Relation), DBType.rec_to_nominal(trec))``', wrap: false
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
module ActiveRecord::Persistence
|
220
|
+
extend RDL::Annotate
|
221
|
+
type :update!, '(``DBType.rec_to_schema_type(trec, true)``) -> %bool', wrap: false
|
222
|
+
type :update, '(``DBType.rec_to_schema_type(trec, true)``) -> %bool', wrap: false
|
223
|
+
type :update_attribute, '(Symbol, ``DBType.update_attribute_input(trec, targs)``) -> %bool', wrap: false
|
224
|
+
end
|
225
|
+
|
226
|
+
module ActiveRecord::Calculations
|
227
|
+
extend RDL::Annotate
|
228
|
+
type :count, '() -> Integer', wrap: false
|
229
|
+
type :count, '(``DBType.count_input(trec, targs)``) -> Integer', wrap: false
|
230
|
+
type :sum, '(``DBType.count_input(trec, targs)``) -> Integer', wrap: false
|
231
|
+
end
|
232
|
+
|
233
|
+
class ActiveRecord_Relation
|
234
|
+
## In practice, this is actually a private class nested within
|
235
|
+
## each ActiveRecord::Base, e.g. Person::ActiveRecord_Relation.
|
236
|
+
## Using this class just for type checking.
|
237
|
+
extend RDL::Annotate
|
238
|
+
include ActiveRecord::Relation::QueryMethods
|
239
|
+
include ActiveRecord::FinderMethods
|
240
|
+
include ActiveRecord::Calculations
|
241
|
+
include ActiveRecord::Delegation
|
242
|
+
|
243
|
+
type_params [:t], :dummy
|
244
|
+
|
245
|
+
type :each, '() -> Enumerator<t>', wrap: false
|
246
|
+
type :each, '() { (t) -> %any } -> Array<t>', wrap: false
|
247
|
+
type :empty?, '() -> %bool', wrap: false
|
248
|
+
type :present?, '() -> %bool', wrap: false
|
249
|
+
type :create, '(``DBType.rec_to_schema_type(trec, true)``) -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
250
|
+
type :create, '() -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
251
|
+
type :create!, '(``DBType.rec_to_schema_type(trec, true)``) -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
252
|
+
type :create!, '() -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
253
|
+
type :new, '(``DBType.rec_to_schema_type(trec, true)``) -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
254
|
+
type :new, '() -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
255
|
+
type :build, '(``DBType.rec_to_schema_type(trec, true)``) -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
256
|
+
type :build, '() -> ``DBType.rec_to_nominal(trec)``', wrap: false
|
257
|
+
type :destroy_all, '() -> ``DBType.rec_to_array(trec)``', wrap: false
|
258
|
+
type :delete_all, '() -> Integer', wrap: false
|
259
|
+
type :map, '() { (t) -> u } -> Array<u>'
|
260
|
+
type :all, '() -> self', wrap: false ### kind of a silly method, always just returns self
|
261
|
+
type :collect, "() { (t) -> u } -> Array<u>", wrap: false
|
262
|
+
type :find_each, "() { (t) -> x } -> nil", wrap: false
|
263
|
+
type :to_a, "() -> ``DBType.rec_to_array(trec)``", wrap: false
|
264
|
+
type :[], "(Integer) -> t", wrap: false
|
265
|
+
type :size, "() -> Integer", wrap: false
|
266
|
+
type :update_all, '(``DBType.rec_to_schema_type(trec, true)``) -> Integer', wrap: false
|
267
|
+
end
|
268
|
+
|
269
|
+
|
270
|
+
class DBType
|
271
|
+
## given a type (usually representing a receiver type in a method call), this method returns the nominal type version of that type.
|
272
|
+
## if the given type represents a joined table, then we return the nominal type version of the *base* of the joined table.
|
273
|
+
## [+ t +] is the type for which we want the nominal type.
|
274
|
+
def self.rec_to_nominal(t)
|
275
|
+
case t
|
276
|
+
when RDL::Type::SingletonType
|
277
|
+
val = RDL.type_cast(t.val, "Class", force: true)
|
278
|
+
raise RDL::Typecheck::StaticTypeError, "Expected class singleton type, got #{val} instead." unless val.is_a?(Class)
|
279
|
+
return RDL::Type::NominalType.new(val)
|
280
|
+
when RDL::Type::GenericType
|
281
|
+
raise RDL::Typecheck::StaticTypeError, "got unexpected type #{t}" unless t.base.klass == ActiveRecord_Relation
|
282
|
+
param = t.params[0]
|
283
|
+
case param
|
284
|
+
when RDL::Type::GenericType
|
285
|
+
## should be JoinTable
|
286
|
+
## When getting an indivual record from a join table, record will be of type of the base table in the join
|
287
|
+
raise RDL::Typecheck::StaticTypeError, "got unexpected type #{param}" unless param.base.klass == JoinTable
|
288
|
+
return param.params[0]
|
289
|
+
when RDL::Type::NominalType
|
290
|
+
return param
|
291
|
+
else
|
292
|
+
raise RDL::Typecheck::StaticTypeError, "got unexpected type #{t.params[0]}"
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
RDL.type DBType, 'self.rec_to_nominal', "(RDL::Type::Type) -> RDL::Type::Type", wrap: false, typecheck: :type_code, effect: [:+, :+]
|
297
|
+
|
298
|
+
def self.rec_to_array(trec)
|
299
|
+
RDL::Type::GenericType.new(RDL::Globals.types[:array], rec_to_nominal(trec))
|
300
|
+
end
|
301
|
+
RDL.type DBType, 'self.rec_to_array', "(RDL::Type::Type) -> RDL::Type::GenericType", wrap: false, typecheck: :type_code, effect: [:+, :+]
|
302
|
+
|
303
|
+
## given a receiver type in various kinds of query calls, returns the accepted finite hash type input,
|
304
|
+
## or a union of types if the receiver represents joined tables.
|
305
|
+
## [+ trec +] is the type of the receiver in the method call.
|
306
|
+
## [+ check_col +] is a boolean indicating whether or not the column types (i.e., values in the finite hash type) will be checked.
|
307
|
+
def self.rec_to_schema_type(trec, check_col, takes_array=false)
|
308
|
+
case trec
|
309
|
+
when RDL::Type::GenericType
|
310
|
+
raise "Unexpected type #{trec}." unless (trec.base.klass == ActiveRecord_Relation) || (trec.base.klass == ActiveRecord::QueryMethods::WhereChain)
|
311
|
+
param = trec.params[0]
|
312
|
+
case param
|
313
|
+
when RDL::Type::GenericType
|
314
|
+
## should be JoinTable
|
315
|
+
raise "unexpected type #{trec}" unless param.base.klass == JoinTable
|
316
|
+
base_name = RDL.type_cast(param.params[0], "RDL::Type::NominalType", force: true).klass.to_s.singularize.to_sym ### singularized symbol name of first param in JoinTable, which is base table of the joins
|
317
|
+
type_hash = table_name_to_schema_type(base_name, check_col, takes_array).elts
|
318
|
+
pp1 = param.params[1]
|
319
|
+
case pp1
|
320
|
+
when RDL::Type::NominalType
|
321
|
+
## just one table joined to base table
|
322
|
+
joined_name = pp1.klass.to_s.singularize.to_sym
|
323
|
+
joined_type = RDL::Type::OptionalType.new(table_name_to_schema_type(joined_name, check_col, takes_array))
|
324
|
+
type_hash = type_hash.merge({ joined_name.to_s.pluralize.underscore.to_sym => joined_type })
|
325
|
+
when RDL::Type::UnionType
|
326
|
+
## multiple tables joined to base table
|
327
|
+
joined_hash = RDL.type_cast(Hash[pp1.types.map { |t|
|
328
|
+
joined_name = RDL.type_cast(t, "RDL::Type::NominalType", force: true).klass.to_s.singularize.to_sym
|
329
|
+
joined_type = table_name_to_schema_type(joined_name, check_col, takes_array)
|
330
|
+
[joined_name.to_s.pluralize.underscore.to_sym, joined_type]
|
331
|
+
}
|
332
|
+
], "Hash<Symbol, RDL::Type::FiniteHashType>", force: true)
|
333
|
+
else
|
334
|
+
raise "unexpected type #{trec}"
|
335
|
+
end
|
336
|
+
return RDL::Type::FiniteHashType.new(type_hash, nil)
|
337
|
+
when RDL::Type::NominalType
|
338
|
+
tname = param.klass.to_s.to_sym
|
339
|
+
return table_name_to_schema_type(tname, check_col, takes_array)
|
340
|
+
else
|
341
|
+
raise RDL::Typecheck::StaticTypeError, "Unexpected type parameter in #{trec}."
|
342
|
+
end
|
343
|
+
when RDL::Type::SingletonType
|
344
|
+
val = RDL.type_cast(trec.val, 'Class', force: true)
|
345
|
+
raise RDL::Typecheck::StaticTypeError, "Unexpected receiver type #{trec}." unless val.is_a?(Class)
|
346
|
+
tname = val.to_s.to_sym
|
347
|
+
return table_name_to_schema_type(tname, check_col, takes_array)
|
348
|
+
when RDL::Type::NominalType
|
349
|
+
tname = trec.name.to_sym
|
350
|
+
return table_name_to_schema_type(tname, check_col, takes_array)
|
351
|
+
else
|
352
|
+
raise RDL::Typecheck::StaticTypeError, "Unexpected receiver type #{trec}."
|
353
|
+
end
|
354
|
+
end
|
355
|
+
RDL.type DBType, 'self.rec_to_schema_type', "(RDL::Type::Type, %bool, ?%bool) -> RDL::Type::FiniteHashType", wrap: false, typecheck: :type_code, effect: [:+, :+]
|
356
|
+
|
357
|
+
## turns a given table name into the appropriate finite hash type based on table schema, with optional or top-type values
|
358
|
+
## [+ tname +] is the table name as a symbol
|
359
|
+
## [+ check_col +] is a boolean indicating whether or not column types will eventually be checked
|
360
|
+
def self.table_name_to_schema_type(tname, check_col, takes_array=false)
|
361
|
+
#h = RDL.type_cast({}, "Hash<%any, RDL::Type::Type>", force: true)
|
362
|
+
ttype = RDL::Globals.ar_db_schema[tname]
|
363
|
+
raise RDL::Typecheck::StaticTypeError, "No table type for #{tname} found." unless ttype
|
364
|
+
tschema = RDL.type_cast(ttype.params[0], "RDL::Type::FiniteHashType", force: true).elts.except(:__associations)
|
365
|
+
h = Hash[tschema.map { |k, v|
|
366
|
+
if check_col
|
367
|
+
v = RDL::Type::UnionType.new(v, RDL::Type::GenericType.new(RDL::Globals.types[:array], v)) if takes_array
|
368
|
+
[k, RDL::Type::OptionalType.new(v)]
|
369
|
+
else
|
370
|
+
[k, RDL::Type::OptionalType.new(RDL::Globals.types[:top])]
|
371
|
+
end
|
372
|
+
}]
|
373
|
+
RDL::Type::FiniteHashType.new(RDL.type_cast(h, "Hash<%any, RDL::Type::Type>", force: true), nil)
|
374
|
+
end
|
375
|
+
RDL.type DBType, 'self.table_name_to_schema_type', "(Symbol, %bool, ?%bool) -> RDL::Type::FiniteHashType", wrap: false, typecheck: :type_code, effect: [:+, :+]
|
376
|
+
|
377
|
+
def self.where_input_type(trec, targs)
|
378
|
+
handle_sql_strings(trec, targs) if targs[0].is_a? RDL::Type::PreciseStringType
|
379
|
+
tschema = rec_to_schema_type(trec, true, true)
|
380
|
+
return RDL::Type::UnionType.new(tschema, RDL::Globals.types[:string], RDL::Globals.types[:array]) ## no indepth checking for string or array cases
|
381
|
+
end
|
382
|
+
RDL.type Object, 'self.handle_sql_strings', "(RDL::Type::Type, Array<RDL::Type::Type>) -> %any", wrap: false, effect: [:+, :+]
|
383
|
+
RDL.type DBType, 'self.where_input_type', "(RDL::Type::Type, Array<RDL::Type::Type>) -> RDL::Type::UnionType", wrap: false, typecheck: :type_code, effect: [:+, :+]
|
384
|
+
|
385
|
+
def self.find_input_type(trec, targs)
|
386
|
+
handle_sql_strings(trec, targs) if targs[0].is_a? RDL::Type::PreciseStringType
|
387
|
+
rec_to_schema_type(trec, true)
|
388
|
+
end
|
389
|
+
|
390
|
+
def self.update_attribute_input(trec, targs)
|
391
|
+
col = targs[0].val
|
392
|
+
col_type = targs[1]
|
393
|
+
schema = DBType.rec_to_schema_type(trec, true)
|
394
|
+
schema.elts[col]
|
395
|
+
end
|
396
|
+
|
397
|
+
def self.where_noarg_output_type(trec)
|
398
|
+
case trec
|
399
|
+
when RDL::Type::SingletonType
|
400
|
+
## where called directly on class
|
401
|
+
RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord::QueryMethods::WhereChain), rec_to_nominal(trec))
|
402
|
+
when RDL::Type::GenericType
|
403
|
+
## where called on ActiveRecord_Relation
|
404
|
+
raise RDL::Typecheck::StaticTypeError, "Unexpected receiver type #{trec}." unless trec.base.klass == ActiveRecord_Relation
|
405
|
+
return RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord::QueryMethods::WhereChain), trec.params[0])
|
406
|
+
else
|
407
|
+
raise RDL::Typecheck::StaticTypeError, "Unexpected receiver type #{trec}."
|
408
|
+
end
|
409
|
+
end
|
410
|
+
RDL.type DBType, 'self.where_noarg_output_type', "(RDL::Type::Type) -> RDL::Type::GenericType", wrap: false, typecheck: :type_code, effect: [:+, :+]
|
411
|
+
|
412
|
+
def self.not_input_type(trec, targs)
|
413
|
+
tschema = rec_to_schema_type(trec, true)
|
414
|
+
return RDL::Type::UnionType.new(tschema, RDL::Globals.types[:string], RDL::Globals.types[:array]) ## no indepth checking for string or array cases
|
415
|
+
end
|
416
|
+
RDL.type DBType, 'self.not_input_type', "(RDL::Type::Type, Array<RDL::Type::Type>) -> RDL::Type::UnionType", wrap: false, typecheck: :type_code, effect: [:+, :+]
|
417
|
+
|
418
|
+
def self.exists_input_type(trec, targs)
|
419
|
+
raise "Unexpected number of arguments to ActiveRecord::Base#exists?." unless targs.size <= 1
|
420
|
+
case targs[0]
|
421
|
+
when RDL::Type::FiniteHashType
|
422
|
+
typ = rec_to_schema_type(trec, false)
|
423
|
+
else
|
424
|
+
## any type can be accepted, only thing we're intersted in is when a hash is given
|
425
|
+
## TODO: what if we get a nominal Hash type?
|
426
|
+
typ = targs[0]
|
427
|
+
end
|
428
|
+
return RDL::Type::OptionalType.new(RDL::Type::UnionType.new(RDL::Globals.types[:integer], RDL::Globals.types[:string], typ))
|
429
|
+
end
|
430
|
+
RDL.type DBType, 'self.exists_input_type', "(RDL::Type::Type, Array<RDL::Type::Type>) -> RDL::Type::Type", wrap: false, typecheck: :type_code, effect: [:+, :+]
|
431
|
+
|
432
|
+
|
433
|
+
def self.find_output_type(trec, targs)
|
434
|
+
case targs.size
|
435
|
+
when 0
|
436
|
+
raise RDL::Typecheck::StaticTypeError, "No arguments given to ActiveRecord::Base#find."
|
437
|
+
when 1
|
438
|
+
arg0 = targs[0]
|
439
|
+
case arg0
|
440
|
+
when RDL::Globals.types[:integer], RDL::Globals.types[:string]
|
441
|
+
DBType.rec_to_nominal(trec)
|
442
|
+
when RDL::Type::SingletonType
|
443
|
+
# expecting symbol or integer here
|
444
|
+
case arg0.val
|
445
|
+
when Integer
|
446
|
+
DBType.rec_to_nominal(trec)
|
447
|
+
when Symbol
|
448
|
+
## TODO
|
449
|
+
## Actually, this is deprecated in later versions
|
450
|
+
raise RDL::Typecheck::StaticTypeError, "Unexpected arg type #{arg0} in call to ActiveRecord::Base#find."
|
451
|
+
else
|
452
|
+
raise RDL::Typecheck::StaticTypeError, "Unexpected arg type #{arg0} in call to ActiveRecord::Base#find."
|
453
|
+
end
|
454
|
+
when RDL::Type::GenericType
|
455
|
+
RDL::Type::GenericType.new(RDL::Globals.types[:array], DBType.rec_to_nominal(trec))
|
456
|
+
when RDL::Type::TupleType
|
457
|
+
RDL::Type::GenericType.new(RDL::Globals.types[:array], DBType.rec_to_nominal(trec))
|
458
|
+
else
|
459
|
+
raise RDL::Typecheck::StaticTypeError, "Unexpected arg type #{arg0} in call to ActiveRecord::Base#find."
|
460
|
+
end
|
461
|
+
else
|
462
|
+
DBType.rec_to_nominal(trec)
|
463
|
+
end
|
464
|
+
end
|
465
|
+
RDL.type DBType, 'self.find_output_type', "(RDL::Type::Type, Array<RDL::Type::Type>) -> RDL::Type::Type", wrap: false, typecheck: :type_code, effect: [:+, :+]
|
466
|
+
|
467
|
+
def self.joins_one_input_type(trec, targs)
|
468
|
+
return RDL::Globals.types[:top] unless targs.size == 1 ## trivial case, won't be matched
|
469
|
+
case trec
|
470
|
+
when RDL::Type::SingletonType
|
471
|
+
base_klass = RDL.type_cast(trec, "RDL::Type::SingletonType<Symbol>").val
|
472
|
+
when RDL::Type::GenericType
|
473
|
+
raise "Unexpected type #{trec}." unless (RDL.type_cast(trec, "RDL::Type::GenericType").base.klass == ActiveRecord_Relation)
|
474
|
+
param = RDL.type_cast(trec, "RDL::Type::GenericType").params[0]
|
475
|
+
case param
|
476
|
+
when RDL::Type::GenericType
|
477
|
+
raise "Unexpected type #{trec}." unless (param.base.klass == JoinTable)
|
478
|
+
base_klass = RDL.type_cast(param.params[0], "RDL::Type::NominalType", force: true).klass
|
479
|
+
when RDL::Type::NominalType
|
480
|
+
base_klass = param.klass
|
481
|
+
else
|
482
|
+
raise "unexpected parameter type in #{trec}"
|
483
|
+
end
|
484
|
+
else
|
485
|
+
raise "unexpected receiver type #{trec}"
|
486
|
+
end
|
487
|
+
arg0 = targs[0]
|
488
|
+
case arg0
|
489
|
+
when RDL::Type::SingletonType
|
490
|
+
sym = RDL.type_cast(arg0, "RDL::Type::SingletonType<Symbol>").val
|
491
|
+
raise RDL::Typecheck::StaticTypeError, "Unexpected arg type #{trec} in call to joins." unless sym.is_a?(Symbol)
|
492
|
+
raise RDL::Typecheck::StaticTypeError, "#{trec} has no association to #{arg0}, cannot perform joins." unless associated_with?(RDL.type_cast(base_klass, "Symbol", force: true), sym)
|
493
|
+
return arg0
|
494
|
+
when RDL::Type::FiniteHashType
|
495
|
+
RDL.type_cast(RDL.type_cast(arg0, "RDL::Type::FiniteHashType").elts, "Hash<Symbol, RDL::Type::Type>", force: true).each { |key, val|
|
496
|
+
raise RDL::Typecheck::StaticTypeError, "Unexpected hash arg type #{arg0} in call to joins." unless key.is_a?(Symbol) && val.is_a?(RDL::Type::SingletonType) && RDL.type_cast(val, "RDL::Type::SingletonType<Object>").val.is_a?(Symbol)
|
497
|
+
val_sym = RDL.type_cast(val, "RDL::Type::SingletonType<Symbol>").val
|
498
|
+
raise RDL::Typecheck::StaticTypeError, "#{trec} has no association to #{key}, cannot perform joins." unless associated_with?(RDL.type_cast(base_klass, "Symbol", force: true), key)
|
499
|
+
key_klass = key.to_s.singularize.camelize
|
500
|
+
raise RDL::Typecheck::StaticTypeError, "#{key} has no association to #{val_sym}, cannot perform joins." unless associated_with?(key_klass, val_sym)
|
501
|
+
}
|
502
|
+
return arg0
|
503
|
+
else
|
504
|
+
raise RDL::Typecheck::StaticTypeError, "Unexpected arg type #{arg0} in call to joins."
|
505
|
+
end
|
506
|
+
end
|
507
|
+
RDL.type DBType, 'self.joins_one_input_type', "(RDL::Type::Type, Array<RDL::Type::Type>) -> RDL::Type::Type", wrap: false, typecheck: :type_code, effect: [:+, :+]
|
508
|
+
|
509
|
+
def self.joins_multi_input_type(trec, targs)
|
510
|
+
return RDL::Globals.types[:top] unless targs.size > 1 ## trivial case, won't be matched
|
511
|
+
targs.each { |arg|
|
512
|
+
joins_one_input_type(trec, [arg])
|
513
|
+
}
|
514
|
+
return targs[0] ## since this method is called as first argument in type
|
515
|
+
end
|
516
|
+
RDL.type DBType, 'self.joins_multi_input_type', "(RDL::Type::Type, Array<RDL::Type::Type>) -> RDL::Type::Type", wrap: false, typecheck: :type_code, effect: [:+, :+]
|
517
|
+
|
518
|
+
def self.associated_with?(rec, sym)
|
519
|
+
tschema = RDL::Globals.ar_db_schema[rec.to_s.to_sym]
|
520
|
+
raise RDL::Typecheck::StaticTypeError, "No table type for #{rec} found." unless tschema
|
521
|
+
schema = RDL.type_cast(tschema.params[0], "RDL::Type::FiniteHashType", force: true).elts
|
522
|
+
assoc = schema[:__associations]
|
523
|
+
raise RDL::Typecheck::StaticTypeError, "Table #{rec} has no associations, cannot perform joins." unless assoc
|
524
|
+
RDL.type_cast(RDL.type_cast(assoc, "RDL::Type::FiniteHashType").elts, "Hash<Symbol, RDL::Type::Type>", force: true).each { |key, value|
|
525
|
+
case value
|
526
|
+
when RDL::Type::SingletonType
|
527
|
+
return true if RDL.type_cast(value.val, "Object", force: true) == sym ## no need to change any plurality here
|
528
|
+
when RDL::Type::UnionType
|
529
|
+
## for when rec has multiple of the same kind of association
|
530
|
+
value.types.each { |t|
|
531
|
+
raise "Unexpected type #{t}." unless t.is_a?(RDL::Type::SingletonType) && (RDL.type_cast(t, "RDL::Type::SingletonType<Object>").val.class == Symbol)
|
532
|
+
return true if RDL.type_cast(t, "RDL::Type::SingletonType<Symbol>").val == sym
|
533
|
+
}
|
534
|
+
else
|
535
|
+
raise RDL::Typecheck::StaticTypeError, "Unexpected association type #{value}"
|
536
|
+
end
|
537
|
+
}
|
538
|
+
return false
|
539
|
+
end
|
540
|
+
RDL.type DBType, 'self.associated_with?', "(Class or Symbol or String, Symbol) -> %bool", wrap: false, typecheck: :type_code, effect: [:+, :+]
|
541
|
+
|
542
|
+
def self.get_joined_args(targs)
|
543
|
+
arg_types = RDL.type_cast([], "Array<RDL::Type::Type>", force: true)
|
544
|
+
targs.each { |arg|
|
545
|
+
case arg
|
546
|
+
when RDL::Type::SingletonType
|
547
|
+
raise RDL::Typecheck::StaticTypeError, "Unexpected joins arg type #{arg}" unless (RDL.type_cast(arg.val, "Object", force: true).class == Symbol)
|
548
|
+
arg_types = arg_types + [RDL::Type::NominalType.new(RDL.type_cast(arg.val, "Symbol", force: true).to_s.singularize.camelize)]
|
549
|
+
when RDL::Type::FiniteHashType
|
550
|
+
hsh = arg.elts
|
551
|
+
raise 'not supported' unless hsh.size == 1
|
552
|
+
key, val = RDL.type_cast(hsh.first, "[Symbol, RDL::Type::SingletonType<Symbol>]", force: true)
|
553
|
+
val = val.val
|
554
|
+
arg_types = arg_types + [RDL::Type::UnionType.new(RDL::Type::NominalType.new(key.to_s.singularize.camelize), RDL::Type::NominalType.new(val.to_s.singularize.camelize))]
|
555
|
+
else
|
556
|
+
raise "Unexpected arg type #{arg} to joins."
|
557
|
+
end
|
558
|
+
}
|
559
|
+
if arg_types.size > 1
|
560
|
+
return RDL::Type::UnionType.new(*arg_types)
|
561
|
+
elsif arg_types.size == 1
|
562
|
+
return arg_types[0]
|
563
|
+
else
|
564
|
+
raise "oops, didn't expect to get here."
|
565
|
+
end
|
566
|
+
end
|
567
|
+
RDL.type DBType, 'self.get_joined_args', "(Array<RDL::Type::Type>) -> RDL::Type::Type", wrap: false, typecheck: :type_code, effect: [:+, :+]
|
568
|
+
|
569
|
+
def self.joins_output(trec, targs)
|
570
|
+
arg_type = get_joined_args(targs)
|
571
|
+
case trec
|
572
|
+
when RDL::Type::SingletonType
|
573
|
+
joined = arg_type
|
574
|
+
when RDL::Type::GenericType
|
575
|
+
raise "Unexpected type #{trec}." unless (trec.base.klass == ActiveRecord_Relation)
|
576
|
+
param = trec.params[0]
|
577
|
+
case param
|
578
|
+
when RDL::Type::GenericType
|
579
|
+
raise "Unexpected type #{trec}." unless (param.base.klass == JoinTable)
|
580
|
+
joined = RDL::Type::UnionType.new(param.params[1], arg_type)
|
581
|
+
when RDL::Type::NominalType
|
582
|
+
joined = arg_type
|
583
|
+
else
|
584
|
+
raise "unexpected parameter type in #{trec}"
|
585
|
+
end
|
586
|
+
else
|
587
|
+
raise "unexpected type #{trec}"
|
588
|
+
end
|
589
|
+
jt = RDL::Type::GenericType.new(RDL::Type::NominalType.new(JoinTable), rec_to_nominal(trec), joined)
|
590
|
+
ret = RDL::Type::GenericType.new(RDL::Type::NominalType.new(ActiveRecord_Relation), jt)
|
591
|
+
return ret
|
592
|
+
end
|
593
|
+
RDL.type DBType, 'self.joins_output', "(RDL::Type::Type, Array<RDL::Type::Type>) -> RDL::Type::Type", wrap: false, typecheck: :type_code, effect: [:+, :+]
|
594
|
+
|
595
|
+
def self.plus_output_type(trec, targs)
|
596
|
+
typs = RDL.type_cast([], "Array<RDL::Type::Type>", force: true)
|
597
|
+
[trec, targs[0]].each { |t|
|
598
|
+
case t
|
599
|
+
when RDL::Type::GenericType
|
600
|
+
raise "Expected ActiveRecord_Relation." unless t.base.name == "ActiveRecord_Relation"
|
601
|
+
param0 = t.params[0]
|
602
|
+
case param0
|
603
|
+
when RDL::Type::GenericType
|
604
|
+
raise "Unexpected paramter type in #{t}." unless param0.base.name == "JoinTable"
|
605
|
+
typs = typs + [param0.params[0]] ## base of join table
|
606
|
+
typs = typs + [param0.params[1]] ## joined tables
|
607
|
+
when RDL::Type::NominalType
|
608
|
+
typs = typs + [param0]
|
609
|
+
else
|
610
|
+
raise "unexpected paramater type in #{t}"
|
611
|
+
end
|
612
|
+
else
|
613
|
+
raise "unexpected type #{t}"
|
614
|
+
end
|
615
|
+
}
|
616
|
+
RDL::Type::GenericType.new(RDL::Type::NominalType.new(Array), RDL::Type::UnionType.new(*typs))
|
617
|
+
end
|
618
|
+
RDL.type DBType, 'self.plus_output_type', "(RDL::Type::Type, Array<RDL::Type::Type>) -> RDL::Type::GenericType", wrap: false, typecheck: :type_code, effect: [:+, :+]
|
619
|
+
|
620
|
+
def self.count_input(trec, targs)
|
621
|
+
hash_type = rec_to_schema_type(trec, true)## Bug found here. orginally had: rec_to_schema_type(trec, targs).elts
|
622
|
+
typs = RDL.type_cast([], "Array<RDL::Type::Type>", force: true)
|
623
|
+
hash_type.elts.each { |k, v| ## bug here, originally had: hash_type.each { |k, v|
|
624
|
+
if v.is_a?(RDL::Type::FiniteHashType)
|
625
|
+
## will reach this with joined tables, but we're only interested in column names
|
626
|
+
RDL.type_cast(v, 'RDL::Type::FiniteHashType', force: true).elts.each { |k1, v1|
|
627
|
+
typs = typs + [RDL::Type::SingletonType.new(k1)] unless v1.is_a?(RDL::Type::FiniteHashType) ## potentially two dimensions in joined table
|
628
|
+
}
|
629
|
+
else
|
630
|
+
typs = typs + [RDL::Type::SingletonType.new(k)]
|
631
|
+
end
|
632
|
+
}
|
633
|
+
return RDL::Type::OptionalType.new(RDL::Type::UnionType.new(*typs))
|
634
|
+
end
|
635
|
+
RDL.type DBType, 'self.count_input', "(RDL::Type::Type, Array<RDL::Type::Type>) -> RDL::Type::OptionalType", wrap: false, typecheck: :type_code, effect: [:+, :+]
|
636
|
+
|
637
|
+
end
|