/tutor

Supplemental teachings for your Ruby classes.

Primary LanguageRubyMIT LicenseMIT

Tutor

Build Status Gem Version

For Ruby 2+

Supplemental teachings for your Ruby classes.

Table of Contents

  1. Introduction
  2. Installation
  3. Features
    1. Attributes
      1. Type-checking
      2. Defaults
      3. Aliasing
      4. Custom getter & setter
      5. Inheritance
  4. Testing
  5. Development
  6. Contributing
  7. License

Introduction

Tutor adds some useful patterns and idioms to mixin to your classes.

e.g. Attributes with type and defaults

class Vertex; end
class Polygon
  include Tutor::Attributes

  attribute(:sides, type: Integer, default: 3)
  attribute(:vertices, default: lambda { |polygon| Array.new(polygon.sides) { Vertex.new } })
end

p = Polygon.new
p.intialize_attributes
p.sides # => 3
p.vertices # => [#<Vertex>, #<Vertex>, #<Vertex>]

### Installation

If you're not using Bundler...

Install the gem via:

gem install tutor

Then require it into your application with:

require 'tutor'
If you're using Bundler...

Add the gem to your Gemfile:

gem 'tutor'

And then bundle install to install the gem and its dependencies.

### Features

#### Attributes

Enable attributes on a class by including Tutor::Attributes module into the class/module you want to add attributes to.

class Polygon
  include Tutor::Attributes
end

Then add an attribute to add a name and name= method to the class.

class Polygon
  include Tutor::Attributes

  attribute(:sides)
end

Polygon.method_defined?(:sides) # => true
Polygon.method_defined?(:sides=) # => true

Of course, if this is all you need, then you might be better served just using attr_accessor. However, attribute gives you access to a few additional options.

##### Type-checking

attribute(:sides, type: Integer)

Add type-checking by adding the type option, and the class you want to type check against. Any value that is of that type, or inherits from it, will be permitted. Any other value will raise an ArgumentError.

By default, nil passes the type check. However, if you want to disallow nil values, you can set the nullable: false option.

attribute(:sides, type: Integer, nullable: false)

##### Defaults

attribute(:vertices, default: 3)

Adding the default option sets a default value for attribute, when initialize_attributes is called. To automatically set these defaults, add the function call to your initialize function.

def initialize
  initialize_attributes
end

You can also pass it a Hash of attributes, which will override any default values.

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

You can also set the default to a Proc or lambda. This is useful for non-static values, like class objects, where you want unique default objects per instance of your class.

# NOTE: The following two definitions are NOT equivalent.

# Sets default to the same object for all instances.
attribute(:vertices, default: Vertex.new)

# Sets default to the different object for each instance.
attribute(:vertices, default: -> { Vertex.new })

These blocks optionally can be defined to accept an argument. In which case, the object being initialized will be passed in. Any explicitly provided attribute values, or previously set defaults will be accessible.

attribute :vertices,
  default: lambda { |object| object.sides.times { Vertex.new } }

##### Aliasing

attribute(:nodes)
attribute(:vertices, alias: :nodes)

Alias any other attribute with the alias option, and the name of the method.

Custom getter & setter
attribute :vertices,
  get: lambda { |object| object.nodes },
  set: lambda { |object, value| object.nodes = value }

You can add custom get or set behavior for the attribute, by passing a Proc into the get or set options.

attribute :vertices,
  get: lambda { |object| object.nodes },
  set: false

Passing a false or nil value for either get or set will skip that method declaration.

##### Inheritance

class Angle; end

class Polygon
  include Tutor::Attributes
  attribute(:sides, type: Integer)
end

class Triangle < Polygon
  include Tutor::Attributes
  attribute(:angles, default: lambda { |o| o.sides.times { Angle.new } })
end

Triangle.new.initialize_attributes(sides: 3)
# => #<Triangle @sides=3, @angles=[ #<Angle>, #<Angle>, #<Angle>]>

Attributes can be initialized and accessed from inheriting classes. If an attribute name in a subclass conflicts with an already existing attribute or method, it will raise a NameError. You can, however, override the parent by passing the override option.

class Angle; end

class Polygon
  include Tutor::Attributes
  attribute(:sides, type: Integer, default: 3)
end

class Square < Polygon
  include Tutor::Attributes
  attribute(:sides, type: Integer, default: 4, override: true)
end

Square.new.initialize_attributes
# => #<Square @sides=4>

Testing

Description pending.

Development

Install dependencies using bundle install. Run tests using bundle exec rspec.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/delner/tutor.

License

The gem is available as open source under the terms of the MIT License.