uringmachine 0.8.2 → 0.10
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/CHANGELOG.md +9 -0
- data/TODO.md +0 -1
- data/ext/um/um.c +49 -3
- data/ext/um/um.h +7 -0
- data/ext/um/um_class.c +6 -0
- data/ext/um/um_const.c +36 -0
- data/ext/um/um_utils.c +4 -0
- data/lib/uringmachine/actor.rb +1 -1
- data/lib/uringmachine/version.rb +1 -1
- data/lib/uringmachine.rb +15 -1
- data/test/test_fiber.rb +23 -3
- data/test/test_um.rb +46 -2
- data/uringmachine.gemspec +0 -2
- metadata +1 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 85de1c3c67da5ef2a64b64a0370312208009683e9a42504bfbd7577bb6bee9f7
|
4
|
+
data.tar.gz: 13a34495c1987c85891ee5db9c196fbf09b6ebcd9a2221026341023e63e5a8e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b10f6a2c6e4d0bccaebb75c50e702ca0126eeb712a25ac32631b21ddcafd550dcceab2028a4f1a9b380538aa19067e0fa08346137a760307193af0acef64ed1
|
7
|
+
data.tar.gz: 0dc3949e9256f139cc1caab14b2de6b63e16af61c569de59b761153d8d05a2030bad0e299ca9044452cc628fd25accff8fe3e89c59e6d4c053f049339ddce2c3
|
data/CHANGELOG.md
CHANGED
data/TODO.md
CHANGED
data/ext/um/um.c
CHANGED
@@ -521,7 +521,7 @@ VALUE um_setsockopt(struct um *machine, int fd, int level, int opt, int value) {
|
|
521
521
|
|
522
522
|
#ifdef HAVE_IO_URING_PREP_CMD_SOCK
|
523
523
|
struct um_op op;
|
524
|
-
um_prep_op(machine, &op,
|
524
|
+
um_prep_op(machine, &op, OP_SETSOCKOPT);
|
525
525
|
struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
526
526
|
io_uring_prep_cmd_sock(sqe, SOCKET_URING_OP_SETSOCKOPT, fd, level, opt, &value, sizeof(value));
|
527
527
|
|
@@ -557,7 +557,7 @@ VALUE um_shutdown(struct um *machine, int fd, int how) {
|
|
557
557
|
|
558
558
|
VALUE um_open(struct um *machine, VALUE pathname, int flags, int mode) {
|
559
559
|
struct um_op op;
|
560
|
-
um_prep_op(machine, &op,
|
560
|
+
um_prep_op(machine, &op, OP_OPEN);
|
561
561
|
struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
562
562
|
io_uring_prep_open(sqe, StringValueCStr(pathname), flags, mode);
|
563
563
|
|
@@ -571,7 +571,7 @@ VALUE um_open(struct um *machine, VALUE pathname, int flags, int mode) {
|
|
571
571
|
|
572
572
|
VALUE um_waitpid(struct um *machine, int pid, int options) {
|
573
573
|
struct um_op op;
|
574
|
-
um_prep_op(machine, &op,
|
574
|
+
um_prep_op(machine, &op, OP_WAITPID);
|
575
575
|
struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
576
576
|
|
577
577
|
siginfo_t infop;
|
@@ -587,6 +587,52 @@ VALUE um_waitpid(struct um *machine, int pid, int options) {
|
|
587
587
|
return rb_ary_new_from_args(2, INT2NUM(infop.si_pid), INT2NUM(infop.si_status));
|
588
588
|
}
|
589
589
|
|
590
|
+
#define hash_set(h, sym, v) rb_hash_aset(h, ID2SYM(rb_intern(sym)), v)
|
591
|
+
|
592
|
+
VALUE statx_to_hash(struct statx *stat) {
|
593
|
+
VALUE hash = rb_hash_new();
|
594
|
+
|
595
|
+
hash_set(hash, "dev", UINT2NUM(stat->stx_dev_major << 8 | stat->stx_dev_minor));
|
596
|
+
hash_set(hash, "rdev", UINT2NUM(stat->stx_rdev_major << 8 | stat->stx_rdev_minor));
|
597
|
+
hash_set(hash, "blksize", UINT2NUM(stat->stx_blksize));
|
598
|
+
hash_set(hash, "attributes", UINT2NUM(stat->stx_attributes));
|
599
|
+
hash_set(hash, "nlink", UINT2NUM(stat->stx_nlink));
|
600
|
+
hash_set(hash, "uid", UINT2NUM(stat->stx_uid));
|
601
|
+
hash_set(hash, "gid", UINT2NUM(stat->stx_gid));
|
602
|
+
hash_set(hash, "mode", UINT2NUM(stat->stx_mode));
|
603
|
+
hash_set(hash, "ino", UINT2NUM(stat->stx_ino));
|
604
|
+
hash_set(hash, "size", UINT2NUM(stat->stx_size));
|
605
|
+
hash_set(hash, "blocks", UINT2NUM(stat->stx_blocks));
|
606
|
+
hash_set(hash, "atime", DBL2NUM(um_timestamp_to_double(stat->stx_atime.tv_sec, stat->stx_atime.tv_nsec)));
|
607
|
+
hash_set(hash, "btime", DBL2NUM(um_timestamp_to_double(stat->stx_btime.tv_sec, stat->stx_btime.tv_nsec)));
|
608
|
+
hash_set(hash, "ctime", DBL2NUM(um_timestamp_to_double(stat->stx_ctime.tv_sec, stat->stx_ctime.tv_nsec)));
|
609
|
+
hash_set(hash, "mtime", DBL2NUM(um_timestamp_to_double(stat->stx_mtime.tv_sec, stat->stx_mtime.tv_nsec)));
|
610
|
+
return hash;
|
611
|
+
}
|
612
|
+
|
613
|
+
VALUE um_statx(struct um *machine, int dirfd, VALUE path, int flags, unsigned int mask) {
|
614
|
+
struct um_op op;
|
615
|
+
um_prep_op(machine, &op, OP_STATX);
|
616
|
+
struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
|
617
|
+
|
618
|
+
struct statx stat;
|
619
|
+
memset(&stat, 0, sizeof(stat));
|
620
|
+
|
621
|
+
if (NIL_P(path))
|
622
|
+
path = rb_str_new_literal("");
|
623
|
+
|
624
|
+
io_uring_prep_statx(sqe, dirfd, StringValueCStr(path), flags, mask, &stat);
|
625
|
+
|
626
|
+
VALUE ret = um_fiber_switch(machine);
|
627
|
+
if (um_check_completion(machine, &op))
|
628
|
+
ret = INT2NUM(op.result.res);
|
629
|
+
|
630
|
+
RB_GC_GUARD(ret);
|
631
|
+
raise_if_exception(ret);
|
632
|
+
|
633
|
+
return statx_to_hash(&stat);
|
634
|
+
}
|
635
|
+
|
590
636
|
/*******************************************************************************
|
591
637
|
multishot ops
|
592
638
|
*******************************************************************************/
|
data/ext/um/um.h
CHANGED
@@ -25,9 +25,12 @@ enum op_kind {
|
|
25
25
|
OP_SCHEDULE,
|
26
26
|
|
27
27
|
OP_SLEEP,
|
28
|
+
OP_OPEN,
|
28
29
|
OP_READ,
|
29
30
|
OP_WRITE,
|
30
31
|
OP_CLOSE,
|
32
|
+
OP_STATX,
|
33
|
+
|
31
34
|
OP_ACCEPT,
|
32
35
|
OP_RECV,
|
33
36
|
OP_SEND,
|
@@ -38,6 +41,8 @@ enum op_kind {
|
|
38
41
|
OP_GETSOCKOPT,
|
39
42
|
OP_SETSOCKOPT,
|
40
43
|
OP_SHUTDOWN,
|
44
|
+
|
45
|
+
OP_WAITPID,
|
41
46
|
|
42
47
|
OP_FUTEX_WAIT,
|
43
48
|
OP_FUTEX_WAKE,
|
@@ -176,6 +181,7 @@ void um_buffer_checkin(struct um *machine, struct um_buffer *buffer);
|
|
176
181
|
void um_free_buffer_linked_list(struct um *machine);
|
177
182
|
|
178
183
|
struct __kernel_timespec um_double_to_timespec(double value);
|
184
|
+
double um_timestamp_to_double(__s64 tv_sec, __u32 tv_nsec);
|
179
185
|
int um_value_is_exception_p(VALUE v);
|
180
186
|
VALUE um_raise_exception(VALUE v);
|
181
187
|
|
@@ -209,6 +215,7 @@ VALUE um_write(struct um *machine, int fd, VALUE str, int len);
|
|
209
215
|
VALUE um_close(struct um *machine, int fd);
|
210
216
|
VALUE um_open(struct um *machine, VALUE pathname, int flags, int mode);
|
211
217
|
VALUE um_waitpid(struct um *machine, int pid, int options);
|
218
|
+
VALUE um_statx(struct um *machine, int dirfd, VALUE path, int flags, unsigned int mask);
|
212
219
|
|
213
220
|
VALUE um_accept(struct um *machine, int fd);
|
214
221
|
VALUE um_accept_each(struct um *machine, int fd);
|
data/ext/um/um_class.c
CHANGED
@@ -130,6 +130,11 @@ VALUE UM_write(int argc, VALUE *argv, VALUE self) {
|
|
130
130
|
return um_write(machine, NUM2INT(fd), buffer, bytes);
|
131
131
|
}
|
132
132
|
|
133
|
+
VALUE UM_statx(VALUE self, VALUE dirfd, VALUE path, VALUE flags, VALUE mask) {
|
134
|
+
struct um *machine = um_get_machine(self);
|
135
|
+
return um_statx(machine, NUM2INT(dirfd), path, NUM2INT(flags), NUM2UINT(mask));
|
136
|
+
}
|
137
|
+
|
133
138
|
VALUE UM_close(VALUE self, VALUE fd) {
|
134
139
|
struct um *machine = um_get_machine(self);
|
135
140
|
return um_close(machine, NUM2INT(fd));
|
@@ -342,6 +347,7 @@ void Init_UM(void) {
|
|
342
347
|
rb_define_method(cUM, "sleep", UM_sleep, 1);
|
343
348
|
rb_define_method(cUM, "periodically", UM_periodically, 1);
|
344
349
|
rb_define_method(cUM, "write", UM_write, -1);
|
350
|
+
rb_define_method(cUM, "statx", UM_statx, 4);
|
345
351
|
|
346
352
|
rb_define_method(cUM, "waitpid", UM_waitpid, 2);
|
347
353
|
|
data/ext/um/um_const.c
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#include "ruby.h"
|
2
2
|
|
3
3
|
#include <fcntl.h>
|
4
|
+
#include <sys/stat.h>
|
4
5
|
#include <sys/wait.h>
|
5
6
|
|
6
7
|
#include <arpa/inet.h>
|
@@ -16,6 +17,41 @@
|
|
16
17
|
#define DEF_CONST_INT(mod, v) rb_define_const(mod, #v, INT2NUM(v))
|
17
18
|
|
18
19
|
void um_define_net_constants(VALUE mod) {
|
20
|
+
DEF_CONST_INT(mod, AT_FDCWD);
|
21
|
+
DEF_CONST_INT(mod, AT_EMPTY_PATH);
|
22
|
+
DEF_CONST_INT(mod, AT_NO_AUTOMOUNT);
|
23
|
+
DEF_CONST_INT(mod, AT_SYMLINK_NOFOLLOW);
|
24
|
+
DEF_CONST_INT(mod, AT_STATX_SYNC_AS_STAT);
|
25
|
+
DEF_CONST_INT(mod, AT_STATX_FORCE_SYNC);
|
26
|
+
DEF_CONST_INT(mod, AT_STATX_DONT_SYNC);
|
27
|
+
|
28
|
+
DEF_CONST_INT(mod, STATX_TYPE);
|
29
|
+
DEF_CONST_INT(mod, STATX_MODE);
|
30
|
+
DEF_CONST_INT(mod, STATX_NLINK);
|
31
|
+
DEF_CONST_INT(mod, STATX_UID);
|
32
|
+
DEF_CONST_INT(mod, STATX_GID);
|
33
|
+
DEF_CONST_INT(mod, STATX_ATIME);
|
34
|
+
DEF_CONST_INT(mod, STATX_MTIME);
|
35
|
+
DEF_CONST_INT(mod, STATX_CTIME);
|
36
|
+
DEF_CONST_INT(mod, STATX_INO);
|
37
|
+
DEF_CONST_INT(mod, STATX_SIZE);
|
38
|
+
DEF_CONST_INT(mod, STATX_BLOCKS);
|
39
|
+
DEF_CONST_INT(mod, STATX_BASIC_STATS);
|
40
|
+
DEF_CONST_INT(mod, STATX_BTIME);
|
41
|
+
DEF_CONST_INT(mod, STATX_ALL);
|
42
|
+
DEF_CONST_INT(mod, STATX_MNT_ID);
|
43
|
+
DEF_CONST_INT(mod, STATX_DIOALIGN);
|
44
|
+
DEF_CONST_INT(mod, STATX_MNT_ID_UNIQUE);
|
45
|
+
#ifdef STATX_SUBVOL
|
46
|
+
DEF_CONST_INT(mod, STATX_SUBVOL);
|
47
|
+
#endif
|
48
|
+
#ifdef STATX_WRITE_ATOMIC
|
49
|
+
DEF_CONST_INT(mod, STATX_WRITE_ATOMIC);
|
50
|
+
#endif
|
51
|
+
#ifdef STATX_DIO_READ_ALIGN
|
52
|
+
DEF_CONST_INT(mod, STATX_DIO_READ_ALIGN);
|
53
|
+
#endif
|
54
|
+
|
19
55
|
DEF_CONST_INT(mod, MSG_CONFIRM);
|
20
56
|
DEF_CONST_INT(mod, MSG_DONTROUTE);
|
21
57
|
DEF_CONST_INT(mod, MSG_DONTWAIT);
|
data/ext/um/um_utils.c
CHANGED
@@ -11,6 +11,10 @@ inline struct __kernel_timespec um_double_to_timespec(double value) {
|
|
11
11
|
return ts;
|
12
12
|
}
|
13
13
|
|
14
|
+
inline double um_timestamp_to_double(__s64 tv_sec, __u32 tv_nsec) {
|
15
|
+
return (double)tv_sec + ((double)tv_nsec) / 1000000000;
|
16
|
+
}
|
17
|
+
|
14
18
|
#define RAISE_EXCEPTION(e) rb_funcall(e, ID_invoke, 0);
|
15
19
|
|
16
20
|
inline int um_value_is_exception_p(VALUE v) {
|
data/lib/uringmachine/actor.rb
CHANGED
data/lib/uringmachine/version.rb
CHANGED
data/lib/uringmachine.rb
CHANGED
@@ -88,9 +88,23 @@ class UringMachine
|
|
88
88
|
def add_done_listener(queue)
|
89
89
|
(@done_listeners ||= []) << queue
|
90
90
|
end
|
91
|
+
|
92
|
+
def mailbox
|
93
|
+
@mailbox ||= UM::Queue.new
|
94
|
+
end
|
91
95
|
end
|
92
96
|
|
93
97
|
class ::Fiber
|
94
|
-
include
|
98
|
+
include FiberExtensions
|
99
|
+
end
|
100
|
+
|
101
|
+
module ThreadExtensions
|
102
|
+
def machine
|
103
|
+
@machine ||= UM.new
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class ::Thread
|
108
|
+
include ThreadExtensions
|
95
109
|
end
|
96
110
|
end
|
data/test/test_fiber.rb
CHANGED
@@ -63,7 +63,7 @@ class FiberTerminateTest < UMBaseTest
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
-
class
|
66
|
+
class FiberJoinTest < UMBaseTest
|
67
67
|
def test_join
|
68
68
|
q = UM::Queue.new
|
69
69
|
x = nil
|
@@ -151,14 +151,14 @@ class ScopeTest < UMBaseTest
|
|
151
151
|
|
152
152
|
t0 = monotonic_clock
|
153
153
|
machine.scope do
|
154
|
-
|
154
|
+
machine.spin do
|
155
155
|
x1 = 1
|
156
156
|
machine.sleep 0.01
|
157
157
|
ensure
|
158
158
|
x1 = 0
|
159
159
|
end
|
160
160
|
|
161
|
-
|
161
|
+
machine.spin do
|
162
162
|
x2 = 1
|
163
163
|
machine.sleep 0.03
|
164
164
|
ensure
|
@@ -172,3 +172,23 @@ class ScopeTest < UMBaseTest
|
|
172
172
|
assert_equal 0, x2
|
173
173
|
end
|
174
174
|
end
|
175
|
+
|
176
|
+
class FiberMailboxTest < UMBaseTest
|
177
|
+
def test_fiber_mailbox
|
178
|
+
m = Fiber.current.mailbox
|
179
|
+
assert_kind_of UM::Queue, m
|
180
|
+
|
181
|
+
m2 = Fiber.current.mailbox
|
182
|
+
assert_equal m, m2
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
class ThreadMachineTest < UMBaseTest
|
187
|
+
def test_thread_machine
|
188
|
+
m = Thread.current.machine
|
189
|
+
assert_kind_of UM, m
|
190
|
+
|
191
|
+
m2 = Thread.current.machine
|
192
|
+
assert_equal m, m2
|
193
|
+
end
|
194
|
+
end
|
data/test/test_um.rb
CHANGED
@@ -29,7 +29,7 @@ end
|
|
29
29
|
|
30
30
|
class SnoozeTest < UMBaseTest
|
31
31
|
def test_snooze_while_sleeping_fiber
|
32
|
-
|
32
|
+
machine.spin do
|
33
33
|
machine.sleep(0.1)
|
34
34
|
end
|
35
35
|
|
@@ -655,7 +655,6 @@ class AcceptEachTest < UMBaseTest
|
|
655
655
|
end
|
656
656
|
|
657
657
|
def test_accept_each_interrupted
|
658
|
-
conns = []
|
659
658
|
count = 0
|
660
659
|
terminated = nil
|
661
660
|
f = @machine.spin do
|
@@ -678,6 +677,8 @@ class AcceptEachTest < UMBaseTest
|
|
678
677
|
|
679
678
|
assert f.done?
|
680
679
|
assert terminated
|
680
|
+
ensure
|
681
|
+
s.close
|
681
682
|
end
|
682
683
|
end
|
683
684
|
|
@@ -1231,3 +1232,46 @@ class WaitTest < UMBaseTest
|
|
1231
1232
|
assert_raises(Errno::ECHILD) { machine.waitpid(1, UM::WEXITED) }
|
1232
1233
|
end
|
1233
1234
|
end
|
1235
|
+
|
1236
|
+
class StatxTest < UMBaseTest
|
1237
|
+
def test_statx
|
1238
|
+
io = File.open(__FILE__, 'r')
|
1239
|
+
ustat = machine.statx(io.fileno, nil, UM::AT_EMPTY_PATH, UM::STATX_ALL)
|
1240
|
+
rstat = File.stat(__FILE__)
|
1241
|
+
|
1242
|
+
assert_equal rstat.dev, ustat[:dev]
|
1243
|
+
assert_equal rstat.ino, ustat[:ino]
|
1244
|
+
assert_equal rstat.mode, ustat[:mode]
|
1245
|
+
assert_equal rstat.nlink, ustat[:nlink]
|
1246
|
+
assert_equal rstat.uid, ustat[:uid]
|
1247
|
+
assert_equal rstat.gid, ustat[:gid]
|
1248
|
+
assert_equal rstat.rdev, ustat[:rdev]
|
1249
|
+
assert_equal rstat.size, ustat[:size]
|
1250
|
+
assert_equal rstat.blksize, ustat[:blksize]
|
1251
|
+
assert_equal rstat.blocks, ustat[:blocks]
|
1252
|
+
assert_equal rstat.atime.to_i, ustat[:atime].to_i
|
1253
|
+
assert_equal rstat.ctime.to_i, ustat[:ctime].to_i
|
1254
|
+
assert_equal rstat.mtime.to_i, ustat[:mtime].to_i
|
1255
|
+
|
1256
|
+
ustat2 = machine.statx(UM::AT_FDCWD, __FILE__, 0, UM::STATX_ALL)
|
1257
|
+
assert_equal rstat.dev, ustat2[:dev]
|
1258
|
+
assert_equal rstat.ino, ustat2[:ino]
|
1259
|
+
assert_equal rstat.mode, ustat2[:mode]
|
1260
|
+
assert_equal rstat.nlink, ustat2[:nlink]
|
1261
|
+
assert_equal rstat.uid, ustat2[:uid]
|
1262
|
+
assert_equal rstat.gid, ustat2[:gid]
|
1263
|
+
assert_equal rstat.rdev, ustat2[:rdev]
|
1264
|
+
assert_equal rstat.size, ustat2[:size]
|
1265
|
+
assert_equal rstat.blksize, ustat2[:blksize]
|
1266
|
+
assert_equal rstat.blocks, ustat2[:blocks]
|
1267
|
+
assert_equal rstat.atime.to_i, ustat2[:atime].to_i
|
1268
|
+
assert_equal rstat.ctime.to_i, ustat2[:ctime].to_i
|
1269
|
+
assert_equal rstat.mtime.to_i, ustat2[:mtime].to_i
|
1270
|
+
ensure
|
1271
|
+
io.close
|
1272
|
+
end
|
1273
|
+
|
1274
|
+
def test_statx_bad_path
|
1275
|
+
assert_raises(Errno::ENOENT) { machine.statx(UM::AT_FDCWD, 'foobar', 0, UM::STATX_ALL) }
|
1276
|
+
end
|
1277
|
+
end
|
data/uringmachine.gemspec
CHANGED
@@ -22,7 +22,5 @@ Gem::Specification.new do |s|
|
|
22
22
|
|
23
23
|
s.add_development_dependency 'rake-compiler', '1.2.9'
|
24
24
|
s.add_development_dependency 'minitest', '5.25.4'
|
25
|
-
s.add_development_dependency 'http_parser.rb', '0.8.0'
|
26
25
|
s.add_development_dependency 'benchmark-ips', '2.14.0'
|
27
|
-
s.add_development_dependency 'localhost', '1.3.1'
|
28
26
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: uringmachine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: '0.10'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
@@ -37,20 +37,6 @@ dependencies:
|
|
37
37
|
- - '='
|
38
38
|
- !ruby/object:Gem::Version
|
39
39
|
version: 5.25.4
|
40
|
-
- !ruby/object:Gem::Dependency
|
41
|
-
name: http_parser.rb
|
42
|
-
requirement: !ruby/object:Gem::Requirement
|
43
|
-
requirements:
|
44
|
-
- - '='
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
version: 0.8.0
|
47
|
-
type: :development
|
48
|
-
prerelease: false
|
49
|
-
version_requirements: !ruby/object:Gem::Requirement
|
50
|
-
requirements:
|
51
|
-
- - '='
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
version: 0.8.0
|
54
40
|
- !ruby/object:Gem::Dependency
|
55
41
|
name: benchmark-ips
|
56
42
|
requirement: !ruby/object:Gem::Requirement
|
@@ -65,20 +51,6 @@ dependencies:
|
|
65
51
|
- - '='
|
66
52
|
- !ruby/object:Gem::Version
|
67
53
|
version: 2.14.0
|
68
|
-
- !ruby/object:Gem::Dependency
|
69
|
-
name: localhost
|
70
|
-
requirement: !ruby/object:Gem::Requirement
|
71
|
-
requirements:
|
72
|
-
- - '='
|
73
|
-
- !ruby/object:Gem::Version
|
74
|
-
version: 1.3.1
|
75
|
-
type: :development
|
76
|
-
prerelease: false
|
77
|
-
version_requirements: !ruby/object:Gem::Requirement
|
78
|
-
requirements:
|
79
|
-
- - '='
|
80
|
-
- !ruby/object:Gem::Version
|
81
|
-
version: 1.3.1
|
82
54
|
email: sharon@noteflakes.com
|
83
55
|
executables: []
|
84
56
|
extensions:
|