Creating objects from YAML, already initialized constant - ruby

Two problems, that probably are related:
I'm retreiving a number of 'persons' from a YAML-file to an array, and now i'm trying to create classes from that array.
These objects are then to placed in a new array.
It actually works out fine, if you dont consider the fact that the object added last replaces all the previously added.
In my case i get five identical copies of object #5, where i rather like to see five different ones.
My guess is that the error results somewhere in my iterator to get all the 'persons' from the YAML.
I'm getting a cuople of warnings, regarding the 're-use' of constants:
NEWSTR and NEWAL.
getPost = 0
loopa = 0
while loopa < personsInYAML
NEWSTR = TEST.fetch(getPost)
NEWAL = NEWSTR.split(' ')
getPost+=1
puts "*****************************************"
nyloop = loopa+1
puts "PERSON: " + nyloop.to_s + " name: " + NEWAL.fetch(1)
nameToArray = Person.new
outputArray.insert(loopa, nameToArray)
loopa+=1
end
Persons-class
class Person
def initialize
#name
#age
#length
#weight
#misc
end
def name
name = NEWAL.fetch(1)
return name
end
if NEWAL.include?("age:")
def age
x = NEWAL.index("age:")+1
age = NEWAL.fetch(x)
return age
end
end
if NEWAL.include?("length:")
def length
x = NEWAL.index("length:")+1
length = NEWAL.fetch(x)
return length
end
end
if NEWAL.include?("weight:")
def weight
x = NEWAL.index("weight:")+1
weight = NEWAL.fetch(x)
return weight
end
end
if NEWAL.include?("misc:")
def misc
x = NEWAL.index("misc:")+1
misc = NEWAL.fetch(x)
return misc
end
end
end

You're taking the wrong approach to populating your Person class. The only thing your loop is doing is to create brand new Person classes and stick them in an array. It isn't actually initializing the person class at all.
It looks like what you are trying to do is to use a constant (which you don't hold constant) to pass information to the Person class. However, the code that you have in your Person class that is outside of the methods is only going to be run once - when the class loads for the first time, NOT at the time that you make a new Person.
You'd be better off changing your initialize method to take some arguments, and to create the class with appropriate arguments within the loop.
def initialize(name, age = nil, length = nil, weight = nil, misc = nil)
# assign instance variables here
#name = name
...
end
You appear to be trying to create dynamic accessors to the instance variables. This doesn't make a whole lot of sense. Just define accessors on all of them, and handle the case where the instance variables are nil in whatever code is calling the Person class.

Related

Not displaying it's corresponding values with it's key for Hash

Ok i am not here to ask for an answer. But to be honest i am not really good in class variable. So i would appreciate you can guide me along with this piece of code.
I have read on class variable at those docs. I some what kind of understand it. But it comes to applying it for my own use. I would get confused.
class Square
##sqArray = {}
#attr_accessor :length
def initialize
if defined?(##length)
randno = "%s" % [rand(20)]
##length = randno.to_i
##sqArray = ##length
else
randno = "%s" % [rand(20)]
##length = randno.to_i
##sqArray = ##length
end
end
def Area
##area = ##length * ##length
return ##area
##sqArray[##length.to_sym] = ##area
puts ##sqArray
end
end
s1 = Square.new
puts s1.Area
Let me explain this piece of code. Basically every time i create a Square object it would go to initialize method. A random number will be generated and pass it to ##length, and ##length will be assigned to hash ##sqArray as it's key. But now the problem is when i create a new object s1. When i want to display the Area i want to test out to print the hash ##sqArray with it's length as it's key and area as it's value. But now the problem is only returning it's area only. e.g 114 only.
suppose to be e.g [ 24 => 114]
When defining the object's property (i.e. it's length), the correct approach is to use an instance variable, not a class variable. This is because (in your particular example), length is an attribute of a specific square and not something that applies to all squares. Your code should look something like this:
class Square
def initialize(length = rand(20))
#length = length
end
def area
#length * #length
end
end
s1 = Square.new
puts s1.area
Now, I am a little unclear what exactly you aim to achieve by use of that class variable ##sqArray - but for example, you could use this store a list of all defined Squares:
class Square
##squares_list = []
def self.all_known
##squares_list
end
def initialize(length = rand(20))
#length = length
##squares_list << self
end
def area
#length * #length
end
end
This would allow you to write code like:
s1 = Square.new #=> #<Square:0x0000000132dbc8 #length=9>
s2 = Square.new(20) #=> #<Square:0x000000012a1038 #length=20>
s1.area #=> 81
s2.area #=> 400
Square.all_known #=> [#<Square:0x0000000132dbc8 #length=9>, #<Square:0x000000012a1038 #length=20>]
Class variables have some odd behaviour and limited use cases however; I would generally advise that you avoid them when starting out learning Ruby. Have a read through a ruby style guide to see some common conventions regarding best practice - including variable/method naming (use snake_case not camelCase or PascalCase), whitespace, etc.

Dynamically creating objects in Ruby

I have a class whose initialize method defines a few instance variables and does some calculations. I need to create about 60 objects of that class. Each object has an ID number at the end. E.g.:
object1 = Dynamic.new(x, y)
object2 = Dynamic.new(x, y)
object3 = Dynamic.new(x, y)
...
I could just define them all by hand, but that would be quite inefficient. Is there any way to dynamically create each object?
You can always make a loop and push all the objects into an array. An array position might also be needed for knowing which object is each. This isn't quite what you wanted (atleast I don't think so), but it should suffice.
class Dynamic
##instances_of_class = 0
def initialize(x,y)
#...
#array_position = ##instances_of_class
##instances_of_class += 1
end
end
ary = []
50.times do
ary << Dynamic.new(x,y)
end
Edit: This solution, as said in the comments, can cause bugs if you change the array, so here's an alternate solution.
require 'File.rb'
i = 1
varFile = File.open("File.rb","a+")
50.times do
varFile.puts "variable#{i} = Object.new"
i += 1
end
Inside of File.rb will be 50 uniquely named variables that you can use.
I would be curious to know why you need this. It's an unusual requirement, and often that means that you can avoid the problem instead of solving it. I think TheLuigi's solution would work, but if you use a class variable then these Id's will be shared across multiple classes. You can instead use an instance variable, with something like the following:
class A
def self.next_id
#id ||= 0 ; #id += 1
end
def initialize
#id = A.next_id
end
end
A.new
# => #<A:0x007fd6d414c640 #id=1>
A.new
# => #<A:0x007fd6d41454a8 #id=2>
If you just want sixty objects accessible from a variable, you should have them in an array referred to by a single variable.
objects = Array.new(60){Dynamic.new(x, y)}
Your object1, object2, ... will correspond to objects[0], objects[1], ... respectively.

Passing variables/parameters from one Ruby class to a loaded Ruby program

The below combined programs should ask for a number remove the first digit (lets call this new number x) and then compute x % 7. Ex: (1121546 % 7) = 5
This all appears to be working except that the number entered in will always compute to 0. modulo_7.rb works by itself and will print the correct outcome when passed a parameter.
The question is am I not passing the variables/ parameters properly or is there something else that is getting in the way?
class Number_check_interface
def get_cert_number
print "You are about to check receive the check number for a policy/cert id."
#cert_id = nil
until #cert_id.is_a?(Fixnum) do
puts " Enter the policy/cert ID. "
begin
#cert_id = Integer(gets)
rescue ArgumentError
end
end
end
end
class Run_number_check_interface
def run
load 'modulo_7.rb'
n = Number_check_interface.new
n.get_cert_number
checking_policy_number = Get_policy_check_digit.new(#cert_id)
checking_policy_number.create_check_digit
end
end
run = Run_number_check_interface.new
run.run
modulo_7.rb
This program removes the first digit (index 0) of a 7 digit number and returns the difference 7%18 is 4 since 4 is remainder of how many times 7 can fit into 18.
class Get_policy_check_digit
def initialize(cert_id)
#instance variable
#cert = cert_id
end
def create_check_digit
cert_id_6 = #cert.to_s
cert_id_6.slice!(0)
puts cert_id_6
check_digit = cert_id_6.to_i % 7
puts "Your check digit is #{check_digit}"
end
end
# n = Get_policy_check_digit.new(1121546) When uncommented this will create a new instance
# of Get_policy_check_digit with the parameter 1121546
# n.create_check_digit This will run the method create_check_digit with the
# parameter 1121546
Instance variables are scoped to an individual instance of a class. So when you say #cert_id = Integer(gets) inside Number_check_interface, you are only setting #cert_id for that particular instance of Number_check_interface. Then, when you later write Get_policy_check_digit.new(#cert_id) inside Run_number_check_interface, you are referring to an entirely different #cert_id, one which is specific to that particular instance of Run_number_check_interface, and not to the Number_check_interface you stored in n earlier.
The simple solution is to return #cert_id from Number_check_interface#get_cert_number, and then pass the returned value to Get_policy_check_digit.new:
class Number_check_interface
def get_cert_number
print "You are about to check receive the check number for a policy/cert id."
#cert_id = nil # This is an instance variable. It's only accessible
# from inside this instance of `Number_check_interface`
until #cert_id.is_a?(Fixnum) do
puts " Enter the policy/cert ID. "
begin
#cert_id = Integer(gets)
rescue ArgumentError
end
end
return #cert_id # Return the contents of #cert_id
end
end
class Run_number_check_interface
def run
load 'modulo_7.rb'
n = Number_check_interface.new
cert_number = n.get_cert_number # Here we call `get_cert_number` and
# set `cert_number` to it's return
# value.
# Notice how we use `cert_number` here:
checking_policy_number = Get_policy_check_digit.new(cert_number)
checking_policy_number.create_check_digit
end
end
Other tips:
Convention in Ruby is to name classes with CamelCase.
Require dependencies at the top of your files, not in the middle of method calls.
Unless you have a very good reason not to, use require, not load
You might want to think a bit harder about what purpose these classes serve, and what behavior they are intending to encapsulate. The API seems a bit awkward to me right now. Remember, tell, don't ask.
Why are these separate classes? The design here seems strange, is this ported from another language? It's more complicated than it needs to be. Without changing your structure, here's what's wrong:
In Run_number_check_interface you are reading #cert_id, it doesn't have an instance variable named that, but Number_check_interface does. Just return it from get_cert_number:
class Number_check_interface
def get_cert_number
print "You are about to check receive the check number for a policy/cert id."
cert_id = nil
until cert_id.is_a?(Fixnum) do
puts " Enter the policy/cert ID. "
begin
cert_id = Integer(gets)
rescue ArgumentError
end
end
cert_id # <-- returing the value here
end
end
class Run_number_check_interface
def run
load 'modulo_7.rb'
n = Number_check_interface.new
cert_id = n.get_cert_number # <-- saving here
checking_policy_number = Get_policy_check_digit.new(cert_id) # <-- no longer an ivar
checking_policy_number.create_check_digit
end
end
run = Run_number_check_interface.new
run.run

Can I use methods for String class within the String class in Ruby?

My new method for a string object in ruby is supposed to return a hash of the count of each character within a string (loaded in from a .txt file) and I am probably trying to go about it the easy way, however I can't seem to make it work without passing the object. I was wondering if there was a way to do this without passing a string. Any help would be appreciated.
Here is my code
class String
def frequency
Object.downcase
Object.gsub("\n", " ")
h = {}
h["A:"] = Object.count('a')
h["B:"] = Object.count('b')
h["C:"] = Object.count('c')
h["D:"] = Object.count('d')
h["E:"] = Object.count('e')
h["F:"] = Object.count('f')
h["G:"] = Object.count('g')
h["H:"] = Object.count('h')
h["I:"] = Object.count('i')
h["J:"] = Object.count('j')
h["K:"] = Object.count('k')
h["L:"] = Object.count('l')
h["M:"] = Object.count('m')
h["N:"] = Object.count('n')
h["O:"] = Object.count('o')
h["P:"] = Object.count('p')
h["Q:"] = Object.count('q')
h["R:"] = Object.count('r')
h["S:"] = Object.count('s')
h["T:"] = Object.count('t')
h["U:"] = Object.count('u')
h["V:"] = Object.count('v')
h["W:"] = Object.count('w')
h["K:"] = Object.count('x')
h["Y:"] = Object.count('y')
h["Z"] = Object.count('z')
return h
end
end
Sounds like you are talking about self, which is the ruby keyword that refers to the current object. Note that self is implied if you just call the method. So to use your example
class String
def frequency
count('a')
end
end
would return the number of as in the string
"asdfa".frequency #=> 2
Just a note, but your current method is very repetitive, and you might want to think about taking advantage of a loop to reduce the amount of code. Also you are not counting capital letters :)
Rather than a very long, un-DRY method that iterates your object 26 times, how about using some Ruby:
def frequency
Hash[downcase.gsub(/[^a-z]/,'').chars.group_by(&:to_s).map{|char, group| ["#{char.upcase}:", group.size]}]
end
You can break this apart onto separate lines if you find it easier to read (and to look up the methods in the API [1]):
def frequency
intermediate_variable = downcase
intermediate_variable = intermediate_variable.gsub(/[^a-z]/,'') # only keep a-z characters
intermediate_variable = intermediate_variable.chars.group_by(&:to_s) # split the string into its component characters and then group that array by the characters (run this on an array to see what it does :-) could also have written it `.group_by{|e|e}`
intermediate_variable = intermediate_variable.map{|char, group| ["#{char.upcase}:", group.size]} # map the array of grouped characters into an array of character and counts (formatting the 'character' how you would like your hash key configured
Hash[intermediate_variable] # make a hash of the characters and their counts
end
[1] http://ruby-doc.org/core-2.0.0/Enumerable.html http://ruby-doc.org/core-2.0.0/String.html
Here is the version I used which is a complete copy of the Rosetta Letter Frequency:
#!/usr/bin/env ruby
def letter_frequency(string)
freq = Hash.new(0)
string.each_char.lazy.grep(/[[:alpha:]]/).map(&:upcase).each_with_object(freq) do |char, freq_map|
freq_map[char] += 1
end
end
In ruby you can just open the class and add the method, like:
class String
def my_method
my_method_code
end
end
Then you just call the method string.my_method. However in your case I would rather use a Ruby module. Here is a code sample, very similar to a class but cleaner imho:
#!/usr/bin/env ruby
module MyString
def self.letter_frequency(string)
freq = Hash.new(0)
string.each_char.lazy.grep(/[[:alpha:]]/).map(&:upcase).each_with_object(freq) do |char, freq_map|
freq_map[char] += 1
end
return freq
end
end
p MyString.letter_frequency('absd')
Modules are more suited for adding your own classes into projects avoiding name colliding and creating mixins.
I would just create a hash like this:
class String
def frequency
chars.each_with_object(Hash.new(0)) do |char, h|
h["#{char.upcase}:"] += 1 if char[/[[:alpha:]]/]
end
end
end

ruby recursive loop with sub-block return design

I have a Ruby application that I'm developing that, for some reason, does not work as expected when using a recursive function that contains a block inside to return a value from a different class' function call (easier to see in the example code below). The odd thing is that when I created a minimal sample to try and find out what was going on, the sample works as expected. Example:
require 'json'
class Simple
attr_accessor :name, :children
def initialize(name,children=nil)
#name = name
#children = children
end
end
a = Simple.new('A')
b = Simple.new('B',[a])
c = Simple.new('C',[b])
d = Simple.new('D')
e = Simple.new('E',[d])
f = Simple.new('F')
g = Simple.new('G',[e,f])
foo = [c,e,g]
def looper(d)
holder = nil
d.each do |item|
# puts item.name
if item.name == 'D'
holder = Simple.new('Z',[])
elsif !item.children.nil?
holder = looper(item.children)
end
end
return holder
end
bar = looper(foo)
puts "Returned from looper: #{bar.name}"
In my actual code I ended up using the a class instance variable to get the response (which also works in the sample code). Example snippet of the function from above modified to the other pattern:
def looper(d)
holder = nil
d.each do |item|
# puts item.name
if item.name == 'D'
#holder = Simple.new('Z',[])
elsif !item.children.nil?
looper(item.children)
end
end
#holder
end
So my question is, is it good practice to use the instance variable? Any down sides to doing so since it works for my actual code whereas the first example pattern does not?
In your first piece of code, I'd expect to see a nil from your input, where in your second version you will get the object Simple.new('Z',[])
If that's your problem, it's because item g has children, the first one will set a value recusrively, but the second one will unset the value, so the second time through the loop, holder gets set to nil.
Edit: Actually my analysis of the results form the example above is wrong, because the last item in the top level list does contain the searched-for item. However, the analysis of the problem and difference in behaviour between the two solutions still holds in general.
You probably just want this:
def looper(d)
holder = nil
d.each do |item|
# puts item.name
if item.name == 'D'
holder = Simple.new('Z',[])
break
elsif !item.children.nil?
holder = looper(item.children)
break if holder
end
end
return holder
end
The break statement prevents you assigning again to holder . . .
Alternatively use Ruby internals like #first or #any? to express your search.
Assigning to instance variables as per your second example to inter-communicate between recursions is just fine, they are effectively shared state between all depths and iterations while the recursion runs. So it doesn't break recursion or Ruby per se, although you have to watch out for other kinds of unwanted interaction: For example you cannot assume an instance variable was set in a particular place during recursion, compared to current depth or loop item . . .

Resources