Difference in a one line block code between === and match? - ruby

I have the following code example to check whether some elements in an array match or not the given regular expression:
["SELECT column1, column2 FROM table1, table2 WHERE column2='value';",
"SELECT * FROM Customers WHERE Last_Name='Smith';",
"SELECT * FROM Friends"].none? { |sql| /WHERE/i.match?(sql) }
# false
As you can see, I'm using match? just to see if both the receiver and the method parameter match. I'm not interested on the pros of using match as it's stated on the docs:
Returns a true or false indicates whether the regexp is matched or not without updating $~ and other related variables. If the second parameter is present, it specifies the position in the string to begin the search.
So, I could easily use === for that:
["SELECT column1, column2 FROM table1, table2 WHERE column2='value';",
"SELECT * FROM Customers WHERE Last_Name='Smith';",
"SELECT * FROM Friends"].none? { |sql| /WHERE/ === sql }
# false
Which returns the very same. Maybe isn't so common to use case equality for this kind of things, but that allows me to pass the regular expression as the none? parameter, and avoid opening the block, as I'm using Ruby 2.5+:
array.none?(/WHERE/) # false
And works pretty much the same (also the same for every enumerable predicate method):
array.any? { |sql| /WHERE/i.match?(sql) } # true
array.any? { |sql| /WHERE/ === sql } # true
array.any?(/WHERE/) # true
So, my question is; would it be the same for me, considering these cases to use === to check every string instance in the arrays match with the given regular expression? If so, I could replace them all to just pass the regular expression as the method (none?, any?) parameter.

The reason for Regexp#=== existing is primarily for use in case statements, like:
case (str)
when /WHERE/i
# ...
end
As internally that calls the === to check for matches. This is why you can do a lot of really wild things like:
case (str)
when String
when 0..20
when 'example'
when /text/
end
Among other things, all by virtue of the === comparison.
It's worth noting it's not intended to be used as you have. match? communicates quite clearly your intent, but === may be confusing as it looks a lot like == which indicates "comparison", but this is not any ordinary comparison.
The good news is Ruby allows using a regular expression directly with any? as well as an alternative like:
[
"SELECT column1, column2 FROM table1, table2 WHERE column2='value';",
"SELECT * FROM Customers WHERE Last_Name='Smith';",
"SELECT * FROM Friends"
].grep(/WHERE/i).any?
Where the grep method can take either a string or a regular expression.
The grep method is great for filtering, but if you just want to know if any of them match it's not as efficient as any?, so I'd use that instead.

Related

Ruby if statement to exclude multiple string variations

I'm trying to parse an array that I've created to ultimately write the 'good' values to a file. The array may look something like this, however the contents may change, so I can't match for a certain value:
array = ["10.10.10.0/24", "10.10.10.1/32", "10.10.10.129/32", "127.0.0.0/8", "169.254.0.0/16", "192.168.1.0/24", "255.255.255.255/32"]
I believe that it makes sense to check the array values before writing to the file and not write the values I know that I don't want. In this case, the values would always be:
10.10.10.1/32
10.10.10.129/32
127.0.0.0/8
169.254.0.0/16
255.255.255.255/32
My initial if statement looked like this, which sort of accomplished what I am after, but not completely:
if !network.include?("/32" || "127.0.0.0/8" || "169.254.0.0/16" || "255.255.255.255/32")
file.write("#{network}\n")
end
Which results in (lines 2 & 3 shouldn't have been included):
10.10.10.0/24
127.0.0.0/8
169.254.0.0/16
192.168.1.0/24
What have I done wrong? Is there a better way to perform the lookup/matching/exclusion?
networks = ["10.10.10.0/24", "10.10.10.1/32", "10.10.10.129/32", "127.0.0.0/8", "169.254.0.0/16", "192.168.1.0/24", "255.255.255.255/32"]
banned_networks = [/\/32/, "127.0.0.0/8", "169.254.0.0/16", "255.255.255.255/32"]
networks.reject do |e|
case e
when *banned_networks
true
end
end.each {|network| file.write("#{network}\n")}
You can't use "or" || like that.
Better might be...
exclude_entries = [ '/32',
'127.0.0.0/8',
'169.254.0.0/16',
'255.255.255.255/32'
]
match_pattern = Regex.new(exclude_entries.join('|'))
(array.reject{|n| n =~ match_pattern}.each do |network|
file.write("#{network}\n")
end
The problem is that the expression "/32" || "127.0.0.0/8" always returns "/32" ... the "or" just returns the first "truthy" value and "/32" is "truthy"
Edited to use regular expression so as to exclude partial text.

Ruby Regexp match matching repetitive values

I'm facing some issues with Ruby Regexp match.
I have the following query and I'd like to scan my parameters:
EXEC sp_executesql N'exec dbo.MyProcedure #UserID=#p0,#Products=#p1,#CountryCodes=#p2'
, N'#p0 int,#p1 nvarchar(max) ,#p2 nvarchar(max)'
, #p0 = 123569
, #p1 = N'1633,1634'
, #p2 = N'39A,CS,DE,ES,FR,GB,IT,NL,AB,BS,BU,CR,ET,FI,HU,LA,LT,MD,ME,MV,PL,RO,RS,SK,SV,GR,PT,TR,AT,CH,LI,GG,KS,UK,GI,MN,RR,CY,MT,BL,RU,DK,NO,SE,BE,IC,IE,LX'
I've just formatted query to look neat and readable. In my source there are fewer tabs and whitespaces.
Ideally, I'd like to get three matches:
#p0 = 123569
#p1 = N'1633,1634'
#p2 = N'39A,CS,DE,ES,FR,GB,IT,NL,AB,BS,BU,CR,ET,FI,HU,LA,LT,MD,ME,MV,PL,RO,RS,SK,SV,GR,PT,TR,AT,CH,LI,GG,KS,UK,GI,MN,RR,CY,MT,BL,RU,DK,NO,SE,BE,IC,IE,LX'
However, my Regexp pattern merges #p1 and #p2 and this is what I get:
#p0 = 123569
#p1 = N'1633,1634',#p2 = N'39A,CS,DE,ES,FR,GB,IT,NL,AB,BS,BU,CR,ET,FI,HU,LA,LT,MD,ME,MV,PL,RO,RS,SK,SV,GR,PT,TR,AT,CH,LI,GG,KS,UK,GI,MN,RR,CY,MT,BL,RU,DK,NO,SE,BE,IC,IE,LX'
I can see where's the issue, however I'm quite new to Regexp and I cannot figure out how to write it properly. This is my expression:
(\#p[0-9]+)+\=(\N\'.*\'|[0-9]+|NULL)
I'm testing my Regex expression here: http://rubular.com/r/OF5EVD5Nau
You main problem is the .* part in the second alternation, by default regex are greedy and match as much as possible.
You can turn it into lazy or ungreedy by adding a ? after the repetition operator.
So with little change this would do:
(#p[0-9]+)\s+=\s+(\d+|N'.+?'$|NULL)
Rubular example
There's some unknown from you description on the spaces, I used + as repetition operator assuming there will be at least 1 present around the = sign, as in the single quotes, I assume they are never empty. Replace by * if they are optionnal.

Regex to capture string into ruby method params

I Looking for an Regex to capture this examples of strings:
first_paramenter, first_hash_key: 'class1 class2', second_hash_key: true
first_argument, single_hash_key: 'class1 class2'
first_argument_without_second_argument
The pattern rules are:
The string must start some word (the first parameter) /^(\w+)/
The second parameter is optional
If second parameter provided, must have one comma after fisrt parameter
The second argument is an hash, with keys and values. Values can be true, false or an string enclosed by quotes
The hash keys must start with letter
I'm using this regex, but it matches with the only second example:
^(\w+),(\s[a-z]{1}[a-z_]+:\s'?[\w\s]+'?,?)$
I'd go with something like:
^(\w+)(?:, ([a-z]\w+): ('[^']*')(?:, ([a-z]\w+): (\w+))?)?
Here's a Rubular example of it.
(?:...) create non-capturing groups which we can easily test for existence using ?. That makes it easy to test for optional chunks.
([a-z]\w+) is an easy way to say "it must start with a letter" while allowing normal alpha, digits and "_".
As far as testing for "Values can be true, false or an string enclosed by quotes", I'd do that in code after capturing. It's way too easy to create a complex pattern, and then be unable to maintain it later. It's better to use simple ones, then look to see whether you got what you expected, than to try to enforce it inside the regex.
in the third example, your regex return 5 matches. It would be better if return only one. It's possible?
I'm not sure what you're asking. This will return a single capture for each, but why you'd want that makes no sense to me if you're capturing parameters to send to a method:
/^(\w+(?:, [a-z]\w+: '[^']*'(?:, [a-z]\w+: \w+)?)?)/
http://rubular.com/r/GLVuSOieI6
There is frequently a choice to be made between attacking an entire string with a single regex or breaking the string up with one or more String methods, and then going after each piece separately. The latter approach often makes debugging and testing easier, and may also make the code intelligible to mere mortals. It's always a judgement call, of course, but I think this problem lends itself well to the divide and conquer approach. This is how I'd do it.
Code
def match?(str)
a = str.split(',')
return false unless a.shift.strip =~ /^\w+$/
a.each do |s|
return false unless ((key_val = s.split(':')).size == 2) &&
key_val.first.strip =~ /^[a-z]\w*$/ &&
key_val.last.strip =~ /^(\'.*?\'|true|false)$/
end
true
end
Examples
match?("first_paramenter, first_hash_key: 'class1 class2',
second_hash_key: true")
#=>true
match?("first_argument, single_hash_key: 'class1 class2'")
#=>true
match?("first_argument_without_second_argument")
#=>true
match?("first_parameter, first_hash_key: 7")
#=>false
match?("dogs and cats, first_hash_key: 'class1 class2'")
#=>false
match?("first_paramenter, first_hash_key: 'class1 class2',
second_hash_key: :true")
#=>false
You've got the basic idea, you have a bunch of small mistakes in there
/^(\w+)(,\s[a-z][a-z_]+:\s('[^']*'|true|false))*$/
explained:
/^(\w+) # starts with a word
(
,\s # the comma goes _inside_ the parens since its optional
[a-z][a-z_]+:\s # {1} is completely redundant
( # use | in a capture group to allow different possible keys
'[^']*' | # note that '? doesn't make sure that the quotes always match
true |
false
)
)*$/x # can have 0 or more hash keys after the first word

ruby regex not working to remove class name from sql

I have:
BEFORE Gsub sql ::::
SELECT record_type.* FROM record_type WHERE (name = 'Registrars')
sql = sql.gsub(/SELECT\s+[^\(][A-Z]+\./mi,"SELECT ")
AFTER GSUB SQL ::::
SELECT record_type.* FROM record_type WHERE (name = 'Registrars')
The desired result is to remove the "record_type." from the statement:
So it should be :
SELECT * FROM record_type WHERE (name = 'Registrars')
After the regex is run.
I didn't write this, it's in the asf-soap-adaptor gem. Can someone tell me why it doesn't work, and how to fix?
I suppose it should be written like this...
sql = sql.gsub(/SELECT\s+[^\(][A-Z_]+\./mi,"SELECT ")
... as the code in the question won't match if the field name contains _ (underscore) symbol. I suppose that's why this code is in gem: it can work in some conditions (i.e., with underscoreless field names).
Still, I admit I don't understand why exactly this replacement should be done - and shouldn't it include 0-9 check as well (as, for example, 'record_id1' field still won't be matched - and replaced - by the character class in the regular expression; you may have to either expand it, like [0-9A-Z_], or just replace completely with \w).
so your before and after gsubs are the same? I can't tell you why it doesn't work if you dont tell me your expected result. Also for help with interpreting ruby regular expressions check out rubular.com

Whats the XPath equivalent to SQL In query?

I would like to know whats the XPath equivalent to SQL In query. Basically in sql i can do this:
select * from tbl1 where Id in (1,2,3,4)
so i want something similar in XPath/Xsl:
i.e.
//*[#id= IN('51417','1121','111')]
Please advice
(In XPath 2,) the = operator always works like in.
I.e. you can use
//*[#id = ('51417','1121','111')]
A solution is to write out the options as separate conditions:
//*[(#id = '51417') or (#id = '1121') or (#id = '111')]
Another, slightly less verbose solution that looks a bit like a hack, though, would be to use the contains function:
//*[contains('-51417-1121-111-', concat('-', #id, '-'))]
Literally, this means you're checking whether the value of the id attribute (preceeded and succeeded by a delimiter character) is a substring of -51417-1121-111-. Note that I am using a hyphen (-) as a delimiter of the allowable values; you can replace that with any character that will not appear in the id attribute.

Resources