Ruby Scripting
#27
Effects of Relying on a Bad Explanation:
Getters, Setters & Wrapper Methods

When someone else was explaining how the getter and setter methods work in Ruby, the next thing this guy mentioned was that they were some sort of wrapper methods created around the corresponding variables to let the program access them. Later on you could also read that it limit the circumstances under which the variables can be altered by mistake, but is that true?

I've got some sad news for all those people that once thought it was true. No, that's not how it works and it doesn't prevent you from changing the value at any inconvenient moment and those methods can be accessed from ANYWHERE at ANY GIVEN TIME.

Thus, some var=(val) method isn't a wrapper of @some, it's its actual setter method. It simply was manually created instead of using Module's attr_writter shortcut. Even if the setter included a conditional statement, it'd remain as a setter.

If you don't want any scene or window or sprite to be able to alter those variables, then don't define their setter methods. Tongue sticking out
Thinking Well, you could add a condition either in the line of code where you need to alter them externally OR add a condition inside the setter method to prevent it from ever being a nil value or a negative number or anything the like.

Is it a good idea of calling a manually defined getter or setter method a wrapper?

Nope, it isn't. It'd be quite terrible for setters. You better reserve that term for other instances where you'll definitely need it.

It'd be a wrapper method in Ruby IF:
  • It had another name or a different number of arguments like set_vars(a, b)
  • It's a custom spriteset class that lets you change all of its sprites' visibility state with a visible=(bool) method.
  • The Game_Party#add_actor method that will limit the number of heroes that can join our party in the RM engine.
  • Of course, there might be other ways to create wrappers that I haven't mentioned here like Game_Actors class being a wrapper class for a @data Array object that contains Game_Actor objects, i.e. $game_actors[1] calls the Game_Actors#[](index) method that internally returns the value of @data[index] or creates a brand new Game_Actor object.

Is there a way to easily prove that those getters and setters don't behave like mere wrappers?

Yes, there is! Let's use a simple test code.

Code:
class Person
  def initialize
    @age = 1
  end

  def age
    print "Current age #{@age}\n"
    @age
  end

  def age=(new_age)
    print "Altering your age now!\n"
    @age = new_age
  end
  attr_reader :age
  attr_writer :age
  # Or just call this instead: attr_accessor :age
end

The code above would define 2 methods for the same @age variable, its getter and its setter in that specific order. They are supposed to print something on screen on both RMXP and RMVX. In RMVX ACE it would require you to open the console window first.

But there's a catch! Shocked By calling the attr_reader and attr_writer methods, or simply the attr_accessor method, we have overwritten the getter and the setter we had manually created for the @age variable.

What that means is that now we won't ever be able to print anything on the popup window or the console window depending on which RM engine you're using.

Happy with a sweat OK, you still can #comment out the attr_ lines to be able to read those strings as first intended.


NOTE

I have added the initialize method to the Person class to make sure that the @age variable will never return a nil value, which is the default return value of any variable that has not been assigned a different value at some point.



Other Ways to Deal With It

Later on Dog Wulfo showed some interest in adding some alternate code to let people handle the return of a nil value in a different way, especially if I had not defined the initialize method there.

The code I had proposed would look like this then:

Code:
class Person
  def age
    print "Current age #{@age}\n"
    @age ||= 1
  end

  def age=(new_age)
    print "Altering your age now!\n"
    @age = new_age
  end
  attr_reader :age
  attr_writer :age
  # Or just call this instead: attr_accessor :age
end

What does that altered line of code do there?

Well, it's pretty simple indeed. If the @age has not been predefined, it will pick 1 as its default value. If at some point the value of @age has been altered, let's say that now it's 15, it will ignore 1 and return 15 instead.

Confused Why? Because it's no longer equal to nil. Only nil values would get replaced using the ||= operator.

Yet, don't forget that the attr_ methods are still in use so they won't ever print anything on a popup window or the console window if any.

If it were able to print anything, you'll soon notice that the age getter method would print a nil (an empty string "") instead of a number as its current age. Sad

Is that the only way we could have done it?

Tongue sticking out Nope, there's yet another way to deal with it, but it's not as efficient as using the ||= operator there.

Grinning Here's the code that Dog Wulfo had suggested a while ago. It presupposes that there has been no attempt to define the initialize method so far.

Code:
class Person
  def age
    @age = 1 if @age.nil?
    print "Current age #{@age}\n"
    @age
  end

  def age=(new_age)
    print "Altering your age now!\n"
    @age = new_age
  end
  attr_reader :age
  attr_writer :age
  # Or just call this instead: attr_accessor :age
end

As you can see, the interpreter would have to always make the check in a separate line before printing the @age variable via print and then returning the value of @age... IF the attr_reader or attr_writer or attr_accessor methods had not been used there or had been #commented out.

You shouldn't underestimate that piece of code. It does work as intended, but the ||= operator makes it easy to skip it.

Nonetheless, there's a caveat! Shocked

The ||= operator won't let you preassign the value before printing the previous value of @age. Confused So it does have a downside after all.
"For God has not destined us for wrath, but for obtaining salvation through our Lord Jesus Christ," 1 Thessalonians 5:9

Maranatha!

The Internet might be either your friend or enemy. It just depends on whether or not she has a bad hair day.

[Image: SP1-Scripter.png]
[Image: SP1-Writer.png]
[Image: SP1-Poet.png]
[Image: SP1-PixelArtist.png]
[Image: SP1-Reporter.png]

My Original Stories (available in English and Spanish)

List of Compiled Binary Executables I have published...
HiddenChest & Roole

Give me a free copy of your completed game if you include at least 3 of my scripts! Laughing + Tongue sticking out

Just some scripts I've already published on the board...
KyoGemBoost XP VX & ACE, RandomEnkounters XP, KSkillShop XP, Kolloseum States XP, KEvents XP, KScenario XP & Gosu, KyoPrizeShop XP Mangostan, Kuests XP, KyoDiscounts XP VX, ACE & MV, KChest XP VX & ACE 2016, KTelePort XP, KSkillMax XP & VX & ACE, Gem Roulette XP VX & VX Ace, KRespawnPoint XP, VX & VX Ace, GiveAway XP VX & ACE, Klearance XP VX & ACE, KUnits XP VX, ACE & Gosu 2017, KLevel XP, KRumors XP & ACE, KMonsterPals XP VX & ACE, KStatsRefill XP VX & ACE, KLotto XP VX & ACE, KItemDesc XP & VX, KPocket XP & VX, OpenChest XP VX & ACE
Reply


Messages In This Thread
Ruby Scripting - by kyonides - 08-29-2019, 04:51 AM
RE: Ruby Scripting - by kyonides - 08-30-2019, 05:47 AM
RE: Ruby Scripting - by kyonides - 09-03-2019, 07:24 AM
RE: Ruby Scripting - by kyonides - 09-06-2019, 05:46 AM
RE: Ruby Scripting - by kyonides - 09-09-2019, 05:00 AM
RE: Ruby Scripting - by kyonides - 06-05-2021, 08:20 PM
RE: Ruby Scripting - by kyonides - 11-28-2021, 03:31 AM
RE: Ruby Scripting - by kyonides - 05-02-2022, 02:43 AM
RE: Ruby Scripting - by kyonides - 01-27-2023, 08:01 AM
RE: Ruby Scripting - by kyonides - 02-03-2023, 02:42 AM
RE: Ruby Scripting - by DerVVulfman - 02-03-2023, 03:52 AM
RE: Ruby Scripting - by kyonides - 02-03-2023, 04:33 AM
RE: Ruby Scripting - by kyonides - 04-19-2023, 12:47 AM
RE: Ruby Scripting - by kyonides - 04-19-2023, 01:12 AM
RE: Ruby Scripting - by kyonides - 06-08-2023, 09:24 PM
RE: Ruby Scripting - by kyonides - 06-08-2023, 09:46 PM
RE: Ruby Scripting - by kyonides - 06-12-2023, 06:29 AM
RE: Ruby Scripting - by kyonides - 07-14-2023, 10:37 PM
RE: Ruby Scripting - by kyonides - 07-15-2023, 07:32 AM
RE: Ruby Scripting - by kyonides - 07-16-2023, 06:05 AM
RE: Ruby Scripting - by kyonides - 07-17-2023, 04:01 AM
RE: Ruby Scripting - by kyonides - 08-22-2023, 08:59 AM
RE: Ruby Scripting - by kyonides - 08-28-2023, 03:34 AM
RE: Ruby Scripting - by kyonides - 11-18-2023, 09:02 AM
RE: Ruby Scripting - by kyonides - 04-16-2024, 06:57 AM
RE: Ruby Scripting - by kyonides - 12-21-2024, 09:02 PM
RE: Ruby Scripting - by kyonides - 03-24-2025, 07:50 PM
RE: Ruby Scripting - by kyonides - Today, 02:32 AM

Possibly Related Threads…
Thread Author Replies Views Last Post
   Ruby - Behind the Scenes DerVVulfman 0 1,525 07-15-2023, 05:52 PM
Last Post: DerVVulfman
Information  Assorted Ruby chm documents. hanetzer 10 37,228 08-17-2020, 04:19 AM
Last Post: kyonides
Brick  Learn Ruby! greenraven 2 8,201 05-16-2014, 12:25 PM
Last Post: greenraven
   Creating Your Own Scripting System DerVVulfman 3 8,056 10-12-2009, 03:37 PM
Last Post: Alpha-Mad
   How to make interesting NPC's without scripting Third333Strike 0 4,140 12-06-2008, 04:59 PM
Last Post: Third333Strike



Users browsing this thread: 2 Guest(s)