Ruby newb requesting assistance with basic classes - ruby

I am trying to figure out why my code below is not working. As you can see, the code is very brief and simple.
Could someone please help me understand why I am receiving this error message:
"NoMethodError: undefined method `<<' for nil:NilClass"
when I enter this command into terminal (after loading script):
mex_cuisine.add_recipe("charro beans")
The script:
class Cookbook
attr_accessor :title, :recipes, :recipe
def initialize(title)
#title = title
#recipes = []
end
def add_recipe(recipe)
#recipe = recipe
#recipes << #recipe
end
end
class Recipe
attr_accessor :name, :ingredients, :steps
def initialize(name, ingredients, steps)
#name = name
#ingredients = ingredients
#steps = steps
end
end
Your assistance is much appreciated.
Thank you.
**Edit:
Substituting this code:
def add_recipe(recipe)
#recipes.push(recipe)
puts "Added a recipe to the collection: #{recipe.title}"
end
causes the same error... Here is the long version:
NoMethodError: undefined method push' for nil:NilClass
from cookbook.rb:10:inadd_recipe'
from (irb):173
from /Users/patrickmeaney/.rvm/rubies/ruby-2.1.1/bin/irb:11:in `'
**Edit:
Here is the given test code:
mex_cuisine = Cookbook.new("Mexican Cooking")
burrito = Recipe.new("Bean Burrito", ["tortilla", "bean"], ["heat beans", "place beans in tortilla", "roll up"])
mex_cuisine.recipes # []
mex_cuisine.add_recipe(burrito)

Put the following code in a file called my_prog.rb:
class Cookbook
attr_accessor :title, :recipes, :recipe
def initialize(title)
#title = title
#recipes = []
end
def add_recipe(recipe)
#recipe = recipe
#recipes << #recipe
end
end
class Recipe
attr_accessor :name, :ingredients, :steps
def initialize(name, ingredients, steps)
#name = name
#ingredients = ingredients
#steps = steps
end
end
class Cookbook
attr_accessor :title, :recipes, :recipe
def initialize(title)
#title = title
#recipes = []
end
def add_recipe(recipe)
#recipe = recipe
#recipes << #recipe
end
end
class Recipe
attr_accessor :name, :ingredients, :steps
def initialize(name, ingredients, steps)
#name = name
#ingredients = ingredients
#steps = steps
end
end
mex_cuisine = Cookbook.new("Mexican Cooking")
burrito = Recipe.new("Bean Burrito", ["tortilla", "bean"], ["heat beans", "place beans in tortilla", "roll up"])
mex_cuisine.recipes # []
mex_cuisine.add_recipe(burrito)
Open a terminal window and cd to the same directory as my_prog.rb. Then run your program:
ruby my_prog.rb
There will be no errors.

Related

Wrong prawn method or wrong object?

I am trying to get programm started where i cant read in a csv File an it prints the data out on a pdf-File. Now i have a problem.
Heres is my Code:
------------------------------------
require_relative 'friends'
class List
attr_accessor :list_name, :list
def initialize(list_name)
#list_name = list_name
#list = []
end
def list_name
#list_name
end
def liste
#list
end
def wert(place)
#list[place].to_s
end
def list_length
#list.length
end
def print_list
#list.each do |freunde|
"#{freunde.name},#{freunde.age}"
end
end
def add_friend(a_friend)
#list.push(a_friend)
end
def load_friend(from_file)
File.readlines(from_file).each do |line|
add_friend(Freunde.from_csv(line))
end
end
end
-------------------------------------------
require_relative 'list'
class Friends
attr_accessor :name,:age
def initialize(name, age)
#name = name
#age = age
end
def self.from_csv(string)
name, age = string.split(',')
Freunde.new(name,age)
end
def friends
#name
end
end
-------------------------------------------
require 'prawn'
require_relative 'list'
require_relative 'friends'
class Generating
include Prawn::View
def initialize
#document = Prawn::Document.new(:page_size => "A4")
#fontpath = File.expand_path("../data/fonts", __FILE__)
liste1 = Listen.new("Friendslist")
liste1.load_friend("test.csv")
print_list
save
end
def print_friends
font("#{#fontpath}/Arial.ttf") do
font_size 11
text_box("#{liste1.print_list}", :at => [15,405], :height => 50,
:width => 250)
end
end
def save
self.render_file "Hello.pdf"
end
end
---------------------------------------------
When i now create a new generating-Object:
gen = Generating.new
then it fails the whole programm because the error says method unknow (print_list). Am i submitting the wrong object for the method(print_list), or am using the text output methods of prawn wrong?
print_list is an instance method of List class, and you call it on self object, which is there an instance of Generating. It should be:
liste1 = Listen.new("Friendslist")
liste1.load_friend("test.csv")
#⇓⇓⇓⇓⇓⇓
liste1.print_list

Using initializing instance as a parameter in other's class instance initialization

I have this classes (just a simplified example, not real ones)
class Shop
attr_accessor :name, :products
def initialize(name, products_names=%w(apple orange apple cucumber fennel))
self.name = name
self.products = products_names.map {|name| Product.new(self, name)}
end
end
class Product
attr_accessor :shop, :name
def initialize(shop, name)
self.shop = shop
self.name = name
end
def existing_products_count # problem_comes_here
shop.products #i need to process all products initialized for current shop
.select{|p| p.name==name}.size
end
def uniq_code
"#{name}_#{existing_products_count+1}"
end
end
And here is two questions:
Is this a good approach to pass self for Product instance initialization
and
How can i solve my case to process all already existing shop products for new product initialization
Thank you
UPDATE
all i invented for now is (at least it works like i need)
class Shop
attr_accessor :name, :products
def initialize(name, products_names=%w(apple orange apple cucumber fennel))
Product.class_variable_set(:##all, []) # << added
self.name = name
self.products = products_names.map {|name| Product.new(self, name)}
end
end
class Product
attr_accessor :shop, :name
def self.all # << added
##all
end
def initialize(shop, name)
self.shop = shop
self.name = name
self.class.all << self # << added
end
def existing_products_count # problem goes away here
self.class.all.products # << changed
.select{|p| p.name==name}.size
end
def uniq_code
"#{name}_#{existing_products_count+1}"
end
end
but i feel bad about this kind of solution (i don't know why) and will be appreciate for better one
I'm not sure if I understand the question, but here is one interpretation. Note how I've changed the method Product#uniq_code.
class Shop
attr_accessor :name, :products
def initialize(name, products_names=%w(apple orange apple cucumber fennel))
self.name = name
self.products = products_names.map {|name| Product.new(self, name)}
end
end
class Product
attr_accessor :shop, :name
def initialize(shop, name)
self.shop = shop
self.name = name
end
def uniq_code # problem_comes_here
puts "buy #{self.name} #{self.shop.name}"
end
end
Now let's have an example:
at_store = Shop.new("in store", ["bulldozer", "Ducati"])
online = Shop.new("online", ["can opener", "Makita router"])
arr = [at_store, online]
We can do this:
arr.flat_map { |s| s.products }.each { |p| p.send(:uniq_code) }
# buy bulldozer in store
# buy Ducati in store
# buy can opener online
# buy Makita router online
Is this roughly what you were looking for?
Edit: to save the product instances for a given shop instance:
product_instances = at_store.products
#=> [#<Product:0x007f93f984f938
# #shop=#<Shop:0x007f93f984f988 #name="in_store",
# #products=[...]>, #name="bulldozer">,
# #<Product:0x007f93f984f910
# #shop=#<Shop:0x007f93f984f988 #name="in_store",
# #products=[...]>, #name="Ducati">]
for use later:
product_instances.each { |p| p.uniq_code }
# buy bulldozer in_store
# buy Ducati in_store

Ruby 2.1: Composing a Bicycle of Parts -> private method `select' called for nil:NilClass (NoMethodError)

I am getting an error when running this code. The Following is the output:
L
Bicycle#Ex3.rb:32:in `spares': private method `select' called for nil:NilClass (NoMethodError)
from Bicycle#Ex3.rb:10:in `spares'
from Bicycle#Ex3.rb:111:in `<main>'
Here is the code:
class Bicycle
attr_reader :size, :parts
def initialize(args={})
#size = args[:size]
#parts = args[:parts]
end
def spares
parts.spares # return an array
end
def lead_days
1
end
#...
end
class Parts
attr_reader :parts
def initialize(args={})
#parts = parts
end
def size
parts.size
end
def spares
parts.select{|part| part.needs_spare}
end
end
class Part
attr_reader :name, :description, :needs_spare
def initialize(args)
#name = args[:name]
#description = args[:description]
#needs_spare = args.fetch(:needs_spare, true)
end
end
class RoadBikeParts < Parts
attr_reader :tape_color
def post_initialize(args)
#tape_color = args[:tape_color]
end
def local_spares
{tape_color: tape_color}
end
def default_tire_size
'23'
end
end
class MountainBikeParts < Parts
attr_reader :front_shock, :rear_shock
def post_initialize(args)
#front_shock = args[:front_shock]
#rear_shock = args[:rear_shock]
end
def local_spares
{ rear_shock: rear_shock}
end
def default_tire_size
'2.1'
end
end
chain = Part.new(
name: 'chain',
description: '10 speed')
road_tire = Part.new(
name: 'tape_size',
description: '23')
tape = Part.new(
name: 'tape_color',
description: 'red')
mountain_tire = Part.new(
name: 'tire_size',
description: '2.1')
rear_shock = Part.new(
name: 'rear_shock',
description: 'Fox')
front_shock = Part.new(
name: 'front_shock',
description: 'Manitou',
needs_spare: false)
road_bike_part = Parts.new([chain, road_tire, tape])
road_bike = Bicycle.new(
size: 'L',
parts: Parts.new([chain,
road_tire,
tape]))
puts road_bike.size
#puts road_bike.parts.size
puts road_bike.spares.size
It is clear this line --> puts road_bike.spares.size is given the error NoMethodError, however, I am not sure how I can make a work around to correct this issue for this example. The spares method is returning an array of Part objects, however it seems my problem lies in the fact the spares method .select is private from the calling object.
Any advice to revise this code would be great. Thanks.
What's happening here is that Parts#parts is nil. You're getting the error on this line:
# parts is nil
parts.select{|part| part.needs_spare}
In the initializer of Parts, its parts attribute does not get assigned properly:
def initialize(args={})
#parts = parts
end
So when being initialized, it assigns #parts with the value of parts. But since parts is not a local variable there, it calls the Parts#parts method, which returns nil.
If you change the initializer to the following:
def initialize(parts)
#parts = parts
end
You'll be able to run the code. But subclasses of Parts seem to expect a Hash in the initializer, rather than an Array like their super class does though.

Ruby: update attribute of an object

I have these two classes in Ruby:
Prod.rb
class Prod
attr_reader :code, :price
def initialize code, price
#code = code
#price = price
end
end
Buy.rb
class Buy
def initialize()
#items = []
end
def addToBasket item
#items << item
end
def changePrice
#items.each do |item|
item.price = 0.00
end
end
end
When I am testing the app with the code below, I get this error pointing to the item.price = 0.00 above:
test_1(MyTest): NoMethodError: undefined method 'price=' for #<Prod:0x24d76e8>
I can print the value of item.price but I cannot update it. Any ideas?
MyTest.rb
def setup
#prod1 = Prod.new("1", 19.95)
end
def test_1
b = Buy.new()
b.addToBasket(#prod1)
[...]
end
This is because you don't have a price= method defined in class Prod. You only defined a getter with attr_reader :code, :price. If you to create both getter and setter, user attr_accessor in your Prod class:
class Prod
attr_accessor :code, :price
def initialize code, price
#code = code
#price = price
end
end
You can learn more about getters and setters in ruby in my article: Ruby for Admins: Objects.

Undefined local variable error with simple program that adds items to array

class Books
attr_accessor :name, :book_id
def initialize(name, book_id)
#name = name,
#book_id = book_id
end
end
class BookCollection
def intialize
#book_names = []
end
def add_to_books(book_name)
book_name.push(book_names)
end
end
book1 = Books.new("catch22", "12345")
book_collection1 = BookCollection.new
book_collection1.add_to_books(book1.name)
puts book_collection1
end
That is my code and the error I'm getting is "undefined local variable or method `book_names'". I tried adding " attr_accessor :book_names" and when I do that the printed output doesn't make sense.
There are a few mistakes in your code:
line 4 should not end with a comma.
initialize in class BookCollection is misspelled, resulting in #book_names not being initialized. #book_names therefore equals nil when you attempt to add an element to it with push. nil does not have a method push; hence the exception, and the message printed with the exception.
book_name.push(book_names) should be #book_name.push(book_name). (#book_name must be an instance_variable, as opposed to a local variable, to be visible outside a method, within the class definition.
puts book_collection1 prints the class instance; you want to print #book_names.
Here I've fixed your code. I've used << instead of push. Either is OK, but the former seems to be favored my most.
class Books
attr_accessor :name, :book_id
def initialize(name, book_id)
puts "name = #{name}, book_id = #{book_id}"
#name = name
#book_id = book_id
end
end
class BookCollection
attr :book_names
def initialize
#book_names = []
end
def add_to_books(book_name)
#book_names << book_name
end
end
book_collection1 = BookCollection.new
book1 = Books.new("Catch22", "12345")
book2 = Books.new("Hawaii", "67890")
book_collection1.add_to_books(book1.name)
book_collection1.add_to_books(book2.name)
book_collection1.book_names # => ["Catch22", "Hawaii"]
Probably just a typo at
book_name.push(book_names)
Should have been
book_names.push(book_name)
With attr_accessor :book_names

Resources