Having trouble with how classes work - ruby-on-rails

I'm testing this code for a class assignment to make sure it works.
I don't quite understand code yet. Maybe give me some pointers.
require 'minitest/autorun'
require './human'
require './coffee'
## This assignment made more sense when I wrote it in the morning.
class CaffeineTest < MiniTest::Test
def test_humans_tend_to_be_sleepy
tyler = Human.new "Tyler"
assert tyler.alertness < 0.1
end
def test_humans_need_coffee
randy = Human.new "Randy"
refute randy.has_coffee?
assert randy.needs_coffee?
end
def test_humans_can_drink_coffee
sherri = Human.new "Sherri"
tsmf = Coffee.new "Triple Shot Mocha Frappuccino"
assert tsmf.full?
sherri.buy tsmf
sherri.drink!
assert_in_epsilon sherri.alertness, 0.33, 0.1
refute tsmf.full?
refute tsmf.empty?
end
def test_humans_can_drink_all_the_coffee
trevor = Human.new "Trevor"
tsmf = Coffee.new "Triple Shot Mocha Frappuccino"
trevor.buy tsmf
3.times { trevor.drink! }
assert tsmf.empty?
assert trevor.alertness > 0.9
end
end
Here's what I've added to it.
class Human
def initialize(name)
#name = name
#alertness = 0.0
#flavor = Coffee.new(name)
#coffee = 0
end
def alertness
#alertness
end
def has_coffee?()
false
end
def needs_coffee?()
true
end
def drink!()
#alertness += 0.33
#coffee -= 0.33
end
def empty?
if #coffee <= 0
true
else
false
end
end
def buy(type)
#flavor
#coffee += 1 #add tsmf to some kind of array or some shit
end
end
class Coffee
def initialize(drink)
#coffee = 0
end
def full?
if #coffee >= 0
true
else
false
end
end
def has_coffee?
false
end
end
I keep getting failures on empty? and full?. Please guide me a little here.

require 'minitest/autorun'
## This assignment made more sense when I wrote it in the morning.
class CaffeineTest < MiniTest::Test
def test_humans_tend_to_be_sleepy
tyler = Human.new "Tyler"
assert tyler.alertness < 0.1
end
def test_humans_need_coffee
randy = Human.new "Randy"
refute randy.has_coffee?
assert randy.needs_coffee?
end
def test_humans_can_drink_coffee
sherri = Human.new "Sherri"
tsmf = Coffee.new "Triple Shot Mocha Frappuccino"
assert tsmf.full?
sherri.buy tsmf
sherri.drink!
assert_in_epsilon sherri.alertness, 0.33, 0.1
refute sherri.empty?
end
def test_humans_can_drink_all_the_coffee
trevor = Human.new "Trevor"
tsmf = Coffee.new "Triple Shot Mocha Frappuccino"
trevor.buy tsmf
3.times { trevor.drink! }
assert tsmf.empty?
assert trevor.alertness > 0.9
end
end
class Human
def initialize(name)
#name = name
#alertness = 0.0
#flavor = Coffee.new(name)
#coffee = 0
end
def alertness
#alertness
end
def has_coffee?()
false
end
def needs_coffee?()
true
end
def drink!()
#alertness += 0.33
#coffee -= 0.33
end
def empty?
if #coffee <= 0
true
else
false
end
end
def buy(type)
#flavor
#coffee += 1 #add tsmf to some kind of array or some shit
end
end
class Coffee
def initialize(drink)
#coffee = 0
end
def full?
if #coffee >= 0
true
else
false
end
end
def has_coffee?
false
end
def empty?
if #coffee <= 0
true
else
false
end
end
end

Related

I need help fixing this "Could not parse audio" error in ruby

I am trying to make an audio player program in ruby for a Uni assignment, and while trying to call the audio files into ruby, i keep running into the same error message, seen below
C:/Ruby31-x64/lib/ruby/gems/3.1.0/gems/gosu-1.4.3/lib/gosu/compat.rb:116:in `initialize': Could not parse audio file songs/Beautiful.mp3: Couldn't open songs/Beautiful.mp3 (RuntimeError)
from C:/Ruby31-x64/lib/ruby/gems/3.1.0/gems/gosu-1.4.3/lib/gosu/compat.rb:116:in `initialize'
from test.rb:141:in `new'
from test.rb:141:in `playTrack'
from test.rb:65:in `initialize'
from test.rb:185:in `new'
from test.rb:185:in `<main>'
Ive attached both the code itself and the contents on input.txt for more context, ive ommited the areas unrelated to the error to get the post size down
require 'rubygems'
require 'gosu'
class ArtWork
attr_accessor :bmp
def initialize(file)
#bmp = Gosu::Image.new(file)
end
end
class Album
attr_accessor :title, :artist, :artwork, :tracks
def initialize (title, artist, artwork, tracks)
#title = title
#artist = artist
#artwork = artwork
#tracks = tracks
end
end
class Track
attr_accessor :name, :location, :dim
def initialize(name, location, dim)
#name = name
#location = location
#dim = dim
end
end
class Dimension
attr_accessor :leftX, :topY, :rightX, :bottomY
def initialize(leftX, topY, rightX, bottomY)
#leftX = leftX
#topY = topY
#rightX = rightX
#bottomY = bottomY
end
end
class MusicPlayerMain < Gosu::Window
def initialize
super SCREEN_W, SCREEN_H
self.caption = "Music Player"
#track_font = Gosu::Font.new(25)
#album = read_album()
#track_playing = 0
playTrack(#track_playing, #album)
end
def read_track(a_file, idx)
track_name = a_file.gets.chomp
track_location = a_file.gets.chomp
leftX = X_LOCATION
topY = 100 * idx + 50
rightX = leftX + #track_font.text_width(track_name)
bottomY = topY + #track_font.height()
dim = Dimension.new(leftX, topY, rightX, bottomY)
track = Track.new(track_name, track_location, dim)
return track
end
def read_tracks(a_file)
count = a_file.gets.chomp.to_i
tracks = []
i = 0
while i < count
track = read_track(a_file, i)
tracks << track
i += 1
end
return tracks
end
def read_album()
a_file = File.new("input.txt", "r")
title = a_file.gets.chomp
artist = a_file.gets.chomp
artwork = ArtWork.new(a_file.gets.chomp)
tracks = read_tracks(a_file)
album = Album.new(title, artist, artwork.bmp, tracks)
a_file.close()
return album
end
def draw_albums(albums)
#album.artwork.draw(50, 50 , z = ZOrder::PLAYER, 0.3, 0.3)
#album.tracks.each do |track|
display_track(track)
end
end
def draw_current_playing(idx)
draw_rect(#album.tracks[idx].dim.leftX - 10, #album.tracks[idx].dim.topY, 5, #track_font.height(), Gosu::Color::RED, z = ZOrder::PLAYER)
end
def area_clicked(leftX, topY, rightX, bottomY)
if mouse_x > leftX && mouse_x < rightX && mouse_y > topY && mouse_y < bottomY
return true
end
return false
end
def display_track(track)
#track_font.draw(track.name, X_LOCATION, track.dim.topY, ZOrder::PLAYER, 1.0, 1.0, Gosu::Color::BLACK)
end
def playTrack(track, album)
#song = Gosu::Song.new(album.tracks[track].location)
#song.play(false)
end
def draw_background()
draw_quad(0,0, TOP_COLOR, 0, SCREEN_H, TOP_COLOR, SCREEN_W, 0, BOTTOM_COLOR, SCREEN_W, SCREEN_H, BOTTOM_COLOR, z = ZOrder::BACKGROUND)
end
def update
if not #song.playing?
#track_playing = (#track_playing + 1) % #album.tracks.length()
playTrack(#track_playing, #album)
end
end
def draw
draw_background()
draw_albums(#album)
draw_current_playing(#track_playing)
end
def needs_cursor?; true; end
def button_down(id)
case id
when Gosu::MsLeft
# --- Check which track was clicked on ---
for i in 0..#album.tracks.length() - 1
if area_clicked(#album.tracks[i].dim.leftX, #album.tracks[i].dim.topY, #album.tracks[i].dim.rightX, #album.tracks[i].dim.bottomY)
playTrack(i, #album)
#track_playing = i
break
end
end
end
end
end
MusicPlayerMain.new.show if __FILE__ == $0
Windwaker
Love Language
images/Love_Language.jpg
11
Beautiful
songs/Beautiful.mp3
Lucy
songs/Lucy.mp3
Nighthawk
songs/Nighthawk.mp3
Dopamine Freestyle
songs/Dopamine Freestyle.mp3
Me + You
songs/Me + You, But Mostly Me.mp3
Glow
songs/Glow.mp3
Trenches
songs/Trenches.mp3
Superstitious Fantasy
songs/Superstitious Fantasy.mp3
Love Language
songs/Love Language.mp3
Hide & Seek
songs/Hide & Seek.mp3
The Rain
songs/The Rain.mp3

Testing whether a Ruby object is in fact waiting

I have a system, comprised of a BoundedQueue class, a Producer class that pushes items into a BoundedQueue object and a Consumer class that takes items out of the BoundedQueue object, the Producer and Consumer exist on separate threads. So that items aren't lost when they're pushed to a full queue I use a condition variable and a mutex to tell the Producer to wait until the queue has a space.
I need to create a test case that checks that the Producer is waiting when the queue is full, I'm not sure if I'm being ditsy or not but I just can't think of how to do this properly.
BoundedQueue class:
class BoundedQueue
attr_reader :count
def initialize(size)
#mutex = Mutex.new
#rep = Array.new(size) if size > 0
#size = size
#back = size-1
#front = 0
#count = 0
#condvar = ConditionVariable.new
end
def isEmpty?
#count == 0
end
def isFull?
#count == #size
end
def put(item)
#mutex.synchronize do
if item != nil
while isFull?
#condvar.wait(#mutex)
end
#back += 1
#back = 0 if #back >= #size
#rep[#back] = item
#count += 1
end
end
end
def get
#mutex.synchronize do
result = nil
if (!isEmpty?)
result = #rep[#front]
#rep[#front] = nil
#front += 1
#front = 0 if #front >= #size
#count -= 1
#condvar.signal
end
result
end
end
end
Producer class:
class Producer
def initialize(id, no_items, queue)
#id = "Producer#{id}"
#no_items = no_items
#queue = queue
end
def produce
while #no_items != 0
#queue.put("Item #{#no_items} from #{#id} ")
puts "#{#id} putting item #{#no_items} into the queue "
#no_items -= 1
end
end
end
Consumer class:
class Consumer
def initialize(queue)
#queue = queue
end
def consume
while !#queue.isEmpty?
puts "#{#queue.get} consumed from the queue"
sleep(Random.rand(10))
end
end
end
The test case so far:
require "./boundedQueue.rb"
require "./producer.rb"
require "./consumer.rb"
mutex = Mutex.new
cv = ConditionVariable.new
test_queue = BoundedQueue.new(5)
puts "Creating the producer"
producerOne = Producer.new(0,7,test_queue)
puts "Creating consumer"
consumer = Consumer.new(test_queue)
puts "Creating threads"
a = Thread.new{
producerOne.produce
}
b = Thread.new{
consumer.consume
}
puts "Joining threads"
b.join
a.join

Ruby - uninitialized constant

I have this 4 classes, the code is simple but when I try to generate, it returns an error like
in `<main>': uninitialized constant PruebasUnitarias (NameError)
The code is this and I dont know what is wrong, I thought it was because of requires, but I have all in one document and still crash.
class Complejo
def initialize (real, imaginario)
#real = real
#imaginario = imaginario
end
def sumar (complejo)
#real = #real + complejo.real
#imaginario = #imaginario + complejo.imaginario
end
attr_reader :real, :imaginario
end
class Prueba
def assertCierto(valor)
return valor
end
def assertFalso(valor)
return valor
end
def assertIgual(num1, num2)
if(num1 == num2)
return true
end
return false
end
def assertDistinto(num1, num2)
if(num1 != num2)
return true
end
false
end
def assertNil param
if (param == nil)
return true
end
false
end
def assertContiene(param1, param2)
param1.include?(param2)
end
end
class PruebasUnitarias::Prueba
def run
metodos = self.methods
if(self.respond_to? :inicializacion)
self.inicializacion
end
end
end
class PruebaComplejo < PruebasUnitarias::Prueba
def inicializacion
#c1 = Complejo.new(3,5)
#c2 = Complejo.new(1,-1)
end
def prueba_suma
#c1.sumar(#c2)
assertIgual(#c1.real, 4)
assertIgual(#c1.imaginario, 4)
end
def prueba_suma_cero
#c2.sumar(Complejo.new(0,0))
assertCierto(#c2.real==1)
assertCierto(#c2.imaginario==-1)
end
def prueba_suma_nula
#c2.sumar(nil)
assertIgual(#c2.real, 1)
assertIgual(#c2.imaginario, -1)
end
def imprimir (complejo)
puts "complejo: #{complejo.real}, #{complejo.imaginario}i"
end
end
You have to declare the module before you can put a class in it. Try this instead:
module PruebasUnitarias
class Prueba
...
end
end
class PruebaComplejo < PruebasUnitarias::Prueba
...
end
Just as a note about your programming style, you need to think in simpler terms when it comes to testing your values. Consider these changes:
def assertCierto(valor)
valor
end
def assertFalso(valor)
valor
end
def assertIgual(num1, num2)
num1 == num2
end
def assertDistinto(num1, num2)
num1 != num2
end
def assertNil param
param == nil
end
It's not necessary to use an if test to see if something is equal or not equal and then return true or false. The test itself does that so simply compare the values and let Ruby return the result by default.
1 == 1 # => true
1 == 2 # => false
1 != 1 # => false
1 != 2 # => true
Also, use snake_case for method names, not camelCase. ItsAReadabilityThing:
def assert_cierto(valor)
def assert_falso(valor)
def assert_igual(num1, num2)
def assert_distinto(num1, num2)
def assert_nil param

A ruby script and a rails model: Same code - different behaviour

I have coded the following ruby script:
require 'open-uri'
require 'Nokogiri'
require 'anemone'
class JobFox
attr_accessor :company_url,
:jobs_page,
:max_words,
:jobs_part,
:jobs_container,
:element_score,
:max_score,
:jobs
def calc_element_score(element)
self.element_score += (element['class'].to_s.scan(/job|career|position|opening/).count + element['id'].to_s.scan(/job|career|position|opening/).count) * 100
self.element_score += element.to_s.scan(/job|career|position|opening/).count * 5
element.css('a').each do |a|
self.element_score += a.to_s.scan(/job|career|position|opening/).count * 7
end
element.css('li').each do |li|
self.element_score += li.to_s.scan(/job|career|position|opening/).count * 5
end
element.css('h').each do |h|
self.element_score += h.to_s.scan(/job|career|position|opening/).count * 3
end
if self.element_score > self.max_score
self.max_score = self.element_score
self.jobs_part = element
end
if element.children.count == 0
self.element_score = 0
end
end
end
fox = JobFox.new
fox.company_url = 'http://www.website.com'
fox.max_words = 0
fox.jobs = []
# CRAWL THE WEBSITE TO FIND THE JOBS LINK
Anemone.crawl(fox.company_url, :depth_limit => 3) do |anemone|
anemone.on_pages_like(/job|jobs|career|careers|team|about/) do |page|
begin
puts "SCANNING: " + page.url.to_s
# SCAN THE HTML AND FIND THE OCCURENCES OF THE WORD "JOB"
source_html = open(page.url).read
job_occurences = source_html.scan(/job|jobs|work|position/).count
# IF MORE OCCURENCES THAN BEFORE, WE KEEP THE PAGE URL
if job_occurences > fox.max_words
fox.max_words = job_occurences
fox.jobs_page = page.url
end
rescue Exception => e
puts e
end
end
end
fox.jobs_container = Nokogiri::HTML(open(fox.jobs_page))
fox.element_score = fox.max_score = 0
fox.jobs_container.css('div, section').each do |container|
container.traverse do |element|
fox.calc_element_score(element)
end
end
fox.jobs_part.traverse do |element|
element.css('a').each do |job|
fox.jobs << job.text
end
end
# REMOVE POSSIBLE DUPLICATE ENTRIES
fox.jobs = fox.jobs.uniq
puts fox.jobs
and I am trying to port it to a rails application - not as a script/task but as a model function:
require 'anemone'
require 'open-uri'
require 'Nokogiri'
class Company < ActiveRecord::Base
has_many :jobs
accepts_nested_attributes_for :jobs
# CALCULATE THE RELATEDNESS OF EACH HTML ELEMENT
def calculate_element_score(element)
#jobs_expression = '/job|career|position|opening/'
#element_score += (element['class'].to_s.scan(#jobs_expression).count + element['id'].to_s.scan(#jobs_expression).count) * 100
#element_score += element.to_s.scan(#jobs_expression).count * 5
element.css('a').each do |a|
#element_score += a.to_s.scan(#jobs_expression).count * 7
end
element.css('li').each do |li|
#element_score += li.to_s.scan(#jobs_expression).count * 5
end
element.css('h').each do |h|
#element_score += h.to_s.scan(#jobs_expression).count * 3
end
if #element_score > #max_score
#max_score = #element_score
#jobs_part = element
end
if element.children.count == 0
#element_score = 0
end
end
# CRAWL THE WEBSITE TO FIND THE JOBS PAGE
def find_jobs_page
max_words = 0
Anemone.crawl(self.website, :depth_limit => 3) do |anemone|
anemone.on_pages_like(/job|jobs|career|careers|team|about/) do |page|
begin
# SCAN THE HTML AND FIND OCCURENCES OF RELEVANT WORDS
source_html = open(page.url).read
job_occurences = source_html.scan(/job|jobs|work|position/).count
# IF MORE OCCURENCES THAN BEFORE, KEEP THE PAGE URL
if job_occurences > max_words
max_words = job_occurences
self.jobs_page = page.url
end
rescue Exception => e
puts e
end
end
end
end
# FIND THE CONTAINER THAT HAS THE JOB LISTINGS
def find_jobs_container
jobs_container = Nokogiri::HTML(open(self.jobs_page))
#element_score = #max_score = 0
#jobs_expression = '/job|career|position|opening/'
jobs_container.css('div, section').each do |container|
container.traverse do |element|
self.calculate_element_score(element)
end
end
end
# ADD THE JOBS FROM THE PAGE TO THE COMPANY ASSOCIATION
def extract_jobs
#jobs_part.traverse do |element|
element.css('a').each do |job|
j = JOBS.new()
j.title = job.text
j.url = job
self.jobs << j
end
end
end
# THE METHOD TO FIND ALL THE JOBS FOR A COMPANY
def find_jobs
self.find_jobs_page
self.find_jobs_container
self.extract_jobs
end
end
Everything works just fine apart from the calculate_element_score method - #elements_score is always 0. Have I understood something entirely wrong regarding global variables?

How should i transform this concern in service object?

I have a concern allowing me to give the back end user the ability to sort elements. I use it for a few different elements. The rails community seems to be pretty vocal against concern and callbacks, i'd like to have a few pointers on how to better model the following code :
require 'active_support/concern'
module Rankable
extend ActiveSupport::Concern
included do
validates :row_order, :presence => true
scope :next_rank, lambda { |rank| where('row_order > ?',rank).order("row_order asc").limit(1)}
scope :previous_rank, lambda { |rank| where('row_order < ?',rank).order("row_order desc").limit(1)}
scope :bigger_rank, order("row_order desc").limit('1')
before_validation :assign_rank
end
def invert(target)
a = self.row_order
b = target.row_order
self.row_order = target.row_order
target.row_order = a
if self.save
if target.save
true
else
self.row_order = a
self.save
false
end
else
false
end
end
def increase_rank
return false unless self.next_rank.first && self.invert(self.next_rank.first)
end
def decrease_rank
return false unless self.previous_rank.first && self.invert(self.previous_rank.first)
end
private
def assign_default_rank
if !self.row_order
if self.class.bigger_rank.first
self.row_order = self.class.bigger_rank.first.row_order + 1
else
self.row_order=0
end
end
end
end
I think a Concern is a good choice for what you are trying to accomplish (particularly with validations and scopes because ActiveRecord does those two very well). However, if you did want to move things out of the Concern, apart from validations and scopes, here is a possibility. Just looking at the code it seems like you have a concept of rank which is represented by an integer but can become it's own object:
class Rank
def initialize(rankable)
#rankable = rankable
#klass = rankable.class
end
def number
#rankable.row_order
end
def increase
next_rank ? RankableInversionService.call(#rankable, next_rank) : false
end
def decrease
previous_rank ? RankableInversionService.call(#rankable, previous_rank) : false
end
private
def next_rank
#next_rank ||= #klass.next_rank.first
end
def previous_rank
#previous_rank ||= #klass.previous_rank.first
end
end
To extract out the #invert method we could create a RankableInversionService (referenced above):
class RankableInversionService
def self.call(rankable, other)
new(rankable, other).call
end
def initialize(rankable, other)
#rankable = rankable
#other = other
#original_rankable_rank = rankable.rank
#original_other_rank = other.rank
end
def call
#rankable.rank = #other.rank
#other.rank = #rankable.rank
if #rankable.save && #other.save
true
else
#rankable.rank = #original_rankable_rank
#other.rank = #original_other_rank
#rankable.save
#other.save
false
end
end
end
To extract out the callback you could have a RankableUpdateService which will assign the default rank prior to saving the object:
class RankableUpdateService
def self.call(rankable)
new(rankable).call
end
def initialize(rankable)
#rankable = rankable
#klass = rankable.class
end
def call
#rankable.rank = bigger_rank unless #rankable.ranked?
#rankable.save
end
private
def bigger_rank
#bigger_rank ||= #klass.bigger_rank.first.try(:rank)
end
end
Now you concern becomes:
module Rankable
extend ActiveSupport::Concern
included do
# validations
# scopes
end
def rank
#rank ||= Rank.new(self)
end
def rank=(rank)
self.row_order = rank.number; #rank = rank
end
def ranked?
rank.number.present?
end
end
I'm sure there are issues with this code if you use it as is, but you get the concept. Overall I think the only thing that might be good to do here is extracting out a Rank object, other than that it might be too much complexity that the concern encapsulates pretty nicely.

Resources