I am trying to place 3 values into each index of an array. The values are for the class, assignment, and grade. They come from a class object called Grade_Log.
I am running into an issue with the output.
class Grade_Log
attr_accessor :which_class, :assignment_type, :grade
def initialize(which_class, assignment_type, grade)
#which_class = which_class
#assignment_type = assignment_type
#grade = grade
end
end
#values are assigned to add_class, add_assignment, and add_grade
grade_index[grade_index_tally] = Grade_Log.new(add_class,add_assignment,add_grade)
puts grade_index[grade_index_tally]
I would like for it to output, for example, "PHYSICS, HOMEWORK, 95", but instead I am getting the following. #<Grade_Log:0x0000000002baaa20>
If you want to be able to puts an instance of your Grade_Log class, you'll need to define to_s for your class. Right now, it's likely using the default to_s method.
I think you'd want something like this:
class Grade_Log
... # your code
def to_s
"#{which_class}, #{assignment_type}, #{grade}"
end
end
Which is basically the same thing as:
def to_s
[which_class, assignment_type, grade].join(', ')
end
When you instantiate a Grade_Log object, the initialize method is called and the parameters are assigned to attributes:
tmp = GradeLog.new("PHYSICS", "HOMEWORK", 95)
If you simply puts the variable you get the object information:
puts tmp #=> #<Grade_Log:0x007fc27213f3a8>
To access the attributes you should do
puts tmp.which_class #=> PHYSICS
puts tmp.assignment_type #=> HOMEWORK
puts tmp.grade #=> 95
A fast way to see the object content is the inspect method:
puts tmp.inspect #=> #<Grade_Log:0x007fd7e6917358 #which_class="PHYSICS", #assignment_type="HOMEWORK", #grade=95>
which is the same as using Kernel#p:
p tmp #=> #<Grade_Log:0x007fd7e6917358 #which_class="PHYSICS", #assignment_type="HOMEWORK", #grade=95>
Another option is to access the instance variables, Object.html#instance_variables and Object.html#instance_variable_get:
tmp.instance_variables.map { |v| tmp.instance_variable_get(v) } #=> ["PHYSICS", "HOMEWORK", 95]
Or define a custom method inside your class, to get the array of values, for example (no need to edit the method when a new attribute is added):
def attributes_values
instance_variables.map { |v| instance_variable_get(v) }
end
So you can call just
tmp.attributes_values #=> ["PHYSICS", "HOMEWORK", 95]
Finally, if you want format the output as a string you could define a to_s method to override the default Object#to_s method, to call puts tmp.
Related
How's it possible in ruby ?
class Test
# Creating singleton method
def self.some_singleton_method(param1)
puts param1
end
end
# calling singleton method by creating method on fly as a parameter to it
Test.some_singleton_method def method_name(some_param)
# do something
end
## method_name
I've tried many places looking around, can't come up with an idea how's it's working.
Thanks!
It is possible, since def is keyword, that creates new method in current scope, which is Object since you're calling it on the "top" level, i.e. not inside any class. Starting from Ruby 2.1, def returns method name as a symbol, so your code is actually equivalent to
name = def method_name(some_param)
// do something
end
Test.some_singleton_method(name) # outputs "method_name"
EDIT: Thanks to Cary Swoveland for clarification that def is actually a keyword and not a method.
Here are two ways to do that.
#1
class Test
def self.doit(m)
send(m) yield
end
end
Test.doit(:hello) do
puts 'hi'
end
#=> :hello
Test.new.hello
#=> "hi"`.
#2
class Test
def self.doit(str)
eval(str)
end
end
Test.doit "def hello; puts 'hi'; end"
#=> :hello
Test.new.hello
#=> "hi"`.
What is best practice / syntax for trying to extract internal methods within a class?
class Foo
def initialize
end
def get_value
array = (API CALL TO GET ARRAY)
array.array_lookup("Bar")
end
def array_lookup(query)
self.each do |hash|
if hash[:key] == query
p hash[:value]
end
end
end
end
foo = Foo.new
foo.get_value #=> : undefined method `array_lookup' for #<Array:0x007fd3a49a2ca0 (NoMethodError)
The error message is telling me that my array object doesn't know how to respond to my method which makes sense in that I have an array that doesn't have this method, though I'm wondering how to fix this and similar uses. Do I overwrite the array class? Do I change my self.syntax?
array_lookup is Foo's method. So inside Foo class, you can call it by
array_lookup("Bar")
(without array.)
How about something like this? You turn your custom object into a subclass of Array so you get the array methods like #each. Actually, come to think of it, a better implementation might include mixing in the Enumerable module into your custom class (thinking composition over inheritance).
class Foo < Array
# More robust to change in application if you allow passing
# the query into this method. Just a suggestion.
def get_value(query)
request_data
lookup(query)
end
protected
def request_data
# API call to get data, assume this is array with contents
data = []
# Set contents of this object to contents of returned array
replace(data)
end
def lookup(query)
each do |hash|
if hash[:key] == query
puts hash[:value]
end
end
end
end
foo = Foo.new
foo.get_value("BAR")
I need to make a program in ruby to generate a robot name like KU765 or NG274 style
and to store them and check it to avoid repetition.
I also need to make a "reset" method to delete all stored names and start again.
This program is not working for some reason. I hope somebody helps me to find the mistake.
Thanks a lot.
class Robot
attr_accessor :named , :stored_names , :rl
def self.name
new.name
end
##rl = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def name
named = ""
named << ##rl[rand(26).to_i]
named << ##rl[rand(26).to_i]
named << rand(100..999).to_s
named.save_name
named.check_name
end
def save_name
stored_names = []
stored_names << named
end
def check_name
stored_names.uniq!
end
def reset
stored_names = Array.new
end
end
Here's another way to construct the Robot class that you may wish to consider. (My answers are not normally this long or detailed, but I wrote this in part to clarify aspects of Ruby's object model in my own mind. I hope it might help others do the same.)
Code
PREFACE = ('A'..'Z').to_a << ?_
SUFFIX = ('0'..'9').to_a
PREFACE_SIZE = 2
SUFFIX_SIZE = 3
class Robot
def self.reset() #bots = [] end
reset
def self.new() (#bots << super).last end
def self.bots() #bots end
def self.delete(bot) #bots.delete(bot) end
def self.bot_names() #bots.map { |b| b.name } end
attr_reader :name
def initialize() #name = add_name end
private
def add_name
loop do
#name = gen_name
return #name unless self.class.bot_names.include?(#name)
end
end
def gen_name
PREFACE.sample(PREFACE_SIZE).join << SUFFIX.sample(SUFFIX_SIZE).join
end
end
Example
Robot.bots #=> []
robbie = Robot.new #=> #<Robot:0x000001019f4988 #name="AP436">
robbie.name #=> "AP436"
Robot.bots #=> [#<Robot:0x000001019f4988 #name="AP436">]
r2d2 = Robot.new #=> #<Robot:0x000001019cd450 #name="KL628">
r2d2.name #=> "KL628"
Robot.bots #=> [#<Robot:0x000001019f4988 #name="AP436">,
# #<Robot:0x000001019cd450 #name="KL628">]
Robot.bot_names #=> ["AP436", "KL628"]
Robot.delete(robbie) #=> #<Robot:0x000001019f4988 #name="AP436">
Robot.bots #=> [#<Robot:0x000001019cd450 #name="KL628">]
Robot.bot_names #=> ["KL628"]
Robot.reset #=> []
c3po = Robot.new #=> #<Robot:0x000001018ff8c0 #name="VO975">
Robot.bots #=> [#<Robot:0x000001018ff8c0 #name="VO975">]
Explanation
When the class is parsed, the class method reset is first created, then the line reset is executed. As self => Robot when that occurs, the class method reset is executed, initializing #bots to an empty array.
The responsibility for saving and modifying a list of instances of Robot lies with the class. This list is held in the class instance variable #bots.
Instance of Robot are created by invoking Robot::new, which allocates memory and then invokes the (private) instance method initialize. Where is new? Since we have not defined it as a class method in Robot, there are two possibilities: it is inherited from one of Robot's ancestors (Robot.ancestors => [Robot, Object, Kernel, BasicObject]) or it is an instance method of the class Class, as that is the class for which Robot is an instance (i.e., Robot.class => Class) Let's find out which: Class.instance_method(:new) => #<UnboundMethod: Class#new> (or Class.instance_methods.include?(:new) => true), Object.method(:new) => #<Method: Class#new>. It's both! But that makes sense, because all classes are instances of Class, including Robot's superclass, Object. #<Method: Class#new> returned by Object.method(:new) shows new is defined in Class (which can alternatively be seen with Robot.method(:new).owner => Class. Very cool, eh? If you didn't know this already, and can follow what I've said in this paragraph, you've just learned the essence of Ruby's object model!
Suppose we add the class method new, shown below, to Robot. super invokes the class method Object::new (which is the instance method Class#new), passing any arguments of new (here there aren't any). Object::new returns the instance that it creates, which Robot::new in turn returns. Therefore, this method would simply be a conduit and and have no effect on the results.
def self.new
super
end
We can make a small change to the above method to add a copy of the instance that is created by Object::new to the array #bots:
def self.new
instance = super
#bots << instance
instance
end
I have written this a little more compactly as:
def self.new
(#bots << super).last
end
I've used the method Array#sample to randomly draw PREFACE_SIZE characters from PREFACE and SUFFIX_SIZE characters from SUFFIX_SIZE. sample samples without replacement, so you will not get, for example, "UU112". If you want to sample with replacement, replace the method gen_name with the following:
def gen_name
str = PREFACE_SIZE.times.with_object('') { |_,s| s << PREFACE.sample }
SUFFIX_SIZE.times { str << SUFFIX.sample }
str
end
I have created a class method bots to return the value of the class instance variable #bots. This could alternatively be done by defining a read accessor for #bots on Robots' singleton class:
class << self
attr_reader :name
end
When Robot.reset is invoked, what happens to all the instances of Robot it had contained? Will they be left to wander the forest, rejected and homeless? In languages like C you need to release their memory before casting them aside. In Ruby and many other modern languages that's not necessary (and generally can't be done). Ruby's "garbage collection" keeps track of all objects, and kills off (after releasing memory) any that are no longer referenced by any other object. Nice, eh?
The task itself is not hard, but I don't like the way your code is organised. This is what I would do in the first stage:
class Robot
class Name < String
class << self
def sign
"#{[*?A..?Z].sample}#{[*?A..?Z].sample}"
end
def number
"#{rand 1..9}#{rand 0..9}#{rand 0..9}"
end
def new
super << sign << number
end
end
end
end
And then:
Robot::Name.new
When constructing a list of names it is easy to check that they are unique. This is how I'd go about it:
class Robot
class Names < Array
def self.generate n
new.tap { |array| n.times do array.add_random_name end }
end
def add_random_name
name = Name.new
include?( name ) ? add_random_name : self << name
end
end
end
And then:
Robot::Names.generate 7
def save_name
stored_names = []
stored_names << named
end
Every time, you create a name, and call save_name you delete all previously created names, by assigning an empty array to stored_names
EDIT:
There were a few more errors, let me first post a working solution:
class Robot
attr_accessor :named , :stored_names , :rl
def initialize()
#stored_names = []
end
##rl = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ".chars.to_a
def name
#named = ""
#named << ##rl.sample
#named << ##rl.sample
#named << rand(100..999).to_s
save_name
check_name
end
def save_name
#stored_names << #named
end
def check_name
#stored_names.uniq!
end
def reset
#stored_names = Array.new
end
end
To access the members of your object, you have to prefix them with #.
You called save_name and check_name on #named, which is a string and doesn't provide these methods
#stored_names must be initialized to an empty array, before you can push elements into it with <<. This is normally done in the class's constructor initialize()
I understand this isn't efficient, but this will work.
letters = [*'A'..'Z'] =>
numbers = [*100..999]
names = []
2.times{names.push(letters.shuffle.first)} => Randomizing array and choosing element
names.push(numbers.shuffle.first)
names.join => Creates a String object out of the array elements
This isn't pretty, but it gets the job done.
This is how I automate Cary's approach with my y_support/name_magic:
require 'y_support/all'
class Robot
★ NameMagic
def name_yourself
begin
self.name = "#{[*?A..?Z].sample( 2 ).join}#{rand 100..999}"
rescue NameError; retry end
end
end
3.times { Robot.new.name_yourself }
Robot.instances #=> [PR489, NS761, OE663]
Robot.forget "PR489"
Robot.instances #=> [NS761, OE663]
Robot.forget_all_instances
Robot.instances #=> []
Robot.new.name_yourself
Robot.instances #=> [IB573]
In Ruby, we define class member functions like this
class Dog
def bark
puts "woof"
end
end
What I want to know and have been thoroughly unsuccessful in googling is, how and where does one define methods to act upon object-arrays in Ruby?
I wish to be able to do something like this:
dogs = [Dog.new, Dog.new, Dog.new]
dogs.some_function
How and where do I define some_function?
Note: I am not after solutions to a specific problem, more so the steps in how to define such a function in general.
To make all your dogs bark, you should use each on the array:
dogs.each(&:bark)
which is equivalent to
dogs.each { |dog| dog.bark }
Very rarely you need to define a method on an array, in which case it will be available on all arrays, containing anything. To do that, you need to declare it inside the Array class, by declaring it again:
class Array
def some_function
# do something...
end
end
And then you could run:
dogs = [Dog.new, Dog.new, Dog.new]
dogs.some_function
and also
numbers = [1, 2, 3, 4]
numbers.some_function
You could also create a class that inherits from Array such as
class DogWalker < Array
def some_function
self.each(&:bark)
end
end
class Dog
def bark
puts "woof"
end
end
d = DogWalker.new([Dog.new,Dog.new,Dog.new])
d.some_function
#woof
#woof
#woof
#=> [#<Dog:0x2a5a2e8>, #<Dog:0x2a5a2d0>, #<Dog:0x2a5a2b8>]
This means that you can only call this method on an instance of DogWalker (as well as all the methods available to Array) but does not alter Array itself. Giving you better control of the objects included in it. e.g.
class DogWalker < Array
def initialize(args)
raise ArgumentError.new("Must be an Array of Dogs") unless args.is_a?(Array) && args.all?{|e| e.is_a?(Dog)}
super
end
end
d = DogWalker.new([Dog.new,Dog.new,Dog.new])
#=>[#<Dog:0x2a5a2e8>, #<Dog:0x2a5a2d0>, #<Dog:0x2a5a2b8>]
d = DogWaler.new([Dog.new,12])
#=>ArgumentError: Must be an Array of Dogs
Array.new([1,2,3])
#=>[1,2,3]
As #UriAgassi pointed out very rarely do you need to alter base classes especially if you are designing extended functionality that does not need to/cannot apply across object types. Here is an example:
class Array
def some_function
self.map(&:bark)
end
end
class Dog
def bark
"woof"
end
end
d = [Dog.new,Dog.new]
d.some_function
#=> ["woof","woof"]
d = [1,2,3,4]
d.some_function
#=>NoMethodError: undefined method `bark' for 1:Fixnum
Since Array can accept any object type the Array is fine but the some_function method requires an object that responds_to bark which will raise in the event the object cannot perform the task requested.
In your example 'dogs' is the instance of Array class.
If you want to extend Array class you can add method to it like this:
class Array
def some_function
end
end
Previously, I asked about a clever way to execute a method on a given condition "Ruby a clever way to execute a function on a condition."
The solutions and response time was great, though, upon implementation, having a hash of lambdas gets ugly quite quickly. So I started experimenting.
The following code works:
def a()
puts "hello world"
end
some_hash = { 0 => a() }
some_hash[0]
But if I wrap this in a class it stops working:
class A
#a = { 0 => a()}
def a()
puts "hello world"
end
def b()
#a[0]
end
end
d = A.new()
d.b()
I can't see why it should stop working, can anyone suggest how to make it work?
that code doesn't work. it executes a at the time it is added to the hash, not when it is retrieved from the hash (try it in irb).
It doesn't work in the class because there is no a method defined on the class (you eventually define a method a on the instance.
Try actually using lambdas like
{0 => lambda { puts "hello world" }}
instead
First of all, you are not putting a lambda in the hash. You're putting the result of calling a() in the current context.
Given this information, consider what the code in your class means. The context of a class definition is the class. So you define an instance method called a, and assign a class instance variable to the a hash containing the result of calling a in the current context. The current context is the class A, and class A does not have a class method called a, so you're trying to put the result of a nonexistent method there. Then in the instance method b, you try to access an instance variable called #a -- but there isn't one. The #a defined in the class context belongs to the class itself, not any particular instance.
So first of all, if you want a lambda, you need to make a lambda. Second, you need to be clear about the difference between a class and an instance of that class.
If you want to make a list of method names to be called on certain conditions, you can do it like this:
class A
def self.conditions() { 0 => :a } end
def a
puts "Hello!"
end
def b(arg)
send self.class.conditions[arg]
end
end
This defines the conditions hash as a method of the class (making it easy to access), and the hash merely contains the name of the method to call rather than a lambda or anything like that. So when you call b(0), it sends itself the message contained in A.conditions[0], which is a.
If you really just want to pretty this sort of thing up,
why not wrap all your methods in a class like so:
# a container to store all your methods you want to use a hash to access
class MethodHash
alias [] send
def one
puts "I'm one"
end
def two
puts "I'm two"
end
end
x = MethodHash.new
x[:one] # prints "I'm one"
x.two # prints "I'm one"
or, to use your example:
# a general purpose object that transforms a hash into calls on methods of some given object
class DelegateHash
def initialize(target, method_hash)
#target = target
#method_hash = method_hash.dup
end
def [](k)
#target.send(#method_hash[k])
end
end
class A
def initialize
#a = DelegateHash.new(self, { 0 => :a })
end
def a()
puts "hello world"
end
def b()
#a[0]
end
end
x = A.new
x.a #=> prints "hello world"
x.b #=> prints "hello world"
One other basic error that you made is that you initialized #a outside of any instance method -
just bare inside of the definition of A. This is a big time no-no, because it just doesn't work.
Remember, in ruby, everything is an object, including classes, and the # prefix means the instance
variable of whatever object is currently self. Inside an instance method definitions, self is an instance
of the class. But outside of that, just inside the class definition, self is the class object - so you defined
an instance variable named #a for the class object A, which none of the instances of A can get to directly.
Ruby does have a reason for this behaviour (class instance variables can be really handy if you know what
you're doing), but this is a more advanced technique.
In short, only initialize instance variables in the initialize method.
table = {
:a => 'test',
:b => 12,
:c => lambda { "Hallo" },
:d => def print(); "Hallo in test"; end
}
puts table[:a]
puts table[:b]
puts table[:c].call
puts table[:d].send( :print )
Well, the first line in your class calls a method that doesn't exist yet. It won't even exist after the whole class is loaded though, since that would be a call to the class method and you've only defined instance methods.
Also keep in mind that {0 => a()} will call the method a(), not create a reference to the method a(). If you wanted to put a function in there that doesn't get evaluated until later, you'd have to use some kind of Lambda.
I am pretty new to using callbacks in Ruby and this is how I explained it to myself using an example:
require 'logger'
log = Logger.new('/var/tmp/log.out')
def callit(severity, msg, myproc)
myproc.call(sev, msg)
end
lookup_severity = {}
lookup_severity['info'] = Proc.new { |x| log.info(x) }
lookup_severity['debug'] = Proc.new { |x| log.debug(x) }
logit = Proc.new { |x,y| lookup_sev[x].call(y) }
callit('info', "check4", logit)
callit('debug', "check5", logit)
a = ->(string="No string passed") do
puts string
end
some_hash = { 0 => a }
some_hash[0].call("Hello World")
some_hash[0][]