Ruby class with static method calling a private method?
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
Calling a private instance method from a class method in Ruby
Using private or protected really don't do that much in Ruby. You can call send on any object and use any method it has.
class Foo
def Foo.bar(my_instance, n)
my_instance.send(:plus, n)
end
end
How to create a private class method?
private
doesn't seem to work if you are defining a method on an explicit object (in your case self
). You can use private_class_method
to define class methods as private (or like you described).
class Person
def self.get_name
persons_name
end
def self.persons_name
"Sam"
end
private_class_method :persons_name
end
puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name
Alternatively (in ruby 2.1+), since a method definition returns a symbol of the method name, you can also use this as follows:
class Person
def self.get_name
persons_name
end
private_class_method def self.persons_name
"Sam"
end
end
puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name
Call private class method from private instance method
First let me try to explain why the code does not work
class MyModel < ActiveRecord::Base
def self.create_instance
model = MyModel.new
# in here, you are not inside of the instance scope, you are outside of the object
# so calling model.somemething can only access public method of the object.
model.init_some_dependencies
...
end
...
You could bypass private calling of the method with model.send :init_some_dependencies
. But I think in this case there is probably better solution.
I would guess that init_some_dependencies
probably contain more business / domain logic rather than persistence. That's why I would suggest to pull out this logic into a "Domain Object" (or some call it Service Object). Which is just a plain ruby object that contain domain logic.
This way you could separate persistence logic to ActiveRecord and the domain logic to that class. Hence you will not bloat the ActiveRecord Model. And you get the bonus of testing
the domain logic without the need of ActiveRecord. This will make your test faster.
You could create a file say `lib/MyModelDomain.rb'
class MyModelDomain
attr_accessor :my_model
def initialize(my_model)
@my_model = my_model
end
def init_some_dependencies
my_model.property = 'some value example'
end
end
Now you could use this object say something like this
class MyModel < ActiveRecord::Base
def self.create_instance
model = MyModel.new
domain = MyModelDomain.new(model)
domain.init_some_dependencies
domain.my_model
end
def initialize_instance
# do some other work
other_init
domain = MyModelDomain.new(self)
domain.init_some_dependencies
end
end
You might also want to move the initialize_instance
if you think it's necessary
Some resource that go deep into this pattern:
- http://railscasts.com/episodes/398-service-objects
- https://www.destroyallsoftware.com/screencasts/catalog/extracting-domain-objects
Is there a way to call a private Class method from an instance in Ruby?
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.
static and private method behavior when calling direct on object of child class sounds like overriding?
Polymorphism is not for static methods. Static methods are called with JVM instructions invokestatic, whereas polymorphism is achieved with invokevirtual. The calls to static methods are determined at compile time, and polymorphic methods are dynamically dispatched at runtime.
You can easily tweak your code so that A.staticMethod() is called, by just assigning new B() to a variable of type A.
public static void main(String[] args) {
new B().privateMethod();
A b = new B(); // change here.
b.staticMethod(); // A.staticMethod() is called here.
}
Call Private methods outside class definition
Something like this should work
module AllClassesMethods
def self.included(base)
base.class_eval do
do_z :param do
set_property 'a', :x, :y, false
set_property 'b', :x, :y, false
end
end
end
end
module OnlyBMethods
def self.included(base)
base.class_eval do
do_z :param do
set_property 'only for class B', :x, :y, true
end
end
end
end
class A
include ModuleB::PrivateMethods
include AllClassesMethods
def self.inherited(klass)
klass.include AllClassesMethods
end
end
class B < A
include OnlyBMethods
end
A
and any class that inherits from A
will include AllClassesMethods
, running the code in its included
method. It has to be explicitly included on each inherited class, or else the included
method will only get called for the parent A
. The class_eval
block executes within the including class's context, so it's just like opening up the class in your class definition. Only B
is including OnlyBMethods
, and therefore is the only one triggerring the included
implementation of both Modules.
There's another approach you could use. If you define a class method macro in an extend
ed module, the class method will be executed within the class context, also giving you easy access to it's private methods (I say "easy" access because in Ruby you can always access an object's private methods from any context by using send
)
module AllClassesMethods
def does_z
do_z :param do
set_property 'a', :x, :y, false
set_property 'b', :x, :y, false
end
end
def does_z_for_b
do_z :param do
set_property 'only for class B', :x, :y, true
end
end
end
class A
include ModuleB::PrivateMethods
extend AllClassesMethods
does_z
def self.inherited(klass)
klass.does_z
end
end
class B < A
does_z_for_b
end
What is the use in class/static methods in ruby?
Your example isn't a good one.
Class methods might deal with managing all instances that exist of a class, and instance methods deal with a single instance at a time.
class Book
def self.all_by_author(author)
# made up database call
database.find_all(:books, where: { author: author }).map do |book_data|
new book_data # Same as: Book.new(book_data)
end
end
def title
@title
end
end
books = Book.all_by_author('Jules Vern')
books[0].title #=> 'Journey to the Center of the Earth'
In this example we have a class named Book
. It has a class method all_by_author
. It queries some pretend database and returns an array of Book
instances. The instance method title
fetches the title of a single Book
instance.
So the class method managing a collection of instances, and the instance method manages just that instance.
In general, if a method would operate on a group of instances, or is code related to that class but does not directly read or update a single instance, then it probably should be a class method.
Related Topics
Resque Multiple Workers in Development Mode
Best Way to Combine Fragment and Object Caching for Memcached and Rails
How to Delete Specific Characters from a String in Ruby
How to Write Specs for Code That Depends on Environment Variables
Capistrano & Bash: Ignore Command Exit Status
Vcrproxy: Record Phantomjs Ajax Calls with Vcr Inside Capybara
How to Get an Array with Column Names of a Table
Ruby: What Does the Asterisk in "P *1..10" Mean
Rails Console - Find Where Created at = Certain Day
Is Ruby a Scripting Language or an Interpreted Language
Neither Ruby and Nor Irb Can Load .Rb File in Current Directory
Sorting: Sort Array Based on Multiple Conditions in Ruby
How to Reflect in the Database a New Belongs_To and Has_Many Relationship in Ruby on Rails
Cucumber Not Showing Coloured Output in Windows
Generating Unique Token on the Fly with Rails
How to Read a Barcode from an Image