RUBY - How to call a method from another class - ruby

I'm new to ruby and programming in general and I'm having some issues getting methods to work from another class. The method I'm trying to get to work is new_employee and its option 2 if you run the business.rb
business.rb file contains class Business
class Business
attr_accessor :name
def run
self.welcome
end
def welcome
while true
puts "Welcome to Team Green Turf! What do you want to do today?"
puts "1. Add new customer"
puts "2. Add new employee"
puts "3. View current revenue"
choice = gets.chomp.to_i
case choice
when 1
puts "hello!"
when 2
puts new_employee()
when 3
exit
end
end
end
end
team_green_turf = Business.new
team_green_turf.run
---------------------------------
employees.rb file
require_relative 'business'
class Employees
attr_accessor :name
def initialize(name)
#name = name
end
def new_employee(name)
puts "What is the employees name?"
name = gets.chomp
e1 = Employees.new(name)
end
end

There are two main ideas:
You need to ensure that the Ruby interpreter has loaded the file containing the method before you attempt to call it. In your case, you are executing the code at the botton of business.rb before the employees.rb file has been fully loaded, so the Employee class and its methods will not be defined. You could fix this in many different ways, but I suggest moving the last two lines of business.rb into their own file called run.rb and putting require_relative 'business' at the top of run.rb, and putting require_relative 'employees' at the top of business.rb, and removing the require_relative 'business' from the top of employees.rb. (The Employees class does not actually use or depend on the Business class so it doesn't make sense to require it.)
You need to provide the proper prefixes to the method to get it to be called. In your case, you want to call the function from the Business class, you would write Employees.new_employee. You would also need to change new_employee to be a class method by putting self. before its name when you are defining it: def self.new_employee(name)....

Related

How to fix "uninitialized constant List::Task (NameError)" in Ruby

Output: <'class:List'> uninitialized constant List::Task (NameError)
You have created a new list
What I think is happening is that when I call Task.new, the List class is looking for possibly a task method or variable within its own class.
So far I tried using include Task and require Task in my List class with no luck. I also tried to declare the List class in my Task class. I also tried making the list class a parent of the Task class. After some digging online I thought it was the Ruby version and even changed the PATH to an older ruby version.
class List
attr_reader :all_tasks
if __FILE__ == $PROGRAM_NAME
my_list = List.new
puts 'You have created a new list'
my_list.add(Task.new('Make breakfest'))
puts 'You added a task'
end
def initialize
#all_tasks = []
end
def add(task)
all_tasks << task
end
end
class Task
attr_reader :description
def initialize(description)
#description = description
end
end
It might have helped if you provided more information about where (like: in which files) the classes are defined and how you call/execute your ruby code.
Usually, you would put code in a lib directory, with one class per file and strong semantics (class List goes into lib/list.rb), but its a tiny bit more complicated than that.
If you want to hack around and learn play a bit with ruby it is also perfectly understandable, that you do not want to cope with requireing (loading) other files, dealing with dependencies and all that (although this will need to happen one day as with most other programming languages).
For now, this would fix your issue and should get you going:
# file name: task_list_program.rb (or anything you want)
class Task
attr_reader :description
def initialize(description)
#description = description
end
end
class List
attr_reader :all_tasks
def initialize
#all_tasks = []
end
def add(task)
all_tasks << task
end
end
# note that for playing around in a single file, you actually
# do not even need the 'if ...' part here
if __FILE__ == $PROGRAM_NAME
my_list = List.new
puts 'You have created a new list'
my_list.add(Task.new('Make breakfest'))
puts 'You added a task'
end
The main "trick" here is to move the if __FILE__ == ... thing out of your class definition, because otherwise (you are accidentally dealing with a special case here), stuff evaluated in that class at runtime will not be able to pick up and reference other "definitions" in the way you seem to expect.
I hope this helps you to get going. The differences between the code example could teach you a lot, e.g. about "namespaces" (which afaik are not a real concept in Ruby), but I believe this short answer is good enough to get you started and have some fun with ruby (and welcome at SO by the way)!
("execute" the file like this: ruby task_list_program.rb).
class List
class Task
attr_reader :description
def initialize(description)
#description = description
end
end
attr_reader :all_tasks
if __FILE__ == $PROGRAM_NAME
my_list = List.new
puts 'You have created a new list'
List::Task.new('Make breakfest')
# my_list.add(Task.new('Make breakfest'))
puts 'You added a task'
end
def initialize
#all_tasks = []
end
def add(task)
all_tasks << task
end
end
The first issue what we had in this file, that we try to call Task class before implemented!
You don't correct call Task class my_list.add(Task.new('Make breakfest'))
correct is List::Task.new('Make breakfast')

new Class item displaying as object instead of string

I'm still extremely new to Ruby. I took a class on Codecademy and I'm currently doing the "Final" where I have to make a todo list.
One of the parts of the todo list is to be able to add tasks (obviously). Another part is to be able to show all current tasks. Now, technically, both of these are working. But, when I create a new task using the class I made (Task) and then show the tasks, it displays the object ID instead of the string. If I simply use my add method without using my Task class, it will display the string like I want it too.
My goal is to get my script to display the string while using the Task class. If someone could please explain to me why it's not working and how I can fix it, I'd appreciate that.
Here's the code:
## Classes ##
#List Class - Used for anything involving the list
class List
attr_reader :all_tasks
def initialize
#all_tasks = []
end
def add(task)
all_tasks << task
end
def show
all_tasks
end
end
#Task Class - Used for anything involving Tasks
class Task
attr_reader :description
def initialize(description)
#description = description
end
end
## Modules ##
module Promptable
def prompt(message = "What would you like to do?", symbol = " >: ")
print message
print symbol
gets.chomp
end
def show
menu
end
end
module Menu
def menu
puts "
'add' - Add a task to the list \n
'delete' - Delete a task from the list \n
'update' - Update a task in the list \n
'show' - Shows current tasks in list"
end
end
#Methods - various methods
#Program Runner
if __FILE__ == $PROGRAM_NAME
include Menu
include Promptable
my_list = List.new
puts "Please choose from the following list: "
until ['q'].include?(user_input = prompt(show).downcase)
case user_input
when 'add'
puts "What task would you like to do?"
my_list.add(Task.new(gets.chomp))
when 'q'
puts "Qutting...."
when 'show'
puts my_list.show
else "That is not a valid command"
end
end
end
puts my_list.show
Will display the tasks one by one. Since the Task class doesn't have a to_s method, the default one will be used. Just add one:
class Task
# ...
alias to_s description
end
BTW strings are objects too. Pretty much everything in Ruby is an object.

Why is my second class not allowing me to call my methods from first class?

I am creating a code for a small game of 4 horses running a set distance across my terminal. I have it to where it is outputting my horses that I have added and my users horse, but when I go to my next class to build the race it self, I keep getting method undefined errors. I searched for something similar but couldn't find anything. learningruby.com has some roundabout answers to it, but not showing me what im missing.
class Horses
##list_of_horses = []
attr_accessor :name
attr_accessor :position
def initialize
self.name = nil
self.position = 0
end
def self.add_horse(*horse_variables)
horse = Horses.new
horse.name = horse_variables[0]
##list_of_horses.push horse
end
def self.add_user(*user_variables)
add_user = Horses.new
add_user.name = user_variables[0]
##list_of_horses.push add_user
end
def self.display_data
puts "*" * 60
##list_of_horses.each do |racer|
print "-" * racer.position
puts racer.name
end
end
def move_forward
self.position += rand(1..5)
end
def self.display_horses
##list_of_horses
end
end
horse1 = Horses.add_horse ("Jackrabbit")
horse2 = Horses.add_horse ("Pokey")
horse3 = Horses.add_horse ("Snips")
user1 = Horses.add_user ("Jim")
Horses.display_data
Now when I run just this file, It will give me the printout in my terminal of
Jackrabbit
Pokey
Snips
Jim
But when I start trying to call the methods I have created in my Horses class in my next class of Race even outside of the Race class itself, Im returning method undefined.
require_relative 'Horses_class.rb'
no_winner = true
class Race
def begin_race
puts "And the Race has begun!"
end
end
while no_winner == true
puts begin_race
racing = Race.new
racing.Horses.display_data
end
So why am I not allowed to call my other methods? should I be using a splat or is there something more simplistic that im missing? Thank you in advanced.
Jim
Your begin_race method seems to be out of scope when you're calling it. You need to use either the . or the :: (scope) operator to access it.
class Race
def self.begin_race
puts "And the race has begun!"
end
end
Race::begin_race
# or
Race.begin_race
Also, when you call racing.Horses.display_data you must make sure that your Horses class is a sub-class of you racing class. You can not call a sub-class via an object, you must call it through the class constant.
class Race
class Horses
def self.display_data
puts "The Data"
end
end
end
# Access 'display_data'
Race::Horses.display_data
So in this case your require_relative should be within your Race class and your while block should look like
while no_winner == true
Race.begin_race
Race::Horses.display_data
end

Using Class to instantiate my Song class

I'm going through the Learn Ruby the Hard Way - ex40
Currently, the code works fine. That is not my problem. My problem is every time I add a new song. A) I need to create an instance variable inside the initialize method. B) Then, I have to give it an attr_reader.
What I know if I can A) not have to keep creating new instance variable, but simply variables inside the Song class. B) Not have to create an attr_reader for each variable.
class Song
def initialize()
#jcole_lighter = "Come here, I\'m about to take you higher"
#hold_on_drake = ["Cause you\'re a good girl and you know it",
"You act so different around me",
"Cause you\'re a good girl and you know it"]
end
def sing_me_a_song()
for line in initialize
puts line
end
end
attr_reader :jcole_lighter
attr_reader :hold_on_drake
end
thing = Song.new
puts thing.jcole_lighter()
puts "-"*10
thing= Song.new
puts thing.hold_on_drake()
Check this out for a good explanation of attr_reader, attr_writer, and attr_accessor.
And check this out for learning how to add parameters to the constructor.
You could have :attr_accessor :artists inside Song and in initialize do this:
#artists = Array.new
Then you can have a method add:
def add(artist)
#artists << artist
end
Just an idea. Always happy to help a Drake fan.

Ruby design pattern: How to make an extensible factory class?

Ok, suppose I have Ruby program to read version control log files and do something with the data. (I don't, but the situation is analogous, and I have fun with these analogies). Let's suppose right now I want to support Bazaar and Git. Let's suppose the program will be executed with some kind of argument indicating which version control software is being used.
Given this, I want to make a LogFileReaderFactory which given the name of a version control program will return an appropriate log file reader (subclassed from a generic) to read the log file and spit out a canonical internal representation. So, of course, I can make BazaarLogFileReader and GitLogFileReader and hard-code them into the program, but I want it to be set up in such a way that adding support for a new version control program is as simple as plopping a new class file in the directory with the Bazaar and Git readers.
So, right now you can call "do-something-with-the-log --software git" and "do-something-with-the-log --software bazaar" because there are log readers for those. What I want is for it to be possible to simply add a SVNLogFileReader class and file to the same directory and automatically be able to call "do-something-with-the-log --software svn" without ANY changes to the rest of the program. (The files can of course be named with a specific pattern and globbed in the require call.)
I know this can be done in Ruby... I just don't how I should do it... or if I should do it at all.
You don't need a LogFileReaderFactory; just teach your LogFileReader class how to instantiate its subclasses:
class LogFileReader
def self.create type
case type
when :git
GitLogFileReader.new
when :bzr
BzrLogFileReader.new
else
raise "Bad log file type: #{type}"
end
end
end
class GitLogFileReader < LogFileReader
def display
puts "I'm a git log file reader!"
end
end
class BzrLogFileReader < LogFileReader
def display
puts "A bzr log file reader..."
end
end
As you can see, the superclass can act as its own factory. Now, how about automatic registration? Well, why don't we just keep a hash of our registered subclasses, and register each one when we define them:
class LogFileReader
##subclasses = { }
def self.create type
c = ##subclasses[type]
if c
c.new
else
raise "Bad log file type: #{type}"
end
end
def self.register_reader name
##subclasses[name] = self
end
end
class GitLogFileReader < LogFileReader
def display
puts "I'm a git log file reader!"
end
register_reader :git
end
class BzrLogFileReader < LogFileReader
def display
puts "A bzr log file reader..."
end
register_reader :bzr
end
LogFileReader.create(:git).display
LogFileReader.create(:bzr).display
class SvnLogFileReader < LogFileReader
def display
puts "Subersion reader, at your service."
end
register_reader :svn
end
LogFileReader.create(:svn).display
And there you have it. Just split that up into a few files, and require them appropriately.
You should read Peter Norvig's Design Patterns in Dynamic Languages if you're interested in this sort of thing. He demonstrates how many design patterns are actually working around restrictions or inadequacies in your programming language; and with a sufficiently powerful and flexible language, you don't really need a design pattern, you just implement what you want to do. He uses Dylan and Common Lisp for examples, but many of his points are relevant to Ruby as well.
You might also want to take a look at Why's Poignant Guide to Ruby, particularly chapters 5 and 6, though only if you can deal with surrealist technical writing.
edit: Riffing of off Jörg's answer now; I do like reducing repetition, and so not repeating the name of the version control system in both the class and the registration. Adding the following to my second example will allow you to write much simpler class definitions while still being pretty simple and easy to understand.
def log_file_reader name, superclass=LogFileReader, &block
Class.new(superclass, &block).register_reader(name)
end
log_file_reader :git do
def display
puts "I'm a git log file reader!"
end
end
log_file_reader :bzr do
def display
puts "A bzr log file reader..."
end
end
Of course, in production code, you may want to actually name those classes, by generating a constant definition based on the name passed in, for better error messages.
def log_file_reader name, superclass=LogFileReader, &block
c = Class.new(superclass, &block)
c.register_reader(name)
Object.const_set("#{name.to_s.capitalize}LogFileReader", c)
end
This is really just riffing off Brian Campbell's solution. If you like this, please upvote his answer, too: he did all the work.
#!/usr/bin/env ruby
class Object; def eigenclass; class << self; self end end end
module LogFileReader
class LogFileReaderNotFoundError < NameError; end
class << self
def create type
(self[type] ||= const_get("#{type.to_s.capitalize}LogFileReader")).new
rescue NameError => e
raise LogFileReaderNotFoundError, "Bad log file type: #{type}" if e.class == NameError && e.message =~ /[^: ]LogFileReader/
raise
end
def []=(type, klass)
#readers ||= {type => klass}
def []=(type, klass)
#readers[type] = klass
end
klass
end
def [](type)
#readers ||= {}
def [](type)
#readers[type]
end
nil
end
def included klass
self[klass.name[/[[:upper:]][[:lower:]]*/].downcase.to_sym] = klass if klass.is_a? Class
end
end
end
def LogFileReader type
Here, we create a global method (more like a procedure, actually) called LogFileReader, which is the same name as our module LogFileReader. This is legal in Ruby. The ambiguity is resolved like this: the module will always be preferred, except when it's obviously a method call, i.e. you either put parentheses at the end (Foo()) or pass an argument (Foo :bar).
This is a trick that is used in a few places in the stdlib, and also in Camping and other frameworks. Because things like include or extend aren't actually keywords, but ordinary methods that take ordinary parameters, you don't have to pass them an actual Module as an argument, you can also pass anything that evaluates to a Module. In fact, this even works for inheritance, it is perfectly legal to write class Foo < some_method_that_returns_a_class(:some, :params).
With this trick, you can make it look like you are inheriting from a generic class, even though Ruby doesn't have generics. It's used for example in the delegation library, where you do something like class MyFoo < SimpleDelegator(Foo), and what happens, is that the SimpleDelegator method dynamically creates and returns an anonymous subclass of the SimpleDelegator class, which delegates all method calls to an instance of the Foo class.
We use a similar trick here: we are going to dynamically create a Module, which, when it is mixed into a class, will automatically register that class with the LogFileReader registry.
LogFileReader.const_set type.to_s.capitalize, Module.new {
There's a lot going on in just this line. Let's start from the right: Module.new creates a new anonymous module. The block passed to it, becomes the body of the module – it's basically the same as using the module keyword.
Now, on to const_set. It's a method for setting a constant. So, it's the same as saying FOO = :bar, except that we can pass in the name of the constant as a parameter, instead of having to know it in advance. Since we are calling the method on the LogFileReader module, the constant will be defined inside that namespace, IOW it will be named LogFileReader::Something.
So, what is the name of the constant? Well, it's the type argument passed into the method, capitalized. So, when I pass in :cvs, the resulting constant will be LogFileParser::Cvs.
And what do we set the constant to? To our newly created anonymous module, which is now no longer anonymous!
All of this is really just a longwinded way of saying module LogFileReader::Cvs, except that we didn't know the "Cvs" part in advance, and thus couldn't have written it that way.
eigenclass.send :define_method, :included do |klass|
This is the body of our module. Here, we use define_method to dynamically define a method called included. And we don't actually define the method on the module itself, but on the module's eigenclass (via a small helper method that we defined above), which means that the method will not become an instance method, but rather a "static" method (in Java/.NET terms).
included is actually a special hook method, that gets called by the Ruby runtime, everytime a module gets included into a class, and the class gets passed in as an argument. So, our newly created module now has a hook method that will inform it whenever it gets included somewhere.
LogFileReader[type] = klass
And this is what our hook method does: it registers the class that gets passed into the hook method into the LogFileReader registry. And the key that it registers it under, is the type argument from the LogFileReader method way above, which, thanks to the magic of closures, is actually accessible inside the included method.
end
include LogFileReader
And last but not least, we include the LogFileReader module in the anonymous module. [Note: I forgot this line in the original example.]
}
end
class GitLogFileReader
def display
puts "I'm a git log file reader!"
end
end
class BzrFrobnicator
include LogFileReader
def display
puts "A bzr log file reader..."
end
end
LogFileReader.create(:git).display
LogFileReader.create(:bzr).display
class NameThatDoesntFitThePattern
include LogFileReader(:darcs)
def display
puts "Darcs reader, lazily evaluating your pure functions."
end
end
LogFileReader.create(:darcs).display
puts 'Here you can see, how the LogFileReader::Darcs module ended up in the inheritance chain:'
p LogFileReader.create(:darcs).class.ancestors
puts 'Here you can see, how all the lookups ended up getting cached in the registry:'
p LogFileReader.send :instance_variable_get, :#readers
puts 'And this is what happens, when you try instantiating a non-existent reader:'
LogFileReader.create(:gobbledigook)
This new expanded version allows three different ways of defining LogFileReaders:
All classes whose name matches the pattern <Name>LogFileReader will automatically be found and registered as a LogFileReader for :name (see: GitLogFileReader),
All classes that mix in the LogFileReader module and whose name matches the pattern <Name>Whatever will be registered for the :name handler (see: BzrFrobnicator) and
All classes that mix in the LogFileReader(:name) module, will be registered for the :name handler, regardless of their name (see: NameThatDoesntFitThePattern).
Please note that this is just a very contrived demonstration. It is, for example, definitely not thread-safe. It might also leak memory. Use with caution!
One more minor suggestion for Brian Cambell's answer -
In you can actually auto-register the subclasses with an inherited callback. I.e.
class LogFileReader
cattr_accessor :subclasses; self.subclasses = {}
def self.inherited(klass)
# turns SvnLogFileReader in to :svn
key = klass.to_s.gsub(Regexp.new(Regexp.new(self.to_s)),'').underscore.to_sym
# self in this context is always LogFileReader
self.subclasses[key] = klass
end
def self.create(type)
return self.subclasses[type.to_sym].new if self.subclasses[type.to_sym]
raise "No such type #{type}"
end
end
Now we have
class SvnLogFileReader < LogFileReader
def display
# do stuff here
end
end
With no need to register it
This should work too, without the need for registering class names
class LogFileReader
def self.create(name)
classified_name = name.to_s.split('_').collect!{ |w| w.capitalize }.join
Object.const_get(classified_name).new
end
end
class GitLogFileReader < LogFileReader
def display
puts "I'm a git log file reader!"
end
end
and now
LogFileReader.create(:git_log_file_reader).display
This is how I would make an extensible factory class.
module Factory
class Error < RuntimeError
end
class Base
##registry = {}
class << self
def inherited(klass)
type = klass.name.downcase.to_sym
##registry[type] = klass
end
def create(type, *args, **kwargs)
klass = ##registry[type]
return klass.new(*args, **kwargs) if klass
raise Factory::Error.new "#{type} is unknown"
end
end
end
end
class Animal < Factory::Base
attr_accessor :name
def initialize(name)
#name = name
end
def walk?
raise NotImplementedError
end
end
class Cat < Animal
def walk?; true; end
end
class Fish < Animal
def walk?; false; end
end
class Salmon < Fish
end
duck = Animal.create(:cat, "Garfield")
salmon = Animal.create(:salmon, "Alfredo")
pixou = Animal.create(:duck, "Pixou") # duck is unknown (Factory::Error)

Resources