I realize this perhaps a naive question but still I cant figure out how to call one method from another in a Ruby class.
i.e. In Ruby is it possible to do the following:
class A
def met1
end
def met2
met1 #call to previously defined method1
end
end
Thanks,
RM
Those aren't class methods, they are instance methods. You can call met1 from met2 in your example without a problem using an instance of the class:
class A
def met1
puts "In met1"
end
def met2
met1
end
end
var1 = A.new
var1.met2
Here is the equivalent using class methods which you create by prefixing the name of the method with its class name:
class A
def A.met1
puts "In met1"
end
def A.met2
met1
end
end
A.met2
Related
I have a module I'd like to be able to wrap every method in a class, whether it be an instance method or a class method, but am having a bit of trouble trying to get it to work in both circumstances. Below is what I have right now, it works for wrapping instance methods but calling class methods doesn't seem to work. I assume the module proxy isn't installed correctly for overriding class methods, but I'm not sure what to do to fix that.
I'm limited to ruby 2.7, btw.
module Wrapper
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
module Proxy
end
def wrap_method(name)
puts "wrapping #{name}"
Proxy.define_method(name) do |*args|
puts "in wrap_method, about to call #{name}"
super *args
end
end
def wrap_methods
# wrap any methods that were defined before this was called
self.instance_methods(false).each {|name| wrap_method(name) }
self.singleton_methods(false).each {|name| wrap_method(name) }
# wrap any methods that are defined after this is called
self.class.define_method(:singleton_method_added) do |name|
if respond_to?(:wrap_method)
wrap_method(name)
end
end
self.class.define_method(:method_added) do |name|
if respond_to?(:wrap_method)
wrap_method(name)
end
end
self.prepend(Proxy)
end
end
end
module Top
module Second
class Main
include Wrapper
wrap_methods
def self.first_class_method
puts "in self.first_class_method"
end
def self.second_class_method
puts "in self.second_class_method"
end
def some_instance_method
puts "in some_instance_method"
end
end
end
end
Top::Second::Main.first_class_method
Top::Second::Main.second_class_method
Top::Second::Main.new.some_instance_method
Output:
wrapping first_class_method
wrapping second_class_method
wrapping some_instance_method
in self.first_class_method
in self.second_class_method
in wrap_method, about to call some_instance_method
in some_instance_method
You prepend Proxy to the class itself, and this modifies the method resolution for instances of the class, but not for the class itself.
To achieve the second, you have to
self.singleton_class.prepend(Proxy)
in addition.
What is the idea behind creating a new instance of a method inside the class << self construct?
I understand methods are put under the class << self block to make them class methods but what does it mean to create a new instance of the method itself?
class foo
class << self
def bar(param)
new.bar(some_param)
end
end
end
I think what your trying to describe is a convenience method:
class FooService
def initialize
#bar= Bar.new
end
# this does the actual work
def call
results = #bar.do_some_work
results.each do
# ...
end
end
# this is just a convenient wrapper
def self.call
new.call
end
end
This lets you call the FooService.call class method for instead of manually instantiating the class with FooService.new.call. It does not really look like that much from this simple example but its really useful for abstracting away object initialization in things like service objects or to combine initializer arguments with method arguments.
class ApiClient
def initialize(api_key)
#api_key = api_key
end
def get(path)
# ...
end
def self.get(path, api_key: ENV['API_KEY'])
new(api_key).call(path)
end
end
ApiClient.get('foo')
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.
I'm using Ruby 1.9.3 and trying to make some tests with RSpec.
I have a class:
class A
def method1
"test"
end
end
class B < SimpleDelegator
def initialize(events)
#events = events
end
end
Now I'm trying to test delegation behaviour:
require 'spec_helper'
RSpec.describe B do
let(:a) { A.new }
let(:b) { B.new(a) }
it "Should delegate unknown calls to A object" do
expect(b.method1).not_to eq(nil)
end
end
I get the following error:
NoMethodError:
undefined method `method1' for nil:B
Seems that the test would pass if add method_missing manually:
class B < SimpleDelegator
def initialize(events)
#events = events
end
def method_missing(meth, *args, &blk)
#events.send(meth, *args, &blk)
end
end
What I'm doing wrong here?
Thanks
The problem is that you added a initializer to the class B without calling super and passing the instance you want to decorate. Your code should look like this:
class A
def method1
"test"
end
end
class B < SimpleDelegator
def initialize(events)
#events = events
super(events)
end
end
You don't need to define an initialize method on B. SimpleDelegator defines one for you. When you defined your own initialize method, you overrode the initialize method you inherited from the SimpleDelegator class.
Try this:
class A
def method1
"test"
end
end
class B < SimpleDelegator
end
This is from irb: B.new(A.new).method1 #=> "test"
You could define your own initialize method and call super, but I wouldn't unless you really had to.
I am trying to get the name of the class from within a static method within the class:
class A
def self.get_class_name
self.class.name.underscore.capitalize.constantize
end
end
Though this returns Class instead of A. Any thoughts on how do I get A instead?
Eventually I also want to have a class B that inherits from A that will use the same method and will return B when called.
The reason I am doing this is because I have another object under this domain eventually: A::SomeOtherClass which I want to use using the result I receive.
Remove .class:
class A
def self.get_class_name
self.name.underscore.capitalize.constantize
end
end
self in a context of a class (rather than the context of an instance method) refers to the class itself.
This is why you write def self.get_class_name to define a class method. This means add method get_class_name to self (aka A). It is equivalent to def A.get_class_method.
It is also why when you tried self.class.name you got Class - the Object#class of A is Class.
To make this clearer, consider the output of:
class A
puts "Outside: #{self}"
def self.some_class_method
puts "Inside class method: #{self}"
end
def some_instance_method
puts "Inside instance method: #{self}"
end
end
A.some_class_method
A.new.some_instance_method
Which is:
Outside: A
Inside class method: A
Inside instance method: #<A:0x218c8b0>
The output for this:
class NiceClass
def self.my_class_method
puts "This is my #{name}"
end
end
NiceClass.my_class_method
Will be:
This is my NiceClass