kanbanworkflowstimelinescrumrubyroadmapproject-planningproject-managementopenprojectangularissue-trackerifcgantt-chartganttbug-trackerboardsbcf
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
283 lines
7.1 KiB
283 lines
7.1 KiB
class Report::Operator
|
|
include Report::QueryUtils
|
|
include Report::Validation
|
|
|
|
#############################################################################################
|
|
# Wrapped so we can place this at the top of the file.
|
|
def self.define_operators # :nodoc:
|
|
|
|
# Defaults
|
|
defaults do
|
|
def sql_operator
|
|
name
|
|
end
|
|
|
|
def where_clause
|
|
"%s %s '%s'"
|
|
end
|
|
|
|
def modify(query, field, *values)
|
|
query.where [where_clause, field, sql_operator, *values]
|
|
query
|
|
end
|
|
|
|
def label
|
|
@label ||= self.class.name.to_sym
|
|
end
|
|
end
|
|
|
|
# Operators from Redmine
|
|
new ">t-", :label => :label_less_than_ago do
|
|
include DateRange
|
|
def modify(query, field, value)
|
|
super query, field, -value.to_i, 0
|
|
end
|
|
end
|
|
|
|
new "w", :arity => 0, :label => :label_this_week do
|
|
def modify(query, field, offset = nil)
|
|
offset ||= 0
|
|
from = Time.now.at_beginning_of_week - ((l(:general_first_day_of_week).to_i % 7) + 1).days
|
|
from -= offset.days
|
|
'<>d'.to_operator.modify query, field, from, from + 7.days
|
|
end
|
|
end
|
|
|
|
new "t+", :label => :label_in do
|
|
include DateRange
|
|
def modify(query, field, *values)
|
|
super query, field, values.first.to_i, values.first.to_i
|
|
end
|
|
end
|
|
|
|
new "<=", :label => :label_less_or_equal
|
|
|
|
new "!", :label => :label_not_equals do
|
|
def modify(query, field, *values)
|
|
where_clause = "(#{field} IS NULL"
|
|
where_clause += " OR #{field} NOT IN #{collection(*values)}" unless values.compact.empty?
|
|
where_clause += ")"
|
|
query.where where_clause
|
|
query
|
|
end
|
|
end
|
|
|
|
new "t-", :label => :label_ago do
|
|
include DateRange
|
|
def modify(query, field, *values)
|
|
super query, field, -values.first.to_i, -values.first.to_i
|
|
end
|
|
end
|
|
|
|
new "!~", :arity => 1, :label => :label_not_contains do
|
|
def modify(query, field, *values)
|
|
value = values.first || ''
|
|
query.where "LOWER(#{field}) NOT LIKE '%#{quote_string(value.to_s.downcase)}%'"
|
|
query
|
|
end
|
|
end
|
|
|
|
new "=", :label => :label_equals do
|
|
def modify(query, field, *values)
|
|
if values.compact.empty?
|
|
query.where "1=0"
|
|
else
|
|
query.where "#{field} IN #{collection(*values)}"
|
|
end
|
|
query
|
|
end
|
|
end
|
|
|
|
new "~", :arity => 1, :label => :label_contains do
|
|
def modify(query, field, *values)
|
|
value = values.first || ''
|
|
query.where "LOWER(#{field}) LIKE '%#{quote_string(value.to_s.downcase)}%'"
|
|
query
|
|
end
|
|
end
|
|
|
|
new "<t+", :label => :label_in_less_than do
|
|
include DateRange
|
|
def modify(query, field, value)
|
|
super query, field, 0, value.to_i
|
|
end
|
|
end
|
|
|
|
new "t", :label => :label_today do
|
|
include DateRange
|
|
def modify(query, field)
|
|
super query, field, 0, 0
|
|
end
|
|
end
|
|
|
|
new ">=", :label => :label_greater_or_equal
|
|
|
|
new "!*", :arity => 0, :where_clause => "%s IS NULL", :label => :label_none
|
|
|
|
new "<t-", :label => :label_more_than_ago do
|
|
include DateRange
|
|
def modify(query, field, value)
|
|
super query, field, nil, -value.to_i
|
|
end
|
|
end
|
|
|
|
new ">t+", :label => :label_in_more_than do
|
|
include DateRange
|
|
def modify(query, field, value)
|
|
super query, field, value.to_i, nil
|
|
end
|
|
end
|
|
|
|
new "*", :arity => 0, :where_clause => "%s IS NOT NULL", :label => :label_all
|
|
|
|
# Our own operators
|
|
new "<", :label => :label_less
|
|
new ">", :label => :label_greater
|
|
|
|
new "=n", :label => :label_equals do
|
|
def modify(query, field, value)
|
|
query.where "#{field} = #{clean_currency(value)}"
|
|
query
|
|
end
|
|
end
|
|
|
|
new "0", :label => :label_none, :where_clause => "%s = 0"
|
|
new "y", :label => :label_yes, :arity => 0, :where_clause => "%s IS NOT NULL"
|
|
new "n", :label => :label_no, :arity => 0, :where_clause => "%s IS NULL"
|
|
|
|
new "<d", :label => :label_less_or_equal, :validate => :dates do
|
|
def modify(query, field, value)
|
|
return query if value.to_s.empty?
|
|
"<".to_operator.modify query, field, quoted_date(value)
|
|
end
|
|
end
|
|
|
|
new ">d", :label => :label_greater_or_equal, :validate => :dates do
|
|
def modify(query, field, value)
|
|
return query if value.to_s.empty?
|
|
">".to_operator.modify query, field, quoted_date(value)
|
|
end
|
|
end
|
|
|
|
new "<>d", :label => :label_between, :validate => :dates do
|
|
def modify(query, field, from, to)
|
|
return query if from.to_s.empty? || to.to_s.empty?
|
|
query.where "#{field} BETWEEN '#{quoted_date from}' AND '#{quoted_date to}'"
|
|
query
|
|
end
|
|
end
|
|
|
|
new "=d", :label => :label_date_on, :validate => :dates do
|
|
def modify(query, field, value)
|
|
return query if value.to_s.empty?
|
|
"=".to_operator.modify query, field, quoted_date(value)
|
|
end
|
|
end
|
|
|
|
end
|
|
#############################################################################################
|
|
|
|
module CoreExt
|
|
::String.send :include, self
|
|
::Symbol.send :include, self
|
|
def to_operator
|
|
Report::Operator.find self
|
|
end
|
|
end
|
|
|
|
def self.new(name, values = {}, &block)
|
|
all[name.to_s] ||= super
|
|
end
|
|
|
|
#TODO: this should be inheritable by subclasses
|
|
def self.all
|
|
@@all_operators ||= {}
|
|
end
|
|
|
|
def self.load
|
|
return if @done
|
|
@done = true
|
|
define_operators
|
|
end
|
|
|
|
def self.find(name)
|
|
all[name.to_s] or raise ArgumentError, "Operator #{name.inspect} not defined"
|
|
end
|
|
|
|
def self.defaults(&block)
|
|
class_eval &block
|
|
end
|
|
|
|
def self.default_operator
|
|
find "="
|
|
end
|
|
|
|
def self.integer_operators
|
|
["<", ">", "<=", ">="].map { |s| s.to_operator}
|
|
end
|
|
|
|
def self.null_operators
|
|
["*", "!*"].map { |s| s.to_operator}
|
|
end
|
|
|
|
def self.string_operators
|
|
["!~", "~"].map { |s| s.to_operator}
|
|
end
|
|
|
|
def self.time_operators
|
|
#["t-", "t+", ">t-", "<t-", ">t+", "<t+"].map { |s| s.to_operator}
|
|
["t", "w", "<>d", ">d", "<d", "=d"].map { |s| s.to_operator}
|
|
end
|
|
|
|
def self.default_operators
|
|
["=", "!"].map { |s| s.to_operator}
|
|
end
|
|
|
|
attr_reader :name
|
|
|
|
def initialize(name, values = {}, &block)
|
|
@name = name.to_s
|
|
validation_methods = values.delete(:validate)
|
|
register_validations(validation_methods) unless validation_methods.nil?
|
|
values.each do |key, value|
|
|
metaclass.class_eval { define_method(key) { value } }
|
|
end
|
|
metaclass.class_eval(&block) if block
|
|
end
|
|
|
|
def to_operator
|
|
self
|
|
end
|
|
|
|
def to_s
|
|
name
|
|
end
|
|
|
|
def arity
|
|
@arity ||= begin
|
|
num = method(:modify).arity
|
|
# modify takes two more arguments before the values
|
|
num < 0 ? num + 2 : num - 2
|
|
end
|
|
end
|
|
|
|
def inspect
|
|
"#<#{self.class.name}:#{name.inspect}>"
|
|
end
|
|
|
|
def <=>(other)
|
|
self.name <=> other.name
|
|
end
|
|
|
|
module DateRange
|
|
def modify(query, field, from, to)
|
|
query.where ["#{field} > '%s'", quoted_date((Date.yesterday + from).to_time.end_of_day)] if from
|
|
query.where ["#{field} <= '%s'", quoted_date((Date.today + to).to_time.end_of_day)] if to
|
|
query
|
|
end
|
|
end
|
|
|
|
# Done with class method definition, let's initialize the operators
|
|
load
|
|
|
|
end |