Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Ruby, how can I apply an attr_accessor from a module that is being extended?

Tags:

ruby

I am trying to modularize some Ruby code by organizing methods into separate modules. Originally I had something like this:

class Joe
  attr_accessor :name

  def self.arms
    2
  end

  def self.legs
    2
  end
end

I tried doing something like this:

class Joe
  extend Person
end

module Person
  include Name
  include Arms
  include Legs
end

module Name
  attr_accessor :name
end

module Arms
  def arms
    2
  end
end

module Legs
  def legs
    2
  end
end

However, the part that is not working is the attr_accessor. I've tried all different combinations of include/extend, def self.included(base); base.extend and I can't seem to find the right combination to make everything work together. How can I do this?


Update: I think the part that I left out was that each of the modules could potentially have both instance methods and class methods. So I currently have something like this:

class Joe
  include Person
end

module Person
  include Name::InstanceMethods
  include Arms::InstanceMethods
  include Legs::InstanceMethods

  def self.included(base)
    base.extend Name::ClassMethods
    base.extend Arms::ClassMethods
    base.extend Legs::ClassMethods
  end
end

module Name
  module ClassMethods; end

  module InstanceMethods
    attr_accessor :name
  end
end

module Arms
  module ClassMethods
    def arms
      2
    end
  end

  module InstanceMethods; end
end

module Legs
  module ClassMethods
    def legs
      2
    end
  end

  module InstanceMethods; end
end

While this works, it feels messy. It also feels like the Person module knows too much about the instance methods and the class methods. If I were to modify the Name module to remove the empty/unused ClassMethods module, I would also have to modify the Person class.

like image 595
Andrew Avatar asked Oct 02 '15 05:10

Andrew


People also ask

What is the use of attR method in Ruby?

In Ruby, the attr_* methods are in charge of the member access control. The attr method creates an instance variable and a getter method for each attribute name passed as argument. An argument can be a Symbol or a String that will be converted to Symbol

What is attr_accessor in Ruby?

That’s where attr_accessor comes in. You can tell Ruby to create these methods for you with attr_accessor. This is a Ruby method that creates other methods for you. What methods? These are the same methods we created before…

What is the difference between GIT and attr_accessor in Ruby?

@Angelfirenze, git has nothing to do with attr_accessor. Git is a version control software, whereas attr_accessor is a method in Ruby. Let's say you have a class Person. class Person end person = Person.new person.name # => no method error Obviously we never defined method name. Let's do that.

How do you get access to a member in Ruby?

In many programming languages, this concept is implemented by using getters and setters for each member. In Ruby, the attr_* methods are in charge of the member access control. The attr method creates an instance variable and a getter method for each attribute name passed as argument.


1 Answers

include is defined in Module, and therefore can only be called on modules and classes (which are modules). It adds the constants, (instance) methods and (module) variables from the given module(s) to the receiver by calling append_features.

extend on the other hand is defined in Object, i.e. it is not restricted to modules and classes. It adds the instance methods from the given module(s) to the receiver or, more precisely, to the receiver's singleton class.

Here's an example module with an instance method hello:

module Mod
  def hello
    "Hello from #{self.class} '#{self}'"
  end
end

If we extend an instance (as opposed to a class), then hello becomes an instance method:

str = 'abc'
str.extend(Mod)
str.hello
#=> "Hello from String 'abc'"

If we extend a class, then hello becomes a class method:

String.extend(Mod)
String.hello
#=> "Hello from Class 'String'"

This is because class methods are really just instance methods defined in the singleton class of a class.

That said, there are several options to define both, class and instance methods, by calling extend and / or include:

1. extend and include

This is the most basic one, you could move include Name from Person into Joe:

module Person
  include Arms, Legs
end

class Joe
  extend Person
  include Name
end

2. extend and include in superclass

Or you could make Person a class that extends and includes the other modules and use it as Joe's superclass:

class Person
  extend Arms, Legs
  include Name
end

class Joe < Person
end

The next options involve some Ruby magic - they use a callback to invoke include upon extend or vice versa:

3. include from within extended

You could use the extended callback to include Name from within Person:

module Person
  include Arms, Legs
  def self.extended(mod)
    mod.include(Name)
  end
end

class Joe
  extend Person
end

4. extend from within included

Or you could include Person from within Joe and use the included callback to call extend:

module Person
  include Name
  def self.included(mod)
    mod.extend Arms, Legs
  end
end

class Joe
  include Person
end

3 and 4 look clean from within Joe but it might not be obvious (maybe even confusing?) that including or extending Person also defines class or instance methods.

like image 82
Stefan Avatar answered Sep 29 '22 18:09

Stefan