Shoes 4 ,Cannot call objects outside main app - ruby

Im developing a really simple application in Ruby and decided to use Shoes Gui, im trying to use the new fourth version but i cannot call shoes objects outside the app, for example in the Shoes Manual http://shoesrb.com/manual/Rules.html this code doesn't work:
class Messenger
def initialize(stack)
#stack = stack
end
def add(msg)
#stack.append do
para msg
end
end
end
when i pass the stack object from the app nothing happens, thanks for your help

I think you just missed out reading the manual completely. The manual very clearly says that this could apparently should work but it doesn't since the App object isn't around any more. Fortunately, each shoes object has a app method, so:
You should replace your code with:
class Messenger
def initialize(stack)
#stack = stack
end
def add(msg)
#stack.app do
#stack.append do
para msg
end
end
end
end
Hope it helps :)

I had the same issue described here, but with the right piece of code from the manual. I have found something, so I'm posting it here, hoping it'll help someone (or that someone will explain me what I've not found).
Here is what I had first (it doesn't work, no matter where you put your class instantiation).
class Messenger
def initialize(stack)
#stack = stack
end
def add(msg)
#stack.app do
#stack.append do
para msg
end
end
end
end
Shoes.app do
stack margin: 20, width: 20 do
subtitle "Shoes box"
para "Maaamaaaaa"
button "Mama ?" do
s = Messenger.new(#box)
s.add("Yeah, mama !")
end
end
#box = stack
end
First thing : if you got these two pieces of code in the same file, you could do that :
class Messenger
...
#stack.app do
#box.append do
para msg
end
...
end
It works.
It doesn't work if you remove the #stack.app do block or if you move this class in another file though. If you put your class instantiation somewhere else, it doesn't work either.
More interesting, now, and working even if you put your Messenger class somewhere else, you can do that :
class Messenger
...
def add(msg)
#stack.app do
para msg
end
...
Shoes.app do
...
button "Mama ?" do
#box.append do
s = Messenger.new(#box)
s.add("Yeah, mama !")
end
end
...
end
Now you also can move your class instantiation anywhere in your app block... but it still has to be BEFORE the box you're trying to append to, and that same box has to be declared BEFORE the button which is trying to add to it. Making the thing a bit tricky. Otherwise, you can let your class instantiation here. It means you will create a new object every time you're trying to append something to your box. And it is ugly. But it works.
Edit : just saw that, you can replace the #stack.append do by self.append do in the Messenger class. Though it doesn't really append anything, since it's adding the msg at the begining of your window.

First of all, change class Messenger to class Messenger < Shoes. This is essential for any and all classes in Shoes. Then, put the Shoes.app statement after the end statement of the class, but don't add the do. Your code should look like this:
class Messenger < Shoes
# Your code goes here...
end
Shoes.app [styling goes here]
NOTE: Put the Shoes.app only once, at the end, after all classes have been defined.

Related

Class methods vs Instances when using Page object model in ruby

I hope you are having a great day!
I'm wandering something, when using the POM (Page object model) we always create new instances of our classes, like the following simple example:
class LoginPage
def initialize(driver)
#driver = driver
end
def click_button
#driver.find_element(xpath: "//button[#title='Login']").click
end
end
# We create a new instance and we click the button
login_page = LoginPage.new(driver)
login_page.click_button
This makes sense for me, we can create multiple pages if we need to, we can update the state of whatever we need.
However, if we only would have one page open at the time, and we know nothing changes, doesn't it make more sense to take a class method based approach like?:
class LoginPage
class << self
def click_button
#driver.find_element(xpath: "//button[#title='Login']").click
end
end
end
# We create a new instance and we click the button
LoginPage.click_button
Please let me know your ideas, and if you have tried this approach before

Why the program pass all tests if I use regular if statement in the method but says `stack level too deep` when using a ternary operator instead?

I was working on coding challenge called Robot name. I also had tests for that. The program passed all the tests. The code is below..
class Robot
attr_accessor :name
##robots = []
def initialize
#name = self.random_name
##robots << self.name
end
def random_name
name = ''
2.times do
name << ('a'..'z').to_a.sample
end
3.times do
name << (1..9).to_a.sample.to_s
end
no_duplicate(name.upcase)
end
def reset
#name = self.random_name
end
def no_duplicate(name)
if ##robots.include? name
reset
else
name
end
end
end
If you need to see the tests file you can look it up here robot_name_tests.
Then I started to refactor and one of the first things was to refactor no_duplicate method. So after refactoring the code looked like this
class Robot
...
# the rest of code stayed the same
def no_duplicate(name)
##robots.include? name ? reset : name
end
end
With this version all tests showed SystemStackError: stack level too deep. Why does it give this error and what is going on behind the scenes in both cases considering the code provided? Thanks!
I like your poetry mode code but it has led you into trouble here.
One way to kinda keep it in poetry mode but fix your operator priority issue is to do this:
def no_duplicate(name)
(##robots.include? name) ? reset : name
end
Update: if you work in Big Corporation With Coding Standards you will need to make it a bit more boring. I thought this was obvious but the gallery is correctly noting the usual solution:
##robots.include?(name) ? reset : name

User interrupt in Ruby infinite loop (multiple classes)?

I found another question very similar to mine with a solution that worked for me when I wrote it all in one simple script. I even wrote a second simple example sort of simulating what I'm trying to do, and it seemed to still work.
My simulation was:
class A
def looper(&block)
Thread.new do
loop do
exit if gets.chomp == 'q'
end
end
loop do
block.call
end
end
end
class B < A
def looper
super do
puts 'howddyyyy from B'
end
end
end
This works fine, exiting when you press q<Enter>. However, when I tried to implement this into my actual project, it fails to work. I'll post the code from the method in question in the child class, as the parent class is literally exactly the same as the example above.
def looper
super do
if obj = Object.first(:process_status => STATUS_UNPROCESSED)
puts "[Object ##{obj.id}] Processing..."
puts "-" * 60
obj.set_failed
if #obj.process(obj)
obj.set_processed
end
puts "-" * 60
puts "[Object ##{obj.id}] Finished!"
puts
puts
else
sleep 10
end
end
end
So, for some reason, this doesn't work. I put a puts into the new Thread (listening for q), and it seems to output the puts before every loop of block.call. Maybe it just isn't able to get the key, by which I mean, maybe the timeframe in which you have to enter q<Enter> is way too small? I'm not sure, which is why I'm asking some advice here. My only other guess is that it has something to do with the methods called within this method (process, or possible the Sequel calls to the database) blocking the other thread(s)?
I'm new to threading, so I have no clue.
Okay, everybody. I feel a little stupid for typing all that up, as I came to a solution not five minutes later (and one I had overlooked here on Stack Overflow).
For anyone facing a similar issue in the future, this is what I ended up doing (in the parent class):
def looper(&block)
interrupted = false
trap("INT") { interrupted = true }
until interrupted do
block.call
end
exit
end
This manages to achieve what I was essentially trying to do.
Thanks for reading!

Using the Ruby Shoes GUI toolkit, how would I go about being able to edit my GUI in different areas of code

I'm writing a horse race/bet game using Shoes and I was wondering how I would be able to change the GUI in different areas of code. When I run this, I get the horse on one application and then the race line on another application, but I want them both on the same application. Do I need to set the actual Shoes app as a variable itself?
class Horse
def initialize()
#puts "YOYOYOYO"
##number=i
Shoes.app{
#icon= image 'horsey.jpg'
#icon.left = 100
#icon.top = 50
}
end
def neigh()
#puts "Neighhhh"
end
def raceTime()
time=rand(100)%20
return time+10
end
end
class HorseIcon
def initialize(h)
#horse= h
#imageloc='horsey.jpg'
end
end
class Game
def initialize(h1, h2)
contestants=[h1, h2]
Shoes.app{
#icon= image 'raceline.jpg'
#icon.left = 100
#icon.top = 70
}
end
def race()
end
end
game= Game.new(1,2)
seabiscuit= Horse.new()
You are using two separate Shoes.app classes. I think that's your problem.
Judging by your code you seem to have a background in some other language, like Python.
I suggest you clone the Shoes git and look at 'Shoes/samples' directory and play around with it.
Or just look at this.
It will help you see what the code should look like.
PS : It will also give you some pointers toward Ruby style. You normally don't use {} for block when using multiple lines. You would use:
Shoes.app do
# code goes here
end

ruby alternative to class << thing

I want to rewrite several methods of HighLine to customise my console and at the moment my code looks like this:
cmd = ask("#{#prompt_label} #{#prompt_separator} ", #tab_completion_candidates) do |q|
q.readline = true
# rewriting the Question class to make it do what we want
class << q
HERE I WRITE MY CODE
end
end
I would like to be able to separate my changes from my main console file, so let's say i have a class Console::Question that contains all the changes I want to do in HighLine::Console, I'd like to be able to do something like that:
Console::Question << q
end
But unfortunately that doesn't work :)
Any solution?
Thanks for your time.
If you put your changes in a module rather than a class then you can then do
q.extend(YourModule)
e.g. to override valid_answer?
module QuestionCustomisations
def valid_answer?
# your code here
end
end
q.extend(QuestionCustomisations)
This will apply your changes in just the object instance which is passed to the block.

Resources