I have a class with N methods. I want to set one of these methods to private. How can I do this?
class Example
def methodA
end
def methodP
end
private :methodP
end
I like this way:
class Example
def public_method1
end
private def used_by_public_method1
end
def public_method2
end
end
Another option (that I find more confusing):
class Example
def public_method1
end
def public_method2
end
private
def used_by_public_method1
end
# Don't accidentally put public methods down here.
end
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 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
Here are the Ruby classes I have:
class MyBase
class << self
def static_method1
##method1_var ||= "I'm a base static method1"
end
def static_method1=(value)
##method1_var = value
end
def static_method2
##method2_var ||= "I'm a base static method2"
end
def static_method2=(value)
##method2_var = value
end
end
def method3
MyBase::static_method1
end
end
class MyChild1 < MyBase
end
class MyChild2 < MyBase
class << self
def static_method1
##method1_var ||= "I'm a child static method1"
end
end
end
c1 = MyChild1.new
puts c1.method3 #"I'm a base static method1" - correct
c2 = MyChild2.new
puts c2.method3 # "I'm a base static method1" - incorrect. I want to get "I'm a child static method1"
I'm aware of attr_accessor and modules, but I can't use use them here because I want them to give default values in MyBase class. I want to override MyBase.static_method1 in MyChild2.
The problem is that method3 is always explicitly calling the method on the base class. Change it to this:
def method3
self.class.static_method1
end
After that, consider not using ##.
## in ruby is extremely counterintuitive and rarely means what you think it means.
The problem with ## is that it is shared across the all of the inherited classes and the base class. See this blog post for an explanation.
I know global variables should never be used but right now it's the only thing that I can get to work. So I'm looking for alternatives. What I want to do is pass #array which is is in method two in class New, to method one. The only way I was able to accomplish this is with $array.
module Test::Abc
class << self
def one
....
end
class New
def two
#array=[]
end
end
end
end
Here's what I did to get the result I needed...
module Test::Abc
class << self
def one(array)
....
end
end
class New
def two
#array=[]
array=#array
Test::Abc::one(array)
end
end
end
Here's what I came up with as a solution...
module Test::Abc
class << self
def one(array)
....
end
end
class New
def two
#array=[]
array=#array
Test::Abc::one(array)
end
end
end
Along with your answer, this should also work (slight modification):
module Test::Abc
class << self
def one(array)
....
end
end
class New
def two
#array=[]
Test::Abc::one(#array)
end
end
end
I know I can execute the following to add methods to the String class
class String
def do_something
puts self.size
end
end
var = "test"
var.do_something
and this will return 4
I want to be able to have a module with a function that takes in a String, but be able to call the do_something method on this string (see below for example) - is it possible?
EDIT: Added sample code that is not working
module TestModule
class String
def do_something
puts self.size
end
end
def self.test(str)
str.do_something
end
end
This gives the error: undefined method 'do_something' for "hello":String (NoMethodError)
The way your code is written, you're defining a new class called TestModule::String. If you want to modify the built-in Ruby String class, you need to use the fully-qualified name of String (with the ""::") if you want to keep the declaration inside the module.
module TestModule
class ::String
def do_something
puts self.size
end
end
def self.test(str)
str.do_something
end
end
Adding the "::" tells Ruby that the String class that you want is not part of the TestModule.
It's probably cleaner to just declare String outside of TestModule in the same file.
If you don't want to pollute the global String class, you could just modify the specific String instance that you want to add the method to.
module TestModule
def self.test(str)
do_somethingify!(str)
str.do_something
end
def self.do_somethingify!(str)
unless str.respond_to? :do_something
str.instance_eval do
def do_something
puts size
end
end
end
end
end
Maybe this?
module TestModule
module_function
def test(str)
str.instance_eval{doSomething}
end
end
Test.test(str)
Edit Changed due to the change in the question
Just put the definition of doSomething outside out the TestModule class.
class String
def doSomething
puts size
end
end
module TestModule
module_function
def test(str)
str.doSomething
end
end