2

I'd like to capitalize the first_name and last_name of my model instances using the before_save method. Of course I could do this:

before_save do 
  self.first_name = first_name.capitalize
  self.last_name = last_name.capitalize
end

But I'd much rather alter the two attributes in one fell swoop. Is there a way to select certain columns in my model and apply the desired method to them?

Carl Edwards
  • 11,370
  • 7
  • 46
  • 88
  • 1
    This is actually modifying the data before it is translated into a SQL query. This is still contained in only one INSERT/UPDATE statement – MrYoshiji Oct 07 '14 at 20:20
  • not sure if `downcase` capitalizes string character(s). Are you sure about what you're trying to do? – Surya Oct 07 '14 at 20:21
  • @Surya Sorry about that. Revised the code to reflect the question – Carl Edwards Oct 07 '14 at 20:22
  • @MrYoshiji Correct. Not so much "query" the database but for lack of a better term, select the desired columns of the model and apply the capitalize method. – Carl Edwards Oct 07 '14 at 20:25
  • But eventually you'll have to write those column names, right? Why you think that this is not the way you want it? – Surya Oct 07 '14 at 20:27

3 Answers3

3

You could do something like this

before_save :capitalize_attributes

private
   def capitalize_attributes
     capitalizable = ["first_name","last_name"]
     self.attributes.each do |attr,val|
       #based on comment either of these will work
       #if you want to store nil in the DB then
       self.send("#{attr}=",val.strip.capitalize) if capitalizable.include?(attr) && !val.nil?
       #if you want to store a blank string in the DB then 
        self.send("#{attr}=",val.to_s.strip.capitalize) if capitalizable.include?(attr)
     end
   end

Then you can just add the attributes you want capitalized to the capitalizable array. I use a similar code to upcase all Strings in certain models just to keep data clean an consistent.

engineersmnky
  • 19,379
  • 2
  • 31
  • 45
  • With this I get: `NoMethodError: undefined method 'strip' for nil:NilClass` – Carl Edwards Oct 07 '14 at 20:35
  • 1
    @CarlEdwards Okay so you are submitting nil as a value for something that's fine you can fix that see updated post. You could drop `#strip` if you want I like it because I don't want users submitting names like "John ". `#strip` will remove the leading and trailing whitespace in this case. – engineersmnky Oct 07 '14 at 20:37
  • This checks out thanks! Despite going through the Ruby docs I'm still a little unclear of what `send` accomplishes. Would mind explaining what it does in this case? – Carl Edwards Oct 07 '14 at 20:42
  • 1
    @CarlEdwards in simple terms `#send` allows you to call a method on on object without specifically writing out the method so this is actually calling `first_name= val.strip.capitalize` and `last_name= val.strip.capitalize`. This is in essence how every method is called although some use `public_send` depending on the scope it is being called from. For more in depth explanations that will not fit in a comment just google what does send do in ruby. – engineersmnky Oct 07 '14 at 20:46
  • Thanks so much for your help – Carl Edwards Oct 07 '14 at 20:52
1

Building on @engineersmnky's answer further for Rails 4+ with Concerns (more here):

app/models/concerns/model_hooks.rb

module ModelHooks
  extend ActiveSupport::Concern

  included do
    before_save :capitalize_attributes
  end

  def capitalize_attributes
     self.attributes.each do |attr,val|
       # if the attribute only has spaces, then this will store nil in the DB
       self.send("#{attr}=",val.strip.capitalize) if self.capitalizable_attrs.include?(attr) && !val.nil?
     end    
  end
end

then in your models:

class Trail < ApplicationRecord
  include ModelHooks

  def capitalizable_attrs
    ["name"] # return an array of attributes you want to capitalize
  end

end
Community
  • 1
  • 1
rmcsharry
  • 4,357
  • 4
  • 50
  • 87
0

This is just an another version of @engieeringmnky's answer:

before_save :capitalize_attributes

private
   def capitalize_attributes
     self.attributes.select{ |a| ["first_name","last_name"].include? a }.each do |attr, val|
       self.send("#{attr}=", val.try(:strip).try(:capitalize))
     end
   end
engineersmnky
  • 19,379
  • 2
  • 31
  • 45
Surya
  • 14,897
  • 2
  • 44
  • 70