5

I have two files, one with module ToMix:

module ToMix
    @module_var = "modulevar"
    def self.mix_function
        puts "mix_function SELF: #{@module_var}"
    end
    def mix_function
        puts "mix_function: #{@module_var}"
    end     
    class MixClass
        attr_accessor :mixclassvar
        def initialize(value)
            @mixclassvar = value
        end
    end
end

which I want to mixin to the class TestInclude in the other file:

class TestInclude
    require "ToMixFile"
    include ToMix
end

Could someone explain why the instance variable @module_var and methods self.mix_function, mix_function are undefined? And how would I define them?

t2 = TestInclude.new()
t2.mix_function                           # => error undefined (exected call to mix_function)
t2.module_var = "test set module_var"     # => error undefined
TestInclude.mix_function                  # => error undefined (expected call to self.mix_function)
TestInclude.method_defined? :mix_function # => false
sawa
  • 165,429
  • 45
  • 277
  • 381
user1297102
  • 661
  • 8
  • 14

1 Answers1

4

Modules add functions to things; attr_accessor adds functions for interacting with a variable.

module ToMix
    @module_var = "module_var"
    attr_accessor :mixed_var
    def initialize
      @mixed_var = "mixed_var"
    end

    def mix_function
        puts "mix_function: #{@mixed_var}"
    end
    def self.module_function
        @module_var
    end
end

class Mixed
  include ToMix
end

Mixed.new.mixed_var

Notably,

"".extend(ToMix).mixed_var == nil # no error, but no value, interesting!

but

(a = "".extend(ToMix)).mixed_var = "interesting"
a.mixed_var == "interesting"

and

ToMix.module_function == "module_var"

https://stackoverflow.com/a/151774/171916 http://www.natontesting.com/2009/09/28/accessing-instance-variables-declared-in-ruby-modules/ How to dynamically alter inheritance in Ruby

Edit: Those wiser than I should correct me if I'm wrong, but module and class definitions are themselves objects. Defining a @var in a module definition thus adds the var to the module object itself

Edit: Those wiser than I have corrected me: While class and module definitions sort of behave like a singleton, they are not themselves objects. You can think of def self.bacon and @var outside of methods as c++ static methods and variables, but they can /only/ be access like a static, even if you're in an instance.

Community
  • 1
  • 1
Narfanator
  • 5,595
  • 3
  • 39
  • 71
  • 1
    Module and class definitions aren't objects. Unfortunately, it would be really cool and really powerful if they were. Modules and classes, however, *are* objects just like any other, and thus they can have instance variables just like any other object. – Jörg W Mittag Jun 01 '13 at 12:26
  • @JörgWMittag *module* is an object but *module definition* is not. Didn't get the point. Could you explain more please? – Arup Rakshit Jun 01 '13 at 15:17
  • thanks! that second link is really helpful in describing how to use 'super' in the mixin class w/ 'def initialize' in the module. I presume he means that by just calling "ToMix.module_var" won't by itself do anything because that is a declaration and not associated with any object unlike how "Mixed.new" would create an actual instance and then you would call methods/variables against that. um, is that right? – user1297102 Jun 01 '13 at 19:52
  • so this is my understanding now (plz correct me if i'm wrong), instance variables in modules are not DIRECTLY accessible and can only be modified/queried thru methods (in the module or mixin class). Using attr_accessor, creates attribute functions within the module (that get mixin) that allow for this two-way access. The use of 'initialize/super' is just like any other method (with the same relation to the instance variables) and can set the mixin's instance of the variable. correct? – user1297102 Jun 01 '13 at 20:34
  • 1
    Yes but not really, AFAIK: The instance variable doesn't exist at all until some member function does a `@var = "var"`, at which point, *that object* has an `@var`. So when you call an accessor, or the initializer, you're calling *that object's* accessor or initializer, which, simply by assigning to `@var`, creates `@var` as a member variable of that object. Looks like how you described it, but isn't. – Narfanator Jun 01 '13 at 22:12
  • ok so the method access is right but with the addendum that the instance variable won't be created (and so won't be available) until they're first assigned to. I found these links too: http://stackoverflow.com/questions/963378/ruby-rails-context-of-self-in-modules-and-libraries, http://www.rubyist.net/~slagell/ruby/localvars.html, http://stackoverflow.com/questions/826734/when-do-ruby-instance-variables-get-set – user1297102 Jun 01 '13 at 23:13