create a record in a different class(table) - ruby-on-rails

I have a class People and class User (from Devise).
When someone signs up a user row(object) gets created in the User class(table).
I would also like the user.rb model to create a row(object) in the People class(table).
(The user.rb also has "has_one :person" in it.)
I tried the following without success:
after_create :create_person
protected
def create_person
self.create_person email: self.email
end
How could I code this?

after_create :create_person
protected
def create_person
Person.create(self.attributes)
end
But take care, if you want to update the person record when the corresponding user record is updated use after_save and Person.find_or_create_by_email(self.email)

The code you show should work,
the only reason why it might not work would be validations.
Doing create_person will do a save not a save!.
If you have validation on the Person model, it may be failing.

Related

Rails: callback on another model

In an application I have, there are 2 models: Comment and User
I want for example to be able to call a method in User when Comment is updated; as an example send_email. Why not just write send_email in Comment? Because then I'd be contradicting encapsulation.
My question then is, what is the right way to call a callback method on another model. Example:
after_update :user.send_email
Yes you are right, after_update can be used for the callback.Assuming the comment and user mappings generically
#comment.rb
after_update :send_email_to_user
private
def send_email_to_user
user.send_email
end
#user.rb
def send_email
end
after_update takes either the name of a method. In your example :user.send_email is not a valid method name. One way is to create a method that calls send_email on the user object, and then register this as the callback. See Rails Guides for full documentation on Active Record Callbacks.
after_update :send_email
def send_email
user.send_email
end

how to run a one-time database change on a single user

I have Customer and each customer has_many Properties. Customers belong to a Company.
I'm trying to add a certain Property to each one of a single Company's Customers. I only want this change to happen once.
I'm thinking about using a migration but it doesn't seem right to create a migration for a change that I only ever want to happen once, and only on one of my users.
Is there a right way to do this?
You can just use rails console.
In rails c:
Company.where(conditions).last.customers.each do |customer|
customer.properties << Property.where(condition)
customer.save!
end
Validation
Depending on how you're changing the Customer model, I'd include a simple vaidation on the before_update callback to see if the attribute is populated or not:
#app/models/Customer.rb
class Customer < ActiveRecord::Base
before_update :is_valid?
private
def is_valid?
return if self.attribute.present?
end
end
This will basically check if the model has the attribute populated. If it does, it means you'll then be able to update it, else it will break
--
Strong_Params
An alternative will be to set the strong_params so that the attribute you want to remain constant will not be changed when you update / create the element:
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
...
private
def strong_params
params.require(:model).permit(:only, :attributes, :to, :update)
end
end
It would be much more helpful if you explained the context as to why you need this type of functionality - that will give people the ability to create a real solution, instead of proposing ideas

Devise: Capitalize First Name

I added a new field in devise called firstname, and I want it to be capitalized by devise during registration.
I first ran:
rails generate migration add_username_to_users firstname:string
then
rake db:migrate
After that I added firstname to the configure_permitted_parameters in the application_controller.rb and updated the views. I basically used this but stripped out some unnecessary stuff.
I dont know where I should put the code for capitalizing the firstname and lastname (as well as some other validating). Any guidance would be appreciated. Thanks.
I think you should put capitalization of first and last names in your User model. Every time a user is saved, you can capitalize the first and last name. In addition, all validation (or attribute pre-processing/sanitization) can be done at the model level as well.
class User < ActiveRecord::Base
before_save :capitalize_names
def capitalize_names
self.firstname = firstname.camelcase
self.lastname = lastname.camelcase
end
end
before_create
Joe Kennedy's answer is correct - you should use the before_create ActiveRecord callback
The difference here is that Devise doesn't do anything with your actual data modelling - it basically just creates a series of controllers to handle the user registration & login processes
--
If you want to ensure certain attributes of your User model are saved in a particular style, you'll be best setting it in the model itself:
#app/models/user.rb
Class User < ActiveRecord::Base
before_create :set_firstname
private
def set_firstname
self.firstname.titeize
end
end
This should allow you to set the attribute to have the first letters of each word capitalized
--
System
An alternative would be to look at your system
Why are you insisting the data be stored this way? It seems very inefficient to save all your data in the same for the sake of styling.
I would use the CSS text-transform function to do this:
#app/assets/stylesheets/application.css
.first_name { text-transform: capitalize; }
#app/views/users/show.html.erb
<%= content_tag :span, #user.firstname, class: "first_name" %>
Best Solution Ever:
class Role < ApplicationRecord
before_save :capitalize_names
def capitalize_names
self.name.titlecase
end
end
Output will be:
'super admin'.titlecase
Super Admin
This should probably go in the User Controller (or whichever controller inherits from the Devise Controller and creates the new user). In the create method, before you save the user to the database, add whatever attributes you want to it (i.e. capitalizing the first letter) and then save it.
def create
User.create(email: params[:email], first_name: params[:first_name].capitalize)
end
Although I'd suggest you just output the capitalize in your views and not when saving.

How to create permalink with id?

How would I create a permalink with an id for a new model?
E.g
animal = Animal.create(name: 'cool dog') #creates animal with id of 1 and name of dog
animal.permalink => "1-cool-dog"
How do you add the proper callback so that id is inserted? before_save or after_save doesn't work
after_save :update_permalink #or before_save
def update_permalink
self.permalink = "#{id} #{name}".parameterize
end
What ends up happening is I get "cool-dog" instead of "1-cool-dog"
And I get why. It's setting an attribute without saving it on after_save. But doesn't work on before_save either because id hasn't been created on a new record.
According to http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
You should use after_commit instead of after_save
Both save and destroy come wrapped in a transaction that ensures that
whatever you do in validations or callbacks will happen under its
protected cover. So you can use validations to check for values that
the transaction depends on or you can raise exceptions in the
callbacks to rollback, including after_* callbacks.
As a consequence changes to the database are not seen outside your
connection until the operation is complete. For example, if you try to
update the index of a search engine in after_save the indexer won’t
see the updated record. The after_commit callback is the only one that
is triggered once the update is committed. See below.
As I commented above you may want to simply override the to_param method of your Animal Model like this.
def to_param
"#{id}-#{name.parameterize}"
end
This will make all of your urls automatically like the permalink you are trying to create and you can still use Animal.find(params[:id])
Perhaps you don't need to save the permalink to the database at all.
def permalink
"#{self.id} #{self.name}"
end
This approach would add a permalink to the model by concatenating the id and name each time the permalink is read.

Ruby on Rails 3 - create an instance of one model from another model

I have the following problem, but first I will make some assumptions
The example is just to explain my problem in an easy way
The tables models are not related
I have two tables (models) Users an Emails
Users
id
name
email
Emails
id
account
So, the idea is every time I create a user, I want to create an instance of Email, where Emails.account = Users.email
I tried using callback
def after_create
Email.create!(:account => user.email)
end
But it didn't work.
Is there another way to achieve this?
You are almost there, except you don't need to reference a 'user' variable in your after_create because you are in the User model.
Try the following:
class User < ActiveRecord::Base
after_create :create_email
def create_email
Email.create!(:account => email)
end
end

Resources