what does : mean in private :getWidth, :getHeight?
# define private accessor methods
def getWidth
#width
end
def getHeight
#height
end
# make them private
private :getWidth, :getHeight
The : sigil in Ruby denotes a symbol. Symbols are immutable strings which are used as identifiers all over the Ruby language.
private :getWidth, :getHeight
Sets the visibility of the getWidth and getHeight method to private. Its an alternative way of writing:
private
def getWidth
#width
end
def getHeight
#height
end
Beyond that this code is very unidiomatic. Getter methods in Ruby should not be prefixed and method names should always be snake_case and not camelCase.
private
def width
#width
end
def height
#height
end
Or:
private
attr_reader :height, :width
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 the following code:
class A
attr_reader :x, :y
private_class_method :new
def self.with_data
a = new
a.x = 2
a.y = 'sid'
a
end
end
The intent is to restrict changing values of x and y variables once the class is initialized through the factory method with_data. However, I want this to be allowed when the object is initialized from within the class, as evident from the code above.
But I am getting the following error when I invoke obj = A.with_data:
NoMethodError: undefined method `x='
Should't this be allowed from inside class? Do I need to define attr_writer for this? That would jeopardize encapsulation.
Also, I don't want to define a private setter method for each attribute in the class, as it might run into upto 30 instance level variables. Does ruby provide any feature to get around this?
Versions:
Ruby 1.9.3
So what you need in your case is Object#instance_variable_set:
class A
attr_reader :x, :y
private_class_method :new
def self.with_data
a = new
a.instance_variable_set(:#x, 2)
a.instance_variable_set(:#y, 'sid')
a
end
end
Usage:
a = A.with_data
#=> #<A:0x007ff37c979d30 #x=2, #y="sid">
a.x
#=> 2
a.x = 3
#=> NoMethodError: undefined method `x=' for #<A:0x007ff37c979d30 #x=2, #y="sid">
As the name implies, attr_reader will only define a getter, so you can use accessors inside the class either.
That said, what exactly are you trying to achieve? The following class will initialize attributes, expose them via a reader and not make them easily changeable from "outside". Isn't that just what you wanted to to?
class A
attr_reader :x, :y
def initialize
#x = 2
#y = 'sid'
end
end
The intent is to restrict changing values of x and y variables once
the class is initialized through the factory method with_data
class Foo
attr_reader :bar, :baz # <==== assures you only read, not write
def initialize
#bar = :bar
#baz = :baz
end
end
Now you can only read attributes, not write them:
foo = Foo.new
=> #<Foo:0x007ff6148f0a90 #bar=:bar, #baz=:baz>
foo.bar
#=> :bar
foo.bar = 2
#=> NoMethodError: undefined method `bar=' for #<Foo:0x007ff6148f0a90 #bar=:bar, #baz=:baz
I must admit that I don't understand your adversity for using initialize or an attr_writer. I feel the cleanest solution for when you have only one factory method is to use the standard name for factory methods in Ruby, namely new:
class A
attr_reader :x, :y
def initialize(x, y) self.x, self.y = x, y end
def self.new
super(2, 'sid')
end
private
attr_writer :x, :y
end
If you have multiple factory methods and want to make absolutely sure that nobody accidentally calls new, this is a good solution:
class A
attr_reader :x, :y
def initialize(x, y) self.x, self.y = x, y end
private_class_method :new
def self.with_data
new(2, 'sid')
end
private
attr_writer :x, :y
end
If you really, really, really must, you can replicate what new is doing in your factory methods. After all, the implementation of new is quite trivial:
class Class
def new(*args, &block)
obj = allocate
obj.__send__(:initialize, *args, &block)
obj
end
end
class Event
#event_list = {}
attr_reader :name, :value
def initialize(name, value)
#name = name
#value = value
end
def to_s
"#{#value}"
end
class << self
def event_list
#event_list
end
def event_list=(value); end
def register_event(name, value)
#event_list[name] = Event.new(name, value)
end
def registered_events
event_list
end
end
end
In the above code snippet I can access #event_list using Event.event_list, interesting thing is I am able to modify this variable from outside
Event.event_list[:name] = "hello"
Event.event_list # => { :name => 'hello' }
How can I avoid this ?, I don't want to modify #event_list from outside.
As far as I know, you can't stop outside code from modifying your instance variables in Ruby. Even if you don't use attr_reader and attr_writer it can still be accessed using Object#instance_variable_set. Ruby doesn't have private variables (or constants), only variables that you are politely asked not to modify.
If you don't define event_list=, that is seen as an indication that #event_list is a private variable. This is the solution to your problem.
Then there is the problem with mutable objects. Since almost all objects in Ruby sadly are mutable, usually if you can just get a reference to an object then you can change it.
This can be solved with Object#freeze which stops an object from being modified. Unfortunately this means that not even you can modify it.
Ruby is simply not very good for locking things down. This openness is a core part of the language that you probably need to learn to work with.
As others have said, just make the methods private:
class Event
#event_list = {a: 'dog'}
class << self
def pub_event_list
#event_list
end
def pub_event_list=(other)
#event_list=other
end
private
def event_list
#event_list
end
def event_list=(value)
#event_list = value
end
end
end
Event.event_list
#=> NoMethodError: private method `event_list' called for Event:Class
Event.pub_event_list
#=> {:a=>"dog"}
Event.event_list= {b: 'cat'}
#=> #NoMethodError: private method `event_list=' called for Event:Class
Event.pub_event_list= {b: 'cat'}
#=> {:b=>"cat"}
You are defining def event_list on your singleton which is providing access to the variable. Make that method private
EDIT:
When you are declaring #event_list it has scope in your singleton methods.
class Event
#event_list = {}
class << self
def event_list
# scoped from above
#event_list
end
end
end
Event.event_list #=> {}
To remove access simply make the method private:
class Event
#event_list = {}
class << self
private
def event_list
# scoped from above
#event_list
end
end
end
Event.event_list #=> NoMethodError: private method `event_list' called for Event:Class
Here is what I done for now, I don't know if it is a good solution
class Event
#event_list = {}
attr_reader :name, :value
def initialize(name, value)
#name = name
#value = value
end
def to_s
"#{#value}"
end
class << self
def register_event(name, value)
#event_list = #event_list.merge(name => Event.new(name, value))
end
def registered_events
#event_list.freeze
end
end
end
Now I can access #event_list without letting others to modify.
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
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..