opal 1.1.1 → 1.2.0.beta1
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/.codeclimate.yml +3 -2
- data/.github/ISSUE_TEMPLATE/bug-report.md +47 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.github/workflows/build.yml +11 -5
- data/.gitignore +1 -0
- data/.jshintrc +1 -1
- data/Gemfile +0 -4
- data/HACKING.md +1 -1
- data/README.md +19 -15
- data/UNRELEASED.md +41 -0
- data/benchmark-ips/bm_array_unshift.rb +7 -0
- data/bin/opal-mspec +2 -0
- data/docs/compiler.md +1 -1
- data/examples/rack/Gemfile +0 -1
- data/examples/rack/Gemfile.lock +0 -4
- data/lib/opal/cli.rb +1 -0
- data/lib/opal/cli_options.rb +4 -0
- data/lib/opal/cli_runners/nodejs.rb +4 -0
- data/lib/opal/cli_runners/source-map-support-browser.js +3 -1
- data/lib/opal/cli_runners/source-map-support-node.js +3 -1
- data/lib/opal/cli_runners/source-map-support.js +3 -1
- data/lib/opal/compiler.rb +2 -2
- data/lib/opal/nodes/args/arity_check.rb +1 -0
- data/lib/opal/nodes/args/parameters.rb +6 -0
- data/lib/opal/nodes/class.rb +1 -13
- data/lib/opal/nodes/literal.rb +14 -7
- data/lib/opal/nodes/module.rb +13 -9
- data/lib/opal/nodes/variables.rb +13 -4
- data/lib/opal/nodes/while.rb +54 -17
- data/lib/opal/parser.rb +1 -5
- data/lib/opal/parser/patch.rb +34 -0
- data/lib/opal/repl.rb +7 -0
- data/lib/opal/rewriter.rb +2 -0
- data/lib/opal/rewriters/arguments.rb +4 -1
- data/lib/opal/rewriters/forward_args.rb +54 -0
- data/lib/opal/rewriters/logical_operator_assignment.rb +5 -2
- data/lib/opal/rewriters/opal_engine_check.rb +5 -7
- data/lib/opal/version.rb +1 -1
- data/opal/corelib/array.rb +42 -20
- data/opal/corelib/array/pack.rb +6 -1
- data/opal/corelib/complex.rb +2 -0
- data/opal/corelib/constants.rb +3 -3
- data/opal/corelib/hash.rb +36 -38
- data/opal/corelib/module.rb +2 -7
- data/opal/corelib/number.rb +2 -180
- data/opal/corelib/numeric.rb +156 -0
- data/opal/corelib/object_space.rb +102 -0
- data/opal/corelib/random.rb +31 -66
- data/opal/corelib/random/formatter.rb +122 -0
- data/opal/corelib/range.rb +50 -19
- data/opal/corelib/runtime.js +82 -21
- data/opal/corelib/string.rb +86 -52
- data/opal/corelib/string/encoding.rb +140 -25
- data/opal/corelib/string/unpack.rb +26 -40
- data/opal/opal.rb +1 -0
- data/opal/opal/full.rb +1 -0
- data/spec/filters/bugs/array.rb +0 -22
- data/spec/filters/bugs/basicobject.rb +3 -0
- data/spec/filters/bugs/encoding.rb +0 -2
- data/spec/filters/bugs/exception.rb +1 -0
- data/spec/filters/bugs/float.rb +0 -2
- data/spec/filters/bugs/hash.rb +2 -7
- data/spec/filters/bugs/integer.rb +0 -2
- data/spec/filters/bugs/kernel.rb +16 -3
- data/spec/filters/bugs/language.rb +6 -14
- data/spec/filters/bugs/marshal.rb +1 -3
- data/spec/filters/bugs/module.rb +16 -1
- data/spec/filters/bugs/numeric.rb +4 -12
- data/spec/filters/bugs/objectspace.rb +67 -0
- data/spec/filters/bugs/pack_unpack.rb +0 -9
- data/spec/filters/bugs/pathname.rb +1 -0
- data/spec/filters/bugs/proc.rb +8 -0
- data/spec/filters/bugs/random.rb +3 -6
- data/spec/filters/bugs/range.rb +83 -113
- data/spec/filters/bugs/set.rb +2 -0
- data/spec/filters/bugs/string.rb +31 -70
- data/spec/filters/bugs/struct.rb +2 -0
- data/spec/filters/bugs/time.rb +8 -2
- data/spec/filters/unsupported/float.rb +3 -0
- data/spec/filters/unsupported/freeze.rb +1 -0
- data/spec/filters/unsupported/integer.rb +3 -0
- data/spec/filters/unsupported/refinements.rb +5 -0
- data/spec/filters/unsupported/string.rb +100 -95
- data/spec/filters/unsupported/time.rb +4 -0
- data/spec/lib/compiler_spec.rb +16 -0
- data/spec/lib/rewriters/forward_args_spec.rb +61 -0
- data/spec/lib/rewriters/logical_operator_assignment_spec.rb +1 -1
- data/spec/lib/rewriters/numblocks_spec.rb +44 -0
- data/spec/lib/rewriters/opal_engine_check_spec.rb +49 -4
- data/spec/opal/core/language/forward_args_spec.rb +53 -0
- data/spec/opal/core/language/infinite_range_spec.rb +13 -0
- data/spec/opal/core/language/memoization_spec.rb +16 -0
- data/spec/opal/core/module_spec.rb +38 -2
- data/spec/opal/core/number/to_i_spec.rb +28 -0
- data/spec/opal/core/runtime/bridged_classes_spec.rb +16 -0
- data/spec/opal/core/runtime/constants_spec.rb +20 -1
- data/spec/opal/core/string/subclassing_spec.rb +16 -0
- data/spec/opal/core/string/unpack_spec.rb +22 -0
- data/spec/opal/core/string_spec.rb +4 -4
- data/spec/ruby_specs +4 -1
- data/stdlib/json.rb +3 -1
- data/stdlib/securerandom.rb +55 -35
- data/tasks/testing.rake +6 -3
- data/test/nodejs/test_string.rb +25 -0
- data/vendored-minitest/minitest/assertions.rb +2 -0
- metadata +31 -10
- data/lib/opal/parser/with_c_lexer.rb +0 -15
data/opal/corelib/numeric.rb
CHANGED
|
@@ -145,6 +145,162 @@ class Numeric
|
|
|
145
145
|
to_f.round(digits)
|
|
146
146
|
end
|
|
147
147
|
|
|
148
|
+
def step(limit = undefined, step = undefined, to: undefined, by: undefined, &block)
|
|
149
|
+
%x{
|
|
150
|
+
if (limit !== undefined && to !== undefined) {
|
|
151
|
+
#{raise ArgumentError, 'to is given twice'}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (step !== undefined && by !== undefined) {
|
|
155
|
+
#{raise ArgumentError, 'step is given twice'}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (to !== undefined) {
|
|
159
|
+
limit = to;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (by !== undefined) {
|
|
163
|
+
step = by;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (limit === undefined) {
|
|
167
|
+
limit = nil;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function validateParameters() {
|
|
171
|
+
if (step === nil) {
|
|
172
|
+
#{raise TypeError, 'step must be numeric'}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (step != null && #{step == 0}) {
|
|
176
|
+
#{raise ArgumentError, "step can't be 0"}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (step === nil || step == null) {
|
|
180
|
+
step = 1;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
var sign = #{step <=> 0};
|
|
184
|
+
|
|
185
|
+
if (sign === nil) {
|
|
186
|
+
#{raise ArgumentError, "0 can't be coerced into #{step.class}"}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (limit === nil || limit == null) {
|
|
190
|
+
limit = sign > 0 ? #{Float::INFINITY} : #{-Float::INFINITY};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
#{Opal.compare(self, limit)}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function stepFloatSize() {
|
|
197
|
+
if ((step > 0 && self > limit) || (step < 0 && self < limit)) {
|
|
198
|
+
return 0;
|
|
199
|
+
} else if (step === Infinity || step === -Infinity) {
|
|
200
|
+
return 1;
|
|
201
|
+
} else {
|
|
202
|
+
var abs = Math.abs, floor = Math.floor,
|
|
203
|
+
err = (abs(self) + abs(limit) + abs(limit - self)) / abs(step) * #{Float::EPSILON};
|
|
204
|
+
|
|
205
|
+
if (err === Infinity || err === -Infinity) {
|
|
206
|
+
return 0;
|
|
207
|
+
} else {
|
|
208
|
+
if (err > 0.5) {
|
|
209
|
+
err = 0.5;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return floor((limit - self) / step + err) + 1
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function stepSize() {
|
|
218
|
+
validateParameters();
|
|
219
|
+
|
|
220
|
+
if (step === 0) {
|
|
221
|
+
return Infinity;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (step % 1 !== 0) {
|
|
225
|
+
return stepFloatSize();
|
|
226
|
+
} else if ((step > 0 && self > limit) || (step < 0 && self < limit)) {
|
|
227
|
+
return 0;
|
|
228
|
+
} else {
|
|
229
|
+
var ceil = Math.ceil, abs = Math.abs,
|
|
230
|
+
lhs = abs(self - limit) + 1,
|
|
231
|
+
rhs = abs(step);
|
|
232
|
+
|
|
233
|
+
return ceil(lhs / rhs);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return enum_for(:step, limit, step, &`stepSize`) unless block_given?
|
|
240
|
+
|
|
241
|
+
%x{
|
|
242
|
+
validateParameters();
|
|
243
|
+
|
|
244
|
+
var isDesc = #{step.negative?},
|
|
245
|
+
isInf = #{step == 0} ||
|
|
246
|
+
(limit === Infinity && !isDesc) ||
|
|
247
|
+
(limit === -Infinity && isDesc);
|
|
248
|
+
|
|
249
|
+
if (self.$$is_number && step.$$is_number && limit.$$is_number) {
|
|
250
|
+
if (self % 1 === 0 && (isInf || limit % 1 === 0) && step % 1 === 0) {
|
|
251
|
+
var value = self;
|
|
252
|
+
|
|
253
|
+
if (isInf) {
|
|
254
|
+
for (;; value += step) {
|
|
255
|
+
block(value);
|
|
256
|
+
}
|
|
257
|
+
} else if (isDesc) {
|
|
258
|
+
for (; value >= limit; value += step) {
|
|
259
|
+
block(value);
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
for (; value <= limit; value += step) {
|
|
263
|
+
block(value);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return self;
|
|
268
|
+
} else {
|
|
269
|
+
var begin = #{to_f}.valueOf();
|
|
270
|
+
step = #{step.to_f}.valueOf();
|
|
271
|
+
limit = #{limit.to_f}.valueOf();
|
|
272
|
+
|
|
273
|
+
var n = stepFloatSize();
|
|
274
|
+
|
|
275
|
+
if (!isFinite(step)) {
|
|
276
|
+
if (n !== 0) block(begin);
|
|
277
|
+
} else if (step === 0) {
|
|
278
|
+
while (true) {
|
|
279
|
+
block(begin);
|
|
280
|
+
}
|
|
281
|
+
} else {
|
|
282
|
+
for (var i = 0; i < n; i++) {
|
|
283
|
+
var d = i * step + self;
|
|
284
|
+
if (step >= 0 ? limit < d : limit > d) {
|
|
285
|
+
d = limit;
|
|
286
|
+
}
|
|
287
|
+
block(d);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return self;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
counter = self
|
|
297
|
+
|
|
298
|
+
while `isDesc ? #{counter >= limit} : #{counter <= limit}`
|
|
299
|
+
yield counter
|
|
300
|
+
counter += step
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
148
304
|
def to_c
|
|
149
305
|
Complex(self, 0)
|
|
150
306
|
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# helpers: respond_to, falsy, truthy
|
|
2
|
+
|
|
3
|
+
module ObjectSpace
|
|
4
|
+
module_function
|
|
5
|
+
|
|
6
|
+
%x{
|
|
7
|
+
var callers = {}, registry, add_caller, delete_callers;
|
|
8
|
+
if (typeof FinalizationRegistry === "function") {
|
|
9
|
+
registry = new FinalizationRegistry(function(id) {
|
|
10
|
+
if (typeof callers[id] !== "undefined") {
|
|
11
|
+
for (var i = 0; i < callers[id].length; i++) {
|
|
12
|
+
#{`callers[id][i]`.call(`id`)};
|
|
13
|
+
}
|
|
14
|
+
delete callers[id];
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
add_caller = function(id, value) {
|
|
18
|
+
callers[id] = callers[id] || [];
|
|
19
|
+
callers[id].push(value);
|
|
20
|
+
}
|
|
21
|
+
delete_callers = function(id) {
|
|
22
|
+
delete callers[id];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
// A weak polyfill for FinalizationRegistry
|
|
27
|
+
registry = {
|
|
28
|
+
register: function(){},
|
|
29
|
+
unregister: function(){}
|
|
30
|
+
};
|
|
31
|
+
add_caller = function(){};
|
|
32
|
+
delete_callers = function(){};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
def define_finalizer(obj, aproc = undefined, &block)
|
|
37
|
+
%x{
|
|
38
|
+
if ($truthy(block)) aproc = block;
|
|
39
|
+
if ($falsy(aproc)) aproc = #{proc};
|
|
40
|
+
if (!$respond_to(aproc, '$call')) {
|
|
41
|
+
#{raise ArgumentError, "Wrong type argument #{aproc.class} (should be callable)"};
|
|
42
|
+
}
|
|
43
|
+
var id = #{obj.__id__};
|
|
44
|
+
add_caller(id, aproc);
|
|
45
|
+
try {
|
|
46
|
+
registry.register(obj, id, obj);
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
delete_callers(id);
|
|
50
|
+
#{raise ArgumentError, "cannot define finalizer for #{obj.class}"};
|
|
51
|
+
}
|
|
52
|
+
return [0, aproc];
|
|
53
|
+
}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def undefine_finalizer(obj)
|
|
57
|
+
%{
|
|
58
|
+
var id = #{obj.__id__};
|
|
59
|
+
registry.unregister(obj);
|
|
60
|
+
delete_callers(id);
|
|
61
|
+
return obj;
|
|
62
|
+
}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class WeakMap
|
|
66
|
+
include Enumerable
|
|
67
|
+
|
|
68
|
+
def initialize
|
|
69
|
+
@weak_map = `new WeakMap()`
|
|
70
|
+
@primitive_map = {}
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def [](p1)
|
|
74
|
+
%x{
|
|
75
|
+
if (typeof p1 !== "function" && typeof p1 !== "object") return #{@primitive_map[p1]};
|
|
76
|
+
return #{@weak_map}.get(p1);
|
|
77
|
+
}
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def []=(p1, p2)
|
|
81
|
+
%x{
|
|
82
|
+
if (typeof p1 !== "function" && typeof p1 !== "object") return #{@primitive_map[p1] = p2};
|
|
83
|
+
return #{@weak_map}.set(p1, p2);
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def include?(p1)
|
|
88
|
+
%x{
|
|
89
|
+
if (typeof p1 !== "function" && typeof p1 !== "object") return #{@primitive_map.key? p1};
|
|
90
|
+
return #{@weak_map}.has(p1);
|
|
91
|
+
}
|
|
92
|
+
end
|
|
93
|
+
alias member? include?
|
|
94
|
+
alias key? include?
|
|
95
|
+
|
|
96
|
+
%i[each each_key each_value each_pair keys values size length].each do |i|
|
|
97
|
+
define_method i do |*|
|
|
98
|
+
raise NotImplementedError, "##{i} can't be implemented on top of JS interfaces"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
data/opal/corelib/random.rb
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
|
+
# helpers: falsy
|
|
2
|
+
|
|
1
3
|
class Random
|
|
2
4
|
attr_reader :seed, :state
|
|
3
5
|
|
|
6
|
+
def self._verify_count(count)
|
|
7
|
+
%x{
|
|
8
|
+
if ($falsy(count)) count = 16;
|
|
9
|
+
if (typeof count !== "number") count = #{`count`.to_int};
|
|
10
|
+
if (count < 0) #{raise ArgumentError, 'negative string size (or size too big)'};
|
|
11
|
+
count = Math.floor(count);
|
|
12
|
+
return count;
|
|
13
|
+
}
|
|
14
|
+
end
|
|
15
|
+
|
|
4
16
|
def initialize(seed = Random.new_seed)
|
|
5
17
|
seed = Opal.coerce_to!(seed, Integer, :to_int)
|
|
6
18
|
@state = seed
|
|
@@ -29,13 +41,7 @@ class Random
|
|
|
29
41
|
end
|
|
30
42
|
|
|
31
43
|
def self.urandom(size)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if size < 0
|
|
35
|
-
raise ArgumentError, 'negative string size (or size too big)'
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
Array.new(size) { rand(255).chr }.join.encode('ASCII-8BIT')
|
|
44
|
+
::SecureRandom.bytes(size)
|
|
39
45
|
end
|
|
40
46
|
|
|
41
47
|
def ==(other)
|
|
@@ -45,74 +51,33 @@ class Random
|
|
|
45
51
|
end
|
|
46
52
|
|
|
47
53
|
def bytes(length)
|
|
48
|
-
length =
|
|
54
|
+
length = Random._verify_count(length)
|
|
49
55
|
|
|
50
56
|
Array.new(length) { rand(255).chr }.join.encode('ASCII-8BIT')
|
|
51
57
|
end
|
|
52
58
|
|
|
59
|
+
def self.bytes(length)
|
|
60
|
+
DEFAULT.bytes(length)
|
|
61
|
+
end
|
|
62
|
+
|
|
53
63
|
def rand(limit = undefined)
|
|
64
|
+
random_number(limit)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Not part of the Ruby interface (use #random_number for portability), but
|
|
68
|
+
# used by Random::Formatter as a shortcut, as for Random interface the float
|
|
69
|
+
# RNG is primary.
|
|
70
|
+
def random_float
|
|
54
71
|
%x{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return Opal.$$rand.rand(self.$rng);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function randomInt() {
|
|
61
|
-
return Math.floor(randomFloat() * limit);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function randomRange() {
|
|
65
|
-
var min = limit.begin,
|
|
66
|
-
max = limit.end;
|
|
67
|
-
|
|
68
|
-
if (min === nil || max === nil) {
|
|
69
|
-
return nil;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
var length = max - min;
|
|
73
|
-
|
|
74
|
-
if (length < 0) {
|
|
75
|
-
return nil;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (length === 0) {
|
|
79
|
-
return min;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (max % 1 === 0 && min % 1 === 0 && !limit.excl) {
|
|
83
|
-
length++;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return self.$rand(length) + min;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (limit == null) {
|
|
90
|
-
return randomFloat();
|
|
91
|
-
} else if (limit.$$is_range) {
|
|
92
|
-
return randomRange();
|
|
93
|
-
} else if (limit.$$is_number) {
|
|
94
|
-
if (limit <= 0) {
|
|
95
|
-
#{raise ArgumentError, "invalid argument - #{limit}"}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (limit % 1 === 0) {
|
|
99
|
-
// integer
|
|
100
|
-
return randomInt();
|
|
101
|
-
} else {
|
|
102
|
-
return randomFloat() * limit;
|
|
103
|
-
}
|
|
104
|
-
} else {
|
|
105
|
-
limit = #{Opal.coerce_to!(limit, Integer, :to_int)};
|
|
106
|
-
|
|
107
|
-
if (limit <= 0) {
|
|
108
|
-
#{raise ArgumentError, "invalid argument - #{limit}"}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return randomInt();
|
|
112
|
-
}
|
|
72
|
+
self.state++;
|
|
73
|
+
return Opal.$$rand.rand(self.$rng);
|
|
113
74
|
}
|
|
114
75
|
end
|
|
115
76
|
|
|
77
|
+
def self.random_float
|
|
78
|
+
DEFAULT.random_float
|
|
79
|
+
end
|
|
80
|
+
|
|
116
81
|
def self.generator=(generator)
|
|
117
82
|
`Opal.$$rand = #{generator}`
|
|
118
83
|
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
class Random
|
|
2
|
+
module Formatter
|
|
3
|
+
def hex(count = nil)
|
|
4
|
+
count = Random._verify_count(count)
|
|
5
|
+
%x{
|
|
6
|
+
var bytes = #{bytes(count)};
|
|
7
|
+
var out = "";
|
|
8
|
+
for (var i = 0; i < #{count}; i++) {
|
|
9
|
+
out += bytes.charCodeAt(i).toString(16).padStart(2, '0');
|
|
10
|
+
}
|
|
11
|
+
return #{`out`.encode('US-ASCII')};
|
|
12
|
+
}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def random_bytes(count = nil)
|
|
16
|
+
bytes(count)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def base64(count = nil)
|
|
20
|
+
Base64.strict_encode64(random_bytes(count)).encode('US-ASCII')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def urlsafe_base64(count = nil, padding = false)
|
|
24
|
+
Base64.urlsafe_encode64(random_bytes(count), padding).encode('US-ASCII')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def uuid
|
|
28
|
+
str = hex(16).split('')
|
|
29
|
+
str[12] = '4'
|
|
30
|
+
str[16] = `(parseInt(#{str[16]}, 16) & 3 | 8).toString(16)`
|
|
31
|
+
str = [str[0...8], str[8...12], str[12...16], str[16...20], str[20...32]]
|
|
32
|
+
str = str.map(&:join)
|
|
33
|
+
str.join('-')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Implemented in terms of `#bytes` for SecureRandom, but Random overrides this
|
|
37
|
+
# method to implement `#bytes` in terms of `#random_float`. Not part of standard
|
|
38
|
+
# Ruby interface - use random_number for portability.
|
|
39
|
+
def random_float
|
|
40
|
+
bs = bytes(4)
|
|
41
|
+
num = 0
|
|
42
|
+
4.times do |i|
|
|
43
|
+
num <<= 8
|
|
44
|
+
num |= bs[i].ord
|
|
45
|
+
end
|
|
46
|
+
num.abs / 0x7fffffff
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def random_number(limit = undefined)
|
|
50
|
+
%x{
|
|
51
|
+
function randomFloat() {
|
|
52
|
+
return #{random_float};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function randomInt(max) {
|
|
56
|
+
return Math.floor(randomFloat() * max);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function randomRange() {
|
|
60
|
+
var min = limit.begin,
|
|
61
|
+
max = limit.end;
|
|
62
|
+
|
|
63
|
+
if (min === nil || max === nil) {
|
|
64
|
+
return nil;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
var length = max - min;
|
|
68
|
+
|
|
69
|
+
if (length < 0) {
|
|
70
|
+
return nil;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (length === 0) {
|
|
74
|
+
return min;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (max % 1 === 0 && min % 1 === 0 && !limit.excl) {
|
|
78
|
+
length++;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return randomInt(length) + min;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (limit == null) {
|
|
85
|
+
return randomFloat();
|
|
86
|
+
} else if (limit.$$is_range) {
|
|
87
|
+
return randomRange();
|
|
88
|
+
} else if (limit.$$is_number) {
|
|
89
|
+
if (limit <= 0) {
|
|
90
|
+
#{raise ArgumentError, "invalid argument - #{limit}"}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (limit % 1 === 0) {
|
|
94
|
+
// integer
|
|
95
|
+
return randomInt(limit);
|
|
96
|
+
} else {
|
|
97
|
+
return randomFloat() * limit;
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
limit = #{Opal.coerce_to!(limit, Integer, :to_int)};
|
|
101
|
+
|
|
102
|
+
if (limit <= 0) {
|
|
103
|
+
#{raise ArgumentError, "invalid argument - #{limit}"}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return randomInt(limit);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def alphanumeric(count = nil)
|
|
112
|
+
count = Random._verify_count(count)
|
|
113
|
+
map = ['0'..'9', 'a'..'z', 'A'..'Z'].map(&:to_a).flatten
|
|
114
|
+
Array.new(count) do |i|
|
|
115
|
+
map[random_number(map.length)]
|
|
116
|
+
end.join
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
include Random::Formatter
|
|
121
|
+
extend Random::Formatter
|
|
122
|
+
end
|