$1, $2, $3 ... are Perl legacy. They are capture group variables, that is, they capture the groups inside the regular expression.
A named group is indicated by parentheses. So, the first capture group matches ([^\.]), which is any non dot character, and (\..*) matches a dot character \. and any other characters after it.
Note that the second group is optional, so in the line below you have the ternary expression $2 ? $2 : "", which is a crypty-ish way to get either the value of the capture of a blank string.
The int, dec = $1, $2_or_blank_string is a parallel assignment. Ruby supports assigning more than one variable at once, it's not different than doing int = $1.reversed then dec = $2 So int now holds the integer part (reversed) and dec the decimal part of the number. We are interested in the first one for now.
The next empty while does a string substitution. The method gsub! replaces all occurences of the regular expression for the value in the seconf argument. But it returns nil if no change happened, which ends the while.
The /(,|\.|^)(\d{3})(\d)/ expression matches:
(,|\.|^) A comma, a point or the beginning of the string
(\d{3}) Three digits
(\d) A fourth digit
Then replaces it for \1\2,\3. The \n in a string substitution mean the nth capture group, just as the $n variables do. So, it basically does: if I have four digits, just add a comma after the third one. Repeat until no group of four digits is found
Then, just reverse the integer part again and append the decimal part.