Question

Here's an example of a script that I want to be able to support:

class Testing
  include Dialect

  url_is 'http://localhost:9292'
end

@page = Testing.new
@page.view

The things to note here are:

  1. I'm including a Dialect module. (Code shown below.)
  2. I'm calling a url_is() method directly on the class.
  3. I'm calling a view() method on an instance of the class.

The url_is() method is going to set a @url variable. The view() method ideally would be able to read that variable. That is what I don't seem to be able to do. Here is the code that supports the above script:

module Dialect
  module Generator
    module Assertion
      def url_is(url)
        @url = url
      end
    end
  end
end

module Dialect
  def self.included(caller)
    caller.extend Dialect::Generator::Assertion
  end

  def view
    puts "#{@url}"
  end
end

Here you can see that when Dialect is included, I have an included method that extends my Assertion module. That allows url_is() to work and url_is() does set the instance variable. You can also see I define view() which I want to be able to access that @url variable.

It's the latter part that's not happening. I get why in that I'm trying to read a variable from the Dialect::Generator::Assertion module in the Dialect module but I'm not sure what the best approach is to allow this.

I investigated module and class variable scope and Ruby mixins and instance variables but I haven't found anything that I can determine deals with the situation I'm describing.

Was it helpful?

Solution

I think you're getting confused by the class instance variable @view.

When you access an instance variable, the "instance" being referred to is self. In an instance method, self is the instance of the class (as you would expect). In a class, self is the class itself (an instance of Class)! This should illustrate the distinction:

class Foo
  @x = 0

  def initialize
    @x = 1
  end

  def self.x
    @x
  end

  def x
    @x
  end
end

Foo.x # 0
Foo.new.x # 1
Foo.x # still 0, they are entirely separate variables

When you extend Dialect::Generator::Assertion, url_is is added as a class method to Test, so @url is being set as an instance variable for Test itself. But when you include Dialect, view is added as an instance method to Test so the @url it is accessing is for an instance of Test.

In the view method, you can simply specify that you need the class version of the variable

def view
  self.class.class_eval { "#{@url}" }
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top