Handling forms with class table inheritance models - ruby-on-rails

I would like to use form_for except I have class table inheritance models using the citier gem. They are defined as such:
class Fruit < ActiveRecord::Base
# calories:integer
# color:string
end
class Apple < Fruit
# is_sauce:boolean
end
class Banana < Fruit
# is_peeled:boolean
end
The problem is that I want the first part of my form to fill out attributes for my Fruit model. Then depending on a select field on what type of fruit (Apple, Banana), I then want to fill out attributes for that particular model yet I still want validations with the form_for helper. Any suggestions on how I can approach this... or additional clarification? Thanks.

What I ended up doing was asking for the model before creating the form. Then using many partials.

Related

Calling models with _ us controllers

I have a table in the model called pg_search_documents, how do I work with it in the controllers?
I'm trying like this:
def show
#search = PgSearchDocument.find(params[:content])
end
But the so-called "PgSearchDocument" seems to be wrong.
You need to make sure you have a model declared in your app. If you have not done so, create the following file:
app/models/pg_search_document.rb
class PgSearchDocument < ActiveRecord::Base
end
In Rails 5 you would use:
class PgSearchDocument < ApplicationRecord
end
Please note the following naming conventions in Rails:
Database table name is plural snake case: pg_search_documents
Model filename is singular snake case: pg_search_document.rb
Model class name is singular camel case: PgSearchDocument

Ruby on Rails single table inheritance sample form

i'm a super newbie in rails and i need to see a sample code on how to implement Single table iheritance, i have a model called Listing as a super class, and i have subclasses LawFirms and Paralegal, these all extend the Listing model, now i need to be able to create a new listing, but when i am creating i need the form to have an option to select either Law Firm or Paralegal, when Law Firm option is selected, it should show a form for creating a LawFirm object which is different from the Paralegal Object because a law firm has advocates and a paralegal wont have advocates.
So far my models look like this
class Listing < ActiveRecord::Base
end
class LawFirm < Listing
has_many :advocates
end
class Paralegal < Listing
end
How do i create the controller? and the form?
I'm not sure that inheritance is the right solution for this. Inheritance is used for an is-a relationship. For example, a Nissan is a car so Nissan would inherit from the car class. You might be better off having LawFrim or Paralegal as objects in a Listing using a nested resource in rails. You could then add some checks in the controller to make sure it only has one or the other of those objects.

rails: create scaffold for models to inherit from superclass?

I'm new to Rails, still getting my feet wet, so please pardon me if this is either trivial or "the wrong way" to do things.
I'd like to create a superclass for some scaffolded models. For example, I'd like to create a scaffold for Men and for Women, but I want them both to inherit from a People superclass; Men and Women would inherit fields like height and weight from the People class.
Where/how do I define this People superclass? How do I define the subclasses Men and Women via scaffolding?
Usually I do something like:
rails g scaffold People type:string name:string birth:date height:integer
class People < ActiveRecord::Base
end
Important use the reserved word 'type'! That's where the table will keep which type the class is. Run the migration.
So, for the the subclasses you can do:
rails g scaffold Men --parent=People
resulting Men:
class Men < People
end
Same for Women:
rails g scaffold Women --parent=People
Resulting
class Women < People
end
No migration will be generated for the subclasses.
I'm not sure but this approach only works for STI.
Hope it, helps!
This is something I've thought about doing with my application. I haven't done it yet, and I wouldn't recommend it if you are new to rails. I would either make separate models entirely, or make one model, and have the attribute gender, which should be either a 0 or a 1, and then make a method that returns the string for the corresponding gender.
EDIT
So I opened up the rails console, and from what I could see, it is possible totally possible, all you need to do is declare the class, and if you want to use different tables, set_table_name
class This < That
set_table_name :this
end
class There < This
set_table_name :there
end
Or you could use one table, but if your trying to stay DRY, I would use two.
If you want to use the scaffold generator, you will have to run the typical rails g scaffold Men for each class you want views for (men and women). The model that this generates inherits from the ActiveRecord::Base class. The inheritance marker is the less than symbol (<).
# THESE WILL BE THE DEFAULT GENERATED MODELS
class Men < ActiveRecord::Base
end
class Women < ActiveRecord::Base
end
You will then manually create the super class User
class User < ActiveRecord::Base
end
and then edit the Men and Women models to inherit from User
# men.rb
class Men < User
end
# women.rb
class Women < User
end
lets say you wanted to subclass with one table, you could would right the migrations for that table, and then add the attr_accessible to the appropriate subclass.
attr_accessible is a rails security feature. It determines which attributes may be set in mass assignment. Anything related to security, site rank, etc. should not be accessible.
Example:
attr_accessible :favorite_food, :interests, :password, :email # THIS IS GOOD
attr_accessible :admin, :has_access_to_missile_launch_codes # THIS IS BAD
because then someone could undermine your security system by passing
params => { :man => { :admin => true }}
The main point is that using these attr_accessible will determine which type of user can set what. Obviously you can DRY this up by putting shared features in the super-class. Hope this helps
You should also read about the super keyword, and the self keyword. If your running an inherited setup you will eventually want to use these.
AFAIK you'd need to tweak the existing scaffolding templates, I don't believe there's a means to specify the controller base class. That said, I think in Rails 3 you can copy the templates into $ROOT/lib/templates/rails/... where ... depends on which you want to change.
That said, what's the real goal in doing this in a scaffold? In general, models will (a) only rarely be subclasses, and (b) even more rarely be the same subclass.
Just edit them by hand.
watch this screencast on single table inheritance.
http://railscasts.com/episodes/394-sti-and-polymorphic-associations
Single table inheritance and where to use it in Rails

rails model subclassing -> multi table inheritance or polymorphism

Here is my setup, followed by an explanation of what I am trying to accomplish.
class Layer < ActiveRecord::Base
has_and_belongs_to_many :components
end
class Component < ActiveRecord::Base
has_and_belongs_to_many :layers
end
class ImageComponent < Component
# I want this table to inherit from the Component table
# I should be able to add image-specific fields to this table
end
class VideoComponent < Component
# I want this table to inherit from the Component table
# I should be able to add video-specific fields to this table
end
What I want to be able to do:
layer.components << ImageComponent.create
layer.components << VideoComponent.create
In practice, I realize that ImageComponent and VideoComponent will actually have to inherit from ActiveRecord::Base. Is there any way to nicely implement model subclassing in Rails?
Right now I have my Component model setup to be polymorphic such that ImageComponent and VideoComponent each has_one :component, as: :componentable. This adds a layer of annoyance and ugliness to my code though:
image_component = ImageComponent.create
component = Component.create
component.componentable = image_component
layer.components << component
I guess a simple way to explain this is that I want to implement a habtm relationship between Layers and Components. I have multiple types of Components (i.e. ImageComponent, VideoComponent) that each have the same base structure but different fields associated with them. Any suggestions on ways this can be accomplished? I feel that I am missing something because my code feels hackish.
The "official" way to achieve this in Rails is to use Single Table Inheritance. Support for STI is built into ActiveRecord: http://api.rubyonrails.org/classes/ActiveRecord/Base.html#class-ActiveRecord::Base-label-Single+table+inheritance
If you want to use Multi Table Inheritance you would have to implement it by yourself...
here the main issue is between the Component and its types and not Layer and Component. i had a similar problem. will explain the solution specific to ur problem.
Store the type(Image/Video) as resource for Component and have a controller for Component and not all the types()
let the model structure be as
Component < ActiveRecord::Base
accepts_nested_attributes_for :resource
belongs_to :resource, :polymorphic => true, :dependent => :destroy
def resource_attributes=(params = {})
self.resource = spec_type.constantize.new unless self.resource
self.resource.attributes = params.select{|k| self.resource.attribute_names.include?(k) || self.resource.class::ACCESSOR.include?(k.to_sym)}
end
#component will be either image or video and not both
Image < ActiveRecord::Base
has_one :component, as :resource
Video < ActiveRecord::Base
has_one :component, as :resource
and a single controller as ComponentsController for CRUD of Component. Since the Component accepts attributes for resource(ie image/video), u can save the component as well as the resource and add normal validations for each resource.
the basic view for adding a Component can be as
= form_for(#component, :url => components_path, :method => :post) do |f|
= fields of Component
= f.fields_for :resource, build_resource('image') do |image|
= fields of resource Image
= f.fields_for :resource, build_resource('video') do |video|
= fields of resource Video
the fields for Image/Video can be added using the helper method
module ComponentsHelper
def build_resource(klass)
klass = "{klass.capitalize}"
object = eval("#{klass}.new")
if #component.resource.class.name == klass
object = #component.resource
end
return object
end
end
since the Component can have only one related resource(image/video), u need to select the the resource type on the view(in my case it was a dropdown list) and depending upon the selected resource show its fields and hide/remove all other resources fields(if image is selected, remove video fields using javascript). When the form is submitted, the method from Component model filters out all the key-value pairs for the intended resource and creates the component and its related resource.
Also
1) keep the field names for each resource unique cause when the form is submitted, the hidden resource(unwanted resources) fields are submitted which overwrite the intended resource fields.
2) the above model structure gives problem for resource attr_accessor only(they are not accessible on rails console). it can be solved as
ACCESSOR = ['accessor1', 'accessor2'] #needed accessors
has_one :component, :as => :resource
attr_accessor *ACCESSOR
See how to implement jobpost functionality that has 3 fixed categoris
i hope this helps.
With STI, you are sharing the same table with several model classes, so if you want subclassed models to have unique fields (database columns), then they need to be represented in that common table. From the comments in your example, it appears that this is what you want.
There is a trick you can do, however, which involves having a string column in the table that each model can use to store custom serialized data. In order to do this, it has to be OK that these data elements aren't indexed, because you won't be able to easily search on them within SQL. Let's say you call this field aux. Put this in the parent model:
require 'ostruct'
serialize :aux, OpenStruct
Now let's say you want the fields called manager and experience in a subclassed model, but none of the other STI models need this field and you won't need to search based on these attributes. So you can do this in the subclassed model:
# gets the value
def manager
return self.aux.manager
end
# sets the value
def manager=(value)
self.aux.manager = value
end
# gets the value
def experience
return self.aux.experience
end
# sets the value
def experience=(value)
self.aux.experience = value
end
In this example, single table inheritance still works fine and you also get custom persistant attributes for subclassed models. This gives you the benefits of sharing code and database resources among several models, but also allows each model to have unique attributes.

How can I extend a model with rails?

I want to have a general product model that has basic info like name, description, sku number etc. I also want to have another model that is a specific type of product that essentially extends the product model. For example: I'd like to have a clothing model that has additional columns like color, size, etc.
What is the best practice to implement this? I am thinking polymorphism or Single Table Inheritance. Maybe I am going down the wrong path??
Single Table Inheritance (documentation) is a common approach. Another is to use modules for shared functionality.
Here is an example of using modules.
module Product
def method_for_all_products
# ...
end
end
class Clothing < ActiveRecord::Base
include Product
def clothing_specific_method
# ...
end
end
class Furniture < ActiveRecord::Base
include Product
end

Resources