Skip to content

Commit 5018b55

Browse files
committed
[Feature #17316] Add Object#instance_variable_set_unless_defined
1 parent b897a47 commit 5018b55

2 files changed

Lines changed: 78 additions & 9 deletions

File tree

object.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3046,6 +3046,52 @@ rb_obj_ivar_defined(VALUE obj, VALUE iv)
30463046
return rb_ivar_defined(obj, id);
30473047
}
30483048

3049+
/*
3050+
* call-seq:
3051+
* obj.instance_variable_set_unless_defined(symbol, value) -> value
3052+
* obj.instance_variable_set_unless_defined(string, value) -> value
3053+
* obj.instance_variable_set_unless_defined(symbol) {|symbol| ...} -> value
3054+
* obj.instance_variable_set_unless_defined(string) {|symbol| ...} -> value
3055+
*
3056+
* Returns the value of the given instance variable if the instance
3057+
* variable is set. Otherwise if it is not set, sets that instance
3058+
* variable to the given +value+ or the result of the given block.
3059+
* String arguments are converted to symbols.
3060+
*
3061+
* class Fred
3062+
* def initialize(p1, p2)
3063+
* @a, @b = p1, p2
3064+
* end
3065+
* end
3066+
* fred = Fred.new('cat', nil)
3067+
* fred.instance_variable_set_unless_defined(:@a, "dog") #=> "cat"
3068+
* fred.instance_variable_set_unless_defined(:@b, 99) #=> nil
3069+
* fred.instance_variable_defined?("@c") #=> false
3070+
* fred.instance_variable_set_unless_defined(:@c, "grey") #=> "grey"
3071+
* fred.instance_variable_get("@c") #=> "grey"
3072+
*/
3073+
static VALUE
3074+
rb_obj_ivar_set_undef(int argc, VALUE *argv, VALUE obj)
3075+
{
3076+
int arity = rb_block_given_p() ? 1 : 2;
3077+
rb_check_arity(argc, arity, arity);
3078+
VALUE iv = argv[0];
3079+
VALUE v;
3080+
ID id = id_for_var(obj, iv, instance);
3081+
3082+
if (id) {
3083+
if (!UNDEF_P(v = rb_ivar_lookup(obj, id, Qundef))) return v;
3084+
iv = ID2SYM(id);
3085+
}
3086+
else if (!id) {
3087+
iv = rb_str_intern(iv);
3088+
}
3089+
v = argc > 1 ? argv[1] : rb_yield(iv);
3090+
rb_check_frozen(obj);
3091+
if (!id) id = rb_sym2id(iv);
3092+
return rb_ivar_set(obj, id, v);
3093+
}
3094+
30493095
/*
30503096
* call-seq:
30513097
* mod.class_variable_get(symbol) -> obj
@@ -4532,6 +4578,7 @@ InitVM_Object(void)
45324578
rb_define_method(rb_mKernel, "instance_variable_get", rb_obj_ivar_get, 1);
45334579
rb_define_method(rb_mKernel, "instance_variable_set", rb_obj_ivar_set_m, 2);
45344580
rb_define_method(rb_mKernel, "instance_variable_defined?", rb_obj_ivar_defined, 1);
4581+
rb_define_method(rb_mKernel, "instance_variable_set_unless_defined", rb_obj_ivar_set_undef, -1);
45354582
rb_define_method(rb_mKernel, "remove_instance_variable",
45364583
rb_obj_remove_instance_variable, 1); /* in variable.c */
45374584

test/ruby/test_object.rb

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,12 @@ def test_methods_prepend_singleton
280280
assert_equal([:foo], k.private_methods(false))
281281
end
282282

283+
class ToStrCounter
284+
def initialize(str = "@foo") @str = str; @count = 0; end
285+
def to_str; @count += 1; @str; end
286+
def count; @count; end
287+
end
288+
283289
def test_instance_variable_get
284290
o = Object.new
285291
o.instance_eval { @foo = :foo }
@@ -291,9 +297,7 @@ def test_instance_variable_get
291297
assert_raise(NameError) { o.instance_variable_get("bar") }
292298
assert_raise(TypeError) { o.instance_variable_get(1) }
293299

294-
n = Object.new
295-
def n.to_str; @count = defined?(@count) ? @count + 1 : 1; "@foo"; end
296-
def n.count; @count; end
300+
n = ToStrCounter.new
297301
assert_equal(:foo, o.instance_variable_get(n))
298302
assert_equal(1, n.count)
299303
end
@@ -308,9 +312,7 @@ def test_instance_variable_set
308312
assert_raise(NameError) { o.instance_variable_set("bar", 1) }
309313
assert_raise(TypeError) { o.instance_variable_set(1, 1) }
310314

311-
n = Object.new
312-
def n.to_str; @count = defined?(@count) ? @count + 1 : 1; "@foo"; end
313-
def n.count; @count; end
315+
n = ToStrCounter.new
314316
o.instance_variable_set(n, :bar)
315317
assert_equal(:bar, o.instance_eval { @foo })
316318
assert_equal(1, n.count)
@@ -327,13 +329,33 @@ def test_instance_variable_defined
327329
assert_raise(NameError) { o.instance_variable_defined?("bar") }
328330
assert_raise(TypeError) { o.instance_variable_defined?(1) }
329331

330-
n = Object.new
331-
def n.to_str; @count = defined?(@count) ? @count + 1 : 1; "@foo"; end
332-
def n.count; @count; end
332+
n = ToStrCounter.new
333333
assert_equal(true, o.instance_variable_defined?(n))
334334
assert_equal(1, n.count)
335335
end
336336

337+
def test_instance_variable_set_unless_defined
338+
o = Object.new
339+
o.instance_eval { @foo = :foo; @bar = nil }
340+
called = nil
341+
assert_equal(:foo, o.instance_variable_set_unless_defined(:@foo, :baz))
342+
assert_equal(:foo, o.instance_variable_set_unless_defined(:@foo) {|v| called = v; :baz})
343+
assert_equal(nil, called)
344+
assert_equal(nil, o.instance_variable_set_unless_defined(:@bar, :bar))
345+
assert_equal(nil, o.instance_variable_set_unless_defined(:@bar) {|v| called = v; :bar})
346+
assert_equal(nil, called)
347+
assert_equal(:baz, o.instance_variable_set_unless_defined(:@baz, :baz))
348+
assert_equal(:baz, o.instance_variable_get(:@baz))
349+
assert_equal(:qux, o.instance_variable_set_unless_defined(:@qux) {|v| called = v; :qux})
350+
assert_equal(:qux, o.instance_variable_get(:@qux))
351+
assert_equal(:@qux, called)
352+
n = ToStrCounter.new("@quux")
353+
assert_equal(:quux, o.instance_variable_set_unless_defined(n) {|v| called = v; :quux})
354+
assert_equal(:quux, o.instance_variable_get(:@quux))
355+
assert_equal(:@quux, called)
356+
assert_equal(1, n.count)
357+
end
358+
337359
def test_remove_instance_variable
338360
{ 'T_OBJECT' => Object.new,
339361
'T_CLASS,T_MODULE' => Class.new(Object),

0 commit comments

Comments
 (0)