"upcasing!" model attributes

Hi.

A couple of days ago I posted a question about how to simplify the
task of converting string values to uppercase when saving a row and
with some help I got to the point of writing the following in a
before_save method to test the value’s contents:

self.attributes.each_pair do |k,v|
puts “Column #{k}: regular value = #{v} - upcase value =
#{v.upcase!}” if v.class.name == ‘String’
end

The problem I am having is that I can use upcase! all I want against
‘v’ in this case but that will not change the column value, which is
what I need to do. I have tried to use
self.attribute_for_name(k).upcase! and some other things but I either
got syntax errors (method not found, etc.) or what I tried did not
work. I am relatively new to RoR and any help would be greatly
appreciated.

Thanks.

Pepe

pepe wrote:

self.attributes.each_pair do |k,v|
puts “Column #{k}: regular value = #{v} - upcase value =
#{v.upcase!}” if v.class.name == ‘String’
end

Right. In this case, v is just a local variable of sorts, so it’s not
going to change the object. You need to do something along the lines of

self.send(k).upcase! if self.send(k).class == String

I just did a quick test in console, and that worked. There might be a
better way to do it, though.

Peace,
Phillip

before_save :upcase_attributes

private

Upcase any attributes that are strings

def upcase_attributes
self.attributes.each_pair do |key, value|
self[key] = value.upcase if self[key].is_a? String
end
end

That’s probably the most appropriate way to do it. Instead of using the
methods, I just access the attributes themselves which can be faster.

Also note that I’m not overriding before_save directly, but rather
using a
method call. This way I can do multiple things before saving.

Have you tried explicitly setting v = v.upcase in your before_save
callback?

Hi –

On Fri, 11 Jul 2008, Brian H. wrote:

Also note that I’m not overriding before_save directly, but rather using a
method call. This way I can do multiple things before saving.

You can actually mix and match. If you do this:

class MyModel < ARB

def before_save
puts “Instance method”
end

before_save :do_something
before_save { puts “block given to before_save” }
before_save { puts “another block given to before_save” }
def do_something
puts “Method referenced by before_save”
end
end

then when you save, you’ll get this:

Method referenced by before_save
block given to before_save
another block given to before_save
Instance method

David


Rails training from David A. Black and Ruby Power and Light:
Intro to Ruby on Rails July 21-24 Edison, NJ
Advancing With Rails August 18-21 Edison, NJ
See http://www.rubypal.com for details and updates!

Hi –

On Fri, 11 Jul 2008, Reid Lynch wrote:

#{v.upcase!}" if v.class.name == ‘String’

Have you tried explicitly setting v = v.upcase in your before_save
callback?

That won’t work. It just attaches the identifier ‘v’ to a new object.

h = { “one” => 1 }
h.each {|k,v| v = “ONE” }
p h # => { “one” => 1 }

You have to assign to the hash via its keys.

David


Rails training from David A. Black and Ruby Power and Light:
Intro to Ruby on Rails July 21-24 Edison, NJ
Advancing With Rails August 18-21 Edison, NJ
See http://www.rubypal.com for details and updates!

On 11 Jul 2008, at 22:27, David A. Black wrote:

You have to assign to the hash via its keys.

Although prior to rails 2.1 fiddling with the attributes hash is a
waste of time because it always used to return you a fresh (and deep)
copy of the attributes. This changed in rails 2.1

Fred

Hi –

On Fri, 11 Jul 2008, Frederick C. wrote:

h.each {|k,v| v = “ONE” }
p h # => { “one” => 1 }

You have to assign to the hash via its keys.

Although prior to rails 2.1 fiddling with the attributes hash is a
waste of time because it always used to return you a fresh (and deep)
copy of the attributes. This changed in rails 2.1

I had lapsed into focusing on the assignment semantics per se, but
indeed if it’s a copy anyway, it won’t do any good even if one does
assign to it. It doesn’t look like it changed in 2.1.0:

def attributes
self.attribute_names.inject({}) do |attrs, name|
attrs[name] = read_attribute(name)
attrs
end
end

Is there another way in that you’re thinking of, or maybe it’s on
edge?

David


Rails training from David A. Black and Ruby Power and Light:
Intro to Ruby on Rails July 21-24 Edison, NJ
Advancing With Rails August 18-21 Edison, NJ
See http://www.rubypal.com for details and updates!

On 11 Jul 2008, at 23:42, David A. Black wrote:

  attrs
end

end

Isn’t that ok though (in this particular case), since that’s basically
def attributes
self.attribute_names.inject({}) do |attrs, name|
attrs[name] = @attributes[name]
attrs
end
end

ie self.attributes does have as values the same objects that
@attributes has as values. We can’t changed attributes by assigning to
self.attributes, but in place (eg upcase!) modification of
self.attributes should work.

On the other hand, before 2.1 it was basically

attrs[name] = @attributes[name].clone

in which case you’ve haven’t got a snowballs chance in hell of
changing an attribute by acting on self.attributes.

I think this demonstrates that trying to modify attributes by acting
on self.attributes is not a good idea :slight_smile:

Fred

Hi –

On Sat, 12 Jul 2008, Frederick C. wrote:

  attrs[name] = @attributes[name]
  attrs
end

end

ie self.attributes does have as values the same objects that
@attributes has as values. We can’t changed attributes by assigning to
self.attributes, but in place (eg upcase!) modification of
self.attributes should work.

OK, I see what you mean. I was thinking you meant that assigning to
keys would work (e.g., obj.attributes[“name”] = “David”).

On the other hand, before 2.1 it was basically

attrs[name] = @attributes[name].clone

in which case you’ve haven’t got a snowballs chance in hell of
changing an attribute by acting on self.attributes.

I think this demonstrates that trying to modify attributes by acting
on self.attributes is not a good idea :slight_smile:

I agree. Of course there’s #attributes=, which thickens the plot a
bit.

David


Rails training from David A. Black and Ruby Power and Light:
Intro to Ruby on Rails July 21-24 Edison, NJ
Advancing With Rails August 18-21 Edison, NJ
See http://www.rubypal.com for details and updates!

Hey, that’s nifty. I love this community :slight_smile:

First of all I would like to thank everybody for their help. I have
learned quite a few things by reading all the postings. However I got
a little lost after Frederick C.'s posting and if you guys have
time I’d appreciate a comment on the following:

  1. What is a ‘deep’ copy of an attribute?
  2. What is @attributes? Based on what I know, could it be an instance
    variable of the model with attributes information?
  3. And finally, what is #attributes=? It’s the first time I see
    something like that.

And now to the code:

I first tried Phillip Koebe’s code but for some reason it wouldn’t
work but when I tried Brian H.'s it surely did the trick. Thanks a
lot Brian!

Reid, yes I tried to directly upcase the value but that wouldn’t work.
I tried pretty much everything I could think of with my limited
knowledge of RoR.

Again, thanks a lot for all the help and comments.

Pepe

On Jul 13, 2:38 pm, pepe [email protected] wrote:

First of all I would like to thank everybody for their help. I have
learned quite a few things by reading all the postings. However I got
a little lost after Frederick C.'s posting and if you guys have
time I’d appreciate a comment on the following:

  1. What is a ‘deep’ copy of an attribute?
    Say you have an array and you make a copy of it. That can be two
    things:
  • a new array (but which contains the same objects as the old one): a
    shallow copy
  • a new array, which contains copies of the contents of the old array:
    ( a deep copy)
  1. What is @attributes? Based on what I know, could it be an instance
    variable of the model with attributes information?
    It’s where the attributes are actually stored. I would consider this
    an implementation detail
  1. And finally, what is #attributes=? It’s the first time I see
    something like that.

The leading hash is a notation for instance methods. It means ‘the
method that is called when you do someobject.attributes = foo’

Fred

Thanks a lot for the explanations, Fred. I appreciate it. :slight_smile:

Pepe

On Jul 13, 1:40 pm, Frederick C. [email protected]

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs