swiknaba
swiknaba

Reputation: 83

How do you define & call a "variable.my_function" method in Ruby within a class?

So the basic setting is the following:

in app/services/SomeService/ABC.rb

class SomeService::ABC 
 FORBIDDEN_CHARS = {" " => "+", "'" => "%27", "/" => "%2F", ":" => "%3A", "&" => "%26"}
 def my_function(input)
  #now do something fancy with the input
  var = .... #a String is created
  var.to_linkable
 # continue with something fancy
 end

 def to_linkable
  FORBIDDEN_CHARS.each{|key,value| self.gsub!(key,value)}
 end
end

This results in the error: undefined method `to_linkable' for "bla bla":String

However in this way it works:

def to_linkable(link)
    FORBIDDEN_CHARS.each{|key,value| link.gsub!(key,value)}
end

but I have to call it with 'to_linkable(link)' instead of just putting 'link.to_linkable'. If I copy paste my code in the Terminal the first way, there is no errors when calling 'link.to_linkable'.

So my question is: How (and where?) do I properly create such a to_linkable method? I might want to use it later on so that's why I want it to be an easy to use method. Thanks very much :) And yes, I am a Ruby newbie, coming over from Matlab :D

Upvotes: 2

Views: 62

Answers (4)

Tom Lord
Tom Lord

Reputation: 28285

First of all, don't reinvent the wheel. Use URI.escape, instead of trying to manually define which characters to replace.

However, for the sake of learning, if we go with your implementation...

var is a string. If you wish to call a method on the string, then it must be defined in the String class. It is usually not advised to add methods to core classes like this, but the following would work:

class String
  FORBIDDEN_LINK_CHARS = {" " => "+", ...}
  def to_linkable
    FORBIDDEN_LINK_CHARS.each{|key,value| self.gsub!(key,value)}
    self
  end
end

"hello world".to_linkable # => "hello+world"

Alternatively, if you don't want to extend the String class, but still wish to call an instance method without passing the variable, then you need to somewhere set var as an instance variable in the class. This may or may not be a good idea, depending on the structure of the class.

For example, the following works:

class SomeService::ABC 
  FORBIDDEN_CHARS = {" " => "+", ...}
  def my_function(input)
    @var = .... #a String is created
    make_var_linkable
  end

  def make_var_linkable
    FORBIDDEN_CHARS.each{|key,value| @var.gsub!(key,value)}
  end
end

...However, going back to my original point, the actual implementation I'd recommend is neither of the above. Just do:

require 'uri'

class SomeService::ABC 
  def my_function(input)
    var = URI.escape(....)
  end
end

Upvotes: 3

Stefan
Stefan

Reputation: 114138

If you don't want to "pollute" the whole string class, you can extend a specific string instance with a module:

module SomeService
  FORBIDDEN_CHARS = {" " => "+", "'" => "%27", "/" => "%2F", ":" => "%3A", "&" => "%26"}

  class ABC
    def my_function(input)
      # ...
      var = # a String is created
      var.extend(Linkable)
      var.to_linkable
      # ...
    end
  end

  module Linkable
    def to_linkable
      FORBIDDEN_CHARS.each { |key, value| gsub!(key,value) }
    end
  end
end

Upvotes: 0

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

Both answers given so far are using FORBIDDEN_LINK_CHARS.length number of gsub operations, which is not quite optimal.

module StringWithForbiddenChars
  FORBIDDEN_LINK_CHARS = {" " => "+", ...}
  def to_linkable
    gsub Regexp.union(FORBIDDEN_LINK_CHARS.keys), FORBIDDEN_LINK_CHARS
  end
  def to_linkable!
    self.tap do |s|
      s.gsub! Regexp.union(FORBIDDEN_LINK_CHARS.keys), FORBIDDEN_LINK_CHARS
    end
  end
end
String.prepend StringWithForbiddenChars
# or to extend the specific instance of a string:
# (str = "hello world").extend(StringWithForbiddenChars)

"hello world".to_linkable
#⇒ "hello+world"

Upvotes: 2

Maxim Pontyushenko
Maxim Pontyushenko

Reputation: 3043

When you write class String; ...; end inside another class you are creating new class instead of editing existing ruby core String class. Basically it can be said like String::ABC != String. So you should firstly define your new String method like this:

class String
  FORBIDDEN_CHARS = {" " => "+", "'" => "%27", "/" => "%2F", ":" => "%3A", "&" => "%26"}

  def to_linkable
    FORBIDDEN_CHARS.each{ |key, value| self.gsub!(key,value) }
    self
  end      
end

After that you can use this method in any place with string objects. For example in your SomeService class

class SomeService
  def my_function(input)
    #now do something fancy with the input
    var = "#{input}    " #a String is created
    var.to_linkable
    # continue with something fancy
  end
end

So in the end you can do this:

SomeService.new.my_function("': 123123&")
# =>"%27%3A+123123%26++++"

Upvotes: 1

Related Questions