scuttle-jesuit
scuttle-jesuit

Reputation: 678

Accessing instance method from class method

I have a rails 4 app that has an alert model and tests associated to each alert.

When a new alert is created I have a an after_create filter that uses an instance method to create a new test:

class Alert < ActiveRecord::Base
    has_many :tests
    after_create :create_test

    private

    def create_test

    #bunch of code using external api to get some data  


     Test.create 

    end
end

I also have a cron job that I want to use to create a new test for each alert. My plan was to have a class method to do that:

def self.scheduled_test_creation
        @alerts = Alert.all
        @alerts.each do |a|
          a.create_test        
        end
    end

That won't work because the instance method is private. I know I can get around this using send for example. Or I can make the methods public. Or I can rewrite that bunch of api code in the instance method.

I am just not sure what the best way would be. I don't want to write the same code twice and I want to make sure is good practice. Maybe in this case the methods don't have to be private - I know the difference between public/private/protected but I don't really understand when methods should be private/protected.

Any help would be greatly appreciated

Upvotes: 0

Views: 113

Answers (2)

Topher Hunt
Topher Hunt

Reputation: 4804

I agree with @SergioTulentsev: while in the long run you may be better served by breaking out this logic into a service class, in the short run you simply shouldn't make a method private if it needs to be called outside of the instance.

In some cases you actually want to access a private method, for example when verifying object state during tests. This is easy to do:

@alert.instance_eval{ create_test }

You can even fetch or alter instance variables this way:

@alert.instance_eval{ @has_code_smells = true }

In general, if you feel the need to do this, it's a warning smell that your logic needs to be rethunk. Ignoring that sort of smell is what turns Ruby from a wonderful language into a way-too-powerful language that allows you to shoot yourself in the foot. But it's doable.

Upvotes: 0

fylooi
fylooi

Reputation: 3870

I like service classes for interactions between multiple models. Callbacks can make the logic quite hard to follow.

Eg:

class AlertCreator
  def initialize(alert)
    @alert = alert
  end

  def call
    if @alert.save
      alert_test = TestBuilder.new(@alert).call
      alert_test.save
      true
    end
  end
end

class TestBuilder
  def initialize(alert)
    @alert = alert
  end

  def call
    # external API interaction stuff
    # return unsaved test
  end
end

Inside your controller, you'd call AlertCreator.new(@alert).call instead of the usual @alert.save.

Upvotes: 2

Related Questions