0

I have a method I wish to use, to be make my code a little more legible (in my opinion)

My code:

def format_chosen_address(address)
  # Add country before the postcode
  postcode = address.split("\n").last
  country = postcode.is_welsh? ? 'WALES' : 'ENGLAND'
  address.split("\n").insert(-2, country).join(' ')
end


def is_welsh?
    welsh_postcodes = ['SA1 6AZ', 'SA7 9BR', 'CF10 1AX']
    welsh_postcodes.include? self
end

I get the following exception:

NoMethodError: undefined method `is_welsh?' for "SA1 6AZ":String

Where have I gone wrong because thought self and boolean methods could be used this way?

I can get it to work if I do the following, but it doesn't read as nice to me.

def format_chosen_address(address)
  # Add country before the postcode
  postcode = address.split("\n").last
  country = is_welsh?(postcode) ? 'WALES' : 'ENGLAND'
  address.split("\n").insert(-2, country).join(' ')
end

def is_welsh?(postcode
  welsh_postcodes = ['SA1 6AZ', 'SA7 9BR', 'CF10 1AX']
  welsh_postcodes.include? postcode
end
4
  • 2
    The is_welsh? method will only work like that if you define it inside the String class Commented Jan 23, 2020 at 11:23
  • Of course!! Thank you Commented Jan 23, 2020 at 11:31
  • 1
    In Ruby you usually avoid is_ or has_ prefixes for predicate methods. Commented Jan 23, 2020 at 12:09
  • 2
    When you have a method that you would like to call on something such as postal_code.welsh? postal code starts looking less and less like a primitive string and more like it's own class of thing, so even if consideration determines "not worth it's own class" it's at least worth that consideration; a postal code class may be too specific but you might see other classes that make sense and, say, promote address (there's a lot of functionality for address in here, as well) instead of the specific postal code. Commented Jan 23, 2020 at 12:19

3 Answers 3

2

The problem is that you are calling postcode.is_welsh? which means you are calling the method is_welsh? on the string, but you didn't define the method on String but within another class.

Actually, I advise adding methods like to the String class. It is too specific to pollute the whole String namespace. Instead, I suggest just leaving that method as a (probably as a private) helper method and passing the postal code string to it as an argument:

def is_welsh?(postcode)
  welsh_postcodes = ['SA1 6AZ', 'SA7 9BR', 'CF10 1AX']
  welsh_postcodes.include? postcode
end

and call it like:

country = is_welsh?(postcode) ? 'WALES' : 'ENGLAND'
Sign up to request clarification or add additional context in comments.

1 Comment

100% I don't want to be adding to the String xlass for something so small
1

You can define the is_welsh? inside the String class like this:

class String
  def is_welsh?
    welsh_postcodes = ['SA1 6AZ', 'SA7 9BR', 'CF10 1AX']
    welsh_postcodes.include? self
  end
end

But it doesn't really make sense for every String to have a method that tells if it's Welsh or not. So the better option is to make it a method that takes a parameter (like your second example in the question):

def is_welsh?(postcode)
  welsh_postcodes = ['SA1 6AZ', 'SA7 9BR', 'CF10 1AX']
  welsh_postcodes.include? postcode
end

Comments

0

You could also use a module to add certain helpers to the singleton class of the string. Meaning that they are only added for that specific instance of String and not for all strings.

module PostcodeHelpers
  def is_welsh?
    welsh_postcodes = ['SA1 6AZ', 'SA7 9BR', 'CF10 1AX']
    welsh_postcodes.include?(self)
  end

  def country
    is_welsh? ? 'WALES' : 'ENGLAND'
  end
end

def format_chosen_address(address)
  *lines, postcode = address_lines(address)
  [*lines, postcode.country, postcode].join(' ')
end

private # assuming you are in class or module context

def address_lines(address)
  *lines, postcode = address.split("\n")

  # Default to empty string since you don't want to extend nil by accident.
  postcode ||= ''
  postcode.extend(PostcodeHelpers)

  [*lines, postcode]
end

Like you can see this might be overkill for this scenario and produces quite a bit of additional code.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.