Ruby
Article
By Claudio Lassala

Properties and Methods in Ruby from a .NET POV

By Claudio Lassala
Help us help you! You'll get a... FREE 6-Month Subscription to SitePoint Premium Plus you'll go in the draw to WIN a new Macbook SitePoint 2017 Survey Yes, let's Do this It only takes 5 min

C# developers are used to working with fields, properties, and methods. But there’s certain confusion when it comes to understanding how those work in Ruby. So let’s have a quick C# refresher.

Fields, Properties, and Methods in C#

Fields are variables scoped either to an object (instance fields) or to a class (static fields). As a best practice, developers don’t expose fields publicly, as doing so exposes too much of the internals of a class. An instance field is declared like so:

public class Customer
{
  private int _age;
}

Notice that the underscore before “age” is just a common convention that several C# developers follow. In order to expose such a field, one creates a “property”, which is defined as a pair of a getter and a setter method:

public class Customer
{
  private int _age;

  public int Age 
  {
    get { return _age; }
    set { _age = value; }
  }
}

A property is defined only with a get method in case it’s only meant to be “accessed”, but not “assigned”. In cases where getters and setters simply get or set the value of a field, one may use a feature of C# called auto properties:

public class Customer
{
  public int Age { get; set; } 
}

When a property is defined like that, the C# compiler produces code similar to the one I had shown before (it creates the backing field, and the implementation of getters/setters).

Now, What Does That Look Like in Ruby…?

The following post briefly covered instance and class variables: NET to Ruby: Methods and Variables. In order to declare an instance variable, we define it in a class’ initializer (its constructor), preceeding it with an @:

class Customer

  def initialize
    @age = 18
  end
end

At this point, @age is very similar to a private field in C#, in that it can be accessed anywhere within the class where it is defined, but not outside of it. In order to access or assign it from the outside, we must define accessor methods to it:

class Customer

  def initialize
    @age = 18
  end

  def age
    @age
  end

  def age=(value)
    @age = value
  end
end

Different than in C#, where we defined an Age property containing set and get methods, here we created an age method that returns the value of the @age instance variable, and also an age= method, which takes in a value and assigns it to the instance variable.

Similarly to C#’s auto properties, Ruby also offers a construct that allow for easy creation of accessors. The code above could be rewritten like so:

class Customer

  def initialize
    @age = 18
  end

  attr_accessor :age
end

attr_accessor is not a keyword in Ruby; it is something called a “class macro”, which is a meta programming technique to add dynamic behavior to a class. In this case, what gets added are the reader and writer accessors. We can also only add either the reader or write accessors by using attr_reader or attr_writer, respectively.

--ADVERTISEMENT--

Accessors That Do a Little More…

It’s very common to create a property in C# where the accessors may do a little more than just get and/or set the value of an instance variable. For example, maybe the getter needs to concatanate values before returning it:

public class Customer
{
  private string _firstName;
  private string _lastName;

  public string FullName 
  {
    get { return string.Format("{0} {1}", _firstName, _lastName); }
  }
}

In the example above, the FullName property has a getter that concatenates the _firstName and _lastName fields. Such practice is common in Ruby as well:

class Customer

  def initialize
    @first_name = "Claudio"
    @last_name = "Lassala"
  end

  def full_name
    "#{@first_name} #{@last_name}"
  end
end

Keep in mind that parenthesis are optional in Ruby, so a method such as full_name above can be called like so:

cust = Customer.new
puts cust.first_name

Cleaning Up the Code With Metaprogramming

Quite often we add boolean properties to a class so we can ask an object about certain things. Take this example:

public class User
{
  private string _profile;

  public User(string profile)
  {
    _profile = profile;
  }

  public bool IsDoctor
  {
    get { return _profile == "doctor"; }
  }

  public bool IsPatient
  {
    get { return _profile == "patient"; }
  } 
}

Now assume that there are more types of profiles then just “doctor” and “patient”, so the User class would end up having one property for each type of profile in this case. Similar class could be created in Ruby like so:

class User

  def initialize(profile)
    @profile = profile
  end

  def doctor?
    @profile == 'doctor'
  end

  def patient?
    @profile == 'patient'
  end
end

What’s with the “?” suffix for the doctor? and patient? methods? Often, when a method in Ruby is meant to return a boolean, the method is called a predicate method, and it is common practice to have it with a “?” suffix. So instead of writing code such as “if user.is_doctor”, we write “if user.doctor?”.

If there’s really potential for the User class to have several methods such as doctor? and patient? (one for each type of profile), the class could be rewritten to leverage some metaprogramming. Here’s an example of what that could look like:

class User

  def initialize(profile)
    @profile = profile
  end

  def method_missing(method_name, *args, &block)
    method_name = method_name.to_s
    if method_name.ends_with?("?")
      return @profile == method_name.chop
    end

    super if self.responds_to?(method_name)
  end
end

Notice that both the doctor? and patient? methods are gone, and a method_missing method has been added. What happens now is that whenever some code queries the user object like “if user.doctor?”, such method is missing in the class, and the method_missing method gets called. In the example above, the method_missing checks whether the method being called on the object (“method_name”) ends with a “?”, and if it does, it compares @profile to it. If new profiles are supported, there is no need to add properties to the User class, as it is ready to handle it.

Summary

It’s important to understand the fact that there aren’t really “properties” in Ruby (there are methods, instead), and those can either be created automatically (using attr_accessor, attr_reader, attr_writer class macros) or manually (by simply defining the methods), since these things are used quite a bit in every application.

I hope you now have a better grip on how C# and Ruby compare when it comes to properties. Leave a comment if you have any questions or insight.

Login or Create Account to Comment
Login Create Account
Recommended
Sponsors
Get the latest in Ruby, once a week, for free.