Ruby weird assignment behaviour - ruby

Is this a ruby bug?
target_url_to_edit = target_url
if target_url_to_edit.include?("http://")
target_url_to_edit["http://"] = ""
end
logger.debug "target url is now #{target_url}"
This returns target_url without http://

You need to duplicate the in-memory object because variable names are just references to in-memory objects:
target_url_to_edit = target_url.dup
Now target_url_to_edit gets assigned a new copy of the original object.
For your case this code probably does the same in just one line (no dup, no if):
target_url_to_edit = target_url.sub(%r{^http://}, "")

No, this is not a bug in Ruby, this is just how shared mutable state works, not just in Ruby but in any programming language.
Think about it this way: my mom calls me "son", my friends call me "Jörg". If I cut my hair, then it doesn't matter which name you use to refer to me: I am the same person, regardless of whether you call me "son" or "Jörg" or "Mr. Mittag" or "hey, douchebag", therefore my hair will always be short. It doesn't magically grow back if you call me by a different name.
The same thing happens in your code: you refer to the string by two different names, but it doesn't matter which name you use; if the string changes, then it changes.
The solution is, of course, to not share mutable state and to not mutate shared state, like in #hurikhan77's answer.

That is not a bug. It is the intended behavior because target_url_to_edit points to the same object in memory as target_url since Ruby uses references for object assignment. If you know C, it is similar to pointers.

Here is how to change its behaviour to force passing by value (note the star sign):
target_url_to_edit = *target_url.to_s
if target_url_to_edit.include?("http://")
target_url_to_edit["http://"] = ""
end
logger.debug "target url is now #{target_url}"
And just like many things in ruby, hard to find where it's documented...

Related

Why is there no `.split!` in Ruby?

It just seems pretty logical to have it when there's even a downcase!. Has anyone else run into this use case in Ruby?
For the curious, I'm trying to do this:
def some_method(foo)
foo.downcase!.split!(" ")
## do some stuff with foo later. ##
end
some_method("A String like any other")
Instead of this:
def some_method(foo)
foo = foo.downcase.split(" ")
## do some stuff with foo later. ##
end
some_method("A String like any other")
Which isn't a really big deal...but ! just seems cooler.
Why is there no .split! in Ruby?
It just seems pretty logical to have it when there's even a downcase!.
It may be logical, but it is impossible: objects cannot change their class or their identity in Ruby. You may be thinking of Smalltalk's become: which doesn't and cannot exist in Ruby. become: changes the identity of an object and thus can also change its class.
I don't see this "use case" as very important.
The only thing a "bang method" is doing is saving you the trouble of assigning a variable.
The reason "bang methods" are the exception instead of the rule is they can produce confusing results if you don't understand them.
i.e. if you write
a = "string"
def my_upcase(string)
string.upcase!
end
b = my_upcase(a)
then both a and b will have transformed value even if you didn't intend to change a. Removing the exclamation point fixes this example, but if you're using mutable objects such as hashes and arrays you'll have to look out for this in other situations as well.
a = [1,2,3]
def get_last_element(array)
array.pop
end
b = get_last_element(a)
Since Array#pop has side effects, a is now 1,2. It has the last element removed, which might not have been what you intended. You could replace .pop here with [-1] or .last to get rid of the side effect
The exclamation point in a method name is essentially warning you that there are side effects. This is important in the concept of functional programming, which prescribes side effect free code. Ruby is very much a functional programming language by design (although it's very object oriented as well).
If your "use case" boils down to avoiding assigning a variable, that seems like a really minor discomfort.
For a more technical reason, though, see Jorg Mittag's answer. It's impossible to write a method which changes the class of self
this
def some_method(foo)
foo = foo.downcase.split(" ")
end
some_method("A String like any other")
is the same as this
def some_method(foo)
foo.downcase.split
end
some_method("A String like any other")
Actually, both of your methods return the same result. We can look at a few examples of methods that modify the caller.
array.map! return a modified original array
string.upcase! return a modified original string
However,
split modifies the class of the caller, changing a string to an array.
Notice how the above examples only modify the content of the object, instead of changing its class.
This is most likely why there isn't a split! method, although it's pretty easy to define one yourself.
#split creates an array out of a string, you can't permanently mutate(!) the string into being an array. Because the method is creating a new form from the source information(string), the only thing you need to do to make it permanent, is to bind it to a variable.

Constant Assignment Bug in Ruby?

We caught some code in Ruby that seems odd, and I was wondering if someone could explain it:
$ irb
irb(main):001:0> APPLE = 'aaa'
=> "aaa"
irb(main):002:0> banana = APPLE
=> "aaa"
irb(main):003:0> banana << 'bbb'
=> "aaabbb"
irb(main):004:0> banana
=> "aaabbb"
irb(main):005:0> APPLE
=> "aaabbb"
Catch that? The constant was appended to at the same time the local variable was.
Known behavior? Expected?
Known behaviour. Constants don't mean that you can't modify the object it refers to, merely that it'll give you a warning (and only a warning) if you assign it to a different object.
In short, ruby constants aren't.
Note: This behaviour is listed in an answer to "What are the Ruby Gotchas a newbie should be warned about?" It's a worthwhile read.
Catch that? The constant was appended to at the same time the local variable was.
No, it wasn't appended to, and neither was the local variable.
The single object that both the constant and the local variable are referring to was appended to, but neither the constant nor the local variable was changed. You cannot modify or change a variable or constant in Ruby (at least not in the way that your question implies), the only thing you can change is objects.
The only two things you can do with variables or constants is dereferencing them and assigning to them.
The constant is a red herring here, it is completely irrelevant to the example given. The only thing that is relevant is that there is only one single object in the entire example. That single object is accessible under two different names. If the object changes, then the object changes. Period. It does not mysteriously split itself in two. Which name you use to look at the changed object doesn't matter. There is only one object anyway.
This works exactly the same as in any other programming language: if you have multiple references to a mutable object in, say, Python, Java, C#, C++, C, Lisp, Smalltalk, JavaScript, PHP, Perl or whatever, then any change to that object will be visible no matter what reference is used, even if some of those references are final or const or whatever that particular language calls it.
This is simply how shared mutable state works and is just one of the many reasons why shared mutable state is bad.
In Ruby, you can generally call the freeze method on any object to make it immutable. However, again, you are modifying the object here, so anybody else who has a reference to that object will all the sudden find that the object has become immutable. Therefore, just to be safe, you need to copy the object first, by calling dup. But of course, that's not enough either, if you think of an array, for example: if you dup the array, you get a different array, but the objects inside the array are the still the same ones in the original array. And if you freeze the array, then you can no longer modify the array, but the objects in the array may very well still be mutable:
ORIG = ['Hello']
CLONE = ORIG.dup.freeze
CLONE[0] << ', World!'
CLONE # => ['Hello, World!']
That's shared mutable state for you. The only way to escape this madness is either to give up shared state (e.g. Actor Programming: if nobody else can see it, then it doesn't matter how often or when it changes) or mutable state (i.e. Functional Programming: if it never changes, then it doesn't matter how many others see it).
The fact that one of the two variables in the original example is actually a constant is completely irrelevant to the problem. There two main differences between a variable and a constant in Ruby: they have different lookup rules, and constants generate a warning if they are assigned to more than once. But in this example, the lookup rules are irrelevant and the constant is assigned to only once, so there really is no difference between a variable and a constant in this case.
You can freeze constants if you want them to be unchangable:
>> APPLE = 'aaa'
=> "aaa"
>> banana = APPLE
=> "aaa"
>> APPLE.freeze
=> "aaa"
>> banana.frozen?
=> true
>> banana << 'bbb'
TypeError: can't modify frozen string
from (irb):5:in `<<'
from (irb):5
Constants in Ruby aren't "constants". You might as well use any other name; putting them in all caps doesn't change anything, interpreter-wise, about the object, unless you try to change the pointer's address.
If you look at it that way, the behavior is obvious and necessary; Apple is a pointer to a string object, and so is banana. You then edit the object that banana is pointing to. Apple is pointing to that same object, so the change is reflected there too.

Why is "#{String}" a common idiom in Ruby

A Ruby dev I know asked this; my answer is below... Are there other, better reasons?
Why do so many Ruby programmers do
"#{string}"
rather than
string
since the second form is simpler and more efficient?
Is this a common idiom for Ruby developers? I don't see it that much.
Smaller changes when you later need to do more than simply get the value of the string, but also prepend/append to it at the point of use seems to be the best motivation I can find for that idiom.
There is only one case where this is a recommended idiom :
fname = 'john'
lname = 'doe'
name = "#{fname} #{lname}"
The code above is more efficient than :
name = fname + ' ' + lname
or
name = [fname, lname].join(' ')
What's the broader context of some of the usages? The only thing I can come up with beyond what's already been mentioned is as a loose attempt at type safety; that is, you may receive anything as an argument, and this could ensure that whatever you pass in walks like a duck..or, well, a string (though string.to_s would arguably be clearer).
In general though, this is probably a code smell that someone along the way thought was Best Practices.
I use this kind of code, so that I can pass nil as string and it still will work on a string, rather than seeing some exceptions flying:
def short(string = nil)
"#{string}"[0..7]
end
And it's easier/faster to append some debug code, if it's already in quotes.
So in short: It's more convenient.
Interesting answers, everyone. I'm the developer who asked the original question. To give some more context, I see this occasionally at my current job, and also sometimes in sample code on the Rails list, with variables that are known in advance to contain strings. I could sort of understand it as a substitute for to_s, but I don't think that's what's going on here; I think people just forget that you don't need the interpolation syntax if you're just passing a string variable.
If anyone tried to tell me this was a best practice, I'd run away at top speed.
maybe it is easy way to convert any to string? Because it is the same as call to_s method. But it is quite strange way :).
a = [1,2,3]
"#{a}"
#=> "123"
a.to_s
#=> "123"
I could image this being useful in cases where the object being interpolated is not always a String, as the interpolation implicitly calls #to_s:
"#{'bla'}" => "bla"
"#{%r([a-z])}" => "(?-mix:[a-z])"
"#{{:bla => :blub}}" => "blablub"
May make sense when logging something, where you don't care so much about the output format, but never want an error because of a wrong argument type.

Reclassing an instance in Python

I have a class that is provided to me by an external library. I have created a subclass of this class. I also have an instance of the original class.
I now want to turn this instance into an instance of my subclass without changing any properties that the instance already has (except for those that my subclass overrides anyway).
The following solution seems to work.
# This class comes from an external library. I don't (want) to control
# it, and I want to be open to changes that get made to the class
# by the library provider.
class Programmer(object):
def __init__(self,name):
self._name = name
def greet(self):
print "Hi, my name is %s." % self._name
def hard_work(self):
print "The garbage collector will take care of everything."
# This is my subclass.
class C_Programmer(Programmer):
def __init__(self, *args, **kwargs):
super(C_Programmer,self).__init__(*args, **kwargs)
self.learn_C()
def learn_C(self):
self._knowledge = ["malloc","free","pointer arithmetic","curly braces"]
def hard_work(self):
print "I'll have to remember " + " and ".join(self._knowledge) + "."
# The questionable thing: Reclassing a programmer.
#classmethod
def teach_C(cls, programmer):
programmer.__class__ = cls # <-- do I really want to do this?
programmer.learn_C()
joel = C_Programmer("Joel")
joel.greet()
joel.hard_work()
#>Hi, my name is Joel.
#>I'll have to remember malloc and free and pointer arithmetic and curly braces.
jeff = Programmer("Jeff")
# We (or someone else) makes changes to the instance. The reclassing shouldn't
# overwrite these.
jeff._name = "Jeff A"
jeff.greet()
jeff.hard_work()
#>Hi, my name is Jeff A.
#>The garbage collector will take care of everything.
# Let magic happen.
C_Programmer.teach_C(jeff)
jeff.greet()
jeff.hard_work()
#>Hi, my name is Jeff A.
#>I'll have to remember malloc and free and pointer arithmetic and curly braces.
However, I'm not convinced that this solution doesn't contain any caveats I haven't thought of (sorry for the triple negation), especially because reassigning the magical __class__ just doesn't feel right. Even if this works, I can't help the feeling there should be a more pythonic way of doing this.
Is there?
Edit: Thanks everyone for your answers. Here is what I get from them:
Although the idea of reclassing an instance by assigning to __class__ is not a widely used idiom, most answers (4 out of 6 at the time of writing) consider it a valid approach. One anwswer (by ojrac) says that it's "pretty weird at first glance," with which I agree (it was the reason for asking the question). Only one answer (by Jason Baker; with two positive comments & votes) actively discouraged me from doing this, however doing so based on the example use case moreso than on the technique in general.
None of the answers, whether positive or not, finds an actual technical problem in this method. A small exception is jls who mentions to beware of old-style classes, which is likely true, and C extensions. I suppose that new-style-class-aware C extensions should be as fine with this method as Python itself (presuming the latter is true), although if you disagree, keep the answers coming.
As to the question of how pythonic this is, there were a few positive answers, but no real reasons given. Looking at the Zen (import this), I guess the most important rule in this case is "Explicit is better than implicit." I'm not sure, though, whether that rule speaks for or against reclassing this way.
Using {has,get,set}attr seems more explicit, as we are explicitly making our changes to the object instead of using magic.
Using __class__ = newclass seems more explicit because we explicitly say "This is now an object of class 'newclass,' expect a different behaviour" instead of silently changing attributes but leaving users of the object believing they are dealing with a regular object of the old class.
Summing up: From a technical standpoint, the method seems okay; the pythonicity question remains unanswered with a bias towards "yes."
I have accepted Martin Geisler's answer, because the Mercurial plugin example is a quite strong one (and also because it answered a question I even hadn't asked myself yet). However, if there are any arguments on the pythonicity question, I'd still like to hear them. Thanks all so far.
P.S. The actual use case is a UI data control object that needs to grow additional functionality at runtime. However, the question is meant to be very general.
Reclassing instances like this is done in Mercurial (a distributed revision control system) when extensions (plugins) want to change the object that represent the local repository. The object is called repo and is initially a localrepo instance. It is passed to each extension in turn and, when needed, extensions will define a new class which is a subclass of repo.__class__ and change the class of repo to this new subclass!
It looks like this in code:
def reposetup(ui, repo):
# ...
class bookmark_repo(repo.__class__):
def rollback(self):
if os.path.exists(self.join('undo.bookmarks')):
util.rename(self.join('undo.bookmarks'), self.join('bookmarks'))
return super(bookmark_repo, self).rollback()
# ...
repo.__class__ = bookmark_repo
The extension (I took the code from the bookmarks extension) defines a module level function called reposetup. Mercurial will call this when initializing the extension and pass a ui (user interface) and repo (repository) argument.
The function then defines a subclass of whatever class repo happens to be. It would not suffice to simply subclass localrepo since extensions need to be able to extend each other. So if the first extension changes repo.__class__ to foo_repo, the next extension should change repo.__class__ to a subclass of foo_repo and not just a subclass of localrepo. Finally the function changes the instanceø's class, just like you did in your code.
I hope this code can show a legitimate use of this language feature. I think it's the only place where I've seen it used in the wild.
I'm not sure that the use of inheritance is best in this case (at least with regards to "reclassing"). It seems like you're on the right track, but it sounds like composition or aggregation would be best for this. Here's an example of what I'm thinking of (in untested, pseudo-esque code):
from copy import copy
# As long as none of these attributes are defined in the base class,
# this should be safe
class SkilledProgrammer(Programmer):
def __init__(self, *skillsets):
super(SkilledProgrammer, self).__init__()
self.skillsets = set(skillsets)
def teach(programmer, other_programmer):
"""If other_programmer has skillsets, append this programmer's
skillsets. Otherwise, create a new skillset that is a copy
of this programmer's"""
if hasattr(other_programmer, skillsets) and other_programmer.skillsets:
other_programmer.skillsets.union(programmer.skillsets)
else:
other_programmer.skillsets = copy(programmer.skillsets)
def has_skill(programmer, skill):
for skillset in programmer.skillsets:
if skill in skillset.skills
return True
return False
def has_skillset(programmer, skillset):
return skillset in programmer.skillsets
class SkillSet(object):
def __init__(self, *skills):
self.skills = set(skills)
C = SkillSet("malloc","free","pointer arithmetic","curly braces")
SQL = SkillSet("SELECT", "INSERT", "DELETE", "UPDATE")
Bob = SkilledProgrammer(C)
Jill = Programmer()
teach(Bob, Jill) #teaches Jill C
has_skill(Jill, "malloc") #should return True
has_skillset(Jill, SQL) #should return False
You may have to read more about sets and arbitrary argument lists if you aren't familiar with them to get this example.
This is fine. I've used this idiom plenty of times. One thing to keep in mind though is that this idea doesn't play well with old-style classes and various C extensions. Normally this wouldn't be an issue, but since you are using an external library you'll just have to make sure you're not dealing with any old-style classes or C extensions.
"The State Pattern allows an object to alter its behavior when its internal state changes. The object will appear to change it's class." - Head First Design Pattern. Something very similar write Gamma et.al. in their Design Patterns book. (I have it at my other place, so no quote). I think that's the whole point of this design pattern. But if I can change the class of an object at runtime, most of the time i don't need the pattern (there are cases when State Pattern does more than simulate a class change).
Also, changing class at runtime doesn't always work:
class A(object):
def __init__(self, val):
self.val = val
def get_val(self):
return self.val
class B(A):
def __init__(self, val1, val2):
A.__init__(self, val1)
self.val2 = val2
def get_val(self):
return self.val + self.val2
a = A(3)
b = B(4, 6)
print a.get_val()
print b.get_val()
a.__class__ = B
print a.get_val() # oops!
Apart from that, I consider changing class at runtime Pythonic and use it from time to time.
Heheh, fun example.
"Reclassing" is pretty weird, at first glance. What about the 'copy constructor' approach? You can do this with the Reflection-like hasattr, getattr and setattr. This code will copy everything from one object to another, unless it already exists. If you don't want to copy methods, you can exclude them; see the commented if.
class Foo(object):
def __init__(self):
self.cow = 2
self.moose = 6
class Bar(object):
def __init__(self):
self.cat = 2
self.cow = 11
def from_foo(foo):
bar = Bar()
attributes = dir(foo)
for attr in attributes:
if (hasattr(bar, attr)):
break
value = getattr(foo, attr)
# if hasattr(value, '__call__'):
# break # skip callables (i.e. functions)
setattr(bar, attr, value)
return bar
All this reflection isn't pretty, but sometimes you need an ugly reflection machine to make cool stuff happen. ;)
This technique seems reasonably Pythonic to me. Composition would also be a good choice, but assigning to __class__ is perfectly valid (see here for a recipe that uses it in a slightly different way).
In ojrac's answer, the break breaks out of the for-loop and doesn't test any more attributes. I think it makes more sense to just use the if-statement to decide what to do with each attribute one at a time, and continue through the for-loop over all attributes. Otherwise, I like ojrac's answer, as I too see assigning to __class__ as weird. (I'm a beginner with Python and as far as I remember this is my first post to StackOverFlow. Thanks for all the great information!!)
So I tried to implement that. I noticed that dir() doesn't list all the attributes. http://jedidjah.ch/code/2013/9/8/wrong_dir_function/ So I added 'class', 'doc', 'module' and 'init' to the list of things to add if they're not there already, (although they're probably all already there), and wondered whether there were more things dir misses. I also noticed that I was (potentially) assigning to 'class' after having said that was weird.
I will say this is perfectly fine, if it works for you.

Why are exclamation marks used in Ruby methods?

In Ruby some methods have a question mark (?) that ask a question like include? that ask if the object in question is included, this then returns a true/false.
But why do some methods have exclamation marks (!) where others don't?
What does it mean?
In general, methods that end in ! indicate that the method will modify the object it's called on. Ruby calls these as "dangerous methods" because they change state that someone else might have a reference to. Here's a simple example for strings:
foo = "A STRING" # a string called foo
foo.downcase! # modifies foo itself
puts foo # prints modified foo
This will output:
a string
In the standard libraries, there are a lot of places you'll see pairs of similarly named methods, one with the ! and one without. The ones without are called "safe methods", and they return a copy of the original with changes applied to the copy, with the callee unchanged. Here's the same example without the !:
foo = "A STRING" # a string called foo
bar = foo.downcase # doesn't modify foo; returns a modified string
puts foo # prints unchanged foo
puts bar # prints newly created bar
This outputs:
A STRING
a string
Keep in mind this is just a convention, but a lot of Ruby classes follow it. It also helps you keep track of what's getting modified in your code.
The exclamation point means many things, and sometimes you can't tell a lot from it other than "this is dangerous, be careful".
As others have said, in standard methods it's often used to indicate a method that causes an object to mutate itself, but not always. Note that many standard methods change their receiver and don't have an exclamation point (pop, shift, clear), and some methods with exclamation points don't change their receiver (exit!). See this article for example.
Other libraries may use it differently. In Rails an exclamation point often means that the method will throw an exception on failure rather than failing silently.
It's a naming convention but many people use it in subtly different ways. In your own code a good rule of thumbs is to use it whenever a method is doing something "dangerous", especially when two methods with the same name exist and one of them is more "dangerous" than the other. "Dangerous" can mean nearly anything though.
This naming convention is lifted from Scheme.
1.3.5 Naming conventions
By convention, the names of procedures
that always return a boolean value
usually end in ``?''. Such procedures
are called predicates.
By convention, the names of procedures
that store values into previously
allocated locations (see section 3.4)
usually end in ``!''. Such procedures
are called mutation procedures. By
convention, the value returned by a
mutation procedure is unspecified.
! typically means that the method acts upon the object instead of returning a result. From the book Programming Ruby:
Methods that are "dangerous," or modify the receiver, might be named with a trailing "!".
It is most accurate to say that methods with a Bang! are the more dangerous or surprising version. There are many methods that mutate without a Bang such as .destroy and in general methods only have bangs where a safer alternative exists in the core lib.
For instance, on Array we have .compact and .compact!, both methods mutate the array, but .compact! returns nil instead of self if there are no nil's in the array, which is more surprising than just returning self.
The only non-mutating method I've found with a bang is Kernel's .exit! which is more surprising than .exit because you cannot catch SystemExit while the process is closing.
Rails and ActiveRecord continues this trend in that it uses bang for more 'surprising' effects like .create! which raises errors on failure.
From themomorohoax.com:
A bang can used in the below ways, in order of my personal preference.
An active record method raises an error if the method does not do
what it says it will.
An active record method saves the record or a method saves an
object (e.g. strip!)
A method does something “extra”, like posts to someplace, or does
some action.
The point is: only use a bang when you’ve really thought about whether
it’s necessary, to save other developers the annoyance of having to
check why you are using a bang.
The bang provides two cues to other developers.
that it’s not necessary to save the object after calling the
method.
when you call the method, the db is going to be changed.
Simple explanation:
foo = "BEST DAY EVER" #assign a string to variable foo.
=> foo.downcase #call method downcase, this is without any exclamation.
"best day ever" #returns the result in downcase, but no change in value of foo.
=> foo #call the variable foo now.
"BEST DAY EVER" #variable is unchanged.
=> foo.downcase! #call destructive version.
=> foo #call the variable foo now.
"best day ever" #variable has been mutated in place.
But if you ever called a method downcase! in the explanation above, foo would change to downcase permanently. downcase! would not return a new string object but replace the string in place, totally changing the foo to downcase.
I suggest you don't use downcase! unless it is totally necessary.
!
I like to think of this as an explosive change that destroys all that has gone before it. Bang or exclamation mark means that you are making a permanent saved change in your code.
If you use for example Ruby's method for global substitutiongsub!the substitution you make is permanent.
Another way you can imagine it, is opening a text file and doing find and replace, followed by saving. ! does the same in your code.
Another useful reminder if you come from the bash world is sed -i has this similar effect of making permanent saved change.
Bottom line: ! methods just change the value of the object they are called upon, whereas a method without ! returns a manipulated value without writing over the object the method was called upon.
Only use ! if you do not plan on needing the original value stored at the variable you called the method on.
I prefer to do something like:
foo = "word"
bar = foo.capitalize
puts bar
OR
foo = "word"
puts foo.capitalize
Instead of
foo = "word"
foo.capitalize!
puts foo
Just in case I would like to access the original value again.
Called "Destructive Methods" They tend to change the original copy of the object you are referring to.
numbers=[1,0,10,5,8]
numbers.collect{|n| puts n*2} # would multiply each number by two
numbers #returns the same original copy
numbers.collect!{|n| puts n*2} # would multiply each number by two and destructs the original copy from the array
numbers # returns [nil,nil,nil,nil,nil]
My answer explains the significance of Ruby methods with exclamation marks/shebangs in the context of Ruby on Rails (RoR) model validations.
Essentially, whenever developers define Model validations (explained here), their ultimate goal is to decline a database record change & raise/throw the relevant exception(s) in case invalid data has been submitted to update the record in question.
RoR ActiveRecord gem defines various model manipulation methods (Ruby on Rails guides.). Among the methods, the valid? method is the only one that triggers validation without database action/modification. The rest of the methods attempt to change the database.
These methods trigger callbacks whenever they run. Some of the methods in the list feature a sister method with a shebang. What is the difference between the two? It has to do with the form of callback returned whenever a record validation fails.
Methods without the exclamation/shebang merely return a boolean false in the event of record validation failure while the methods with a shebang raise/throw an exception which can then be handled appropriately in code.
Just as a heads-up, since I experienced this myself.
In Ruby, ! mutates the object and returns it. Otherwise it will return nil.
So, if you are doing some kind of operations on an array for example, and call the method .compact! and there is nothig to compact, it will return nil.
Example:
arr = [1, 2, 3, nil]
arr.compact!
=> [1, 2, 3]
Run again arr.compact!
=> nil
It is better to explicitly return again the array arr if you need to use it down the line, otherwise you will get the nil value.
Example:
arr = [1, 2, 3]
arr.compact! => nil
arr # to get the value of the array

Resources