calling child class method in parent class method - ruby-on-rails

I have rails 4 application and this models
class Product < AR::Base
default_scope -> { where(product_type: self.to_s) }
after_initialize { self.product_type = self.class.to_s }
end
and many others like this
class Orange < Product #Apple, Carrot, ...
end
if i call in console >> Orange.new, the callback after_initialize works like expected: it sets instance attribute product_type to Orange, so self.class is determined as Orange class like i need
but
if i call >> Orange.all, the method self.to_s inside default_scope is applied to Product class =(
So, here is the question: how can i use Orange class name inside default_scope in parent class (Product) in case i do not want to write any methods inside Orange class (because there are many subclasses like Orange and i want to leave everything DRY).
And so on, if i have Apple class, it name has to be used for filtering all Product with default_scope if i call >> Apple.all (some kind of polymorphic association)
thank you.

Just use STI here:
class Product < ActiveRecord::Base
self.inheritance_column = :product_type
end
class Orange < Product
end
# then...
> Orange.all
Orange Load (0.1ms) SELECT "products".* FROM "products" WHERE "products"."product_type" IN ('Orange')
Ie. don't do the default_scope or the after_initialize, because Rails already has you covered as soon as you inherit from another model Rails will assume you're using STI and it will add the right thing in the query.

Related

Ruby parent object gain access to child attributes

So I am wondering if there is a better way of doing this. I have a parent class that has three 1 to 1 relationships, children.
class Parent
has_one :child_one
has_one :child_two
has_one :child_three
...
end
Then within each child object I state belongs_to :parent, now ChildOne has attribute1, attribute2, ..., attribute30 etc.
I am using a gem that uses yaml to build calculations, but it can only access the table or model of the Parent class. It means that all the attributes i need from the ChildOne class I'll have to pull like this
def calculation_one
cal_one = self.child_one.attribute1
end
and onward. This would mean that I would have a model that's freakin fat just linking children attributes. Is there a better way of doing this?
Update
What i am looking for is a way to basically attr_accessor a subclass?
class Parent
attr_accessor :child_one, :attribute1
end
person = Parent.new
person.attribute1 = "awesome"
person.attribute1 # => "awesome"
I think what you're looking for is the Delegate module in rails : you can call delegate to let a related model respond to a method call like that :
class Parent < ActiveRecord::Base
has_one :child_one
delegate :attribute1, to: :child_one
end
Full documentation : http://apidock.com/rails/Module/delegate

Associations: one to many with existing table

Rails beginner here:
I already have a database and table, so the naming convention is giving me some headaches
class Item < ActiveRecord::Base
belongs_to :categorie, :foreign_key => "catid"
end
class Categorie < ActiveRecord ...
has_many :item
end
i = Item.first # Ok
c = i.Categorie # Ok, finds proper Categorie based on "catid" of i
c.Item # fails with Categorie_id column not found ! how can i map Categorie_id to "catid"?
You're a rails beginner but you might not be a programmer beginner so I'll dive in and explain classes a little bit.
A class is simply a data object that holds methods. Nothing more. Here's a simple one that holds one method:
class Cow
def talk
"moo"
end
end
Cow is the class, talk is the method. Now, if we have the above classes in memory, we cannot do this in the console:
talk
Because that method isn't available at the global scope. This is a good thing, because this could cause bugs and is inefficient. Imagine if we have a few animals:
class Cat
def talk
"meow"
end
end
class Dog
def talk
"woof"
end
end
Running talk, how would the computer know which talk to run? Instead, we call the method that's inside the class like this:
Cow.talk #=> "moo"
Cat.talk #=> "meow"
Dog.talk #=> "woof"
Hopefully now, this code:
Item.first
is less cryptic. Item is a class, and first is a method available inside that class.
Now I know Item is a model, but in rails, models are simply classes that inherit a bunch of useful methods from ActiveRecord. At the top of the Item model you should see this:
class Item < ActiveRecord::Base
That's what pulls in all of the useful methods, such as the first method we're using. Because of this inheritance, we can imagine your Item class looks a bit like this:
class Item < ActiveRecord::Base
def first
# code is in here that queries the table in your database that has
# the downcased and pluralized name of Item (so items) and returns the first
# row of that table
end
# down here is all of your methods you've probably created. Validations and the like.
end
first, rather than return a string like in my example does something far more useful; it queries the table in your database that has the downcased and pluralized name of its class. So Item.first queries the items table, and returns the first row.
Now, I have to be honest, despite what you say, I find it highly doubtful that i.Categorie finds the proper Categorie based on the "catid" of i. If it truly does I feel you've done some crazy workaround to get that working. This is what should happen:
i.Categorie
NoMethodError: undefined method `Categorie' for #<Item:0x00000005905830>
In plain English, this means
NoMethodError: there is no 'Categorie' method inside that instance of the 'Item' class.
And this makes sense because I see no 'Categorie' method in here:
class Item < ActiveRecord::Base
def first
# code is in here that queries the table in your database that has
# the downcased and pluralized name of Item (so items) and returns the first
# row of that table
end
# down here is all of your methods you've probably created. Validations and the like.
end
Now the reason c.Item doesn't work is because c is set to nil because nil was returned by i.Categorie due to the non-method error, and nil certainly doesn't have the method Item inside it.
c = i.Categorie # c is set to nil due to noMethodError
c.Item
NoMethodError: undefined method `Item' for nil:NilClass
Hopefully you understand a bit more what's going on now. If you want your code to work you should be doing this. Look closely, there are a few nuances:
i = Item.first # i is set to the first instance of Item
c = i.categorie # c is set to the instance of Categorie that i belongs to
is = c.items # returns an array consisting of all the Item instances that belong to the Categorie instance in c
We could also do this:
is.first # returns i
So where do all these handy methods come from? The categorie method inside i (i.category), the items method inside c (c.items)? The answer is they're created dynamically by Rails based on your inheritance and pulled into the relevant model by < ActiveRecord::Base.
By "based on your inheritance" I mean, how you've used the inheritance methods, belongs_to and has_many:
class Item < ActiveRecord::Base
belongs_to :categorie, :foreign_key => "catid" # creates categorie method that returns the instance of Categorie this instance of Item belongs to
end
class Categorie < ActiveRecord ...
has_many :item # creates items method that returns an array of all the instances of Item that belong to this instance of Categorie
end
I would also point out that Categorie is a pretty terrible Model name, purely because it's spelt wrongly. Maybe Type would be better?
You can do
Item.create (:catid => #categorie.id)
#categorie = Categorie.find(params[:id]) or with Categorie.all
place the each loop & find the id .
First you should have used Category for model because rails intelligently understands the plural categories or tables.
Secondly, you should have something like this;
class Item < ActiveRecord::Base
belongs_to :Category, :foreign_key => "catid"
end
class Categorie < ActiveRecord ...
has_many :items
end
i = Item.first
c = i.Category
c.items #to find all items that belong to the category c

ActiveRecord Multi Level Single Table Inheritance Query

I am trying to use Single Table Inheritance to query for all records within a class hierarchy from a class that is not the base class for the single table inheritance. For example, given the following class heirarchy.
class Animal < ActiveRecord::Base; end
class Dog < Animal; end
class Mutt < Dog; end
class PureBred < Dog; end
I want to be able to query for all of the dogs
dogs = Dog.all
and dogs be a list of instances of Mutt and PureBred.
Can this be done? I tried a proof of concept by setting the default_scope in the Dog class to
default_scope { where(:type => ['Mutt', 'PureBred']) }
but ActiveRecord is still appending the more restrictive condition of WHERE type IN ('Dog')
No; you're running up against a technical limitation of Rails' implementation of STI.
You'll have to query the base class:
Animal.where(type: %w(Dog Mutt PureBred))

Rails: List only direct subclasses of ActiveRecord::Base subclass

I am trying to get all the direct subclasses of a class that inherits from ActiveRecord::Base; that is, only classes that inherit directly from the base class, not subclasses of subclasses. But the normal way of doing this does not appear to work with ActiveRecord::Base subclasses.
Normally, this is done using the Class::subclasses method:
class C1; end
class C2 < C1; end
class C3 < C2; end
C1.subclasses
#=> [C2]
I want to do this for an ActiveRecord::Base subclass:
class T1 < ActiveRecord::Base; end
class T2 < T1; end
class T3 < T2; end
T1.subclasses
#=> [T2(Table doesn't exist), T3(Table doesn't exist)]
I get both the child and grandchild classes, which is not what I want! The same fundamental behavior occurs for classes that do have tables defined.
Already, this points out that ActiveRecord::Base subclasses act a little differently in that inspect() is overridden to provide the table name. So it's not too far a stretch to guess that they overrode subclasses as well.
Is it possible to get a list only of the direct subclasses of an
ActiveRecord::Base subclass?
And why did they change subclasses()?
According to this:
http://apidock.com/rails/ActiveRecord/Base/subclasses/class
ActiveRecord::Base had a subclasses method that simply returned the value of descendants until version 3.1 (actually this says 3.0.9, but I think this is when it was deprecated; you can see it in the source code until 3.1). descendants returns everything that is < the receiver (the behavior you observed).
Here is the commit that removed it:
https://github.com/rails/rails/commit/9b610049bb4f73dbcdc670879683ec2a1a2ab780
Rails 3.1 and after should behave as you describe. If you are using Rails >= 3.1, then I'm not sure how to explain what you are seeing.
Reposted code from my comment above because comments don't allow good formatting. This works for my purposes:
class T1 < ActiveRecord::Base
def self.my_subclasses
Object.singleton_class.instance_method(:subclasses).bind(self).call
end
end

How to dynamically set a belongs_to association in ActiveRecord?

I'm trying to create a mixin that allows an ActiveRecord model to act as a delegate for another model. So, doing it the typical way:
class Apple < ActiveRecord::Base
def foo_species
"Red delicious"
end
end
class AppleWrapper < ActiveRecord::Base
belongs_to :apple
# some meta delegation code here so that AppleWrapper
# has all the same interface as Apple
end
a = Apple.create
w = AppleWrapper.create
w.apple = a
w.foo_species
# => 'Red delicious'
What I want is to abstract this behavior into a Mixin, so that given a bunch of data models, I can create "Wrapper" classes that are also ActiveRecords, but that each wrapper corresponds to a specific class. Why? Each of the data models have calculations, aggregations with other models, and I want the "Wrapper" classes to contain fields (in the schema) that correspond to these calculations...so in effect. the Wrapper acts as a cached version of the original data model, with the same interface.
I will have to write out each Wrapper...so for Apple, Orange, Pear, there is a different Wrapper model for each of them. However, I just want to abstract out the wrapper behavior...so that there's a class level method that sets what the Wrapper points to, a la:
module WrapperMixin
extend ActiveSupport::Concern
module ClassMethods
def set_wrapped_class(klass)
# this sets the relation to another data model and does the meta delegation
end
end
end
class AppleWrapper < ActiveRecord::Base
include WrapperMixin
set_wrapped_class Apple
end
class OrangeWrapper < ActiveRecord::Base
include WrapperMixin
set_wrapped_class Orange
end
How would I set this up? And would this have to be a STI type relation? That is, does the Wrapper class have to have a wrapped_record_id and a wrapped_record_type?
You can use belongs_to in your set_wrapped_class method.
def set_wrapped_class(klass)
belongs_to klass.to_s.downcase.to_sym
end

Resources