I thought that when calling a private method it was unacceptable to place a explicit receiver. Well I did this in Ruby 2.0 and I can get results:
class Test
def public_method
self.set_size=10
end
def return_size
#size
end
private
def set_size=(size)
#size = size
end
end
test = Test.new
test.public_method
p test.return_size
Why is this?
Private setters can be called with an explicit receiver of self. In fact, they have to be called with an explicit receiver, because otherwise they couldn't be called at all, since
foo = bar
is an assignment to a local variable, not a method call.
You are right except on one thing...setters (def method=) can be called with an explicit receiver of self, so that you can call private setters.
So, actually if you were about to do this:
class Test
def public_method
self.say_hi
end
def return_size
#size
end
private
def say_hi
puts "oh hay there"
end
end
test = Test.new
test.public_method
test.return_size
It would throw a private method say_hi called for..
Related
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 have code in a class that dynamically creates methods using define_method. I want to split it into two parts to make the code more understandable. I need to put a part of the code into a private block.
Here is how my code looks:
class Foo
["bar", "baz"].each do |method|
create_method(method)
end
private
def create_method(name)
define_method(name) do
puts "HELL"
end
end
end
Foo.new.bar
`create_method' for Foo:Class (NoMethodError)
I don't understand why it doesn't work.
There are several problems with your code. The error you are getting has absolutely nothing to do with private or public at all. The error message says that the method create_method cannot be found. There are two reasons for that:
You are calling it before it is defined. You need to move the call to create_method after its definition.
create_method is defined as an instance method, i.e. for calling it on instances of Foo, but you are calling it on Foo itself. You have to define it as a method somewhere in Foo's class (i.e. Class), one of its ancestors (e.g. Module), or Foo's singleton class.
I will define it as a singleton method of Foo here, but if the method really is as generic as you have showed in your example, then it probably rather belongs in Module instead.
class Foo
class << self
private
def create_method(name)
define_method(name) do
puts "HELL"
end
end
end
["bar", "baz"].each do |method|
create_method(method)
end
end
Foo.new.bar
# HELL
Not sure what went wrong for you, but this seemed to work for me:
#!/usr/bin/env ruby
class C
def initialize
create_method
end
def create_method
private_create_method
end
private
def private_create_method
class << self
define_method(:foo) { puts "I am a public method." }
end
end
end
C.new.foo # outputs: I am a public method.
Use:
MyClass.instance_eval { public :public_method }
Or run:
public :public_method
from within the MyClass class.
If you replace private_create_method with
def private_create_method
self.class.send(:define_method, :foo) { puts "I am a public method." }
end
then
C.new.foo
I am a public method
I found this neat delegator based 'tee' implementation on SO:
https://stackoverflow.com/a/6410202/2379703
And I'm curious what is means for #targets (instance variable) means in the context of a class method:
require 'logger'
class MultiDelegator
def initialize(*targets)
#targets = targets
end
def self.delegate(*methods)
methods.each do |m|
define_method(m) do |*args|
#targets.map { |t| t.send(m, *args) }
end
end
self
end
class <<self
alias to new
end
end
log_file = File.open("debug.log", "a")
log = Logger.new MultiDelegator.delegate(:write, :close).to(STDOUT, log_file)
I get that it defining the methods write/close but #targets isn't even defined at this point since .to (aliased to new) has yet to be called so I'd assume #targets is nil.
Can anyone give an explanation as to the logistics of how this code works? Does ruby not even attempt to access/resolve #targets until the method in question is attempted to be called, which would be by the logger after it was instantiated?
The define_method method is called on a class to create an instance method. Inside that method, the self (and the instance variable) are instances of the class.
For example:
class Foo
#bar = "CLASS"
def initialize
#bar = "INSTANCE"
end
def self.make_method
define_method :whee do
p #bar
end
end
end
begin
Foo.new.whee
rescue NoMethodError=>e
puts e
end
#=> undefined method `whee' for #<Foo:0x007fc0719794b8 #bar="INSTANCE">
Foo.make_method
Foo.new.whee
#=> "INSTANCE"
It is correct that you can ask about instance variables that have never been created, at any time:
class Bar
def who_dat
puts "#dat is #{#dat.inspect}"
end
end
Bar.new.who_dat
#=> dat is nil
The same is true of other aspects of the language. As long as the code in the method is syntactically valid, it may be defined, even if invoking it causes a runtime error:
class Jim
def say_stuff
stuff!
end
end
puts "Good so far!"
#=> Good so far!
j = Jim.new
begin
j.say_stuff
rescue Exception=>e
puts e
end
#=> undefined method `stuff!' for #<Jim:0x007f9c498852d8>
# Let's add the method now, by re-opening the class
class Jim # this is not a new class
def stuff!
puts "Hello, World!"
end
end
j.say_stuff
#=> "Hello, World!"
In the above I define a say_stuff method that is syntactically valid, but that calls a method that does not exist. This is find. The method is created, but not invoked.
Then I try to invoke the method, and it causes an error (which we catch and handle cleanly).
Then I add the stuff! method to the class. Now I can run the say_stuff method (on the same instance as before!) and it works just fine.
This last example shows how defining a method does not run it, or require that it would even work when it is run. It is dynamically evaluated each time it is invoked (and only at that time).
I have a class with a number of static methods. Each one has to call a common method, but I'm trying not to expose this latter method. Making it private would only allow access from an own instance of the class? Protected does not seem like it would solve the problem here either.
How do I hide do_calc from being called externally in a static context? (Leaving it available to be called from the first two static methods.)
class Foo
def self.bar
do_calc()
end
def self.baz
do_calc()
end
def self.do_calc
end
end
First off, static is not really part of the Ruby jargon.
Let's take a simple example:
class Bar
def self.foo
end
end
It defines the method foo on an explicit object, self, which in that scope returns the containing class Bar.
Yes, it can be defined a class method, but static does not really make sense in Ruby.
Then private would not work, because defining a method on an explicit object (e.g. def self.foo) bypasses the access qualifiers and makes the method public.
What you can do, is to use the class << self syntax to open the metaclass of the containing class, and define the methods there as instance methods:
class Foo
class << self
def bar
do_calc
end
def baz
do_calc
end
private
def do_calc
puts "calculating..."
end
end
end
This will give you what you need:
Foo.bar
calculating...
Foo.baz
calculating...
Foo.do_calc
NoMethodError: private method `do_calc' called for Foo:Class
You can define a private class method with private_class_method like this:
class Foo
def self.bar
do_calc
end
def self.baz
do_calc
end
def self.do_calc
#...
end
private_class_method :do_calc
end
Or as of Ruby 2.1:
class Foo
def self.bar
do_calc
end
private_class_method def self.do_calc
#...
end
end
Other than self.class.send :method, args..., of course. I'd like to make a rather complex method available at both the class and instance level without duplicating the code.
UPDATE:
#Jonathan Branam: that was my assumption, but I wanted to make sure nobody else had found a way around. Visibility in Ruby is very different from that in Java. You're also quite right that private doesn't work on class methods, though this will declare a private class method:
class Foo
class <<self
private
def bar
puts 'bar'
end
end
end
Foo.bar
# => NoMethodError: private method 'bar' called for Foo:Class
Here is a code snippet to go along with the question. Using "private" in a class definition does not apply to class methods. You need to use "private_class_method" as in the following example.
class Foo
def self.private_bar
# Complex logic goes here
puts "hi"
end
private_class_method :private_bar
class <<self
private
def another_private_bar
puts "bar"
end
end
public
def instance_bar
self.class.private_bar
end
def instance_bar2
self.class.another_private_bar
end
end
f=Foo.new
f=instance_bar # NoMethodError: private method `private_bar' called for Foo:Class
f=instance_bar2 # NoMethodError: private method `another_private_bar' called for Foo:Class
I don't see a way to get around this. The documentation says that you cannot specify the receive of a private method. Also you can only access a private method from the same instance. The class Foo is a different object than a given instance of Foo.
Don't take my answer as final. I'm certainly not an expert, but I wanted to provide a code snippet so that others who attempt to answer will have properly private class methods.
Let me contribute to this list of more or less strange solutions and non-solutions:
puts RUBY_VERSION # => 2.1.2
class C
class << self
private def foo
'Je suis foo'
end
end
private define_method :foo, &method(:foo)
def bar
foo
end
end
puts C.new.bar # => Je suis foo
puts C.new.foo # => NoMethodError
Nowadays you don't need the helper methods anymore. You can simply inline them with your method definition. This should feel very familiar to the Java folks:
class MyClass
private_class_method def self.my_private_method
puts "private class method"
end
private def my_private_method
puts "private instance method"
end
end
And no, you cannot call a private class method from an instance method. However, you could instead implement the the private class method as public class method in a private nested class instead, using the private_constant helper method. See this blogpost for more detail.
If your method is merely a utility function (that is, it doesn't rely on any instance variables), you could put the method into a module and include and extend the class so that it's available as both a private class method and a private instance method.
This is the way to play with "real" private class methods.
class Foo
def self.private_bar
# Complex logic goes here
puts "hi"
end
private_class_method :private_bar
class <<self
private
def another_private_bar
puts "bar"
end
end
public
def instance_bar
self.class.private_bar
end
def instance_bar2
self.class.another_private_bar
end
def calling_private_method
Foo.send :another_private_bar
self.class.send :private_bar
end
end
f=Foo.new
f.send :calling_private_method
# "bar"
# "hi"
Foo.send :another_private_bar
# "bar"
cheers
This is probably the most "native vanilla Ruby" way:
class Foo
module PrivateStatic # like Java
private def foo
'foo'
end
end
extend PrivateStatic
include PrivateStatic
def self.static_public_call
"static public #{foo}"
end
def public_call
"instance public #{foo}"
end
end
Foo.static_public_call # 'static public foo'
Foo.new.public_call # 'instance public foo'
Foo.foo # NoMethodError: private method `foo' called for Foo:Class
Foo.new.foo # NoMethodError: private method `foo' called for #<Foo:0x00007fa154d13f10>
With some Ruby metaprogramming, you could even make it look like:
class Foo
def self.foo
'foo'
end
extend PrivateStatic
private_static :foo
end
Ruby's metaprogramming is quite powerful, so you could technically implement any scoping rules you might want. That being said, I'd still prefer the clarity and minimal surprise of the first variant.
Unless I'm misunderstanding, don't you just need something like this:
class Foo
private
def Foo.bar
# Complex logic goes here
puts "hi"
end
public
def bar
Foo.bar
end
end
Of course you could change the second definition to use your self.class.send approach if you wanted to avoid hardcoding the class name...