acts_as_relatable

Installation

(This gem has only been tested with Rails 3 for the moment, compatibility with Rails 3.1 is coming soon)

Gem installation :

gem install acts_as_relatable

Or you can put it in your Gemfile :

gem "acts_as_relatable", "~> 0.0.5"

and then run :

bundle install

You’ll then need to run the migration generator for the Relationship model :

r generate acts_as_relatable:migration

Followed by :

rake db:migrate

Usage

# Model definition

class Tool < ActiveRecord::Base
  acts_as_relatable :product
end

class Product < ActiveRecord::Base
  acts_as_relatable :recipe
end

class Recipe < ActiveRecord::Base
  acts_as_relatable :product, :tool
end

# Create some entries

@bread = Product.create(:name => "Bread")
@butter = Product.create(:name => "Butter")
@pancake = Recipe.create(:name => "Pancakes")
@mixer = Tool.create(:name => "Mixer")

Creating relationships

@butter.relates_to!(@pancake)   # #<ActsAsRelatable::Relationship id: 2, relator_id: 2, relator_type: "Product", related_id: 1, related_type: "Recipe">
@pancake.relates_to!(@mixer)    # #<ActsAsRelatable::Relationship id: 3, relator_id: 1, relator_type: "Recipe", related_id: 1, related_type: "Tool">
@bread.relates_to!(@butter)     # #<ActsAsRelatable::Relationship id: 4, relator_id: 1, relator_type: "Product", related_id: 2, related_type: "Product">

# By default, relationships are both-sided, it means that on the first line above,
# @butter is related to @pancake, but @pancake is also related to @butter.
# If you don't want/need this behaviour, you can pass false as a second argument
# to the relates_to! instance method :

@butter.relates_to!(@pancake, false)

# You can also create relationships with the ActsAsRelatable::Relationship class method

ActsAsRelatable::Relationship.make_between(@butter, @pancake)  # Passing false as a third argument make it "one sided"

Fetching relationships

@butter.related_recipes         # [#<Recipe id: 1, name: "Pancakes">]
@butter.related_products        # [#<Product id: 1, name: "Bread"]

@butter.relateds                # {:recipes=>[#<Recipe id: 1, name: "Pancakes">], :products=>[#<Product id: 1, name: "Bread">]}
@pancake.relateds               # {:tools=>[#<Tool id: 1, name: "Mixer">], :products=>[#<Product id: 2, name: "Butter">]}

#Testing relationships

@butter.related_to? @bread      # true
@bread.related_to? @pancake     # false

#Destroying relationships (This instance method destroys both relationships if it's a both-sided one)

@butter.destroy_relation_with @pancake

Getting Relatable types

@pancake.relatable_types        #  [:product, :tool]

Configuration

To create the acts_as_relatable config file, open a shell and run :

r g acts_as_relatable:config

It will create a file called acts_as_relatable.rb in config/initializers

Relationship Shortcut

By default, the Relationship model is accessible with :

ActsAsRelatable::Relationship
ActsAsRelatable::Relationship.count

If you want to access it directly with

Relationship
Relationship.first

Just uncomment this line in the config file (see Configuration section above to see how to generate it)

Relationship = ActsAsRelatable::Relationship

Relationship model extension

You can add class/instance methods to the ActsAsRelatable::Relationship model by adding a module in your project. Here is how to do it :

Add a new file named relationship_extension.rb in your app/models directory.

Let’s call this module RelationshipExtension. The file may look like :

module RelationshipExtension
  def self.included(klass)
    klass.extend(ClassMethods).relate
  end

  module ClassMethods

    def relate
      include RelationshipExtension::InstanceMethods
    end

  end

  module InstanceMethods
    def to_s
      "#{relator.class} #{relator.id} is related with #{related.class} #{related.id}"
    end
  end
end

Generate the config file (see Configuration section above to see how to generate it)

Open this file and uncomment this line :

ActsAsRelatable::Relationship.send(:include, RelationshipExtension)

I define a to_s instance method in this example. It is now a part of the ActsAsRelatable::Relationship model and you can use it like that :

ActsAsRelatable::Relationship.first.to_s

it will return :

"Product 1 is related with Recipe 1"

Contributing to acts_as_relatable

  • Check out the latest master to make sure the feature hasn’t been implemented or the bug hasn’t been fixed yet

  • Check out the issue tracker to make sure someone already hasn’t requested it and/or contributed it

  • Fork the project

  • Start a feature/bugfix branch

  • Commit and push until you are happy with your contribution

  • Make sure to add tests for it. This is important so I don’t break it in a future version unintentionally.

  • Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.

TODOS

  • Rails 3.1 compatibility

  • Add a make_between method in Relationship model

  • Improve README

  • Specs for generators

Copyright © 2011 Malamitsas. See LICENSE.txt for further details.