How would I coerce the behavior of irb to treat variable identifiers as strings when used in method signatures?
I am trying to create a irb based calculation tool and I want to reduce the typing of users who use this tool in the irb shell. Assume my users are not ruby programmers or know much about the syntax of ruby. The may have some facility with the command line.
I have a file
calculator.rb
inside this file is
def calculate(value, units)
... some logic
end
I instruct the user to fire up irb like so
irb -r path/to/calculator.rb
I instruct the user to type
calculate(10, inches)
get return value in irb
how can I do this without requiring the user to understand that they have to wrap the second parameter in quotation marks. In other words I don't want the user to have to type
calculate(10, "inches")
is it possible to cast the user input as a string instead of a variable identifier before it is passed to my method inside my script? Maybe what I want to do is not possible without fundamentally breaking irb shell?
If this is for non-programmers how about using puts and gets?
def calculate
puts "Which number would you like to convert?"
number = gets.to_i
puts "What do you want to convert it to?"
type = gets
# your conversion logic
puts result
end
You can actually do it the way you requested using method_missing. Any matching units will get converted to strings instead of raising exceptions.
SO_CALC_UNITS = %w[inches feet yards meters parsecs]
def method_missing(method)
if SO_CALC_UNITS.include?(method.to_s)
method.to_s
else
super(method)
end
end
Related
As you may know, there's a special variable _ of last expression in REPL's like irb and pry.
But can I get the result of last expression from Ruby platform?
Because of Ruby's dynamic nature and flexible compiler/virtual machine, maybe it's possible to get it from the Ruby's guts.
So I can avoid to write construction like this:
def some_method
result = begin
# some code
end
# do something with result
result
end
P.S. tap is not a solution for me
I'm trying to use the steam-condenser gem to pull a list of games for a particular user (myself). So far I have the following:
require 'steam-condenser'
id = SteamId.new 'tamachan87'
games_owned = id.games
games_owned is now a hash, containing keys and arrays.
If I call games_owned.values in IRB I will get a result that contains all the information of those games, from it's ID number to the name to its logo hash.
However, when I use the following:
games_owned.each do |key, array|
puts "==== #{key} ===="
puts array
end
I get just the first value of the array such as:
==== 200260 ====
#<SteamGame:0x00000100beb0a8>
Each value/array thing has an #name variable which is the only thing I want to pull.
Could someone please help me to better understand these hashes and how I can pull specific data (#name) from them?
Thanks in advance.
The return value of SteamId#games is not a hash of arrays, it's a hash of SteamGame objects.
Your example code could be written like that:
games_owned.each do |app_id, game|
puts "==== #{app_id} ===="
puts game.name
end
See the documentation of SteamId for more information.
The reason you get an unfriendly class name and memory value is that the SteamGame class does not define an inspect method. However if all you're interested is printing the names of the games, use puts key.name
If you want to print your Steamgame object in a pretty way by using "puts"
you should overwrite "to_s" in your Steamgame class.
The reason you are getting different results is puts uses to_s method to print its argument (which is
steamgame object itself in your case with specific id).
If you use "p" or "puts array.inspect" instead of "puts", it will debug your object and you will see irb like results.
If you have an attribute like #name in your Steamgame class, you should be able to get it just by saying:
games_owned.values[i].name or puts array.name.
did you try that?
In the process of understanding ruby, I was trying to overide '+' with a default argument value. Something like this.
class C
def something(a = 5)
puts "Received: #{a}"
end
def +(b = 10)
puts "Received: #{b}"
end
end
Now
x = C.new
x.something #=> Received: 5
x.something(88) #=> Received: 88
x.+ #=> IRB shows ? whereas I was expecting an output 'Received: 10'
Is this because of operator precedence?
Problem with IRB (look like it doesn't handle such cases). If you create separate .rb file and run it you will get expected output:
Received: 5
Received: 88
Received: 10
IRB is parsing the + and expecting a second parameter for the binary operation. If you provide parenthesis it works correctly:
x.+() #=> Received: 10
IRb uses a different parser than Ruby does. So, in some weird corner cases, IRb may parse code differently than Ruby. If you want to see whether something is valid Ruby or not, you should ask Ruby not IRb.
The reason for this is mainly that Ruby always parses the entire file at once, so it always knows when an expression ends. IRb on the other hand, has to "guess" every time when you press ENTER whether you simply want to continue the expression on a new line or whether you wanted to evaluate the expression as-is. As a result, IRb cannot just use the Ruby parser, it needs to have its own. And Ruby's grammar is so complex that writing your own parser is really really hard. That's why such bugs and corner cases pop up from time to time even in a piece of software as old and as widely used as IRb.
I'm starting to learn Ruby. I read that arguments where passed by reference to a method,
however I don't understand the difference between these two methods.
def print(text)
puts text
end
and
def print(*text)
puts text
end
Using a * means that we are passing a pointer like in C?
The *text is what's called the splat operator in Ruby. It basically means if you pass multiple arguments to the second print they will get slurped into the single text variable.
See The Splat Operator in Ruby
The * before a parameter name in a Ruby parameter list is used for variable length arguments, so they are similar to the ... in C/C++ for varargs.
def vlaFunc(*args)
puts args
end
vlaFunc(1,2,3)
# output is [1,2,3]
There are no pointers in Ruby, * in this context is generally referred to as the "splat" operator:
http://4loc.wordpress.com/2009/01/16/the-splat-operator-in-ruby/
http://theplana.wordpress.com/2007/03/03/ruby-idioms-the-splat-operator/
In this case the method can take an arbitrary number of arguments, which will be available in the array text.
First you have two nice methods started there. But I would say try to avoid using puts inside them. You don't need it anyway. A method will always yield the last statement evaluated. something = text would get the job done. And I don't need to answer now about the differences.
Your first two replies are very good there. But you may want to try something like this
j = *[] #=> nil in 1.8 but [] in 1.9
It's been the new kid on the block for a time now. Guess what it does?
I'm writing a ruby method that takes a string input but I don't want to type the quotes.
For example:
def noquotes(input)
puts input
end
noquotes('12Dec11Bel01') # ---> 12Dec11Bel01
noquotes(12Dec11Bel01) # ---> Currently yields an error
What I'd like to be able to do is enter the method input without the quotes (second example) and still get the right result. I tried using .to_str to ensure the input was treated as a string, but it didn't work.
Hehe, sorry, but you can't mangle with the syntax tree in Ruby. If you don't make quotes, it will be parsed as a variable or method call.
What you can do is
def method_missing(meth, *args)
meth.to_s
end
but use that wisely and with scoping, as in
class DSL # You'd use that here
def dsl(&block)
instance_eval(block)
end
def method_missing(meth, *args)
meth.to_s
end
def noquotes(input)
puts input
end
end
def dsl(&block)
DSL.new.dsl(&block)
end
dsl do
noquotes(foobar)
end
Use with caution and only if you know what you're doing! And only in DSL. And not even there. Really. Don't do it.
This isn't possible without doing horrible maintainence-nightmare things. Think about how the Ruby interpreter needs to parse your input. Without the quotes, it has no way to know that 12Dec11Bel01 is intended as a string, and not a call to another method or the name of a variable.
Type the quotes as skip the parentheses. It's the same number of characters.