I am trying to add an attribute to an object after it's been created using a method from within the object Class. I'd like to put this code in the def set_sell_by and def get_sell_by methods, if this is possible. So, in the end I'd like to do apple.set_sell_by(10) and then get that value later by doing apple.get_sell_by to check if the item has 5 days or less left to sell it.
class Grocery_Inventory
attr_accessor :product, :store_buy, :quantity, :serial_number, :customer_buy
def initialize(product, store_buy, quantity, serial_number, customer_buy)
#product = product
#store_buy = store_buy
#quantity = quantity + 5
#serial_number = serial_number
#customer_buy = customer_buy
end
def get_product_name
p product
self
end
def get_cost_customer
p "$#{customer_buy}"
self
end
def get_product_quantity
p "You have #{quantity} #{product}"
self
end
def set_sell_by
#some code...
self
end
def get_sell_by
if sell_by < 5
p "You need to sell this item within five days."
self
else
p "Item doesn't currently need to be sold."
self
end
end
end
apples = Grocery_Inventory.new("apples", 1.00, 5, 123, 0.25)
apples.get_product_name
apples.get_cost_customer
apples.get_product_quantity
Ruby is very lax in this regard. Simply access a variable with #and if it doesn't exist it will be created.
def set_sell_by
#sell_by = value
self
end
Related
Okay, this is a little hard to explain but I will try (For starters I am only just learning to code so it may be something super simple I'm missing..)
I created a few classes, I put a name in those classes, I put them in an array, I then chose one at random and try to puts the name, and it outputs blank.
Am I doing this all completely wrong? I've been learning ruby for about 3 months now so I'm sure there is a lot I don't know.
class A
attr :name
def set_name
#name = "Aaa"
end
def get_name
return #name
end
end
class B
attr :name
def set_name
#name = "Bbb"
end
def get_name
return #name
end
end
class C
attr :name
def set_name
#name = "Ccc"
end
def get_name
return #name
end
end
name_a = A.new
name_b = B.new
name_c = C.new
which_name = Array.new
which_name[0] = name_a
which_name[1] = name_b
which_name[2] = name_c
roll = rand(max 3)
puts which_name[roll].get_name
I then chose one at random and try to puts the name, and it outputs
blank.
You never called the #set_name method in your code. You can add this:
name_a.set_name
name_b.set_name
name_c.set_name
Also, you probably want to look into #attr_accessor.
I need to randomly pick a name from an array in Ruby and then check if it uppercase. So far I have:
def namegenerator
return #name.sample
end
def namechecker
if name.upcase then
check = TRUE
else
check = FALSE
end
end
It needs to be as two separate methods like this.
Something like this:
def sample_word(words)
words.sample
end
def upcase?(word)
word == word.upcase
end
And then something like:
words = %w[APPLE banana CherRy GRAPE]
word = sample_word(words)
puts word # e.g. BANANA
puts upcase?(word) # will print true
If you just want to check just the first letter:
names = %w(Kirk mccoy scott Spock)
names.sample.then { |name| [name, name[0] == name[0].upcase] }
#=> ["mccoy", false]
Maybe something like this:
class NameGenerator
def initialize(size, items)
#name = ""
#size = size
#items = items
end
def namegenerator
#name = #items.sample(#size).to_s
end
def namechecker?
#name == #name.upcase
end
def name
#name
end
end
ng = NameGenerator.new 1, ["name", "Name", "NAME"]
ng.namegenerator
puts ng.name, ng.namechecker?
Update
I've posted code without much thinking about abstraction and i think it would be much better to encapsulate name and upper case check to separate class and make it immutable, then make generator class that selects one entity from collection.
class NameGenerator
def initialize(items)
#items = items
end
def next
#items.sample
end
end
class Name
attr_reader :name
def initialize(name)
#name = name
end
def is_uppercase?
#name.match(/\p{Lower}/) == nil
end
end
ng = NameGenerator.new [
Name.new("name"),
Name.new("Name"),
Name.new("NAME"),
Name.new("na-me")
]
name = ng.next
puts name.name, name.is_uppercase?
Write a simple DSL for creating a shopping list. We should be able to specify the item name and quantity..
Something like.
sl = ShoppingList.new
sl.items do
add("Toothpaste",2)
add("Computer",1)
I am trying to add items and quantity both to a list, but facing trouble using shovel operation and also faced error to call this list outside the class. Can I list these items without using hash ?
class Array
def initialize
#list = []
#total = 0
end
def add(items, quantity)
if #list.include?(items) == false
#list << items
else #list.include?(items) == true
#list
end
#total.each {|x| quantity += x }
end
def items(&block)
#list.each(&block)
end
def total
#total
end
def display
#list
end
end
sl = Array.new
sl.items do
add('Toothpaste', 2)
add('Computer', 1)
add('Toothpaste', 3)
end
puts sl.list
puts sl.total
Expected Result :
s.list # => Should display list of items with quantity.
s.total # => Should display total of all quantities.
There was an attr_reader and attr_accessor missing in your code and the loop was not working (at least for me). With those attr_reader and attr_accessor you can get rid of at least two methods.
I wonder why you don't want to use a hash ..
Here's some working code, no hashes but an array of arrays, in my implementation counting the total numbers of items must be done at the end when all items are added.
class ShoppingList
attr_reader :list
attr_accessor :total
def initialize
#list = []
#total = 0
end
def add(items, quantity)
if #list.include?(items) == false
#list << [items, quantity]
else
#list
end
end
def total_number_of_items
quantities = list.map { |item| item[1] }
total = quantities.inject(0){|sum,x| sum + x }
end
end
sl = ShoppingList.new
sl.add('Toothpaste', 2)
sl.add('Computer', 1)
sl.add('Toothpaste', 3)
puts sl.list
puts sl.total_number_of_items
P.S. I renamed the method from Array to ShoppingList.
I know, this was not the question, but isn't that super-easy with a hash?
class ShoppingList
attr_reader :list
def initialize
#list = {}
end
def add(product_type, quantity)
if list[product_type].nil?
list[product_type] = quantity
else
list[product_type] += quantity
end
end
end
sl = ShoppingList.new
sl.add('Toothpaste', 2)
sl.add('Computer', 1)
sl.add('Toothpaste', 3)
pp sl.list.inspect
pp sl.list.keys.inspect
pp sl.list.values.inspect
Result:
"{\"Toothpaste\"=>5, \"Computer\"=>1}"
"[\"Toothpaste\", \"Computer\"]"
"[5, 1]"
I wondering of how to check the owner of certain method/class from other class.
For example:
class Value
attr_accessor :money
def initialize
#money = 0.0
end
def get_money
return self.money
end
def transfer_money(target, amount)
self.money -= amount
target.money += amount
end
end
class Nation
attr_accessor :value
def initialize
#value = Value.new
end
end
class Nation_A < Nation
def initialize
super
end
def pay_tribute_to_decendant_country
value.transfer_money(Nation_B.value, 500)
end
end
class Nation_B < Nation
def initialize
super
end
def pay_tribute_to_decendant_country
value.transfer_money(Nation_C.value, 200)
end
end
class Nation_C < Nation
def initialize
super
end
def pay_tribute_to_decendant_country
value.transfer_money(Nation_A.value, 300)
end
end
Yea, makes no sense how the decendant goes in a circle, but I'd like to implement the idea that different subclass has different argument.
The list is pretty long (I've installed at least 40 of these already with much more complex desendant branches and much more methods that call transfer_money from Value class). Then I have some idea to implement to the structure. I'd like to add currency, but to override all transfer_money method call would be a tremendous task for me to apply. Therefore I create a hash table that generate the call for me.
class Nation
def self.get_descendants
ObjectSpace.each_object(Class).select { |klass| klass < self }
end
end
module Additional_Value
currency_table = {}
min = 50
max = 100
def self.range (min, max)
rand * (max-min) + min
end
Nation.get_descendants.each do |derived_classes|
currency_table[derived_classes] == self.range min, max
end
end
class Value
attr_accessor :currency
def initialize
#money = 0
#currency = Additional_Value::currency_table
end
def transfer_money(target, amount)
self.money -= amount
amount = amount * #currency[self.class.owner] / #currency[target.class.owner]
target.money += amount
end
end
and I need to figure out how to define owner class. I tried using the caller, but it returns me string / array of string instead of object, method or calle work only for the same method, the 'sender' gem gives me an idea, but it's written in C, and I need to use the default library due to my circumstances.
Greatly appreciated.
Edit:
I'll rewrite the problem in a shorter way:
class Slave
def who_is_the_owner_of_me
return self.class.owner unless self.class.owner.nil?
end
end
class Test
attr_accessor :testing
def initialize
#testing = Slave.new
end
end
class Test2 < Test1
end
a = Test.new
b = Test2.new
c = Slave.new
a.testing.who_is_the_owner_of_me #=> Test
b.testing.who_is_the_owner_of_me #=> Test2
c.who_is_the_owner_of_me #=> main
Let's go to the code directly:
#!/usr/bin/ruby
require 'tk'
class Epg
def initialize
#var = "bad"
#cvs = nil
#items_demo = TkRoot.new() {title "EPG"}
TkFrame.new(#items_demo) {|cf|
#var = "good"
#cvs = TkCanvas.new(cf) {|c|}
puts "#cvs 1 is #{#cvs}"
puts "#var 1 is #{#var}"
}.pack('side'=>'top', 'fill'=>'both', 'expand'=>'yes')
puts "#cvs 2 is #{#cvs}"
puts "#var 2 is #{#var}"
end #initialize
def test
#var = "bad"
puts " #var 3 :#{#var}"
(1..3).each {|x| #var="good"}
puts " #var 4 :#{#var}"
end
end
e= Epg.new
e.test
Here is the output:
#cvs 1 is #<Tk::Canvas:0xb7cecb08>
#var 1 is good
#cvs 2 is
#var 2 is bad ##var has NOT been changed by the code in the block
#var 3 :bad
#var 4 :good ##var has been changed by the code in the block
Why we see different behavior here?
You can think of blocks as closing over both the set of local variables and the current self.
In Ruby, you will always have access to local variables, no matter what. The self encapsulates instance methods on the current object as well as instance variables.
Consider the following code:
class Table
def initialize(legs)
#legs = legs
end
def with_legs
yield #legs
end
end
And then:
def some_calling_method
name = "Yehuda"
Table.new(4) {|legs| puts "#{name} gnaws off one of the #{legs} legs" }
end
By Ruby's block semantics, you can be assured that name will be available inside the block, even without looking at the method you're calling.
However, consider the following:
class Person
def initialize(name)
#name = name
end
def gnaw
Table.new(4).with_legs do |legs|
puts "#{#name} gnaws off one of the #{legs} legs"
end
end
end
Person.new("Yehuda").gnaw
In this case, we are accessing the #name instance variable from inside the block. It works great in this case, but is not guaranteed. What if we implemented table a bit differently:
class Table
def initialize(legs)
#legs = legs
end
def with_legs(&block)
self.instance_eval(&block)
end
end
Effectively, what we're saying is "evaluate the block in the context of a different self." In this case, we are evaluating the block in the context of the table. Why would you do that?
class Leg
attr_accessor :number
def initialize(number)
#number = number
end
end
class Table
def initialize(legs)
#legs = legs
end
def with_leg(&block)
Leg.new(rand(#legs).instance_eval(&block)
end
end
Now, you could do:
class Person
def initialize(name)
#name = name
end
def gnaw
Table.new(4).with_leg do
puts "I'm gnawing off one of leg #{number}"
end
end
end
If you wanted access to the person object inside of the block, you'd have to do:
class Person
def initialize(name)
#name = name
end
def gnaw
my_name = name
Table.new(4).with_leg do
puts "#{my_name} is gnawing off one of leg #{number}"
end
end
end
As you can see, the use of instance_eval can make it simpler and less bulky to access methods of a far-off object inside a block, but comes at the cost of making the self unaccessible. The technique is usually used in DSLs, where a number of methods are injected into the block, but the self doesn't matter that much.
This is what's happening with Tk; they're using instance_eval to inject their own self into the block, which is wiping your self clean.
The explanation is that TkFrame.new uses instance_eval and thus the assignment #var = "good" changes the instance variable of TkFrame. Try this out:
class A
def initialize(&b)
instance_eval(&b)
end
end
class B
def initialize
#x = 10
#a = A.new do
#x = 20
end
end
end
p B.new
This is what you'll see:
#<B:0x10141314 #x=10, #a=#<A:0x10141300 #x=20>>