Reputation: 1349
I have a SaaS application where an Account has many Users.
In an Account, the account owner can specify certain filters on the users that are applying to be part of the account. For instance, he can set that these requirement conditions must be met in order for user to be accepted into account : user.age > 21, user.name.count > 4.
I was thinking of creating a FilterCondition model, which belongs to Account, where a row could look like account_id: 1, attribute: "age", condition_string: "> 21"
or account_id: 1, attribute: "phone_type", condition_string: "== iphone"
and then when I want to only accept users that meet these conditions do something like
#User.rb
def passes_requirements?
account.filter_conditions.each do |filter_conditions|
attr = filter_condition.attribute
return false if self.attr != filter_condition.condition
end
true
end
I know that is completely wrong syntax, but it should get the point across on what I would like to do.
Any suggested ways to allow accounts to save requirement conditions, and then check on Users to see if they meet those requirements?
Upvotes: 4
Views: 1386
Reputation: 2246
It will be easier if the condition string is separated into a comparator (e.g >
) and the value to be compared:
class FilterCondition < ActiveRecord::Base
belongs_to :account
validates :attribute, presence: true
validates :comparator, presence: true
validates :value, presence: true
def matching_users(query_chain = User.where(account: account))
query_chain.where("#{attribute} #{safe_comparator} ?", value)
end
private
def safe_comparator
safe_values = ['=', '>', '>=', '<', '<='] # etc
return comparator if safe_values.include? comparator
''
end
end
The safe_comparator
method reduces the risk of SQL injection into the query. Chaining a collection of filters is a bit tricky, but something like the following idea should work.
class Account < ActiveRecord::Base
#....
has_many :filter_conditions
def filtered_users
query = User.where(account: self)
filter_conditions.each do |filter_condition|
query = filter_condition.matching_users(query)
end
query
end
end
account = Account.first
filter_1 = FilterCondition.create(
account: account,
attribute: :age,
comparator: '>=',
value: 21
)
filter_2 = FilterCondition.create(
account: account,
attribute: :age,
comparator: '<=',
value: 99
)
account.filtered_users
Upvotes: 5