rdl 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/rails_types.rb +1 -0
- data/lib/rdl.rb +56 -0
- data/lib/rdl/config.rb +121 -0
- data/lib/rdl/contracts/and.rb +29 -0
- data/lib/rdl/contracts/contract.rb +7 -0
- data/lib/rdl/contracts/flat.rb +31 -0
- data/lib/rdl/contracts/or.rb +25 -0
- data/lib/rdl/contracts/proc.rb +24 -0
- data/lib/rdl/switch.rb +20 -0
- data/lib/rdl/types/annotated_arg.rb +41 -0
- data/lib/rdl/types/finitehash.rb +81 -0
- data/lib/rdl/types/generic.rb +100 -0
- data/lib/rdl/types/intersection.rb +66 -0
- data/lib/rdl/types/lexer.rex +39 -0
- data/lib/rdl/types/lexer.rex.rb +148 -0
- data/lib/rdl/types/method.rb +219 -0
- data/lib/rdl/types/nil.rb +50 -0
- data/lib/rdl/types/nominal.rb +80 -0
- data/lib/rdl/types/optional.rb +54 -0
- data/lib/rdl/types/parser.racc +150 -0
- data/lib/rdl/types/parser.tab.rb +654 -0
- data/lib/rdl/types/singleton.rb +62 -0
- data/lib/rdl/types/structural.rb +72 -0
- data/lib/rdl/types/top.rb +50 -0
- data/lib/rdl/types/tuple.rb +61 -0
- data/lib/rdl/types/type.rb +24 -0
- data/lib/rdl/types/type_inferencer.rb +73 -0
- data/lib/rdl/types/union.rb +74 -0
- data/lib/rdl/types/var.rb +51 -0
- data/lib/rdl/types/vararg.rb +54 -0
- data/lib/rdl/types/wild.rb +26 -0
- data/lib/rdl/util.rb +56 -0
- data/lib/rdl/wrap.rb +505 -0
- data/lib/rdl_types.rb +2 -0
- data/types/other/chronic.rb +5 -0
- data/types/other/paperclip_attachment.rb +7 -0
- data/types/other/securerandom.rb +4 -0
- data/types/rails-4.2.1/fixnum.rb +3 -0
- data/types/rails-4.2.1/string.rb +3 -0
- data/types/rails-tmp/action_dispatch.rb +406 -0
- data/types/rails-tmp/active_record.rb +406 -0
- data/types/rails-tmp/devise_contracts.rb +216 -0
- data/types/ruby-2.2.0/_aliases.rb +4 -0
- data/types/ruby-2.2.0/abbrev.rb +5 -0
- data/types/ruby-2.2.0/array.rb +137 -0
- data/types/ruby-2.2.0/base64.rb +10 -0
- data/types/ruby-2.2.0/basic_object.rb +13 -0
- data/types/ruby-2.2.0/benchmark.rb +11 -0
- data/types/ruby-2.2.0/bigdecimal.rb +15 -0
- data/types/ruby-2.2.0/bigmath.rb +12 -0
- data/types/ruby-2.2.0/class.rb +17 -0
- data/types/ruby-2.2.0/complex.rb +42 -0
- data/types/ruby-2.2.0/coverage.rb +6 -0
- data/types/ruby-2.2.0/csv.rb +5 -0
- data/types/ruby-2.2.0/date.rb +6 -0
- data/types/ruby-2.2.0/dir.rb +38 -0
- data/types/ruby-2.2.0/encoding.rb +23 -0
- data/types/ruby-2.2.0/enumerable.rb +98 -0
- data/types/ruby-2.2.0/enumerator.rb +26 -0
- data/types/ruby-2.2.0/exception.rb +15 -0
- data/types/ruby-2.2.0/file.rb +124 -0
- data/types/ruby-2.2.0/fileutils.rb +6 -0
- data/types/ruby-2.2.0/fixnum.rb +45 -0
- data/types/ruby-2.2.0/float.rb +54 -0
- data/types/ruby-2.2.0/gem.rb +245 -0
- data/types/ruby-2.2.0/hash.rb +72 -0
- data/types/ruby-2.2.0/integer.rb +31 -0
- data/types/ruby-2.2.0/io.rb +103 -0
- data/types/ruby-2.2.0/kernel.rb +89 -0
- data/types/ruby-2.2.0/marshal.rb +5 -0
- data/types/ruby-2.2.0/matchdata.rb +26 -0
- data/types/ruby-2.2.0/math.rb +53 -0
- data/types/ruby-2.2.0/numeric.rb +46 -0
- data/types/ruby-2.2.0/object.rb +73 -0
- data/types/ruby-2.2.0/pathname.rb +106 -0
- data/types/ruby-2.2.0/process.rb +127 -0
- data/types/ruby-2.2.0/random.rb +15 -0
- data/types/ruby-2.2.0/range.rb +38 -0
- data/types/ruby-2.2.0/rational.rb +31 -0
- data/types/ruby-2.2.0/regexp.rb +30 -0
- data/types/ruby-2.2.0/set.rb +58 -0
- data/types/ruby-2.2.0/string.rb +145 -0
- data/types/ruby-2.2.0/strscan.rb +7 -0
- data/types/ruby-2.2.0/symbol.rb +29 -0
- data/types/ruby-2.2.0/time.rb +68 -0
- data/types/ruby-2.2.0/uri.rb +18 -0
- data/types/ruby-2.2.0/yaml.rb +5 -0
- metadata +131 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e35c7df4fa4c7c7852d44fb9b82226bc58f75feb
|
4
|
+
data.tar.gz: c6c470102b2c15ff9df545c7f824fd3a354534f9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4093b03c90a84370d15a225f9b527786b72886bf605674673ce72a4c9fbccac39bb19b64f2b51ba087816a314b65358380f75e1f2274ec20778c5f669fff5201
|
7
|
+
data.tar.gz: 1d31f30746169d02e578e49e39f973f4828c6793f1a77f3cea0b623c7323e84949d94649b6f5d501136519f78238f4092bcfc9ab9f112adc98b571dae1d78020
|
data/lib/rails_types.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_rel "../types/rails-#{Rails::VERSION::STRING}/*.rb"
|
data/lib/rdl.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'delegate'
|
3
|
+
require 'require_all'
|
4
|
+
|
5
|
+
module RDL
|
6
|
+
end
|
7
|
+
|
8
|
+
require_relative 'rdl/config.rb'
|
9
|
+
def RDL.config
|
10
|
+
yield(RDL::Config.instance)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Hash from class name to method name to :pre/:post/:type to array of contracts
|
14
|
+
# class names are strings (because they need to be manipulated in case they include ::)
|
15
|
+
# (class names may have Util.add_singleton_marker applied to them to indicate they're singleton classes.)
|
16
|
+
# method names are symbols
|
17
|
+
$__rdl_contracts = Hash.new
|
18
|
+
|
19
|
+
# Map from full_method_name to number of times called when wrapped
|
20
|
+
$__rdl_wrapped_calls = Hash.new 0
|
21
|
+
|
22
|
+
# Hash from class name to array of symbols that are the class's type parameters
|
23
|
+
$__rdl_type_params = Hash.new
|
24
|
+
|
25
|
+
# Hash from class name to method name to its alias method name
|
26
|
+
# class names are strings
|
27
|
+
# method names are symbols
|
28
|
+
$__rdl_aliases = Hash.new
|
29
|
+
|
30
|
+
# Set of [class, method] pairs to wrap.
|
31
|
+
# class is a string
|
32
|
+
# method is a symbol
|
33
|
+
$__rdl_to_wrap = Set.new
|
34
|
+
|
35
|
+
# List of contracts that should be applied to the next method definition
|
36
|
+
$__rdl_deferred = []
|
37
|
+
|
38
|
+
# Create switches to control whether wrapping happens and whether
|
39
|
+
# contracts are checked. These need to be created before rdl/wrap.rb
|
40
|
+
# is loaded.
|
41
|
+
require_rel 'rdl/switch.rb'
|
42
|
+
$__rdl_wrap_switch = RDL::Switch.new
|
43
|
+
$__rdl_contract_switch = RDL::Switch.new
|
44
|
+
|
45
|
+
require_rel 'rdl/types/*.rb'
|
46
|
+
require_rel 'rdl/contracts/*.rb'
|
47
|
+
require_rel 'rdl/util.rb'
|
48
|
+
require_rel 'rdl/wrap.rb'
|
49
|
+
#require_rel 'rdl/stats.rb'
|
50
|
+
|
51
|
+
$__rdl_parser = RDL::Type::Parser.new
|
52
|
+
|
53
|
+
# Hash from special type names to their values
|
54
|
+
$__rdl_special_types = {'%any' => RDL::Type::TopType.new,
|
55
|
+
'%bool' => RDL::Type::UnionType.new(RDL::Type::NominalType.new(TrueClass),
|
56
|
+
RDL::Type::NominalType.new(FalseClass)) }
|
data/lib/rdl/config.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
class RDL::Config
|
4
|
+
include Singleton
|
5
|
+
|
6
|
+
attr_accessor :nowrap
|
7
|
+
attr_accessor :gather_stats
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@nowrap = Set.new
|
11
|
+
@gather_stats = true
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_nowrap(*klasses)
|
15
|
+
klasses.each { |klass| @nowrap.add klass }
|
16
|
+
end
|
17
|
+
|
18
|
+
def remove_nowrap(*klasses)
|
19
|
+
klasses.each { |klass| @nowrap.delete klass }
|
20
|
+
end
|
21
|
+
|
22
|
+
# To use, copy these 3 lines to the test file of a gem
|
23
|
+
=begin
|
24
|
+
require_relative '../rdl3/rdl/lib/rdl.rb'
|
25
|
+
require_relative '../rdl3/rdl/lib/rdl_types.rb'
|
26
|
+
RDL::Config.instance.profile_stats
|
27
|
+
=end
|
28
|
+
def profile_stats(outname="/#{__FILE__[0...-3]}",outdir="")
|
29
|
+
require 'profile'
|
30
|
+
Profiler__.stop_profile # Leave setup out of stats
|
31
|
+
|
32
|
+
at_exit do
|
33
|
+
Profiler__.stop_profile
|
34
|
+
$__rdl_contract_switch.off {
|
35
|
+
puts "START."
|
36
|
+
puts "Performing Profile Analysis"
|
37
|
+
# Class Name => [Times Contract Called | Times Called | Time | Time | Class Profile]
|
38
|
+
# Implications:
|
39
|
+
# [nil] -> Method exists in object space, but not used
|
40
|
+
# [-1] -> Contract exists for method, but method not profiled
|
41
|
+
# [-1, ...] -> Method profiled, but no contract exists
|
42
|
+
totals = {}
|
43
|
+
|
44
|
+
puts "Retrieving Profiler Data"
|
45
|
+
Profiler__.class_variable_get(:@@maps).values.each do |threadmap|
|
46
|
+
threadmap.each do |key, data|
|
47
|
+
total_data = (totals[key.to_s] ||= [-1, 0, 0.0, 0.0, key])
|
48
|
+
total_data[1] += data[0]
|
49
|
+
total_data[2] += data[1]
|
50
|
+
total_data[3] += data[2]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
puts "Scanning Object Space"
|
55
|
+
kls = []
|
56
|
+
ObjectSpace.each_object { |obj|
|
57
|
+
if kls.include? obj.class then
|
58
|
+
next
|
59
|
+
end
|
60
|
+
kls << obj.class
|
61
|
+
mthds = obj.public_methods(false) + obj.private_methods(false) + obj.protected_methods(false)
|
62
|
+
puts "Class #{obj.class}"
|
63
|
+
mthds.each{ |mthd|
|
64
|
+
puts " :#{mthd}"
|
65
|
+
totals["#{obj.class}::#{mthd.to_s}".gsub('::','#')] ||= [nil] unless mthd.to_s =~ /new/
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
p "Scanning RDL Contract Log"
|
70
|
+
$__rdl_wrapped_calls.each{ |mname,ct|
|
71
|
+
if (totals[mname]) then
|
72
|
+
if (totals[mname][0]) then
|
73
|
+
totals[mname][0] = ct
|
74
|
+
else
|
75
|
+
totals[mname][0] = -1
|
76
|
+
end
|
77
|
+
end
|
78
|
+
}
|
79
|
+
|
80
|
+
puts "Analyzing Statistics"
|
81
|
+
filtered = {}
|
82
|
+
totals.each{ |k,v|
|
83
|
+
if (not (k=~/(rdl)|(RDL)/)) and (v[0].nil? or v[0]==-1) then filtered[k]=v end
|
84
|
+
}
|
85
|
+
|
86
|
+
puts "Writing Output"
|
87
|
+
require 'json'
|
88
|
+
fpath = "#{outdir}/#{outname}_rdlstat.json".gsub('//','')
|
89
|
+
File.open(fpath,'w') do |file|
|
90
|
+
file.puts "POTENTIAL PROBLEMS"
|
91
|
+
filtered.each{ |k,v|
|
92
|
+
begin
|
93
|
+
k =~ /((?:.+\#)*)(.+)/
|
94
|
+
x = $1[0...-1] # Store $1 and $2 before overriden
|
95
|
+
y = $2
|
96
|
+
if (x =~ /\<Class\:/) then
|
97
|
+
puts "Cannot evaluate user-defined class #{x}"
|
98
|
+
else
|
99
|
+
kls = eval x.gsub('#','::')
|
100
|
+
mthds = kls.public_methods(false) + kls.protected_methods(false) # Ignoring private methods
|
101
|
+
if (mthds.include? y.to_sym)
|
102
|
+
file.printf "%-20s %-s", k, v.to_s
|
103
|
+
file.puts ""
|
104
|
+
else
|
105
|
+
puts "Ignoring inheritance problem for #{k}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
rescue
|
109
|
+
end
|
110
|
+
}
|
111
|
+
file.puts " "
|
112
|
+
file.puts "JSON OBJECT"
|
113
|
+
file.puts totals.to_json
|
114
|
+
end
|
115
|
+
puts "DONE."
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
Profiler__.start_profile # Restart profiler after setup
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module RDL::Contract
|
2
|
+
class AndContract < Contract
|
3
|
+
attr_reader :contracts
|
4
|
+
|
5
|
+
def initialize(*contracts)
|
6
|
+
@contracts = contracts
|
7
|
+
end
|
8
|
+
|
9
|
+
# [:slf:] is bound to self when the contracts are checked
|
10
|
+
def check(slf, *v, &blk)
|
11
|
+
AndContract.check_array(@contracts, slf, *v, &blk)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Check an array of contracts a
|
15
|
+
# [:slf:] is bound to self when the contracts are checked
|
16
|
+
def self.check_array(a, slf, *v, &blk)
|
17
|
+
# All contracts must be satisfied
|
18
|
+
a.all? { |c| c.check(slf, *v, &blk) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
AndContract.array_to_s(@contracts)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.array_to_s(a)
|
26
|
+
a.join(' && ')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module RDL::Contract
|
2
|
+
class FlatContract < Contract
|
3
|
+
attr_accessor :desc
|
4
|
+
|
5
|
+
def initialize(desc="No Description", &blk)
|
6
|
+
@pred = blk
|
7
|
+
@desc = desc
|
8
|
+
end
|
9
|
+
|
10
|
+
def check(slf, *v, &blk)
|
11
|
+
$__rdl_contract_switch.off {
|
12
|
+
if (@pred &&
|
13
|
+
((@pred.arity < 0) ? (@pred.arity.abs - 1) <= v.size : @pred.arity == v.size)) then
|
14
|
+
unless blk ? slf.instance_exec(*v, blk, &@pred) : slf.instance_exec(*v, &@pred) # TODO: Fix blk
|
15
|
+
# unless blk ? pred.call(*v, &blk) : pred.call(*v)
|
16
|
+
raise ContractError,
|
17
|
+
"#{v.inspect} does not satisfy #{self.to_s}"
|
18
|
+
end
|
19
|
+
else
|
20
|
+
raise ContractError,
|
21
|
+
"Invalid number of arguments: Expecting #{@pred.arity}, got #{v.size}"
|
22
|
+
end
|
23
|
+
}
|
24
|
+
return true
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
@desc
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RDL::Contract
|
2
|
+
class OrContract < Contract
|
3
|
+
attr_reader :contracts
|
4
|
+
|
5
|
+
def initialize(*contracts)
|
6
|
+
@contracts = contracts
|
7
|
+
end
|
8
|
+
|
9
|
+
def check(slf, *v, &blk)
|
10
|
+
# All contracts must be satisfied
|
11
|
+
@contracts.each { |c|
|
12
|
+
begin
|
13
|
+
c.check(slf, *v, &blk)
|
14
|
+
return true
|
15
|
+
rescue ContractError
|
16
|
+
end
|
17
|
+
}
|
18
|
+
raise ContractError, "#{v.inspect} does not satisfy #{self}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
@contracts.join(' && ')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RDL::Contract
|
2
|
+
class ProcContract < Contract
|
3
|
+
attr_accessor :pre_cond, :post_cond
|
4
|
+
|
5
|
+
def initialize(pre_cond:nil, post_cond:nil)
|
6
|
+
@pre_cond = pre_cond
|
7
|
+
@post_cond = post_cond
|
8
|
+
end
|
9
|
+
|
10
|
+
def wrap(slf, &blk)
|
11
|
+
Proc.new {|*v, &other_blk|
|
12
|
+
@pre_cond.check(slf, *v, &other_blk)
|
13
|
+
tmp = other_blk ? slf.instance_exec(*v, other_blk, &blk) : slf.instance_exec(*v, &blk) # TODO fix blk
|
14
|
+
# tmp = blk.call(*v, &other_blk) # TODO: Instance eval with self
|
15
|
+
@post_cond.check(slf, tmp, *v, &other_blk)
|
16
|
+
tmp
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
"(#{@pre_cond}) -> (#{@post_cond})"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/rdl/switch.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
class RDL::Switch
|
2
|
+
def initialize
|
3
|
+
@switch = true
|
4
|
+
end
|
5
|
+
def off?()
|
6
|
+
return not(@switch)
|
7
|
+
end
|
8
|
+
def off()
|
9
|
+
return unless @switch
|
10
|
+
tmp = @switch
|
11
|
+
@switch = false
|
12
|
+
begin
|
13
|
+
ret = yield
|
14
|
+
ensure
|
15
|
+
@switch = tmp
|
16
|
+
end
|
17
|
+
return ret
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require_relative 'type'
|
2
|
+
|
3
|
+
module RDL::Type
|
4
|
+
class AnnotatedArgType < Type
|
5
|
+
attr_reader :name
|
6
|
+
attr_reader :type
|
7
|
+
|
8
|
+
# Note: Named argument types aren't hashconsed.
|
9
|
+
|
10
|
+
def initialize(name, type)
|
11
|
+
@name = name
|
12
|
+
@type = type
|
13
|
+
raise RuntimeError, "Attempt to create vararg type with non-type" unless type.is_a? Type
|
14
|
+
super()
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
"#{@type.to_s} #{@name}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def eql?(other)
|
22
|
+
self == other
|
23
|
+
end
|
24
|
+
|
25
|
+
def ==(other) # :nodoc:
|
26
|
+
return (other.instance_of? AnnotatedArgType) && (other.name == @name) && (other.type == @type)
|
27
|
+
end
|
28
|
+
|
29
|
+
def hash # :nodoc:
|
30
|
+
return (57 + @name.hash) * @type.hash
|
31
|
+
end
|
32
|
+
|
33
|
+
def member?(obj, *args)
|
34
|
+
@type.member?(obj, *args)
|
35
|
+
end
|
36
|
+
|
37
|
+
def instantiate(inst)
|
38
|
+
return AnnotatedArgType.new(@name, @type.instantiate(inst))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require_relative 'type'
|
2
|
+
|
3
|
+
module RDL::Type
|
4
|
+
# A specialized GenericType for fixed-sized maps from values to
|
5
|
+
# types, for "named" arguments in Ruby. Values are compared with ==
|
6
|
+
# to see if they match.
|
7
|
+
class FiniteHashType < Type
|
8
|
+
attr_reader :elts
|
9
|
+
|
10
|
+
@@cache = {}
|
11
|
+
|
12
|
+
class << self
|
13
|
+
alias :__new__ :new
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.new(elts)
|
17
|
+
t = @@cache[elts]
|
18
|
+
return t if t
|
19
|
+
t = FiniteHashType.__new__(elts)
|
20
|
+
return (@@cache[elts] = t) # assignment evaluates to t
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(elts)
|
24
|
+
elts.each { |k, t|
|
25
|
+
raise RuntimeError, "Got #{t.inspect} where Type expected" unless t.is_a? Type
|
26
|
+
raise RuntimeError, "Type may not be annotated or vararg" if (t.instance_of? AnnotatedArgType) || (t.instance_of? VarargType)
|
27
|
+
}
|
28
|
+
@elts = elts
|
29
|
+
super()
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
"{" + @elts.map { |k, t| k.to_s + ": " + t.to_s }.join(', ') + "}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def eql?(other)
|
37
|
+
self == other
|
38
|
+
end
|
39
|
+
|
40
|
+
def ==(other) # :nodoc:
|
41
|
+
return (other.instance_of? FiniteHashType) && (other.elts == @elts)
|
42
|
+
end
|
43
|
+
|
44
|
+
def <=(other)
|
45
|
+
return true if other.instance_of? TopType
|
46
|
+
return self == other
|
47
|
+
# Subtyping with Hash not allowed
|
48
|
+
# All positions of HashTuple are invariant since tuples are mutable
|
49
|
+
end
|
50
|
+
|
51
|
+
def member?(obj, *args)
|
52
|
+
t = RDL::Util.rdl_type obj
|
53
|
+
return t <= self if t
|
54
|
+
rest = @elts.clone # shallow copy
|
55
|
+
|
56
|
+
return false unless obj.instance_of? Hash
|
57
|
+
|
58
|
+
# Check that every mapping in obj exists in @map and matches the type
|
59
|
+
obj.each_pair { |k, v|
|
60
|
+
return false unless @elts.has_key?(k)
|
61
|
+
t = @elts[k]
|
62
|
+
t = t.type if t.instance_of? OptionalType
|
63
|
+
return false unless t.member?(v)
|
64
|
+
rest.delete(k)
|
65
|
+
}
|
66
|
+
|
67
|
+
# Check that any remaining types are optional
|
68
|
+
rest.each_pair { |k, t|
|
69
|
+
return false unless t.instance_of? OptionalType
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def instantiate(inst)
|
74
|
+
FiniteHashType.new(Hash[@elts.map { |k, t| [k, t.instantiate(inst)] }])
|
75
|
+
end
|
76
|
+
|
77
|
+
def hash
|
78
|
+
h = 229 * @elts.hash
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|