lib/appfuel/domain/expr.rb in appfuel-0.2.3 vs lib/appfuel/domain/expr.rb in appfuel-0.2.4
- old
+ new
@@ -1,127 +1,96 @@
module Appfuel
module Domain
# Domain expressions are used mostly by the criteria to describe filter
- # conditions. The class represents a basic expression like "id eq 6", the
- # problem with this expression is that we need additional information in
- # order to properly map it to something like a db expression. This call
- # ensures that additional information exists. Most importantly we need
- # a fully qualified domain name in the form of "feature.domain".
+ # conditions. The class represents a basic expression like "id = 6", the
+ # problem with this expression is that "id" is relative to the domain
+ # represented by the criteria. In order to convert that expression to a
+ # storage expression for a db, that expression must be fully qualified in
+ # the form of "features.feature_name.domain.id = 6" so that the mapper can
+ # correctly map to database attributes. This class provides the necessary
+ # interfaces to allow a criteria to qualify all of its relative expressions.
+ # It also allows fully qualifed expressions to be used.
class Expr
- include DomainNameParser
- OPS = {
- eq: '=',
- gt: '>',
- gteq: '>=',
- lt: '<',
- lteq: '<=',
- in: 'IN',
- like: 'LIKE',
- ilike: 'ILIKE',
- between: 'BETWEEN'
- }
- attr_reader :feature, :domain_basename, :domain_name, :domain_attr, :value
+ attr_reader :feature, :domain_basename, :domain_attr, :attr_list, :op, :value
- # Assign the fully qualified domain name, its basename and its attribute
- # along with the operator and value. Operator and value are assumed to
- # be the first key => value pair of the hash.
- #
- # @example
- # feature domain
- # Expr.new('foo.bar', 'id', eq: 6)
- #
- # or
- # global domain
- # Expr.new('bar', 'name', like: '%Bob%')
- #
- #
- # @param domain [String] fully qualified domain name
- # @param domain_attr [String, Symbol] attribute name
- # @param data [Hash] holds operator and value
- # @option data [Symbol] the key is the operator and value is the value
- #
- # @return [Expr]
- def initialize(domain, domain_attr, data)
- fail "operator value pair must exist in a hash" unless data.is_a?(Hash)
- @feature, @domain_basename, @domain_name = parse_domain_name(domain)
+ def initialize(domain_attr, op, value)
+ @attr_list = parse_domain_attr(domain_attr)
+ @op = op.to_s.strip
+ @value = value
- operator, value = data.first
- @domain_attr = domain_attr.to_s
- self.op = operator
- self.value = value
+ fail "op can not be empty" if @op.empty?
+ fail "attr_list can not be empty" if @attr_list.empty?
+ end
- fail "domain name can not be empty" if @domain_name.empty?
- fail "domain attribute can not be empty" if @domain_attr.empty?
+ def qualify_feature(feature, domain)
+ fail "this expr is already qualified" if qualified?
+
+ attr_list.unshift(domain)
+ attr_list.unshift(feature)
+ attr_list.unshift('features')
+ self
end
- def feature?
- !@feature.nil?
+ def qualify_global(domain)
+ fail "this expr is already qualified" if qualified?
+ attr_list.unshift(domain)
+ attr_list.unshift('global')
+ self
end
def global?
- !feature?
+ attr_list[0] == 'global'
end
- # @return [Bool]
- def negated?
- @negated
+ def conjunction?
+ false
end
- def expr_string
- data = yield domain_name, domain_attr, OPS[op]
- lvalue = data[0]
- operator = data[1]
- rvalue = data[2]
+ def qualified?
+ attr_list[0] == 'global' || attr_list[0] == 'features'
+ end
- operator = "NOT #{operator}" if negated?
- "#{lvalue} #{operator} #{rvalue}"
+ def feature
+ index = global? ? 0 : 1
+ attr_list[index]
end
- def to_s
- "#{domain_name}.#{domain_attr} #{OPS[op]} #{value}"
+ def domain_basename
+ index = global? ? 1 : 2
+ attr_list[index]
end
- def op
- negated? ? "not_#{@op}".to_sym : @op
+ def domain_name
+ "#{feature}.#{domain_basename}"
end
- private
+ def domain_attr
+ start_range = global? ? 2 : 3
+ end_range = -1
+ attr_list.slice(start_range .. end_range).join('.')
+ end
- def op=(value)
- negated, value = value.to_s.split('_')
- @negated = false
- if negated == 'not'
- @negated = true
- else
- value = negated
- end
- value = value.to_sym
- unless supported_op?(value)
- fail "op has to be one of [#{OPS.keys.join(',')}]"
- end
- @op = value
+ def to_s
+ "#{attr_list.join('.')} #{op} #{value}"
end
- def value=(data)
- case op
- when :in
- unless data.is_a?(Array)
- fail ":in operator must have an array as a value"
- end
- when :range
- unless data.is_a?(Range)
- fail ":range operator must have a range as a value"
- end
- when :gt, :gteq, :lt, :lteq
- unless data.is_a?(Numeric)
- fail ":gt, :gteq, :lt, :lteq operators expect a numeric value"
- end
+ def validate_as_fully_qualified
+ unless qualified?
+ fail "expr (#{to_s}) is not fully qualified, mapping will not work"
end
- @value = data
+ true
end
- def supported_op?(op)
- OPS.keys.include?(op)
+ private
+
+ def parse_domain_attr(list)
+ list = list.split('.') if list.is_a?(String)
+
+ unless list.is_a?(Array)
+ fail "Domain attribute must be a string in the form of " +
+ "(foo.bar.id) or an array ['foo', 'bar', 'id']"
+ end
+ list
end
end
end
end