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
Related
I am trying to work our for myself access modifiers in Ruby. I have:
class Person
def initialize (first_name, last_name, age)
#first_name=first_name
#last_name=last_name
#age=age
end
def show()
puts #first_name
puts #last_name
puts #age
end
protected
def compare(other)
self.instance_variable_get(:#age)<=>other.instance_variable_get(:#age)
end
end
p1=Person.new("Some", "Body", "99")
p1.show
puts "\n"
p2=Person.new("Who", "Ever", "21")
p2.show
puts "\n"
p1.compare(p2)
I am getting the error "protected method `compare' called for # (NoMethodError)"
I have tried calling from within the class and without. I pasted the without version here. I thought that protected methods could be called on other objects of the same class. What does this error mean and how would I properly use a protected method here? Thank you for your help.
You got the wrong view of the protected visibility. The Ruby doc says:
The second visibility is protected. When calling a protected method the sender must be a subclass of the receiver or the receiver must be a subclass of the sender. Otherwise a NoMethodError will be raised.
So the restriction of the visibility is applied to the sender, not the receiver as what you thought.
If you want to call compare outside of the instance methods, you need to use public visibility. You need to remove the protected modifier if you can. This is the recommended way.
If the code is fixed and you cannot modify that piece of code, you can use the Object#send method. Object#send will bypass the visibility constraint and can access even private methods.
p1.send(:compare, p2)
Or you can reopen the class and change the visibility of the compare class:
# you code here
# reopen and modify visibility
class Person
public :compare
end
p1.compare(p2)
You can call a protected method in a public method of the class...
class Person
def initialize (first_name, last_name, age)
#first_name=first_name
#last_name=last_name
#age=age
end
def same_age?(other)
age == other.age
end
def show
puts #first_name
puts #last_name
puts #age
end
protected
def age
#age
end
end
p1=Person.new("Some", "Body", "99")
p1.show
puts "\n"
p2=Person.new("Who", "Ever", "21")
p2.show
puts "\n"
# calls a method that calls a protected method
p1.same_age?(p2)
=> false
# but you can't call #age directly...
begin
p1.age
rescue NoMethodError
puts "no method error (protected)"
end
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 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
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.
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.