Search code examples
rubyhanami

NoMethodError: undefined method for String with custom module in Hanami


I am using Hanami and I created a custom module in /lib/supports/utils.rb. I'm requiring all the files located /lib/supports in /lib/myapp that it's like the following:

require 'hanami/model'
require 'hanami/mailer'

Dir["#{__dir__}/myapp/**/*.rb"].each { |file| require_relative file }
Dir["#{__dir__}/supports/**/*.rb"].each { |file| require_relative file }

Hanami::Model.configure do

# and so on

In /lib/supports/utils.rb, I have:

# using the gem 'slugify'
require 'slugify'

module MyModule
  module Utils
    module Slug
      def slug_it(random = false)
        if random
          slugify + '-' + SecureRandom.hex(10).to_s
        else
          slugify
        end
      end
    end
  end
end

I tried to include MyModule::Utils::Slug in a repository but it always returns NoMethodError: undefined method `slug_it' for "string":String. An example:

class EventRepository
  include Hanami::Repository
  include MyModule::Utils::Slug

  def self.create_or_update(attrs)
    found = find(attrs.id)
    attrs = event_attributes(attrs)

    if found
      unless found.slug.include? attrs[:name].slug_it
        attrs[:slug] = attrs[:name].slug_it(true)
      end

      found.update(attrs)
      update found
    else
      attrs[:slug] = attrs[:name].slug_it(true)
      create Event.new(attrs)
    end
  end
end

It only works if I add at the bottom of /lib/supports/utils.rb:

class String
  include MyModule::Utils::Slug
end

I would like to always include the module like include Hanami::Repository in EventRepository.

What am I doing wrong?


Solution

  • When you include MyModule::Utils::Slug into EventRepository, the methods defined in the included module are available on an instance of EventRepository not on String. If you want to use the module as-is, you need to include it on String. If you don't want to include it on the global class you could do

    module MyModule
      module Utils
        module Slug
          def slug_it(string, random = false)
            if random
              string.slugify + '-' + SecureRandom.hex(10).to_s
            else
              string.slugify
            end
          end
        end
      end
    end
    

    and then modify the slug creation to pass the string you want to slugify

    unless found.slug.include? slug_it(attrs[:name])
      attrs[:slug] = slug_it(attrs[:name], true)
    end