snusnu / dm-is-remixable

A rewrite of dm-is-remixable that tries to feel more dm'ish and adds desired behavior

This URL has Read+Write access

commit  051c7e4f82c166f052c2afc3274ede4530f8fa61
tree    7397981d4fc5827dd8abc6be1fea7d469bbf2fbe
parent  d863b35b5ba0d7cc307fc26e1638c24da6c5f72f
README.rdoc

dm-is-remixable

A rewrite of the wonderful dm-is-remixable gem by Cory O’Daniel.

Features

  # API that closely matches that of datamapper's +has+ method.
  # Relationships can be declared directly inside the remixable module.
  # Full support for remixing models as intermediates in m:m relationships (multiple times in the same class)
  # A small, readable and easy to understand codebase.

Goals (roughly in that order)

  # [NEEDS SPECS] Allow to pass additional query options to the generated relationships to/through remixables
  # [NEEDS SPECS] Allow to remix other remixables inside the remixable module
  # Think about how best to include/extend functionality into the remixed and the remixing model
  # Allow to remix target or intermediate models without establishing any relationships
  #   This will allow to naturally define different relationships to the same remixed model without calling remix
  # Full support for CPKs (by allowing to declare full FK property options in :source_key and :target_key options)
  # Allow remixing a parent model (i.e. the remixer belongs_to the remixed model)
  # Add back support for enhance (although it shouldn't be needed that often anymore)
  # think about possibilities to integrate with Cory O'Daniel's dm-property-manager (to get that polymorphic feeling)

Status

  # 136 examples, 0 failures
  # 97.2%   3 file(s)   259 Lines   176 LOC

API usage (not cut into stone, but promising)

  module Addressable

    extend DataMapper::Model

    is :remixable

    property :id,      Serial
    property :address, String, :nullable => false

    belongs_to :country
    has n, :phone_numbers

  end

  module Linkable

    extend DataMapper::Model

    is :remixable

    property :id,         Serial

    property :created_at, DateTime
    property :updated_at, DateTime

  end

  class Link

    include DataMapper::Resource

    property :id,  Serial
    property :uri, URI

  end

  class Country

    include DataMapper::Resource

    property :id,   Serial
    property :name, String

    has n, :person_addresses

  end

  class PhoneNumber

    include DataMapper::Resource

    property :id,     Serial
    property :number, String

    belongs_to :person_address

  end

  class Person

    include DataMapper::Resource

    property :id,   Serial
    property :name, String

  end

  #
  # Relate a remixable module to the receiving class.
  #
  # @param [Fixnum] cardinality
  # @param [Symbol,String] relationship_name
  # @param [Hash] options
  #
  # @option options [Symbol,String,Model] :remixable
  # @option options [Symbol,String,Model] :model
  # @option options [Symbol,String,Model,Hash] :through
  #
  #
  # @example 1:1 remixable with default naming conventions
  #
  #   Person.remix 1, :address, 'PersonAddress', :remixable => :addressable
  #   OR
  #   Person.remix 1, :address, 'PersonAddress', :remixable => 'Addressable'
  #   OR
  #   Person.remix 1, :address, 'PersonAddress', :remixable => Addressable
  #
  #   1) Defines the PersonAddress model and includes the Addressable module.
  #   2) Establishes the following relationships on the participating models.
  #
  #   Person.has 1, :address, 'PersonAddress', :target_key => [:person_id]
  #   PersonAddress.belongs_to :person, 'Person'
  #
  #
  #
  # @example 1:1 remixable with explicit options
  #
  #   Person.remix 1, :address, 'PersonAddress',
  #     :remixable  => :addressable,
  #     :target_key => [:human_id]
  #
  #   OR
  #
  #   Person.remix 1, :address, 'PersonAddress',
  #     :remixable  => 'Addressable',
  #     :target_key => [:human_id]
  #
  #   OR
  #
  #   Person.remix 1, :address, 'PersonAddress',
  #     :remixable  => Addressable,
  #     :target_key => [:human_id]
  #
  #   1) Defines the PersonAddress model and includes the Addressable module.
  #   2) Establishes the following relationships on the participating models.
  #
  #   Person.has 1, :address, 'PersonAddress', :target_key => [:human_id]
  #   PersonAddress.belongs_to :human, 'Person'
  #
  #
  #
  # @example 1:m remixables with default naming conventions
  #
  #   Person.remix n, :addresses, 'PersonAddress', :remixable => :addressable
  #   OR
  #   Person.remix n, :addresses, 'PersonAddress', :remixable => 'Addressable'
  #   OR
  #   Person.remix n, :addresses, 'PersonAddress', :remixable => Addressable
  #
  #   1) Defines the PersonAddress model and includes the Addressable module.
  #   2) Establishes the following relationships on the participating models.
  #
  #   Person.has n, :addresses, 'PersonAddress', :target_key => [:person_id]
  #   PersonAddress.belongs_to :person, 'Person'
  #
  #
  #
  # @example 1:m remixables with explicit options
  #
  #   Person.remix n, :addresses, 'PersonAddress',
  #     :remixable  => :addressable,
  #     :target_key => [:human_id]
  #
  #   OR
  #
  #   Person.remix n, :addresses, 'PersonAddress',
  #     :remixable  => 'Addressable',
  #     :target_key => [:human_id]
  #
  #   OR
  #
  #   Person.remix n, :addresses, 'PersonAddress',
  #     :remixable  => Addressable,
  #     :target_key => [:human_id]
  #
  #   1) Defines the PersonAddress model and includes the Addressable module.
  #   2) Establishes the following relationships on the participating models.
  #
  #   Person.has n, :addresses, 'PersonAddress', :target_key => [:human_id]
  #   PersonAddress.belongs_to :human, 'Person'
  #
  #
  #
  # @example m:m through remixable with default naming conventions
  #
  #   Person.remix n, :references, 'Link', :through => :linkable
  #   OR
  #   Person.remix n, :references, 'Link', :through => 'Linkable'
  #   OR
  #   Person.remix n, :references, 'Link', :through => Linkable
  #
  #   1) Defines the PersonReference model and includes the Linkable module.
  #   2) Establishes the following relationships on the participating models.
  #
  #   PersonReference.belongs_to :person
  #   PersonReference.belongs_to :link
  #
  #   Person.has n, :person_references, 'PersonReference'
  #   Link.has   n, :person_references, 'PersonReference'
  #
  #   Person.has n, :references, 'Link', :through => :person_references, :via => :link
  #
  #
  #
  # @example m:m through remixable with explicit options
  #
  #   Person.remix n, :references, 'Link',
  #     :through => [ :person_references, {
  #       :remixable  => :linkable,
  #       :model      => 'PersonReferenceLink', # defaults to 'PersonReference'
  #       :source_key => [:human_id],           # defaults to :person_id
  #       :target_key => [:reference_id]        # defaults to :link_id
  #     }]
  #
  #   OR
  #
  #   Person.remix n, :references, 'Link',
  #     :through => [ :person_references, {
  #       :remixable  => 'Linkable',
  #       :model      => 'PersonReferenceLink', # defaults to 'PersonReference'
  #       :source_key => [:human_id],           # defaults to :person_id
  #       :target_key => [:reference_id]        # defaults to :link_id
  #     }]
  #
  #   OR
  #
  #   Person.remix n, :references, 'Link',
  #     :through => [ :person_references, {
  #       :remixable  => Linkable,
  #       :model      => 'PersonReferenceLink', # defaults to 'PersonReference'
  #       :source_key => [:human_id],           # defaults to :person_id
  #       :target_key => [:reference_id]        # defaults to :link_id
  #     }]
  #
  #   1) Defines the PersonReference model and includes the Linkable module.
  #   2) Establishes the following relationships on the participating models.
  #
  #   PersonReferenceLink.belongs_to :human, 'Person'
  #   PersonReferenceLink.belongs_to :reference, 'Link'
  #
  #   Person.has n, :person_links, 'PersonReferenceLink', :target_key => [:human_id]
  #   Link.has   n, :person_links, 'PersonReferenceLink', :target_key => [:reference_id]
  #
  #   Person.has n, :references, 'Link', :through => :person_references, :via => :reference
  #
  #
  #
  # @example self referential m:m through remixable (probably needs more work)
  #
  #   Person.remix n, :friends, 'Person',
  #     :through => [ :social_contacts, {
  #       :remixable  => :social_contact,
  #       :model      => 'Friendship',      # defaults to 'PersonReference'
  #       :source_key => [:person_id],      # defaults to :person_id
  #       :target_key => [:other_person_id] # defaults to :person_id
  #     }]
  #
  #   OR
  #
  #   Person.remix n, :friends, 'Person',
  #     :through => [ :social_contacts, {
  #       :remixable  => 'SocialContact',
  #       :model      => 'Friendship',      # defaults to 'PersonReference'
  #       :source_key => [:person_id],      # defaults to :person_id
  #       :target_key => [:other_person_id] # defaults to :person_id
  #     }]
  #
  #   OR
  #
  #   Person.remix n, :friends, 'Person',
  #     :through => [ :social_contacts, {
  #       :remixable  => SocialContact,
  #       :model      => 'Friendship',      # defaults to 'PersonReference'
  #       :source_key => [:person_id],      # defaults to :person_id
  #       :target_key => [:other_person_id] # defaults to :person_id
  #     }]
  #
  #   1) Defines the PersonReference model and includes the Linkable module.
  #   2) Establishes the following relationships on the participating models.
  #
  #   Friendship.belongs_to :person, 'Person'
  #   Friendship.belongs_to :other_person, 'Person'
  #
  #   Person.has n, :friendships, :model => 'Friendship', :target_key => [:person_id]
  #   Link.has   n, :friendships, :model => 'Friendship', :target_key => [:other_person_id]
  #
  #   Person.has n, :friends, :model => 'Person', :through => :friendships, :via => :other_person
  #
  def remix(cardinality, relationship_name, *args)
    # ...
  end

Copyright

Copyright © 2009 Martin Gamsjaeger (snusnu). See LICENSE for details.