patternrubyrailsMinor
Determining whether a task is active based on many conditions
Viewed 0 times
conditionsdeterminingactivebasedmanywhethertask
Problem
I wrote a Rails model instance method that checks many separate conditions, and returns a single boolean. It does not employ any nesting of branching structures. Each check has its own comments describing its reasoning. Every check is necessary for the method to return an accurate value, and none of the checks are likely to be reused independently of the other checks. The function is 34 lines long including comments. A colleague reviewing the code suggested that the method may be too long, and therefore would be more readable if broken out into multiple helper methods for greater readability.
```
def active?
#Check columns on self first, so if those dont pass,
#no other records will need to be loaded.
return false if self[:active] == false && !ignore_column
return false if self.suspended # here for thoroughness. tasks should never be marked active and suspended.
return false if self.completed_at # here for thoroughness. tasks should never be marked active and completed.
if failed_attempts >= MAX_FAILED_ATTEMPTS # here for thoroughness. tasks should never be marked active after the max failed attempts.
if !suppress_error_emails
SystemMailer.error message:"Too many failed attempts for task execution. #{failed_attempts}/#{MAX_FAILED_ATTEMPTS} failed attempts for Task #{id}."
end
return false
end
#If the origin is inactive and not because it is a withdrawn status, then the task should be inactive.
return false if !evergreen && origin && !origin.active && origin.is_a?(Crm::Status) && !origin.indicates_closed # Status or Subscription
return false if sequenceable.is_a?(Crm::Case) && sequenceable.sales_stage_id.to_i>Crm::SalesStage.id('Placed') #placed policies shouldn't fire tasks
#Recruits are always marked inactive because they are users that can't log in.
#If the person is inactive but not a recruit, the task should be inactive.
is_a_recruit=person.try(:is_a?,Usage::User) && person.try(:role)==Usage::Role['recruit']
retur
```
def active?
#Check columns on self first, so if those dont pass,
#no other records will need to be loaded.
return false if self[:active] == false && !ignore_column
return false if self.suspended # here for thoroughness. tasks should never be marked active and suspended.
return false if self.completed_at # here for thoroughness. tasks should never be marked active and completed.
if failed_attempts >= MAX_FAILED_ATTEMPTS # here for thoroughness. tasks should never be marked active after the max failed attempts.
if !suppress_error_emails
SystemMailer.error message:"Too many failed attempts for task execution. #{failed_attempts}/#{MAX_FAILED_ATTEMPTS} failed attempts for Task #{id}."
end
return false
end
#If the origin is inactive and not because it is a withdrawn status, then the task should be inactive.
return false if !evergreen && origin && !origin.active && origin.is_a?(Crm::Status) && !origin.indicates_closed # Status or Subscription
return false if sequenceable.is_a?(Crm::Case) && sequenceable.sales_stage_id.to_i>Crm::SalesStage.id('Placed') #placed policies shouldn't fire tasks
#Recruits are always marked inactive because they are users that can't log in.
#If the person is inactive but not a recruit, the task should be inactive.
is_a_recruit=person.try(:is_a?,Usage::User) && person.try(:role)==Usage::Role['recruit']
retur
Solution
Some comments on your code:
In a first refactor, I'd write a more readable
On a second refactor, I'd probably move all logic to separate methods. Now you could write:
- Keep the line length below 80-100 or the code becomes unreadable.
self[:active] == false->!self[:active]
- Checks should be terser, encapsulate logic in methods.
- A
returnat the end of a code block is neither necessary nor idiomatic.
- [Subjective] I usually prefer full-fledge conditionals to guards, indentation helps a lot to see what's going on.
In a first refactor, I'd write a more readable
case:def active?
case
when (!self[:active] && !ignore_column) || suspended || completed_at
false
when failed_attempts >= MAX_FAILED_ATTEMPTS
if suppress_error_emails
false
end
message = "Too many failed attempts for task execution. " +
"#{failed_attempts}/#{MAX_FAILED_ATTEMPTS} failed attempts for Task #{id}."
SystemMailer.error(message: message)
end
# ...
# more conditions here, you get the idea
# ...
when template &&
(status_opted_out? || con_mktg_opted_out? || user_mktg_opted_out?)
false
else
true
end
endOn a second refactor, I'd probably move all logic to separate methods. Now you could write:
def active?
condition1? && condition2? && ... && conditionN?
endCode Snippets
def active?
case
when (!self[:active] && !ignore_column) || suspended || completed_at
false
when failed_attempts >= MAX_FAILED_ATTEMPTS
if suppress_error_emails
false
end
message = "Too many failed attempts for task execution. " +
"#{failed_attempts}/#{MAX_FAILED_ATTEMPTS} failed attempts for Task #{id}."
SystemMailer.error(message: message)
end
# ...
# more conditions here, you get the idea
# ...
when template &&
(status_opted_out? || con_mktg_opted_out? || user_mktg_opted_out?)
false
else
true
end
enddef active?
condition1? && condition2? && ... && conditionN?
endContext
StackExchange Code Review Q#127651, answer score: 2
Revisions (0)
No revisions yet.