Reputation: 678
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
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
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