gopls replace variable in the entire project - go

The question born from the fact that gorename does not support modules. There is a replacement tool called gopls, but the example is not exhaustive so I'm wondering if there is way to rename a variable in the entire project (otherwise I'm failing to understand the usefulness of the rename part)
gopls rename --help says:
Usage: rename [flags]
Example:
gopls rename helper/helper.go:8:6
gopls rename helper/helper.go:#53
Is there a way to rename a variable in the entire project like gorename is able to do?

I recommend you use gopls as an LSP server to integrate with your editor. Your editor should provide an lsp-rename command to conveniently invoke the rename functionality.
As for using gopls rename on the command line, if you have this module:
--- go.mod ---
module foo
--- foo.go ---
package foo
var Foo = 123
Inside the module's directory, run gopls rename -d foo.go:3:5 Hello to preview the diff ("3:5" means line 3, column 5):
% gopls rename -d foo.go:3:5 Hello
--- /Users/muir/scratch/foo/foo.go.orig
+++ /Users/muir/scratch/foo/foo.go
## -1,3 +1,3 ##
package foo
-var Foo = 123
+var Hello = 123
Then run gopls rename -w foo.go:3:5 Hello to write out the changes. Assuming you are using a recent version of gopls, this will rename within the entire module.

Related

How do I execute a Move script with the Aptos CLI?

Let's say I have a Move script like this:
script {
use std::signer;
use aptos_framework::aptos_account;
use aptos_framework::aptos_coin;
use aptos_framework::coin;
fun main(src: &signer, dest: address, desired_balance: u64) {
let src_addr = signer::address_of(src);
let balance = coin::balance<aptos_coin::AptosCoin>(src_addr);
if (balance < desired_balance) {
aptos_account::transfer(src, dest, desired_balance - balance);
};
addr::my_module::do_nothing();
}
}
This is calling functions on the aptos_coin.move module, which is deployed on chain. What it does isn't so important for this question, but in short, it checks that the balance of the destination account is less than desired_balance, and if so, tops it up to desired_balance.
Notice also how it calls a function in a Move module I've defined:
module addr::my_module {
public entry fun do_nothing() { }
}
Where do I put these files? Do I need a Move.toml? How do I run my script with the CLI?
Let's run through how to execute a Move script with a step by step example, this should answer all your questions.
Make a new directory to work from:
mkdir testing
cd testing
Setup the Aptos CLI:
aptos init
The CLI will ask you which network you want to work with (e.g. devnet, testnet, mainnet). It will also ask you for your private key (which looks like this: 0xf1adc8d01c1a890f17efc6b08f92179e6008d43026dd56b71e7b0d9b453536be), or it can generate a new one for you, as part of setting up your account.
From here, initialize a new Move project:
aptos move init --name my_script
Now you need to make a file for your script. So, take the script you created above, and put it in sources/, e.g. like this:
testing/
Move.toml
sources/
top_up.move
In other words, top_up.move should contain the script you included in the question.
Now do the same thing with the Move module, leaving you with this:
testing/
Move.toml
sources/
top_up.move
my_module.move
Now you can compile the script:
$ aptos move compile --named-addresses addr=81e2e2499407693c81fe65c86405ca70df529438339d9da7a6fc2520142b591e
Compiling, may take a little while to download git dependencies...
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING my_script
{
"Result": []
}
Note how I use the --named-addresses argument. This is necessary because in your code, you refer to this named address called addr. The compiler needs to know what this refers to. Instead of using this CLI argument, you could put something like this in your Move.toml:
[addresses]
addr = "b078d693856a65401d492f99ca0d6a29a0c5c0e371bc2521570a86e40d95f823"
Finally you can run the compiled script:
$ aptos move run-script --compiled-script-path build/my_script/bytecode_scripts/main.mv --args address:b078d693856a65401d492f99ca0d6a29a0c5c0e371bc2521570a86e40d95f823 --args u64:5
Do you want to submit a transaction for a range of [17000 - 25500] Octas at a gas unit price of 100 Octas? [yes/no] >
yes
{
"Result": {
"transaction_hash": "0x655f839a45c5f14ba92590c321f97c3c3f9aba334b9152e994fb715d5648db4b",
"gas_used": 178,
"gas_unit_price": 100,
"sender": "81e2e2499407693c81fe65c86405ca70df529438339d9da7a6fc2520142b591e",
"sequence_number": 53,
"success": true,
"timestamp_us": 1669811892262502,
"version": 370133122,
"vm_status": "Executed successfully"
}
}
Note that the path of the compiled script is under build/my_script/, not build/top_up/. This is because it uses the name of the project contained in Move.toml, which is my_script from when we ran aptos move init --name my_script.
So to answer one of your questions, yes you need a Move.toml, you can't currently just execute a script file on its own with the CLI. The compiler needs this determine what Aptos framework to use for example.
See the code used in this answer here: https://github.com/banool/move-examples/tree/main/run_script.
See also how to do this with the Rust SDK instead of the CLI: How do I execute a Move script on Aptos using the Rust SDK?.
P.S. There is a more streamlined way to execute a script. Instead of running aptos move compile and then aptos move run-script --compiled-script-path separately, you can just do this:
$ aptos move run-script --script-path sources/my_script.move --args address:b078d693856a65401d492f99ca0d6a29a0c5c0e371bc2521570a86e40d95f823 --args u64:5
This will do both steps with a single CLI command. Note however that there are some major footguns with this approach, see https://github.com/aptos-labs/aptos-core/issues/5733. So I'd recommend using the previous two-step approach for now.

Is there a way to change the working directory of fiddle?

I'm trying to load a C shared library within Ruby using Fiddle.
Here is a minimal example:
require 'fiddle'
require 'fiddle/import'
module Era
extend Fiddle::Importer
dlload './ServerApi.so'
extern 'int era_init_lib()'
extern 'void era_deinit_lib()'
extern 'int era_process_request(const char* request, char** response)'
extern 'void era_free(char* response)'
end
Era.era_init_lib
begin
# ...
ensure
Era.era_deinit_lib
end
The shared library loads without issues. However when I call Era.era_init_lib it tries to load additional libraries (Network.so and Protobuf.so). I have these file located in the current working directory (in the same directory as ServerApi.so).
However when I try to execute the code above I receive the following error:
! Failed to load library: /home/username/.rvm/rubies/ruby-2.6.5/bin/Network.so, error: /home/username/.rvm/rubies/ruby-2.6.5/bin/Network.so: cannot open shared object file: No such file or directory
If I place the file at the location the error describes everything works fine.
My guess is that the C working directory of fiddle is different from the Ruby working directory. I would like to keep the project files within the project and not in the Ruby installation directory.
How can I use Network.so from my project folder?
All the *.so files are provided by a third-party. I do not have the source and as a result cannot change these files. The function signatures are provided by the documentation.
Searching for Network.so in the strace gives me these results:
readlink("/proc/self/exe", "/home/username/.rvm/rubies/ruby-2."..., 4096) = 44
openat(AT_FDCWD, "/home/username/.rvm/rubies/ruby-2.6.5/bin/Network.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
futex(0x7fcc16666d90, FUTEX_WAKE_PRIVATE, 2147483647) = 0
futex(0x7fcc16b44520, FUTEX_WAKE_PRIVATE, 2147483647) = 0
write(2, "! Failed to load library: ", 26! Failed to load library: ) = 26
write(2, "/home/username/.rvm/rubies/ruby-2."..., 50/home/username/.rvm/rubies/ruby-2.6.5/bin/Network.so) = 50
write(2, ", error: ", 9, error: ) = 9
write(2, "/home/username/.rvm/rubies/ruby-2."..., 109/home/username/.rvm/rubies/ruby-2.6.5/bin/Network.so: cannot open shared object file: No such file or directory) = 109
write(2, "\n", 1) = 1
I've also written a C script which does the same thing which works perfectly fine when the files are dropped into the same directory. So it might be the fault of the library, which I assume checks the location of the current running program, then tries to load the library from that folder. This would explain the behavior when ran as a Ruby script (since it runs as part of the Ruby program), whereas a C binary runs standalone.
For those that want to re-create the (Linux) issue. You can download the necessary files from here. Which gives you the server-linux-x86_64.sh file.
Supported distros are: Suse, Ubuntu, Debian, Red Hat and CentOS but others may also work fine.
You can either run the installer, which should place the files in /opt/eset/RemoteAdministrator/Server. Or, assuming most of you don't want to install the full application you can run the following command:
sed '1,/^# Start of TAR\.GZ file #$/d' server-linux-x86_64.sh | sed '1d' > server-linux-x86_64.tar.gz
Which removes all the installer instructions from the .sh file and only leaves the binary .tar.gz data, writing it to server-linux-x86_64.tar.gz.
Copy the files ServerApi.so, Protobuf.so and Network.so into a directory of your liking. Create a Ruby script (with the question code) in the same directory and run the script.
Because ServerApi.so checks /proc/self/exe for the location of all subsequent files to load, and it is very difficult to modify this target by normal means, it is easier to just modify ServerApi.so itself so that it uses something else besides proc for the source.
If we run strings ServerApi.so, we can verify that the location to check is stored inside a string in ServerApi.so:
strings ServerApi.so | grep 'proc/self/exe'
B/proc/self/exe
So now all we need to do is modify this string to something else that works for us.
The easiest way to modify the string is to replace it with something that is exactly the same length as the original. This way we do not have to worry about changing the end-of-string zero padding or accidentally changing the total size of ServerApi.so.
Here we can see a suitable candidate could be /tmp/scriptexe:
/proc/self/exe
/tmp/scriptexe <- same length
So let's do that:
sed -e 's/proc\/self\/exe/tmp\/scriptexe/' ServerApi.so > ServerApi_Mod.so
Now we can verify the change:
strings ServerApi_Mod.so | grep scriptexe
B/tmp/scriptexe
Next we need to create /tmp/scriptexe to actually point to our Ruby script:
ln -s /the/full/path/to/our/ruby/script.rb /tmp/scriptexe
Then we modify our script:
dlload './ServerApi_Mod.so
Now we can run it as normal:
ruby script.rb
And everything should work.
If we read the strace output we see that the library obtains the current executable location from /proc/self/exe, and then searches subsequent libraries from there.
/proc/self/exe is not easily modifiable, but by using a hard link to a Ruby executable in the current directory we can trick it to point to a new folder.
Problem is making a hard link requires root.
In any case, here is a self-contained solution (note that it will ask for root password the first time you run it, in order to create the hard link).
Put this at the top of your script:
# Obtain path to current executable
exe = File.readlink("/proc/self/exe")
# Check if we are running the hard-liked version
if !exe.match /localruby/
if !File.exist?('localruby')
# Create a hard link to the current Ruby exe using sudo
system("sudo ln #{exe} localruby")
end
puts "Restarting..."
# In order to prevent infinite busy loop in case of some mishap
sleep 1
# Rerun self using the hard-linked Ruby executable.
# This will make /proc/self/exe point to the hard-link, which then
# allows the ESET library to search for .so files in current folder.
exec('./localruby', File.expand_path(__FILE__))
end
require 'fiddle'
require 'fiddle/import'
# ...rest of your script goes here...
A simple solution without any extra Ruby code is to just create the hard link manually, and then always run the script with ./localruby myscript.rb, instead of using the normal ruby myscript.rb.

How to parse git diff of specific file using Ruby?

I have an Android XML (string) file that is edited in a Ruby script. I would like to list and output the changes that were made then. I have tried it with Nokogiri and nokogiri/diff. But it does not have the desired result.
I also have the feeling that it has problems when a new line is added in the middle of it. All in all, I think it would be easiest if I could use git diff.
I've also found ruby-git gem, but I still could not get it to work. Especially because I only need the diff of a specific file.
require 'git'
Git.configure do |config|
#not sure if I actually need something?
end
g = Git.open(path_to_my_dir, :log => Logger.new(STDOUT))
g.diff(path_to_file)
#or
g.diff().path(path_to_file)
Can Someone please help me out? :-(
You thought in the right direction.
For this purpose use
require 'git'
g = Git.open('path/to/dir')
g.diff('your.file').patch #=> changes in your.file
For example we had empty files git.rb and smth in our git-repo.
Then we changed them and checked difference:
$ git diff
diff --git a/git.rb b/git.rb
index e69de29..3ff224d 100644
--- a/git.rb
+++ b/git.rb
## -0,0 +1,3 ##
+require 'git'
+g = Git.open(__dir__)
+puts g.diff('smth').patch
diff --git a/smth b/smth
index e69de29..7c5bd35 100644
--- a/smth
+++ b/smth
## -0,0 +1 ##
+we want to know changes
As already guessed from modified git.rb, now we will see changes only in smth:
$ ruby git.rb
diff --git a/smth b/smth
index e69de29..7c5bd35 100644
--- a/smth
+++ b/smth
## -0,0 +1 ##
+we want to know changes
In case there are no changes, you will get empty string "".
You might want to use the McIlroy-Hunt longest common subsequence (LCS) algorithm directly instead of using derivates/wrappers of it.
See https://github.com/halostatue/diff-lcs
The diff will change if you compare changes of a vs. b as opposed to changes of b vs. a, but you can run it against an array or against a whole file of course.
The gem also has the classic diff tool formatting (used by diff or git) if you prefer that instead of using its direct output.

Combine many ruby source files into a single file

I'm working on a project in ruby, and I have many source files each declaring a few classes and methods in a module. I've been looking around and I may just be missing something, but I can't seem to find a way to merge all of my source files into a single file. I want a single file because the end product here is meant to be a command line tool, and users won't want to install 20 files in order to run a single command.
Is there any way to take many ruby source files and process all of the require statements ahead of time to create a single file that can be run by itself as a stand-alone program?
A few things that may be useful (or harmful?):
The only file with code that is not within a function is the main file. Therefore, only the main file will have code that is run immediately after the file is parsed.
I require all needed files immediately at the start of each file (after the initial comment), so the require statements are all at the top of the file, and all dependancies are listed at the start of each file
I call require on all files required by each file, regardless of weather or not they may have been included already.
Example (A few files from my project):
<filename> --> <include1>
<include2>
...
build.rb [the main file] --> BuildSystem.rb
Utilities.rb
BuildSystem.rb --> Project.rb
YamlFile.rb
XmlFile.rb
Utilities.rb
Project.rb --> YamlFile.rb
XmlFile.rb
Utilities.rb
What I'm looking for would be something that would allow me to combine all 5 of these files into a single build file that can be installed just by putting it in the right place. Any help would be great, thanks!
The only file with code that is not within a function is the main file. Therefore, only the main file will have code that is run immediately after the file is parsed.
Because of this, you may be able to simply run the following from the shell (assuming a OS X / *nix system):
touch main.rb
cat Utilities.rb >> main.rb
cat XmlFile.rb >> main.rb
cat YamlFile.rb >> main.rb
cat Project.rb >> main.rb
cat BuildSystem.rb >> main.rb
cat build.rb >> main.rb # must be appended last
You can put this into a shell script to "build" your output file each time you make a change.
Using this method, you will have require statements scattered throughout the output main.rb file, but since they are idempotent it won't have any negative effects.

Using a different name for calling a ruby gem from the command line

Is there any way to change the name that the user has to use when calling from the command line? For example, I have a Thor command line app called super_awesome_gem. I want my gem to be called super_awesome_gem, but when the user calls it from the command line I just want them to be able to call sup_awe or something.
I've tried editing the gemspec file and the file and folder names, but I can't figure out what the proper way to do this would be, or even if there is one.
Is there a way to name a gem one way and have the command line call be a different name?
Your gem name and the executables it bundles don't have to be the same at all. In your gemspec, you can define a list of executables via executables:
Gem::Specification.new do |s|
s.name = "super_awesome_gem"
# other gemspec stuff
s.executables = ["sup_awe"]
end
As long as sup_awe is listed in the gemspec's files list, and is executable, that will be in the user's path after they install your gem. Bundler, when bootstrapping your gemspec, makes this even simpler
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
Anything in bin/ will be treated as an executable.
That is the long way of saying that your exectuable/bin file can be named whatever you want, and doesn't have to be named for your gem.
Another way to achieve this is an alias:
alias my_command=original_command
Just place it where it fits you best.
A third way is to use a wrapper_script, which is a script with the desired name which then calls the original command and passes it all arguments it got:
#!/bin/sh
original_command $#
or in cmd.exe on windows:
#echo off
original_command %*

Resources