I have PORO (Plain Old Ruby Object) to deal with some business logic. It receives an ActiveRecord object and classifies it. For simplicity, take the following as an example:
class Classificator STATES = { 1 => "Positive", 2 => "Neutral", 3 => "Negative" } def initializer(item) @item = item end def name STATES.fetch(state_id) end private def state_id return 1 if @item.value > 0 return 2 if @item.value == 0 return 3 if @item.value < 0 end end
However, I also want to make queries that group objects based on these state_id "virtual attributes". I am currently doing this by creating this attribute in SQL queries and using it in GROUP BY statements. See an example:
class Classificator::Query SQL_CONDITIONS = { 1 => "items.value > 0", 2 => "items.value = 0", 3 => "items.value < 0" } def initialize(relation = Item.all) @relation = relation end def count @relation.select(group_conditions).group('state_id').count end private def group_conditions 'CASE ' + SQL_CONDITIONS.map do |k, v| 'WHEN ' + v.to_s + " THEN " + k.to_s end.join(' ') + " END AS state_id" end end
That way, I can get this business logic in SQL and make such a query in a very efficient way.
The problem is this: I have duplicated business logic. It exists in the ruby โโcode for classifying a single object, and also in SQL for classifying a collection of objects at the database level.
Is this a bad practice? Is there any way to avoid this? I really was able to do this by following these steps:
item = Item.find(4) items.select(group_conditions).where(id: item.id).select('state_id')
But by doing this, I lose the ability to classify objects that are not stored in the database. Another way is to classify each object in ruby โโusing Iterator, but then I will lose database performance.
It seems inevitable to maintain duplicate business logic if I need the best of two cases. But I just want to be sure of that. :)
Thanks!