Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace word using gsub function in ruby

i am trying to replace some word from a string with gsub function in ruby, but sometimes that works fine and in some cases giving this error? is there any issues with this format

NoMethodError (undefined method `gsub!' for nil:NilClass):   

model.rb

class Test < ActiveRecord::Base
  NEW = 1
  WAY = 2
  DELTA = 3

  BODY = {

    NEW => "replace this ID1",
    WAY => "replace this ID2 and ID3",
    DELTA => "replace this ID4"
  }
end

another_model.rb

class Check < ActiveRecord::Base
  Test::BODY[2].gsub!("ID2", self.id).gsub!("ID3", self.name)
end
like image 851
Developer Avatar asked Feb 06 '15 10:02

Developer


2 Answers

Ah, I found it! gsub! is a very weird method. Firstly, it replaces the string in place, so it actually modifies your string. Secondly, it returns nil when no substitution has been made. This all sums up to the error you're getting.

The first time you execute that call, it modifies the string assigned to a constant, so it reads as "replace this 3 and name". When you try to run it for a second time, the first gsub will fail to find a string it is looking for so will return nil. The second gsub is then executed on the nil.

On how to solve it - it all depends on what you're trying to achieve. For me, it is somewhat risky to change other class constants (breaks encapsulation). If you just want to get the result without modifying the original string, use gsub (no bang). Or even better, convert those string into a method and use interpolation instead of substitution.

like image 181
BroiSatse Avatar answered Nov 02 '22 03:11

BroiSatse


If the strings are just patterns, that should be replaced before get used. A better way would be string Interpolation.

class Test < ActiveRecord::Base
  # Here use symbols instead, because symbols themselfs are immutable
  # so :way will always equal :way
  BODY = {
    :new => "replace this %{ID1}",
    :way => "replace this %{ID2} and %{ID3}",
    :delta => "replace this %{ID4}"
  }    
end
# HERE you should create another constant based on the 
# Test class constants
class Check < ActiveRecord::Base
  BODY = {
         :way => Test::BODY[:way] % {ID2: self.id, ID3: self.name}
  }

 # Or you can make it a method
 def self.body
   Test::BODY[:way] % {ID2: self.id, ID3: self.name}
 end
end

this will change every apearance of the hash keys in the string

for example:

str = "%{num1} / %{num1} = 1"
str % {num1: 3} # 3 / 3 = 1

And like @BroiSatse said, you should not change constants of other classes or within the same class itself, at the end they are constants.

like image 33
Nafaa Boutefer Avatar answered Nov 02 '22 03:11

Nafaa Boutefer