Can someone explain this ruby code? - ruby

From the rails postgresql_adapter.rb. I get what it's trying to do, I just don't get how it happens. It's really to do with the <<-SQL that I'm lost.
exec_query(<<-SQL, 'SCHEMA', binds).rows.first[0].to_i > 0
SELECT COUNT(*)
FROM pg_tables
WHERE tablename = $1
#{schema ? "AND schemaname = $2" : ''}
SQL
I've seen code before where you could say:
blah = <<-X
some
lines
of
test
X
But I've never seen this done within the argument to a function call. I'm really confused by this. Can someone explain to me what exactly is going on here?

You can use a heredoc-marker (like <<-SQL in your example) anywhere (or even multiple times) in a line and the heredoc will then start on the following line and continue until the end-marker is met (in case of multiple markers, the (n+1)th heredoc will start after the nth end-marker and continue up to the (n+1)th end-marker). The content of each heredoc will then be inserted at the place where the corresponding marker was used.
So
foo(<<BAR, 42)
bar
BAR
is the same as
foo("bar\n", 42)
and
foo(<<BAR, <<BAZ)
bar
BAR
baz
BAZ
is the same as
foo("bar\n", "baz\n")

Related

Does ruby's case statement fall through?

I am writing a hangman game in ruby and I wanted to use a case statement to determine which body part to place corresponding to a number of incorrect guesses. I made this game using a board class I use for other games like chess and connect-4 because I have a method which serializes the board class allowing me to save and load the game without any extra code. For the game to be saved, I needed some way of determining the number of incorrect guesses for the hangman without adding extra variables to the board class. To solve this I used an instance variable on the board class called history, which can be used to push moves from the game to the boards history. When the board gets serialized, the history is saved as well, which can be read by the game and used to determine incorrect guesses.
In the hangman game, I have a method called read history (which I use for all the games since it solves the serialization issue described above). The read_history method is responsible for reading the past guesses, display them, and determine the number of incorrect guesses. This number is then passed to a hang method which determines which body parts of the hangman to add.
def hang(incorrect)
case incorrect
when 0
#hangman = [" ", " ", " "]
break
when 7
#hangman[2][2] = '\\'
when 6
#hangman[2][0] = '/'
when 5
#hangman[2][1] = '*'
when 4
#hangman[1][2] = '\\'
when 3
#hangman[1][0] = '/'
when 2
#hangman[1][1] = '|'
when 1
#hangman[0][1] = 'o'
end
end
If I were writing this in java, and a value of 5 were passed to the above method, it would read the statement until it hit "when 5" or in java terms "case 5:". It would notice that there is not a break in the statement and will move down the list executing the code in "case 4:" and repeating until a break is found. If 0 were passed however it would execute the code, see the break, and would not execute and other statements.
I am wondering if Ruby is capable of using case statements the way java does in the way that they fall through to the next statement. For my particular problem I am aware that I can use a 0.upto(incorrect) loop and run the cases that way, but I would like to know the similarities and differences in the case statement used in ruby as opposed to the switch-case used in java
No, Ruby's case statement does not fall through like Java. Only one section is actually run (or the else). You can, however, list multiple values in a single match, e.g. like this site shows.
print "Enter your grade: "
grade = gets.chomp
case grade
when "A", "B"
puts 'You pretty smart!'
when "C", "D"
puts 'You pretty dumb!!'
else
puts "You can't even use a computer!"
end
It's functionally equivalent to a giant if-else. Code Academy's page on it recommends using commas to offer multiple options. But you can still won't be able to execute more than one branch of logic.
It does not fall through.
Ruby just doesn't have the same behavior as Java for this type of statement.
If you want to simulate the fall through behavior, you can do something like this:
def hang(incorrect)
#hangman = [" ", " ", " "]
#hangman[2][2] = '\\' if incorrect > 6
#hangman[2][0] = '/' if incorrect > 5
#hangman[2][1] = '*' if incorrect > 4
#hangman[1][2] = '\\' if incorrect > 3
#hangman[1][0] = '/' if incorrect > 2
#hangman[1][1] = '|' if incorrect > 1
#hangman[0][1] = 'o' if incorrect > 0
#hangman
end

Error in my ruby code [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I can't get this code to work. Can anyone help me by pointing out the error in this code so that I can understand where I made mistakes?
STDOUT.print 'Do you wish to input another length in meters? '
more = STDIN.getString
more = STDIN.getint( );
more = more.toUpper( )
while(more[1] = 'Y')
STDOUT.puts 'Enter length in meters: '
gets(meter)
f = meter * 3.28084
feet = f.toInt
inches = (12.0 * (feet - f)).to_i
print 'The length is '
if feet = 1
STDIN.print feet + 'foot ';
else
STDOUT.print feet + 'feet '
if inches = 1
STDOUT.print inches + ' inch.\n'
else if (inches < 1)
STDOUT inches + ' inches.\n'
else
STDOUT.print '.\n'
STDOUT.print 'Do you wish to input another length in meters: '
more = STDIN.getint
end
Where do I start?
In Ruby, you need to terminate your blocks with end. You did it for while, but not for if.
Ruby uses elsif; you can't write else if.
Ruby does not have toInt; it's called to_i, as you use in the very next line
gets(meter) is an error; you need to say meter = gets
STDIN does not have getString, it has gets. It also doesn't have getint, you need to write gets.to_i.
toUpper does not exist, use upcase, as in more = more.upcase. You can also use the more readable and more efficient more.upcase!.
In if and while, you have assignment =, where you presumably want to have comparison ==.
more[1] is the second character of more; the first being more[0].
more = ... is being called twice in a row. That means the first value you input will be discarded without effect.
STDIN.print is an obvious mistake for STDOUT.print.
You can use puts "..." instead of print "...\n".
STDIN and STDOUT are redundant when you are using gets, print and others; STDIN.gets is identical to gets, STDOUT.print is identical to print.
STDOUT inches + ' inches.\n' is an obvious mistake, since STDOUT is not a function.
'.\n' contains three characters: a period, a backslash and a letter. The double-quoted ".\n" contains two: a period and a newline.
Ruby does not typically use ;, and it does not usually use empty parentheses for calling 0-parameter functions. These are just stylistic errors, and won't impact runtime.
There may or may not be more.

How to get multiple occurences of a pattern between two definite endpoints

I have a string like:
CREATE TABLE foobar (
bar foo,
foo bar
) DISTRIBUTED BY
I would like to get all column definitions from this string. I tried:
my_string.scan /CREATE TABLE .*\n([^\n]*?)\n.*DISTRIBUTED BY/
But it does not return with the desired values (["bar foo,", "foo bar"]) . Any ideas?
The key point of scan method is each new match begins when the last one ends:
a = "cruel world"
a.scan(/.../) #=> ["cru", "el ", "wor"]
So you need to define your pattern so that it will match both at the beginning and in the middle of the string. Needless to say, that won't be easy to build such a look-behind expression.
But I wonder will this be enough to your specific goals:
s = <<HR
CREATE TABLE foobar (
bar foo,
foo bar
) DISTRIBUTED BY}
HR
ax = s.scan /\s+(.+?)(?:,\n|\n\))/
#=> [["bar foo"], ["foo bar"]]
As you see, I didn't try to match CREATE TABLE here, assuming the string has the query ready.
I think this is what you were trying for:
/CREATE TABLE .*\n((?:.*\n)+).*DISTRIBUTED BY/
(?:.*\n) matches an individual line, so ((?:.*\n)+) captures one or more lines in group #1. The linefeed at the end of the last line (foo bar) is included, but you can delete that at the same time you clean up the commas (e.g. from bar foo,).
If you're thinking about doing anything more complicated, think about using an actual parser; regex do not play well with SQL.
Probably this is the way to go.
my_string.split[1..-2].map(&:strip)

Ruby regex: split string with match beginning with either a newline or the start of the string?

Here's my regular expression that I have for this. I'm in Ruby, which — if I'm not mistaken — uses POSIX regular expressions.
regex = /(?:\n^)(\*[\w+ ?]+\*)\n/
Here's my goal: I want to split a string with a regex that is *delimited by asterisks*, including those asterisks. However: I only want to split by the match if it is prefaced with a newline character (\n), or it's the start of the whole string. This is the string I'm working with.
"*Friday*\nDo not *break here*\n*But break here*\nBut again, not this"
My regular expression is not splitting properly at the *Friday* match, but it is splitting at the *But break here* match (it's also throwing in a here split). My issue is somewhere in the first group, I think: (?:\n^) — I know it's wrong, and I'm not entirely sure of the correct way to write it. Can someone shed some light? Here's my complete code.
regex = /(?:\n^)(\*[\w+ ?]+\*)\n/
str = "*Friday*\nDo not *break here*\n*But break here*\nBut again, not this"
str.split(regex)
Which results in this:
>>> ["*Friday*\nDo not *break here*", "*But break here*", "But again, not this"]
I want it to be this:
>>> ["*Friday*", "Do not *break here*", "*But break here*", "But again, not this"]
Edit #1: I've updated my regex and result. (2011/10/18 16:26 CST)
Edit #2: I've updated both again. (16:32 CST)
What if you just add a '\n' to the front of each string. That simplifies the processing quite a bit:
regex = /(?:\n)(\*[\w+ ?]+\*)\n/
str = "*Friday*\nDo not *break here*\n*But break here*\nBut again, not this"
res = ("\n"+str).split(regex)
res.shift if res[0] == ""
res
=> [ "*Friday*", "Do not *break here*",
"*But break here*", "But again, not this"]
We have to watch for the initial extra match but it's not too bad. I suspect someone can shorten this a bit.
Groups 1 & 2 of the regex below :
(?:\A|\\n)(\*.*?\*)|(?:\A|\\n)(.*?)(?=\\n|\Z)
Will give you your desired output. I am no ruby expert so you will have to create the list yourself :)
Why not just split at newlines? From your example, it looks that's what you're really trying to do.
str.split("\n")

How to write a Ruby switch statement (case...when) with regex and backreferences?

I know that I can write a Ruby case statement to check a match against a regular expressions.
However, I'd like to use the match data in my return statement. Something like this semi-pseudocode:
foo = "10/10/2011"
case foo
when /^([0-9][0-9])/
print "the month is #{match[1]}"
else
print "something else"
end
How can I achieve that?
Thanks!
Just a note: I understand that I wouldn't ever use a switch statement for a simple case as above, but that is only one example. In reality, what I am trying to achieve is the matching of many potential regular expressions for a date that can be written in various ways, and then parsing it with Ruby's Date class accordingly.
The references to the latest regex matching groups are always stored in pseudo variables $1 to $9:
case foo
when /^([0-9][0-9])/
print "the month is #{$1}"
else
print "something else"
end
You can also use the $LAST_MATCH_INFO pseudo variable to get at the whole MatchData object. This can be useful when using named captures:
case foo
when /^(?<number>[0-9][0-9])/
print "the month is #{$LAST_MATCH_INFO['number']}"
else
print "something else"
end
Here's an alternative approach that gets you the same result but doesn't use a switch. If you put your regular expressions in an array, you could do something like this:
res = [ /pat1/, /pat2/, ... ]
m = nil
res.find { |re| m = foo.match(re) }
# Do what you will with `m` now.
Declaring m outside the block allows it to still be available after find is done with the block and find will stop as soon as the block returns a true value so you get the same shortcutting behavior that a switch gives you. This gives you the full MatchData if you need it (perhaps you want to use named capture groups in your regexes) and nicely separates your regexes from your search logic (which may or may not yield clearer code), you could even load your regexes from a config file or choose which set of them you wanted at run time.

Resources