Ruby Argument error in convert function - ruby-on-rails

I am trying to run a program from a book that I am learning ruby from, but I keep getting this error no matter how I try to fix it
ex41.rb:70:in `convert': wrong number of arguments (0 for 2) (ArgumentError)
from ex41.rb:117:in `block (2 levels) in <main>'
from ex41.rb:113:in `each'
from ex41.rb:113:in `block in <main>'
from ex41.rb:108:in `loop'
from ex41.rb:108:in `<main>'
I do understand that I have the wrong number of arguments, but the thing is I can't find where I'm getting them wrong, could someone please explain this to me? For convienience I put (LINE XXX(as a number)) by the lines that cause me trouble. Thank you.
# calls external file from web
require 'open-uri'
# assigns WORD_URL to website
WORD_URL = "http://learncodethehardway.org/words.txt"
# creates empty array WORDS
WORDS =[]
# creates hash PHRASES
PHRASES = {
"class ### < ### \nend" =>
"Make a class named ### that is-a ###.",
# my assumption is that ### ### and the like will sb in words, lets find
out
"class ###\n\tdef initialize(###)\n\tend\nend" =>
"class ### has-a initialize that takes ### parameters.",
"class ###\n\tdef ***(###)\n\tend\nend" =>
"class ### has-a function named *** that takes ### parameters.",
"*** = ###.new()" =>
"Set *** to an instance of class ###.",
"***.***(###)" =>
"From *** get the *** function, and call it with parameters ###",
"***.*** = '***'" =>
"From ***, get the *** attribute and set it equal to '***'."
}
# creates variable for first argument if argument is english
PHRASE_FIRST = ARGV[0] == "english"
# opens WORD_URL function and creates function f
open(WORD_URL) {|f|
# for every line in f chomps break character and pushes out word to f
f.each_line {|word| WORDS.push(word.chomp)}
}
# defines function craft_names with variables rand_words, snippet and
pattern, and assigns caps value to false
def craft_names(rand_words, snippet, pattern, caps=false)
# assigns vriable names to snippet.scan on pattern maps and does the
following
function
names = snippet.scan(pattern).map do
# assigns word to rand_words that have popped off the end
word = rand_words.pop()
# Guessing again, maybe capitalizes the first word
caps ? word.capitalize : word
# ends do
end
# returns names twice
return names * 2
# ends craft_names function
end
# defines function craft_params with variables rand_words snippet, and
pattern
def craft_params(rand_words, snippet, pattern)
# assigns names to action scan with variable pattern with action
# length on beginning part of array to snippet and runs the following
# function using map
names = (0...snippet.scan(pattern).length).map do
# assigns variable param_count to an action that takes a random 3 and adds one to it
param_count = rand(3) + 1
# assigns variable params to one long ass action that maps 0 to param_count
value and pops out the last word
params = (0...param_count).map { |x| rand_words.pop() }
# joins params list, (I'm sure its a list) with string ,
params.join(", ")
# ends names for loop
end
# returns names twice
return names * 2
# ends craft_params function
end
# defines convert function with variables snippet and phrase
(LINE 70) def convert(snippet, phrase)
# sords words randomly and assigns words to rand_words
rand_words = WORDS.sort_by {rand}
# assigns class names to function craft mnames on rand_words, snippet /###/, and caps=true
class_names = craft_names(rand_words, snippet, /###/, caps=true)
# assigns other_names to craft_names with variables rand_words, snippet, and /\*\*\*/
other_names = craft_names(rand_words, snippet, /\*\*\*/)
# assigns param_names to craft_params with rand_words, snippet, and ### as variables
param_names = craft_params(rand_words, snippet, /###/)
# assigns empty array results
results = []
# on all variables snippet and phrase matchups perform the function
sentence
[snippet, phrase].each do |sentence|
# fake class names, also copies sentence
result = sentence.gsub(/###/) {|x| param_names.pop}
# fake other names
result.gsub!(/\*\*\*/)
# fake parameter lists
result.gsub!(/###/)
# returns result
results.push(result)
# ends function
end
# returns results
return results
# ends convert function
end
# keep going until they hit ctrl-d
# continual loop
(LINE 108)loop do
# assigns variable snippets to Prases.keys and sorts randomly
snippets = PHRASES.keys().sort_by {rand}
# for snippet in snippets
(LINE113) snippets.each do |snippet|
# assigns PHRASES on snippet to phrase
phrase = PHRASES[snippet]
# asssigns question and answer to converted snippet and phrase
#respectively
(LINE117)question, answer = convert[snippet, phrase]
# if values in phrase firs are equal to answer, question
if PHRASE_FIRST
# question, answer = answer, question
question, answer = answer, question
# ends if
end
# prints question, two line breaks, and > without line break
print question, "\n\n> "
# closes program unless input
exit(0) unless $stdin.gets
# prints line break ANSWER: answer line break line break
puts "\nANSWER: %s\n\n"
# ends unless
end
# ends loop
end

TL;DR – Use convert(snippet, phrase) instead of convert[snippet, phrase]
When you write
convert[snippet, phrase]
... it is equivalent to:
convert()[snippet, phrase]
Adding a space:
convert [snippet, phrase]
... would call the method with an array:
convert([snippet, phrase])
To actually call the method with two arguments, use:
convert(snippet, phrase)
or
convert snippet, phrase

Related

How to return the same result from str.scan and str.match in Ruby using regex

I have a method to capture the extension as a group using a regex:
def test(str)
word_match = str.match(/\.(\w*)/)
word_scan = str.scan(/\.(\w*)/)
puts word_match, word_scan
end
test("test.rb")
So it will return:
.rb
rb
Why would I get a different answer?
The reason is that match and scan return different objects. match returns either a MatchData object or a String while scan returns an Array. You can see this by calling the class method on your variables
puts word_match.class # => MatchData
puts word_scan.class # => Array
If you take a look at the to_s method on MatchData you'll notice it returns the entire matched string, rather than the captures. If you wanted just the captures you could use the captures method.
puts word_match.captures # => "rb"
puts word_match.captures.class # => Array
If you were to pass a block to the match method you would get a string back with similar results to the scan method.
word_match = str.match(/\.(\w*)/) { |m| m.captures } # => [["rb"]]
puts word_scan.inspect #=> ["rb"]
puts word_match #=> "rb
More information on these methods and how they work can be found in the ruby-doc for the String class.
Don't write your own code for this, take advantage of Ruby's own built-in code:
File.extname("test.rb") # => ".rb"
File.extname("a/b/d/test.rb") # => ".rb"
File.extname(".a/b/d/test.rb") # => ".rb"
File.extname("foo.") # => "."
File.extname("test") # => ""
File.extname(".profile") # => ""
File.extname(".profile.sh") # => ".sh"
You're missing some cases. Compare the above to the output of your attempts:
fnames = %w[
test.rb
a/b/d/test.rb
.a/b/d/test.rb
foo.
test
.profile
.profile.sh
]
fnames.map { |fn|
fn.match(/\.(\w*)/).to_s
}
# => [".rb", ".rb", ".a", ".", "", ".profile", ".profile"]
fnames.map { |fn|
fn.scan(/\.(\w*)/).to_s
}
# => ["[[\"rb\"]]",
# "[[\"rb\"]]",
# "[[\"a\"], [\"rb\"]]",
# "[[\"\"]]",
# "[]",
# "[[\"profile\"]]",
# "[[\"profile\"], [\"sh\"]]"]
The documentation for File.extname says:
Returns the extension (the portion of file name in path starting from the last period).
If path is a dotfile, or starts with a period, then the starting dot is not dealt with the start of the extension.
An empty string will also be returned when the period is the last character in path.
On Windows, trailing dots are truncated.
The File class has many more useful methods to pick apart filenames. There's also the Pathname class which is very useful for similar things.

Don't change string value on insert

I have a Model user with the following method:
def number_with_hyphen
number&.insert(8, "-")
end
When I run it several times in my tests I get the following output:
users(:default).number_with_hyphen
"340909-1234"
(byebug) users(:default).number_with_hyphen
"340909--1234"
(byebug) users(:default).number_with_hyphen
"340909---1234"
(byebug) users(:default).number_with_hyphen
"340909----1234"
It changes the number ?Here are the docs https://apidock.com/ruby/v1_9_3_392/String/insert
When I restructure my method to:
def number_with_hyphen
"#{number}".insert(8, "-") if number
end
If works like expected. The output stays the same!
How would you structure the code, how would you perform the insert?
which method should I use instead. Thanks
If you're using the insert method, which in the documentation explicitly states "modifies str", then you will need to avoid doing this twice, rendering it idempotent, or use another method that doesn't mangle data.
One way is a simple regular expression to extract the components you're interested in, ignoring any dash already present:
def number_with_hyphen
if (m = number.match(/\A(\d{8})\-?(\d+)\z/))
[ m[1], m[2] ].join('-')
else
number
end
end
That ends up being really safe. If modified to accept an argument, you can test this:
number = '123456781234'
number_with_hyphen(number)
# => "12345678-1234"
number
# => "123456781234"
number_with_hyphen(number_with_hyphen(number))
# => "12345678-1234"
number_with_hyphen('1234')
# => "1234"
Calling it twice doesn't mangle anything, and any non-conforming data is sent through as-is.
Do a clone of the string:
"#{number}".clone.insert(8, '-')

Find a specific text segment within a file and than search for specific line in ruby

I'm trying to find a specific text segment within a text file and than a specific line within the text segment. The algorithm should be as follow:
1)First, search for a line which contains the keyword "Macros"
2)The next found line must contain the keyword "Name"
3)And finally print me the next line
As pseudo code I mean something like this:
File.open(file_name) do |f|
f.each_line {|line|
if line.include?("Macros")
and if next line.include?("Name")
print me the line after
end
Any suggestions?
I would use boolean flags to remember that I already matched the parts of the condition:
File.open(file_name) do |file|
marcos_found = false
name_found = false
file.each_line do |line|
if line.include?('Macros')
marcos_found = true
elsif found_marcos && line.include?("Name")
name_found = true
elsif marcos_found && name_found
puts line
break # do not search further or print later matches
end
end
end
You could use a regex:
r = /
\bMacros\b # Match "Macros" surrounded by word breaks
.*?$ # Match anything lazily to the end of the line
[^$]* # Match anything lazily other than a line break
\bName\b # Match "Name" surrounded by word breaks
.*?\n # Match anything lazily to the end of the line
\K # Discard everything matched so far
.*?$ # Match anything lazily to the end of the line
/x # Extended/free-spacing mode
Suppose:
text = <<-_
You can use
Macros in C
to replace code.
Ruby doesn't
have Macros.
"Name That Tune"
was an old TV
show.
_
Let's write this to file:
FName = "test"
File.write(FName, text)
#=> 104
read it back into a string:
str = File.read(FName)
#=> "You can use\nMacros in C\nto replace code.\nRuby doesn't\nhave " +
# "Macros.\n\"Name That Tune\"\nwas an old TV\nshow.\n"
and test the regex:
text.scan r
#=> ["was an old TV"]

How to set "programmatically"\"iteratively" each class object attribute to a value?

I am using Ruby on Rails 3.0.9 and RSpec 2. I am trying to refactoring some spec file in the following way (in order to test with less code similar class object attribute values):
[
:attribute_a,
:attribute_b,
:attribute_c
].each do |attr|
before do
# HERE I would like to set the "current" 'attr' related to the
# class object instance 'attribute_< letter >' (read below for
# more information) each time the iterator is called (note: all
# following attributes are NOT attr_accesible - for that reason
# I use the 'user.attribute_< letter >' in the 'before do'
# statement)
#
# # Iteration 1
# user.attribute_a = 'a_value'
# # No changes to 'user.attribute_b'
# # No changes to 'user.attribute_c'
#
# # Iteration 2
# # No changes to 'user.attribute_a'
# user.attribute_b = 'a_value'
# # No changes to 'user.attribute_c'
#
# # Iteration 3
# # No changes to 'user.attribute_a'
# # No changes to 'user.attribute_b'
# user.attribute_c = 'a_value'
# Maybe I should make something like the following but that, as well
# as the 'send' method must be used, doesn't work (the below code-line
# is just an example of what I would like to do).
#
# user.send(:"#{attr}") = 'a_value'
end
...
end
How can I improve the above code so to accomplish what I aim (I refer to the user.send(:"#{attr}") = 'a_value' part in order to set "programmatically" - that is, set a different attribute value for each iteration made - each user attribute value to 'a_value')?
You should use .send and append an = to the method name to invoke the setter, passing the value as the second argument to send:
[
:attribute_a,
:attribute_b,
:attribute_c
].each do |attr|
before do
user.send("#{attr}=", 'a_value')
end
You're effectively doing this:
user.send('attribute_a=', 'a_value');
Your syntax (user.send(:"#{attr}") = 'a_value') is wrong/weird for a couple reasons:
There's no reason to convert :attr to a string and them immediately back to a symbol
You can't assign a value to the return value of .send

In Ruby, how to write a method to display any object's instance variable names and its values

Given any object in Ruby (on Rails), how can I write a method so that it will display that object's instance variable names and its values, like this:
#x: 1
#y: 2
#link_to_point: #<Point:0x10031b298 #y=20, #x=38>
(Update: inspect will do except for large object it is difficult to break down the variables from the 200 lines of output, like in Rails, when you request.inspect or self.inspect in the ActionView object)
I also want to be able to print <br> to the end of each instance variable's value so as to print them out nicely on a webpage.
the difficulty now seems to be that not every instance variable has an accessor, so it can't be called with obj.send(var_name)
(the var_name has the "#" removed, so "#x" becomes "x")
Update: I suppose using recursion, it can print out a more advanced version:
#<Point:0x10031b462>
#x: 1
#y: 2
#link_to_point: #<Point:0x10031b298>
#x=38
#y=20
I would probably write it like this:
class Object
def all_variables(root=true)
vars = {}
self.instance_variables.each do |var|
ivar = self.instance_variable_get(var)
vars[var] = [ivar, ivar.all_variables(false)]
end
root ? [self, vars] : vars
end
end
def string_variables(vars, lb="\n", indent="\t", current_indent="")
out = "#{vars[0].inspect}#{lb}"
current_indent += indent
out += vars[1].map do |var, ivar|
ivstr = string_variables(ivar, lb, indent, current_indent)
"#{current_indent}#{var}: #{ivstr}"
end.join
return out
end
def inspect_variables(obj, lb="\n", indent="\t", current_indent="")
string_variables(obj.all_variables, lb, indent, current_indent)
end
The Object#all_variables method produces an array containing (0) the given object and (1) a hash mapping instance variable names to arrays containing (0) the instance variable and (1) a hash mapping…. Thus, it gives you a nice recursive structure. The string_variables function prints out that hash nicely; inspect_variables is just a convenience wrapper. Thus, print inspect_variables(foo) gives you a newline-separated option, and print inspect_variables(foo, "<br />\n") gives you the version with HTML line breaks. If you want to specify the indent, you can do that too: print inspect_variables(foo, "\n", "|---") produces a (useless) faux-tree format instead of tab-based indenting.
There ought to be a sensible way to write an each_variable function to which you provide a callback (which wouldn't have to allocate the intermediate storage); I'll edit this answer to include it if I think of something. Edit 1: I thought of something.
Here's another way to write it, which I think is slightly nicer:
class Object
def each_variable(name=nil, depth=0, parent=nil, &block)
yield name, self, depth, parent
self.instance_variables.each do |var|
self.instance_variable_get(var).each_variable(var, depth+1, self, &block)
end
end
end
def inspect_variables(obj, nl="\n", indent="\t", sep=': ')
out = ''
obj.each_variable do |name, var, depth, _parent|
out += [indent*depth, name, name ? sep : '', var.inspect, nl].join
end
return out
end
The Object#each_variable method takes a number of optional arguments, which are not designed to be specified by the user; instead, they are used by the recursion to maintain state. The given block is passed (a) the name of the instance variable, or nil if the variable is the root of the recursion; (b) the variable; (c) the depth to which the recursion has descended; and (d), the parent of the current variable, or nil if said variable is the root of the recursion. The recursion is depth-first. The inspect_variables function uses this to build up a string. The obj argument is the object to iterate through; nl is the line separator; indent is the indentation to be applied at each level; and sep separates the name and the value.
Edit 2: This doesn't really add anything to the answer to your question, but: just to prove that we haven't lost anything in the reimplementation, here's a reimplementation of all_variables in terms of each_variables.
def all_variables(obj)
cur_depth = 0
root = [obj, {}]
tree = root
parents = []
prev = root
obj.each_variable do |name, var, depth, _parent|
next unless name
case depth <=> cur_depth
when -1 # We've gone back up
tree = parents.pop(cur_depth - depth)[0]
when +1 # We've gone down
parents << tree
tree = prev
else # We're at the same level
# Do nothing
end
cur_depth = depth
prev = tree[1][name] = [var, {}]
end
return root
end
I feel like it ought to be shorter, but that may not be possible; because we don't have the recursion now, we have to maintain the stack explicitly (in parents). But it is possible, so the each_variable method works just as well (and I think it's a little nicer).
I see... Antal must be giving the advanced version here...
the short version then probably is:
def p_each(obj)
obj.instance_variables.each do |v|
puts "#{v}: #{obj.instance_variable_get(v)}\n"
end
nil
end
or to return it as a string:
def sp_each(obj)
s = ""
obj.instance_variables.each do |v|
s += "#{v}: #{obj.instance_variable_get(v)}\n"
end
s
end
or shorter:
def sp_each(obj)
obj.instance_variables.map {|v| "#{v}: #{obj.instance_variable_get(v)}\n"}.join
end
This is a quick adaptation of a simple JSON emitter I wrote for another question:
class Object
def inspect!(indent=0)
return inspect if instance_variables.empty?
"#<#{self.class}:0x#{object_id.to_s(16)}\n#{' ' * indent+=1}#{
instance_variables.map {|var|
"#{var}: #{instance_variable_get(var).inspect!(indent)}"
}.join("\n#{' ' * indent}")
}\n#{' ' * indent-=1}>"
end
end
class Array
def inspect!(indent=0)
return '[]' if empty?
"[\n#{' ' * indent+=1}#{
map {|el| el.inspect!(indent) }.join(",\n#{' ' * indent}")
}\n#{' ' * indent-=1}]"
end
end
class Hash
def inspect!(indent=0)
return '{}' if empty?
"{\n#{' ' * indent+=1}#{
map {|k, v|
"#{k.inspect!(indent)} => #{v.inspect!(indent)}"
}.join(",\n#{' ' * indent}")
}\n#{' ' * indent-=1}}"
end
end
That's all the magic, really. Now we only need some simple defaults for some types where a full-on inspect doesn't really make sense (nil, false, true, numbers, etc.):
module InspectBang
def inspect!(indent=0)
inspect
end
end
[Numeric, Symbol, NilClass, TrueClass, FalseClass, String].each do |klass|
klass.send :include, InspectBang
end
Like this?
# Get the instance variables of an object
d = Date.new
d.instance_variables.each{|i| puts i + "<br />"}
Ruby Documentation on instance_variables.
The concept is commonly called "introspection", (to look into oneself).

Resources