How to model with inheritance a structure of People?

1

Hello, I'm having trouble doing my Person modeling. Theoretically it seems a very simple thing, but I do not find a way that follows, say, "good programming practice". My problem is this:

In my system I have a structure of several people, such as: User, Client, Supplier, Employee ... (among others). On top of all these classes I intend to have a Person class where all my "actors" will inherit. A Client type actor can also be a User and / or Supplier and / or any other actor (after all, this is what happens in real life).

I want to have only ONE table for Person, so that when I associate a Client with a Sale, I associate the Person ID.

Iwouldliketohave,forexample,thefollowingpossibilitieswithmyobjects:

->person=Person.new(name:"Jonn")
-> person.name #> "Jonn"
-> person.user #> nil

-> user = User.new(name: "Joe", login: "joe", password: "123456")
-> user.name #> "Joe"
-> user.login #> "joe"
-> user.person.user.login #> "joe"

Would anyone give me a hint on how I could do modeling for the database tables and encoding for the Models?

Thank you in advance.

    
asked by anonymous 07.05.2015 / 15:25

2 answers

1

Well, I'll post here the solution I found and meet all my needs.

Firstly I created the gem delegate_associations and added it to my project.

The next step was to adjust my migrations:

class CreatePeople < ActiveRecord::Migration
  def change
    create_table :people do |t|
      t.string :name
      t.string :cpf_cnpj    
      t.timestamps
    end
  end
end

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users, id: false do |t|
      t.primary_key :person_id
      t.string :login
      t.string :password
      t.timestamps
    end
  end
end

class CreateSellers < ActiveRecord::Migration
  def change
    create_table :sellers, id: false do |t|
      t.primary_key :person_id
      t.decimal :comission, default: 0.0
      t.timestamps
    end
  end
end

Notice that I removed the column id from User and Seller and added the person_id as the primary key. I did this so that when I associate for example a Seller to a sale, I will always have the id of Person which is the same as Seller .

After that I created 2 Modules:

# app/models/concerns/person_helper.rb
module PersonHelper
    extend ActiveSupport::Concern
    included do
        validates :name, presence: true
    end
end


# app/models/concerns/is_a_person.rb
module IsAPerson
    extend ActiveSupport::Concern
    included do
        include PersonHelper
        belongs_to :person, autosave: true
        delegate_associations to: :person
        delegate_attributes to: :person
        def self.find_by_id(value)
            find_by_person_id(value)
        end
        def person
            super || build_person
        end
    end    
end

My templates are as follows:

# app/models/person.rb
class Person < ActiveRecord::Base
    include PersonHelper

    has_one :user
    accepts_nested_attributes_for :user
    has_one :seller
    accepts_nested_attributes_for :seller
end

# app/models/user.rb
class User < ActiveRecord::Base
    include IsAPerson
    validates :login, :password, presence: true
end

# app/models/seller.rb
class Seller < ActiveRecord::Base
    include IsAPerson
end

Now all the validations and methods that a person has I will not put directly in the model of Person and yes in module PersonHelper so that my User and my Seller can also have the same things. / p>

With this I can have for example these possibilities:

> user = User.new(name: 'Nome da pessoa', login: 'admin', password: '123456')
> user.save # true
> user.person.persisted? # true
> user.seller #nil
> user.build_seller # Irá instanciar um Seller e associar com a Person
> user.seller.comission # 0.0

> user.name = 'Joe'
> user.person.name # Joe

Notice that it looks like User has the name attribute, and every time I set a new name for user it automatically moves to person . This happens by using the gem I made where I use the artifacts from the delegate .

Maybe this is not the most appropriate option, but it has met all my needs and works perfectly.

    
11.05.2015 / 14:51
1

Uses normal relationships 1-1 ( has_one / belongs_to ) or 1-n ( has_many / belongs_to ) as appropriate.

class Person < ActiveRecord::Base
  has_one :user
  has_one :client
  has_one :emplyee
end

class User < ActiveRecord::Base
  belongs_to :person
end

class Client < ActiveRecord::Base
  belongs_to :person
  has_many :sales
end

class Employee < ActiveRecord::Base
  belongs_to :person
end

class Sale < ActiveRecord::Base
  belongs_to :client
end

And then create a new user this way:

person = Person.new(name: "Joe")
person.user = User.new(login: "joe", password: "123456")

If you really need to create both records at once, you can use accepts_nested_attributes_for in the Person class:

class Person < ActiveRecord::Base
  has_one :user
  accepts_nested_attributes_for :user
end

person = Person.new(name: 'Joe', user_attributes: {login: "joe", password: "123456"})
    
08.05.2015 / 17:49