Igor Alexandrov

Programming and something else…

Attrio – Typed Attributes for Ruby Objects

| Comments

Do you remember my previous post about Yandex::Webmaster gem? In the end of the post there was an example of typed attributes in plain Ruby class. I said that we plan to extract this functionality from Yandex::Webmaster into a separate gem and we’ve done it today!

Most of you think that we are reinventing wheel again. Ruby community already has Virtus, ActiveAttr and a couple of other libraries that provide similar functionality.

But stop! Have ever looked in Virtus code? Or why should load ActiveModel as a dependency while using ActiveAttr??? Or why Virtus redefines #attributes= method in my model without even asking me? To much questions for libraries that just should provide functionality to define attributes.

That is why we in JetRockets decided that we should create our own solution. It should be clear, easy to use, extend and work without any dependencies.

Meet Attrio! Attrio is not as monstrous as Virtus, does not have any external dependencies like ActiveAttr, but still rather powerful and configurable.

To use Attrio include it into your class and then call #define_attributes block:

1
2
3
4
5
6
7
8
9
10
class User
  include Attrio

  define_attributes do
    attr :name, String
    attr :age, Integer
    attr :birthday, DateTime
    attr :active, Boolean
  end
end

Now instance of your User model has the following methods:

  • name, name=
  • age, age=
  • birthday, birthday=
  • active, active? ,active=

Attrio supports several build in types like: Boolean, String, Integer, Float, Date, Time, DateTime, Symbol. But actually every Ruby class can be used as a type in Attrio. If it has a typecast and typecasted? method then value for the attribute will be created with this methods, if not – then common new method will be called.

Attrio supports default values for the attributes:

1
2
3
4
5
6
7
8
9
10
11
12
class User
  include Attrio

  define_attributes do
    attr :name, String
    attr :age, Integer, :default => 18
  end
end

user = User.new
user.age
# => 18

By default Attrio defines #attributes accessor which contains a Hash with attributes names as keys and instances of Attrio::Attribute as values.

1
2
3
4
5
6
user = User.new
user.attributes
# => {
#    :name => #<Attrio::Attribute:0x007fc44e8ca680 @object=#<User:0x007fc44e8b2b48>, @name="name", @type=String, @options={}, @writer_method_name="name=", @writer_visibility=:public, @instance_variable_name="@name", @reader_method_name="name", @reader_visibility=:public>,
#    :age => #<Attrio::Attribute:0x007fc44e8d4c98 @object=#<User:0x007fc44e8b2b48>, @name="age", @type=Attrio::Types::Integer, @options={}, @writer_method_name="age=", @writer_visibility=:public, @instance_variable_name="@age", @reader_method_name="age", @reader_visibility=:public>
# }

Accessor name can be easily overridden by passing :as option to define_attributes block.

1
2
3
4
5
6
7
8
9
class User
  include Attrio
  
  define_attributes :as => 'api_attributes' do
    attr :name, String
    attr :age, Integer
    attr :birthday, DateTime
  end
end
1
2
user = User.new
user.api_attributes # => {...}

Attrio is well tested and works under 1.9.2, 1.9.3, 2.0.0, Rubinius and JRuby and we will continue to support all these interpreters and future releases.

The first gem that will use Attrio will be Yandex::Webmaster, after it Yandex::Metrika will also be rewritten. Also we are glad to say, that Attrio is the first gem, that was developed under our company account on GitHub and I hope that it is not the last one.

Read more about Attrio in its README file on GitHub and don’t forget to share with us your thoughts and ideas: write your comments here and open issues on GitHub.

Comments