Calling a ruby script using a shell command fails at 'load_rakefile' - ruby-on-rails

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!

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)

VBS script fails to run when called as a Scheduled Task in Windows 8

I have created a VBS script that runs perfectly fine when manually run on Windows 8 Home computers even on IDs that have no admin rights. This same script fails repeatedly no matter what user credentials I've used to run it through a scheduled task on computer start up. I've tried the system, admin, and unprivileged user IDs that are on the machine all with no success.
This same VBS script has no issues at all being run from a scheduled task on multiple Windows 7, and Windows 8.1 Home and Professional machines.
This script attempts to successfully ping a website, and upon successful ping will POST data to the web page with the current time of the local computer.
boolExitFlag = False
Do
If Ping("HOSTNAME") Then
Call pingsuccess
boolExitFlag = True
End If
WScript.sleep 1000
Loop while boolExitFlag <> True
Sub pingsuccess
set oHTTP = CreateObject("Microsoft.XMLHTTP")
oHTTP.open "POST", "URL",false
oHTTP.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
oHTTP.setRequestHeader "Content-Length", Len("send=send&time=" & Hour(Now) & ":" & Minute(Now) & ":" & Second(Now))
oHTTP.send "send=send&time=" & Hour(Now) & ":" & Minute(Now) & ":" & Second(Now)
End Sub
Function Ping(strHost)
Dim oPing, oRetStatus, bReturn
Set oPing = GetObject("winmgmts:{impersonationLevel=impersonate}").ExecQuery("select * from Win32_PingStatus where address='" & strHost & "'")
For Each oRetStatus In oPing
If IsNull(oRetStatus.StatusCode) Or oRetStatus.StatusCode <> 0 Then
bReturn = False
Else
bReturn = True
End If
Set oRetStatus = Nothing
Next
Set oPing = Nothing
Ping = bReturn
End Function
When the scheduled task is run on the Windows 8 machines I see
"The task is currently running. (0x41301)" with no success. I have tried nearly EVERYTHING to attempt to get this working outside of writing a batch script to run the VBS script. That just seems excessively unnecessary though.
The only way that I could manage to get Windows 8 to run this VBS script was to create a batch file that called the VBS, and checked the box in the scheduled task to "Run with highest privileges". Without both the script would not run. I am not 100% sure why only Windows 8 fails miserably without doing this.
The batch file contained only the following code.
start "" "C:\path\to\script.vbs"

What's the proper way to run Ruby scripts in parallel?

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.

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`

Strange variable-argument problem in Capistrano Task

Hello stackoverflow experts,
I got a very strange problem in a task I'm creating with Capistrano. I'm trying to pass a variable from the command line:
>> cap create_dir -s name_of_dir=mydir
task :create_dir do
printf("#{name_of_dir}")
if !(exists?(:name_of_dir)) then
name_of_dir = Capistrano::CLI.ui.ask("Name of dir to be created.")
end
full_path = "/home/#{name_of_dir}"
run "mkdir #{full_path}"
end
The very strange this is that correctly parses the variable when I do printf, but parses as a blank(empty) string in the following command. I really find no explanation for this and I'm sure is not a stuping typo or anything like that?
I'm not expierenced in Ruby like in Java and PHP, I'm affraid that there maybe a strange rule?
Thanks!!
A few suggestions:
Avoid using variables with the same name of internal task variables
use fetch() instead of dealing with if exits? else then...
Here's the code
>> cap create_dir -s name_of_dir=mydir
task :create_dir do
printf("#{name_of_dir}")
directory = fetch(:name_of_dir) { Capistrano::CLI.ui.ask("Name of dir to be created.") }
full_path = "/home/#{directory}"
run "mkdir #{full_path}"
end
In newer versions of capistrano, at least from 2.5.19 which I run now the whole command line argument thing works different now. You call it like this.
cap command argument=value
And the syntax in the code is
ENV.has_key?('argument') and ENV['argument']
That's basically it, but you can look at my blogpost about it for a working example
It looks like in the second line you are checking if the symbol :name_of_dir exists - not the actual value of the variable name_of_dir.
Because you're unlikely to have a filename name_of_dir it will count as not existing... and then name_of_dir (the variable) is overwritten by the Capistrano::CLI.ui.ask command.
Not sure why but that must be killing it somehow.
Try removing the ":" and seeing if that fixes the problem.

Resources