Min & max values for initialize hash - ruby

class Airplane
attr_reader :weight, :aircraft_type
attr_accessor :speed, :altitude, :course
def initialize(aircraft_type, options = {})
#aircraft_type = aircraft_type.to_s
#course = options[:course.to_s + "%"] || rand(1...360).to_s + "%"
end
How I can use minimum and maximum allowable values ​​for the hash in initialize from 1 to 360?
Example:
airplane1 = Airplane.new("Boeing 74", course: 200)
p radar1.airplanes
=> [#<Airplane:0x000000023dfc78 #aircraft_type="Boeing 74", #course="200%"]
But if I set to course value 370, airplane1 should not work

This could be refactored i'm sure but this is what i came up with
class Plane
attr_reader :weight, :aircraft_type
attr_accessor :speed, :altitude, :course
def initialize(aircraft_type, options = {})
#aircraft_type = aircraft_type.to_s
#course = options[:course] || random_course
check_course
end
def check_course
if #course < 1 or #course > 360
#course = 1
puts "Invalid course. Set min"
elsif #course > 360
#course = 360
puts "Invalid course. Set max"
else
#course = #course
end
end
def random_course
#course = rand(1..360)
end
end

course is an angle, isn't it? shouldn't it be 0...360 the valid range for it? and why the final "%"? and why work with a string instead of an integer?
Anyway, that's what I'd write:
#course = ((options[:course] || rand(360)) % 360).to_s + "%"

I think you mean you don't want to let people pass in something like {course: '9000%'} for options and you want to error out if it's invalid. If that's the case, you can just test if it's in range:
def initialize(aircraft_type, options = {})
#aircraft_type = aircraft_type.to_s
allowed_range = 1...360
passed_course = options[:course]
#course = case passed_course
when nil
"#{rand allowed_range}%"
when allowed_range
"#{passed_course}%"
else
raise ArgumentError, "Invalid course: #{passed_course}"
end
end

Related

Getting parts of two classes to interact with a random chance?

I know I'm asking a lot of questions, and I apologize for that.
I am trying to get 2 classes to interact with each other, but with a random chance.
class Hands
attr_reader :name, :element, :skill, :mana, :health, :attack, :fire, :water, :lyfe, :summons
attr_writer :mana, :health
attr_accessor :summon
def initialize(name, element, skill)
#mana = 100
#health = 200
#summons = []
#name = name
#element = element
#skill = skill
end
def summon
#summons << summon
random_number = [1, 2].sample
if #element == "Lyfe"
if random_number == 1
puts "#{#name} summoned #{summon1.name}"
elsif random_number == 2
puts "#{#name} summoned #{summon2.name}"
else
puts "#{#name} can not use this ability!"
end
end
end
end
class Summons
attr_reader :name, :strength, :health
attr_writer :name, :strength, :health
attr_accessor :summon
def initialize(name, strength)
#name = name
#strength = strength
if #strength == "1"
#health = 25
#mana = 25
elsif #strength == "2"
#health = 50
#mana = 50
elsif #strength == "3"
#health = 100
#mana = 75
end
end
end
player1 = Hands.new('Silk', 'Lyfe', 'Summon')
player2 = Hands.new('Nubz', 'Lyfe', 'Manipulate Wildlife')
player3 = Hands.new('Lisk', 'Water', 'Invisible')
player4 = Hands.new('Azzi', 'Water', 'Manipulate Water')
player5 = Hands.new('Zeph', 'Fire', 'Lightning')
player6 = Hands.new('Ford', 'Fire', 'Manipulate Fire')
player7 = Hands.new('Boyd', 'Fire', 'Craft')
summon1 = Summons.new('Berto', '1')
summon2 = Summons.new('Wicket', '1')
summon3 = Summons.new('Skye', '1')
summon4 = Summons.new('Test4', '2')
summon5 = Summons.new('Test5', '2')
summon6 = Summons.new('Test6', '3')
player1.summon
I know I probably have some unnecessary code in there.
I am trying to get one of the players (Hands class) to randomly "summon" one of the summons. I tried using a random_number.sample command, but I got a stack level too deep error. The random_number code is shortened to 1 & 2 for testing purposes, but if it can somehow work and expand to all 6 that would be great.
Any explanation would help!
I got a stack level too deep error
That's because you call summon from within summon:
def summon # <--+
#summons << summon # |
# ^^^^^^------+ invokes itself over and over again
But let's start from the beginning. The Player#summon method needs to somehow access the "summons".
You could create an array of available summons, e.g.
available_summons = [
Summons.new('Berto', '1'),
Summons.new('Wicket', '1'),
Summons.new('Skye', '1')
]
And then pass that array to your summon method:
player1 = Hands.new('Silk', 'Lyfe', 'Summon')
player1.summon(available_summons)
Within your method you would sample from that array and add the sampled element to the player's #summons array: (I removed any additional logic for now)
def summon(available_summons)
summon = available_summons.sample
#summons << summon
puts "#{#name} summoned #{summon.name}"
end
Example with output:
player1 = Hands.new('Silk', 'Lyfe', 'Summon')
player1.summon(summons)
# Silk summoned Berto
player1.summon(summons)
# Silk summoned Skye
The #summons array will be populated as expected:
player1.summons
#=> [
# #<Summons:0x00007fbf1c9d80b8 #name="Berto", #strength="1", #health=25, #mana=25>,
# #<Summons:0x00007fbf1c9189c0 #name="Skye", #strength="1", #health=25, #mana=25>
# ]
Note that passing the array to Player#summon is just one possible solution. You could also pass it to Player.new (i.e. initialize) and store it in an instance variable. Or – if the array is rather static – you could store it right within the Player class, maybe in a constant. It really depends on how you want to structure your code.

attr_accessor - Accessing an objects attributes from another class

I want to access the ogre's object's swings attribute from the Human's class. However, all I am getting is:
NameError: undefined local variable or method ogre for
**<Human:0x007fdb452fb4f8 #encounters=3, #saw_ogre=true>
Most likely a simple solution, and my brain is just not operating this morning. I am running tests with minitest. The test and classes are below:
ogre_test.rb
def test_it_swings_the_club_when_the_human_notices_it
ogre = Ogre.new('Brak')
human = Human.new
ogre.encounter(human)
assert_equal 0, ogre.swings
refute human.notices_ogre?
ogre.encounter(human)
ogre.encounter(human)
assert_equal 1, ogre.swings
assert human.notices_ogre?
end
ogre.rb
class Ogre
attr_accessor :swings
def initialize(name, home='Swamp')
#name = name
#home = home
#encounters = 0
#swings = 0
end
def name
#name
end
def home
#home
end
def encounter(human)
human.encounters
end
def encounter_counter
#encounters
end
def swing_at(human)
#swings += 1
end
def swings
#swings
end
end
class Human
def initialize(encounters=0)
#encounters = encounters
#saw_ogre = false
end
def name
"Jane"
end
def encounters
#encounters += 1
if #encounters % 3 == 0 and #encounters != 0
#saw_ogre = true
else
#saw_ogre = false
end
if #saw_ogre == true
ogre.swings += 1 # <----issue
end
end
def encounter_counter
#encounters
end
def notices_ogre?
#saw_ogre
end
end
The easy fix would be to pass the ogre object as an argument to encounters - assuming encounters isn't used anywhere else without the argument.
class Ogre
...
def encounter(human)
human.encounters(self)
end
...
end
class Human
...
def encounters(ogre)
#encounters += 1
if #encounters % 3 == 0 and #encounters != 0
#saw_ogre = true
else
#saw_ogre = false
end
if #saw_ogre == true
ogre.swings += 1 # <----issue
end
end
...
end

Ruby classes, subclasses and factory methods

I'm working on a TestFirst exercise (temperature_object) and have come to a standstill when it comes to integrating a subclass. So far I've got:
class Temperature
def initialize(opts = {})
#options = opts
#c = #options[:c]
#f = #options[:f]
end
def self.from_celsius(num)
self.new(:c => num)
end
def self.from_fahrenheit(num)
self.new(:f => num)
end
def in_celsius
if #options.has_key?(:c)
#c
elsif #options.has_key?(:f)
ctof(#f)
end
end
def in_fahrenheit
if #options.has_key?(:f)
#f
elsif #options.has_key?(:c)
ftoc(#c)
end
end
def ftoc(num)
(((num * 9) / 5.000) + 32)
end
def ctof(num)
(((num - 32) * 5) / 9.0000)
end
end
class Celsius < Temperature
def initialize(num)
#c = num
end
end
class Fahrenheit < Temperature
def initialize(num)
#f = num
end
end
All of the tests pass until I get to the following:
require "temperature_object"
describe Temperature do
# Here's another way to solve the problem!
describe "Temperature subclasses" do
describe "Celsius subclass" do
it "is constructed in degrees celsius" do
Celsius.new(50).in_celsius.should == 50
Celsius.new(50).in_fahrenheit.should == 122
end
it "is a Temperature subclass" do
Celsius.new(0).should be_a(Temperature)
end
end
describe "Fahrenheit subclass" do
it "is constructed in degrees fahrenheit" do
Fahrenheit.new(50).in_fahrenheit.should == 50
Fahrenheit.new(50).in_celsius.should == 10
end
it "is a Temperature subclass" do
Fahrenheit.new(0).should be_a(Temperature)
end
end
end
end
So, I'm thinking the problem is that I'm trying to go from Temperature.new, which takes a hash, to Celsius.new, which only takes a value. I'm getting an undefined method "has_key?" for nil:NilClass error message. Do I need to set num as a hash value and assign it a key? If so, how do I do that? If not, any suggestions?
Your problem is that you refer to #options, but you don't assign it when creating an instance of Celsius. You should call the super constructor in your inherited classes:
class Celsius < Temperature
def initialize(num)
super(c: num)
end
end
class Fahrenheit < Temperature
def initialize(num)
super(f: num)
end
end
Now, when you call Celsius.new(50) the initialize(opts) will be called as if you called Temperature.new(c: 50), and all members will be properly assigned.

How to implement a blur event for Ruby Shoes

I'm experimenting with Ruby Shoes. I want controls that become editable as you give them focus, and become text again when they loose it. So far I have the following...
class NameBox < Shoes::Widget
def initialize(model, opts = {})
#model = model
#para = para(value)
self.click{
edit
}
self.keypress{|key|
display if key==:enter
}
end
def display
#ed && #ed.hide
#para.show
#para.text = value
end
def edit
#ed ||= edit_line(value) {|e|
#model.rename(e.text)
}
#para.hide
#ed.text = value
#ed.show
end
def value
#model.name
end
end
used by
class Model
attr_reader :name
def initialize(name)
#name = name
end
def rename(new_name)
#name = new_name
end
end
Shoes.app do
#variable = Model.new("1 2 3")
stack do
10.times{ name_box(#variable) }
end
end
This implementation means if you click on more than one control, they will both be edit boxes.
What I was hoping for was a blur event that would let me change the control back to 'display'. This doesn't exist, so.. how would you implement it?
Assume I will be writing a bunch more controls and they all have to abide by this rule of 'one focused control'
** for Bonus points explain why I can't put:
#ed ||= edit_line(value) {|e|
#model.rename(e.text)
}
#ed.hide()
in the initialize and get #ed to be hidden.
How about this one?
class NameBox < Shoes::Widget
def initialize(model, opts = {})
#model = model
#para = para(value)
self.click{
edit
}
end
def display
#ed && #ed.hide
#para.show
#para.text = value
end
def edit
#ed ||= edit_box(value, height: 30) {|e|
e.text[-1] == "\n" ? display : #model.rename(e.text)
}
#para.hide
#ed.text = value
#ed.show
end
def value
#model.name
end
end
class Model
attr_reader :name
def initialize(name)
#name = name
end
def rename(new_name)
#name = new_name
end
end
Shoes.app do
#variable = Model.new("1 2 3")
stack do
10.times{ name_box(#variable) }
end
end

How i can validate attr in Ruby

How I can validate attr in Ruby?
My file airplane.rb
class Airplane
include Validatable
attr_reader :aircraft_type, :weight
attr_accessor :speed, :altitude, :course
def initialize(aircraft_type, options={})
#aircraft_type = aircraft_type.to_s
#course = options[:course] || random_course
#weight = options[:weight] || rand(1...1000)
#speed = options[:speed] || rand(1...500)
#apltitude = options[:apltitude] || rand(50...3000)
#position_x = options[:position_x] || rand(1...3000)
#position_y = options[:position_y] || rand(1...3000)
check_course
end
def position
#position = [#position_x, #position_y]
end
def check_course
if #course < 1
#course = 1
puts "Invalid course. Set min"
elsif #course > 360
#course = 360
puts "Invalid course. Set max"
else
#course = #course
end
end
def random_course
#course = rand(1..360)
end
end
My file validatable.rb where all values ​​must be checked
module Validatable
##validations={}
# I need receive = {presence: [:weight, :length], aircraft_type: [:length]}
def self.validates_presence_of(*attrs)
##validations[:presence] = attrs
end
def validate
##validations.each do |v, fields|
fields.each {|field_name| self.send("validate_#{v}_of", field_name)}
end
end
private
def validate_presence_of(field_name)
end
end
My file init.rb with airplanes attr
airplane1 = Airplane.new("Boeing 74", course: 600, speed: 300, apltitude: 300)
airplane2 = Airplane.new("Boeing 700", course: 250, speed: 300, apltitude: 300)
airplane3 = BigAirplane.new("Boeing 707", weight: 50, speed: 300, apltitude: 400)
How I can finish validatable.rb to validate each value in each airplane?
Use ActiveModel::Validations instead of re-inventing the wheel.
Refer:
http://yehudakatz.com/2010/01/10/activemodel-make-any-ruby-object-feel-like-activerecord/
and
http://www.rubyinside.com/rails-3-0s-activemodel-how-to-give-ruby-classes-some-activerecord-magic-2937.html
and
http://asciicasts.com/episodes/211-validations-in-rails-3
Good luck.

Resources