Project

General

Profile

Feature #16122 ยป struct_value.patch

zverok (Victor Shepelev), 08/23/2019 05:40 PM

View differences:

struct.c
23 23
const rb_iseq_t *rb_method_for_self_aref(VALUE name, VALUE arg, rb_insn_func_t func);
24 24
const rb_iseq_t *rb_method_for_self_aset(VALUE name, VALUE arg, rb_insn_func_t func);
25 25

  
26
VALUE rb_cStruct;
26
VALUE rb_cStruct, rb_cStructValue;
27 27
static ID id_members, id_back_members, id_keyword_init;
28 28

  
29 29
static VALUE struct_alloc(VALUE);
......
311 311
}
312 312

  
313 313
static VALUE
314
setup_struct(VALUE nstr, VALUE members)
314
setup_struct(VALUE nstr, VALUE members, bool is_immutable)
315 315
{
316 316
    long i, len;
317 317

  
......
319 319

  
320 320
    rb_define_alloc_func(nstr, struct_alloc);
321 321
    rb_define_singleton_method(nstr, "new", rb_class_new_instance, -1);
322
    rb_define_singleton_method(nstr, "[]", rb_class_new_instance, -1);
322
    if(!is_immutable) {
323
        rb_define_singleton_method(nstr, "[]", rb_class_new_instance, -1);
324
    }
323 325
    rb_define_singleton_method(nstr, "members", rb_struct_s_members_m, 0);
324 326
    rb_define_singleton_method(nstr, "inspect", rb_struct_s_inspect, 0);
325 327
    len = RARRAY_LEN(members);
......
334 336
	else {
335 337
            define_aref_method(nstr, sym, off);
336 338
	}
337
	define_aset_method(nstr, ID2SYM(rb_id_attrset(id)), off);
339
        if (!is_immutable) {
340
	       define_aset_method(nstr, ID2SYM(rb_id_attrset(id)), off);
341
        }
338 342
    }
339 343

  
340 344
    return nstr;
......
434 438

  
435 439
    if (!name) st = anonymous_struct(rb_cStruct);
436 440
    else st = new_struct(rb_str_new2(name), rb_cStruct);
437
    return setup_struct(st, ary);
441
    return setup_struct(st, ary, false);
438 442
}
439 443

  
440 444
VALUE
......
447 451
    ary = struct_make_members_list(ar);
448 452
    va_end(ar);
449 453

  
450
    return setup_struct(rb_define_class_under(outer, name, rb_cStruct), ary);
454
    return setup_struct(rb_define_class_under(outer, name, rb_cStruct), ary, false);
451 455
}
452 456

  
453 457
/*
......
565 569
    else {
566 570
	st = new_struct(name, klass);
567 571
    }
568
    setup_struct(st, rest);
572
    setup_struct(st, rest, false);
569 573
    rb_ivar_set(st, id_keyword_init, keyword_init);
570 574
    if (rb_block_given_p()) {
571 575
	rb_mod_module_eval(0, 0, st);
......
826 830
inspect_struct(VALUE s, VALUE dummy, int recur)
827 831
{
828 832
    VALUE cname = rb_class_path(rb_obj_class(s));
829
    VALUE members, str = rb_str_new2("#<struct ");
833
    VALUE members, str;
834
    if (rb_obj_is_kind_of(s, rb_cStructValue) == Qtrue) {
835
       str = rb_str_new2("#<value ");
836
    } else {
837
       str = rb_str_new2("#<struct ");
838
    }
830 839
    long i, len;
831 840
    char first = RSTRING_PTR(cname)[0];
832 841

  
......
1284 1293
    return rb_obj_dig(argc, argv, self, Qnil);
1285 1294
}
1286 1295

  
1296
/*
1297
 *  Document-class: Struct::Value
1298
 *
1299
 *  Struct::Value is a simplified version of Struct, representing
1300
 *  immutable and non-Enumerable value object, useful for producing
1301
 *  code in functional style. Value's definition is performed the
1302
 *  same way as Struct's, but, unlike Struct, Value lacks the
1303
 *  following features:
1304
 *
1305
 *    * It does not include Enumerable module, the only way to
1306
 *      iterate through contents is with #each_pair method;
1307
 *    * It does not provide any way of setting attribute values
1308
 *      since the object was created, object is immutable;
1309
 *    * It does not provide hash-alike methods, like <tt>[]</tt>,
1310
 *      +values_at+ or +values+;
1311
 *    * It does not provide +to_a+ method.
1312
 *
1313
 *  The latter is because objects with +to_a+ methods could be
1314
 *  unpacked unexpectedly, which is usually inconvenient for
1315
 *  value objects.
1316
 *
1317
 *      S = Struct.new(:a, :b)
1318
 *      V = Struct::Value.new(:a, :b)
1319
 *
1320
 *      def test(*args)
1321
 *        p ["Args:", args]
1322
 *      end
1323
 *
1324
 *      test(*S.new(1, 2)) # => ["Args:", [1, 2]]
1325
 *      test(*V.new(1, 2)) # => ["Args:", [#<value V a=1, b=2>]]
1326
 *
1327
 *  The rest of Value's limitations targets making the class'
1328
 *  goal and usage more focused: it is just _a value_, not a
1329
 *  collection, and not a temporary storage for related yet
1330
 *  changing data.
1331
 *
1332
 */
1333

  
1334
/*
1335
 *  call-seq:
1336
 *    Struct::Value.new([class_name] [, member_name]+)                        -> ValueClass
1337
 *    Struct::Value.new([class_name] [, member_name]+, keyword_init: true)    -> ValueClass
1338
 *    Struct::Value.new([class_name] [, member_name]+) {|ValueClass| block }  -> ValueClass
1339
 *    ValueClass.new(value, ...)                                              -> object
1340
 *    ValueClass[value, ...]                                                  -> object
1341
 *
1342
 *  The first two forms are used to create a new Struct::Value subclass +class_name+
1343
 *  that can contain a value for each +member_name+.  This subclass can be
1344
 *  used to create instances of the value like any other Class.
1345
 *
1346
 *  See Struct.new for details of defining subclasses, the logic is exactly
1347
 *  the same for Value.
1348
 *
1349
 */
1350
static VALUE
1351
rb_struct_value_s_def(int argc, VALUE *argv, VALUE klass)
1352
{
1353
    VALUE rest, keyword_init;
1354
    VALUE name = Qnil;
1355
    long i;
1356
    VALUE st;
1357
    st_table *tbl;
1358

  
1359
    rb_check_arity(argc, 0, UNLIMITED_ARGUMENTS);
1360
    if (argc > 0) {
1361
        name = argv[0];
1362
        if (SYMBOL_P(name)) {
1363
            name = Qnil;
1364
        }
1365
        else {
1366
            --argc;
1367
            ++argv;
1368
        }
1369
    }
1370

  
1371
    if (RB_TYPE_P(argv[argc-1], T_HASH)) {
1372
        VALUE kwargs[1];
1373
        static ID keyword_ids[1];
1374

  
1375
        if (!keyword_ids[0]) {
1376
            keyword_ids[0] = rb_intern("keyword_init");
1377
        }
1378
        rb_get_kwargs(argv[argc-1], keyword_ids, 0, 1, kwargs);
1379
        --argc;
1380
        keyword_init = kwargs[0];
1381
    }
1382
    else {
1383
        keyword_init = Qfalse;
1384
    }
1385

  
1386
    rest = rb_ident_hash_new();
1387
    RBASIC_CLEAR_CLASS(rest);
1388
    tbl = RHASH_TBL(rest);
1389
    for (i=0; i<argc; i++) {
1390
        VALUE mem = rb_to_symbol(argv[i]);
1391
        if (st_insert(tbl, mem, Qtrue)) {
1392
            rb_raise(rb_eArgError, "duplicate member: %"PRIsVALUE, mem);
1393
        }
1394
    }
1395
    rest = rb_hash_keys(rest);
1396
    st_clear(tbl);
1397
    RBASIC_CLEAR_CLASS(rest);
1398
    OBJ_FREEZE_RAW(rest);
1399
    if (NIL_P(name)) {
1400
        st = anonymous_struct(klass);
1401
    }
1402
    else {
1403
        st = new_struct(name, klass);
1404
    }
1405
    setup_struct(st, rest, true);
1406
    rb_ivar_set(st, id_keyword_init, keyword_init);
1407
    if (rb_block_given_p()) {
1408
        rb_mod_module_eval(0, 0, st);
1409
    }
1410

  
1411
    return st;
1412
}
1413

  
1414
#if 0 /* for RDoc */
1415
/*
1416
 *  call-seq:
1417
 *     value == other     -> true or false
1418
 *
1419
 *  Equality---Returns +true+ if +other+ has the same value subclass and has
1420
 *  equal member values (according to Object#==).
1421
 *
1422
 *     Customer = Struct::Value.new(:name, :address, :zip)
1423
 *     joe   = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
1424
 *     joejr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
1425
 *     jane  = Customer.new("Jane Doe", "456 Elm, Anytown NC", 12345)
1426
 *     joe == joejr   #=> true
1427
 *     joe == jane    #=> false
1428
 */
1429

  
1430
static VALUE
1431
rb_struct_value_equal(VALUE s, VALUE s2)
1432
{
1433
}
1434

  
1435
/*
1436
 * call-seq:
1437
 *   value.eql?(other)   -> true or false
1438
 *
1439
 * Hash equality---+other+ and +struct+ refer to the same hash key if they
1440
 * have the same value subclass and have equal member values (according to
1441
 * Object#eql?).
1442
 */
1443

  
1444
static VALUE
1445
rb_struct_value_eql(VALUE s, VALUE s2)
1446
{
1447
}
1448

  
1449

  
1450
/*
1451
 * call-seq:
1452
 *   value.hash   -> integer
1453
 *
1454
 * Returns a hash value based on this value's contents.
1455
 *
1456
 * See also Object#hash.
1457
 */
1458

  
1459
static VALUE
1460
rb_struct_value_hash(VALUE s)
1461
{
1462
}
1463

  
1464
/*
1465
 * call-seq:
1466
 *   value.to_s      -> string
1467
 *   value.inspect   -> string
1468
 *
1469
 * Returns a description of this value as a string.
1470
 */
1471

  
1472
static VALUE
1473
rb_struct_value_inspect(VALUE s)
1474
{
1475
}
1476

  
1477
/*
1478
 *  call-seq:
1479
 *     value.to_h                        -> hash
1480
 *     value.to_h {|name, value| block } -> hash
1481
 *
1482
 *  Returns a Hash containing the names and values for the Value's members.
1483
 *
1484
 *  If a block is given, the results of the block on each pair of the receiver
1485
 *  will be used as pairs.
1486
 *
1487
 *     Customer = Struct::Value.new(:name, :address, :zip)
1488
 *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
1489
 *     joe.to_h[:address]   #=> "123 Maple, Anytown NC"
1490
 *     joe.to_h{|name, value| [name.upcase, value.to_s.upcase]}[:ADDRESS]
1491
 *                          #=> "123 MAPLE, ANYTOWN NC"
1492
 */
1493

  
1494
static VALUE
1495
rb_struct_value_to_h(VALUE s)
1496
{
1497
}
1498

  
1499
/*
1500
 *  call-seq:
1501
 *     struct.members    -> array
1502
 *
1503
 *  Returns the value members as an array of symbols:
1504
 *
1505
 *     Customer = Struct::Value.new(:name, :address, :zip)
1506
 *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
1507
 *     joe.members   #=> [:name, :address, :zip]
1508
 */
1509

  
1510
static VALUE
1511
rb_struct_value_members_m(VALUE obj)
1512
{
1513
}
1514

  
1515
/*
1516
 *  call-seq:
1517
 *     value.each_pair {|sym, obj| block }     -> value
1518
 *     value.each_pair                         -> enumerator
1519
 *
1520
 *  Yields the name and value of each struct member in order.  If no block is
1521
 *  given an enumerator is returned.
1522
 *
1523
 *     Customer = Struct::Value.new(:name, :address, :zip)
1524
 *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
1525
 *     joe.each_pair {|name, value| puts("#{name} => #{value}") }
1526
 *
1527
 *  Produces:
1528
 *
1529
 *     name => Joe Smith
1530
 *     address => 123 Maple, Anytown NC
1531
 *     zip => 12345
1532
 */
1533

  
1534
static VALUE
1535
rb_struct_value_each_pair(VALUE s)
1536
{
1537
}
1538

  
1539
/*
1540
 *  call-seq:
1541
 *     value.deconstruct     -> array
1542
 *
1543
 *  Returns the values for usage in a pattern matching:
1544
 *
1545
 *     Customer = Struct::Value.new(:name, :address, :zip)
1546
 *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
1547
 *     case joe
1548
 *     in "Joe Smith", *rest
1549
 *       ....
1550
 */
1551

  
1552
static VALUE
1553
rb_struct_value_deconstruct(VALUE s)
1554
{
1555
}
1556
#endif
1557

  
1287 1558
/*
1288 1559
 *  Document-class: Struct
1289 1560
 *
......
1347 1618
    rb_define_method(rb_cStruct, "dig", rb_struct_dig, -1);
1348 1619

  
1349 1620
    rb_define_method(rb_cStruct, "deconstruct", rb_struct_to_a, 0);
1621

  
1622
    /* Struct::Value */
1623
    rb_cStructValue = rb_define_class_under(rb_cStruct, "Value", rb_cObject);
1624
    rb_undef_alloc_func(rb_cStruct);
1625
    rb_define_singleton_method(rb_cStructValue, "new", rb_struct_value_s_def, -1);
1626

  
1627
#if 0 /* for RDoc */
1628
    rb_define_method(rb_cStructValue, "initialize", rb_struct_value_initialize_m, -1);
1629
    rb_define_method(rb_cStructValue, "initialize_copy", rb_struct_value_init_copy, 1);
1630

  
1631
    rb_define_method(rb_cStructValue, "==", rb_struct_value_equal, 1);
1632
    rb_define_method(rb_cStructValue, "eql?", rb_struct_value_eql, 1);
1633
    rb_define_method(rb_cStructValue, "hash", rb_struct_value_hash, 0);
1634

  
1635
    rb_define_method(rb_cStructValue, "inspect", rb_struct_value_inspect, 0);
1636
    rb_define_alias(rb_cStructValue,  "to_s", "inspect");
1637
    rb_define_method(rb_cStructValue, "to_h", rb_struct_value_to_h, 0);
1638

  
1639
    rb_define_method(rb_cStructValue, "each_pair", rb_struct_value_each_pair, 0);
1640

  
1641
    rb_define_method(rb_cStructValue, "members", rb_struct_value_members_m, 0);
1642

  
1643
    rb_define_method(rb_cStructValue, "deconstruct", rb_struct_value_deconstruct, 0);
1644
#endif
1645

  
1646
    rb_define_method(rb_cStructValue, "initialize", rb_struct_initialize_m, -1);
1647
    rb_define_method(rb_cStructValue, "initialize_copy", rb_struct_init_copy, 1);
1648

  
1649
    rb_define_method(rb_cStructValue, "==", rb_struct_equal, 1);
1650
    rb_define_method(rb_cStructValue, "eql?", rb_struct_eql, 1);
1651
    rb_define_method(rb_cStructValue, "hash", rb_struct_hash, 0);
1652

  
1653
    rb_define_method(rb_cStructValue, "inspect", rb_struct_inspect, 0);
1654
    rb_define_alias(rb_cStructValue,  "to_s", "inspect");
1655
    rb_define_method(rb_cStructValue, "to_h", rb_struct_to_h, 0);
1656

  
1657
    rb_define_method(rb_cStructValue, "each_pair", rb_struct_each_pair, 0);
1658

  
1659
    rb_define_method(rb_cStructValue, "members", rb_struct_members_m, 0);
1660

  
1661
    rb_define_method(rb_cStructValue, "deconstruct", rb_struct_to_a, 0);
1350 1662
}
1351 1663

  
1352 1664
#undef rb_intern
test/ruby/test_struct_value.rb
1
# -*- coding: us-ascii -*-
2
# frozen_string_literal: false
3
require 'test/unit'
4
require 'timeout'
5

  
6
# Note that @Value (test instance variable) is used here to run tests both on
7
# Value and its subclass. Approach is copied from test_struct.rb
8
#
9
# Most of the tests also copied from there, as Value C implementation mostly
10
# reuses appropriate Struct's methods. This is also the reason test doesn't
11
# copy specific bug/very small/very large values tests.
12
module TestStructValue
13
  def test_value_value
14
    value_test = @Value.new("Test", :foo, :bar)
15
    assert_equal(@Value::Test, value_test)
16

  
17
    test = value_test.new(1, 2)
18
    assert_equal(1, test.foo)
19
    assert_equal(2, test.bar)
20
    # FIXME: Better to have hash, but currently deconstruct only supports arrays?..
21
    assert_equal([1, 2], test.deconstruct)
22
  end
23

  
24
  def test_inherit
25
    klass = @Value.new(:a)
26
    klass2 = Class.new(klass)
27
    o = klass2.new(1)
28
    assert_equal(1, o.a)
29
  end
30

  
31
  def test_members
32
    klass = @Value.new(:a)
33
    o = klass.new(1)
34
    assert_equal([:a], klass.members)
35
    assert_equal([:a], o.members)
36
  end
37

  
38
  def test_value_new
39
    assert_raise(NameError) { @Value.new("foo") }
40
    assert_nothing_raised { @Value.new("Foo") }
41
    @Value.instance_eval { remove_const(:Foo) }
42
    assert_nothing_raised { @Value.new(:a) { } }
43
    assert_raise(RuntimeError) { @Value.new(:a) { raise } }
44
  end
45

  
46
  def test_empty_value
47
    val = @Value.new
48
    assert_equal "#<value >", val.new.inspect
49
  end
50

  
51
  def test_value_new_with_keyword_init
52
    @Value.new("KeywordInitTrue", :a, :b, keyword_init: true)
53
    @Value.new("KeywordInitFalse", :a, :b, keyword_init: false)
54

  
55
    assert_raise(ArgumentError) { @Value::KeywordInitTrue.new(1, 2) }
56
    assert_nothing_raised { @Value::KeywordInitFalse.new(1, 2) }
57
    assert_nothing_raised { @Value::KeywordInitTrue.new(a: 1, b: 2) }
58
    assert_raise(ArgumentError) { @Value::KeywordInitTrue.new(1, b: 2) }
59
    assert_raise(ArgumentError) { @Value::KeywordInitTrue.new(a: 1, b: 2, c: 3) }
60
    assert_equal "#{@Value}::KeywordInitFalse", @Value::KeywordInitFalse.inspect
61
    assert_equal "#{@Value}::KeywordInitTrue(keyword_init: true)", @Value::KeywordInitTrue.inspect
62

  
63
    kw = @Value::KeywordInitTrue.new(a: 1, b: 2)
64
    assert_equal(1, kw.a)
65
    assert_equal(2, kw.b)
66

  
67
    @Value.instance_eval do
68
      remove_const(:KeywordInitTrue)
69
      remove_const(:KeywordInitFalse)
70
    end
71
  end
72

  
73
  def test_initialize
74
    klass = @Value.new(:a)
75
    assert_raise(ArgumentError) { klass.new(1, 2) }
76
    klass = @Value.new(:total) do
77
      def initialize(a, b)
78
        super(a+b)
79
      end
80
    end
81
    assert_equal 3, klass.new(1,2).total
82
  end
83

  
84
  def test_each_pair
85
    klass = @Value.new(:a, :b)
86
    o = klass.new(1, 2)
87
    assert_equal([[:a, 1], [:b, 2]], o.each_pair.to_a)
88
  end
89

  
90
  def test_inspect
91
    klass = @Value.new(:a)
92
    o = klass.new(1)
93
    assert_equal("#<value a=1>", o.inspect)
94

  
95
    @Value.new("Foo", :a)
96
    o = @Value::Foo.new(1)
97
    assert_equal("#<value #@Value::Foo a=1>", o.inspect)
98
    @Value.instance_eval { remove_const(:Foo) }
99

  
100
    klass = @Value.new(:a, :b)
101
    o = klass.new(1, 2)
102
    assert_equal("#<value a=1, b=2>", o.inspect)
103

  
104
    klass = @Value.new(:@a)
105
    o = klass.new(1)
106
    assert_equal(1, o.__send__(:@a))
107
    assert_equal("#<value :@a=1>", o.inspect)
108

  
109
    methods = klass.instance_methods(false)
110
    assert_equal([:@a].sort.inspect, methods.sort.inspect)
111
    assert_include(methods, :@a)
112
  end
113

  
114
  def test_init_copy
115
    klass = @Value.new(:a)
116
    o = klass.new(1)
117
    assert_equal(o, o.dup)
118
  end
119

  
120
  def test_equal
121
    klass1 = @Value.new(:a)
122
    klass2 = @Value.new(:a, :b)
123
    o1 = klass1.new(1)
124
    o2 = klass1.new(1)
125
    o3 = klass2.new(1)
126
    assert_equal(o1, o2)
127
    assert_not_equal(o1, o3)
128
  end
129

  
130
  def test_hash
131
    klass = @Value.new(:a)
132
    o = klass.new(1)
133
    assert_kind_of(Integer, o.hash)
134
    assert_kind_of(String, o.hash.to_s)
135
  end
136

  
137
  def test_eql
138
    klass1 = @Value.new(:a)
139
    klass2 = @Value.new(:a, :b)
140
    o1 = klass1.new(1)
141
    o2 = klass1.new(1)
142
    o3 = klass2.new(1)
143
    assert_operator(o1, :eql?, o2)
144
    assert_not_operator(o1, :eql?, o3)
145
  end
146

  
147
  def test_error
148
    assert_raise(TypeError){
149
      @Value.new(0)
150
    }
151
  end
152

  
153
  def test_redefinition_warning
154
    @Value.new("RedefinitionWarning")
155
    e = EnvUtil.verbose_warning do
156
      @Value.new("RedefinitionWarning")
157
    end
158
    assert_match(/redefining constant #@Value::RedefinitionWarning/, e)
159
  end
160

  
161
  def test_nonascii
162
    value_test = @Value.new("R\u{e9}sum\u{e9}", :"r\u{e9}sum\u{e9}")
163
    assert_equal(@Value.const_get("R\u{e9}sum\u{e9}"), value_test, '[ruby-core:24849]')
164
    a = value_test.new(42)
165
    assert_equal("#<value #@Value::R\u{e9}sum\u{e9} r\u{e9}sum\u{e9}=42>", a.inspect, '[ruby-core:24849]')
166
    e = EnvUtil.verbose_warning do
167
      @Value.new("R\u{e9}sum\u{e9}", :"r\u{e9}sum\u{e9}")
168
    end
169
    assert_nothing_raised(Encoding::CompatibilityError) do
170
      assert_match(/redefining constant #@Value::R\u{e9}sum\u{e9}/, e)
171
    end
172
  end
173

  
174
  def test_junk
175
    value_test = @Value.new("Foo", "a\000")
176
    o = value_test.new(1)
177
    assert_equal(1, o.send("a\000"))
178
    @Value.instance_eval { remove_const(:Foo) }
179
  end
180

  
181
  def test_to_h
182
    klass = @Value.new(:a, :b, :c, :d, :e, :f)
183
    o = klass.new(1, 2, 3, 4, 5, 6)
184
    assert_equal({a:1, b:2, c:3, d:4, e:5, f:6}, o.to_h)
185
  end
186

  
187
  def test_to_h_block
188
    klass = @Value.new(:a, :b, :c, :d, :e, :f)
189
    o = klass.new(1, 2, 3, 4, 5, 6)
190
    assert_equal({"a" => 1, "b" => 4, "c" => 9, "d" => 16, "e" => 25, "f" => 36},
191
                 o.to_h {|k, v| [k.to_s, v*v]})
192
  end
193

  
194
  def test_question_mark_in_member
195
    klass = @Value.new(:a, :b?)
196
    x = Object.new
197
    o = klass.new("test", x)
198
    assert_same(x, o.b?)
199
  end
200

  
201
  def test_bang_mark_in_member
202
    klass = @Value.new(:a, :b!)
203
    x = Object.new
204
    o = klass.new("test", x)
205
    assert_same(x, o.b!)
206
  end
207

  
208
  def test_new_dupilicate
209
    assert_raise_with_message(ArgumentError, /duplicate member/) {
210
      @Value.new(:a, :a)
211
    }
212
  end
213

  
214
  class TopStructValue < Test::Unit::TestCase
215
    include TestStructValue
216

  
217
    def initialize(*)
218
      super
219
      @Value = Struct::Value
220
    end
221
  end
222

  
223
  class SubStructValue < Test::Unit::TestCase
224
    include TestStructValue
225
    SubStructValue = Class.new(Struct::Value)
226

  
227
    def initialize(*)
228
      super
229
      @Value = SubStructValue
230
    end
231
  end
232
end