rails: create scaffold for models to inherit from superclass? - ruby-on-rails

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

Related

Rails 5 nested attributes alternative way?

I'm creating an app that it needs to be customizable.
Ej:
I have:
Position scaffold
Category scaffold
Class scaffold
Type Scaffold
Now, the situation is, position table along other attributes, includes a category attr, a class attr and a type attr. The problem I have is that every category, class and type is created and owned by another controller and moedel.
Now, here is where my problem comes to life.
when I tried to create a position (even though I can actually see all categories, classes and types listed on my position view via form.select) I cannot save a position with a category, class nor type.
I tried nested_attributes and don't work quite good in this situation.
What is the appropriate method tho attack this from scratch base on my scaffolds?
Any help will be very much appreciated!
Thanks in advance!
It is a little tough to figure out what you need without seeing your code, but I'll try my best to help.
Keep in mind that Class and Type are reserved words in rails; Columns named _type are used for polymorphic relationships. And Class is used well for ruby classes. So as a general rule of thumb I would stay clear of those terms.
For you Position model:
class Position < ActiveRecord::Base
has_many :categories
has_many :types
has_many :classes
accepts_nested_attributes_for :categories, :types, :classes
end
Then for your other models:
class NameOfClass < ActiveRecord::Base
belongs_to :position
end
You also need to make sure that you add these nested attributes to the whitelist on you controllers. It should include any columns that you want to accept that will update other models. Keep in mind that this has to be the last thing listed.
In your Position Controller
PositionsController < ApplicationController
private
def position_params
params.require(:position).permit(:id, :position_column_a, :another_column,
categories_attributes: [:id, :position_id, :any_other_column_you_want],
types_attributes: [:id, :position_id, :again_any_other_column])
Again, before you do anything I would look into renaming your scaffolds. the best way is to delete them and then regenerate. This will do the trick:
rails d scaffold Type
If you just need a simple dropdown of a list of categories you can try enums. However, it is not good for options that need to be customizable for the user. I typically only used them for statuses.
https://bendyworks.com/blog/rails-enum-sharp-knife
You can also read the API docs: http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

How do I create a base class that abstracts a has and belongs to many relationship in Rails 4.1

I have the following HABTM relation and corresponding logic that is identical in at least 3 different tables, so I wanted to factor it, and related methods, into a common base class. The problem is, even though the association does show up in the object's association_cache, trying to access it results in an exception (ArgumentError comparison of nil object with a string). I'm confused about why this isn't working, as this seems like basic OOP programming.
My base model is set up like this:
class ProfileItem < ActiveRecord::Base
self.abstract_class = true
has_and_belongs_to_many :profiles
attr_accessor :profile_ids
before_destroy :clean_up
before_save :update_profiles
def get_profile_names
self.profiles.each do |p| # << exception here
...
Look into a concept called concerns, introduced in Rails 4. Abstracting a class inherited from ActiveRecord::Base is a recipe for trouble.
A good explanation How to use concerns in Rails 4.

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.

how to enforce inclusion rules in ActiveRecord associations

I'm new to rails and I have a question on how best to enforce custom rules on my model associations.
For example, suppose I have:
class Person < ActiveRecord::Base
belongs_to :organization
end
class Organization < ActiveRecord::Base
has_many :people
end
and now suppose that I only want to allow the Organization.people << Person.new(...) command to succeed if the new Person object is compatible with the other people that were previously added to the Organization. This would entail running a validation check across all the existing elements of Organization.people and deciding whether the new Person could be added or not.
It seems to me that I can do this by overriding all the Organization.people assignment operators (such as << and =) and putting my validation logic in the override routine.
Is this best way to accomplish this?
Thanks!
I think you could put a validation in the Person class. It would run a test against the other people in self.organiation.people. I don't know if I would override the << on the has many relationship only because if you decide to create a person like Person.new(:organization => some_org) your << override would not get used. If the validation lives on the Person class, it would get exercises no matter how you create the person.

Rails: dynamic environmental settings without magic numbers

Short version: Where should I store environment-specific IDs? ENV['some-variable']? Somewhere else?
Long version:
Let's say I have a model called Books and a book has a Category. (For the sake of this question, let's say a book only has one category.)
class Book < ActiveRecord::Base
belongs_to :category
end
class Category < ActiveRecord::Base
has_many :books
end
Now let's say one category is called 'erotica.' And I want to suppress erotica books in my type ahead. That seems straight forward. But in production and in development 'erotica' has a different ID. I don't want my code to be ID dependent. I don't want it to be string dependent (in case 'erotica' is renamed pr0n or whatever).
I think I should have something like
def suppress_method
suppress_category_id = look_up_suppression_id
...
end
but where should 'look up' look?
Thanks!
Taking this approach will be brittle, what if you want to suppress multiple categories? Erotica and Politics? The best design here is for you to actually add 'suppressed' as a boolean to category in a migration, and maintain that in your application's administration interface. After you've done that you can add a named scope like:
class Category < ActiveRecord::Base
named_scope :not_suppressed, :conditions=>{:suppressed=>false}
# or for rails 3
scope :not_suppressed, where(:suppressed=>false)
end
Then just update your type ahead code to do:
Category.not_suppressed.find ...
Rather than
Category.find

Resources