I'm putting together some spock tests for a Jenkins plugin and along the way I want to check the build variables match one of some valid ones. These are coming from a MatrixProject
but how do I do that?
I have this but it looks a bit clunky but does work
gen_build.getRuns.every(){
it.getBuildVariables().equals([axis1: 'textz', axis2: 'text1']) ||
it.getBuildVariables().equals([axis1: 'textz', axis2: 'text2']) ||
it.getBuildVariables().equals([axis1: 'texty', axis2: 'text2'])
}
Edit this is the spock spec for the Jenkins job-dsl Matrix Job plugin I'd like useful tests for
def 'CombinationFilter'() {
given:
def job = configure( $/
job(type:MatrixJob){
name "generated"
axis{
text("axis1", ["textz", "texty"])
text("axis2", ["text1", "text2"])
}
steps{
shell('return 255')
}
combinationFilter("axis1=='textz' || axis2=='text2'")
sequential(false)
}
/$)
when:
def job_build = job.scheduleBuild2(0).get()
def gen = rule.getInstance().getItem("generated")
def gen_build = gen.scheduleBuild2(0).get()
def gen_runs = gen_build.getRuns()
then:
job_build.logFile.text.contains("SUCCESS")
gen_build.logFile.text.contains("FAILURE")
gen_runs.every(){it.logFile.text.contains("FAILURE")}
gen_runs.every(){it.getBuildVariables().equals([axis1: 'textz', axis2: 'text1']) || it.getBuildVariables().equals([axis1: 'textz', axis2: 'text2']) || it.getBuildVariables().equals([axis1: 'texty', axis2: 'text2'])}
gen_runs.size() == 3
}
Following code should work. If You expect any further simplifications it would be easier to share more code.
gen_build.getRuns.every { it.buildVariables in [[axis1: 'textz', axis2: 'text1'],[axis1: 'textz', axis2: 'text2'],[axis1: 'texty', axis2: 'text2']]}
You can also revert the assertion:
[[axis1: 'textz', axis2: 'text1'],[axis1: 'textz', axis2: 'text2'],[axis1: 'texty', axis2: 'text2']].containsAll(gen_build.getRuns*.buildVariables)
Related
I have this method which works fine but I'm thinking that it may be improved, either for readability and/or efficience.
def default_something
something =
Legal::Something.find_for_A_in_placea('xx', claim.blabla.identifier) ||
Legal::Something.find_for_B_in_placeb('xx', claim.eligible.first.bleble.indivcode) ||
Legal::Something.find_for_B_in_placeb('xx', claim.eligible.last.blublu.indivcode) ||
Legal::Something.find_by(id: DEFAULT_SOMETHING_ID)
{
'name' => something.name,
'day' => something.meta_data[:day],
'hour' => something.meta_data[:hour],
}
end
I can "beautify it" by creating some more methods like:
def default_something
something = def A || def B || (etc)
end
def A
Legal::Something.find_for_A_in_placea('xx', claim.blabla.identifier)
end
def B
Legal::Something.find_for_B_in_placeb('xx', claim.eligible.first.bleble.indivcode) ||
Legal::Something.find_for_B_in_placeb('xx', claim.eligible.last.blublu.indivcode)
end
In addition I should say:
find_for_B part only retrieves a value when claim.eligible.first.bleble.indivcode || claim.eligible.last.blublu.indivcode = 'ASL'
Is the "beautified" version the way to go?
And/or should I add an if statement regarding
Legal::Something.find_for_B_in_placeb('xx', claim.eligible.first.bleble.indivcode) ||
Legal::Something.find_for_B_in_placeb('xx', claim.eligible.last.blublu.indivcode)
to improve efficiency and readability, stating it happens only when "indivcode" = "ASL"?
What else can I do?
I have a if else condition in my ruby code listed below:
if !category.nil?
return false unless company_matches.any? { |w|
comparison = /(\s|^)#{w}(\s|$)/i
(title.index(comparison) || description.index(comparison) || clean_title.index(comparison) || clean_desc.index(comparison)) && (category == 'Business')}
else
return false unless company_matches.any? { |w|
comparison = /(\s|^)#{w}(\s|$)/i
(title.index(comparison) || description.index(comparison) || clean_title.index(comparison) || clean_desc.index(comparison))}
end
How can i simplify this to make it look more subtle?
company_matches.any? do |company|
[title, description, clean_title, clean_desc].any? do |attribute|
attribute.match? /(\s|^)#{company}(\s|$)/i
end && (category == 'Business' || category.nil?)
end
You can create a method to do your comparisons so you don't repeat yourself. You can also abstract the comparison variable out of the if .. else block. Here's what I have:
comparison = /(\s|^)#{w}(\s|$)/i
result = perform_comparison(title, description, clean_title, clean_desc, comparison)
unless category.nil?
return false unless company_matches.any? { |w| result && (category == 'Business')}
else
return false unless company_matches.any? { |w| result }
end
# somewhere else in your code
def perform_comparison(title, description, clean_title, clean_desc, comparison)
title.index(comparison) || description.index(comparison) || clean_title.index(comparison) || clean_desc.index(comparison)
end
Let's say I have a list of records like:
transactions = Transaction.all
And I have the following instance methods #currency, #geo, #industry. I want to select records which has the following criteria:
Select all transactions that has field currency which equals to #currency unless #currency is nil and in this case we'll ignore the condition (currency would mean all currencies when it's nil)
Select all transactions that has field geo which equals to #geo unless #geo is nil.
Select all transactions that has field industry which equals to #industry unless #industry is nil.
I tried multiple #select but with no luck something like:
transactions.select{ |i| (i.currency == #currency) unless #currency.nil? }.
.select{ |i| (i.geo == #geo) unless #geo.nil? }.
.select{ |i| (i.industry == #industry) unless #industry.nil? }
The problem with your example is the unless #currency.nil? will return nil (which is falsey) if #currency is nil, which is the opposite of what you intended.
You should use || instead:
transactions.select{ |i| (i.currency == #currency) || #currency.nil? }.
select{ |i| (i.geo == #geo) || #geo.nil? }.
select{ |i| (i.industry == #industry) || #industry.nil? }
In this case, if #currency is nil, the first condition will return true, and all elements will pass the select box to the next one...
Another option would be to run the select block only is the parameter is not nil. In this case, you'd like to break the line into separate blocks:
transactions.select!{ |i| (i.currency == #currency) } unless #currency.nil?
transactions.select!{ |i| (i.geo == #geo) } unless #geo.nil?
transactions.select!{ |i| (i.industry == #industry) } unless #industry.nil?
transactions.select do |t|
(#currency.nil? || t.currency == #currency) &&
(#geo.nil? || t.geo == #geo) &&
(#industry.nil? || t.industry == #industry)
end
this should do the job.
Or, if you are into dynamics:
[:currency, :geo, :industry].all? do |field|
(ivar = instance_variable_get("##{field}")).nil? || t.send(field) == ivar
end
Use AR/SQL instead of Ruby processing when possible:
transactions.where(currency: #currency, geo: #geo, industry: #industry)
Multiple use of select is superfluous in this situation. You can use && and || logical operators:
transactions.select do |transaction|
(#currency.nil? || transaction.currency == #currency) &&
(#geo.nil? || transaction.geo == #geo) &&
(#industry.nil? || transaction.industry == #industry)
end
I've tried reading some tutorials on refactoring and I am struggling with conditionals. I don't want to use a ternary operator but maybe this should be extracted in a method? Or is there a smart way to use map?
detail.stated = if value[:stated].blank?
nil
elsif value[:stated] == "Incomplete"
nil
elsif value[:is_ratio] == "true"
value[:stated] == "true"
else
apply_currency_increment_for_save(value[:stated])
end
If you move this logic into a method, it can be made a lot cleaner thanks to early return (and keyword arguments):
def stated?(stated:, is_ratio: nil, **)
return if stated.blank? || stated == "Incomplete"
return stated == "true" if is_ratio == "true"
apply_currency_increment_for_save(stated)
end
Then...
detail.stated = stated?(value)
stated = value[:stated]
detail.stated = case
when stated.blank? || stated == "Incomplete"
nil
when value[:is_ratio] == "true"
value[:stated] == "true"
else
apply_currency_increment_for_save stated
end
What's happening: when case is used without an expression, it becomes the civilized equivalent of an if ... elsif ... else ... fi.
You can use its result, too, just like with if...end.
Move the code into apply_currency_increment_for_save
and do:
def apply_currency_increment_for_save(value)
return if value.nil? || value == "Incomplete"
return "true" if value == "true"
# rest of the code. Or move into another function if its too complex
end
The logic is encapsulated and it takes 2 lines only
I like #Jordan's suggestion. However, it seems the call is incomplete -- the 'is_ratio' parameter is also selected from value but not supplied.
Just for the sake of argument I'll suggest that you could go one step further and provide a class that is very narrowly focused on evaluating a "stated" value. This might seem extreme but it fits with the notion of single responsibility (the responsibility is evaluating "value" for stated -- while the 'detail' object might be focused on something else and merely makes use of the evaluation).
It'd look something like this:
class StatedEvaluator
attr_reader :value, :is_ratio
def initialize(value = {})
#value = ActiveSupport::StringInquirer.new(value.fetch(:stated, ''))
#is_ratio = ActiveSupport::StringInquirer.new(value.fetch(:is_ratio, ''))
end
def stated
return nil if value.blank? || value.Incomplete?
return value.true? if is_ratio.true?
apply_currency_increment_for_save(value)
end
end
detail.stated = StatedEvaluator.new(value).stated
Note that this makes use of Rails' StringInquirer class.
Is there a pretty way to make a series of method calls in ruby UNTIL one returns true?
This was my first thought, but was thinking there might be a nicer way:
if method_one
elsif method_two
elsif method_three
else
puts "none worked"
end
You can use Enumerable#any? as well.
[ :m1, :m2, :m3 ].any?{ |method| object.send( method )} || "None Worked"
There are number of Ruby-ish options. One interesting is:
method_one || method_two || method_three || Proc.new { puts "none worked" }.call
or
method_one || method_two || method_three || lambda { puts "none worked" }.call
Try this:
[:m1, :m2, :m3, ...].find{ |m| send(m) } != nil || "none worked"
Returns true if one of the methods returns true otherwise returns none worked.