Ruby on Rails inheritance? [duplicate] - ruby

How to implement inheritance with active records?
For example, I want a class Animal, class Dog, and class Cat.
How would the model and the database table mapping be?

Rails supports Single Table Inheritance.
From the AR docs:
Active Record allows inheritance by
storing the name of the class in a
column that by default is named "type"
(can be changed by overwriting
Base.inheritance_column). This means
that an inheritance looking like this:
class Company < ActiveRecord::Base; end
class Firm < Company; end
class Client < Company; end
class PriorityClient < Client; end
When you do Firm.create(:name =>
"37signals"), this record will be
saved in the companies table with type
= "Firm". You can then fetch this row again using Company.find(:first, "name
= ‘37signals’") and it will return a Firm object.
If you don‘t have a type column
defined in your table, single-table
inheritance won‘t be triggered. In
that case, it‘ll work just like normal
subclasses with no special magic for
differentiating between them or
reloading the right type with find.
A pretty good tutorial is here: http://juixe.com/techknow/index.php/2006/06/03/rails-single-table-inheritance/

Models:
class Animal < ActiveRecord::Base; end
class Dog < Animal; end
class Cat < Animal; end
Migration:
class CreateAnimals < ActiveRecord::Migration
def self.up
create_table :animals do |t|
# Other attributes...
t.string :type
end
end
def self.down
drop_table :animals
end
end

ActiveRecord supports mapping inheritance hierarchies to a single table(Single-table inheritance. Table would have a column type which stores name of actual class and is used to select other class-specific columns.
It is possible to implement multi-table inheritance mapping, as shown here, but this particular way is not portable, AFAIK.

Delegated Types
One particular way of doing this is via Delegated Types - this makes sense only if you want to paginate all animals together, and to view cats and dogs together, then the delegated type is particularly useful. I also like it because you don't need to have empty columns, for where it doesn't make sense, as is the case with Single Table Inheritance solutions.
# Schema: entries[ id, created_at, updated_at, animalable_type, animalable_id ]
class Animal < ApplicationRecord
delegated_type :animalable, types: %w[ Cat Dog ]
end
module Animalable
extend ActiveSupport::Concern
included do
has_one :animal, as: :animalable, touch: true
end
end
# Schema: cats[ id, selfishness_level ]
class Cat < ApplicationRecord
include Animalable
end
# Schema: dogs[ id, favourite_game, wag_tail_level ]
class Dog < ApplicationRecord
include Animalable
end

Related

How can I set "global" variables that can be accessed in controllers and models in Rails

I have a table that has set entries. I would like to access those entries as variables in both my models and controllers without querying the database every time to set those variables.
I am able to get it to work by creating duplicate "concerns" for my models and controllers. I could also set global variables in my ApplicationController. Or i could initialize them in every place that I need them. What would be the correct rails way to set and access global variables that can be accessed in both controllers and models?
class ItemType
has_many :items
end
class Item
belongs_to :item_type
belongs_to :foo
end
class Foo
has_many :items
def build_item
bar_item_type = ItemType.find_by(:name => "bar")
self.items.build(
:foo_id => self.id,
:item_type_id => bar_item_type.id
)
end
end
class ItemsController
def update
bar_item_type = ItemType.find_by(:name => "bar")
#item.update(:item_type_id => bar_item_type.id)
end
end
In the example, you can see that I am declaring the bar_item_type variable in both my Foo model and my ItemsController. I would like to DRY up my code base by being able to create and access that variable once for my rails project instead of having to make that same database call everywhere.
I would advocate against such hard-coded or DB state-dependent code. If you must do it, here's how one of the ways I know it can be done:
# models
class ItemType < ActiveRecord::Base
has_many :items
# caches the value after first call
def self.with_bar
##with_bar ||= transaction { find_or_create_by(name: "bar") }
end
def self.with_bar_id
with_bar.id
end
end
class Item < ActiveRecord::Base
belongs_to :item_type
belongs_to :foo
scope :with_bar_types, -> { where(item_type_id: ItemType.with_bar_id) }
end
class Foo < ActiveRecord::Base
has_many :items
# automatically sets the foo_id, no need to mention explicitly
# the chained with_bar_types automatically sets the item_type_id to ItemType.with_bar_id
def build_item
self.items.with_bar_types.new
end
end
# Controller
class ItemsController
def update
#item.update(item_type_id: ItemType.with_bar_id)
end
end
If you MUST use a constant, there are a few ways to do it. But you must take into account that you are instantiating an ActiveRecord model object which is dependent on data being present in the database. This is not recommend, because you now have model and controller logic relying on data being present in the database. This might be ok if you have seeded your database and that it won't change.
class ItemType
BAR_TYPE ||= where(:name => "bar").limit(1).first
has_many :items
end
Now where ever you need this object you can call it like this:
bar_item_type = ItemType::BAR_TYPE

You can have_one if you're true

In my website (written with sinatra) I am trying to set up a database. I have 2 tables, here referred to as Table1 and Table2.
models.rb
class Table1 < ActiveRecord::Base
Table1.where(bool:true) has_one :table2 # PSUDO-CODE
# So that every record where bool:true has the relationship
# but every record where bool:false or bool:nil doesn't
end
class Table2 < ActiveRecord::Base
belongs_to :table1
end
I am trying to find a way to make the section labeled PSUDO-CODE into actual code. How can I do that?
You can't do this directly: a class either has a relationship or it doesn't (although of course there may be no associated record)
You can set conditions on an association, but to the best of my knowledge you can only really set conditions on the associated collection (i.e. table 2 in this case)
You can however override the generated method, so for example
class Table1 < ActiveRecord::Base
has_one :table2
def table2(*args)
bool ? super : nil
end
end
This works with current versions of activerecord - not how far back this is supported (older version defined the association methods directly on the class so you couldn't call super)

Clean association definitions with Ruby Sequel

I am using Jeremy Evan's Sequel to populate an (SQLite) database with data I scrape from web pages.
The database involves a number of many_to_many relationships that I express with Associations.
The associations are created in class definitions, which are always evaluated when the script is run.
Importantly, the association class definitions need to have the necessary tables in place.
Thus the table creation methods should be in the top level with the association definitions.
Here is an example:
module Thing
db = Sequel.Sqlite('data.sqlite')
db.create_table(:clients)
String :client_id, :primary_key => true
String :client_data
end
db.create_table(:orders)
String :order_id, :primary_key => true
String :order_data
end
db.create_table(:orders_clients)
String :order_id
String :client_id
primary_key [:order_id,:client_id]
end
class Person < Sequel::Model
unrestrict_primary_key
many_to_many :orders
end
class Order < Sequel::Model
unrestrict_primary_key
many_to_many :orders
end
end
First of all, I think that this is a rather dirty solution, since my method calls and class definitions sit in the same namespace.
If I try to separate the class definitions, I get No database associated with Sequel::Model error (which makes sense, but I want to defer the evaluation of the association definitions, having those after the table calls, whenever they might happen).
I want to be able to create the tables and associations in a method call. Thus, I could for example pass the name of the new database file:
def create_tables_and_schema (database_name)
db = Sequel.Sqlite(database_name)
db.create_table... #three of those, as above
class Person < Sequel::Model
unrestrict_primary_key
many_to_many :orders
end
class Order < Sequel::Model
unrestrict_primary_key
many_to_many :orders
end
end
What I think is needed is a different way to express table relations.
Any suggestions on approach and style are appreciated. Please ask for clarifications if the explanation is confusing.
Your method calls and class definitions do not need to sit in the same namespace, it's just that the tables need to be created before the model classes. An easy way to separate them is to move the table creation to a separate file. Also, usually you assign the database object to a constant.
create_tables.rb:
DB.create_table(:clients)
String :client_id, :primary_key => true
String :client_data
end
DB.create_table(:orders)
String :order_id, :primary_key => true
String :order_data
end
DB.create_table(:orders_clients)
String :order_id
String :client_id
primary_key [:order_id,:client_id]
end
models.rb:
DB = Sequel.sqlite('data.sqlite')
require 'create_tables'
class Person < Sequel::Model
unrestrict_primary_key
many_to_many :orders
end
class Order < Sequel::Model
unrestrict_primary_key
many_to_many :orders
end
You mentioned that you want to create the tables and associations in a method call, but that doesn't make sense if you are creating classes with constants. The main reason to create them via a method call is to allow for multiple databases at runtime, but that wouldn't work with your model classes since they are defined with constant names.
If you don't need multiple databases at runtime, the example above should work if you just want to separate the table creation from the model creation.
If you do need multiple databases at runtime, then creating the tables and models via a method call makes sense, but you need to create anonymous model classes, as otherwise you will have problems.

Rails 3 scope only select certain attributes for a has_many relationship

This looks like it should be something pretty easy but I can't seem to get it to work. I have a model with a has_many relationship and I'd like a scope on the parent that allows me to select only certain attributes for each.
An example:
class Bakery < ActiveRecord::Base
has_many :pastries
scope :summary, select([:id, :name, 'some option calling pastries.summary'])
class Pastry < ActiveRecord::Base
belongs_to :bakery
scope :summary, select([:id, :image_url])
I'd like to be able to call something like Bakery.first.summary and get a Bakery model with only the id and name populated and for each pastry in it's pastries array to only have the id and image_url attributes populated.
You could do this, but it won't affect the SQL queries that are made as a result (assuming you're trying to optimise the underlying query?):
class Pastry
...
def summary
{
:id => self.id,
:image_url => self.image_url
}
end
end
class Bakery
...
def summary
pastries.collect {|i| i.summary }
end
end
This would then give you an array of hashes, not model instances.
ActiveRecord doesn't behave how you're expecting with models - it will fetch whatever data it thinks you need. You could look at using the Sequel gem instead, or executing a raw SQL query such as:
Pastry.find_by_sql("SELECT id, name from ...")
But this could give you unexpected behaviour.

How to Validate Rails 3 Relational Model?

New to Rails and ActiveRecord, not sure how I can apply validation though multiple levels of relational model. I've searched all the tutorials that I can find are all validation in just one model.
How can I validate multiple relational models ?
Say I have multiple models, one model called field stores different type of attribute in sports game.(ex. final score, goals for, goals against etc) and another model value that stores all the actual values like game scores into the field
So I will have 2 classes
class Field < ActiveRecord::Base
has_many :value
end
class Value < ActiveRecord::Base
belongs_to :field
end
How can I validate the value of each attributes in value model that are corresponding to it's field ?
In rails 3 : You can create a file in your lib directory (ensure that that this file is loaded by your application) and put in a class with a validate method :
class HumanValidator < ActiveModel::Validator
def validate(record)
record.errors[:base] << "This person is dead" unless check(human)
end
private
def check(record)
(record.age < 200) && (record.age > 0)
end
end
In all model you want to validate, you invoke the validation with the of the class like this:
# app/models/person.rb
class Customer < ActiveRecord::Base
validates_with HumanValidator
end
Go read this, for validate novelty in rails 3, soure link text

Resources