Module: AIXM::Concerns::Association

Overview

Associate features and components with a minimalistic implementation of has_many, has_one and belongs_to associations.

When adding or assigning an object on the associator (where the has_many or has_one declaration is made), the object is verified and must be an instance of the declared class or a superclass thereof.

When assigning an object on the associated (where the belongs_to declaration is made), the object is not verified. However, since the actual assignment is always delegated to the associator, unacceptable objects will raise errors.

Examples:

Simple has_many association

class Blog
  has_many :posts   # :post has to be a key in AIXM::CLASSES
end
class Post
  belongs_to :blog
end
blog, post = Blog.new, Post.new
# --either--
blog.add_post(post)        # => Blog
blog.posts.count           # => 1
blog.posts.first == post   # => true
post.blog == blog          # => true
blog.remove_post(post)     # => Blog
blog.posts.count           # => 0
# --or--
post.blog = blog           # => Blog
blog.posts.count           # => 1
blog.posts.first == post   # => true
post.blog == blog          # => true
post.blog = nil            # => nil
blog.posts.count           # => 0
# --or--
post_2 = Post.new
blog.add_posts([post, post_2])
blog.posts.count                    # => 2
blog.posts == [post, post_2]        # => true
blog.remove_posts([post_2, post])
blog.posts.count                    # => 0

Simple has_one association

class Blog
  has_one :posts   # :post has to be a key in AIXM::CLASSES
end
class Post
  belongs_to :blog
end
blog, post = Blog.new, Post.new
# --either--
blog.post = post      # => Post (standard assignment)
blog.add_post(post)   # => Blog (alternative for chaining)
blog.post == post     # => true
post.blog == blog     # => true
blog.post = nil       # => nil
blog.post             # => nil
post.blog             # => nil
# --or--
post.blog = blog      # => Blog (standard assignment)
post.add_blog(blog)   # => Post (alternative for chaining)
post.blog == blog     # => true
blog.post == post     # => true
post.blog = nil       # => nil
post.blog             # => nil
blog.post             # => nil

Association with readonly belongs_to (idem for has_one)

class Blog
  has_many :posts   # :post has to be a key in AIXM::CLASSES
end
class Post
  belongs_to :blog, readonly: true
end
blog, post = Blog.new, Post.new
post.blog = blog           # => NoMethodError

Association with explicit class (idem for has_one)

class Blog
  include AIXM::Concerns::Association
  has_many :posts, accept: 'Picture'
end
class Picture
  include AIXM::Concerns::Association
  belongs_to :blog
end
blog, picture = Blog.new, Picture.new
blog.add_post(picture)
blog.posts.first == picture   # => true

Polymorphic associator (idem for has_one)

class Blog
  has_many :posts, as: :postable
end
class Feed
  has_many :posts, as: :postable
end
class Post
  belongs_to :postable
end
blog, feed, post_1, post_2, post_3 = Blog.new, Feed.new, Post.new, Post.new, Post.new
blog.add_post(post_1)
post_1.postable == blog   # => true
feed.add_post(post_2)
post_2.postable == feed   # => true
post_3.postable = blog    # => NoMethodError

Polymorphic associated (idem for has_one)

class Blog
  include AIXM::Concerns::Association
  has_many :items, accept: ['Post', :picture]
end
class Post
  include AIXM::Concerns::Association
  belongs_to :blog, as: :item
end
class Picture
  include AIXM::Concerns::Association
  belongs_to :blog, as: :item
end
blog, post, picture = Blog.new, Post.new, Picture.new
blog.add_item(post)
blog.add_item(picture)
blog.items.count             # => 2
blog.items.first == post     # => true
blog.items.last == picture   # => true
post.blog == blog            # => true
picture.blog == blog         # => true

Add method which enriches passed associated object (has_many only)

class Blog
  has_many :posts do |post, related_to: nil|      # this defines the signature of add_post
    post.related_to = related_to || @posts.last   # executes in the context of the current blog
  end
end
class Post
  belongs_to :blog
  attr_accessor :related_to
end
blog, post_1, post_2, post_3 = Blog.new, Post.new, Post.new, Post.new
blog.add_post(post_1)
post_1.related_to             # => nil
blog.add_post(post_2)
post_2.related_to == post_1   # => true
blog.add_post(post_3, related_to: post_1)
post_3.related_to == post_1   # => true

Add method which builds and yields new associated object (has_many only)

class Blog
  include AIXM::Concerns::Association
  has_many :posts do |post, title:| end
end
class Post
  include AIXM::Concerns::Association
  belongs_to :blog
  attr_accessor :title, :text
  def initialize(title:)   # same signature as "has_many" block above
    @title = title
  end
end
blog = Blog.new
blog.add_post(title: "title") do |post|   # note that no post instance is passed
  post.text = "text"
end
blog.posts.first.title   # => "title"
blog.posts.first.text    # => "text"

Defined Under Namespace

Modules: ClassMethods Classes: Array

Class Method Summary collapse

Class Method Details

.included(base) ⇒ Object



267
268
269
# File 'lib/aixm/concerns/association.rb', line 267

def self.included(base)
  base.extend(ClassMethods)
end