From fb36702d5e746c12cda66e14ad41819db9598567 Mon Sep 17 00:00:00 2001 From: Andy Waite Date: Sun, 29 Mar 2026 19:08:38 -0400 Subject: [PATCH 1/2] Add rule discouraging instance_variable_get and instance_variable_set Co-Authored-By: Claude Sonnet 4.6 --- README.adoc | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.adoc b/README.adoc index cb5b0333..b92ec2d7 100644 --- a/README.adoc +++ b/README.adoc @@ -3869,6 +3869,39 @@ Parent.print_class_var # => will print 'child' As you can see all the classes in a class hierarchy actually share one class variable. Class instance variables should usually be preferred over class variables. +=== Avoid Dynamic Instance Variable Access [[no-dynamic-instance-variable-access]] + +Avoid `instance_variable_get` and `instance_variable_set`. +These methods bypass encapsulation and couple callers to internal implementation details. +Prefer defining explicit accessor methods instead. +Framework or library code that needs to work generically with arbitrary objects may be a justified exception. + +[source,ruby] +---- +# bad +class Person + def initialize(name) + @name = name + end +end + +person = Person.new('Alice') +person.instance_variable_get(:@name) # => 'Alice' +person.instance_variable_set(:@name, 'Bob') + +# good +class Person + attr_reader :name + + def initialize(name) + @name = name + end +end + +person = Person.new('Alice') +person.name # => 'Alice' +---- + === Leverage Access Modifiers (e.g. `private` and `protected`) [[visibility]] Assign proper visibility levels to methods (`private`, `protected`) in accordance with their intended usage. From d806fd8993037917f06a019aa2dd63d1016f9b00 Mon Sep 17 00:00:00 2001 From: Andy Waite Date: Sun, 29 Mar 2026 19:16:15 -0400 Subject: [PATCH 2/2] Move no-dynamic-instance-variable-access rule to Metaprogramming section Co-Authored-By: Claude Sonnet 4.6 --- README.adoc | 66 ++++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/README.adoc b/README.adoc index b92ec2d7..25ee83cc 100644 --- a/README.adoc +++ b/README.adoc @@ -3869,39 +3869,6 @@ Parent.print_class_var # => will print 'child' As you can see all the classes in a class hierarchy actually share one class variable. Class instance variables should usually be preferred over class variables. -=== Avoid Dynamic Instance Variable Access [[no-dynamic-instance-variable-access]] - -Avoid `instance_variable_get` and `instance_variable_set`. -These methods bypass encapsulation and couple callers to internal implementation details. -Prefer defining explicit accessor methods instead. -Framework or library code that needs to work generically with arbitrary objects may be a justified exception. - -[source,ruby] ----- -# bad -class Person - def initialize(name) - @name = name - end -end - -person = Person.new('Alice') -person.instance_variable_get(:@name) # => 'Alice' -person.instance_variable_set(:@name, 'Bob') - -# good -class Person - attr_reader :name - - def initialize(name) - @name = name - end -end - -person = Person.new('Alice') -person.name # => 'Alice' ----- - === Leverage Access Modifiers (e.g. `private` and `protected`) [[visibility]] Assign proper visibility levels to methods (`private`, `protected`) in accordance with their intended usage. @@ -5823,6 +5790,39 @@ class Person end ---- +=== Avoid Dynamic Instance Variable Access [[no-dynamic-instance-variable-access]] + +Avoid `instance_variable_get` and `instance_variable_set`. +These methods bypass encapsulation and couple callers to internal implementation details. +Prefer defining explicit accessor methods instead. +Framework or library code that needs to work generically with arbitrary objects may be a justified exception. + +[source,ruby] +---- +# bad +class Person + def initialize(name) + @name = name + end +end + +person = Person.new('Alice') +person.instance_variable_get(:@name) # => 'Alice' +person.instance_variable_set(:@name, 'Bob') + +# good +class Person + attr_reader :name + + def initialize(name) + @name = name + end +end + +person = Person.new('Alice') +person.name # => 'Alice' +---- + === No Monkey Patching [[no-monkey-patching]] Do not mess around in core classes when writing libraries (do not monkey-patch them).