I must be missing something about how people do this in Ruby.
If '#protected' is uncommented we get:
in 'what': protected method 'zoop' called for Foo:Class (NoMethodError)
Is there a better way to approach protected class methods?
class Foo
class << self
#protected
def zoop
"zoop"
end
end
public
def what
"it is '#{self.class.zoop}'"
end
protected
end
a = Foo.new
p a.what # => "it is 'zoop'"
I would like zoop to be protected or private (no calling 'Foo.zoop'), but so far, I can't seem to find an elegant way.
It barely matters to make methods private or protected in Ruby, since you can just call send() to get around them.
If you want zoop to stay protected, use send() like this:
def what
"it is '#{self.class.send(:zoop)}'"
end
Upon further discussions with rue: and drbrain: in ruby-lang, it turns out that my impulse to save memory by placing utility functions at the class level was misplaced.
In Ruby, the instance methods hang off the class anyway, and the answer is to go ahead and place the utility functions at the instance level as private.
In summary, a utility function that is accessed only by instance methods:
class Foo
def what
"it is '#{zoop}'"
end
private
def zoop
"zoop"
end
end
p Foo.new.what # => "it is 'zoop'"
For a utility function that needs to be called from instance and class methods, a nested module seemed to be a popular approach:
class Foo
module Util
def self.zoop
"zoop"
end
end
def what
"it is '#{Util.zoop}'"
end
class << self
def class_what
"for all time it is '#{Util.zoop}'"
end
end
end
p Foo.new.what # => "it is 'zoop'"
p Foo.class_what # => "for all time it is 'zoop'"
p Foo::Util.zoop # visible, alas
Related
If I have this in my main program:
def hi
puts 'hi'
end
self.hi
it won't work because hi is private.
I've learned that all methods in Ruby are public by default, but this doesn't seem to be the case, why?
It is tricky to define a method in the main environment. A method is private by default when you define it in main. So you either have to use it as private:
def hi
puts 'hi'
end
hi
or explicitly make it public:
def hi
puts 'hi'
end
public :hi
self.hi
Methods are public by default, except for "top-level procedures". If you define something which looks like a top-level procedure, it will actually be defined as a private instance method of Object.
def main_method
p "This is from main_method"
end
public :main_method
class Klass
def initialize
Object.main_method # Or we can simply say main_method as we do in the second exaple
end
end
puts Klass.new
It is the way to create Object 'class methods' as shown. If it were private, then we could not specify the receiver.
When it is private, as it is by default, then we would call it like this:
def main_method
p "This is from main_method"
end
class Klass
def initialize
main_method # With it being private, implicit self only
end
end
puts Klass.new
So is there any advantage to either way? I guess if you make it private, you send some kind of communication to the developer that you have a preference and this should be used as a private method, rather than a public method.
In practice, I don't see an advantage.
I want a method to be executed every 5 minutes, I implemented whenever for ruby (cron). But it does not work. I think my method is not accessible.
The method I want to execute is located in a class. I think I have to make that method static so I can access it with MyClass.MyMethod. But I can not find the right syntax or maybe I am looking in the wrong place.
Schedule.rb
every 5.minutes do
runner "Ping.checkPings"
end
Ping.rb
def checkPings
gate = Net::Ping::External.new("10.10.1.1")
#monitor_ping = Ping.new()
if gate.ping?
MonitorPing.WAN = true
else
MonitorPing.WAN = false
end
#monitor_ping.save
end
To declare a static method, write ...
def self.checkPings
# A static method
end
... or ...
class Myclass extend self
def checkPings
# Its static method
end
end
You can use static methods in Ruby like this:
class MyModel
def self.do_something
puts "this is a static method"
end
end
MyModel.do_something # => "this is a static method"
MyModel::do_something # => "this is a static method"
Also notice that you're using a wrong naming convention for your method. It should be check_pings instead, but this does not affect if your code works or not, it's just the ruby-style.
Change your code from
class MyModel
def checkPings
end
end
to
class MyModel
def self.checkPings
end
end
Note there is self added to the method name.
def checkPings is an instance method for the class MyModel whereas def self.checkPings is a class method.
Instead of extending self for the whole class, you can create a block that extends from self and define your static methods inside.
you would do something like this :
class << self
#define static methods here
end
So in your example, you would do something like this :
class Ping
class << self
def checkPings
#do you ping code here
# checkPings is a static method
end
end
end
and you can call it as follows : Ping.checkPings
There are some ways to declare a static method in RoR.
#1
class YourClassName
class << self
def your_static_method (params)
# Your code here
end
end
end
#2
class YourClassName
def self.your_status_method
// Your code here
end
end
You cannot have static methods in Ruby. In Ruby, all methods are dynamic. There is only one kind of method in Ruby: dynamic instance methods.
Really, the term static method is a misnomer anyway. A static method is a method which is not associated with any object and which is not dispatched dynamically (hence "static"), but those two are pretty much the definition of what it means to be a "method". We already have a perfectly good name for this construct: a procedure.
class Example
private
def example_test
puts 'Hello'
end
end
e = Example.new
e.example_test
This of course will not work, because we specified explicit receiver - instance of Example (e), and that is against a "private rule".
But I cannot understand, why one cannot do in Ruby this:
class Foo
def public_m
self.private_m # <=
end
private
def private_m
puts 'Hello'
end
end
Foo.new.public_m
The current object inside public_m method definition (i.e. self) is the instance of Foo. So why it is not allowed? To fix that I have to change self.private_m to just private_m. But why this differ, isn't the self an instance of Foo inside public_m? And who is the receiver of bare-word private_m call? Isn't that self - what actually you omit because, Ruby will do it for you (will call private_m on self)?
I hope I didn't confuse it too much, I am still fresh to Ruby.
EDIT:
Thank you for all the answers. Putting them all together I was able (finally) to grok the obvious (and not so obvious for someone, who have never seen things like Ruby): that self itself can be
explicit and implicit receiver and that make the difference. So there are two rules, if you want to call a private method: self must be implicit receiver, and that self must be an instance of current class (Example in that case - and that takes place only when self if inside instance method definition, during this method execution). Please correct me if I am wrong.
class Example
# self as an explicit receiver (will throw an error)
def explicit
self.some_private_method
end
# self as an implicit receiver (will be ok)
def implicit
some_private_method
end
private
def some_private_method; end
end
Example.new.implicit
Message for anyone who could find this question during the google trails: this may be helpful - http://weblog.jamisbuck.org/2007/2/23/method-visibility-in-ruby
Here's the short and the long of it. What private means in Ruby is a method cannot be called with an explicit receivers, e.g. some_instance.private_method(value). So even though the implicit receiver is self, in your example you explicitly use self so the private methods are not accessible.
Think of it this way, would you expect to be able to call a private method using a variable that you have assigned to an instance of a class? No. Self is a variable so it has to follow the same rules. However when you just call the method inside the instance then it works as expected because you aren't explicitly declaring the receiver.
Ruby being what it is you actually can call private methods using instance_eval:
class Foo
private
def bar(value)
puts "value = #{value}"
end
end
f = Foo.new
begin
f.bar("This won't work")
rescue Exception=>e
puts "That didn't work: #{e}"
end
f.instance_eval{ bar("But this does") }
Hope that's a little more clear.
-- edit --
I'm assuming you knew this will work:
class Foo
def public_m
private_m # Removed self.
end
private
def private_m
puts 'Hello'
end
end
Foo.new.public_m
The definition of private in Ruby is "can only be called without an explicit receiver". And that's why you can only call private methods without an explicit receiver. There is no other explanation.
Note that there actually is an exception to the rule: because of the ambiguity between local variables and method calls, the following will always be resolved to be an assignment to a local variable:
foo = :bar
So, what do you do if you want to call a writer called foo=? Well, you have to add an explicit receiver, because without the receiver Ruby simply won't know that you want to call the method foo= instead of assigning to the local variable foo:
self.foo = :bar
But what do you do if you want to call a private writer called foo=? You can't write self.foo = because foo= is private and thus cannot be called with an explicit receiver. Well, actually for this specific case (and this case alone), you can actually use an explicit receiver of self to call a private writer.
It's weird, but many things about Ruby's visibility modifiers are weird. Even if self is the implicit receiver, actually spelling it out makes it explicit in the eyes of the Ruby runtime. When it says that private methods cannot be called with an explicit receiver, that is what it means, even self counts.
IIRC, private methods allow only implicit receiver (which is always self, of course).
Sorry for my prevoius answer. I just don't understand your question.
I changed your code like this:
class Foo
def public_m
private_m # <=
end
def Foo.static_m
puts "static"
end
def self.static2_m
puts "static 2"
end
private
def private_m
puts 'Hello'
end
end
Foo.new.public_m
Foo.static_m
Foo.static2_m
Here is a call of instance method:
def public_m
private_m # <=
end
Here are a call of class methods:
def Foo.static_m
puts "static"
end
def self.static2_m
puts "static 2"
end
Foo.static_m
Foo.static2_m
Adding some enhancements to User Gates solution. Calling a private method to class method or an instance method is pretty much possible. Here is the Code Snippets. But not recommended.
Class Method
class Example
def public_m
Example.new.send(:private_m)
end
private
def private_m
puts 'Hello'
end
end
e = Example.new.public_m
Instance Method
class Example
def self.public_m
Example.new.send(:private_m)
end
private
def private_m
puts 'Hello'
end
end
e = Example.public_m
Does not exactly answer the Question, but you can call private methods this way
class Example
private
def example_test
puts 'Hello'
end
end
e = Example.new
e.send(:example_test)
Just in case someone stumbles this now. From Ruby 2.7, calling a private method with a literal self as the receiver is now allowed.
We can verify this as well by running the original ruby snippet against versions 2.6.9 and 3.1.
(ins)tmp->cat sample.rb
class Foo
def public_m
self.private_m # <=
end
private
def private_m
puts 'Hello'
end
end
Foo.new.public_m
# See no exception is raise with 3.1 version
(ins)tmp->ruby -v
ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-darwin20]
(ins)tmp->ruby sample.rb
Hello
Now if we try to run the same script with version 2.6.9, we see that exception is raised.
(ins)tmp->ruby -v
ruby 2.6.9p207 (2021-11-24 revision 67954) [x86_64-darwin20]
(ins)tmp->
(ins)tmp->ruby sample.rb
sample.rb:3:in `public_m': private method `private_m' called for #<Foo:0x00007ff95289f870> (NoMethodError)
Did you mean? private_methods
from sample.rb:11:in `<main>'
This is a simple style question. What is the preferred means for declaring access controls in Ruby code?
Example A:
#!/usr/bin/env ruby
class MyClass
def method1 # this is public by default
#...
end
protected # subsequent methods will be protected
def method2
#...
end
private # subsequent methods will be private
def method3
#...
end
public # subsequent methods will be public
def method4
#...
end
end
or Example B:
#!/usr/bin/env ruby
class MyClass
def method1
#...
end
def method2
#...
end
def method3
#...
end
def method4
#...
end
public :method1, :method4
protected :method2
private :method3
end
Syntactically, I like Example B. A introduces ambiguity between public methods declared after protected/private methods, although I see no reason why you shouldn't just call method1 after specifying it as being public.
This isn't however about what I like. What is the industry defined norm for this?
The only place I've ever seen the second method used is in Ruby books, and only as a "You can also do this" example.
And you very rarely see the use of "public" like in the first method, as it's the default and people just define all their public methods before any protected/private declarations.
I think it really depends on your coding style. If you read "Clean Code" by Uncle Bob, you (which I personally loved), you're encouraged to write methods or functions that are called by each other closely together. In this case, using the visibility of a method as in example B would make sense:
class Foo
def method1
method2
end
def method2
..
end
private :method2
end
Uncle Bob actually makes a good case for having methods close together, since this prevents scrolling in your code.
Ruby doesn't seem to have a facility for defining a protected/private block like so:
protected do
def method
end
end
This would be nice compared to
protected
def method
end
public
where you might forget to "public" after the protected methods.
It seems possible to implement this using metaprogramming. Any ideas how?
Since you want to group by functionality you can declare all your methods, and then declare which ones are protected and private by using protected followed by the symbols of the methods you want to be protected, and the same for private.
The following class shows what I mean. In this class all methods are public except bar_protected and bar_private which are declared protected and private at the end.
class Foo
def bar_public
print "This is public"
end
def bar_protected
print "This is protected"
end
def bar_private
print "This is private"
end
def call_protected
bar_protected
end
def call_private
bar_private
end
protected :bar_protected
private :bar_private
end
I actually endorse bodnarbm's solution and do not recommend doing this, but since I can't pass up a metaprogramming challenge, here's a hack that will accomplish this:
class Module
def with_protected
alias_if_needed = lambda do |first, second|
alias_method first, second if instance_methods.include? second
end
metaclass = class<<self; self end
metaclass.module_eval {|m| alias_if_needed[:__with_protected_old__, :method_added]}
def self.method_added(method)
protected method
send :__with_protected_old__ if respond_to? :__with_protected_old__
end
yield
metaclass.module_eval do |m|
remove_method :method_added
alias_if_needed[:method_added, :__with_protected_old__]
end
end
end
Old question, but I think this is a bit cleaner:
class Whatever
module ProtectedBits
protected
def foo()
...
end
end
include ProtectedBits
end