opal 1.1.1 → 1.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +3 -2
  3. data/.github/ISSUE_TEMPLATE/bug-report.md +47 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  5. data/.github/workflows/build.yml +11 -5
  6. data/.gitignore +1 -0
  7. data/.jshintrc +1 -1
  8. data/Gemfile +0 -4
  9. data/HACKING.md +1 -1
  10. data/README.md +19 -15
  11. data/UNRELEASED.md +41 -0
  12. data/benchmark-ips/bm_array_unshift.rb +7 -0
  13. data/bin/opal-mspec +2 -0
  14. data/docs/compiler.md +1 -1
  15. data/examples/rack/Gemfile +0 -1
  16. data/examples/rack/Gemfile.lock +0 -4
  17. data/lib/opal/cli.rb +1 -0
  18. data/lib/opal/cli_options.rb +4 -0
  19. data/lib/opal/cli_runners/nodejs.rb +4 -0
  20. data/lib/opal/cli_runners/source-map-support-browser.js +3 -1
  21. data/lib/opal/cli_runners/source-map-support-node.js +3 -1
  22. data/lib/opal/cli_runners/source-map-support.js +3 -1
  23. data/lib/opal/compiler.rb +2 -2
  24. data/lib/opal/nodes/args/arity_check.rb +1 -0
  25. data/lib/opal/nodes/args/parameters.rb +6 -0
  26. data/lib/opal/nodes/class.rb +1 -13
  27. data/lib/opal/nodes/literal.rb +14 -7
  28. data/lib/opal/nodes/module.rb +13 -9
  29. data/lib/opal/nodes/variables.rb +13 -4
  30. data/lib/opal/nodes/while.rb +54 -17
  31. data/lib/opal/parser.rb +1 -5
  32. data/lib/opal/parser/patch.rb +34 -0
  33. data/lib/opal/repl.rb +7 -0
  34. data/lib/opal/rewriter.rb +2 -0
  35. data/lib/opal/rewriters/arguments.rb +4 -1
  36. data/lib/opal/rewriters/forward_args.rb +54 -0
  37. data/lib/opal/rewriters/logical_operator_assignment.rb +5 -2
  38. data/lib/opal/rewriters/opal_engine_check.rb +5 -7
  39. data/lib/opal/version.rb +1 -1
  40. data/opal/corelib/array.rb +42 -20
  41. data/opal/corelib/array/pack.rb +6 -1
  42. data/opal/corelib/complex.rb +2 -0
  43. data/opal/corelib/constants.rb +3 -3
  44. data/opal/corelib/hash.rb +36 -38
  45. data/opal/corelib/module.rb +2 -7
  46. data/opal/corelib/number.rb +2 -180
  47. data/opal/corelib/numeric.rb +156 -0
  48. data/opal/corelib/object_space.rb +102 -0
  49. data/opal/corelib/random.rb +31 -66
  50. data/opal/corelib/random/formatter.rb +122 -0
  51. data/opal/corelib/range.rb +50 -19
  52. data/opal/corelib/runtime.js +82 -21
  53. data/opal/corelib/string.rb +86 -52
  54. data/opal/corelib/string/encoding.rb +140 -25
  55. data/opal/corelib/string/unpack.rb +26 -40
  56. data/opal/opal.rb +1 -0
  57. data/opal/opal/full.rb +1 -0
  58. data/spec/filters/bugs/array.rb +0 -22
  59. data/spec/filters/bugs/basicobject.rb +3 -0
  60. data/spec/filters/bugs/encoding.rb +0 -2
  61. data/spec/filters/bugs/exception.rb +1 -0
  62. data/spec/filters/bugs/float.rb +0 -2
  63. data/spec/filters/bugs/hash.rb +2 -7
  64. data/spec/filters/bugs/integer.rb +0 -2
  65. data/spec/filters/bugs/kernel.rb +16 -3
  66. data/spec/filters/bugs/language.rb +6 -14
  67. data/spec/filters/bugs/marshal.rb +1 -3
  68. data/spec/filters/bugs/module.rb +16 -1
  69. data/spec/filters/bugs/numeric.rb +4 -12
  70. data/spec/filters/bugs/objectspace.rb +67 -0
  71. data/spec/filters/bugs/pack_unpack.rb +0 -9
  72. data/spec/filters/bugs/pathname.rb +1 -0
  73. data/spec/filters/bugs/proc.rb +8 -0
  74. data/spec/filters/bugs/random.rb +3 -6
  75. data/spec/filters/bugs/range.rb +83 -113
  76. data/spec/filters/bugs/set.rb +2 -0
  77. data/spec/filters/bugs/string.rb +31 -70
  78. data/spec/filters/bugs/struct.rb +2 -0
  79. data/spec/filters/bugs/time.rb +8 -2
  80. data/spec/filters/unsupported/float.rb +3 -0
  81. data/spec/filters/unsupported/freeze.rb +1 -0
  82. data/spec/filters/unsupported/integer.rb +3 -0
  83. data/spec/filters/unsupported/refinements.rb +5 -0
  84. data/spec/filters/unsupported/string.rb +100 -95
  85. data/spec/filters/unsupported/time.rb +4 -0
  86. data/spec/lib/compiler_spec.rb +16 -0
  87. data/spec/lib/rewriters/forward_args_spec.rb +61 -0
  88. data/spec/lib/rewriters/logical_operator_assignment_spec.rb +1 -1
  89. data/spec/lib/rewriters/numblocks_spec.rb +44 -0
  90. data/spec/lib/rewriters/opal_engine_check_spec.rb +49 -4
  91. data/spec/opal/core/language/forward_args_spec.rb +53 -0
  92. data/spec/opal/core/language/infinite_range_spec.rb +13 -0
  93. data/spec/opal/core/language/memoization_spec.rb +16 -0
  94. data/spec/opal/core/module_spec.rb +38 -2
  95. data/spec/opal/core/number/to_i_spec.rb +28 -0
  96. data/spec/opal/core/runtime/bridged_classes_spec.rb +16 -0
  97. data/spec/opal/core/runtime/constants_spec.rb +20 -1
  98. data/spec/opal/core/string/subclassing_spec.rb +16 -0
  99. data/spec/opal/core/string/unpack_spec.rb +22 -0
  100. data/spec/opal/core/string_spec.rb +4 -4
  101. data/spec/ruby_specs +4 -1
  102. data/stdlib/json.rb +3 -1
  103. data/stdlib/securerandom.rb +55 -35
  104. data/tasks/testing.rake +6 -3
  105. data/test/nodejs/test_string.rb +25 -0
  106. data/vendored-minitest/minitest/assertions.rb +2 -0
  107. metadata +31 -10
  108. data/lib/opal/parser/with_c_lexer.rb +0 -15
@@ -123,6 +123,7 @@ opal_unsupported_filter "Time" do
123
123
  fails "Time#utc_offset returns the correct offset for US Eastern time zone around daylight savings time change"
124
124
  fails "Time#utc_offset returns the offset in seconds between the timezone of time and UTC"
125
125
  fails "Time#wday returns an integer representing the day of the week, 0..6, with Sunday being 0"
126
+ fails "Time#yday returns an integer representing the day of the year, 1..366" # Expected 117 == 116
126
127
  fails "Time#year returns the four digit year for a Time with a fixed offset"
127
128
  fails "Time#year returns the four digit year for a local Time as an Integer"
128
129
  fails "Time#zone Encoding.default_internal is set doesn't raise errors for a Time with a fixed offset"
@@ -201,4 +202,7 @@ opal_unsupported_filter "Time" do
201
202
  fails "Time.utc ignores fractional seconds if a passed fractional number of microseconds"
202
203
  fails "Time.utc ignores fractional seconds if a passed whole number of microseconds"
203
204
  fails "Time.utc returns subclass instances"
205
+ fails_badly "Marshal.load for a Time loads the zone" # Seasonal failure
206
+ fails_badly "Time#inspect formats the local time following the pattern 'yyyy-MM-dd HH:mm:ss Z'" # Seasonal failure
207
+ fails_badly "Time#to_s formats the local time following the pattern 'yyyy-MM-dd HH:mm:ss Z'" # Seasonal failure
204
208
  end
@@ -42,6 +42,22 @@ RSpec.describe Opal::Compiler do
42
42
  expect_compiled('"hello #{100}"').to include('"hello "', '100')
43
43
  end
44
44
 
45
+ it "should compile ruby ranges" do
46
+ expect_compiled('1..1').to include('$range(1, 1, false)')
47
+ expect_compiled('1...1').to include('$range(1, 1, true)')
48
+ expect_compiled('..1').to include('$range(nil, 1, false)')
49
+ expect_compiled('...1').to include('$range(nil, 1, true)')
50
+ expect_compiled('1..').to include('$range(1, nil, false)')
51
+ expect_compiled('1...').to include('$range(1, nil, true)')
52
+ # Following return Opal.range.$new instead of $range. Some also miss a space.
53
+ expect_compiled('nil..1').to include('(nil, 1, false)')
54
+ expect_compiled('nil...1').to include('(nil,1, true)')
55
+ expect_compiled('"a"..nil').to include('("a", nil, false)')
56
+ expect_compiled('"a"...nil').to include('("a",nil, true)')
57
+ expect_compiled('..nil').to include('(nil, nil, false)')
58
+ expect_compiled('...nil').to include('(nil,nil, true)')
59
+ end
60
+
45
61
  it "should compile method calls" do
46
62
  expect_compiled("self.inspect").to include("$inspect()")
47
63
  expect_compiled("self.map { |a| a + 10 }").to include("$map")
@@ -0,0 +1,61 @@
1
+ require 'lib/spec_helper'
2
+ require 'support/rewriters_helper'
3
+ require 'opal/rewriters/forward_args'
4
+
5
+ RSpec.describe Opal::Rewriters::ForwardArgs do
6
+ include RewritersHelper
7
+ extend RewritersHelper
8
+
9
+ before(:each) { Opal::Rewriters::ForRewriter.reset_tmp_counter! }
10
+
11
+ correct_names = proc do |ast|
12
+ case ast
13
+ when Opal::AST::Node
14
+ ast.children.map do |i|
15
+ correct_names.(i)
16
+ end.yield_self { |children| s(ast.type, *children) }
17
+ when :fwd_rest
18
+ "$fwd_rest"
19
+ when :fwd_block
20
+ "$fwd_block"
21
+ else
22
+ ast
23
+ end
24
+ end
25
+
26
+ include_examples 'it rewrites source-to-AST', <<~ENDSOURCE, correct_names.(parse(<<~ENDDEST))
27
+ def forward(...)
28
+ other(...)
29
+ end
30
+ ENDSOURCE
31
+ def forward(*fwd_rest, &fwd_block)
32
+ other(*fwd_rest, &fwd_block)
33
+ end
34
+ ENDDEST
35
+
36
+ include_examples 'it rewrites source-to-AST', <<~ENDSOURCE, correct_names.(parse(<<~ENDDEST))
37
+ def forward(first_arg, ...)
38
+ other(first_arg, second_arg, ...)
39
+ other(other_arg, ...)
40
+ other(...)
41
+ end
42
+ ENDSOURCE
43
+ def forward(first_arg, *fwd_rest, &fwd_block)
44
+ other(first_arg, second_arg, *fwd_rest, &fwd_block)
45
+ other(other_arg, *fwd_rest, &fwd_block)
46
+ other(*fwd_rest, &fwd_block)
47
+ end
48
+ ENDDEST
49
+
50
+ # Not supported by the parser (nor by the rewriter which would have to rearrange the arguments)
51
+
52
+ # include_examples 'it rewrites source-to-AST', <<~ENDSOURCE, correct_names.(parse(<<~ENDDEST))
53
+ # def forward(a:, ...)
54
+ # other(...)
55
+ # end
56
+ # ENDSOURCE
57
+ # def forward(*fwd_rest, a:, &fwd_block)
58
+ # other(*fwd_rest, &fwd_block)
59
+ # end
60
+ # ENDDEST
61
+ end
@@ -57,7 +57,7 @@ RSpec.describe Opal::Rewriters::LogicalOperatorAssignment do
57
57
  end
58
58
 
59
59
  context 'class variable' do
60
- include_examples 'it rewrites', '@@a ||= 1', '@@a = @@a || 1'
60
+ include_examples 'it rewrites', '@@a ||= 1', '@@a = defined?(@@a) ? (@@a || 1) : 1'
61
61
  include_examples 'it rewrites', '@@a &&= 1', '@@a = @@a && 1'
62
62
  end
63
63
 
@@ -0,0 +1,44 @@
1
+ require 'lib/spec_helper'
2
+ require 'support/rewriters_helper'
3
+ require 'opal/rewriters/numblocks'
4
+
5
+ RSpec.describe Opal::Rewriters::Numblocks do
6
+ include RewritersHelper
7
+ extend RewritersHelper
8
+
9
+ before(:each) { Opal::Rewriters::ForRewriter.reset_tmp_counter! }
10
+
11
+ correct_names = proc do |ast|
12
+ case ast
13
+ when Opal::AST::Node
14
+ ast.children.map do |i|
15
+ correct_names.(i)
16
+ end.yield_self { |children| s(ast.type, *children) }
17
+ when :arg1 then :_1
18
+ when :arg2 then :_2
19
+ when :arg3 then :_3
20
+ else
21
+ ast
22
+ end
23
+ end
24
+
25
+ include_examples 'it rewrites source-to-AST', <<~ENDSOURCE, correct_names.(parse(<<~ENDDEST))
26
+ proc do
27
+ _1
28
+ end
29
+ ENDSOURCE
30
+ proc do |arg1|
31
+ arg1
32
+ end
33
+ ENDDEST
34
+
35
+ include_examples 'it rewrites source-to-AST', <<~ENDSOURCE, correct_names.(parse(<<~ENDDEST))
36
+ proc do
37
+ _3
38
+ end
39
+ ENDSOURCE
40
+ proc do |arg1, arg2, arg3|
41
+ arg3
42
+ end
43
+ ENDDEST
44
+ end
@@ -30,11 +30,11 @@ RSpec.describe Opal::Rewriters::OpalEngineCheck do
30
30
  s(:send, ruby_const_sexp, :==, opal_str_sexp)
31
31
  end
32
32
 
33
- it 'replaces true branch with s(:nil)' do
33
+ it 'replaces the expression with the true branch' do
34
34
  expect_rewritten(
35
35
  s(:if, check, true_branch, false_branch)
36
36
  ).to eq(
37
- s(:if, check, true_branch, s(:nil))
37
+ true_branch
38
38
  )
39
39
  end
40
40
  end
@@ -58,11 +58,11 @@ RSpec.describe Opal::Rewriters::OpalEngineCheck do
58
58
  s(:send, ruby_const_sexp, :!=, opal_str_sexp)
59
59
  end
60
60
 
61
- it 'replaces true branch with s(:nil)' do
61
+ it 'replaces the expression with the false branch' do
62
62
  expect_rewritten(
63
63
  s(:if, check, true_branch, false_branch)
64
64
  ).to eq(
65
- s(:if, check, s(:nil), false_branch)
65
+ false_branch
66
66
  )
67
67
  end
68
68
  end
@@ -79,6 +79,51 @@ RSpec.describe Opal::Rewriters::OpalEngineCheck do
79
79
  end
80
80
  end
81
81
  end
82
+
83
+ it 'supports nested blocks' do
84
+ expect_rewritten(
85
+ # if true
86
+ # if RUBY_ENGINE == 'opal'
87
+ # if RUBY_ENGINE == 'opal'
88
+ # :a
89
+ # end
90
+ # if RUBY_ENGINE != 'opal'
91
+ # :b
92
+ # end
93
+ # end
94
+ # end
95
+
96
+ s(:if,
97
+ s(:true),
98
+ s(:if,
99
+ s(:send, ruby_const_sexp, :==, opal_str_sexp),
100
+ s(:begin,
101
+ s(:if,
102
+ s(:send, ruby_const_sexp, :==, opal_str_sexp),
103
+ s(:sym, :a)
104
+ ),
105
+ s(:if,
106
+ s(:send, ruby_const_sexp, :!=, opal_str_sexp),
107
+ s(:sym, :b)
108
+ )
109
+ )
110
+ )
111
+ )
112
+ ).to eq(
113
+ # if true
114
+ # :a
115
+ # nil
116
+ # end
117
+
118
+ s(:if,
119
+ s(:true),
120
+ s(:begin,
121
+ s(:sym, :a),
122
+ s(:nil)
123
+ )
124
+ )
125
+ )
126
+ end
82
127
  end
83
128
  end
84
129
  end
@@ -0,0 +1,53 @@
1
+ describe "Forward arguments" do
2
+ it "forwards args, kwargs and blocks" do
3
+ def fwd_t1_pass1(...)
4
+ fwd_t1_pass2(...)
5
+ end
6
+
7
+ def fwd_t1_pass2(*args, **kwargs, &block)
8
+ [args.count, kwargs.count, block_given?]
9
+ end
10
+
11
+ fwd_t1_pass1(1, 2, 3, a: 1, b: 2).should == [3, 2, false]
12
+ fwd_t1_pass1(1, 2, &:itself).should == [2, 0, true]
13
+ fwd_t1_pass1(a: 1, b: 2).should == [0, 2, false]
14
+ end
15
+
16
+ it "supports forwarding with initial arguments (3.0 behavior)" do
17
+ def fwd_t2_pass1(initial, ...)
18
+ fwd_t2_pass2(0, initial + 1, ...)
19
+ end
20
+
21
+ def fwd_t2_pass2(a, b, c)
22
+ a + b + c
23
+ end
24
+
25
+ fwd_t2_pass1(2, 3).should == 6
26
+ error = nil
27
+ begin
28
+ fwd_t2_pass1(2, 3, 4) # Too many arguments passwd to fwd_t2_pass2
29
+ rescue ArgumentError
30
+ error = :ArgumentError
31
+ end
32
+ error.should == :ArgumentError
33
+ end
34
+
35
+ it "supports forwarding to multiple methods at once" do
36
+ def fwd_t3_pass1(...)
37
+ fwd_t3_pass2a(...) + fwd_t3_pass2b(...) + fwd_t3_pass2c(...)
38
+ end
39
+
40
+ def fwd_t3_pass2a(*args)
41
+ -2 * args.count
42
+ end
43
+ def fwd_t3_pass2b(*args)
44
+ 1 * args.count
45
+ end
46
+ def fwd_t3_pass2c(*args)
47
+ 0 * args.count
48
+ end
49
+
50
+ fwd_t3_pass1(0, 0, 0).should == -3
51
+ fwd_t3_pass1(0, 0).should == -2
52
+ end
53
+ end
@@ -0,0 +1,13 @@
1
+ describe "Infinite ranges" do
2
+ it "supports endless ranges" do
3
+ range = (10..)
4
+ range.begin.should == 10
5
+ range.end.should == nil
6
+ end
7
+
8
+ it "supports beginless ranges" do
9
+ range = (..10)
10
+ range.begin.should == nil
11
+ range.end.should == 10
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ describe "memoization" do
2
+ it "memoizes a value with complex internal logic" do
3
+ klass = Class.new do
4
+ def memoized_value(dependency: nil)
5
+ @memoized_value ||= begin
6
+ return nil if dependency.nil?
7
+
8
+ dependency.call
9
+ end
10
+ end
11
+ end
12
+
13
+ expect(klass.new.memoized_value(dependency: proc { :value })).to eq :value
14
+ expect(klass.new.memoized_value).to eq nil
15
+ end
16
+ end
@@ -15,8 +15,30 @@ module ModuleSubclassIncludedSpec
15
15
  M2 = Module2.new
16
16
  end
17
17
 
18
- describe 'Module#included' do
19
- it 'gets called in subclasses (regression for https://github.com/opal/opal/issues/1900)' do
18
+ module ModuleCVarSpec
19
+ module Mod0
20
+ def cvar0; @@cvar; end
21
+ def cvarx0; @@cvarx ||= 5; end
22
+ def cvary0; @@cvary; 0; end
23
+ def cvarz0; @@cvarz = @@cvarz || 5; end
24
+ end
25
+
26
+ module Mod1
27
+ include Mod0
28
+ @@cvar = 10
29
+ def cvar1; @@cvar; end
30
+ def cvar1=(new); @@cvar=new; end
31
+ end
32
+
33
+ module Mod2
34
+ include Mod1
35
+ def cvar2; @@cvar; end
36
+ def cvar2=(new); @@cvar=new; end
37
+ end
38
+ end
39
+
40
+ describe 'Module' do
41
+ it '#included gets called in subclasses (regression for https://github.com/opal/opal/issues/1900)' do
20
42
  $ScratchPad = []
21
43
  klass = Class.new
22
44
  klass.include ::ModuleSubclassIncludedSpec::M0
@@ -24,4 +46,18 @@ describe 'Module#included' do
24
46
  klass.include ::ModuleSubclassIncludedSpec::M2
25
47
  $ScratchPad.should == ['A included', 'B included']
26
48
  end
49
+
50
+ it "can access ancestor's @@cvar" do
51
+ klass = Class.new
52
+ klass.include ::ModuleCVarSpec::Mod2
53
+ klass.new.cvar1.should == 10
54
+ klass.new.cvar2.should == 10
55
+ klass.new.cvar2 = 50
56
+ klass.new.cvar1.should == 50
57
+ klass.new.cvar2.should == 50
58
+ klass.new.cvarx0.should == 5
59
+ klass.new.cvary0.should == 0
60
+ ->{ klass.new.cvarz0 }.should raise_error NameError
61
+ ->{ klass.new.cvar0 }.should raise_error NameError
62
+ end
27
63
  end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Number#to_i' do
4
+ it "should not change huge number" do
5
+ 1504642339053716000000.to_i.should == 1504642339053716000000
6
+ end
7
+
8
+ it "should not change negative huge number" do
9
+ -1504642339053716000000.to_i.should == -1504642339053716000000
10
+ end
11
+
12
+ it "equals Number#truncate(0) with huge number" do
13
+ 1504642339053716000000.to_i.should == 1504642339053716000000.truncate(0)
14
+ end
15
+
16
+ it "should not change Infinity" do
17
+ `Infinity`.to_i.should == `Infinity`
18
+ end
19
+
20
+ it "should not change -Infinity" do
21
+ `-Infinity`.to_i.should == `-Infinity`
22
+ end
23
+
24
+ it "should not change NaN" do
25
+ x = `NaN`.to_i
26
+ `Number.isNaN(x)`.should be_true
27
+ end
28
+ end
@@ -121,3 +121,19 @@ describe 'Bridged classes in different modules' do
121
121
  @bridged.new.some_bridged_method.should == [4, 5, 6]
122
122
  end
123
123
  end
124
+
125
+
126
+ describe "Invalid bridged classes" do
127
+ it "raises a TypeError when trying to extend with non-Class" do
128
+ error_msg = /superclass must be a Class/
129
+ -> { class TestClass < `""`; end }.should raise_error(TypeError, error_msg)
130
+ -> { class TestClass < `3`; end }.should raise_error(TypeError, error_msg)
131
+ -> { class TestClass < `true`; end }.should raise_error(TypeError, error_msg)
132
+ -> { class TestClass < `Math`; end }.should raise_error(TypeError, error_msg)
133
+ -> { class TestClass < `Object.create({})`; end }.should raise_error(TypeError, error_msg)
134
+ -> { class TestClass < `Object.create(null)`; end }.should raise_error(TypeError, error_msg)
135
+ -> { class TestClass < Module.new; end }.should raise_error(TypeError, error_msg)
136
+ -> { class TestClass < BasicObject.new; end }.should raise_error(TypeError, error_msg)
137
+ end
138
+ end
139
+
@@ -4,9 +4,15 @@ module RuntimeFixtures
4
4
 
5
5
  class A::B
6
6
  module C
7
-
8
7
  end
9
8
  end
9
+
10
+ module ModuleB
11
+ end
12
+
13
+ module ModuleA
14
+ include ModuleB
15
+ end
10
16
  end
11
17
 
12
18
  describe "Constants access via .$$ with dots (regression for #1418)" do
@@ -14,3 +20,16 @@ describe "Constants access via .$$ with dots (regression for #1418)" do
14
20
  `Opal.Object.$$.RuntimeFixtures.$$.A.$$.B.$$.C`.should == RuntimeFixtures::A::B::C
15
21
  end
16
22
  end
23
+
24
+ describe "Inclusion of modules" do
25
+ it "that have been included by other modules works" do
26
+ # here ClassC would have failed to be created due to a bug in Opal.append_features
27
+ module RuntimeFixtures
28
+ class ClassC
29
+ include ModuleA
30
+ include ModuleB
31
+ end
32
+ end
33
+ RuntimeFixtures::ClassC.new.class.should == RuntimeFixtures::ClassC
34
+ end
35
+ end