I have a PORO (Plain Old Ruby Object) to deal with some business logic. It receives an ActiveRecord
object and classify it. For the sake of 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 do queries that groups objects based on these state_id
"virtual attribute". I'm currently dealing with that by creating this attribute in the SQL queries and using it in GROUP BY
statements. See the 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
This way, I can get this business logic into SQL and make this kind of query in a very efficient way.
The problem is: I have duplicated business logic. It exists in "ruby" code, to classify a single object and also in "SQL" to classify a collection of objects in database-level.
Is it a bad practice? Is there a way to avoid this? I actually was able to do this, doing the following:
item = Item.find(4)
items.select(group_conditions).where(id: item.id).select('state_id')
But by doing this, I loose the ability to classify objects that are not persisted in database. The other way out would be classifying each object in ruby, using an Iterator, but then I would lose database performance.
It's seem to be unavoidable to keep duplicated business logic if I need the best of the two cases. But I just want to be sure about this. :)
Thanks!
Aucun commentaire:
Enregistrer un commentaire