What is the proper way to validate the type when using single table inheritance (STI)? - ruby-on-rails

I am trying to use single table inheritance for some of my models. The base model is a Tournament, and I wish to extend this to create different types of tournaments. For instance, I might want to add a SingleEliminationTournament, or a DoubleEliminationTournament, both of which would inherit from Tournament. I have 2 questions, both of them somewhat related.
1) I would like the user to be able to create tournaments with a form, and to do this they would need to select one of the subclasses. Is there a way to get all of the subclasses and use them to populate a select box or something like that?
2) Since this information is going into a form, it would be nice to be able to validate the input into type. To do this, I would like to add a validation in the Tournament class that could check to make sure the Type was valid.
Obviously, I could hard code the values into the validation and the form, but I would not like to do that. Any help would be appreciated. Thanks!

TheModel.subclasses
would give you a list of types you need to include, but only if the models are loaded at runtime. They will always be loaded in production mode. You will have to load them manually in development mode.

You could create a directory with tournaments in them and load them with Dir.glob('app/tournaments/**/*_tournament.rb'). This gives you a nice listing all the tournament files you've specified. Because of convention, you can then infer the proper class name for each tournament.
Store this list of tournament names somewhere for reference in you validations and forms.

I'm not a Rails expert and I'm not sure if this can be considered clean, but for the validation part of your question, this worked for me:
Inside Tournament model:
def validate_type_implemented
klass = type.constantize rescue Object
raise "Given type not available." unless klass.class == Class and klass <= self.class
end

Related

How to handle a single form to update multiple unrelated models?

I am working on a legacy Rails 3.2 application that has a lot of settings a user can manage. Settings are associated with 3 types of model in the system: User, Company and CompanyUser. In order to avoid having to write database migrations each time a new type of setting is added
I've essentially created a key/value store (1 row for each setting) that has a polymorphic association with each of the above mentioned models. A base Setting class handles all of the common functionality like setting the key, relationships etc. each type of setting extends the base class and can contain it's own validation and/or logic. For example:
class Settings::EmailSignature < Setting
validates :whatever
end
For any model that requires a setting I've implemented a has_setting helper method that sets up the association and provides some delegates to directly get and set the setting without needing to go via the associated model object, the User model might look like:
class User < ActiveRecord::Base
has_setting :email_signature
end
This side of the code is working well, however the problem I have is when I create the form for the settings. For the user it might make sense to have User, Company and CompanyUser settings mixed together in the same form. Using nested attributes doesn't feel like a good solution in this situation as the settings are not related and there is no common parent object. I've considered using a form object to handle mapping each setting to the correct object but that doesn't feel like a great option either as each setting would require knowing it's id, the associated records id and it's type. This would not be particularly easy to manage when building the form.
I'm about to go down the route of having each setting in it's own form and having the record save automatically as the user edits each item. This would mean only a single record is ever saved at a time and will make things much simpler at the controller layer and also provide a lot of flexibility in how settings a grouped. Before I go down this route I wanted to see if there are any other options for submitting a single form in a single transaction that I may have overlooked?
Please note, this application is written in Rails 3.2 and is not in a state in which it can be easily upgraded to Rails 4 right now so any solutions need to work with Rails 3.2.

Force reload another model's methods in rails?

I have a model that defines methods based off of the entries in another model's table: eg Article and Type. An article habtm types and vice versa.
I define in Article.rb:
Type.all.each do |type|
define_method "#{type.name}?" do
is?(:"#{type.name}")
end
end
This works great! it allows me to ensure that any types in the type db result in the methods associated being created, such as:
article.type?
However, these methods only run when you load the Article model. This introduces certain caveats: for example, in Rails Console, if I create a new Type, its method article.type_name? won't be defined until I reload! everything.
Additionally, the same problem exists in test/rspec: if I create a certain number of types, their associated methods won't exist yet. And in rspec, I don't know how to reload the User model.
Does anyone know a solution here? Perhaps, is there some way to, on creation of a new Type, to reload the Article model's methods? This sounds unlikely.. Any advice or guidance would be great!
I think you'll be better off avoiding reloading the model and changing your api a bit. In Article, are you really opposed to a single point of access through a more generic method?
def type?(type)
return is? type if type.is_a? String # for when type is the Type name already
is? type.name # for when an instance of Type is passed
end
If you're set on having separate methods for each type, perhaps something like this would work in your Type class
after_insert do
block = eval <<-END.gsub(/^ {6}/, '')
Proc.new { is? :#{self.name} }
END
Article.send(:define_method, "#{self.name}?", block)
end

Polymorphic inline model forms in django

I have a Person model which has many Animal models as pets. Dog is an Animal with a "favorite bone" field, and Cat is an Animal with a "likes catnip?" field and a "favorite fish" field.
#models
class Person(db.model):
pass
class Animal(db.model):
models.ForeignKey(Person) #owner
name = CharField()
class Dog(Animal):
favorite_bone = CharField()
class Cat(Animal):
favorite_fish = CharField()
likes_catnip = BooleanField()
I would like to inline edit all of a Persons pets, in the Person admin form however, I've read that Django inline admin forms don't support polymorphic inline forms[1], in that, you will only get the parent class fields (e.g. not the favorite_bone or favorite_fish and likes_catnip fields.
Where does this problem come from?
What changes could be made to the framework to accommodate this?
If these changes should not be made, why not?
[1] http://www.mail-archive.com/django-users#googlegroups.com/msg66410.html
(This is an old question, but I thought I'd add an answer in case it is still useful. I've been working on a similar question recently.)
I believe it would be challenging to change Django form-generation to do what you want. The reason is that the inline formset uses a single class/form for all rows of the inline -- there are no configuration options that are evaluated per-row of the inline form. I have convinced myself of this by reading the code itself --- look for "inline" and "formset" in django.contrib.admin.options.py, especially lines 1039-1047 (version 1.5.1). This is also the reason why you can't have some fields read-only in existing items and changeable in new items (see this SO question, for example).
The workarounds found for the readonly case have involved a custom widget that produces the desired behavior such as this one. That still won't directly support polymorphism, however. I think you would need to end up mapping your divergent types back to a common ancestor (e.g. have all pet classes able to return a dict of their unique attributes and values), and then create a single custom widget that renders out the polymorphic part for you. You'd then have to map the values back on save.
This might be more challenging than it is worth, and may lead back to the suggestion in the other answer to not use admin for this :-)
may have a look here.
but i think the modeladmin is currently not able todo such things.
you are able to create a custom edit view for your model...
there is almost everything possible.
It may be possible to do this with Generic Relations.

polymorphic activerecords in rails?

Hi so I have a list of 'areas' that are just areas of my site. I want to be able to insert them in to the database and say what type of area they are, this could be the name of the model that inherits from a base Area. This way I can write a generic controller which uses different views and different logic from the loaded model to decide how the page should act.
Trouble is I have no idea how to do this as I'm new to rails.. Any pointers? (or anyone saying "don't do it like that! do it like this!" would also be much appreciated)
Thanks
I have just found Single Table Inheritance. This appears like it will solve my problem
for more information read here:
http://juixe.com/techknow/index.php/2006/06/03/rails-single-table-inheritance/
You'll want to create a column named :type
Rails will automatically use the type column to determine the class of the child class.
Then you can do something like:
class Area < ActiveRecord::Base
end
class UserPage < Area
end
So then when you do
UserPage.create( :key => 'value')
It will create an entry in your areas table with the type field set to UserPage.

Rails Single Table Inheritance - What is the best way to explicitly set type?

I am using single table inheritance in my rails application, and want to explicitly set the type of an instance.
I have the following;
class Event < ActiveRecord::Base
class SpecialEvent < Event
which is implemented through single table inheritance.
SpecialEvent.new works as expected, but I want to be able to do things like
Event.new(:type => 'SpecialEvent')
So I can create different sub_types easily in the application.
However this doesn't work and seems to set :type to nil, not the value I set it to; I suspect this is because by calling Event.new it is overwriting the :type argument.
Has anyone got a good way of doing this?
If you're trying to dynamically instantiate a subtype, and you have the type as a string, you can do this:
'SpecialEvent'.constantize.new()
from "Pragmatic - Agile Web Development with rails 3rd edition", page 380
There’s also a less obvious constraint (with STI). The attribute type
is also the name of a built-in Ruby method, so accessing it directly
to set or change the type of a row may result in strange Ruby
messages. Instead, access it implicitly by creating objects of the
appropriate class, or access it via the model object’s indexing
interface, using something such as this:
person[:type] = 'Manager'
man, this book really rocks
No, I want to create instances of
sub-types, where I want to
programmatically determine which
sub_type they are
– HermanD
You could use a factory pattern, although I have heard recently that people frown on the overuse of this pattern. Basically, use the factory to create the actual types you want to get
class EventFactory
def EventFactory.create_event(event_type)
event_type.constantize.new()
end
end
To me it sounds like you'll need some mojo in the event#create action:
type = params[:event].delete(:type)
# check that it is an expected value!!!
die unless ['Event', 'SpecialEvent'].include(type)
type.constantize.new(params[:event])
Apparently, Rails does not allow you to set Type directly. Here's what I do...
klass_name = 'Foo'
...
klass = Class.const_get(klass_name)
klass.new # Foo.new
I believe .constantize is a Rails inflector shortcut. const_get is a Ruby method on Class and Module.
Up front I'll agree that STI is often NOT the best way to deal with things. Polymorphism, yes, but it's often better to use a polymorphic association than STI.
That said, I had a system in which STI was important. It was a judicial system and things like court cases were remarkably similar across their types and generally shared all their essential attributes. However, a civil case and a criminal case differed in the elements they managed. This happened at several levels in the system so abstracted my solution.
https://github.com/arvanasse/sti_factory
Long story short, it uses a factory method to leverage the common approach described above. As a result, the controller can remain neutral/ignorant of the particular type of STI class that you're creating.
You can use the Rails safe_constantize method, which will ensure the object/class actually exists.
For example:
def typeify(string)
string.classify.safe_constantize
end
new_special_event = typeify('special_event').new

Resources