Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call private class method from private instance method

Tags:

oop

ruby

I am new to Ruby and came from C# world. In C# it is legal to do stuff like this:

public class Test
{
  public void Method()
  {
     PrivateMethod();
  }

  private void PrivateMethod()
  {
     PrivateStaticMethod();
  }

  private static void PrivateStaticMethod()
  {
  }
}

Is it possible to do something similar in Ruby?

A little bit of context: I have a Rails app... One of the models has a private method that sets up some dependencies. There is a class method that creates initialized instance of the model. For legacy reasons there are some instances of the model that are not initialized correctly. I added an instance method that initializes 'uninitialized' instances where I want to do same initialization logic. Is there a way to avoid duplication?

Sample:

class MyModel < ActiveRecord::Base

  def self.create_instance
    model = MyModel.new
    model.init_some_dependencies # this fails
    model
  end

  def initialize_instance
    // do some other work
    other_init
    // call private method
    init_some_dependencies
  end

  private

  def init_some_dependencies
  end

end

I tried to convert my private method to a private class method, but I still get an error:

class MyModel < ActiveRecord::Base

  def self.create_instance
    model = MyModel.new
    MyModel.init_some_dependencies_class(model)
    model
  end

  def initialize_instance
    # do some other work
    other_init
    # call private method
    init_some_dependencies
  end

  private

  def init_some_dependencies
     MyModel.init_some_dependencies_class(self) # now this fails with exception
  end

  def self.init_some_dependencies_class(model)
    # do something with model
  end

  private_class_method :init_some_dependencies_class

end
like image 677
Andrew Bezzub Avatar asked Aug 10 '13 01:08

Andrew Bezzub


People also ask

Can private method access another private method?

If the method is private you cannot access it from another class.

Can an instance call a private method?

Basically, following the Object Oriented paradigm you can always call a private or protected method within the own class.

Can private methods be called in other classes?

A private method is an access modifier used in a class that can only be called from inside the class where it is defined. It means that you cannot access or call the methods defined under private class from outside.


1 Answers

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
like image 196
ahmy Avatar answered Sep 20 '22 18:09

ahmy