What is the colon 'operator' doing in this example? - ruby

I saw this example in an answer elsewhere and with the following it outputs foobar:
a = :foo
def bar(b)
:"#{b}bar"
end
c = bar(a)
c

The colon isn't an operator inside bar, it is simply a Symbol literal that uses string interpolation to build the Symbol. Some Symbols need to be quoted to avoid syntax issues, for example:
:'a+b'
You can also use double quotes with this syntax and those quotes behave just like double quotes for strings so they support string interpolation. So this:
:"#{b}bar"
is equivalent to:
"#{b}bar".to_sym
or
(b.to_s + 'bar').to_sym
If you #inspect your value you'll get a better idea of what it contains:
puts c.inspect
# :foobar

Related

Use ARGV[] argument vector to pass a regular expression in Ruby

I am trying to use gsub or sub on a regex passed through terminal to ARGV[].
Query in terminal: $ruby script.rb input.json "\[\{\"src\"\:\"
Input file first 2 lines:
[{
"src":"http://something.com",
"label":"FOO.jpg","name":"FOO",
"srcName":"FOO.jpg"
}]
[{
"src":"http://something123.com",
"label":"FOO123.jpg",
"name":"FOO123",
"srcName":"FOO123.jpg"
}]
script.rb:
dir = File.dirname(ARGV[0])
output = File.new(dir + "/output_" + Time.now.strftime("%H_%M_%S") + ".json", "w")
open(ARGV[0]).each do |x|
x = x.sub(ARGV[1]),'')
output.puts(x) if !x.nil?
end
output.close
This is very basic stuff really, but I am not quite sure on how to do this. I tried:
Regexp.escape with this pattern: [{"src":".
Escaping the characters and not escaping.
Wrapping the pattern between quotes and not wrapping.
Meditate on this:
I wrote a little script containing:
puts ARGV[0].class
puts ARGV[1].class
and saved it to disk, then ran it using:
ruby ~/Desktop/tests/test.rb foo /abc/
which returned:
String
String
The documentation says:
The pattern is typically a Regexp; if given as a String, any regular expression metacharacters it contains will be interpreted literally, e.g. '\d' will match a backlash followed by ā€˜dā€™, instead of a digit.
That means that the regular expression, though it appears to be a regex, it isn't, it's a string because ARGV only can return strings because the command-line can only contain strings.
When we pass a string into sub, Ruby recognizes it's not a regular expression, so it treats it as a literal string. Here's the difference in action:
'foo'.sub('/o/', '') # => "foo"
'foo'.sub(/o/, '') # => "fo"
The first can't find "/o/" in "foo" so nothing changes. It can find /o/ though and returns the result after replacing the two "o".
Another way of looking at it is:
'foo'.match('/o/') # => nil
'foo'.match(/o/) # => #<MatchData "o">
where match finds nothing for the string but can find a hit for /o/.
And all that leads to what's happening in your code. Because sub is being passed a string, it's trying to do a literal match for the regex, and won't be able to find it. You need to change the code to:
sub(Regexp.new(ARGV[1]), '')
but that's not all that has to change. Regexp.new(...) will convert what's passed in into a regular expression, but if you're passing in '/o/' the resulting regular expression will be:
Regexp.new('/o/') # => /\/o\//
which is probably not what you want:
'foo'.match(/\/o\//) # => nil
Instead you want:
Regexp.new('o') # => /o/
'foo'.match(/o/) # => #<MatchData "o">
So, besides changing your code, you'll need to make sure that what you pass in is a valid expression, minus any leading and trailing /.
Based on this answer in the thread Convert a string to regular expression ruby, you should use
x = x.sub(/#{ARGV[1]}/,'')
I tested it with this file (test.rb):
puts "You should not see any number [0123456789].".gsub(/#{ARGV[0]}/,'')
I called the file like so:
ruby test.rb "\d+"
# => You should not see any number [].

What can a ruby symbol (syntax) contain?

I want to create regular expression to match ruby symbols, but I need to know what the exact syntax for a symbol is.
Until now I am aware of the following:
:'string'
:"string"
:__underline
:method
:exclamation!
:question?
:#instance
:$global
It's not entirely clear what you are talking about.
If you are talking about what a Symbol can contain, the answer is: anything and everything, including newlines, arbitrary whitespace, control characters, arbitrarily weird and obscure Unicode characters, and everything else.
If you are talking about the various ways of writing Symbol literals, here's my best understanding:
bare : literal: any valid Ruby identifier (e.g. :foo, :Foo, :#foo, :##foo, :$foo, :$:, ā€¦)
single-quoted : literal: everything that's valid in a single-quoted String literal, including escape sequences such as :'\'' and :'\\'
double-quoted : literal: everything that's valid in a double-quoted String literal, including escape sequences such as :"\"", :"\\", and :"\n", as well as string interpolation, which allows you to inject the results of arbitrary Ruby code into the Symbol, e.g. :"#{if rand < 0.5 then RUBY_VERSION else ENV['HOME'] end}"
single-quoted Array of Symbols literal: everything that's valid in a single-quoted Array of Strings literal, e.g. %i|foo bar baz| (equivalent to [:foo, :bar, :baz]), %i(foo\ bar baz) (equivalent to [:'foo bar', :baz]), %i:foo bar: (equivalent to [:foo, :bar])
double-quoted Array of Symbols literal: everything that's valid in a double-quoted Array of Strings literal, e.g. %I|foo #{bar} baz|, etc.
Symbol hash keys in the key: value syntax: every valid Ruby label, e.g. {foo: 42}
Symbol hash keys in the quoted 'key': value syntax: every valid Ruby String literal, including escape sequences and interpolation, e.g. {"foo\n#{bar}": 42}
There are of course a lot of other expressions that evaluate to Symbols:
method definition expressions: def foo;end # => :foo
String#to_sym (alias String#intern): 'foo bar'.to_sym # => :'foo bar'
really, any method that may return a Symbol
http://www.cse.buffalo.edu/~regan/cse305/RubyBNF.pdf enumerates the context-free grammar productions that define Ruby's syntax. CFGs are inherently more powerful than REs, so you might want to consider a different tool for this job--but you can certainly look at this document and try to construct a regexp that matches all cases.

Triple single quote vs triple double quote in Ruby

Why might you use ''' instead of """, as in Learn Ruby the Hard Way, Chapter 10 Study Drills?
There are no triple quotes in Ruby.
Two String literals which are juxtaposed are parsed as a single String literal. So,
'Hello' 'World'
#=> "HelloWorld"
is the same as
'HelloWorld'
#=> "HelloWorld"
And
'' 'Hello' ''
#=> "Hello"
is the same as
'''Hello'''
#=> "Hello"
is the same as
'Hello'
#=> "Hello"
Since adding an empty string literal does not change the result, you can add as many empty strings as you want:
""""""""""""'''''Hello'''''''''
#=> "Hello"
There are no special rules for triple single quotes vs. triple double quotes, because there are no triple quotes. The rules are simply the same as for quotes.
I assume the author confused Ruby and Python, because a triple-quote will not work in Ruby the way author thought it would. It'll just work like three separate strings ('' '' '').
For multi-line strings one could use:
%q{
your text
goes here
}
=> "\n your text\n goes here\n "
or %Q{} if you need string interpolation inside.
Triple-quotes ''' are the same as single quotes ' in that they don't interpolate any #{} sequences, escape characters (like "\n"), etc.
Triple-double-quotes (ugh) """ are the same as double-quotes " in that they do interpolation and escape sequences.
This is further down on the same page you linked.
The triple-quoted versions """ ''' allows for multi-line strings... as does the singly-quoted ' and ", so I don't know why both are available.
In Ruby """ supports interpolation, ''' does not.
Rubyists use triple quotes for multi-line strings (similar to 'heredocs').
You could just as easily use one of these characters.
Just like normal strings the double quotes will allow you to use variables inside of your strings (also known as 'interpolation').
Save this to a file called multiline_example.rb and run it:
interpolation = "(but this one can use interpolation)"
single = '''
This is a multi-line string.
'''
double = """
This is also a multi-line string #{interpolation}.
"""
puts single
puts double
This is the output:
$ ruby multiline_string_example.rb
This is a multi-line string.
This is also a multi-line string (but this one can use interpolation).
$
Now try it the other way around:
nope = "(this will never get shown)"
single = '''
This is a multi-line string #{nope}.
'''
double = """
This is also a multi-line string.
"""
puts single
puts double
You'll get this output:
$ ruby multiline_example.rb
This is a multi-line string #{nope}.
This is also a multi-line string.
$
Note that in both examples you got some extra newlines in your output. That's because multiline strings keep any newlines inside them, and puts adds a newline to every string.

Ruby single and double quotes

I've recently been coding in Ruby and have come from Python, where single and double quotes made no difference to how the code worked as far as I know.
I moved to Ruby to see how it worked, and to investigate the similarities between Ruby and Python.
I was using single-quoted strings once and noticed this:
hello = 'hello'
x = '#{hello} world!'
puts x
It returned '#{hello} world!' rather than 'hello world!'.
After noticing this I tried double quotes and the problem was fixed. Now I'm not sure why that is.
Do single and double quotes change this or is it because of my editor (Sublime text 3)? I'm also using Ruby version 2.0 if it works differently in previous versions.
In Ruby, double quotes are interpolated, meaning the code in #{} is evaluated as Ruby. Single quotes are treated as literals (meaning the code isn't evaluated).
var = "hello"
"#{var} world" #=> "hello world"
'#{var} world' #=> "#{var} world"
For some extra-special magic, Ruby also offers another way to create strings:
%Q() # behaves like double quotes
%q() # behaves like single quotes
For example:
%Q(#{var} world) #=> "hello world"
%q(#{var} world) #=> "#{var} world"
You should read the Literals section of the official Ruby documentation.
It is very concise, so you need to read carefully. But it explains the difference between double-quoted and single-quoted strings, and how they are equivalent to %Q/.../ and %q/.../ respectively.
If you enclose Ruby string in single qoutes, you can't use interpolation. That's how Ruby works.
Single-quoted strings don't process escape sequence \ and they don't do string interpolation.
For a better understanding, take a look at String concatenation vs. interpolation
To answer your question, you have to use "" when you want to do string interpolation:
name = 'world'
puts "Hello #{name}" # => "Hello world"
Using escape sequence:
puts 'Hello\nworld' # => "Hello\nworld"
puts "Hello\nworld" # => "Hello
world"
Ruby supports single-quoted string, for many uses like as follow:
>> 'foo'
=> "foo"
>> 'foo' + 'bar'
=> "foobar"
In above example, those two types of strings are identical. We can use double quote in place of single quote and we will get same output like above example.
As you face problem, while using interpolation in single quoted string because Ruby do not interpolate into single-quoted string. I am taking one example for more understanding:
>> '#{foo} bar'
=> "\#{foo} bar"
Here you can see that return values using double-quoted strings, which requires backslash to escape special characters such as #.
Single quoted string often useful because they are truly literal.
In the string interpolation concept, the essential difference between using single or double quotes is that double quotes allow for escape sequences while single quotes do not.
Let's take an example:
name = "Mike"
puts "Hello #{name} \n How are you?"
The above ruby code with string interpolation will interpolate the variable called name which is written inside brackets with its original value which is Mike. And it will also print the string How are you? in a separate line since we already placed an escape sequence there.
Output:
Hello Mike
How are you?
If you do the same with single quotes, it will treat the entire string as a text and it will print as it is including the escape sequence as well.
name = Mike'
puts 'Hello #{name} \n How are you'?
Output:
Hello #{name} \n How are you?

Stumped by a simple regex

I am trying to see if the string s contains any of the symbols in a regex. The regex below works fine on rubular.
s = "asd#d"
s =~ /[~!##$%^&*()]+/
But in Ruby 1.9.2, it gives this error message:
syntax error, unexpected ']', expecting tCOLON2 or '[' or '.'
s = "asd#d"; s =~ /[~!##$%^&*()]/
What is wrong?
This is actually a special case of string interpolation with global and instance variables that most seem not to know about. Since string interpolation also occurs within regex in Ruby, I'll illustrate below with strings (since they provide for an easier example):
#foo = "instancefoo"
$foo = "globalfoo"
"##foo" # => "instancefoo"
"#$foo" # => "globalfoo"
Thus you need to escape the # to prevent it from being interpolated:
/[~!#\#$%^&*()]+/
The only way that I know of to create a non-interpolated regex in Ruby is from a string (note single quotes):
Regexp.new('[~!##$%^&*()]+')
I was able to replicate this behavior in 1.9.3p0. Apparently there is a problem with the '#$' combination. If you escape either it works. If you reverse them it works:
s =~ /[~!#$#%^&*()]+/
Edit: in Ruby 1.9 #$ invokes variable interpolation, even when followed by a % which is not a valid variable name.
I disagree, you need to escape the $, its the end of string character.
s =~ /[~!##\$%^&*()]/ => 3
That is correct.

Resources