rdl 2.1.0 → 2.2.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 +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
|