Ruby mixin class methods - ruby

I'm attempting to provide a class attribute setter and getter via a mixin, but clearly I'm doing something fundamentally wrong.
This code results in the error mod.rb:16:in '<class:Klass>': undefined method 'set_n' for Klass:Class (NoMethodError):
module Mixin
##n = 0
def self.set_n(n)
##n = n
end
def self.get_n
##n
end
end
class Klass
include Mixin
set_n 100
end
n = Klass.get_n
Little help?

I'd rewrite the module using instance methods and instance variables, i.e.:
module Mixin
def set_n(n)
#n = n
end
def get_n
#n ||= 0
end
end
The above can then be added to your class via extend:
class Klass
extend Mixin
set_n 100
end
Klass.get_n #=> 100
Note that get_ and set_ prefixes are quite unidiomatic in Ruby. You typically use the attribute's name as the getter and append a = for the setter.

As #Cary Swoveland mentioned, module methods can't be mixed in so I'll need to implement a base class:
class BaseClass
##n = 0
def self.set_n(n)
##n = n
end
def self.get_n
##n
end
end
class Klass < BaseClass
set_n 100
end
n = Klass.get_n
This works as intended, but I won't be able to use it in a multiple inheritance scenario (hopefully won't be required).

Related

How to call one class method from another class method

I am relatively new to Ruby and am having a hard time understanding class methods. If I have a class like the one below,
class Foo
def self.a
end
def self.b
end
end
How do I call a from b?
Just use its name:
class Foo
def self.a
puts 'a'
end
def self.b
puts 'b'
a
end
end
[23] pry(main)> Foo.a
a
=> nil
[24] pry(main)> Foo.b
b
a
=> nil
Firstly, recall that classes are modules with certain properties. Let's first look at modules. One built-in module is the Math module. You will see from the doc that Math has no instance methods (so there would be no point to include or prepend that module into a class); all of its methods are module methods. That means they are invoked with an explicit receiver: Math.sqrt(10). Math is merely a library of helper methods, available for use by all modules (and hence, classes).
Let's add a module method to Math:
module Math
def Math.sqr(x)
x*x
end
end
Math.sqr(3)
#=> 9
Suppose we decided to change the name of this module to Maths. We would then have to rewrite this method:
module Maths
def Maths.sqr(x)
x*x
end
end
If is for that reason module methods are generally defined def self.method(...), as self equals the module in which the method is defined. That way, if the module name is changed the module methods don't have to be redefined.
Since classes are modules, class methods are just another name for module methods (though class methods are inherited by subclasses), and the foregoing applies to classes and class methods as well.
Like all methods, module methods are invoked by prefacing the method with its receiver. For example, Math.sqrt(10). Similarly, like all methods, the method can be invoked without including its receiver (i.e., the receiver is implicit) if self equals its receiver at the time it is invoked. A module method's receiver equals self only when the method is invoked from within the module in which it was defined, outside a method, or from with another module method defined in the same module. For example, since sqrt is a module method in Math, I could write:
module Math
def Math.sqrt_times_2(x)
puts "self = #{self}"
2 * sqrt(x)
end
end
Math.sqrt_times_2(25)
# self = Math
#=> 10.0
Since classes are modules all of this applies to class methods as well, but this extends to subclasses as well, through inheritance. To invoke a class method klass_method defined in class Klass, we could invoke the method Klass.klass(...) (possibly followed by a block) from any class, but could (and should) drop the explicit receiver Klass. if it were invoked from within Klass or within a subclass of Klass. Here's an example.
class Klass
def self.klass_method(n)
puts "In klass_method, self = #{self}"
2 * n
end
def self.other_klass_method(n)
puts "In other_klass_method, self = #{self}"
3 * klass_method(n)
end
end
class SubKlass < Klass
def self.sub_klass_method(n)
puts "In sub_klass_method, self = #{self}"
5 * klass_method(n)
end
def sub_klass_instance_method(n)
puts "In sub_klass_instance_method, self = #{self}"
5 * self.class.klass_method(n)
end
end
Klass.klass_method(2)
# In klass_method, self = Klass
#=> 4
Klass.other_klass_method(3)
# In other_klass_method, self = Klass
# In klass_method, self = Klass
#=> 18
SubKlass.sub_klass_method(4)
# In sub_klass_method, self = SubKlass
# In klass_method, self = SubKlass
#=> 40
si = SubKlass.new
# In sub_klass_instance_method, self = #<SubKlass:0x0000586729649c70>
# In klass_method, self = SubKlass
si.sub_klass_instance_method(3)
# In klass_method, self = SubKlass
#=> 30
The last statement above shows that, to invoke a class method from within an instance method, the method's receiver must be set equal to the class in which the instance method is defined.

Get attr_reader, writer, or accessor oustide of the class

I'm currently doing some metaprogramming with ruby, and I'm trying to isolate the methods of class (that class is in another file, that I get by a require). I can get all the methods, thanks to klass.public_instance_methods(false), but I in the sametime, the array given also have all the attributes of the class. How could I isolate them ? In others related questions on SO, they suggest to use klass.instance_variables but when I do that, it only returns an empty array.
I can't seem to wrap my head around that one. I don't understand why there isn't a method specifically for that already...
For example:
I have in a file this class :
class T
attr_reader:a
def initialize(a)
#a = a
end
def meth
#code here
end
end
And, in another file, i have
require_relative 'T.rb'
class meta
def initialize
methods = T.public_instance_methods(false) #=> here methods = [:a,:meth] but I would want only to have [:meth]
#rest of code
end
end
For class defined like this:
class Klass
attr_accessor :variable
def initialize(variable)
#variable = variable
end
def method
end
end
you can find public non-attr instance methods using public_instance_methods and instance_variables methods.
public_instance_methods = Klass.public_instance_methods(false)
# [:method, :variable, :variable=]
instance_variables = Klass.new(nil).instance_variables
# [:#variable]
getters_and_setters = instance_variables
.map(&:to_s)
.map{|v| v[1..-1] }
.flat_map {|v| [v, v + '=']}
.map(&:to_sym)
# [:variable, :variable=]
without_attr = public_instance_methods - getters_and_setters
# [:method]
This is impossible. Ruby's "attributes" are completely normal methods. There is no way to distinguish them from other methods. For example, these two classes are completely indistinguishable:
class Foo
attr_reader :bar
end
class Foo
def bar
#bar
end
end
You can try to be clever and filter them out based on instance variables, but that is dangerous:
class Foo
# can filter this out using #bar
attr_writer :bar
def initialize
#bar = []
end
end
class Foo
def initialize
#bar = []
end
# this looks the same as above, but isn't a normal attribute!
def bar= x
#bar = x.to_a
end
end

How to use 'attr_accesor'

I have a class with a private method:
class MyClass
attr_accessor :my_attr
def some_mth?(num)
# I want to use my_attr as a variale #myattr here
#and here i want to check if arr include num
#myattr.include?(num)
end
private
def some_pvt_mth
#myattr = [1,2,3,4]
for example generation array here
end
end
When I call #myattr inside some_mth, my variable #myattr is nil
How to use variable #myatt inside class, in every method is it possible?
How do I do it properly?
You do not need to define attr_accessor in order to use an instance variable within the defined class. It's purpose is to create a 'getter' and a 'setter' method, but those are only needed for other classes to access the data.
This is a class:
class Foo
def initialize
#my_attr = [1,2,3,4]
end
def attr_includes?(x)
#my_attr.include?(x)
end
end
There's no attr accessor, but this will work.
The attr accessor essentially includes this code in your class...
class Foo
def my_attr
#my_attr
end
def my_attr=(x)
#my_attr = x
end
end
But if you don't want that, you can just leave it out, and access the variable via other methods (such as your include example).
You have to define the instance variable value first:
class MyClass
attr_accessor :my_attr
def initialize
#myattr = [1, 2, 3, 4]
end
def some_mth?(num)
#myattr.include?(num)
end
end

ruby private class method helper

Hi I am trying to create a helper for mass defining ruby methods as private class methods. In general one can define a method as a private class method by using private_class_method key work. But I would like to create a helper in the following style:
class Person
define_private_class_methods do
def method_one
end
def method_two
end
end
end
The way I planned to dynamically define this is in the following way, which is not at all working:
class Object
def self.define_private_class_methods &block
instance_eval do
private
&block
end
end
end
any ideas where I might be going wrong?
$ cat /tmp/a.rb
class Object
def self.define_private_class_methods &cb
existing = methods(false)
instance_eval &cb
(methods(false) - existing).each { |m| singleton_class.send :private, m }
end
end
class Person
define_private_class_methods do
def method_one
puts "¡Yay!"
end
end
end
Person.send(:method_one)
Person.public_send(:method_one)
$ ruby /tmp/a.rb
¡Yay!
/tmp/a.rb:18:in `public_send': private method `method_one'
called for Person:Class (NoMethodError)
Did you mean? method
from /tmp/a.rb:18:in `<main>'
Please note, that it’s hard to understand, what you are trying to achieve and possibly there is better, cleaner and more robust way to achieve this functionality.
Similar, yet different (and semantically more correct IMHO) to #mudasobwa's answer:
class Class
def define_private_class_methods(&definition)
class_methods_prior = methods
singleton_class.class_eval(&definition)
(methods - class_methods_prior).each do |method_name|
private_class_method method_name
end
end
end
class Person
define_private_class_methods do
def method_one
1
end
end
end
Person.method_one # !> NoMethodError: private method `method_one' called for Person:Class
Person.send :method_one # => 1
Note: It will not change the accessibility of a class method that you are currently overwriting.
You could define the methods in an anonymous module by passing the block to Module.new, make each instance method in the module private and extend your class with the module:
class Class
def define_private_class_methods(&block)
mod = Module.new(&block)
mod.instance_methods.each { |m| mod.send(:private, m) }
extend(mod)
end
end
This has the desired result:
class Person
define_private_class_methods do
def method_one
123
end
end
end
Person.send(:method_one)
#=> 123
Person.method_one
#=> private method `method_one' called for Person:Class (NoMethodError)
... and as a bonus, it also gives you a super method: (probably of little use)
class Person
def self.method_one
super * 2
end
end
Person.method_one
#=> 456
Of course, you don't have to use extend, you could just as well define the methods manually:
class Class
def define_private_class_methods(&block)
mod = Module.new(&block)
mod.instance_methods.each do |m|
define_singleton_method(m, mod.instance_method(m))
private_class_method(m)
end
end
end
The essential component is the anonymous module, so you have a (temporary) container to define the methods in.

Is it bad practice to declare instance variable values based on the name of the class?

Let's say I have a couple subclasses that inherit from a base class like this:
class Base
def initialize
#name = self.class.name
#num1 = 10;
#num2 = 5;
end
def speak
puts "My name is #{name}"
end
end
class Sub1 < Base
end
class Sub2 < Base
end
class Sub3 < Base
end
Is it bad practice to declare the instance variable of #name to the name of the class? Would it be better to do something like this?
class Base
def initialize
#feet = 10;
#inches = 5;
end
def speak
puts "My name is #{name}"
end
end
class Sub1 < Base
def initialize
super()
#name = "Sub1"
end
end
class Sub2 < Base
def initialize
super()
#name = "Sub2"
end
end
class Sub3 < Base
def initialize
super()
#name = "Sub3"
end
end
If #name is always intended to be equal to the class name, you don't need it – just use self.class.name or myObject.class.name directly, or define an accessor method which returns it if you don't want to use .class.name from the outside. Initializing it manually would be bad practice since you'd always have to remember to do it in every subclass and changes to subclass names would need to be replicated to the initialization.
If #name is not always going to equal the class name in every subclass, then personally I'd still provide a default implementation where it is initialized from self.class.name and let subclasses override it as necessary.
The first method is more concise. What you probably should be doing is evaluating this in a lazy capacity:
def name
#name ||= self.class.to_s.split(/::/).last
end
That way you don't need to worry about assignment and any subclass can initialize if necessary with an override. Your speak method will continue to work without modification as presumably you had declared attr_reader :name for that to work in the first place.
I think it's quite pointless, look at this output:
class Base
def name
self.class.name
end
end
class Sub1 < Base
# This declaration is useless
def name
self.class.name
end
end
class Sub2 < Base
end
b = Base.new
b.name # "Base"
c = Sub1.new
c.name # "Sub1"
d = Sub2.new
d.name # "Sub2" (this function is inherited from base, but is called from Sub2 class context)
Next thing in ruby, you can use superclass method to get parent class name
Sub2.superclass.name # "Base"

Resources