0.0
No commit activity in last 3 years
No release in over 3 years
Provides a Ruby module that can be extended by a class in order to provide class methods for defining attributes. Attributable automatically generates accessor, equality, hash and inspect methods.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 1.3
~> 0.7.0
~> 10.1.1
~> 2.14.1
~> 0.19.1
 Project Readme

Attributable Build Status Code Climate Dependency Status Test Coverage

A tiny library that makes it easy to create value objects.

Basic usage

require "attributable"

class User
  extend Attributable

  attributes :forename, :surname
end

john = User.new(forename: "John", surname: "Doe")
john.forename # => "John"
john.surname  # => "Doe"

All attributes are read-only:

john.forename = "Jonathan" # => NoMethodError: undefined method `forename='

Default values for attributes can be set via a hash argument to attributes:

class UserWithDefaults
  extend Attributable

  attributes :forename, :surname, active: true
end

anon = UserWithDefaults.new
anon.active   # => true
anon.forename # => nil

Equality

Attributable adds eql? and == methods to your class which compare attribute values and types.

john = User.new(forename: "John", surname: "Doe")
second_john = User.new(forename: "John", surname: "Doe")

john.eql? second_john # => true
john == second_john   # => true

The equality methods return false when compared to an object of the same type with different attribute values:

jane = User.new(forename: "Jane", surname: "Doe")
john.eql? jane # => false
john == jane   # => false

The equality methods return false when compared to an object of a different type, even if the attribute values are equal.

class Admin
  extend Attributable

  attributes :forename, :surname
end

admin_john = Admin.new(forename: "Jane", surname: "Doe")
john.eql? admin_john # => false
john == admin_john   # => false

Because Attributable overrides eql? and ==, it also overrides hash:

john.hash == second_john.hash # => true
john.hash == jane.hash        # => false
john.hash == admin_john.hash  # => false

Pretty printing

Attributable adds an inspect method to your class which display attribute values.

john.inspect # => <User forename="John", surname="Doe">

Using with custom initialisation logic

Attributable provides the initialize_attributes method which can be used if you need to specify your own initialize method. For example:

class UserWithDerivedAttribute
  extend Attributable
  attributes :forename, :surname
  
  attr_accessor :fullname
  
  def initialize(attributes = {})
    initialize_attributes(attributes)
    @fullname = "#{forename} #{surname}"
  end
end

john = UserWithDerivedAttribute.new(forename: "John", surname: "Doe")
john.forename # => "John"
john.fullname # => "John Doe"

Note that, by default, Attributable adds the following initialize method:

def initialize(attributes = {})
  initialize_attributes(attributes)
end

Reuse via inheritance and mix-ins

To reuse attribute declarations, either user Ruby's built-in inheritance, mix-ins, or both:

class Author < User
  attributes blogs: []
end

ronson = Author.new(forename: "Jon", surname: "Ronson")
ronson.inspect # => <Author forename="Jon", surname="Ronson", blogs=[]>

The default values defined in superclasses or mixed-in modules can be changed:

class Ronson < User
  attributes surname: "Ronson"
end

Ronson.new(forename: "Jon").inspect # <Ronson forename="Jon", surname="Ronson">
Ronson.new(forename: "Mark").inspect # <Ronson forename="Mark", surname="Ronson">

Here's the same example, but using a module:

module User
  extend Attributable

  attributes :forename, :surname
end

class Author
  include User
  extend Attributable
  attributes blogs: []
end

ronson = Author.new(forename: "Jon", surname: "Ronson")
ronson.inspect # => <Author forename="Jon", surname="Ronson", blogs=[]>

class Ronson
  include User
  extend Attributable
  attributes surname: "Ronson"
end

Ronson.new(forename: "Jon").inspect # <Ronson forename="Jon", surname="Ronson">
Ronson.new(forename: "Mark").inspect # <Ronson forename="Mark", surname="Ronson">

Note: the include must occur before any call to attributes.

Installation

Add this line to your application's Gemfile:

gem 'attributable'

And then execute:

$ bundle

Or install it yourself as:

$ gem install attributable

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request