Sharing list between functions - windows

I recently asked another question asking how to pass a list from one function to another, which was kindly answered by #Modred. Essentially what I've got now is this:
def run_command():
machines_off = []
# Some stuff .....
machines_off.append(machineName)
# Some stuff ....
wol_machines(machines_off)
def wol_machines(machines_off):
# Some stuff ....
(I've cleared off all the non-essential code for this example as it's 300+ lines).
Now, each function is called by clicking on a tkinter button; the run_command is always run and sometimes will add items to the list 'machines_off'. I only want it to action machines_off if the second function button is clicked. At the moment after clicking on the run_command button, it runs through the whole script, including the second function when I don't want it to. I assume as I'm forwarding the list onto the second function (with the 5th line), it's bypassing the need to click the second functions button.
What do I need to change/add to allow the list from the first function to be available to the second, but not action it until needed??
Many thanks,
Chris.

I'm guessing your code looks something like:
from Tkinter import Tk, Button
def run_command():
machines_off = []
# Some stuff .....
machineName = "foo"
machines_off.append(machineName)
# Some stuff ....
wol_machines(machines_off)
def wol_machines(machines_off):
print "wol_machines was called"
print "contents of machines_off: ", machines_off
# Some stuff ....
root = Tk()
a = Button(text="do the first thing", command=run_command)
b = Button(text="do the second thing", command=wol_machines)
a.pack()
b.pack()
root.mainloop()
If you want the functions to execute independently of one another, you shouldn't call wol_machines from within run_command. You'll have to find some other way for both functions to see the list. One way of doing this is to use a global value.
from Tkinter import Tk, Button
machines_off = []
def run_command():
#reset machines_off to the empty list.
#delete these next two lines if you want to retain old values.
global machines_off
machines_off = []
# Some stuff .....
machineName = "foo"
machines_off.append(machineName)
# Some stuff ....
def wol_machines():
print "wol_machines was called"
print "contents of machines_off: ", machines_off
# Some stuff ....
root = Tk()
a = Button(text="do the first thing", command=run_command)
b = Button(text="do the second thing", command=wol_machines)
a.pack()
b.pack()
root.mainloop()
This is the simplest change to your original code that can give you your desired behavior, but global values are generally considered a sign of bad design. A more object oriented approach could localize the global into a class attribute.
from Tkinter import Tk, Button
class App(Tk):
def __init__(self):
Tk.__init__(self)
self.machines_off = []
a = Button(self, text="do the first thing", command=self.run_command)
b = Button(self, text="do the second thing", command=self.wol_machines)
a.pack()
b.pack()
def run_command(self):
#reset machines_off to the empty list.
#delete this next line if you want to retain old values.
self.machines_off = []
# Some stuff .....
machineName = "foo"
self.machines_off.append(machineName)
# Some stuff ....
def wol_machines(self):
print "wol_machines was called"
print "contents of machines_off: ", self.machines_off
# Some stuff ....
root = App()
root.mainloop()

Related

How to skip an element when iterating through a list Ruby

I have the following script which loops through an element list:
$browser.divs(class:'menu-index-page__menu-category').map do |cat|
cate = cat.h3.text
puts cate
cc = cat.ul(class:'menu-index-page__items')
cc.lis(class:'menu-index-page__item').each do |lit|
lit.fire_event :click
sleep(5)
Once in a while the name of the class list changes to:
menu-index-page__item menu-index-page__item--unavailable
This breaks the script, and I want to be able to skip this whenever it comes up and continue with the original script.
Assuming that you are using Watir v6.5+, the :class locator supports excluding classes. You can find all elements that include class "a", but not "b" by doing:
browser.elements(class: ['a', '!b'])
For your specific example, you can do:
cc.lis(class: ['menu-index-page__item', '!menu-index-page__item--unavailable']).each do |lit|
# your actions on lit
end
When you include the lis using class, you have to be careful because when you locate using menu-index-page__item, it locates everything which includes menu-index-page__item so it locates menu-index-page__item menu-index-page__item--unavailable as well. So try forming xpath in this place, it would work
$browser.divs(class: 'menu-index-page__menu-category').map do |cat|
cate = cat.h3.text
puts cate
cc = cat.ul(class: 'menu-index-page__items')
cc.lis(xpath: "//li[#class='menu-index-page__item']").each do |lit|
lit.fire_event :click
end
end
Or you can exclude the specific list this way
$browser.divs(class:'menu-index-page__menu-category').map do |cat|
cate = cat.h3.text
puts cate
cc = cat.ul(class:'menu-index-page__items')
cc.lis(xpath: "//li[not(#class=menu-index-page__item menu-index-page__item--unavailable')]").each do |lit|
lit.fire_event :click
end
end
And also never do click via fireevent because that's the javascript click, it's not a selenium click, you would be missing many events which would be triggered after the click.
So perform
lit.click

How to create Ruby Tk radiobuttons in nested loops

Using Ruby Tk, I'm wanting to create sets of radio buttons in nested loops: an outer loop for devices (Foo, Bar, Baz), and inner loop for actions on each device (Start, Stop, Pause, Nuke).
Current code:
require 'tk'
device_names = %w/Foo Bar Baz/
action_names = %w/Start Pause Stop Nuke/
button_variables = Array.new(device_names.size)
root = TkRoot.new
foo = bar = nil
device_names.each_with_index do |device_name, i|
TkLabel.new(root) do
text device_name
pack {}
end
action_names.each_with_index do |action_name, j|
TkRadiobutton.new(root) do
text action_name
value j
variable button_variables[i]
pack {}
end
end
end
Tk.mainloop
This produces the 12 radiobuttons, but when I click on any of the 4 Pause buttons, for example, all 4 Pause buttons are set.
Other variations of the code produce the 12 radiobuttons, but all as one set; i.e., only one of the 12 is set at any given time.
What's needed to group these into 3 sets of 4 radiobuttons?
(This code is pared down from a much larger application, which is why it does not make much sense symantically.)
The value passed as variable is the name of a global variable.
The outer loop needs to dynamically define an instance variable, via instance_variable_set:
This code works:
require 'tk'
device_names = %w/Foo Bar Baz/
action_names = %w/Start Pause Stop Nuke/
root = TkRoot.new
device_names.each do |device_name|
TkLabel.new(root) do
text device_name
pack {}
end
instance_variable_set("##{device_name}", nil)
action_names.each do |action_name|
TkRadiobutton.new(root) do
text action_name
value action_name
variable device_name
pack {}
end
end
end
Tk.mainloop

Ruby Hash initialize confusion

I have two classes.
class Sky
attr_accessor :args
def initialize(args)
#args = args
puts 'Initializing sky'
end
end
class ShadowMask
attr_accessor :sky
def initialize(args)
args.each{|k, v| p "#{k}: #{v.to_s}"}
#sky = args.fetch(:sky, Sky.new({}))
end
end
ShadowMask can be created either with a default Sky:
sm_default = ShadowMask.new({})
# Initializing sky
# => #<ShadowMask:0x007fa215230eb0 #sky=#<Sky:0x007fa215230e60 #args={}>>
sm_default.sky
# => #<Sky:0x007fa215230e60 #args={}>
sm_default.sky.args
# => {}
or with a Sky that has been previously created:
skyobj = Sky.new("Sky Object")
# Initializing sky
# => #<Sky:0x007fa21481a020 #args="Sky Object">
sm = ShadowMask.new(:sky => skyobj)
# "sky: #<Sky:0x007fa21481a020>"
# Initializing sky
# => #<ShadowMask:0x007fa21521ae80 #sky=#<Sky:0x007fa21481a020 #args="Sky Object">>
In this second case, the instance of Sky already exists, and I do not want to see the output Initializing sky from the Sky initialization.
The problem with my actual code is that
puts 'Initializing sky'
is a call to a method that performs several calculations to complete the initialization and set several attributes. And this is repeated without need every time a ShadowMask is created.
Interestingly, if I replace
#sky = args.fetch(:sky, Sky.new({}))
with something like
#sky = args.fetch(:sky, 'AnyString')
it works fine, but I would loose the possibility of creating a new Sky if needed.
I am not sure if the problem is in the syntax or I am making a conceptual mistake.
I think you need to pass block to fetch in order to not see Initializing sky:
#sky = args.fetch(:sky) {Sky.new({})}
the idea behind this is that when you call any method, initially its params (in this case Sky.new({})) will be invoked. When you pass block - it will be invoked after inside method fetch, not before.
If all you want is to provide a default Sky object when key :sky is omitted, then fetch is a sub-optimal choice. This will work better:
#sky = args[:sky] || Sky.new({})
The "problem" with fetch is that this will result in a nil sm.sky:
sm = ShadowMask.new(sky: nil)
If this is desired behaviour for you, then use fetch. If not, use ||.

Ruby + recursive function + defining global variable

I am pulling bitbucket repo list using Ruby. The response from bitbucket will contain only 10 repositories and a marker for the next page where there will be another 10 repos and so on ... (they call it pagination)
So, I wrote a recursive function which calls itself if the next page marker is present. This will continue until it reaches the last page.
Here is my code:
#!/usr/local/bin/ruby
require 'net/http'
require 'json'
require 'awesome_print'
#repos = Array.new
def recursive(url)
### here goes my net/http code which connects to bitbucket and pulls back the response in a JSON as request.body
### hence, removing this code for brevity
hash = JSON.parse(response.body)
hash["values"].each do |x|
#repos << x["links"]["self"]["href"]
end
if hash["next"]
puts "next page exists"
puts "calling recusrisve with: #{hash["next"]}"
recursive(hash["next"])
else
puts "this is the last page. No more recursions"
end
end
repo_list = recursive('https://my_bitbucket_url')
#repos.each {|x| puts x}
Now, my code works fine and it lists all the repos.
Question:
I am new to Ruby, so I am not sure about the way I have used the global variable #repos = Array.new above. If I define the array in function, then each call to the function will create a new array overwriting its contents from previous call.
So, how do the Ruby programmers use a global symbol in such cases. Does my code obey Ruby ethics or is it something really amateur (yet correct because it works) way of doing it.
The consensus is to avoid global variables as much as possible.
I would either build the collection recursively like this:
def recursive(url)
### ...
result = []
hash["values"].each do |x|
result << x["links"]["self"]["href"]
end
if hash["next"]
result += recursive(hash["next"])
end
result
end
or hand over the collection to the function:
def recursive(url, result = [])
### ...
hash["values"].each do |x|
result << x["links"]["self"]["href"]
end
if hash["next"]
recursive(hash["next"], result)
end
result
end
Either way you can call the function
repo_list = recursive(url)
And I would write it like this:
def recursive(url)
# ...
result = hash["values"].map { |x| x["links"]["self"]["href"] }
result += recursive(hash["next"]) if hash["next"]
result
end

How to execute some procedure in all ruby files in current directory?

I'm new to Ruby - I'm having troubles on every step...
Imagine a Ruby script main.rb and a lot of unknown script files script1.rb ... scriptN.rb.
Each scriptX.rb contains unique module with one procedure needs to be executed:
Module X
def some_procedure(i)
puts "{#i} Module X procedure executed successfully!"
end
end
All I need is to:
iterate over all files in current directory
if current file has name like /^script.*?\.rb$/
then load it and execute some_procedure
How can I do it in main.rb ?
Thank you in advance!
Choose from these great answers in SO on loading the files: Best way to require all files from a directory in ruby?
Then in your files, just have them execute on load, rather than on a method call.
The problem might be that, when a file is required, it doesn't return the list of modules (or, in general, constants) which it defines. So, unless you don't know which module a script has defined, you will not know where to pass your some_procedure message.
As a workaround, you may try getting the list of defined constants before and after the script was required, find a difference, i.e. list of constants during require, and iterate through all of them, checking which one implements the method you need.
First, we need to put some restriction:
Every file script_my1.rb will have the module named Script_my1. I.e. first letter capitalized, all other letters - lowercase.
Create two files script_my1.rb and script_my2.rb as follows:
---script_my1.rb:
module Script_my1
#value = 0
def self.some_procedure(i)
puts "#{i} my1 executed!"
#value = i
end
def self.another_procedure()
return #value
end
end
---script_my2.rb:
module Script_my2
#value = 0
def self.some_procedure(i)
puts "#{i} my2 executed!"
#value = i
end
def self.another_procedure()
return #value
end
end
Now the main script, that loads and executes some_procedure() in each module, and then another_procedure().
Please notice, that each module can have separated variables with the same name #value.
Moreover, I think every module can be executed in a separate thread and have access to global variables, but I have not tested it yet.
---main.rb:
# Load all files from the current directory
# with name like script_xxx.rb
i = 1
result = nil
Dir['./script_*.rb'].each { |f|
next if File.directory?(f)
require (f)
moduleName = f[2,f.length].rpartition('.rb')[0].capitalize
eval ( "#{moduleName}.some_procedure(%d)" % i )
eval ( "result = #{moduleName}.another_procedure()" )
puts result
i = i + 1
}
Output of this program is:
1 my1 executed!
1
2 my2 executed!
2
That is all!
Some improvement to previous solution can be made. If we want to avoid special naming, we can use global hash to store procedure's names. Each loaded script_xx.rb file would register it's own procedures in this global hash.
Please notice, that in this case we make two cycles:
first we load all files script_xx.b
every file while loading will register it's procedures in $global_procs array.
then iterate over all entries in $global_procs to execute all registered procedures via eval()
Hope, this is a more 'ruby-like' solution!
---script_my1.rb
module My1
#value = 0
def self.some_procedure(i)
puts "#{i} my1 executed!"
#value = i
end
def self.another_procedure()
return #value
end
end
$global_procs << { 'module' => 'My1',
'some_procedure' => 'My1.some_procedure',
'another_procedure' => 'My1.another_procedure' }
---script_my2.rb
module MMM2
#value = 0
def self.some_procedure(i)
puts "#{i} MMM2 executed!"
#value = i
end
def self.another_procedure()
return #value
end
end
$global_procs << { 'module' => 'MMM2',
'some_procedure' => 'MMM2.some_procedure',
'another_procedure' => 'MMM2.another_procedure' }
---main.rb
# Create global array for holding module's info
$global_procs = []
Dir['./script_*.rb'].each { |f|
next if File.directory?(f)
require (f)
}
i = 1
result = nil
$global_procs.each { |p|
puts "Module name: " + p['module']
eval(p['some_procedure']+'(i)')
result = eval(p['another_procedure']+'()')
puts result
i = i + 1
}

Resources