rust 0.7 → 0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/ruby-rust +3 -0
- data/lib/{rust-csv.rb → rust/core/csv.rb} +23 -1
- data/lib/rust/core/rust.rb +221 -0
- data/lib/rust/core/types/all.rb +4 -0
- data/lib/{rust-core.rb → rust/core/types/dataframe.rb} +159 -331
- data/lib/rust/core/types/datatype.rb +195 -0
- data/lib/rust/core/types/factor.rb +158 -0
- data/lib/rust/core/types/language.rb +199 -0
- data/lib/rust/core/types/list.rb +97 -0
- data/lib/rust/core/types/matrix.rb +155 -0
- data/lib/rust/core/types/s4class.rb +78 -0
- data/lib/rust/core/types/utils.rb +122 -0
- data/lib/rust/core.rb +7 -0
- data/lib/rust/external/robustbase.rb +44 -0
- data/lib/rust/models/all.rb +4 -0
- data/lib/rust/models/anova.rb +77 -0
- data/lib/rust/models/regression.rb +258 -0
- data/lib/rust/plots/all.rb +4 -0
- data/lib/rust/plots/basic-plots.rb +143 -0
- data/lib/{rust-plots.rb → rust/plots/core.rb} +89 -167
- data/lib/rust/plots/distribution-plots.rb +75 -0
- data/lib/rust/stats/all.rb +4 -0
- data/lib/{rust-basics.rb → rust/stats/correlation.rb} +45 -2
- data/lib/{rust-descriptive.rb → rust/stats/descriptive.rb} +52 -3
- data/lib/{rust-effsize.rb → rust/stats/effsize.rb} +28 -13
- data/lib/{rust-probabilities.rb → rust/stats/probabilities.rb} +142 -34
- data/lib/{rust-tests.rb → rust/stats/tests.rb} +178 -92
- data/lib/rust.rb +4 -9
- metadata +32 -13
- data/lib/rust-calls.rb +0 -80
@@ -0,0 +1,195 @@
|
|
1
|
+
require_relative '../rust'
|
2
|
+
|
3
|
+
module Rust
|
4
|
+
|
5
|
+
##
|
6
|
+
# Represents a data-type that can be loaded from and written to R.
|
7
|
+
|
8
|
+
class RustDatatype
|
9
|
+
|
10
|
+
##
|
11
|
+
# Retrieves the given +variable+ from R and transforms it into the appropriate Ruby counterpart.
|
12
|
+
# To infer the type, it uses the class method #can_pull? of all the RustDatatype classes to check the types
|
13
|
+
# that are compatible with the given R variable (type and class). If more than a candidate is available, the one
|
14
|
+
# with maximum #pull_priority is chosen.
|
15
|
+
|
16
|
+
def self.pull_variable(variable, forced_interpreter = nil)
|
17
|
+
r_type = Rust._pull("as.character(typeof(#{variable}))")
|
18
|
+
r_class = Rust._pull("as.character(class(#{variable}))")
|
19
|
+
|
20
|
+
if forced_interpreter
|
21
|
+
raise ArgumentError, "Expected null or class as forced_interpreter" if forced_interpreter && !forced_interpreter.is_a?(Class)
|
22
|
+
raise ArgumentError, "Class #{forced_interpreter} can not handle type #{r_type}, class #{r_class}" unless forced_interpreter.can_pull?(r_type, r_class)
|
23
|
+
|
24
|
+
return forced_interpreter.pull_variable(variable, r_type, r_class)
|
25
|
+
end
|
26
|
+
|
27
|
+
candidates = []
|
28
|
+
ObjectSpace.each_object(Class) do |type|
|
29
|
+
if type < RustDatatype
|
30
|
+
if type.can_pull?(r_type, r_class)
|
31
|
+
candidates << type
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
if candidates.size > 0
|
37
|
+
type = candidates.max_by { |c| c.pull_priority }
|
38
|
+
|
39
|
+
puts "Using #{type} to pull #{variable}" if Rust.debug?
|
40
|
+
return type.pull_variable(variable, r_type, r_class)
|
41
|
+
else
|
42
|
+
if Rust._pull("length(#{variable})") == 0
|
43
|
+
return []
|
44
|
+
else
|
45
|
+
return Rust._pull(variable)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Returns the priority of this type when a #pull_variable operation is performed. Higher priority means that
|
52
|
+
# the type is to be preferred over other candidate types.
|
53
|
+
|
54
|
+
def self.pull_priority
|
55
|
+
0
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Writes the current object in R as +variable_name+.
|
60
|
+
|
61
|
+
def load_in_r_as(variable_name)
|
62
|
+
raise "Loading #{self.class} in R was not implemented"
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# EXPERIMENTAL: Do not use
|
67
|
+
|
68
|
+
def r_mirror_to(other_variable)
|
69
|
+
varname = self.mirrored_R_variable_name
|
70
|
+
|
71
|
+
Rust._eval("#{varname} = #{other_variable}")
|
72
|
+
Rust["#{varname}.hash"] = self.r_hash
|
73
|
+
|
74
|
+
return varname
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# EXPERIMENTAL: Do not use
|
79
|
+
|
80
|
+
def r_mirror
|
81
|
+
varname = self.mirrored_R_variable_name
|
82
|
+
|
83
|
+
if !Rust._pull("exists(\"#{varname}\")") || Rust._pull("#{varname}.hash") != self.r_hash
|
84
|
+
puts "Loading #{varname}" if Rust.debug?
|
85
|
+
Rust[varname] = self
|
86
|
+
Rust["#{varname}.hash"] = self.r_hash
|
87
|
+
else
|
88
|
+
puts "Using cached value for #{varname}" if Rust.debug?
|
89
|
+
end
|
90
|
+
|
91
|
+
return varname
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Returns the hash of the current object.
|
96
|
+
|
97
|
+
def r_hash
|
98
|
+
self.hash.to_s
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
def mirrored_R_variable_name
|
103
|
+
return "rust.mirrored.#{self.object_id}"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
##
|
108
|
+
# The null value in R
|
109
|
+
|
110
|
+
class Null < RustDatatype
|
111
|
+
def self.can_pull?(type, klass)
|
112
|
+
return type == "NULL" && klass == "NULL"
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.pull_variable(variable, type, klass)
|
116
|
+
return nil
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class TrueClass
|
122
|
+
def to_R
|
123
|
+
"TRUE"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class FalseClass
|
128
|
+
def to_R
|
129
|
+
"FALSE"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class Object
|
134
|
+
|
135
|
+
##
|
136
|
+
# Returns a string with the R representation of this object. Raises an exception for unsupported objects.
|
137
|
+
|
138
|
+
def to_R
|
139
|
+
raise TypeError, "Unsupported type for #{self.class}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class NilClass
|
144
|
+
def to_R
|
145
|
+
return "NULL"
|
146
|
+
end
|
147
|
+
|
148
|
+
def load_in_r_as(variable)
|
149
|
+
Rust._eval("#{variable} <- NULL")
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class Numeric
|
154
|
+
def to_R
|
155
|
+
self.inspect
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
class Float
|
160
|
+
def to_R
|
161
|
+
return self.nan? ? "NA" : super
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
class Symbol
|
166
|
+
def to_R
|
167
|
+
return self.to_s.inspect
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
class Array
|
172
|
+
def to_R
|
173
|
+
return "c(#{self.map { |e| e.to_R }.join(",")})"
|
174
|
+
end
|
175
|
+
|
176
|
+
def distribution
|
177
|
+
result = {}
|
178
|
+
self.each do |value|
|
179
|
+
result[value] = result[value].to_i + 1
|
180
|
+
end
|
181
|
+
return result
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
class String
|
186
|
+
def to_R
|
187
|
+
return self.inspect
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
class Range
|
192
|
+
def to_R
|
193
|
+
[range.min, range.max].to_R
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require_relative 'datatype'
|
2
|
+
|
3
|
+
module Rust
|
4
|
+
|
5
|
+
##
|
6
|
+
# Mirror of the factor type in R.
|
7
|
+
|
8
|
+
class Factor < RustDatatype
|
9
|
+
def self.can_pull?(type, klass)
|
10
|
+
return klass == "factor"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.pull_variable(variable, type, klass)
|
14
|
+
levels = Rust["levels(#{variable})"]
|
15
|
+
values = Rust["as.integer(#{variable})"]
|
16
|
+
|
17
|
+
return Factor.new(values, levels)
|
18
|
+
end
|
19
|
+
|
20
|
+
def load_in_r_as(variable_name)
|
21
|
+
Rust['tmp.levels'] = @levels.map { |v| v.to_s }
|
22
|
+
Rust['tmp.values'] = @values
|
23
|
+
|
24
|
+
Rust._eval("#{variable_name} <- factor(tmp.values, labels=tmp.levels)")
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Creates a new factor given an array of numeric +values+ and symbolic +levels+.
|
29
|
+
|
30
|
+
def initialize(values, levels)
|
31
|
+
@levels = levels.map { |v| v.to_sym }
|
32
|
+
@values = values
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Returns the levels of the factor.
|
37
|
+
|
38
|
+
def levels
|
39
|
+
@levels
|
40
|
+
end
|
41
|
+
|
42
|
+
def ==(other)
|
43
|
+
return false unless other.is_a?(Factor)
|
44
|
+
|
45
|
+
return @levels == other.levels && self.to_a == other.to_a
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Returns the value of the +i+-th element in the factor.
|
50
|
+
|
51
|
+
def [](i)
|
52
|
+
FactorValue.new(@values[i], @levels[@values[i] - 1])
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Sets the +value+ of the +i+-th element in the factor. If it is an Integer, the +value+ must be between 1 and
|
57
|
+
# the number of levels of the factor. +value+ can be either a FactorValue or a String/Symbol.
|
58
|
+
|
59
|
+
def []=(i, value)
|
60
|
+
raise "The given value is outside the factor bounds" if value.is_a?(Integer) && (value < 1 || value > @levels.size)
|
61
|
+
|
62
|
+
if value.is_a?(FactorValue)
|
63
|
+
raise "Incompatible factor value, different levels used" unless @levels.include?(value.level) || @levels.index(value.level) + 1 == @value.value
|
64
|
+
value = value.value
|
65
|
+
end
|
66
|
+
|
67
|
+
if value.is_a?(String) || value.is_a?(Symbol)
|
68
|
+
value = value.to_sym
|
69
|
+
raise "Unsupported value #{value}; expected #{@levels.join(", ")}" unless @levels.include?(value)
|
70
|
+
|
71
|
+
value = @levels.index(value) + 1
|
72
|
+
end
|
73
|
+
|
74
|
+
@values[i] = value
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Returns an array of FactorValue for the values in this factor.
|
79
|
+
|
80
|
+
def to_a
|
81
|
+
@values.map { |v| FactorValue.new(v, @levels[v - 1]) }
|
82
|
+
end
|
83
|
+
|
84
|
+
def method_missing(method, *args, &block)
|
85
|
+
raise NoMethodError, "Undefined method #{method} for Factor" if method.to_s.end_with?("!") || method.end_with?("=")
|
86
|
+
|
87
|
+
self.to_a.method(method).call(*args, &block)
|
88
|
+
end
|
89
|
+
|
90
|
+
def to_s
|
91
|
+
self.to_a.to_s
|
92
|
+
end
|
93
|
+
|
94
|
+
def inspect
|
95
|
+
self.to_a.inspect
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
# Represents a single value in a factor.
|
101
|
+
|
102
|
+
class FactorValue
|
103
|
+
|
104
|
+
##
|
105
|
+
# Creates a factor with a given +value+ (numeric) and +level+ (symbolic).
|
106
|
+
|
107
|
+
def initialize(value, level)
|
108
|
+
@value = value
|
109
|
+
@level = level
|
110
|
+
end
|
111
|
+
|
112
|
+
def value
|
113
|
+
@value
|
114
|
+
end
|
115
|
+
|
116
|
+
def level
|
117
|
+
@level
|
118
|
+
end
|
119
|
+
|
120
|
+
def to_i
|
121
|
+
@value
|
122
|
+
end
|
123
|
+
|
124
|
+
def to_sym
|
125
|
+
@level
|
126
|
+
end
|
127
|
+
|
128
|
+
def to_R
|
129
|
+
self.to_i
|
130
|
+
end
|
131
|
+
|
132
|
+
def inspect
|
133
|
+
@level.inspect
|
134
|
+
end
|
135
|
+
|
136
|
+
def ==(other)
|
137
|
+
if other.is_a?(FactorValue)
|
138
|
+
@value == other.value && @level == other.level
|
139
|
+
elsif other.is_a?(Integer)
|
140
|
+
@value == other
|
141
|
+
elsif other.is_a?(Symbol)
|
142
|
+
@level == other
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def hash
|
147
|
+
@value.hash + @level.hash
|
148
|
+
end
|
149
|
+
|
150
|
+
def eql?(other)
|
151
|
+
return self == other
|
152
|
+
end
|
153
|
+
|
154
|
+
def method_missing(method, *args, &block)
|
155
|
+
@level.method(method).call(*args, &block)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
require_relative 'datatype'
|
2
|
+
|
3
|
+
module Rust
|
4
|
+
|
5
|
+
##
|
6
|
+
# Mirror of the formula type in R.
|
7
|
+
|
8
|
+
class Formula < RustDatatype
|
9
|
+
def self.can_pull?(type, klass)
|
10
|
+
return klass == "formula" || (klass.is_a?(Array) && klass.include?("formula"))
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.pull_variable(variable, type, klass)
|
14
|
+
formula_elements = Rust._pull("as.character(#{variable})")
|
15
|
+
|
16
|
+
assert("The number of elements of a formula must be 2 or 3: #{formula_elements} given") { formula_elements.size > 1 && formula_elements.size < 4 }
|
17
|
+
if formula_elements.size == 2
|
18
|
+
return Formula.new(nil, formula_elements[1])
|
19
|
+
elsif formula_elements.size == 3
|
20
|
+
return Formula.new(formula_elements[2], formula_elements[1])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def load_in_r_as(variable_name)
|
25
|
+
Rust._eval("#{variable_name} <- #{self.left_part} ~ #{self.right_part}")
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :left_part
|
29
|
+
attr_reader :right_part
|
30
|
+
|
31
|
+
##
|
32
|
+
# Creates a new formula with a given +left_part+ (optional) and +right_part+ (as strings).
|
33
|
+
|
34
|
+
def initialize(left_part, right_part)
|
35
|
+
raise ArgumentError, "Expected string" if left_part && !left_part.is_a?(String)
|
36
|
+
raise ArgumentError, "Expected string" if !right_part.is_a?(String)
|
37
|
+
|
38
|
+
@left_part = left_part || ""
|
39
|
+
@right_part = right_part
|
40
|
+
end
|
41
|
+
|
42
|
+
def ==(oth)
|
43
|
+
return false unless oth.is_a?(Formula)
|
44
|
+
|
45
|
+
return @left_part == oth.left_part && @right_part == oth.right_part
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_R
|
49
|
+
return "#@left_part ~ #@right_part"
|
50
|
+
end
|
51
|
+
|
52
|
+
def inspect
|
53
|
+
return self.to_R.strip
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Mirror of the call type in R.
|
59
|
+
|
60
|
+
class Call < RustDatatype
|
61
|
+
def self.can_pull?(type, klass)
|
62
|
+
return klass == "call"
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.pull_variable(variable, type, klass)
|
66
|
+
return Call.new(Rust["deparse(#{variable})"])
|
67
|
+
end
|
68
|
+
|
69
|
+
def load_in_r_as(variable_name)
|
70
|
+
Rust["call.str"] = @value
|
71
|
+
Rust._eval("#{variable_name} <- str2lang(call.str)")
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Creates a new call with the given +value+ (String).
|
76
|
+
|
77
|
+
def initialize(value)
|
78
|
+
@value = value
|
79
|
+
end
|
80
|
+
|
81
|
+
def value
|
82
|
+
@value
|
83
|
+
end
|
84
|
+
|
85
|
+
def inspect
|
86
|
+
@value
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# Mirror of the environment type in R. Currently not supported.
|
92
|
+
|
93
|
+
class Environment < RustDatatype
|
94
|
+
def self.can_pull?(type, klass)
|
95
|
+
return type == "environment" && klass == "environment"
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.pull_variable(variable, type, klass)
|
99
|
+
warn "Exchanging R environments is not supported!"
|
100
|
+
return Environment.new
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.load_in_r_as(variable)
|
104
|
+
warn "Exchanging R environments is not supported!"
|
105
|
+
Rust._eval("#{variable} <- environment()")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
##
|
110
|
+
# Represents a function call in R. After having set up its name (constructor) and, optionally, its arguments
|
111
|
+
# and options, it can be used the call method to execute it in the R environment.
|
112
|
+
|
113
|
+
class Function
|
114
|
+
attr_reader :name
|
115
|
+
attr_reader :arguments
|
116
|
+
attr_reader :options
|
117
|
+
|
118
|
+
##
|
119
|
+
# Creates a new function with a given +name+.
|
120
|
+
|
121
|
+
def initialize(name)
|
122
|
+
@function = name
|
123
|
+
@arguments = Arguments.new
|
124
|
+
@options = Options.new
|
125
|
+
end
|
126
|
+
|
127
|
+
##
|
128
|
+
# Sets the +options+ (Options type) of the function.
|
129
|
+
|
130
|
+
def options=(options)
|
131
|
+
raise TypeError, "Expected Options" unless options.is_a?(Options)
|
132
|
+
|
133
|
+
@options = options
|
134
|
+
end
|
135
|
+
|
136
|
+
##
|
137
|
+
# Sets the +arguments+ (Arguments type) of the function.
|
138
|
+
|
139
|
+
def arguments=(arguments)
|
140
|
+
raise TypeError, "Expected Arguments" unless options.is_a?(Arguments)
|
141
|
+
|
142
|
+
@arguments = arguments
|
143
|
+
end
|
144
|
+
|
145
|
+
def to_R
|
146
|
+
params = [@arguments.to_R, @options.to_R].select { |v| v != "" }.join(",")
|
147
|
+
return "#@function(#{params})"
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# Calls the function in the R environment.
|
152
|
+
|
153
|
+
def call
|
154
|
+
Rust._eval(self.to_R)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
##
|
159
|
+
# Represents an R variable.
|
160
|
+
|
161
|
+
class Variable
|
162
|
+
##
|
163
|
+
# Creates a variable with the given +name+.
|
164
|
+
|
165
|
+
def initialize(name)
|
166
|
+
@name = name
|
167
|
+
end
|
168
|
+
|
169
|
+
def to_R
|
170
|
+
@name
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
##
|
175
|
+
# Represents the arguments of a function in R. Works as an Array of objects.
|
176
|
+
|
177
|
+
class Arguments < Array
|
178
|
+
def to_R
|
179
|
+
return self.map { |v| v.to_R }.join(", ")
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
##
|
184
|
+
# Represents the options of a function in R. Works as a Hash associating option names to objects.
|
185
|
+
|
186
|
+
class Options < Hash
|
187
|
+
def to_R
|
188
|
+
return self.map { |k, v| "#{k}=#{v.to_R}" }.join(", ")
|
189
|
+
end
|
190
|
+
|
191
|
+
def self.from_hash(hash)
|
192
|
+
options = Options.new
|
193
|
+
hash.each do |key, value|
|
194
|
+
options[key.to_s] = value
|
195
|
+
end
|
196
|
+
return options
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require_relative 'datatype'
|
2
|
+
|
3
|
+
module Rust
|
4
|
+
|
5
|
+
##
|
6
|
+
# Mirror of the list type in R.
|
7
|
+
|
8
|
+
class List < RustDatatype
|
9
|
+
def self.can_pull?(type, klass)
|
10
|
+
return type == "list"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.pull_variable(variable, type, klass)
|
14
|
+
return List.new(klass) if Rust._pull("length(#{variable})") == 0
|
15
|
+
|
16
|
+
names = [Rust["names(#{variable})"]].flatten
|
17
|
+
length = Rust["length(#{variable})"]
|
18
|
+
|
19
|
+
list = List.new(klass, names)
|
20
|
+
for i in 0...length
|
21
|
+
list[i] = Rust["#{variable}[[#{i + 1}]]"]
|
22
|
+
end
|
23
|
+
|
24
|
+
return list
|
25
|
+
end
|
26
|
+
|
27
|
+
def load_in_r_as(variable_name)
|
28
|
+
Rust._eval("#{variable_name} <- list()")
|
29
|
+
@data.each do |key, value|
|
30
|
+
Rust["#{variable_name}[[#{key + 1}]]"] = value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Creates an empty list of a given class (+klass+) and the specified +names+.
|
36
|
+
|
37
|
+
def initialize(klass, names = [])
|
38
|
+
@data = {}
|
39
|
+
@names = names
|
40
|
+
@klass = klass
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Returns the elements for the name +key+.
|
45
|
+
|
46
|
+
def [](key)
|
47
|
+
key = get_key(key)
|
48
|
+
|
49
|
+
return @data[key]
|
50
|
+
end
|
51
|
+
alias :| :[]
|
52
|
+
|
53
|
+
##
|
54
|
+
# Sets the +value+ for name +key+.
|
55
|
+
|
56
|
+
def []=(key, value)
|
57
|
+
key = get_key(key)
|
58
|
+
|
59
|
+
return @data[key] = value
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# Returns the names of the list.
|
64
|
+
|
65
|
+
def names
|
66
|
+
@names
|
67
|
+
end
|
68
|
+
|
69
|
+
def inspect
|
70
|
+
result = ""
|
71
|
+
values_inspected = @data.map { |k, v| [k, v.inspect.split("\n").map { |l| " " + l }.join("\n")] }.to_h
|
72
|
+
max_length = [values_inspected.map { |k, v| v.split("\n").map { |line| line.length }.max.to_i }.max.to_i, 100].min
|
73
|
+
|
74
|
+
@data.keys.each do |i|
|
75
|
+
result << "-" * max_length + "\n"
|
76
|
+
result << (@names[i] || "[[#{i}]]") + "\n"
|
77
|
+
result << values_inspected[i] + "\n"
|
78
|
+
end
|
79
|
+
result << "-" * max_length
|
80
|
+
|
81
|
+
return result
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
def get_key(key)
|
86
|
+
if key.is_a?(String)
|
87
|
+
new_key = @names.index(key)
|
88
|
+
raise ArgumentError, "Wrong key: #{key}" unless new_key
|
89
|
+
key = new_key
|
90
|
+
end
|
91
|
+
|
92
|
+
raise ArgumentError, "The key should be either a string or an integer" unless key.is_a?(Integer)
|
93
|
+
|
94
|
+
return key
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|