Skip to content

Commit ccfde36

Browse files
committed
[GR-40333] adding import_methods support to refinement
PullRequest: truffleruby/3677
2 parents a07e284 + 8d3699e commit ccfde36

File tree

17 files changed

+386
-14
lines changed

17 files changed

+386
-14
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ Compatibility:
9696
* Support optional `level` argument for `File.dirname` method (#2733, @moste00).
9797
* Add `Thread::Backtrace.limit` method (#2733, @andrykonchin).
9898
* Deprecate `rb_gc_force_recycle` and make it a no-op function (#2733, @moste00).
99+
* Add `Refinement#import_methods` method and add deprecation warning for `Refinement#include` and `Refinement#prepend` (#2733, @horakivo).
99100

100101
Performance:
101102

spec/ruby/core/module/refine_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def blah
7171
Module.new do
7272
refine("foo") {}
7373
end
74-
end.should raise_error(TypeError)
74+
end.should raise_error(TypeError, "wrong argument type String (expected Class or Module)")
7575
end
7676

7777
it "accepts a module as argument" do
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module RefinementSpec
2+
3+
module ModuleWithAncestors
4+
include Module.new do
5+
def indent(level)
6+
" " * level + self
7+
end
8+
end
9+
end
10+
end
11+
12+

spec/ruby/core/refinement/import_methods_spec.rb

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require_relative '../../spec_helper'
2+
require_relative 'fixtures/classes'
23

34
describe "Refinement#import_methods" do
45
ruby_version_is "3.1" do
@@ -17,6 +18,205 @@ def indent(level)
1718
end
1819
end
1920
end
21+
22+
it "throws an exception when argument is not a module" do
23+
Module.new do
24+
refine String do
25+
-> {
26+
import_methods Integer
27+
}.should raise_error(TypeError, "wrong argument type Class (expected Module)")
28+
end
29+
end
30+
end
31+
32+
it "imports methods from multiple modules" do
33+
str_utils = Module.new do
34+
def indent(level)
35+
" " * level + self
36+
end
37+
end
38+
39+
str_utils_fancy = Module.new do
40+
def indent_star(level)
41+
"*" * level + self
42+
end
43+
end
44+
45+
Module.new do
46+
refine String do
47+
import_methods str_utils, str_utils_fancy
48+
"foo".indent(3).should == " foo"
49+
"foo".indent_star(3).should == "***foo"
50+
end
51+
end
52+
end
53+
54+
it "imports a method defined in the last module if method with same name is defined in multiple modules" do
55+
str_utils = Module.new do
56+
def indent(level)
57+
" " * level + self
58+
end
59+
end
60+
61+
str_utils_fancy = Module.new do
62+
def indent(level)
63+
"*" * level + self
64+
end
65+
end
66+
67+
Module.new do
68+
refine String do
69+
import_methods str_utils, str_utils_fancy
70+
"foo".indent(3).should == "***foo"
71+
end
72+
end
73+
end
74+
75+
it "still imports methods of modules listed before a module that contains method not defined in Ruby" do
76+
str_utils = Module.new do
77+
def indent(level)
78+
" " * level + self
79+
end
80+
end
81+
82+
string_refined = Module.new do
83+
refine String do
84+
-> {
85+
import_methods str_utils, Kernel
86+
}.should raise_error(ArgumentError)
87+
end
88+
end
89+
90+
Module.new do
91+
using string_refined
92+
"foo".indent(3).should == " foo"
93+
end
94+
end
95+
end
96+
97+
it "warns if a module includes/prepends some other module" do
98+
module1 = Module.new do
99+
end
100+
101+
module2 = Module.new do
102+
include module1
103+
end
104+
105+
Module.new do
106+
refine String do
107+
-> {
108+
import_methods module2
109+
}.should complain(/warning: #<Module:\w*> has ancestors, but Refinement#import_methods doesn't import their methods/)
110+
end
111+
end
112+
113+
Module.new do
114+
refine String do
115+
-> {
116+
import_methods RefinementSpec::ModuleWithAncestors
117+
}.should complain(/warning: RefinementSpec::ModuleWithAncestors has ancestors, but Refinement#import_methods doesn't import their methods/)
118+
end
119+
end
120+
end
121+
122+
it "doesn't import methods from included/prepended modules" do
123+
Module.new do
124+
refine String do
125+
suppress_warning { import_methods RefinementSpec::ModuleWithAncestors }
126+
end
127+
128+
129+
using self
130+
-> {
131+
"foo".indent(3)
132+
}.should raise_error(NoMethodError, /undefined method `indent' for "foo":String/)
133+
end
134+
end
135+
136+
it "doesn't import any methods if one of the arguments is not a module" do
137+
str_utils = Module.new do
138+
def indent(level)
139+
" " * level + self
140+
end
141+
end
142+
143+
string_refined = Module.new do
144+
refine String do
145+
-> {
146+
import_methods str_utils, Integer
147+
}.should raise_error(TypeError)
148+
end
149+
end
150+
151+
Module.new do
152+
using string_refined
153+
-> {
154+
"foo".indent(3)
155+
}.should raise_error(NoMethodError)
156+
end
157+
end
158+
159+
it "imports methods from multiple modules so that methods see other's module's methods" do
160+
str_utils = Module.new do
161+
def indent(level)
162+
" " * level + self
163+
end
164+
end
165+
166+
str_utils_normal = Module.new do
167+
def indent_normal(level)
168+
self.indent(level)
169+
end
170+
end
171+
172+
Module.new do
173+
refine String do
174+
import_methods str_utils, str_utils_normal
175+
end
176+
177+
using self
178+
"foo".indent_normal(3).should == " foo"
179+
end
180+
end
181+
182+
it "imports methods from module so that methods can see each other" do
183+
str_utils = Module.new do
184+
def indent(level)
185+
" " * level + self
186+
end
187+
188+
def indent_with_dot(level)
189+
self.indent(level) + "."
190+
end
191+
end
192+
193+
Module.new do
194+
refine String do
195+
import_methods str_utils
196+
end
197+
198+
using self
199+
"foo".indent_with_dot(3).should == " foo."
200+
end
201+
end
202+
203+
it "doesn't import module's class methods" do
204+
str_utils = Module.new do
205+
def self.indent(level)
206+
" " * level + self
207+
end
208+
end
209+
210+
Module.new do
211+
refine String do
212+
import_methods str_utils
213+
end
214+
215+
using self
216+
-> {
217+
String.indent(3)
218+
}.should raise_error(NoMethodError, /undefined method `indent' for String:Class/)
219+
end
20220
end
21221

22222
context "when methods are not defined in Ruby code" do
@@ -29,6 +229,17 @@ def indent(level)
29229
end
30230
end
31231
end
232+
233+
it "raises ArgumentError when importing methods from C extension" do
234+
require 'zlib'
235+
Module.new do
236+
refine String do
237+
-> {
238+
import_methods Zlib
239+
}.should raise_error(ArgumentError, /Can't import method which is not defined with Ruby code: Zlib#*/)
240+
end
241+
end
242+
end
32243
end
33244
end
34245
end

spec/ruby/core/warning/warn_spec.rb

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def Warning.warn(msg)
7474
end
7575
end
7676

77-
it "should print message when category is :deprecated and Warning[:deprecated] is true" do
77+
it "warns when category is :deprecated and Warning[:deprecated] is true" do
7878
warn_deprecated = Warning[:deprecated]
7979
Warning[:deprecated] = true
8080
begin
@@ -86,7 +86,7 @@ def Warning.warn(msg)
8686
end
8787
end
8888

89-
it "should print message when category is :experimental and Warning[:experimental] is true" do
89+
it "warns when category is :experimental and Warning[:experimental] is true" do
9090
warn_experimental = Warning[:experimental]
9191
Warning[:experimental] = true
9292
begin
@@ -98,7 +98,7 @@ def Warning.warn(msg)
9898
end
9999
end
100100

101-
it "should not print message when category is :deprecated but Warning[:deprecated] is false" do
101+
it "doesn't print message when category is :deprecated but Warning[:deprecated] is false" do
102102
warn_deprecated = Warning[:deprecated]
103103
Warning[:deprecated] = false
104104
begin
@@ -110,7 +110,7 @@ def Warning.warn(msg)
110110
end
111111
end
112112

113-
it "should not print message when category is :experimental but Warning[:experimental] is false" do
113+
it "doesn't print message when category is :experimental but Warning[:experimental] is false" do
114114
warn_experimental = Warning[:experimental]
115115
Warning[:experimental] = false
116116
begin
@@ -121,6 +121,18 @@ def Warning.warn(msg)
121121
Warning[:experimental] = warn_experimental
122122
end
123123
end
124+
125+
it "prints the message when VERBOSE is false" do
126+
-> { Warning.warn("foo") }.should complain("foo")
127+
end
128+
129+
it "prints the message when VERBOSE is nil" do
130+
-> { Warning.warn("foo") }.should complain("foo", verbose: nil)
131+
end
132+
133+
it "prints the message when VERBOSE is true" do
134+
-> { Warning.warn("foo") }.should complain("foo", verbose: true)
135+
end
124136
end
125137

126138
ruby_version_is ''...'3.0' do
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +0,0 @@
1-
fails:Refinement#import_methods when methods are defined in Ruby code imports methods
2-
fails:Refinement#import_methods when methods are not defined in Ruby code raises ArgumentError

spec/tags/core/refinement/include_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/core/refinement/prepend_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/core/warning/warn_tags.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,3 @@ slow:Warning.warn has Warning as the method owner
22
slow:Warning.warn can be overridden
33
slow:Warning.warn does not add a newline
44
slow:Warning.warn returns nil
5-
fails:Warning.warn is called by Kernel.warn with nil category keyword
6-
fails:Warning.warn is called by Kernel.warn with given category keyword converted to a symbol

src/main/java/org/truffleruby/RubyLanguage.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,8 @@ private RubyThread getOrCreateForeignThread(RubyContext context, Thread thread)
256256

257257
@CompilationFinal public LanguageOptions options;
258258
@CompilationFinal private String rubyHome;
259+
@CompilationFinal public String cextPath;
260+
259261
private TruffleFile rubyHomeTruffleFile;
260262

261263
@CompilationFinal private AllocationReporter allocationReporter;
@@ -708,6 +710,7 @@ private void setRubyHome(Env env, String home) {
708710
assert Thread.holdsLock(this);
709711
rubyHome = home;
710712
setRubyHomeTruffleFile(env, home);
713+
cextPath = home == null ? null : home + "/lib/truffle/truffle/cext_ruby.rb";
711714
}
712715

713716
private void setRubyHomeTruffleFile(Env env, String home) {

0 commit comments

Comments
 (0)