From b81528a3efe986c00598ca04032a1090bc25f708 Mon Sep 17 00:00:00 2001 From: Tim Wade Date: Fri, 5 Aug 2016 15:17:17 -0700 Subject: [PATCH 1/4] Restrict datetime handling to datetime operators --- app/models/miq_expression.rb | 43 +++++++++---------- spec/models/miq_expression_spec.rb | 69 +++--------------------------- 2 files changed, 26 insertions(+), 86 deletions(-) diff --git a/app/models/miq_expression.rb b/app/models/miq_expression.rb index e1ddb332876..354385b09dd 100644 --- a/app/models/miq_expression.rb +++ b/app/models/miq_expression.rb @@ -439,31 +439,28 @@ def self._to_ruby(exp, context_type, tz) operator = exp.keys.first case operator.downcase - when "equal", "=", "<", ">", ">=", "<=", "!=", "before", "after" + when "equal", "=", "<", ">", ">=", "<=", "!=" col_type = get_col_type(exp[operator]["field"]) if exp[operator]["field"] - if col_type == :date || col_type == :datetime - col_name = exp[operator]["field"] - col_ruby, _ = operands2rubyvalue(operator, {"field" => col_name}, context_type) - - normalized_operator = normalize_ruby_operator(operator) - mode = case normalized_operator - when ">", "<=" then "end" # (> 23::59:59), (<= 23::59:59) - when "<", ">=" then "beginning" # (< 00::00:00), (>= 00::00:00) + operands = operands2rubyvalue(operator, exp[operator], context_type) + clause = operands.join(" #{normalize_ruby_operator(operator)} ") + when "before", "after" + col_type = get_col_type(exp[operator]["field"]) if exp[operator]["field"] + col_name = exp[operator]["field"] + col_ruby, _ = operands2rubyvalue(operator, {"field" => col_name}, context_type) + + normalized_operator = normalize_ruby_operator(operator) + mode = case normalized_operator + when ">", "<=" then "end" # (> 23::59:59), (<= 23::59:59) + when "<", ">=" then "beginning" # (< 00::00:00), (>= 00::00:00) + end + + clause = if col_type == :date + val = RelativeDatetime.normalize(exp[operator]["value"], tz, mode) + "val=#{col_ruby}; !val.nil? && val.to_date #{normalized_operator} #{quote(val.to_date, :date)}" + else + val = RelativeDatetime.normalize(exp[operator]["value"], tz, mode) + "val=#{col_ruby}; !val.nil? && val.to_time #{normalized_operator} #{quote(val.utc, :datetime)}" end - - if col_type == :date - val = RelativeDatetime.normalize(exp[operator]["value"], tz, mode) - - clause = "val=#{col_ruby}; !val.nil? && val.to_date #{normalized_operator} #{quote(val.to_date, :date)}" - else - val = RelativeDatetime.normalize(exp[operator]["value"], tz, mode) - - clause = "val=#{col_ruby}; !val.nil? && val.to_time #{normalized_operator} #{quote(val.utc, :datetime)}" - end - else - operands = operands2rubyvalue(operator, exp[operator], context_type) - clause = operands.join(" #{normalize_ruby_operator(operator)} ") - end when "includes all" operands = operands2rubyvalue(operator, exp[operator], context_type) clause = "(#{operands[0]} & #{operands[1]}) == #{operands[1]}" diff --git a/spec/models/miq_expression_spec.rb b/spec/models/miq_expression_spec.rb index f3cf24a6ed3..843b5ee02d4 100644 --- a/spec/models/miq_expression_spec.rb +++ b/spec/models/miq_expression_spec.rb @@ -957,41 +957,16 @@ expect(exp.to_ruby).to eq("val=/virtual/retires_on; !val.nil? && val.to_date > '2011-01-10'.to_date") end - it "generates the ruby for a > expression date value" do - exp = MiqExpression.new(">" => {"field" => "Vm-retires_on", "value" => "2011-01-10"}) - expect(exp.to_ruby).to eq("val=/virtual/retires_on; !val.nil? && val.to_date > '2011-01-10'.to_date") - end - it "generates the ruby for a BEFORE expression with date value" do exp = MiqExpression.new("BEFORE" => {"field" => "Vm-retires_on", "value" => "2011-01-10"}) expect(exp.to_ruby).to eq("val=/virtual/retires_on; !val.nil? && val.to_date < '2011-01-10'.to_date") end - it "generates the ruby for a < expression with date value" do - exp = MiqExpression.new("<" => {"field" => "Vm-retires_on", "value" => "2011-01-10"}) - expect(exp.to_ruby).to eq("val=/virtual/retires_on; !val.nil? && val.to_date < '2011-01-10'.to_date") - end - - it "generates the ruby for a >= expression with date value" do - exp = MiqExpression.new(">=" => {"field" => "Vm-retires_on", "value" => "2011-01-10"}) - expect(exp.to_ruby).to eq("val=/virtual/retires_on; !val.nil? && val.to_date >= '2011-01-10'.to_date") - end - - it "generates the ruby for a <= expression with date value" do - exp = MiqExpression.new("<=" => {"field" => "Vm-retires_on", "value" => "2011-01-10"}) - expect(exp.to_ruby).to eq("val=/virtual/retires_on; !val.nil? && val.to_date <= '2011-01-10'.to_date") - end - it "generates the ruby for a AFTER expression with datetime value" do exp = MiqExpression.new("AFTER" => {"field" => "Vm-last_scan_on", "value" => "2011-01-10 9:00"}) expect(exp.to_ruby).to eq("val=/virtual/last_scan_on; !val.nil? && val.to_time > '2011-01-10T09:00:00Z'.to_time(:utc)") end - it "generates the ruby for a > expression with datetime value" do - exp = MiqExpression.new(">" => {"field" => "Vm-last_scan_on", "value" => "2011-01-10 9:00"}) - expect(exp.to_ruby).to eq("val=/virtual/last_scan_on; !val.nil? && val.to_time > '2011-01-10T09:00:00Z'.to_time(:utc)") - end - it "generates the ruby for a IS expression with date value" do exp = MiqExpression.new("IS" => {"field" => "Vm-retires_on", "value" => "2011-01-10"}) expect(exp.to_ruby).to eq("val=/virtual/retires_on; !val.nil? && val.to_date == '2011-01-10'.to_date") @@ -1031,38 +1006,18 @@ expect(exp.to_ruby(tz)).to eq("val=/virtual/retires_on; !val.nil? && val.to_date > '2011-01-10'.to_date") end - it "generates the ruby for a > expression with date value" do - exp = MiqExpression.new(">" => {"field" => "Vm-retires_on", "value" => "2011-01-10"}) - expect(exp.to_ruby(tz)).to eq("val=/virtual/retires_on; !val.nil? && val.to_date > '2011-01-10'.to_date") - end - it "generates the ruby for a BEFORE expression with date value" do exp = MiqExpression.new("BEFORE" => {"field" => "Vm-retires_on", "value" => "2011-01-10"}) expect(exp.to_ruby(tz)).to eq("val=/virtual/retires_on; !val.nil? && val.to_date < '2011-01-10'.to_date") end - it "generates the ruby for a < expression with date value" do - exp = MiqExpression.new("<" => {"field" => "Vm-retires_on", "value" => "2011-01-10"}) - expect(exp.to_ruby(tz)).to eq("val=/virtual/retires_on; !val.nil? && val.to_date < '2011-01-10'.to_date") - end - - it "generates the ruby for a >= expression with date value" do - exp = MiqExpression.new(">=" => {"field" => "Vm-retires_on", "value" => "2011-01-10"}) - expect(exp.to_ruby(tz)).to eq("val=/virtual/retires_on; !val.nil? && val.to_date >= '2011-01-10'.to_date") - end - - it "generates the ruby for a <= expression with date value" do - exp = MiqExpression.new("<=" => {"field" => "Vm-retires_on", "value" => "2011-01-10"}) - expect(exp.to_ruby(tz)).to eq("val=/virtual/retires_on; !val.nil? && val.to_date <= '2011-01-10'.to_date") - end - it "generates the ruby for a AFTER expression with datetime value" do exp = MiqExpression.new("AFTER" => {"field" => "Vm-last_scan_on", "value" => "2011-01-10 9:00"}) expect(exp.to_ruby(tz)).to eq("val=/virtual/last_scan_on; !val.nil? && val.to_time > '2011-01-10T14:00:00Z'.to_time(:utc)") end - it "generates the ruby for a > expression with datetime value" do - exp = MiqExpression.new(">" => {"field" => "Vm-last_scan_on", "value" => "2011-01-10 9:00"}) + it "generates the ruby for a AFTER expression with datetime value" do + exp = MiqExpression.new("AFTER" => {"field" => "Vm-last_scan_on", "value" => "2011-01-10 9:00"}) expect(exp.to_ruby(tz)).to eq("val=/virtual/last_scan_on; !val.nil? && val.to_time > '2011-01-10T14:00:00Z'.to_time(:utc)") end @@ -1092,30 +1047,18 @@ around { |example| Timecop.freeze("2011-01-11 17:30 UTC") { example.run } } context "given a non-UTC timezone" do - it "generates the SQL for a > expression with a value of 'Yesterday' for a date field" do - exp = described_class.new(">" => {"field" => "Vm-retires_on", "value" => "Yesterday"}) + it "generates the SQL for a AFTER expression with a value of 'Yesterday' for a date field" do + exp = described_class.new("AFTER" => {"field" => "Vm-retires_on", "value" => "Yesterday"}) ruby, * = exp.to_ruby("Asia/Jakarta") expect(ruby).to eq("val=/virtual/retires_on; !val.nil? && val.to_date > '2011-01-11'.to_date") end - it "generates the RUBY for a >= expression with a value of 'Yesterday' for a date field" do - exp = described_class.new(">=" => {"field" => "Vm-retires_on", "value" => "Yesterday"}) - ruby, * = exp.to_ruby("Asia/Jakarta") - expect(ruby).to eq("val=/virtual/retires_on; !val.nil? && val.to_date >= '2011-01-11'.to_date") - end - - it "generates the RUBY for a < expression with a value of 'Yesterday' for a date field" do - exp = described_class.new("<" => {"field" => "Vm-retires_on", "value" => "Yesterday"}) + it "generates the RUBY for a BEFORE expression with a value of 'Yesterday' for a date field" do + exp = described_class.new("BEFORE" => {"field" => "Vm-retires_on", "value" => "Yesterday"}) ruby, * = exp.to_ruby("Asia/Jakarta") expect(ruby).to eq("val=/virtual/retires_on; !val.nil? && val.to_date < '2011-01-11'.to_date") end - it "generates the RUBY for a <= expression with a value of 'Yesterday' for a date field" do - exp = described_class.new("<=" => {"field" => "Vm-retires_on", "value" => "Yesterday"}) - ruby, * = exp.to_ruby("Asia/Jakarta") - expect(ruby).to eq("val=/virtual/retires_on; !val.nil? && val.to_date <= '2011-01-11'.to_date") - end - it "generates the RUBY for an IS expression with a value of 'Yesterday' for a date field" do exp = described_class.new("IS" => {"field" => "Vm-retires_on", "value" => "Yesterday"}) ruby, * = exp.to_ruby("Asia/Jakarta") From 97ac5000f60d836fbd7416059e6f215ddb361bad Mon Sep 17 00:00:00 2001 From: Tim Wade Date: Fri, 5 Aug 2016 15:21:36 -0700 Subject: [PATCH 2/4] Separate before/after paths in MiqExpression#to_ruby --- app/models/miq_expression.rb | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/app/models/miq_expression.rb b/app/models/miq_expression.rb index 354385b09dd..669ef1f1c21 100644 --- a/app/models/miq_expression.rb +++ b/app/models/miq_expression.rb @@ -443,23 +443,29 @@ def self._to_ruby(exp, context_type, tz) col_type = get_col_type(exp[operator]["field"]) if exp[operator]["field"] operands = operands2rubyvalue(operator, exp[operator], context_type) clause = operands.join(" #{normalize_ruby_operator(operator)} ") - when "before", "after" + when "before" col_type = get_col_type(exp[operator]["field"]) if exp[operator]["field"] col_name = exp[operator]["field"] col_ruby, _ = operands2rubyvalue(operator, {"field" => col_name}, context_type) - normalized_operator = normalize_ruby_operator(operator) - mode = case normalized_operator - when ">", "<=" then "end" # (> 23::59:59), (<= 23::59:59) - when "<", ">=" then "beginning" # (< 00::00:00), (>= 00::00:00) - end + clause = if col_type == :date + val = RelativeDatetime.normalize(exp[operator]["value"], tz, "beginning") + "val=#{col_ruby}; !val.nil? && val.to_date < #{quote(val.to_date, :date)}" + else + val = RelativeDatetime.normalize(exp[operator]["value"], tz, "beginning") + "val=#{col_ruby}; !val.nil? && val.to_time < #{quote(val.utc, :datetime)}" + end + when "after" + col_type = get_col_type(exp[operator]["field"]) if exp[operator]["field"] + col_name = exp[operator]["field"] + col_ruby, _ = operands2rubyvalue(operator, {"field" => col_name}, context_type) clause = if col_type == :date - val = RelativeDatetime.normalize(exp[operator]["value"], tz, mode) - "val=#{col_ruby}; !val.nil? && val.to_date #{normalized_operator} #{quote(val.to_date, :date)}" + val = RelativeDatetime.normalize(exp[operator]["value"], tz, "end") + "val=#{col_ruby}; !val.nil? && val.to_date > #{quote(val.to_date, :date)}" else - val = RelativeDatetime.normalize(exp[operator]["value"], tz, mode) - "val=#{col_ruby}; !val.nil? && val.to_time #{normalized_operator} #{quote(val.utc, :datetime)}" + val = RelativeDatetime.normalize(exp[operator]["value"], tz, "end") + "val=#{col_ruby}; !val.nil? && val.to_time > #{quote(val.utc, :datetime)}" end when "includes all" operands = operands2rubyvalue(operator, exp[operator], context_type) From 387725ab6dcf78c7402dac4e93dd211946aedb5a Mon Sep 17 00:00:00 2001 From: Tim Wade Date: Fri, 5 Aug 2016 15:24:11 -0700 Subject: [PATCH 3/4] DRY up before/after in MiqExpression#to_ruby --- app/models/miq_expression.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/models/miq_expression.rb b/app/models/miq_expression.rb index 669ef1f1c21..33c95b79a1f 100644 --- a/app/models/miq_expression.rb +++ b/app/models/miq_expression.rb @@ -447,24 +447,20 @@ def self._to_ruby(exp, context_type, tz) col_type = get_col_type(exp[operator]["field"]) if exp[operator]["field"] col_name = exp[operator]["field"] col_ruby, _ = operands2rubyvalue(operator, {"field" => col_name}, context_type) - + val = RelativeDatetime.normalize(exp[operator]["value"], tz, "beginning") clause = if col_type == :date - val = RelativeDatetime.normalize(exp[operator]["value"], tz, "beginning") "val=#{col_ruby}; !val.nil? && val.to_date < #{quote(val.to_date, :date)}" else - val = RelativeDatetime.normalize(exp[operator]["value"], tz, "beginning") "val=#{col_ruby}; !val.nil? && val.to_time < #{quote(val.utc, :datetime)}" end when "after" col_type = get_col_type(exp[operator]["field"]) if exp[operator]["field"] col_name = exp[operator]["field"] col_ruby, _ = operands2rubyvalue(operator, {"field" => col_name}, context_type) - + val = RelativeDatetime.normalize(exp[operator]["value"], tz, "end") clause = if col_type == :date - val = RelativeDatetime.normalize(exp[operator]["value"], tz, "end") "val=#{col_ruby}; !val.nil? && val.to_date > #{quote(val.to_date, :date)}" else - val = RelativeDatetime.normalize(exp[operator]["value"], tz, "end") "val=#{col_ruby}; !val.nil? && val.to_time > #{quote(val.utc, :datetime)}" end when "includes all" From 396c2ed0fdba6ffd4e418750a5ed6518996369e7 Mon Sep 17 00:00:00 2001 From: Tim Wade Date: Fri, 5 Aug 2016 15:44:01 -0700 Subject: [PATCH 4/4] Rubocop: fix TrailingUnderscoreVariable offense --- app/models/miq_expression.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/miq_expression.rb b/app/models/miq_expression.rb index 33c95b79a1f..ab9f42d8359 100644 --- a/app/models/miq_expression.rb +++ b/app/models/miq_expression.rb @@ -446,7 +446,7 @@ def self._to_ruby(exp, context_type, tz) when "before" col_type = get_col_type(exp[operator]["field"]) if exp[operator]["field"] col_name = exp[operator]["field"] - col_ruby, _ = operands2rubyvalue(operator, {"field" => col_name}, context_type) + col_ruby, = operands2rubyvalue(operator, {"field" => col_name}, context_type) val = RelativeDatetime.normalize(exp[operator]["value"], tz, "beginning") clause = if col_type == :date "val=#{col_ruby}; !val.nil? && val.to_date < #{quote(val.to_date, :date)}" @@ -456,7 +456,7 @@ def self._to_ruby(exp, context_type, tz) when "after" col_type = get_col_type(exp[operator]["field"]) if exp[operator]["field"] col_name = exp[operator]["field"] - col_ruby, _ = operands2rubyvalue(operator, {"field" => col_name}, context_type) + col_ruby, = operands2rubyvalue(operator, {"field" => col_name}, context_type) val = RelativeDatetime.normalize(exp[operator]["value"], tz, "end") clause = if col_type == :date "val=#{col_ruby}; !val.nil? && val.to_date > #{quote(val.to_date, :date)}"