Ruby - Running an iteration by calling require 'filename' - ruby

I had a thought and so far the thought has failed so I wanted to share it and have it corrected.
I have a series of Ruby scripts written to walk through a ecommerce site from adding to cart to checking out. The scripts are all referencing each other in order to keep the chain moving along. What I want to do is make a Ruby file called Run_CheckOut.rb but be able to run through several iterations of the checkout by invoking this file x amount of times.
Here was my first try:
i = 0
10.times do
i+= 1
puts "Iteration number: " + i.to_s
require 'Test_OrderService_SubmitCart'
end
When I do this it will only call the required file once but continues to count for i. How can I call the required file 10 times or am I completely off base with this?

am I completely off base with this?
Yes.
What I want to do is make a Ruby file called Run_CheckOut.rb but be able to run through several iterations of the checkout by invoking this file x amount of times.
The point of require is to make code available to your execution environment. require only pulls in the code once -- calling it again has no effect. load will run the included script each time, but that is a poor design choice.
What you want is to invoke something in the source code file. Perhaps you'd like to call a method, or create an object from a class. Define a method:
def do_something
# blah blah
end
require the file at the top of your script:
require 'Test_OrderService_SubmitCart'
and invoke the method in the loop:
i = 0
10.times do
i+= 1
puts "Iteration number: " + i.to_s
do_something
end

You are looking for load (note that you need to append .rb):
load 'Test_OrderService_SubmitCart.rb'
Contrary to load, require executes a source file only once. See the detailed description of what require does in the reference documentation.

Also there is a ruby convention for naming files and methods - lowercased and underscored.
Class and Module names are camel cased. These two conventions are never mixed.
Your final code should look something more like:
require 'order_service'
10.times do |n|
puts "iteration #{n}"
submit_cart
end
Notice the beauty.
Good luck!

Related

Conditionally defining functions in Ruby

I have some code that is run in one of a few different locations: as a command line tool with debug output, as part of a larger program that doesn't take any output, and in a rails environment.
There are occasions where I need to make slight changes to the code based on its location, and I realized the following style seems to work:
print "Testing nested functions defined\n"
CLI = true
if CLI
def test_print
print "Command Line Version\n"
end
else
def test_print
print "Release Version\n"
end
end
test_print()
This results in:
Testing nested functions defined
Command Line Version
I've never come across functions that are defined conditionally in Ruby. Is this safe to do?
This isn't how I'm structuring most of my code, but there are a few functions that require complete rewrites per-system.
I don't think that is a clean way.
My suggestion is to define the same sets of methods (with different definition bodies) in different modules, and conditionally include the relevant module into the class/module you are going to call the methods from.
module CLI
def test_print
... # definition for CLI
end
end
module SomeOtherMode
def test_print
... # definition for some other mode
end
end
class Foo
include some_condition ? CLI : SomeOtherMode
end
Foo.new.test_print
If you are only going to use only one mode per run, and think that it is a waste to define the modules that end up not being used, then you can take a further step; define respective modules (CLI, SomeOtherMode, ...) in separate files, and use autoload.
autoload :CLI, "path/to/CLI"
autoload :SomeOtherMode, "path/to/SomeOtherMode"
It's a form of meta-programming and is generally safe. The real risk is not if it will work as expected, but in testing all the variations you create.
The example you've given here makes it impossible to execute the alternate version. To properly exercise both methods you need a way to force the injection of one or the other.

How to make ruby methods run before and after each [given, when, then] step?

I'm trying to get some easy-to-read time metrics for how long steps of each scenario take to run.
I have methods that insert rows into a logging table like this:
puts "#{Time.now} - Starting Given"
puts "#{Time.now} - Ending Given"
etc...
And I want them to be called automatically at the start and end of each step, but I can only find ways to run them before or after the entire scenario, rather than each step.
Is there a way to do this?
Ruby comes with a benchmarking module to handle this stuff pretty easily for you.
Edit to address the comments below
In Cucumber, there is a hook called Around that allows you to wrap your scenario however you wish. For example, straight from the docs:
Around('#fast') do |scenario, block|
Timeout.timeout(0.5) do
block.call #actually runs the scenario
end
end

Pass variables between separate instances of ruby (without writing to a text file or database)

Lets say I'm running a long worker-script in one of several open interactive rails consoles.
The script is updating columns in a very, very, very large table of records. I've muted the ActiveRecord logger to speed up the process, and instruct the script to output some record of progress so I know how roughly how long the process is going to take. That is what I am currently doing and it would look something like this:
ModelName.all.each_with_index do |r, i|
puts i if i % 250
...runs some process...
r.save
end
Sometimes its two nested arrays running, such that there would be multiple iterators and other things running all at once.
Is there a way that I could do something like this and access that variable from a separate rails console? (such that the variable would be overwritten every time the process is run without much slowdown)
records = ModelName.all
$total = records.count
records.each_with_index do |r, i|
$i = i
...runs some process...
r.save
end
meanwhile mid-process in other console
puts "#{($i/$total * 100).round(2)}% complete"
#=> 67.43% complete
I know passing global variables from one separate instance of ruby to the next doesn't work. I also just tried this to no effect as well
unix console 1
$X=5
echo {$X}
#=> 5
unix console 2
echo {$X}
#=> ""
Lastly, I also know using global variables like this is a major software design pattern no-no. I think that's reasonable, but I'd still like to know how to break that rule if I'd like.
Writing to a text file obviously would work. So would writing to a separate database table or something. That's not a bad idea. But the really cool trick would be sharing a variable between two instances without writing to a text file or database column.
What would this be called anyway? Tunneling? I don't quite know how to tag this question. Maybe bad-idea is one of them. But honestly design-patterns isn't what this question is about.
Some solutions I worked out using the answers:
Here's a quick implementation I set up that appears to be working:
The system typically requires three separate classes (in my case, I use classes for all because I'm in rails and its easier)
the counter class: this is passed into DRb and then all its methods can be accessed via the client. Thus, this object can be extremely complex.
class Counter
attr_accessor :i
def initialize
#i = 0
end
def report(total)
"#{(#i.to_f / total.to_f * 100).round(2)}%"
end
end
The counter server. Counter is passed into this one. In my case the process hangs and thus does not return anything and cannot be accessed. I haven't figured out how to run it as a daemon accessible via object so its best to pass in the uri and control that so nothing needs to be returned. Whats nice about this is you can just let it run for a very long time and rewrite the variables accessible via the accessor methods called via attr_accessor. Ruby makes it super easy.
require 'drb'
class CounterServer
def initialize(uri="druby://:9000")
DRb.start_service(uri, Counter.new)
puts "server running on #{DRb.uri}"
trap("INT") {DRb.stop_service}
DRb.thread.join
end
end
the client. allows you to access the server.
require 'drb'
class CounterClient
attr_reader :client, :total
def initialize(uri="druby://:9000", records)
#client = DRbObject.new nil, uri
#total = records.count
end
def incremement
#client.i += 1
end
def monitor_and_report
values = []
puts "you must first set the #total value! This should be done in the ruby script being monitored by passing in the records as a first variable for initialize" if #client.total.nil?
5/0 if #client.total.nil?
while ((#client.i) < (#client.total))
values << pctg_complete
puts "#{pctg_complete}%"
last_index = (values.count - 1)
percentage_per_second = get_slope(values[last_index], values[last_index - 1])
puts get_eta(percentage_per_second, values[last_index]) unless values.count == 1
sleep 10
end
end
def get_slope(latest, second_latest)
run = 10.0# seconds
rise = (latest - second_latest)
slope = (rise/run)
slope
end
def get_eta(velocity, current)
puts "velocity: #{velocity}"
puts "current: #{current}"
pctg_left_to_complete = (100.0 - current)
puts "pctg_left: #{pctg_left_to_complete}"
estimated_seconds_remaining = pctg_left_to_complete / velocity
minutes_left = (estimated_seconds_remaining / 60.0).round(0)
"estimated #{minutes_left} minutes until completion"
end
So, in the example of my question:
console 1
CounterServer.new
#=> hangs while server runs
console (ruby script) 2
records = ModelName.all
c = CounterClient.new(nil, records)
records.each_with_index do |r, i|
c.increment
...runs code...
r.save
end
console 3
c = CounterClient.new
c.monitor_and_report
Note several weeks later looking at this again:
This code could be much simpler. For one, the Counter class doesn't need to be reporting anything. All it needs is the attr_accessor method. The server & client could be stripped down to only just a few lines as well.
However, the script here has a bit more of a thematic element to it. I have yet to use it once since I wrote it but learning how to communicate between separate ruby consoles seems to me to be a very powerful skill.
UPDATE
The "smaller functions" of this watch no not work. Namely the eta function. It's a piece of ****
You need Drb. It works by creating a distributed ruby service(server), a client then connects to it and is able to fetch Ruby objects from it.
http://www.ruby-doc.org/stdlib-1.9.3/libdoc/drb/rdoc/DRb.html
Have ruby emit the data on a unix channel, like STDERR or STDOUT. Then pipe that output to the other ruby-based receiver.
That secondary process will do nothing but idle and the check its STDIN for data – finding any, it will printed to the screen.
You could also have the long-running script do this by emitting the data when it traps a given signal. Thus, the logging operation happens only as frequently as you signal it.
memcached_client = get_memcashed_singleton
begin
if record_count % 250 == 0
Signal.trap("USR1") do
memcached_client.store('lrp1_percent_done', batch_position )
end
Signal.trap("TERM") do
puts "Terminating..."
stop_processing_at_next_logical_block
end
end
# . . . do some work . . .
while records_left_in_giant_processing_table?
Overall, though, technically a better architectural strategy is that your long-running process opens a client connection to memcached for another pre-existing service within your network – and writes out the log data to that.
The only thing you will pay for that is the memory for the connection, and vanishingly tiny times for communicating to the memory-based server.
You have to think about what it means to write a piece of information so that another process can read it, and what time it takes to do that. Principal and speaking, you either write to a disk for you write to a registered in memory.
A file on disk is akin to a socket.
A register in memory is akin to a report

Is it possible to refer to a parameter passed to a method within the passed block in ruby?

I hope I am not repeating anyone here, but I have been searching google and here and not coming up with anything. This question is really more a matter of "sexifying" my code.
What I am specifically trying to do is this:
Dir.new('some_directory').each do |file|
# is there a way to refer to the string 'some_directory' via a method or variable?
end
Thanks!
Not in general; it's totally up to the method itself what arguments the block gets called with, and by the time each has been called (which calls your block), the fact that the string 'some_directory' was passed to Dir.new has been long forgotten, i.e. they're quite separate things.
You can do something like this, though:
Dir.new(my_dir = 'some_directory').each do |file|
puts "#{my_dir} contains #{file}"
end
The reason it won't work is that new and each are two different methods so they don't have access to each others' parameters. To 'sexify' your code, you could consider creating a new method to contain the two method calls and pass the repeated parameter to that:
def do_something(dir)
Dir.new(dir).each do |file|
# use dir in some way
end
end
The fact that creating a new method has such a low overhead means it's entirely reasonable to create one for as small a chunk of code as this - and is one of the many reasons that make Ruby such a pleasure of a language to work with.
Just break it out into a variable. Ruby blocks are closures so they will have access -
dir = 'some_directory'
Dir.new(dir).each do |file|
# use dir here as expected.
end

Skip iteration from yield block in ruby

Trying to use an ill-conceived framework which collects a list of results from a passed-in block, effectively this:
def sigh(&block)
r = (1..3).collect do |i|
yield(i)
end
# do something with r
end
I want the block I pass in to filter the items, but to skip the collection iteration rather than adding nil to the results like next would (since the framework doesn't compact them.) What's a simple way other than patching the gem? I.e.,
sigh {|i| next unless i == 1 } # results in [1,nil,nil] rather than just [1]
The bad news is that you'll have to patch the gem. Having your code block called by the gem doesn't give your code any special powers to affect how the calling code processes the block's return values.
The good news is that patching the gem can usually be done with a "monkey patch," where your program reopens the gem's class or module and makes the change. In this made-up example, we'll show the class nested in a module, since many gems make use of nested classes and modules:
require 'somegem'
# Monkey patch to cause Somegem's do_something_cool method
# to ignore the SomethingBadHappened exception
module SomeGem
class SomeClass
alias_method :orig_do_something_cool, :do_something_cool
def do_something_cool
orig_do_something_cool
rescue SomethingBadHappened
end
end
end
There is no way to do what you are asking for. If you post more details on the framework you are using, though, someone here may be able to help you think of a different way to work around the problem.
You'll need to patch, like others said. If you want a collection of i which satisfy some condition, the best choice would to replace collect with find_all, and then you could use:
sigh { |i| i == 1 } #=> [1]

Resources