Reputation: 17209
I wrote a script that contains a few method definitions, no classes and some public code. Some of these methods execute some pretty time-consuming shell programs. However, these shell programs only need to be executed the first time the method is invoked.
Now in C, I would declare a static variable in each method to make sure those programs are only executed once. How could I do that in Ruby?
Upvotes: 6
Views: 9481
Reputation: 2567
Making sure shell commands are run only once is a recurring pattern. One solution I wrote, makes a checksum on the input files on the command line and only executes it when the shell command has not run before. It also executes again when input files have changed. See
https://github.com/pjotrp/once-only
Simply use it by prepending 'once-only' to the shell command. E.g.
bowtie -t e_coli reads/e_coli_1000.fq e_coli.map
becomes
once-only bowtie -t e_coli reads/e_coli_1000.fq e_coli.map
For PBS add a --pbs switch.
Upvotes: 0
Reputation: 29915
Unlike the other solutions in this thread, this solution doesn't require you to hold onto any state:
Get the method to remove itself after invocation or to overwrite itself with an empty method:
def hello
puts "hello"
define_singleton_method(:hello) {}
end
OR:
def hello
puts "hello"
singleton_class.send(:undef_method, __method__)
end
Upvotes: 2
Reputation: 108089
The "memoize" gem might be good here. When you memoize a method, it is called no more than once:
require 'memoize'
include Memoize
def thing_that_should_happen_once
puts "foo"
end
memoize :thing_that_should_happen_once
thing_that_should_happen_once # => foo
thing_that_should_happen_once # =>
Upvotes: 4
Reputation: 12830
There is an idiom in ruby: x ||= y
.
def something
@something ||= calculate_something
end
private
def calculate_something
# some long process
end
But there is a problem with this idiom if your 'long running utility' may return a false value (false or nil), since the ||=
operator will still cause the right side to be evaluated.
If you expect false values then use an additional variable, in a way similar to the proposed by DigitalRoss:
def something
return @something if @something_calculated
@something = calculate_something
@something_calculated = true
return @something
end
Don't try to save a line of code by setting the @something_calculated variable first, an then running calculate_something. If your calculate function raises an exception your function will always return nil and will never call the calculate again.
More generally, in Ruby you use instance variables. Note however, that they are visible in all the methods of given object - they are not local to the method.
If you need a variable shared by all instances, define the method in the class object, and in every instance call self.class.something
class User
def self.something
@something ||= calculate_something
end
def self.calculate_something
# ....
end
def something
self.class.something
end
end
Upvotes: 13
Reputation: 11299
def my_time_consuming_method
@result ||= begin
sleep 5
true
end
end
my_time_consuming_method # true after 5 secs
my_time_consuming_method # true directly
Upvotes: 1
Reputation: 146123
def f
system "echo hello" unless @justonce
@justonce = true
end
And, hmm, if you want it to run a shell command on invocation until it succeeds, you might try:
def f x
@justonce = system x unless @justonce
end
Upvotes: 2