2

My program has following structure:

module M1
    class A
        def action
            C.new.foo
        end
    end

    class C
        def foo
            puts "M1::C foo"
        end
    end
end


module M2
    class A
        def action
            C.new.foo
        end
    end

    class C
        def foo
            puts "M2::C foo"
        end
    end
end

As both M1::A and M2::A share same code, I've been thinking of putting common code inside separate class (or module) and inherit from it (include it). Something like this:

class ABase
    def action
        C.new.foo
    end
end

module M1
    class A < ABase
    end

    class C
        def foo
            puts "M1::C foo"
        end
    end
end


module M2
    class A < ABase
    end

    class C
        def foo
            puts "M2::C foo"
        end
    end
end

However, when I tried, I've got into trouble with name resolution uninitialized constant ABase::C. What is the proper way to achieve code sharing in this case?

0

1 Answer 1

1

Due to the way constants are resolved based on definition scope and not in inherited scopes you'll need to bridge that with a method call:

class ABase
  def action
    # Within this scope only constants defined in `ABase` are resolved.
    # As ABase::C and ::C (root-level) don't exist, C can't be resolved.
    # However, a method `c` defined in a subclass will be.
    c.new.foo
  end
end

module M1
  class A < ABase
    def c
      C
    end
  end

  class C
    def foo
      puts "M1::C foo"
    end
  end
end

module M2
  class A < ABase
    # This resolves C as either M2::A::C, M2::ABase::C, M2::C or ::C,
    # whichever it finds first.
    def c
      C
    end
  end

  class C
    def foo
      puts "M2::C foo"
    end
  end
end

Then you get the expected results:

M1::A.new.action
# => M1::C foo
M2::A.new.action
# => M2::C foo
Sign up to request clarification or add additional context in comments.

2 Comments

if there are many classes like C in M1 and M2 is there a way solve it without "aliasing" every class? An idea that comes to my mind is referencing the whole module somehow.
You can always reference M2::C anywhere you like, or maybe something like constant_get(:C) but you'd have to experiment. I've got a feeling this is more of an object-oriented design issue than a Ruby one: If you architect this differently the problem goes away.

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.