Overriding a child association's setter from the parent model - ruby-on-rails-4

I have a parent User model, and each user has_one Contact which hold's the user's contact info.
One of the contact fields is the email. For various legacy reasons and reasons outside the context of this question, I'd love to be able to call #email directly as if it were a property of User
In other words I'd like to do this -
user.email
user.email = "foo#example.com"
Instead of this -
user.contact.email
user.contact.email = "foo#example.com"
I laid out my models as follows, overriding the child's getter/setter from the parent User -
class User < ActiveRecord::Base
has_one :contact
def email
contact.email
end
def email=(value)
contact.email = value
end
end
class Contact < ActiveRecord::Base
belongs_to :user
end
You may have already noticed the problem with the setter - when the User is saved, it doesn't save the child model.
What's the most robust to work around this?
Thanks!

Ok, found the solution for anyone curious - auto-saving
ActiveRecord::AutosaveAssociation is a module that helps with exactly this - saving child associations properly when the parent is saved.
Just need to include an autosave: true. Example -
has_one :contact, autosave: true
It also takes care of destroying records marked for deletion, and a few other fancy things.
I also did a quick check, and it smartly doesn't hit the DB unless it really needs to. That is, if you update the parent only it doesn't bother saving the child association with an other SQL UPDATE statement.
Check out the documentation here.

Related

How to associate these models(user, company), where User can be Employer and Employee?

In Ruby on Rails 4, I've these models:
def User < ActiveRecord::Base
has_one :company, dependent: :destroy
end
def Company < ActiveRecord::Base
belongs_to :user
end
So now, I want User as Employer to be able to have a company, and this Company can have many Users(Employees).
And when I log a User as Employee, I want to able to list all the companies he works.
What is the best way to do this?
I'm not sure I could give you a full working code file without some more information, but I think this is the best way to proceed.
class User < ActiveRecord::Base
self.table_name = "users"
#Define shared associations/methods
end
class Employee < User
has_and_belongs_to_many :companies
#Employee only associations/methods
end
class Employer < User
has_one :company
#Employer only associations/methods
end
class Company < ActiveRecord::Base
has_and_belongs_to_many :employees
belongs_to :employer
end
Since both inherit from the same User model, they will share a table. Since both derived models only use has_x, the foreign key will be in the other table, meaning they can share a table schema without a ton of null values.
Again I'm not sure this will work by itself, but I think it's a good start. Another advantage is that by segregating the code like this, you can independently change functions by the type of person (e.g. you could have a generic log function on user, but make it more specific on Employee and Employer - such as including the company or companies they are tied to).
I realized after the fact that there is another way of doing this. You can make User have a polymorphic association to either Employee or Employer. Then you would check the user record for which type it is, then pull the association and call the methods on that record (Employee or Employer).
The only thing I don't like about this solution is that it involves 3 tables and from what I've seen you can get by with 1.

Is there a better way of overriding devise controller in rails

I have to populate a field in my db when a new user signs-up.But this field is not to be filled by the user, instead will be populated by the application in the db. Here is what i tried:
Added the extra field(investorId) in the migration file.
Overridden the devise controller:
def create
super
if #user.save
#user.investorId = #user.id + X---> some number
#user.save
end
end
Though it is working fine, but I want to know if there are better ways of doing it since I am doing it for the first time.
Thanks,
Sachin
If you need to generate a value before you create a record, you can use the before_create or after_create callbacks on the model.
User < ActiveRecord::Base
after_create :generate_investor_id
private
def generate_investor_id
reload
investor_id = "#{self.id}something"
update_attribute(:investor_id, investor_id)
end
end
Don't override your devise controller, there is no benefit of doing this. Simply put the below logic in your after_create callback -
User < ActiveRecord::Base
after_create :create_investor_id
private
def create_investor_id
self.update_column(:investor_id, id+some_number)
end
end

Multiple Devise models with unique attributes

Short explanation:
I seek architectural advice and help in implementing multiple Devise models in a single app.
More detailed explanation:
My application needs to perform the following behavior:
There are 3 types of users (Partner, Attendee, Speaker) which have some common fields and some unique ones (also, the fields might have different permissions, i.e. Attendee must have a username whereas the Speaker might have it but they don't have to necessarily fill this field in). And moreover, the different user models must have different associations with other tables in the db.
The users need to be able to log in through single log-in form, but the sign-up forms should be different.
So, my first thought was that I should divide the users by roles using Pundit or declarative_authorization or something, but the users don't really have different roles (i.e. permissions) in the app, they rather have different behavior, they might see different content and stuff, so I continued thinking.
My second though was implementing STI and after reading several articles about it, I tried to do that in code.
I generated a Devise User model by doing rails g devise User and after that ran rails g model Attendee and the same for other two users.
Then, I inherited my models from User:
class Attendee < User
end
My User migration looks like this:
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :type
# Devise stuff ...
..................
t.timestamps null: false
end
And other migrations are like this:
create_table :attendees do |t|
t.string :username
t.string :company_name
t.boolean :subscription
t.timestamps null: false
end
Now I realize it was wrong to create separate tables.
I had to put all the possible fields into the User table, is that correct?
Because now when I try to create any new Attendee or Speaker or Partner in rails console, all of these three models have the exact same fields, those the User model has.
But if I add all the possible fields in the User model, how would I perform validations on field presence?
I've read quite a few articles and questions here on SO, but still can't really wrap my head around how to implement all that.
Anyway, is that a correct way to do what I need?
Could anybody explain me in a detailed way how I should implement this kind of behavior and functionality from start to finish, and how should I work with the models after having implemented them?
PS: here's the history of my migrations and the whole github repo
Update
Remembered another issue that stopped my from doing just role separation:
How should I sign up the different users with different sign-up forms? Different routes? I cannot make the user choose their role from the combobox.
You can create conditional validation rules based on the role, but the first place you need to address this is in the new/edit User form, only showing the allowed fields dynamically based on the role:
class User < ActiveRecord::Base
validates :company, presence: true, if: :is_company?
def is_company
# check for the role
self.role == 'company'
end
end
UPDATE: You can pass an extra parameter to the same registration form and use that to differentiate the type of form you display. That's the nicest way. You can also create separate methods in the UserController -> def register_user, def register_company, def register_xxxx

Only show one form for nested_attribute

I would like to show only one nested form for the one-to-many relationship between user and appointments. A user can only create one appointment and the form should only show fields for one appointment regardless of how many appointments he has.
I've tried the :limit option
class User
has_many :appointments, dependent: :destroy
accepts_nested_attributes_for :appointments, limit: 1
end
I also tried limiting the number of appointments built in the controller
class UsersController
def edit
#user = current_user
1.times { #appointment = #user.appointments.build }
end
end
I always end up with a nested form for each appointment the user has plus an additional nested form. I know I could change the relationship to one-to-one but this is for legacy code and its likely that the relationship will be one-to-many in the future.
Is there a way that I can only show one nested form regardless of how many appointments a user has?

Call Back for belongs_to association rails

Like after_add callback for has_and_belongs_to_many association is there any call back or work around to get after_add functionality for belongs_to association.
One work around is to use after/before save call backs and dirty object functionality.
belongs_to :video
after_save :after_save_task
def after_save_task
do_stuff if video_id_changed?
end
def do_stuff
### do stuff
end
But I cannot save(true) in do_stuff since its going into an infinite loop.
Looks like the feature for adding callbacks for has_on and belongs_to is not yet added. see this thread https://github.com/rails/rails/issues/586
One dirty solution for your specific problem would be to add some dirty attribute to suggest if update has already been made.
like this
belongs_to :video
after_save :after_save_task
attr_accessor :stuff_done
def after_save_task
do_stuff if video_id_changed? && !stuff_done
end
def do_stuff
stuff_done = true
### do stuff
## Saving record here would be fine.
end
Again this is truly a hack, there might exist some better solution for this.
How about overriding the setter method?
Rails guides has an exam0le of this here
an excerpt of which states
Overriding generated methods
Association methods are generated in a module that is included into
the model class, which allows you to easily override with your own
methods and call the original generated method with super. For
example:
class Car < ActiveRecord::Base belongs_to :owner belongs_to
:old_owner
def owner=(new_owner)
self.old_owner = self.owner
super
end
end
If your model class is Project, the module is named
Project::GeneratedAssociationMethods. The GeneratedAssociationMethods
module is included in the model class immediately after the
(anonymous) generated attributes methods module, meaning an
association will override the methods for an attribute with the same
name.
I appreciate this is an old question but I stumbled on it looking for a similar solution