I am writing a lua function as custom command for my neovim config.
As the documentation says "The function is called with a single table argument that contains the following keys", but how can i access these keys when the table is not defined to a variable.
I tried calling the function like this:
vim.api.nvim_create_user_command('Build', fn_build(args), { nargs='?' })
and access the values with:
function fn_build(args)
run = args["args"] or nil
end
but i would get a nil error.
#Ani commented:
Try to see if there is anything close to this, in github.com/nanotee/nvim-lua-guide
I found the guide, but it didn't helped me to fix it. I'm not sure if desc is the right variable to use. And how would i even use it. The guide says:
Two additional attributes are available:
desc allows you to control what gets displayed when you run :command {cmd} on a command defined as a Lua callback. Similarly to keymaps, it is recommended to add a desc key to commands defined as Lua functions.
force is equivalent to calling :command! and replaces a command if one with the same name already exists. It is true by default, unlike its Vimscript equivalent.
Am i blind and overseeing something?
Please point me in the right direction
You are calling the function fn_build and registering the return value. Instead you should just pass the function as a parameter. Try the following
vim.api.nvim_create_user_command('Build', fn_build, { nargs='?' })
fn_build should receive opts as an argument try the following:
function fn_build(opts)
run = opts.args or nil
end
Related
I'm trying to call a method inside a class constant. It returns a NoMethodError. Here's a sample code:
class TestingA
CONSTANT_HERE = { details: get_details('example.json') }
def get_details(file)
# retrieve details here
end
end
The error that appears is:
NoMethodError (undefined method `get_details' for TestingA:Class)
Any ideas on why?
Generally, dynamic constant assignment is discouraged in Ruby. Why would you want to define a constant that can possibly change within the life-cycle of an object? We don't know exactly what get_details is doing, but what is the use case of creating an instance method that is called from a constant as well as exposing the method? We can only assume return value is dynamic at this stage. Rubocop is also going arrest you for not freezing the constants, which is bad as linters are a good tool to abide by.
Constants can be changed and there is no way to avoid this as variables in Ruby are not containers: they point towards an object. However, it is your duty to make your code readable. If you see a constant that you cannot easily discern the value of, would you think that is readable?
We should talk about how Ruby loads constants and, more generally, files. Every Ruby application has its entry point. The interpreter will need the file to load and execute the commands of the application. The Ruby interpreter will increment over each statement inside your file and execute them following a specific set of rules, until it parses the entire file. What happens when the interpreter iterates to a constant? What other types of constants are there? Hint: CONSTANT_HERE is not the only constant you are defining.
The class keyword is processed first and the interpreter creates a constant 'TestingA' and stores in that constant a class object. The name of the class instance is "TestingA", a string, named after the constant. Yes, classes and modules are constants. Each class has a constant table, which is where "TestingA" will be stored. After this, the body of the class is interpreted and a new entry is created in the constant table for "CONSTANT_HERE" and the value is set and stored. This is happening before your definition of get_details has been defined: the interpreter wants to set the value, to store it, before "get_details" has been interpreted, which is why you are experiencing the error you are.
Now we know this, and wanting to provide an example of how constants are evaluated in code, you would need to mimic the below in order to have a method defined in a constant:
def get_details(file)
"stub_return"
end
class TestingA
CONSTANT_HERE = { details: get_details('example.json') }
end
In my opinion, the above is not good practise as it is an example mis-use of constant assignment. If you want to get details from a file, define a class/method and call the API/method instead. It's neater, assigns role of responsibility and is less ambiguous.
I am working on my rails RESTful API and I have set up a versioning feature on some of the endpoints. I have a class ApiVersion which Is responsible for determining which controllers to render base on the arguments passed to it on initialization.
The class definition looks as follows:
class ApiVersion
attr_reader :version, :default
def initialize(version, default = false)
#version = version
#default = default
end
# check whether version is specified or is default
def matches?(request)
check_headers(request.headers) || default
end
private
def check_headers(headers)
# check version from Accept headers; expect custom media type 'suits'
accept = headers[:accept]
accept&.include?("application/vnd.suits.#{version}+json")
end
end
The requests work perfectly fine but when I run rubocop -A I get an error that says:
Style/OptionalBooleanParameter: Use keyword arguments when defining method with boolean argument.
def initialize(version, default = false)
I searched on the internet how to fix this type of error & got some interesting ideas which could not work in my case. For example I found one post that said I should alternate the def initialize(version, default = false) with def initialize(version, default: false) which passes the rubocop tests but then I get an internal server error with an exception: ArgumentError: wrong number of arguments (given 2, expected 1).
Does anyone have an idea on how I can fix this, or how I can alternate the class definition, to get around this issue? Thank you
First off: if you disagree with a particular rule in a linter, then turn it off. In particular, this rule is in the "Style" category, so it is not a correctness or security issue, it is a matter of style.
Secondly, boolean parameters are a code smell, since they are often Flag Parameters. A method with a flag parameter will generally do two different things depending on the value of the boolean argument, because … why else would it have the flag parameter?
However, a method that does two different things should probably be two methods.
Or, in this particular case, since it is an object initializer method specifically, that hints at the fact that there should be two classes.
Okay, with that out of the way, the nice thing about Rubocop is that it generally tells you how to fix whatever it is complaining about. In this case, it suggests using a keyword parameter. That doesn't fix the problem that the method is likely still doing two different things, but at least, it gives a name to that difference, so you can see it at the call site.
So, what Rubocop is suggest is to change the positional parameter into a keyword parameter, something like this:
def initialize(version, default: false)
Now, obviously, when you change the parameter list at the definition site, you also need to change every argument list at every call site. So, if you have a call like this (remember that #initialize gets called by ::new):
ApiVersion.new('1.2.3', true)
You need to replace it with
ApiVersion.new('1.2.3', default: true)
I'm having trouble with a little Ruby on Rails I'm building and need some help.
I have a Table with 20+ Columns and a corresponding XML File which can be parsed as some sort of hash with a gem. Every key would be mapped to a column and every value would be a data record in said column.
The way I access a specific value in the already parsed XML file is:
filename["crs","inputkeyhere"]
which returns the value, for example "52" or whatever.
What I am trying to do is upload the file, parse it with the gem and give each column the corresponding value.
My table (or model) is called "Attributeset" and I already know how I can access every column:
#attributeset = Attributeset.new
#attributeset.attributes.keys
So my thought process was:
Iterate over all the keys
Pass every key into a block called |a|
Use the rails possibilty to set attributes by calling the corresponding #attributeset.
Set colum attribute to the corresponding xml key
So my code would go something like this:
#attributeset.attributes.keys.each do |a|
#attributeset.a=filename["crs",a]
end
But my problem is, that ruby thinks ".a" is a method and apparently does not evaluate "a" to the block parameter.
I've read through lambdas and procs and whatnot but didn't really understand how they could work for my specific situation.
Coming from bash scripting maybe my thinking might be wrong but I thought that the .a might get evaluated.
I know I can run the block with yield, but this only works in methods as far as I know..
Any help is appreciated.
Thanks and stay healthy,
Alex
Thanks for the input!
I wanted to make it as clean as possible, and not using any temporary hashes to pass arguments.
I've found the method
write_attribute
which can be used like this:
#attributeset.write_attribute(a, xmp["crs",a])
worked perfectly for me.
You can use []= method to set values dynamically:
#attributeset.attribute_names.each do |attribute|
#attributeset[attribute] = filename["crs", attribute]
end
I have these code that executes a dynamic method. I'm using eval here to execute it but what I wanted to do is changed it to public_send because I was told so and it's much safer.
Current code:
# update workstep logic here.
incoming_status = params[params[:name]]
# grab workflow, this is current data, use this to compare status to in comming status
workflow = get_workorder_product_workstep(params[:workflow_id])
# check current status if its pending allow to update
# security concern EVAL!
if eval("workflow.can_#{incoming_status}?")
# update status
eval("workflow.#{incoming_status}")
# updated attribute handled_by
workflow.update_attributes(handled_by_id: #curr_user.id)
workflow.save
else
flash[:notice] = 'Action not allowed'
end
The eval here is the concern. How can I changed this to public_send?
Here's what I did.
public_send("workflow.can_#{incoming_status}?")
public_send("#{workflow}.can_#{incoming_status}?")
both of them doesn't work. gives me an error of no method. The first public error returns this undefined method workflow.can_queue? for #<Spree::Admin::WorkordersController:0x00007ff71c8e6f00>
But it should work because I have a method workflow.can_queue?
the second error on public is this
undefined method #<Spree::WorkorderProductWorkstep:0x00007ff765663550>.can_queue? for #<Spree::Admin::WorkordersController:0x00007ff76597f798>
I think for the second workflow is being evaluated separately? I'm not sure.
Working with public_send you can change the relevant lines to:
if workflow.public_send("can_#{incoming_status}?")
# update status
workflow.public_send(incoming_status.to_s)
# ...
A note about security and risks
workflow.public_send("can_#{xyz}?") can only call methods on workflow that are public and which start with the prefix can_ and end with ?. That is probably only a small number of methods and you can easily decide if you want to allow all those methods.
workflow.public_send("#{incoming_status'}) is different because it allows all public methods on workflow – even destroy. That means using this without the "can_#{incoming_status}?" is probably a bad idea. Or you should at least first check if incoming_status is in a whitelist of allowed methods.
eval is the worst because it will evaluate the whole string without any context (e.q. an object like workflow). Imaging you have eval("workflow.#{incoming_status}") without to check first if incoming_status is actually allowed. If someone then sends an incoming_status like this "to_s; system('xyz')"then xyz could be everything – like commands to send a hidden file via email, to install a backdoor or to delete some files.
I have the following:
def toggle_follow_user tmp_id
user=User.find(tmp_id)
but when I run specs I get the following warning:
DEPRECATION WARNING: You are passing an instance of ActiveRecord::Base to `find`. Please pass the id of the object by calling `.id`.
what is it trying to tell me and how do I fix it?
Just for the sake of a test... I'm not sure what you are passing in that gets assigned to the tmp_id parameter, but I would surmise its an active record object, can you try this?
def toggle_follow_user tmp_id
user=User.find(tmp_id.id)
end
If that works, its because whatever you were sending into toggle_follow_user was an entire active record "row" where Find wants an integer now (representing the ID... it used to pull it out natively, but that is going away it seems)
You're calling toggle_follow_user(a_user), not toggle_follow_user(a_users_id). Try this:
def toggle_follow_user(user)
# just delete the line where you ::find the user, you're already passing it in
This will fix this particular issue - but check that it doesn't break anything else in your code!
If you are wanting to pass the user's id and find it inside the method, then look a call or two down your stack trace, because it's the whole object that's being passed in.