Properties and Methods in Ruby from a .NET POV

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.

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.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://reinh.com Rein Henrichs

    Methods that are intended to return a boolean(ish) value are called query methods[1] after the pattern from Kent Beck’s book, Smalltalk Best Practice Patterns.

    The method_missing example is a great example of unnecessary metaprogramming. The reduction in duplication is not worth the added cost in complexity and potential debugging difficulties.

    [1] http://reinh.com/blog/2008/07/17/ruby-patterns-query-method.html

    • http://www.lassala.net Claudio Lassala

      Hi Rein,

      Thanks for the clarification about “Query Method”. I’ve heard Ruby people speak of those as “predicate method”, but now I do remember reading about query method in one of Kent’s books.

  • http://maximilianoguzman.com.ar Maximiliano Guzman

    As Rein said, it would be overkill (and overcomplex) to use metaprogramming to replace just two methods. But when your class has many, many more methods with such a generalizable functionality, then it starts to make sense. One place where such method missing wizardry does well is when creating APIs or library wrappers of some kind.

    • http://www.lassala.net Claudio Lassala

      Right, I did mention that the scenario I had actually needed a bunch of methods like that, so it made sense to clean it up with metaprogramming, and so far, I haven’t experienced complexity or troubleshooting problems in that specific case.

  • Dinesh Majrekar

    If you are going down the meatprogramming route for this, I would do something like this:

    https://gist.github.com/1634092

    • http://www.lassala.net Claudio Lassala

      Nice approach, Dinesh. Thanks for posting it. Right from the start of my series of posts here I’ve said I was just getting into Ruby, so I expected to learn from readers like you who can contribute with different ways to tackle a problem (I’m not gonna say “better” ways because there are always different opinions to everything). :)

  • http://burningbush.us Jon Moses

    +1 about the method_missing implementation being vast meta-programming overkill for something like this. Sure, it works, but it’s fragile and over complicated.

    There are a billion ways to accomplish it, but I usually do something like: https://gist.github.com/1640065

  • http://bibwild.wordpress.com Jonathan Rochkind

    Nicely said.

    It’s worth pointing out that a “class macro” is not a special language feature — it’s simply an ordinary class method, that’s implemented to do macro-like things.

    In this case it’s defined on Module (the superclass of Class), and defined as an ‘instance method’ on Module, but that makes it a class method on actual Class definitions. And it’s implementation is in C in MRI. Okay, that actually is kind of confusing, heh. http://apidock.com/ruby/Module/attr_accessor

    But you could actually easily implement it as an ordinary ‘class method’, in pure ruby, in a module you mix-in to your own class with ‘extend’ to give you access to the ‘macro’. That would be an interesting excersize in beginner ‘meta programming’, it’s not too hard to do.

    • http://www.lassala.net Claudio Lassala

      Hi Jonathan,

      Yes, that stuff is complicated indeed. :) I’ve read *the book* on metaprogramming where it explains about that stuff, but I’m gonna have to read it again. You see, those concepts only really sink in when we use them on a real world case, but unfortunately, most examples we see are too contrived.

      Anyway, thanks for your clarification. It encouraged me to read up more on those things again.

  • http://bibwild.wordpress.com Jonathan Rochkind

    Okay, i decided I wanted to do that myself (implement somethign like attr_accessor as pure ruby myself) as an exersize. It ended up being a bit tricky than I expected, but still pretty concise relatively clear code. Try it yourself if you like before looking at my solution, which is definitely just one possible solution, and is just a demo probably has problems for ‘real’ use.

    https://gist.github.com/1640639

    • http://www.lassala.net Claudio Lassala

      Oh, thanks for posting this. Good stuff. Your example is simple enough where people should be able to read it and grok it!