phantomjs: how to make fs.open behave the same as require, for paths - phantomjs

require() works based on the script path. fs.open() works on the invocation (current shell) path.
Let's say i have the tree:
dirA/
dirA/dirB/
dirA/dirB/dirC/
dirA/dirB/dirC/test.js
dirA/dirB/dirC2/include_me.js
dirA/dirB/dirC2/open_me.js
and the contents for test.js:
var myMod = require('../dirC2/include_me.js');
var fs = require('fs');
fs.open('../dirC2/open_me.js')
Regardless of the current path I am when i execute phantomjs $PATH/test.js the require will succeed, and the fs.open will fail (unless $PATH is dirC)
Is there any way to make fs.open() behave as require()?
I'm thinking something like fs.open( phantomjs.getScriptPath() + '../dirC2/open_me.js' );
but my searchfoo failed to find anything to fill in for the made-up getScriptPath method there.
Edit:
posting here the workaround i'm using in case the answer is "No".
/**
* Opens a file relative to the script path.
* this solves an inconsistency between require() and fs.open()
*/
exports.getScriptPath = function( newPath ){
var script, scriptPath;
script = exports.system.args[0];
scriptPath = script.replace(/\/[^\/]+$/, '') // removes everything from
// the last "/" until the end of
// the line, non-greedy
return scriptPath + '/';
}
this is used in a utility module i have. Also it assumes you are calling a script (won't work well if you use said module in an interactive phantomjs session) and unix style paths. if you're using windows, just add \ to the regexp. then it's used like:
filehandle = exports.fs.open( exports.getScriptPath() + '../dirC2/open_me.js', 'r' );

Related

How do I bundle and read files within my extension?

I'm learning how to create vscode extensions and I'm simply trying to emulate a template plugin to be used internally within my company. I'd like to store the template in a file within the extension and then read the contents from that file and output it to the active window. So given the following example directory structure of the extension (other files omitted for brevity):
workspace/
|- templates/
| |- template1.yaml
| |- template2.yaml
|- extension.js
For the command that I'm registering, I'd simply like to read from one of those files and send it to the window as a snippet, but I don't know how to find the path to that folder
// extension.js
function activate(context) {
context.subscriptions.push(
vscode.commands.registerCommand("xxx.my-command"), function() {
let path = // ???/templates/template1.yaml
fs.readFile(path, (err, data) => {
let snippet = new vscode.SnippetString(data);
vscode.window.activeTextEditor.insertSnippet(snippet);
}
})
})
)
}
When I try to use relative paths to that templates folder, it appears to be relative to the VSCode installation. I looked around TextDocument provider etc but all of the examples were relevant to using the active editor as the document. How can I include files with my extension and access the contents of those files like this?
You should use context.asAbsolutePath, which by documentation says:
/**
* Get the absolute path of a resource contained in the extension.
*
* *Note* that an absolute uri can be constructed via {#linkcode Uri.joinPath} and
* {#linkcode ExtensionContext.extensionUri extensionUri}, e.g. `vscode.Uri.joinPath(context.extensionUri, relativePath);`
*
* #param relativePath A relative path to a resource contained in the extension.
* #return The absolute path of the resource.
*/
asAbsolutePath(relativePath: string): string;
For instance
const path = context.asAbsolutePath("templates/template1.yaml");
I also suggest you to avoid using Node's fs. Instead, use workspace.fs whenever possible. It will enable your extension to work on Remotes and Web, when necessary. More details here https://code.visualstudio.com/api/extension-guides/virtual-workspaces#review-that-the-extension-code-is-ready-for-virtual-resources
Hope this helps
I found that I could actually do a couple different things:
const path = require('path')
let templatePath = path.resolve(__dirname, './templates/template1.yaml');
But also as Mark suggested in the comments, using the extension context also provides that path:
/**
* #param {vscode.ExtensionContext} context
*/
function activate(context) {
let templatePath = context.extensionPath + '/templates/template1.yaml';
}
I went with the latter since it is being injected by the runtime which seemed better to me if not simpler. Thanks Mark!

Redis StackExchange LuaScripts with parameters

I'm trying to use the following Lua script using C# StackExchange library:
private const string LuaScriptToExecute = #"
local current
current = redis.call(""incr"", KEYS[1])
if current == 1 then
redis.call(""expire"", KEYS[1], KEYS[2])
return 1
else
return current
end
Whenever i'm evaluating the script "as a string", it works properly:
var incrementValue = await Database.ScriptEvaluateAsync(LuaScriptToExecute,
new RedisKey[] { key, ttlInSeconds });
If I understand correctly, each time I invoke the ScriptEvaluateAsync method, the script is transmitted to the redis server which is not very effective.
To overcome this, I tried using the "prepared script" approach, by running:
_setCounterWithExpiryScript = LuaScript.Prepare(LuaScriptToExecute);
...
...
var incrementValue = await Database.ScriptEvaluateAsync(_setCounterWithExpiryScript,
new[] { key, ttlInSeconds });
Whenever I try to use this approach, I receive the following error:
ERR Error running script (call to f_7c891a96328dfc3aca83aa6fb9340674b54c4442): #user_script:3: #user_script: 3: Lua redis() command arguments must be strings or integers
What am I doing wrong?
What is the right approach in using "prepared" LuaScripts that receive dynamic parameters?
If I look in the documentation: no idea.
If I look in the unit test on github it looks really easy.
(by the way, is your ttlInSeconds really RedisKey and not RedisValue? You are accessing it thru KEYS[2] - shouldnt that be ARGV[1]? Anyway...)
It looks like you should rewrite your script to use named parameters and not arguments:
private const string LuaScriptToExecute = #"
local current
current = redis.call(""incr"", #myKey)
if current == 1 then
redis.call(""expire"", #myKey, #ttl)
return 1
else
return current
end";
// We should load scripts to whole redis cluster. Even when we dont have any.
// In that case, there will be only one EndPoint, one iteration etc...
_myScripts = _redisMultiplexer.GetEndPoints()
.Select(endpoint => _redisMultiplexer.GetServer(endpoint))
.Where(server => server != null)
.Select(server => lua.Load(server))
.ToArray();
Then just execute it with anonymous class as parameter:
for(var setCounterWithExpiryScript in _myScripts)
{
var incrementValue = await Database.ScriptEvaluateAsync(
setCounterWithExpiryScript,
new {
myKey: (RedisKey)key, // or new RedisKey(key) or idk
ttl: (RedisKey)ttlInSeconds
}
)// .ConfigureAwait(false); // ? ;-)
// when ttlInSeconds is value and not key, just dont cast it to RedisKey
/*
var incrementValue = await
Database.ScriptEvaluateAsync(
setCounterWithExpiryScript,
new {
myKey: (RedisKey)key,
ttl: ttlInSeconds
}
).ConfigureAwait(false);*/
}
Warning:
Please note that Redis is in full-stop mode when executing scripts. Your script looks super-easy (you sometimes save one trip to redis (when current != 1) so i have a feeling that this script will be counter productive in greater-then-trivial scale. Just do one or two calls from c# and dont bother with this script.
First of all, Jan's comment above is correct.
The script line that updated the key's TTL should be redis.call(""expire"", KEYS[1], ARGV[1]).
Regarding the issue itself, after searching for similar issues in RedisStackExchange's Github, I found that Lua scripts do not work really well in cluster mode.
Fortunately, it seems that "loading the scripts" isn't really necessary.
The ScriptEvaluateAsync method works properly in cluster mode and is sufficient (caching-wise).
More details can be found in the following Github issue.
So at the end, using ScriptEvaluateAsync without "preparing the script" did the job.
As a side note about Jan's comment above that this script isn't needed and can be replaced with two C# calls, it is actually quite important since this operation should be atomic as it is a "Rate limiter" pattern.

How to open/operate file in batch classes?

I have to import in a Table from file (csv for example).
I used this code:
public void run ()
{
TextIO textIO;
str filename, fileOpen, folder;
int handle;
Io thisMYFile;
FileIoPermission perm;
#File
#avifiles
#OCCRetryCount
[handle, filename] = WINAPI::findFirstFile(folder + "\\*.csv");
fileOpen = strFmt (folder + "\\" + filename);
perm = new FileIoPermission(fileOpen, 'w');
perm.assert();
thisMYFile = new CommaTextIo(fileOpen , 'w');
}
But in Debug the class IO thisMYFile is null and so I can't to read and get information.
In my class declaration I extends RunBaseBarch.
If I used a "normal" classes (not batch) I can read,write,move file etc, but now I can't to open.
I know the WinAPI classes not work in Batch, now I think there is also another way to read/open file in batch? With WinAPIServerclass I can findFirstFile ?
A clarification, I have also a same problem if I don't schedule the process batch : flag Batch processing is false, in the batch dialog, and clicked only Ok. (example image)
If you've a tutorial, any advice or help,
Thanks all,
enjoy!
Beware that the batch runs on an other machine, the AOS will not have access to your local files.
Therefore always use a full UNC file path to the file, ex. \\MyHost\Temp\x.csv
If new CommaTextIO fails to open the file, it returns null, it does not throw an exception. If you do not test for null your code will fail later.

How can I use JScript to create a shortcut that uses "Run as Administrator"

I have a JScript script that runs using cscript.exe. It creates a shortcut on the desktop (and in the start menu) that runs cscript.exe with parameters to run another JScript script. It looks, in relevant part, like this:
function create_shortcut_at(folder, target_script_folder)
{
var shell = new ActiveXObject("WScript.Shell");
var shortcut = shell.CreateShortcut(folder + "\\Run The Script.lnk");
shortcut.TargetPath = "cscript";
shortcut.Arguments = "\""+target_script_folder+"\\script.js\" /aParam /orTwo";
shortcut.IconLocation = target_script_folder+"\\icon.ico";
shortcut.Save();
}
It gets called like create_shortcut_at(desktop_folder, script_folder).
And that works, as far as it goes. It creates the desktop icon, pointing properly to the script and runs it when double-clicked. The problem is that it really needs to run the script "as administrator".
And the script really does need to run "as administrator" -- it installs applications (for all users) and reboots the computer. (For those interested, the script is wpkg.js. Modifying it to self-elevate is undesirable.)
Since the target of the shortcut is actually "cscript.exe", I can't use a manifest for the escalation. I could probably theoretically install a cscript.exe.manifest in the windows directory, but even if that worked, it would be a terrible idea for reasons that are obvious.
I'd also prefer not to use a dummy script, since that is an extra file to deal with and there's another, seemingly reasonable, solution at hand: check the "Run as administrator" box on the shortcut.
Thirty-seconds of investigation reveals that the WScript.Shell ActiveX Object does not have the interfaces required for this. Additional investigation suggests that IShellLinkDataList does. However, IShellLinkDataList is a generic COM Interface. I see several examples around the Internet, most linking here. However, all the examples do it in compiled code (C++, C#, even JScript.NET). I significantly prefer to be able to do it directly in JScript, running from cscript.exe.
That said, I'm all for ideas I didn't contemplate or other solutions.
The official way to mark a shortcut file as requiring elevation is via IShellLinkDataList. It's difficult to use that interface from an automation environment.
But, if you are happy with a hack, you can do it in script, just by flipping a bit in the .lnk file.
When you tick the "run as administrator" box in the Advanced tab of the Shell Properties box, or when you use IShellLinkDataList to set the flags to include SLDF_RUNAS_USER, you're basically just setting one bit in the file.
You can do that "manually" without going through the COM interface. It's byte 21, and you need to set the 0x20 bit on.
(function(globalScope) {
'use strict';
var fso = new ActiveXObject("Scripting.FileSystemObject"),
path = "c:\\path\\goes\\here\\Shortcut2.lnk",
shortPath = path.split('\\').pop(),
newPath = "new-" + shortPath;
function readAllBytes(path) {
var ts = fso.OpenTextFile(path, 1), a = [];
while (!ts.AtEndOfStream)
a.push(ts.Read(1).charCodeAt(0));
ts.Close();
return a;
}
function writeBytes(path, data) {
var ts = fso.CreateTextFile(path, true),
i=0, L = data.length;
for (; i<L; i++) {
ts.Write(String.fromCharCode(data[i]));
}
ts.Close();
}
function makeLnkRunAs(path, newPath) {
var a = readAllBytes(path);
a[0x15] |= 0x20; // flip the bit.
writeBytes(newPath, a);
}
makeLnkRunAs(path, newPath);
}(this));
ps:
function createShortcut(targetFolder, sourceFolder){
var shell = new ActiveXObject("WScript.Shell"),
shortcut = shell.CreateShortcut(targetFolder + "\\Run The Script.lnk"),
fso = new ActiveXObject("Scripting.FileSystemObject"),
windir = fso.GetSpecialFolder(specialFolders.windowsFolder);
shortcut.TargetPath = fso.BuildPath(windir,"system32\\cscript.exe");
shortcut.Arguments = "\"" + sourceFolder + "\\script.js\" /aParam /orTwo";
shortcut.IconLocation = sourceFolder + "\\icon.ico";
shortcut.Save();
}

Scoping in embedded groovy scripts

In my app, I use Groovy as a scripting language. To make things easier for my customers, I have a global scope where I define helper classes and constants.
Currently, I need to run the script (which builds the global scope) every time a user script is executed:
context = setupGroovy();
runScript( context, "global.groovy" ); // Can I avoid doing this step every time?
runScript( context, "user.groovy" );
Is there a way to setup this global scope once and just tell the embedded script interpreter: "Look here if you can't find a variable"? That way, I could run the global script once.
Note: Security is not an issue here but if you know a way to make sure the user can't modify the global scope, that's an additional plus.
Shamelessly stolen from groovy.codehaus :
The most complete solution for people
who want to embed groovy scripts into
their servers and have them reloaded
on modification is the
GroovyScriptEngine. You initialize the
GroovyScriptEngine with a set of
CLASSPATH like roots that can be URLs
or directory names. You can then
execute any Groovy script within those
roots. The GSE will also track
dependencies between scripts so that
if any dependent script is modified
the whole tree will be recompiled and
reloaded.
Additionally, each time you run a
script you can pass in a Binding that
contains properties that the script
can access. Any properties set in the
script will also be available in that
binding after the script has run. Here
is a simple example:
/my/groovy/script/path/hello.groovy:
output = "Hello, ${input}!"
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;
String[] roots = new String[] { "/my/groovy/script/path" };
GroovyScriptEngine gse = new GroovyScriptEngine(roots);
Binding binding = new Binding();
binding.setVariable("input", "world");
gse.run("hello.groovy", binding);
System.out.println(binding.getVariable("output"));
This will print "Hello, world!".
Found: here
Would something like that work for you?
A simple solution is to use the code from groovy.lang.GroovyShell: You can precompile the script like so:
GroovyCodeSource gcs = AccessController.doPrivileged( new PrivilegedAction<GroovyCodeSource>() {
public GroovyCodeSource run() {
return new GroovyCodeSource( scriptCode, fileName, GroovyShell.DEFAULT_CODE_BASE );
}
} );
GroovyClassLoader loader = AccessController.doPrivileged( new PrivilegedAction<GroovyClassLoader>() {
public GroovyClassLoader run() {
return new GroovyClassLoader( parentLoader, CompilerConfiguration.DEFAULT );
}
} );
Class<?> scriptClass = loader.parseClass( gcs, false );
That's was the expensive part. Now use InvokeHelper to bind the compiled code to a context (with global variables) and run it:
Binding context = new javax.script.Binding();
Script script = InvokerHelper.createScript(scriptClass, context);
script.run();