Problem accessing values from YAML file in Ruby - ruby

I am building a hangman game and am trying to add save game and load game functionality. I have saved a game using the following method inside my Hangman class.
def save_game
File.open('saved_game.yml', 'w') { |f| YAML.dump([] << self, f) }
puts 'Game Saved!'
end
When I used this method mid-game I ended up with the following YAML file containing the instance variables at that point in the game:
---
- !ruby/object:Hangman
secret_word: sc---nshots
secret_word_immutable: screenshots
secret_word_display:
- _
- _
- r
- e
- e
- _
- _
- _
- _
- _
- _
guesses: 4
end_game: 0
wrong_letters_guessed: []
right_letters_guessed:
- e
- r
The problem occurs when I try to access the values of the saved instance variables (secret_word, secret_word_immutable, etc...) from the YAML file, and use them to update the initialized values of the variables in a new game.
My load_game method looks like this:
def load_game
yaml = YAML.load_file('saved_game.yml')
#secret_word = yaml.dig('secret_word')
#secret_word_immutable = yaml.dig('secret_word_immutable')
#secret_word_display = yaml.dig('secret_word_display')
#guesses = yaml.dig('guesses')
#end_game = yaml.dig('end_game')
#wrong_letters_guessed = yaml.dig('wrong_letters_guessed')
#right_letters_guessed = yaml.dig('right_letters_guessed')
puts "Saved game loaded!"
end
I have tried different methods of accessing the YAML values, including using bracket notation instead of #dig and using #fetch. I have also tried chaining these methods together and passing the instance variable in as a symbol or string. All methods produce type errors.
I think the problem might have something to do with the fact that the YAML file begins with:
- !ruby/object:Hangman
because while using 'p' to try to figure out what is going on I access the length, class, and value of 'yaml' and yaml[0] I get:
p yaml.length #=> 1
p yaml.class #=> Array
p yaml #=> #<Hangman:0x00007fa771234090>
p yaml[0] #=> #<Hangman:0x00007fa771234090 #secret_word="sc---nshots", #secret_word_immutable="screenshots", #secret_word_display=["_", "_", "r", "e", "e", "_", "_", "_", "_", "_", "_"], #guesses=4, #end_game=0, #wrong_letters_guessed=[], #right_letters_guessed=["e", "r"]>
Thank you so much!

YAML.dump([] << self, f) is pushing your object onto an empty array. It's an obfuscated way of writing YAML.dump([self], f).
Your object is the first element in that array. You'd access the first object.
#secret_word = yaml[0].dig('secret_word')
There doesn't seem to be a purpose to dumping a single element array here, dump just self: YAML.dump(self, f)
Then you can use it directly.
#secret_word = yaml.dig('secret_word')

Related

Syntax error, unexpected tIDENTIFIER, expecting ')' Ruby

I get the following error when running a simple method that takes in a proper noun string and returns the string properly capitalized.
def format_name(str)
parts = str.split
arr = []
parts.map do |part|
if part[0].upcase
else part[1..-1].downcase
arr << part
end
end
return arr.join(" ")
end
Test cases:
puts format_name("chase WILSON") # => "Chase Wilson"
puts format_name("brian CrAwFoRd scoTT") # => "Brian Crawford Scott"
The only possibility that the above code returns a blank output is because your arr is nil or blank. And the reason your arr is blank(yes it is blank in your case) because of this line of code:
if part[0].upcase
in which the statement would always return true, because with every iteration it would check if the first element of the part string can be upcased or not, which is true.
Hence, your else block never gets executed, even if this got executed this would have returned the same string as the input because you are just putting the plain part into the array arr without any formatting done.
There are some ways you can get the above code working. I'll put two cases:
# one where your map method could work
def format_name(str)
parts = str.split
arr = []
arr = parts.map do |part|
part.capitalize
end
return arr.join(" ")
end
# one where your loop code logic works
def format_name(str)
parts = str.split
arr = []
parts.map do |part|
arr << "#{part[0].upcase}#{part[1..-1].downcase}"
end
return arr.join(" ")
end
There are numerous other ways this could work. I'll also put the one I prefer if I am using just plain ruby:
def format_name(str)
str.split(' ').map(&:capitalize)
end
You could also read more about the Open Classes concept to put this into the String class of ruby
Also, checkout camelize method if you're using rails.

What does this ruby snippet do?

As a ruby newbie I am trying to understand a snippet of code in our baseline. Could someone please do that for me ? The snippet appears below
%w{word1 word2}.each { |att| define_method(att.to_sym) { return nil }}
In the context where this line will be run, two methods will be defined
def word1
return nil
end
def word2
return nil
end
For example
class MyClass
%w{word1 word2}.each { |att| define_method(att.to_sym) { return nil }}
end
After my_class.rb file will be loaded you will be able to consume generated methods
test = MyClass.new
test.word1
# or
test.word2
Like jdv said in the comments, for tutorials you might be better of on other websites. Here are all references needed to understand the piece of code provided:
Percent strings, used in %w{word1 word2}
Percent Strings
Besides %(...) which creates a String, the % may
create other types of object. As with strings, an uppercase
letter allows interpolation and escaped characters while a
lowercase letter disables them.
These are the types of percent strings in ruby:
%i: Array of Symbols
%q: String
%r: Regular Expression
%s: Symbol
%w: Array of Strings
%x: Backtick (capture subshell result)
For the two array forms of percent string, if you wish to
include a space in one of the array entries you must escape
it with a “\” character:
%w[one one-hundred\ one]
#=> ["one", "one-hundred one"]
If you are using “(”, “[”, “{”, “<” you must close it with
“)”, “]”, “}”, “>” respectively. You may use most other
non-alphanumeric characters for percent string delimiters
such as “%”, “|”, “^”, etc.
Array#each
each {|item| block} → ary
each → Enumerator
Calls the given block once for each element in self,
passing that element as a parameter. Returns the array
itself.
If no block is given, an Enumerator is returned.
a = [ "a", "b", "c" ]
a.each {|x| print x, " -- " }
produces:
a -- b -- c --
Module#define_method
define_method(symbol, method) → symbol
define_method(symbol) { block } → symbol
Defines an instance method in the receiver. The method
parameter can be a Proc, a Method or an UnboundMethod
object. If a block is specified, it is used as the method
body. This block is evaluated using instance_eval.
class A
def fred
puts "In Fred"
end
def create_method(name, &block)
self.class.define_method(name, &block)
end
define_method(:wilma) { puts "Charge it!" }
end
class B < A
define_method(:barney, instance_method(:fred))
end
a = B.new
a.barney
a.wilma
a.create_method(:betty) { p self }
a.betty
produces:
In Fred
Charge it!
#<B:0x401b39e8>
String#to_sym
to_sym → symbol
Returns the Symbol corresponding to str, creating the
symbol if it did not previously exist. See Symbol#id2name.
"Koala".intern #=> :Koala
s = 'cat'.to_sym #=> :cat
s == :cat #=> true
s = '#cat'.to_sym #=> :#cat
s == :#cat #=> true
This can also be used to create symbols that cannot be
represented using the :xxx notation.
'cat and dog'.to_sym #=> :"cat and dog"
%w{word1 word2} = creating an array that looks like this ['word1', 'word2']
.each = iterating through each value in the array
{} = this is a code block each value in the array will be run through this block
|attr| = block parameter. each value in the array will be placed here
define_method = define a method from the argument
(att.to_sym) = the name of the new method. this will be word1 and then word2
{ return nil } = the body of the new method
So what is happening is you are defining two new methods. One method called word1 and another called word2. Each of these methods will have a body of return nil. They will look like this:
def word1
return nil
end
def word2
return nil
end

Split text while reading line by line

I am trying to read a text file with contents like this
ABC = Thefirststep
XYZ = Secondstep
ABC_XYZ = Finalstep=345ijk!r4+
I am able to read the file line by line using this
#!/usr/bin/ruby
text = '/tmp/data'
f = File.open(text , "r")
f.each_line { |line|
puts line
}
f.close
What I want to do is have the values TheFirststep Secondstep and Finalstep assigned to separate variables. better if we use split().
You could use something like this:
#!/usr/bin/ruby
text = '/tmp/data'
data = []
f = File.open(text , "r")
f.each_line { |line|
data.push( line.split("=").last)
}
f.close
You said you want to, "have the values, 'TheFirststep', 'Secondstep and 'Finalstep' assigned to separate variables.
You cannot create local variables dynamically (not since Ruby v1.8 anyway). That leaves two choices: assign those values to instance variables or use a different data structure, specifically, a hash.
First let's create a data file.
data <=-END
ABC = Thefirststep
XYZ = Secondstep
ABC_XYZ = Finalstep=345ijk!r4+
END
FName = 'test'
File.write(FName, data)
#=> 73
Assign values to instance variables
File.foreach(FName) do |line|
var, value, * = line.chomp.split(/\s*=\s*/)
instance_variable_set("##{var.downcase}", value)
end
#abc
#=> "Thefirststep"
#xyz
#=> "Secondstep"
#abc_xyz
#=> "Finalstep"
The convention for the names of instance variables (after the "#") is to use snake-case, which is why I downcased them.
Store the values in a hash
File.foreach(FName).with_object({}) do |line,h|
var, value, * = line.chomp.split(/\s*=\s*/)
h[var] = value
end
#=> {"ABC"=>"Thefirststep", "XYZ"=>"Secondstep", "ABC_XYZ"=>"Finalstep"}
As easy as this was to do, it's not generally helpful to generate instance variables dynamically or hashes with dynamically created keys. That's because they are only useful if their values can be obtained and possibly changed, which is problematic.
Note that in
var, value, * = line.chomp.split(/\s*=\s*/)
var equals the first element of the array returned by the split operation, value is the second value and * discards the remaining elements, if any.

Subclassing Ruby Hash, object has no methods of Hash?

I'm creating a object of hash in order to write a little script that reads in a file a line at a time, and assigns arrays into my hash class. I get wildly different results depending if I subclass Hash or not, plus using super changes things which I don't' understand.
My main issue is that without subclassing hash ( < Hash) it works perfectly, but I get no methods of Hash (like to iterate over the keys and get things out of it.... Subclassing Hash lets me do those things, but it seems that only the last element of the hashed arrays is ever stored.... so any insight into how you get the methods of a subclass. The Dictionary class is a great example I found on this site, and does exactly what I want, so I'm trying to understand how to use it properly.
filename = 'inputfile.txt.'
# ??? class Dictionary < Hash
class Dictionary
def initialize()
#data = Hash.new { |hash, key| hash[key] = [] }
end
def [](key)
#data[key]
end
def []=(key,words)
#data[key] += [words].flatten
#data[key]
# super(key,words)
end
end
listData = Dictionary.new
File.open(filename, 'r').each_line do |line|
line = line.strip.split(/[^[:alpha:]|#|\.]/)
puts "LIST-> #{line[0]} SUB-> #{line[1]} "
listData[line[0]] = ("#{line[1]}")
end
puts '====================================='
puts listData.inspect
puts '====================================='
print listData.reduce('') {|s, (k, v)|
s << "The key is #{k} and the value is #{v}.\n"
}
If anyone understands what is going on here subclassing hash, and has some pointers, that would be excellent.
Running without explicit < Hash:
./list.rb:34:in `<main>': undefined method `reduce' for #<Dictionary:0x007fcf0a8879e0> (NoMethodError)
That is the typical error I see when I try and iterate in any way over my hash.
Here is a sample input file:
listA billg#microsoft.com
listA ed#apple.com
listA frank#lotus.com
listB evanwhite#go.com
listB joespink#go.com
listB fredgrey#stop.com
I can't reproduce your problem using your code:
d = Dictionary.new #=> #<Dictionary:0x007f903a1adef8 #data={}>
d[4] << 5 #=> [5]
d[5] << 6 #=> [6]
d #=> #<Dictionary:0x007f903a1adef8 #data={4=>[5], 5=>[6]}>
d.instance_variable_get(:#data) #=> {4=>[5], 5=>[6]}
But of course you won't get reduce if you don't subclass or include a class/module that defines it, or define it yourself!
The way you have implemented Dictionary is bound to have problems. You should call super instead of reimplementing wherever possible. For example, simply this works:
class Dictionary < Hash
def initialize
super { |hash, key| hash[key] = [] }
end
end
d = Dictionary.new #=> {}
d['answer'] << 42 #=> [42]
d['pi'] << 3.14 #=> [3.14
d #=> {"answer"=>[42], "pi"=>[3.14]}
If you want to reimplement how and where the internal hash is stored (i.e., using #data), you'd have to reimplement at least each (since that is what almost all Enumerable methods call to) and getters/setters. Not worth the effort when you can just change one method instead.
While Andrew Marshall's answer
already correct, You could also try this alternative below.
Going from your code, We could assume that you want to create an object that
act like a Hash, but with a little bit different behaviour. Hence our first
code will be like this.
class Dictionary < Hash
Assigning a new value to some key in the dictionary will be done differently
in here. From your example above, the assignment won't replace the previous
value with a new one, but instead push the new value to the previous or to
a new array that initialized with the new value if the key doesn't exist yet.
Here I use the << operator as the shorthand of push method for Array.
Also, the method return the value since it's what super do (see the if part)
def []=(key, value)
if self[key]
self[key] << value
return value # here we mimic what super do
else
super(key, [value])
end
end
The advantage of using our own class is we could add new method to the class
and it will be accessible to all of it instance. Hence we need not to
monkeypatch the Hash class that considered dangerous thing.
def size_of(key)
return self[key].size if self[key]
return 0 # the case for non existing key
end
Now, if we combine all above we will get this code
class Dictionary < Hash
def []=(key, value)
if self[key]
self[key] << value
return value
else
super(key, [value])
end
end
def size_of(key)
return self[key].size if self[key]
return 0 # the case for non existing key
end
end
player_emails = Dictionary.new
player_emails["SAO"] = "kirito#sao.com" # note no << operator needed here
player_emails["ALO"] = "lyfa#alo.com"
player_emails["SAO"] = "lizbeth#sao.com"
player_emails["SAO"] = "asuna#sao.com"
player_emails.size_of("SAO") #=> 3
player_emails.size_of("ALO") #=> 1
player_emails.size_of("GGO") #=> 0
p listData
#=> {"SAO" => ["kirito#sao.com", "lizbeth#sao.com", "asuna#sao.com"],
#=> "ALO" => ["lyfa#alo.com"] }
But, surely, the class definition could be replaced with this single line
player_emails = Hash.new { [] }
# note that we wont use
#
# player_emails[key] = value
#
# instead
#
# player_emails[key] << value
#
# Oh, if you consider the comment,
# it will no longer considered a single line
While the answer are finished, I wanna comment some of your example code:
filename = 'inputfile.txt.'
# Maybe it's better to use ARGF instead,
# so you could supply the filename in the command line
# and, is the filename ended with a dot? O.o;
File.open(filename, 'r').each_line do |line|
# This line open the file anonimously,
# then access each line of the file.
# Please correct me, Is the file will properly closed? I doubt no.
# Saver version:
File.open(filename, 'r') do |file|
file.each_line do |line|
# ...
end
end # the file will closed when we reach here
# ARGF version:
ARGF.each_line do |line|
# ...
end
# Inside the each_line block
line = line.strip.split(/[^[:alpha:]|#|\.]/)
# I don't know what do you mean by that line,
# but using that regex will result
#
# ["listA", "", "", "billg#microsoft.com"]
#
# Hence, your example will fail since
# line[0] == "listA" and line[1] == ""
# also note that your regex mean
#
# any character except:
# letters, '|', '#', '|', '\.'
#
# If you want to split over one or more
# whitespace characters use \s+ instead.
# Hence we could replace it with:
line = line.strip.split(/\s+/)
puts "LIST-> #{line[0]} SUB-> #{line[1]} "
# OK, Is this supposed to debug the line?
# Tips: the simplest way to debug is:
#
# p line
#
# that's all,
listData[line[0]] = ("#{line[1]}")
# why? using (), then "", then #{}
# I suggest:
listData[line[0]] = line[1]
# But to make more simple, actually you could do this instead
key, value = line.strip.split(/\s+/)
listData[key] = value
# Outside the block:
puts '====================================='
# OK, that's too loooooooooong...
puts '=' * 30
# or better assign it to a variable since you use it twice
a = '=' * 30
puts a
p listData # better way to debug
puts a
# next:
print listData.reduce('') { |s, (k, v)|
s << "The key is #{k} and the value is #{v}.\n"
}
# why using reduce?
# for debugging you could use `p listData` instead.
# but since you are printing it, why not iterate for
# each element then print each of that.
listData.each do |k, v|
puts "The key is #{k} and the value is #{v}."
end
OK, sorry for blabbering so much, Hope it help.

What does << mean in Ruby?

I have code:
def make_all_thumbs(source)
sizes = ['1000','1100','1200','800','600']
threads = []
sizes.each do |s|
threads << Thread.new(s) {
create_thumbnail(source+'.png', source+'-'+s+'.png', s)
}
end
end
what does << mean?
It can have 3 distinct meanings:
'<<' as an ordinary method
In most cases '<<' is a method defined like the rest of them, in your case it means "add to the end of this array" (see also here).
That's in your particular case, but there are also a lot of other occasions where you'll encounter the "<<" method. I won't call it 'operator' since it's really a method that is defined on some object that can be overridden by you or implemented for your own objects. Other cases of '<<'
String concatenation: "a" << "b"
Writing output to an IO: io << "A line of text\n"
Writing data to a message digest, HMAC or cipher: sha << "Text to be hashed"
left-shifting of an OpenSSL::BN: bn << 2
...
Singleton class definition
Then there is the mysterious shift of the current scope (=change of self) within the program flow:
class A
class << self
puts self # self is the singleton class of A
end
end
a = A.new
class << a
puts self # now it's the singleton class of object a
end
The mystery class << self made me wonder and investigate about the internals there. Whereas in all the examples I mentioned << is really a method defined in a class, i.e.
obj << stuff
is equivalent to
obj.<<(stuff)
the class << self (or any object in place of self) construct is truly different. It is really a builtin feature of the language itself, in CRuby it's defined in parse.y as
k_class tLSHFT expr
k_class is the 'class' keyword, where tLSHFT is a '<<' token and expr is an arbitrary expression. That is, you can actually write
class << <any expression>
and will get shifted into the singleton class of the result of the expression. The tLSHFT sequence will be parsed as a 'NODE_SCLASS' expression, which is called a Singleton Class definition (cf. node.c)
case NODE_SCLASS:
ANN("singleton class definition");
ANN("format: class << [nd_recv]; [nd_body]; end");
ANN("example: class << obj; ..; end");
F_NODE(nd_recv, "receiver");
LAST_NODE;
F_NODE(nd_body, "singleton class definition");
break;
Here Documents
Here Documents use '<<' in a way that is again totally different. You can define a string that spans over multiple lines conveniently by declaring
here_doc = <<_EOS_
The quick brown fox jumps over the lazy dog.
...
_EOS_
To distinguish the 'here doc operator' an arbitrary String delimiter has to immediately follow the '<<'. Everything inbetween that initial delimiter and the second occurrence of that same delimiter will be part of the final string. It is also possible to use '<<-', the difference is that using the latter will ignore any leading or trailing whitespace.
Mostly used in arrays to append the value to the end of the array.
a = ["orange"]
a << "apple"
puts a
gives this ["orange", "apple"] result.
'a << b' means append b to the end of a
It's the operator which allows you to feed existing arrays, by appending new items.
In the example above you are just populating the empty array threads with 5 new threads.
In ruby you always have more the one way to do things. So, Ruby has some nice shortcuts for common method names. like this one is for .push instead of typing out the .push method name, you can simply use <<, the concatenation operator. in fact in some cases you can use any of these for the same operation .push and + with <<.
Like you can see in this example:
alphabet = ["a", "b", "c"]
alphabet << "d" # Update me!
alphabet.push("e") # Update me!
print alphabet
caption = "the boy is surrounded by "
caption << "weezards!" # Me, too!
caption += " and more. " # Me, too!
# .push can no be uses for concatenate
print caption
so you see the result is:
["a", "b", "c", "d", "e"]
the boy is surrounded by weezards! and more.
you can use the operator << to push a element into an array or to concatenate a string to another.
so, what this is this doing is creating a new element/object Thread type and pushing it into the array.
threads << Thread.new(s) {
create_thumbnail(source+'.png', source+'-'+s+'.png', s)
}
In ruby '<<' operator is basically used for:
Appending a value in the array (at last position)
[2, 4, 6] << 8
It will give [2, 4, 6, 8]
It also used for some active record operations in ruby. For example we have a Cart and LineItem model associated as cart has_many line_items. Cart.find(A).line_items will return ActiveRecord::Associations object with line items that belongs to cart 'A'.
Now, to add (or say to associate) another line_item (X) to cart (A),
Cart.find(A).line_items << LineItem.find(X)
Now to add another LineItem to the same cart 'A', but this time we will not going to create any line_item object (I mean will not create activerecord object manually)
Cart.find(A).line_items << LineItem.new
In above code << will save object and append it to left side active record association array.
And many others which are already covered in above answers.
Also, since Ruby 2.6, the << method is defined also on Proc.
Proc#<< allows to compose two or more procs.
It means add to the end (append).
a = [1,2,3]
a << 4
a = [1,2,3,4]

Resources