Rails: storing filter conditions by attributes and checking if the conditions pass?
I have a SaaS application where the account has many users.
In the account, the account owner can specify specific filters for users who apply to participate in the account. For example, it can specify that these requirement conditions must be met in order for a user to be considered: user.age> 21, user.name.count> 4.
I was thinking about creating a FilterCondition model that belongs to an account where the line could look like account_id: 1, attribute: "age", condition_string: "> 21"
oraccount_id: 1, attribute: "phone_type", condition_string: "== iphone"
and then when I only want to accept users meeting those 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 this is completely wrong syntax, but he should figure out what I would like to do.
Any suggested ways to allow accounts to save the requirement terms and then check against Users to see if they meet the requirement?
source to share
It will be easier if the condition string is split into a comparator (for example >
) and a comparable value:
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 method safe_comparator
reduces the risk of SQL injection into the query. The filter collection chaining 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
source to share