What's the proper way to run Ruby scripts in parallel? - ruby-on-rails

Current I have the following code to run some ruby scripts, within a ruby script:
def run(base_directory, run_count)
working_directory = base_directory.gsub("\n","")
for i in 1..run_count
system("ruby " + working_directory + i.to_s + "\\" + "main.rb " + working_directory + i.to_s + "\\")
end
end
However this runs the scripts in a sequence, but I need them to run in parallel. Where I have 10 scripts to run, and I want to run 5 at a time until I reach the number of scripts that needs run. Is there a simple way to accomplish this?

Just discovered this gem parallel. You probably will have to run it like this:
results = Parallel.map(run_count.downto(1).to_a, :in_processes=>run_count){|i| system("ruby " + working_directory + i.to_s + "\\" + "main.rb " + working_directory + i.to_s + "\\") }

Not sure what is the proper way, but I usually just run a one-liner that executes something from the command line:
Say you have a file called create_file_with_input.rb that does whatever, like one line with: File.open("file_#{ARGV[0]}.txt", 'a').
you can do:
run_count = 5
%x(for i in `seq 0 #{run_count-1}`; do (ruby create_file_with_input.rb $i &); done)
That would create 5 files like (file_0.txt, file_1.txt, ..., file_4.txt etc.). Tweak it to fit your code from above and BOOM. You have multiple scripts running.
You could also potentially use fork, although I don't have much personal experience with it. See this question (or use The Google) for forking child processes. This way also provides the benefit of getting the PID of child processes.

Related

this.fs.move requires permission when running generator

I am trying to do the following without requiring manual intervention when running a generator:
Clone a repo
Move a sub directory within it to one level above
Delete the original directory
Can anyone suggest any steps I can take to run this generator without a prompt?
I think I maybe running into async issues where this.fs.move is run at the same time as this.fs.delete after the generator has run?
Possibly related to this:
As asynchronous APIs are harder to use, Yeoman provide a synchronous file-system API where every file gets written to an in-memory file system and are only written to disk once when Yeoman is done running.
This is the code I am using:
child_process.exec('git clone --depth 1 ' + aplugin.url + ' build/plugins/' + aplugin.directoryName + '-hassubdir', () => {
del([
'build/plugins/' + aplugin.directoryName + '/-hassubdir/readme.md',
'build/plugins/' + aplugin.directoryName + '/-hassubdir/.git'
])
.then(() => {
this.fs.move('build/plugins/' + aplugin.directoryName + '-hassubdir/' + aplugin.directoryName, 'build/plugins/' + aplugin.directoryName);
console.log(chalk.green('moved plugin dir to: build/plugins/' + aplugin.directoryName));
this.fs.delete('build/plugins/' + aplugin.directoryName + '-hassubdir');
console.log(chalk.green('deleted: build/plugins/' + aplugin.directoryName + '-hassubdir'));
})
});
Then in my yeoman output in terminal:
conflict build/plugins/pluginname-hassubdir/pluginname
? Overwrite build/plugins/pluginname-hassubdir/pluginname? overwrite
force build/plugins/pluginname-hassubdir/pluginname
create build/plugins/pluginname-hassubdir/pluginname/pluginnamePlugin.php
create build/plugins/pluginname/pluginnamePlugin.php
create build/plugins/pluginname-hassubdir/pluginname/resources/icon.svg
create build/plugins/pluginname/resources/icon.svg
create build/plugins/pluginname-hassubdir/pluginname/variables/pluginnameVariable.php
create build/plugins/pluginname/variables/pluginnameVariable.php
create build/plugins/pluginname-hassubdir/pluginname/resources
create build/plugins/pluginname-hassubdir/pluginname/variables
conflict build/plugins/pluginname-hassubdir
? Overwrite build/plugins/pluginname-hassubdir? (ynaxH)

Calling a ruby script using a shell command fails at 'load_rakefile'

I have run into a problem with using a shell command that calls a Ruby script, which then invokes Rake.
I have built a test automation framework that does the following when run from the command line (I'm on OSX Yosemite):
Calls a Ruby script which sets a bunch of Environment Variables
It then invokes Rake
rake = Rake.application
rake.init
rake.load_rakefile
rake['execute_tests'].invoke
The Rake file runs a Cucumber Task and the test framework then happily launches a browser and starts executing tests.
Cucumber::Rake::Task.new(:execute_tests) do |task|
# => need to populate these so that the cucumber.yml parses
ENV['TEST_WEB_PARALLEL_OS'] = "null"
ENV['TEST_WEB_PARALLEL_OS_VERSION'] = "null"
ENV['TEST_WEB_PARALLEL_BROWSER'] = "null"
ENV['TEST_WEB_PARALLEL_BROWSER_VERSION'] = "null"
#------------------------------------------------
# Specify rake profile
#------------------------------------------------
runProfile = ENV['TEST_PLATFORM'].downcase + "_" + ENV['TEST_INTERFACE'].downcase + "_" + ENV['TEST_ENVIRONMENT'].downcase + "_" + ENV['TEST_TYPE'].downcase
# => running headless
if ENV['TEST_HEADLESS'] == "TRUE"
# => need to truncate poltergeist - using the #poltergeist tag in cucumber was causing issues with other drivers
ENV['TEST_BROWSER'] = "POLTER"
reportProfile = ENV['TEST_PLATFORM'].downcase + "_" + ENV['TEST_INTERFACE'].downcase + "_" + ENV['TEST_ENVIRONMENT'].downcase + "_" + ENV['TEST_TYPE'].downcase + "_" + osHelper.getOperatingSystem.to_s + "_" + ENV['TEST_BROWSER'].downcase + "_hlst"
# => running headed
else
reportProfile = ENV['TEST_PLATFORM'].downcase + "_" + ENV['TEST_INTERFACE'].downcase + "_" + ENV['TEST_ENVIRONMENT'].downcase + "_" + ENV['TEST_TYPE'].downcase + "_" + osHelper.getOperatingSystem.to_s + "_" + ENV['TEST_BROWSER'].downcase + "_lst"
end
#------------------------------------------------
# Set the env var then run profile
#------------------------------------------------
ENV['REPORT_PROFILE'] = reportProfile
ENV['RUN_PROFILE'] = runProfile
task.profile = runProfile
end
This all works perfectly well when I execute from the command line. The problem is that I want to put a basic GUI on the front of the test framework. I am using Shoe3 to do this.
When I call my initial Ruby script from the GUI...
`ruby ./exe/execute_web_tests_local_singlethread.rb salesforce integration regression headed chrome false false`
...then the code executes until the point where the following line tries to execute:
rake.load_rakefile
At this the code fails over. I don't see any output or stacktrace from the sub-process so am unable to debug beyond the point of knowing that the process falls over at the line specified above.
Screenshot of GUI failing
Unfortunately that's as specific as I can be. If anybody could provide any pointers for how I might go about investigating and/or resolving this issue it would be much appreciated.
I have tried using Open3 as well, this has resulted in the same issue.
Cheers
When you shell out to Ruby with ruby ./exe/execute_web_tests_local_singlethread.rb, the child process will run in the current working directory of the parent process (i.e. the one your GUI application is running from).
When rake.load_rakefile is called, it will be looking for the Rakefile relative to the current working directory, not the directory relative to the script you're calling out to.
There's a couple of ways you can fix this. One is by setting the RAKEOPT environment variable in the parent process (your GUI) before you execute the command. This will be inherited by the child process:
ENV['RAKEOPT'] = "--rakefile ./exe/Rakefile"
Alternatively, you can change the working directory in the parent process:
Dir.chdir("./exe") do
`ruby execute_web_tests_local_singlethread.rb salesforce integration regression headed chrome false false`
end
This might not be advisable. If your application is threaded and relying on the current directory (and Shoes may be), you might have some unexpected consequences modifying the current directory.
One last thing: you may not be able to start up a child process that runs a a browser in this way. Both full browsers and headless browsers, to the best of my knowledge, need information about the graphical environment you're running in. This is fine when you're running a process attached to a GUI terminal session, but you might run into other issues trying to spin up another graphical process from inside Shoes.
Hope that helps!

XOJO how to pass arguments to external executable

I need to open external exe file, and in the same time to pass some arguments to it.
The documentation in Xojo Library suggest to use the Shell, but I have not seen the practical example how to do it.
Xojo Shell command Explanation:
Dim sh As New Shell
sh.Execute("Location to a file")
I have tried the following:
sh.Execute("Location to a file" + " " + myArgumentOne + " " + myArgumentTwo)
There is no error, just the *.exe is not being run.
If there is solution using FolderItem, I would gladly use it as well.
You may not need to use a shell. When you have the folderitem, use Launch to execute the program and pass parameters. For instance
dim f as folderitem = GetFolderItem("C:\myprogram.exe", Folderitem.PathTypeShell)
f.Launch("Parameter1, Parameter2")
See http://docs.xojo.com/index.php/FolderItem.Launch
sh.Execute F.ShellPath +"\program.exe " + parameters
F is a Folderitm pointing to the directory of the program and parameters is a string

Output to file is different between staging and local

I am printing some data to a custom log file. This is the code I am currently using:
print eco_logger.info("Date: " + Time.now.strftime("%I:%M:%S") + " | " + "User: " + current_user.email.to_s() + " | " + "Action: Edited User | User: " + #user.first_name.to_s + " " + #user.last_name.to_s + " | Email: " + #user.email.to_s)
In my local dev environment, it works well. Each print would go on a new line like so:
log
log
log
However, when I pushed the application to the staging dev server, the log started to print like this:
logloglog
I also tried \n, to see if that would work; like so:
print eco_logger.info("\nDate: " + Time.now.strftime("%I:%M:%S") + " | " + "User: " + current_user.email.to_s() + " | " + "Action: Edited User | User: " + #user.first_name.to_s + " " + #user.last_name.to_s + " | Email: " + #user.email.to_s)
Locally, it added another space so things were like this:
log
log
log
At staging, things remained the same.
Would anyone be able to shed some light on this issue?
Why not use the built in logger versus your complex print statement?
logger.debug "Person attributes hash: #{#person.attributes.inspect}"
logger.info "Processing the request..."
logger.fatal "Terminating application, raised unrecoverable error!!!"
So assuming you have eco_logger set to write to your own log file, then calling eco_logger.info should be enough to write a line to the file.
I am not at all sure why you are also using print with this, that seems redundant and probably like part of the problem.
Can you explain why you are using both? The print by itself like this will print the line to STDOUT with no linebreak at the end (that is the difference between puts and print).
What it looks like is that in development you might be seeing what is written by the eco_logger.info statement, which will write with line breaks to the eco_logger specified file.
However, on staging I think you are watching some log where the print output to STDOUT is captured, which does not include line breaks.

Precompile / preload cached fragments in Rails

Using Rails 3.1.1 and Herkou
I have 1.000 products in my app. They all have a very slow controller which is effectively solved by fragment caching. Although the data doesn't change very often, it still needs to expire (which I do by sweeping) periodically, in my case once a week.
Now, after sweeping the cached views I don't want my users to create the new fragments by trying to access the products one after another (takes about 6-8 secs at the first load, 2-3 sec for the cached load). I assume I can do that with some sort of script that will load each Product Page one by one and thus make the server create those fragments.
I can imagine this can be handled in three ways:
Run a script on my local machine that will try to access each url with some sort of get-command - Downside: Not very pretty and will affect visitor stats in a way I would not prefer.
Run the same type script on the server after the sweeper, that will load each Product. How can I do that, in that case?
Using a smart Rails command to do this automatically. Is there such an elegant command?
I made this script and it works. The "product.slug" is because I have friendly_id installed. It will produce url-variables with names such as www.mydomain.com/productabc-123/ which will be read by Nokogiri (Nokogiri gem is needed for this solution).
PLEASE NOTE THAT I SWITCHED FROM FRAGMENT CACHING TO ACTION CACHING IN THIS SOLUTION (as opposed to the question, where I am using fragment caching). The important difference for this is when I check the cache if Rails.cache.exist?('views/www.mydomain.com/' + product.slug). For fragment_caching it should be the fragment name there instead.
require 'nokogiri'
require 'open-uri'
Product.all.each do |product|
url = 'http://www.mydomain.com/' + product.slug
begin
if Rails.cache.exist?('views/www.mydomain.com/' + product.slug)
puts url + " is already in cache"
else
doc = Nokogiri::HTML(open(url))
puts "Reads " + url
# Verifies if the caching worked. Only for trouble shooting
if Rails.cache.exist?('views/www.mydomain.com/' + product.slug)
puts "--->" + url + " is NOW in the cache"
else
puts "--->" + url + " is still not in the cache!"
end
sleep 1
end
rescue
puts 'Normal rescue of ' + url
rescue Timeout::Error
puts 'Timeout rescue of ' + url
puts 'Sleep for 5 sec'
sleep 5
retry
end
end
Create a script that runs as rake task, or better yet a worker, that runs and curls the page. There is no need to include a gem when you can just call curl
`curl -A "CacheRefresher" #{ENV['HOSTNAME']}/api/v1/#{klass.name.underscore.pluralize}/#{id} >/dev/null 2>&1`

Resources