Creating a setter method that takes extra arguments in Ruby - ruby

I'm trying to write a method that acts as a setter and takes some extra arguments besides the assigned value. Silly example:
class WordGenerator
def []=(letter, position, allowed)
puts "#{letter}#{allowed ? ' now' : ' no longer'} allowed at #{position}"
end
def allow=(letter, position, allowed)
# ...
end
end
Writing it as an indexer works and I can call it like this:
gen = WordGenerator.new
gen['a', 1] = true
# or explicitly:
gen.[]=('a', 1, true)
But when I try any of the following, the interpreter complains:
gen.allow('a', 1) = false # syntax error
gen.allow=('a', 1, false) # syntax error
Why won't this work, am I missing the obvious?

It doesn't work because the parser doesn't allow it. An equals sign is allowed in expressions of the form identifier = expression, expression.identifier = expression (where identifier is \w+), expression[arguments] = expression and expression.[]= arguments and as part of a string or symbol or character literal (?=). That's it.
gen.send(:allow=, 'a', 1, false) would work, but at that point you could as well just give the method a name that doesn't include a =.

I have come across this and decided to pass my arguments as an array or hash.
E.g.:
def allow=(arguments)
puts arguments[:letter]
puts arguments[:position]
puts arguments[:allowed]
end
object.allow={:letter=>'A',:position=>3,:allowed=>true}

Related

ArgumentError: wrong number of arguments (given 0, expected 1) Ruby

ArgumentError: wrong number of arguments (given 0, expected 1).
The code opens the file and looks at the paragraph and counts, the error
is in the center of the code. An error occurs when a method is called(1).
I can’t understand how to pass the argument methods.
#books = "You can use this knowledge to create small tools that might help."
require "colorize"
class Filecalculation
def select
loop do
puts "# Will we search : calculation_lines paragraph(1)".cyan
print "\n>>>>>> ".yellow
input = gets.chomp
search_method = "calc_#{input}"
if (respond_to?(search_method))
I can’t understand how to pass the argument to this place.
contents = send(search_method, #books)
else
puts "Unknown input: #{input.inspect}, method #{search_method} not defined."
end
end
end
# =================== calc_1 сounting words in Text File
def calc_1 paragraph
word_count = paragraph.split.length
puts "#{word_count} words"
end
end
Filecalculation.new.select
If you call send(search_method) you call a method without arguments. To pass arguments to the method being called, you need to pass them as next send args:
send(search_method, arg1, arg2)
in your case
send(search_method, paragraph)
Docs

Write a method returns argument to string

Coding exercise. Won't run. Please help debug my understanding :)
def my_to_s(num_one, num_three)
return num_three + num_one
end
my_to_s(1+3)
As #sebastian-palma says, you need to pass two arguments in separated by commas. So:
my_to_s(1, 3)
However, your current code simply sums the two numbers. You also need to convert the result to a string. So this would work:
def my_to_s(num_one, num_two)
result = num_one + num_two
return result.to_s
end
However, you don't need to add the return as Ruby always returns the result of the last statement in a method. Also you can combine the statement into one line. So:
def my_to_s(num_one, num_two)
(num_one + num_two).to_s
end
my_to_s(1, 3) # --> "4"

Accidental Type conversion in Ruby with strings

I'm trying to solve a challenge where you take in a string of words but return the longest word in the string. My strategy is to break the string into an array of individual words and search the array. However, I'm getting a type conversion error. What is causing the type conversion error? This is particularly strange to me because I don't actually see any type conversion happening here.
def LongestWord(sen)
sen1 = sen.split("/\W+/")
grt = 0
sen1.each do |i|
if sen1[i].length > sen1[grt].length # Type conversion error
grt = i
end
end
sen1[grt]
end
# keep this function call here
puts LongestWord(STDIN.gets)
The type conversion is caused by the array entry i being converted (probably unsuccessfully) into an integer (though I suppose it could be ruby trying to convert the array into a hash, and use i as a key to the hash).
Your misunderstanding is that you think you're getting the array's indices passed into the block for each. What is passed in to that block is each individual value in the array. I.e., if your string sen is 'this is a silly string', then the values passed are 'this', 'is', 'a', 'silly', and 'string'.
You get the error because, when the code is running, i is the first value of sen1, which results in sen1['some string'] being evaluated.
An array can't have a string index, only a hash can, resulting in the Type error.
Meditate on this:
def longest_word(sen)
sen1 = sen.split # => ["foo", "barbaz"]
grt = 0
sen1.each do |i|
i # => "foo"
sen1 # => ["foo", "barbaz"]
sen1[i] # =>
sen1[grt] # =>
sen1[i].length # =>
sen1[grt].length # =>
if sen1[i].length > sen1[grt].length #Type conversion error
grt = i # =>
end
end
sen1[grt]
end
# keep this function call here
longest_word('foo barbaz')
Breaking it down further, here's the offending problem:
sen1 = 'foo barbaz'.split
sen1['foo'] # =>
# ~> TypeError
# ~> no implicit conversion of String into Integer
You don't see the type conversion, but it is there. In more than one place.
As Derrell Durrett pointed out in his answer, your are assuming (wrongly) that the index of the array is passed to the block, not its elements.
Then you write if sen1[i].length > sen1[grt].length. Let's consider the string is 'this is a silly string'. The first element is 'this' and what you are trying to do is if sen1['this'].length > sen1[0].length. As Ruby arrays always have integer indexes, Ruby tries to convert 'this' to an integer in order to find the element at the specified position. Of course this fails!
But your code is not that far from being right. A few small changes and it will run perfectly well:
def longest_word(sen)
sen1 = sen.split(" ")
grt = 0
sen1.each_index do |i|
if sen1[i].length > sen1[grt].length
grt = i
end
end
sen1[grt]
end
puts LongestWord(STDIN.gets)
Now you'd be passing the indexes with sen1.each_index and it'd be working fine.
Notice that I changed the name of your method to longest_word. This is much better, in fact, because this first capital letter is reserved to constants and class names.
I also would like to point that you are not using a good Ruby style. This could be written like this:
def longest_word(str)
str.split(" ").max_by{ |s| s.length }
end
and the result would be the same.

Whats the ruby syntax for calling a method with multiple parameters and a block?

Ruby doesn't like this:
item (:name, :text) {
label('Name')
}
And I don't know why. I'm attempting to create a DSL. The 'item' method looks like this:
def item(name, type, &block)
i = QbeItemBuilder.new(#ds, name, QbeType.gettype(type))
i.instance_exec &block
end
Take a name for the item, a type for the item, and a block. Construct an item builder, and execute the block in its context.
Regardless of whether or not I need to use instance_exec (I'm thinking that I don't - it can be stuffed in the initialiser), I get this:
SyntaxError (ds_name.ds:5: syntax error, unexpected ',', expecting ')'
item (:name, :text) {
^
How do I invoke method with multiple arguments and a block? What does ruby think I'm trying to do?
The space before parentheses is causing ruby to evaluate (:name, :text) as single argument before calling the method which results in a syntax error. Look at these examples for illustration:
puts 1 # equivalent to puts(1) - valid
puts (1) # equivalent to puts((1)) - valid
puts (1..2) # equivalent to puts((1..2)) - valid
puts (1, 2) # equivalent to puts((1, 2)) - syntax error
puts(1, 2) # valid
Your way of providing the block is syntactically valid, however when the block is not in the same line as the method call it is usually better to use do ... end syntax.
So to answer your question you can use:
item(:name, :text) { label('Name') }
or:
item(:name, :text) do
label('Name')
end
Remove the space before the ( in item (:name, :text) {

Why non-explicit splat param plus default param is wrong syntax for method definition in Ruby 1.9?

I'd like to ask why having a splat param1 and a param2 with default value assignment in Ruby-1.9.3-p0 as below:
def my_method(*param1, param2 = "default"); end
returns
SyntaxError: (irb):1: syntax error, unexpected '=', expecting ')'
My workaround is explicitly wrap param1 in brackets like this:
def my_method((*param1), param2 = "default"); end
Many thanks
Ruby can't parse a parameter with a default after a splat. If you have default assignment in a parameter after a splat, how would Ruby know what to assign the variable to?
def my_method(*a, b = "foo"); end
Let's say I then call my_method:
my_method(1, 2, 3)
Ruby has no way of knowing whether b is missing, in which case you want b to be foo and a is [1,2,3], or if b is present in which case you want it to be 3.

Resources