Whenever a User object is created, create UserInfo object too - ruby-on-rails-3

Whenever a User object is created, I want a UserInfo object to be created too, and linked to it.
Unfortunately this does not create any UserInfo:
class User < ActiveRecord::Base
has_one :user_info
...
def init
self.user_info = user_info
self.save!
end
Why is the init method not called? How to reach my goal?

sombe's technique is right, but his details aren't ideal. In fact, since create_user_info is already a method on User instances, all you want is something like:
class User < ActiveRecord::Base
has_one :user_info
before_create :create_user_info
end
Edit: init doesn't do anything particularly magical under Rails (I... don't think it does under basic Ruby either - are you thinking of initialize? I'll assume you are). initialize is fired off when an instance of the Ruby class is created in memory. That's divorced by quite some margin from an instance of the model being created in the database; a new class instance could be due to you calling build (and not saving yet), or even due to reading an instance out of the database.
If you want to step in on database operations, you need to make use of the ActiveRecord callbacks. You might find my answer to this question useful.

before_save callback triggers on create and update.
I'd suggest to use after_create because before_create can return errors
class User < ActiveRecord::Base
has_one :user_info
...
after_create do
create_user_info
end

In your User model, use a before_save filter instead of init like this:
before_save :create_user_info
...
private
def create_user_info
user_info = UserInfo.new
if user_info.save
self.user_info_id = user_info.id
end
end

Related

How Do I Add Methods To ActiveRecord::Base?

I'm trying to create a customized ActiveRecord::Base that includes additional metadata about the connection. I see two ways to go about this:
1.) Inherit from ActiveRecord::Base and add methods & fields in this subclass.
2.) Encapsulate an ActiveRecord::Base object inside my own class
1 has all kinds of problems with the inability to override initialize, weird problems where it doesn't seem to have custom methods I've added, etc.
undefined method `set_profile' for #<Class:0xf041f0>
2 I have not been able to figure out, due to problems with using ActiveRecord::Base.new
I am trying to make an all-purpose ActiveRecord class that I can dynamically establish_connection & set_table_name on, (i.e. not have one underlying table that this ActiveRecord::Base represents) but I can't seem to find a way to accomplish it. Any ideas?
This works:
class MyTable < ActiveRecord::Base
establish_connection $config['custom-db-config'];
set_table_name 'MY_TABLE'
end
but I need a class I can call these things on repeatedly.
Not entirely sure why you'll want that, but maybe you can try this?
module ActiveRecord
class Base
def self.your_method
# implementation goes here
end
end
end
You will need to save this file and put it in config/intializers.
You can also extend the ActiveRecord::Base class in order to add the those methods dynamically which are directly callable by the class inheriting the ActiveRecord::Base...Many acts_as plugins are defined and made according to this practice...

rails before_destroy partial delete

I'm trying to figure out a way to partially delete/destroy dependent models in rails.
Code looks something like this:
class User < ActiveRecord::Base
has_many :subscriptions
has_many :photos, :dependent => :destroy
has_many :badges, :dependent => :destroy
before_destroy :partial_destroy
def partial_destroy
self.photos.destroy_all
self.badges.destroy_all
return false if self.subscriptions.any?
end
...
Essentially, I want to destroy the photos and badges, but if the user has any subscriptions, I want to keep those, and also keep the user from being destroyed.
I tried with .each { |obj| obj.destroy } and using delete and delete_all, but it seems to not matter.
It looks like rails is performing some kind of a rollback whenever the before_destroy returns false. Is there a way to destroy part of the dependents but not others?
This is old so I expect you've forgotten it, but I stumbled across it.
I'm not surprised delete and delete_all didn't work, since those bypass callbacks.
You're exactly right that Rails performs a rollback if any before_ callback returns false. Because Rails wraps the entire callback chain in a transaction, you're not going to be able to perform database calls (like destroys) inside the chain. What I would recommend is putting a conditional in the callback:
If the user has subscriptions, kick off a background job which will do this partial delete later (outside the callback transaction), and return false from the callback.
If they don't have subscriptions, you don't start the background job, return true from the callback, and destroy your model as usual.
I ended up doing the following:
override destroy on the User model (see below)
not actually deleting the User, but rather destroying the dependants that are not needed, and blanking any fields on the User model itself, e.g. email.
I created a UserDeleter class that takes the user and performs all clearing operations, just to keep things cleaner / having some kind of single-responsibility
overriding destroy
def destroy
run_callbacks(:destroy) do
UserDeleter.new(self).delete
end
end
deleting dependants and clearing data on User
class UserDeleter
def initialize(user)
#user = user
end
def delete
delete_photos
delete_badges
clear_personal_data
# ...
end
private
def delete_photos
#user.photos.destroy_all
end
def clear_personal_data
#user.update_attributes!(
:email => deleted_email,
:nickname => '<deleted>')
end
def deleted_email
"deleted##{random_string}.com"
end
def random_string(length = 20)
SecureRandom.hex(length)[0..length]
end
#...
end

Best way to update elements from an has_many relationship without instantiating full models and triggering the callbacks

This is something I'm trying to do now: A has_many Bs. B has certain callbacks that need to be triggered. Just, when I save from A, I want something to be updated in Bs. Since the Bs can be 10000, I would like not to load them into memory, and still have the benefit of seeing the callbacks triggered. What is the best strategy for this?
And please, no "find_each" or any find by batches variant, I'm aware of them and they will be my last resort in case nothing else works.
When I encountered this problem, I use this solution
define "callback methods" in a class and use they with ids,
define really callbacks in a instance and pass id of record in "class callback"
example of code:
class Post < AR
has_many :comments
after_save do |post|
Post.recalculate_counters(post.comment_ids)
end
end
class Comment < AR
belongs_to :post
after_save :recalculate_couters
def self.recalculate_couters(ids)
... huge and slow update statement ...
end
def recalcuate_couters
self.class.recalculate_couters([id])
end
end
I don't think there's any way you can have the callbacks executed without loading the models. However, if you give up using callback you can use update_all that performs really fast.
You just use:
B.update_all({:timestamp => Time.now}, { :a_id => id })

devise after create hook

Is there a hook or callback that I can implement so that right after the user is created, I would like to invoke some custom code ?
I tried after_confirmation hook in the user model but that didn't work.
Use the standard after_create callback provided by Rails.
class User < ActiveRecord::Base
after_create :do_something
def do_something
puts "Doing something"
end
end
Using a callback is perfectly legit if you're dealing with the internal state of the model you created.
After creating a User, I needed to create default a Team. It's preferable to avoid using callbacks to deal with other objects.
“after_*” callbacks are primarily used in relation to saving or persisting the object. Once the object is saved, the purpose (i.e. responsibility) of the object has been fulfilled, and so what we usually see are callbacks reaching outside of its area of responsibility, and that’s when we run into problems.
From this awesome blog post.
In this case it's better to act on the controller, where you can add your functionality directly, or delegate to a service for an even cleaner solution:
# shell
rails g devise:controllers users
# config/routes.rb
devise_for :users, controllers: { registrations: "users/registrations" }
# app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
after_action :create_default_team, only: :create
private
def create_default_team
Team.create_default(#user) if #user.persisted?
end
end
I'm using Rails 4 with Devise 3.5 with confirmable and had to do this due to various surprises.
class User < ActiveRecord::Base
# don't use after_create, see https://github.com/plataformatec/devise/issues/2615
after_commit :do_something, on: :create
private
def do_something
# don't do self.save, see http://stackoverflow.com/questions/22567358/
self.update_column(:my_column, "foo")
end
end

Rails: Make different references to a DB row refer to the same Ruby object

Suppose I have the following model relationship:
class Player < ActiveRecord::Base
has_many :cards
end
class Card < ActiveRecord::Base
belongs_to :player
end
I know from this question that Rails will return me a copy of the object representing a database row, meaning that:
p = Player.find(:first)
c = p.cards[0]
c.player.object_id == p.object_id # => false
...and therefore if the Player model modifies self, and the Card model modifies self.player in the same request, then the modifications won't take any notice of each other and the last-saved one will overwrite the others.
I'd like to work around this (presumably with some form of caching), so that all requests for a Player with a given id would return the same object (identical object_ids), thereby allowing both models to edit the same object without having to perform a database save-and-reload. I have three questions:
Is there already a plugin or gem to do this?
Are there good reasons why I shouldn't do this?
Can anyone give me some pointers on how to go about doing this?
This is supported in Rails 3.x. You can use the :inverse_of option for the has_many association for example. Documentation here (search for :inverse_of and Bi-directional associations).