Rails - acts_as_list with multiple Models - ruby-on-rails

I've managed to use act_as_list with my Models (it was quite easy) one by one, but now i have a problem.
In my app there are 3 models: Facility, Service and Activity. I need to use acts_as_list on their union... is it possible to do that?
Hope my question is clear

You should use a fourth model with a polymorphic association, then put the list on that.
First, read up on polymorphic associations to understand this: http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
Now you'll want to have a class that looks like this:
class Position < ActiveRecord::Base
belongs_to :positionable, polymorphic: true
end
And a migration that looks like this:
class CreatePositions < ActiveRecord::Migration
def change
create_table :position do |t|
t.integer :positionable_id
t.string :positionable_type
t.timestamps
end
end
end
Then on each of the other models add this:
class Facility < ActiveRecord::Base
has_one :position, as: :positionable
# ...
end

Related

Is there a way in rails to make a model optionally belong to another model?

I am building a rails exercise app that has both exercises and routines. I want each routine to be composed of several exercises (has_many :exercises), but an exercise doesn't necessarily have to belong to a routine. Is there a way to do that?
Reading the guides is always a good start. This works from Rails 5 onwards.
belongs_to :routine, optional: true
You probably want a many-to-many relationship here, rather than a one-to-many relationship.
It would seem you would want for an Exercise to be associated with any number of Routines, and for a Routine to be associated with one or more Exercises.
You end up with something like this:
# app/models/routine.rb
class Routine < ActiveRecord::Base
has_and_belongs_to_many :exercises
end
# app/models/exercise.rb
class Exercise < ActiveRecord::Base
has_and_belongs_to_many :routines
end
# db/migrate/1213123123123_create_exercises_routines_join_table.rb
class CreateExercisesRoutinesJoinTable < ActiveRecord::Migration
def self.change
create_table :exercises_routines, :id => false do |t|
t.integer :exercise_id
t.integer :routine_id
t.index [:category_id, :routine_id]
end
end
end

How to extract certain db fields from a parent model using STI in Rails

Suppose I have a Mechanism model, and its schema is something like this:
t.string "image"
t.string "type"
t.string "speed"
t.string "name"
Now suppose I would like to create a child model called Car, which fully inherits the Mechanism model fields using STI (Car < Mechanism), but also applies its own:
t.string "wheels"
t.string "metal_type"
t.string "fuel_consumption"
...
In the end I would basically like to have many models, that share certain db field with Mechanism model, and behave like a Mechanism.
Now I know that STI does not work like this, but I would like to know what other options do I have?
You can use one of the inheritance gems.
acts_as_relation & multiple_table_inheritance will help you with your work
One non-STI solution would be to have a 1-M relationship between Car and Mechanism
class Car < AtiveRecord::Base
belongs_to :mechanism
end
class Mechanism < ActiveRecord::Base
has_many :cars
end
This would let you do something like
car = Car.first
car.mechanism.speed
Understanding that is not what you're looking for, check out the delegate method.
class Car < AtiveRecord::Base
belongs_to :mechanism
delegate :image, :type, :speed, :name, to: :mechanism
end
class Mechanism < ActiveRecord::Base
has_many :cars
end
Now you can do the following
car = Car.first
car.speed

Trouble changing to has_many through from has_many and belongs_to

I've done about a billion searches and tried a number of things here but I'm still getting errors. I've recently changed to a has many through via a model called joinable(maybe thats the problem) and I can't seem to get things working straight. Part of me thinks it something small as I get the idea of it all but I'm not sure I've done it correctly. I'm also using devise.
Here is what I think are all the relevant portions
User
class User < ActiveRecord::Base
acts_as_voter
has_many :joinables
has_many :pits, through: :joinables
has_many :comments
enum role: [:user, :vip, :admin]
after_initialize :set_default_role, :if => :new_record?
class Pit < ActiveRecord::Base
validates :topic, :author, :summary, presence: true
acts_as_taggable
acts_as_votable
has_many :comments
has_many :joinables
has_many :users, through: :joinables
mount_uploader :image, ImageUploader
I created a separate table called "joinable" and now I'm stuck figuring out how to populate it. I can create a user, but can't create a pit. Do I need to revamp my controllers or is their something small I may be missing? I get the idea but some of the little details are fuzzy based on all that I've read so far. I even tried a HABTM with a join table called Pit_Users.
I'm currently getting "Could not find table 'joinables"
coming from here in my controller
def create
#pit = current_user.pits.create(pit_params)
recent migration
class Joinable < ActiveRecord::Migration
create_table :joinable do |t|
t.integer :pit_id, :user_id
t.timestamps
end
end
I've tried a number of combinations all with similar errors. Many of the tutorials/guides are good with the basics but then seem to be leaving out a few details. That or I'm just missing them. Anyways. Would love it if someone more knowledgeable could point out what are probably obvious mistakes. Thanks.
In the migration file, it should be:
class Joinables < ActiveRecord::Migration
create_table :joinables do |t|
t.integer :pit_id
t.integer :user_id
end
end
And in the app/models/joinable.rb, there should be:
class Joinable < ActiveRecord::Base
belongs_to :user
belongs_to :pit
end
You can verify if it is working at the Rails console. Try this to get a Pit record with the association:
user_1 = User.create( ... )
pit_1 = user_1.pits.create!( ... )
pit_1.users.first # should give you user as user_1
Solution is to run rails generator for model
Run from console
rails generate model Joinable pit:references user:references
And delete your migration file for
class Joinable < ActiveRecord::Migration
create_table :joinable do |t|
t.integer :pit_id, :user_id
t.timestamps
end
end
After running rails generator you will get model named Joinable that is required for relations when using through and it will create appropriate migration for you.

Confusion about ActiveRecord associations and foreign keys

In the below example, do I have to create employee_id in the Office model, or is it created automatically by db:migrate?
class Employee < ActiveRecord::Base
has_one :office
end
class Office < ActiveRecord::Base
belongs_to :employee # foreign key - employee_id
end
Feels like I'm missing something fundamental. I'm trying to get a basic one to many relationship working, where I can use a drop-down select of objects from the one side. Are there any good basic tuts explaining how this works?
I had to create _ids in all the models where I wanted this to work, but it doesn't seem right from examples I've looked at.
two steps.
firstly, you have to create an employee_id field in the office table in the migration file. you will have something like that :
class CreateOffices < ActiveRecord::Migration
def change
create_table :offices do |t|
t.string :name
t.integer :employee_id
t.timestamps
end
end
end
secondly, you have to define the association in the model. by convention, if you name the foreign_key field employee_id, you don't have to specify the name of it in the model.
class Office < ActiveRecord::Base
belongs_to :employee
end
should be enough.
Associations in ActiveRecord comprise two parts. Hooking together the model objects (like you've done) and setting up the database. So you'll need to define the association in your migration like so:
def change
create_table :offices do |t|
# Other migrations
t.references :employee
end
end
Alternatively you can do t.integer :employee_id which will achieve the same end too.

Scaffolding ActiveRecord: two columns of the same data type

Another basic Rails question:
I have a database table that needs to contain references to exactly two different records of a specific data type.
Hypothetical example: I'm making a video game database. I have a table for "Companies." I want to have exactly one developer and exactly one publisher for each "Videogame" entry.
I know that if I want to have one company, I can just do something like:
script/generate Videogame company:references
But I need to have both companies. I'd rather not use a join table, as there can only be exactly two of the given data type, and I need them to be distinct.
It seems like the answer should be pretty obvious, but I can't find it anywhere on the Internet.
Just to tidy things up a bit, in your migration you can now also do:
create_table :videogames do |t|
t.belongs_to :developer
t.belongs_to :publisher
end
And since you're calling the keys developer_id and publisher_id, the model should probably be:
belongs_to :developer, :class_name => "Company"
belongs_to :publisher, :class_name => "Company"
It's not a major problem, but I find that as the number of associations with extra arguments get added, the less clear things become, so it's best to stick to the defaults whenever possible.
I have no idea how to do this with script/generate.
The underlying idea is easier to show without using script/generate anyway. You want two fields in your videogames table/model that hold the foreign keys to the companies table/model.
I'll show you what I think the code would look like, but I haven't tested it, so I could be wrong.
Your migration file has:
create_table :videogames do |t|
# all your other fields
t.int :developer_id
t.int :publisher_id
end
Then in your model:
belongs_to :developer, class_name: "Company", foreign_key: "developer_id"
belongs_to :publisher, class_name: "Company", foreign_key: "publisher_id"
You also mention wanting the two companies to be distinct, which you could handle in a validation in the model that checks that developer_id != publisher_id.
If there are any methods or validation you want specific to a certain company type, you could sub class the company model. This employs a technique called single table inheritance. For more information check out this article: http://wiki.rubyonrails.org/rails/pages/singletableinheritance
You would then have:
#db/migrate/###_create_companies
class CreateCompanies < ActiveRecord::Migration
def self.up
create_table :companies do |t|
t.string :type # required so rails know what type of company a record is
t.timestamps
end
end
def self.down
drop_table :companies
end
end
#db/migrate/###_create_videogames
class CreateVideogames < ActiveRecord::Migration
create_table :videogames do |t|
t.belongs_to :developer
t.belongs_to :publisher
end
def self.down
drop_table :videogames
end
end
#app/models/company.rb
class Company < ActiveRecord::Base
has_many :videogames
common validations and methods
end
#app/models/developer.rb
class Developer < Company
developer specific code
end
#app/models/publisher.rb
class Publisher < Company
publisher specific code
end
#app/models/videogame.rb
class Videogame < ActiveRecord::Base
belongs_to :developer, :publisher
end
As a result, you would have Company, Developer and Publisher models to use.
Company.find(:all)
Developer.find(:all)
Publisher.find(:all)

Resources