What is the purpose or here documents in ruby? - ruby

I have read about here documents on the book "The Ruby Programming Language" and didn't understand what is the purpose of here documents and when will you use it on production code. I would be happy if someone can explain and give some examples of usage.
regards,

In any language that supports them, a heredoc is a convenient way to make a large string literal.
Take the following contrived Ruby script that takes your name and outputs source code for a C program that tells you hello:
#!/usr/bin/env ruby
name = $*[0]
unless name
$stderr.puts "Please supply a name as the first argument to the program"
exit 1
end
source = <<EOF
#include <stdio.h>
int main()
{
puts("Hello, #{name}!");
return 0;
}
EOF
puts source
Other than a heredoc, the other option to make the source is to specify it line-by-line, which becomes tedious and potentially error prone (especially when you have embedded quotes).

Related

Why Can't You See Return Value of a Ruby Script in Command Line?

My question is a follow up to this question: No return on command line when running Ruby script because the answer doesn't offer an explanation:
Take the following file, script.rb:
def hello(names)
names.map {|name| "#{name} is awesome!"}
end
hello(["mark", "tony", "scott"])
When executing the file on the command line with ruby script.rb the return value of the following function does not appear. However, testing the method in IRB or by dropping into the code with PRY outputs an explicit return value. Why isn't the return value visible via script execution?
P.S. I am aware that using puts will output code into the terminal but I'm interested in why the return value doesn't output.
Because both IRB or Pry are REPL's
REPL stands for: read, evaluate, print and loop. And that's exactly what both Pry and IRB are doing.
They will first read your input, evaluate your code, print the result of the code execution and then start over.
A Ruby script can't return a value directly like you want it to, the Bash shell works in the same way, a Bash function can't return a string directly. You can return a string (with stdout) and assign it to the variable.
~:$~ cat scr.rb
~:$~ puts "Something here"
~:$~ echo $(ruby ./scr.rb)
Something here
~:$~ myvar=$(echo $(ruby ./scr.rb))
~:$~ echo $myvar
Something here
It's really simple: Bash (or whatever shell you are using) and Ruby are different programming languages. Bash doesn't know anything about Ruby. It doesn't know what a " Ruby return" is, it doesn't know what a "Ruby array" is, it doesn't know what a "Ruby string" is. Therefore, you simply cannot possibly return a Ruby object to Bash.
In fact, the shell usually just uses some operating system function to execute the Ruby script (e.g. the classical fork and exec or something like vfork or clone). If you wanted to return values this way, the operating system kernel would have to know about the semantics of every programming language ever invented plus every programming language that is going to be invented in the future. That is just not feasible.
Note that a command can return a value to the shell, namely an integer between 0 and 255 intended as a status code (with 0 meaning "success" and nonzero meaning "error"), and you can set that return value by calling Kernel#exit.
I used to have the same question myself when I started coding. If you have a closer look at your code you can see why it doesn't print anything. You are actually no asking it in your code. Imagine having a huge script of thousands of lines and you want to execute it. You would have millions of pointless outputs if ruby myscript.rb worked the same way as the REPLs.
In addition, if you do want it to work that way, you can just do require the script inside the REPL session ( require_relative 'your_script' ) and then if you call your function hello it will work the way you describe.
I can use the ruval gem. It evaluates each statement and returns its value.
$ ruval names.rb
def hello(names)
names.map {|name| "#{name} is awesome!"}
end
=> hello
hello(["mark", "tony", "scott"])
=> ["mark is awesome!", "tony is awesome!", "scott is awesome!"]

Ruby require modules, libraries

I'm new to ruby. I understand that, when I see a ruby script, it usually contains lines similar to this:
#!/usr/bin/env ruby
require 'rubyfunction1'
require 'rubyfunction2'
I understand that the require lines are basically (to put it in simple basic terms), calling other scripts. That is really all there is to it. These other scripts are functions.
Now, suppose, I put the content of the rubyfunction1 and rubyfunction2 scripts into two different variables. How do I require the content of a variable?
Or, suppose I want to be able to do something like this:
require '`/home/swenson/rubyfunction1.rb`'
I understand this is a roundabout way of requiring gems/ruby functions, but I'm curious to know if it is at all possible in this manner.
Basically, if I were to run the /home/swenson/rubyfunction1.rb script by itself on the command line, it will basically output to you the content of the script. It would be equivalent to doing "cat /home/swenson/rubyfunction1.rb".
I want to be able to do something like this:
require '`/home/swenson/rubyfunction1.rb`'
require '`/home/swenson/rubyfunction2.rb`'
or
specvar1 = `/home/swenson/rubyfunction1.rb`
specvar2 = `/home/swenson/rubyfunction2.rb`
require specvar1
require specvar2
Is this possible? Any suggestions I can apply to get it to work?
UPDATE:
So here's what I ended up doing.
Main Script called example.rb:
#!/usr/bin/env ruby
add = `./add.rb` # for my purposes, this will serve as require
subtract = `./subtract.rb` # for my purposes, this will serve as require
eval add
puts "I can add: #{add(3, 2)}"
eval subtract
puts "I can now subtract #{subtract(3, 2)}"
Content of add.rb:
#!/usr/bin/env ruby
puts <<-function
#!/usr/bin/env ruby
def add(a, b)
a + b
end
function
Content of subtract.rb:
#!/usr/bin/env ruby
puts <<-function
#!/usr/bin/env ruby
def subtract(a, b)
a - b
end
function
When run from the command line, I get no errors:
# ./example.rb
I can add: 5
I can now subtract 1
Basically, what I want done is precisely this. However, I know there's probably a optimized way of doing this (without having to directly require the relative file). So please, feel free to help me update or optimize this.
I understand that the require lines are basically (to put it in simple basic terms), calling other scripts. That is really all there is to it.
Yes. load, require, and require_relative simply run a Ruby file. That's it.
These other scripts are functions.
No. They are scripts. There is no such thing as a function in Ruby.
Now, suppose, I put the content of the rubyfunction1 and rubyfunction2 scripts into two different variables. How do I require the content of a variable?
You can't. require runs a file. It takes the name of a file (more precisely, a relative path) as an argument. Ruby code is not the name of a file.
Or, suppose i want to be able to do something like this:
require '`/home/swenson/rubyfunction1.rb`'
I understand this is a roundabout way of requiring gems/ruby functions, but im curious to know if it is at all possible in this manner.
This is possible. There's nothing special about this. It will simply run a file at the path `/home/swenson/rubyfunction1.rb`. That is a slightly unusual path, but there is nothing special about it. It's just a path like any other, with some funny characters in it.
so to iterate what im trying to do, i want to be able to do something like this:
require '`/home/swenson/rubyfunction1.rb`'
require '`/home/swenson/rubyfunction2.rb`'
or
specvar1 = `/home/swenson/rubyfunction1.rb`
specvar2 = `/home/swenson/rubyfunction2.rb`
require specvar1
require specvar2
Is this possible? Any suggestions I can apply to get it to work?
It's not quite clear what you want here. Those two code snippets are in no way equivalent, they do completely different things!
The first one passes the literal strings '`/home/swenson/rubyfunction1.rb`' and '`/home/swenson/rubyfunction2.rb`' as arguments to require. The second one executes two files named /home/swenson/rubyfunction1.rb and /home/swenson/rubyfunction2.rb using the default system shell (CMD.EXE on Windows, /bin/sh on POSIX), gets the standard output as String and passes those strings to require.
Note that in the first case, the backticks ` are part of the filename, whereas in the second case, they are Ruby syntax for calling the Kernel#` method.
So, I think I understand your question correctly, let's say we have 3 files
add.rb
#!/usr/bin/env ruby
def add(a, b)
a + b
end
subtract.rb
#!/usr/bin/env ruby
puts "def subtract(a, b)"
puts " a - b"
puts "end"
example.rb
require './add.rb'
subtract = `./subtract.rb`
puts "I can add: #{add(3, 2)}"
# can't do `subtract`, yet, as we haven't `eval`ed the code even though we've run executed the file
eval subtract
puts "I can now subtract #{subtract(3, 2)}"
And the output of running ruby example.rb on the command line is:
$ ruby example.rb
I can add: 5
I can now subtract 1
So, add.rb just defines a function add. When we require that file, it gets loaded in so we can use that function in our code with no problems.
But, subtract.rb doesn't define a function...it just outputs some code, so running it on the command line looks like:
$ ./subtract.rb
def subtract(a, b)
a - b
end
So now, in our third file example.rb, we require the add.rb and then we can start using add in our code as is, but then we want to execute the subtract.rb (using back ticks here) and capture the output of it. At this point, we can't subtract 2 numbers, because we haven't done anything with the output. Then we use eval to evaluate the output of the subtract method, which will define a method for us, then we can subtract the 2 numbers without a problem.
Note that eval is generally frowned upon because it allows arbitrary code to be executed. Never eval untrusted code unless you know how to tame it. In this case, as #JörgWMittag has pointed out in the comments, this code should be trusted, otherwise you just executed an un-trusted file to get this code. Be careful with user input, though, as that's not trusted.

How do I write shell-like scripts using Ruby?

I have a task of writing a simple Ruby script which would do the following.
Upon execution from the UNIX command line, it would present the user with a prompt at which he should be able to run certain commands, like "dir", "help" or "exit". Upon "exit" the user should return to the Unix shell.
I'm not asking for the solution; I would just like to know how this "shell" functionality can be implemented in Ruby. How do you present the user with a prompt and interpret commands.
I do not need a CLI script that takes arguments. I need something that creates a shell interface.
The type of program you require can easily be made with just a few simple constructs.
I know you're not asking for a solution, but I'll just give you a skeleton to start off and play around with:
#!/usr/bin/env ruby
def prnthelp
puts "Hello sir, what would you like to do?"
puts "1: dir"
puts "2: exit"
end
def loop
prnthelp
case gets.chomp.to_i
when 1 then puts "you chose dir!"
when 2 then puts "you chose exit!"
exit
end
loop
end
loop
Anyways, this is a simplistic example on how you could do it, but probably the book recommended in the comments is better. But this is just to get you off.
Some commands to get you started are:
somevar = gets
This gets user input. Maybe learn about some string methods to manipulate this input can do you some good. http://ruby-doc.org/core-2.0/String.html
chomp will chop off any whitespace, and to_i converts it to an integer.
Some commands to do Unix stuff:
system('ls -la') #=> outputs the output of that command
exit #=> exits the program
Anyways, if you want this kind of stuff, I think it's not a bad idea to look into http://www.codecademy.com/ basically they teach you Ruby by writing small scripts such as these. However, they maybe not be completely adapted to Unix commands, but user input and the likes are certainly handled.
Edit:
As pointed out do use this at the top of your script:
#!/usr/bin/env ruby
Edit:
Example of chomp vs. chop:
full_name = "My Name is Ravikanth\r\n"
full_name.chop! # => "My Name is Ravikanth"
Now if you run chop and there are no newline characters:
puts full_name #=> "My Name is Ravikanth"
full_name.chop! #=> "My Name is Ravikant"
versus:
puts full_name #=> "My Name is Ravikanth\r\n"
full_name.chomp! #=> "My Name is Ravikanth"
full_name.chomp! #=> "My Name is Ravikanth"
See: "Ruby Chop vs Chomp"
Here's a really basic loop:
#!/user/bin/ruby
#
while true do
print "$ "
$stdout.flush
inputs = gets.strip
puts "got your input: #{inputs}"
# Check for termination, like if they type in 'exit' or whatever...
# Run "system" on inputs like 'dir' or whatever...
end
As Stefan mentioned in a comment, this is a huge topic and there are scenarios that will make this complicated. This is, as I say, a very basic example.
Adding to the two other (valid) answers posted so far be wary of using #!/usr/bin/ruby, because ruby isn't always installed there. You can use this instead:
#!/usr/bin/env ruby
Or if you want warnings:
#!/usr/bin/env ruby -w
That way, your script will work irrespective of differences where ruby might be installed on your server and your laptop.
Edit: also, be sure to look into Thor and Rake.
http://whatisthor.com
http://rake.rubyforge.org
Use irb.
I was looking into an alternative to bash and was thinking along the same lines... but ended up choosing fish: http://fishshell.com/
Nonetheless, I was thinking of using irb and going along the lines of irbtools: https://github.com/janlelis/irbtools
Example:
> irb
Welcome to IRB. You are using ruby 1.9.3p0 (2011-10-30 revision 33570) [x86_64-linux]. Have fun ;)
>> ls #=> ["bin", "share", "opt", "lib", "var", "etc", "src"]
>>
In any case, irb is the ruby shell.
Take a look at cliqr which comes with inbuilt support for build a custom shell https://github.com/anshulverma/cliqr/

How do I pass a text file or a string to a Ruby script and output the result in the command line?

As an exercise to learn Ruby, I would like to create a script that will be run from the terminal. It should accept as input either a string or a text file and it should output the result of various string parsing mechanisms that I will write myself.
To get me started, would you please translate this pseudo-code into proper Ruby for me?
In terminal: ruby myscript.rb (either a string or a text file).
In myscript.rb: Retrieve input. Set my_input to the input.
Set my_output to the result of various_string_parsing_voodoo (done to my_input).
puts my_output
I intend to actually write the code myself, but if someone could supply me with a skeleton .rb file to send in "Hello World" and get "[World] is pleased by your [hello]" or something equally inane that'd be a great help.
Here are some key pieces:
ARGV is an array containing the arguments you passed when running your script from command line.
the File class contains several utilies. For example, File.exists?(path) returns true if the path exists, and File.file?(path) returns true if the path exists and is a file (not a dir).
I think this may help you quite a bit.

A question about $LOAD_PATH in Ruby

$: = '/users/joecool/rubylib'
$: << '/users/joecool/rubylib'
$:.unshift('/users/joecool/rubylib')
ruby -c somescript.rb
ruby -e "puts 'Hello, world!'
Can someone direct me to some reading, so that I can figure out what this code does?
$: = '/users/joecool/rubylib'
Sets the load path to that string.
$: << '/users/joecool/rubylib'
Adds that string to the end of the load path array.
$:.unshift('/users/joecool/rubylib')
Adds that string to the beginning of the load path array.
ruby -c somescript.rb
Checks the syntax then exits.
ruby -e "puts 'Hello, world!'
Runs that Ruby snippet. See this reference and the man page.
For general Ruby workings, look at the Pickaxe book (The Pragmatic Programmer's Guide).
For reference about Ruby objects and functions: http://ruby-doc.org/
For the precise questions, you may want to take a look at a list of predefined variables.
$: is the load path, an array containing directories where libraries are searched for. It's a less readable version of $LOAD_PATH.
For arguments to the interpreter, you may want to look at the Unix manpage for Ruby (use 'man ruby', or look at http://linux.die.net/man/1/ruby if you don't have a Unix system in handy).
Specifically:
-c checks the syntax of the script without running it.
-e takes a string that is used as the script, instead of reading the script from a file.
More extensive reading: http://www.ruby-lang.org/en/documentation/
It looks like you need an introductory book about Ruby. There are many, but I would recommend that you take a look at Beginning Ruby by Peter Cooper or Programming Ruby by Dave Thomas. (Those two are different enough in style and organization that one or the other is likely to fit you reasonably well.)

Resources