it's correct create multiple classes in ruby? - ruby

Hi i have a question because i usually use multiple classes in Ruby but i dont know if is that correct?, for example the next code:
Class Main.rb
class Main
require_relative 'Archivo.rb'
require_relative 'Diccionario.rb'
require_relative 'Jsn.rb'
hsh = Diccionario.new
jsn = Jsn.new
fl = Archivo.new
puts "Ingresa Nombre"
nombre = gets
puts "Ingresa Correo"
correo = gets
puts "Ingresa password"
password = gets
hsh.usuario(nombre,correo,password)
jsn.convert_json(hsh.get_usuario)
fl.write('usuario.json',jsn.get_json)
fl.read('usuario.json')
puts fl.get_line
end
Class Diccionario.rb in other file
class Diccionario
$usuarios = Hash.new
require 'json'
def usuario(nombre, correo, password)
$usuarios = {nombre: nombre, correo: correo, password: password}
end
def get_usuario
$usuarios
end
end
Class Jsn.rb in other file
class Jsn
require 'json'
$cadena
def convert_json(cadena)
$cadena = cadena.to_json
end
def get_json
$cadena
end
end
Class Archivo.rb in other file
class Archivo
$line
def read(file)
File.open(file,"r") {|archivo|
$line =archivo.gets
}
end
def write(file,cadena)
File.open(file, "w+") do |f|
f.puts cadena
end
end
def get_line()
$line.to_s
end
end
Thanks :D

You should definitely be creating multiple classes! I would try to think in terms of SOLID principles to ascertain where behaviour belongs and which objects/classes have responsibility for implementing them.
There's an excellent book called Practical Object Oriented Design in Ruby by Sandi Metz. It's a great book about how to organise your code into classes. It talks about SRP (Single Responsibility Principle) and how to drill down to the essence of the problem you're trying to solve. If you can't buy the book then there's a talk that covers the main points here:
https://www.youtube.com/watch?v=8bZh5LMaSmE
She actually mentions that it's a common error to "not create enough classes" and that you should feel confident to break the implementation down further to smaller manageable classes.

Related

Capybara.page not in scope after extending capybara-screenshot's after_failed_example method

I'm trying to override the after_failed_example method so I can inflict some custom file naming on our screenshots. I'm loading the module as an initializer.
So far, so good, but the Capybara.page.current_url is blank, making me think I need to require something additional?
require "capybara-screenshot/rspec"
module Capybara
module Screenshot
module RSpec
class << self
attr_accessor :use_description_as_filename
attr_accessor :save_html_file
end
self.use_description_as_filename = true
self.save_html_file = true
def self.after_failed_example(example)
if example.example_group.include?(Capybara::DSL) # Capybara DSL method has been included for a feature we can snapshot
Capybara.using_session(Capybara::Screenshot.final_session_name) do
puts ">>>> Capybara.page.current_url: " + Capybara.page.current_url.to_s
if Capybara::Screenshot.autosave_on_failure && failed?(example) && Capybara.page.current_url != ''
saver = Capybara::Screenshot.new_saver(Capybara, Capybara.page, Capybara::Screenshot.save_html_file?, set_saver_filename_prefix(example))
saver.save
example.metadata[:screenshot] = {}
example.metadata[:screenshot][:html] = saver.html_path if saver.html_saved?
example.metadata[:screenshot][:image] = saver.screenshot_path if saver.screenshot_saved?
end
end
end
private
def self.set_saver_filename_prefix(example)
return example.description.to_s.gsub(" ", "-") if Capybara::Screenshot.use_description_as_filename?
return Capybara::Screenshot.filename_prefix_for(:rspec, example)
end
end
end
end
end
This is successfully overriding the capybara-screenshot/rspec method, and any of the Capybara::Screenshot static information is accessible, but not Capybara session related information (afa I can tell).
For example, Capybara.page.current_url.to_s is null when overridden, but present when not.
I was missing a require (kind of silly mistake):
require 'capybara/rspec'

Ruby Class Output

I am trying my hands at Ruby, below is the code that I am writing in 2 different ways to understand Ruby Classes. In first block I am using accessor method (combination of accessor read & write) and I want to print final line as "lord of the rings is written by Tolkien and has 400 pages". How can I make that happen? I understand that adding string and integer will throw an error. I can get them to print on separate lines, its just that I can't get them in a sentence.
class Book
attr_accessor :title, :author, :pages
end
book1 = Book.new()
book1.title = 'lord of the rings'
book1.author = 'Tolkien'
book1.pages = 400
puts book1.title
puts book1.author
puts book1.pages
#puts book1.title + " is written by " + book1.author + " and has " + book1.pages + " pages" <<<this errors out for known reason>>>
Second piece of code doing the same thing but I am using instance variable and have figured out how to get desired output. However, please advise if there's a better way of doing this?
class Novel
def initialize(title, author, pages)
#title = title
#author = author
#pages = pages
end
def inspect
"#{#title} is written by #{#author} and has #{#pages} pages"
end
end
novel1 = Novel.new('harry potter', 'JK Rowling', 300)
puts novel1.inspect
In your first example you are providing access the info you want and leaving it up to the client to format the output. For example you could have gotten what you wanted by adding this line in place of your commented line.
puts "#{book1.title} is written by #{book1.author} and has #{book1.pages} pages"
In your second example you are "pushing" that code down into the Novel class and proving a method to produce the output you want. BTW, don't use inspect as a method name, inspect is already a defined method
For example the following will print the same info twice.
class Novel
attr_accessor :title, :author, :pages
def initialize(title, author, pages)
#title = title
#author = author
#pages = pages
end
def info
"#{#title} is written by #{#author} and has #{#pages} pages"
end
end
novel = Novel.new('harry potter', 'JK Rowling', 300)
puts novel.info
puts "#{novel.title} is written by #{novel.author} and has #{novel.pages} pages"

Struggling to write code and tests for my Tube System program, TDD using Rspec and Ruby

I am writing a small program for a train system.
I have a passenger, coach, train and station class (and thus, a spec test for each).
My test for my passenger class is as such:
let (:passenger) {Passenger.new}
it "should not be touched in to a station when initialized" do
expect(passenger.touchedin?).to be false
end
it "should be able to enter coach" do
coach = Coach.new
passenger.enter(coach)
expect{coach.to receive(:enter)}
end
it "should be able to alight coach" do
coach = Coach.new
passenger.alight(coach)
expect{coach.to receive(:alight)}
end
it "should be able to touch into station" do
station = Station.new
passenger.touchin(station)
expect{station.to receive(:touchin)}
end
it "should be able to touch out of station" do
station = Station.new
passenger.touchout(station)
expect{station.to receive(:touchout)}
end
end
And my passenger class is like this (at the moment :p):
class Passenger
def initialize
#touchedin = false
end
def enter(coach)
end
def touchedin?
#touchedin
end
def alight(coach)
end
def touchin(station)
end
def touchout(station)
end
end
I am unsure how to satisfy my tests, if my tests are even correct in the first place.
Any help is really appreciated!
You've not really said how you're modeling the relationship between coaches and passengers, but one way I could think of could be as follows. I'm just putting enough for the coach/passenger relationship (so nothing about touching in as this involves the station) - and I'm using minitest syntax, but I think you can get the idea of what's happening.
class Coach
def initialize
#passengers = []
end
...
end
class Passenger
def initialize
#touched_in = false
end
def alight(coach)
coach.passengers << self.uid # or self, if you want the whole passenger object available
end
...
end
coach = Coach.new
assert_empty coach.passengers
joe = Passenger.new
refute_includes coach.passengers, joe.uid # or joe
joe.alight(coach)
assert_includes coach.passengers, joe.uid # or joe

Assert_equal undefined local variable LRTHW ex52

Hi I made it to the lase exercise os Learn Ruby The Hard Way, and I come at the wall...
Here is the test code:
def test_gothon_map()
assert_equal(START.go('shoot!'), generic_death)
assert_equal(START.go('dodge!'), generic_death)
room = START.go("tell a joke")
assert_equal(room, laser_weapon_armory)
end
And here is the code of the file it should test:
class Room
attr_accessor :name, :description, :paths
def initialize(name, description)
#name = name
#description = description
#paths = {}
end
def ==(other)
self.name==other.name&&self.description==other.description&&self.paths==other.paths
end
def go(direction)
#paths[direction]
end
def add_paths(paths)
#paths.update(paths)
end
end
generic_death = Room.new("death", "You died.")
And when I try to launch the test file I get an error:
generic_death = Room.new("death", "You died.")
I tried to set the "generic_death = Room.new("death", "You died.")" in test_gothon_map method and it worked but the problem is that description of the next object is extremely long, so my questions are:
why assertion doesn't not respond to defined object?
can it be done different way then by putting whole object to testing method, since description of the next object is extremely long...
The nature of local variable is that they are, well, local. This means that they are not available outside the scope they were defined.
That's why ruby does not know what generic_death means in your test.
You can solve this in a couple of ways:
define rooms as constants in the Room class:
class Room
# ...
GENERIC_DEATH = Room.new("death", "You died.")
LASER_WEAPON_ARMORY = Room.new(...)
end
def test_gothon_map()
assert_equal(Room::START.go('shoot!'), Room::GENERIC_DEATH)
assert_equal(Room::START.go('dodge!'), Room::GENERIC_DEATH)
room = Room::START.go("tell a joke")
assert_equal(room, Room::LASER_WEAPON_ARMORY)
end
assert the room by its name, or some other identifier:
def test_gothon_map()
assert_equal(START.go('shoot!').name, "death")
assert_equal(START.go('dodge!').name, "death")
room = START.go("tell a joke")
assert_equal(room.name, "laser weapon armory")
end

Passing an object as subject to rspec

I am running rspec tests on a catalog object from within a Ruby app, using Rspec::Core::Runner::run:
File.open('/tmp/catalog', 'w') do |out|
YAML.dump(catalog, out)
end
...
unless RSpec::Core::Runner::run(spec_dirs, $stderr, out) == 0
raise Puppet::Error, "Unit tests failed:\n#{out.string}"
end
(The full code can be found at https://github.com/camptocamp/puppet-spec/blob/master/lib/puppet/indirector/catalog/rest_spec.rb)
In order to pass the object I want to test, I dump it as YAML to a file (currently /tmp/catalog) and load it as subject in my tests:
describe 'notrun' do
subject { YAML.load_file('/tmp/catalog') }
it { should contain_package('ppet') }
end
Is there a way I could pass the catalog object as subject to my tests without dumping it to a file?
I am not very clear as to what exactly you are trying to achieve but from my understanding I feel that using a before(:each) hook might be of use to you. You can define variables in this block that are available to all the stories in that scope.
Here is an example:
require "rspec/expectations"
class Thing
def widgets
#widgets ||= []
end
end
describe Thing do
before(:each) do
#thing = Thing.new
end
describe "initialized in before(:each)" do
it "has 0 widgets" do
# #thing is available here
#thing.should have(0).widgets
end
it "can get accept new widgets" do
#thing.widgets << Object.new
end
it "does not share state across examples" do
#thing.should have(0).widgets
end
end
end
You can find more details at:
https://www.relishapp.com/rspec/rspec-core/v/2-2/docs/hooks/before-and-after-hooks#define-before(:each)-block

Resources