EDITED:
I've wrote code that uses OptionParser to handle command line input gracefully. I am facing two major hits.
Passing an empty switches '-' doesn't give an error. Of course some programs take that as valid, but mine shouldn't.
The program requires two mandatory switches, but it accepts one switch without complaining! e.g. program.ruby -f foo -b bar is the valid input and both switches are :REQUIRED. But providing only one switch passes without problem and this is not the desired behavior.
For the first case I've done this:
opts.on('-', /\A-\Z/) do
$stderr.print "Invalid empty switch"
exit 1
end
It works fine. But is this the proper way of doing it?
For the second case, I've looked around for a solution within the OptionParser.new block but I couldn't find one. e.g.
unless options.foo && options.bar
puts "Error."
exit 2
end
Doing it outside the OptionParser.new block is the normal way?
If you are using OptionParser, then yes, you need to explicitly disallow the empty switch and manually check the required parameters.
However, if you used another tool for option parsing, such as defunkt's gem choice, you could mark options as required and the invalid options (such as the empty switch) would cause the help to be printed and application to exit. I understand that in some cases it makes more sense to use OptionParser, but personally I prefer to use the more convenient tools out there.
Even though making options required is pretty easy one way or the other, I would recommend that you think your API decision through. How many command line utilities do you know, that have required options? There's a reason why command line is usually separated into options and arguments, with the former being usually optional and the latter usually required. I would stick to that established convention.
I think Thor(https://github.com/wycats/thor) can resolve your problem more efficiently.
Related
According to the Bash Reference Manual, the Bash scripting language is constituted of 4 distinct subclasses of syntactic elements:
built-in commands (alias, cd)
reserved words (if, function)
parameters and variables ($, IFS)
functions (abort, end-of-file - activated with keybindings such as Ctrl-d)
Apart from reading the manual, I became inherently curious if there was a programmatic way to list out or generate all such keywords, at least from one of the above categories. I think this could be useful in some contexts. Sometimes I wish I could see all the options available to me for what I can write in any given moment, and having that information as data, instead of a formatted manual, is convenient, focused, and can be edited, in case you want to strike out commands you know well, or that are too obscure for now.
My understanding is that Bash takes the input into stdin and passes it to the running shell process. When code is distributed in a production-ready form, it is compiled, so it runs faster. Unlike using a Python REPL, you don’t have access to the Bash source code from within Bash, so it is not a very direct route to write a program that searches through source files to find various defined commands. I mean that if you wanted to list all functions, Python has the dir() function which programmatically looks for function names in the namespace. But I don’t think Bash can do that. I think it doesn’t have a special syntax in its source files which makes it easy to find and identify all the keywords. Instead, they will be found if you simply enter them - like cd will “find” the program cd because $PATH returns the path to that command - but there’s no special way to discover them.
Or am I wrong? Technically, you could run a “brute force” search by generating every combination of symbols of every length and record when you did not get “error: unknown command” as a response.
Is there any other clever programmatic way to do this?
I mean I want to see a list of every symbol or string that the bash
compiler
Bash is not a compiler. It and every other shell I know are interpreters of various languages.
recognises and knows what to do with, including commands like
“ls” or just a symbol like “*”. I also want to see the inputs and
outputs for each symbol, i.e., some commands are executed in the shell
prompt by themselves, but what data type do they return?
All commands executed by the shell have an exit status, which is a number between 0 and 255. This is as close to a "return type" as you get. Many of them also produce idiosyncratic output to one or two streams (a standard output stream and a standard error stream) under some conditions, and many have other effects on the shell environment or operating environment.
And some
require a certain data type to standard input.
I can't think of a built-in utility whose expected input is well characterized as having a particular data type. That's not really a stream-oriented concept.
I want to do this just as a rigorous way to study the language.
If you want to rigorously study the language, then you should study its manual, where everything you describe has already been compiled. You might also want to study the POSIX shell command language manual for a slightly different perspective, which is more thorough in some areas, though what it documents differs in a few details from Bash's default behavior.
If you want to compile your own summary of Bash syntax and behavior, then those are the best source materials for such an effort.
You can get a list of all reserved words and syntactic elements of bash using this trick:
help -s '*' | cut -d: -f1
Or more accurately:
help -s \* | awk -F ': ' 'NR>2&&!/variables/{print $1}'
EDITED:
I've wrote code that uses OptionParser to handle command line input gracefully. I am facing two major hits.
Passing an empty switches '-' doesn't give an error. Of course some programs take that as valid, but mine shouldn't.
The program requires two mandatory switches, but it accepts one switch without complaining! e.g. program.ruby -f foo -b bar is the valid input and both switches are :REQUIRED. But providing only one switch passes without problem and this is not the desired behavior.
For the first case I've done this:
opts.on('-', /\A-\Z/) do
$stderr.print "Invalid empty switch"
exit 1
end
It works fine. But is this the proper way of doing it?
For the second case, I've looked around for a solution within the OptionParser.new block but I couldn't find one. e.g.
unless options.foo && options.bar
puts "Error."
exit 2
end
Doing it outside the OptionParser.new block is the normal way?
If you are using OptionParser, then yes, you need to explicitly disallow the empty switch and manually check the required parameters.
However, if you used another tool for option parsing, such as defunkt's gem choice, you could mark options as required and the invalid options (such as the empty switch) would cause the help to be printed and application to exit. I understand that in some cases it makes more sense to use OptionParser, but personally I prefer to use the more convenient tools out there.
Even though making options required is pretty easy one way or the other, I would recommend that you think your API decision through. How many command line utilities do you know, that have required options? There's a reason why command line is usually separated into options and arguments, with the former being usually optional and the latter usually required. I would stick to that established convention.
I think Thor(https://github.com/wycats/thor) can resolve your problem more efficiently.
This seems to me to be a novel idea (since i haven't found any solutions or anyone having implemented it)...
A shell script that automatically runs whenever you git commit or whatever that will let you know if you forgot to delete any debugging or development env specific lines of code in your project.
For example:
Often times (in my Ruby projects) I'll leave lines of code to output variables like
puts params.inspect
or
raise params.inspect
Also, sometimes I'll use different methods so I can easily see the effects such as in cases like using delayed_job where I'd rather call the method without a delay during development.
The problem is sometimes I forget to change those methods back or forget to delete a call to raise params.inspect and I'll inadvertently push that code.
So I thought maybe the simplest solution was to add a comment to any such debugging line such as
raise params.inspect #debug
In essence flagging that line as a development only/debug line. Then in a shell script that runs before some other command like git commit it can use awk or grep to search through all the latest modified files for that #debug comment and stop execution and alert you. However i don't know much about shell scripting so I thought I'd ask for help :)
Although I whole-heartedly recommend following cdeszaq'a advice and discourage doing this sort of thing, it is pretty easy to write a git hook that will prevent you from committing any lines with a particular string. For simplicity, I'm not showing the git rev-parse --verify HEAD that you should use to make this hook work on an initial commit, but if you simply put the following in .git/hooks/pre-commit (and make it executable), you will not be able to commit any lines of code that contain the string '#debug':
#!/bin/sh
if git diff-index -p -M --cached HEAD | grep '#debug' > /dev/null; then
echo 'debug lines found in commit. Aborting' >&2
exit 1
fi
Rather than having to remember to do additional work (removing lines of code) only to have to do more work later when things break again (re-adding that code), why not put in sensible debugging statements from the beginning?
Most languages have fairly expressive and often cheap logging libraries that will allow you to write out various levels of information (error, info, debug, trace) to a number of different locations (a file, a database). Many of these libraries will even let you adjust the logging level for a specific chunk of the code at runtime or even while the program is running.
So, rather than try to bandage up brute-force debugging by scripting away the problem, why not do yourself, and the rest of the world that has to use what you produce, a favor and use an actual logging framework for logging?
As I said in my comment, you can use any programming language you feel comfortable with.
Anyway, searching for other commit hooks, I think this one could be a good one to start with. It basically looks for some words in your files and can be customized just changing the checks array in the top of the file.
#cdeszaq is correct about the logging part.
For behaviour that differs depending on environment, the common way to achieve this is to make the behaviour configurable. delayed_job should read a value from the config file to decide how long to delay. For production environments the config would have one value and for development environments the config would have a different value.
I wonder why I should use:
puts "In folder #{File.join ENV[HOME], projects}"
Instead of:
puts "In folder #{ENV[HOME]/projects}"
I am aware of that File.join will put the appropriate separator (/ vs \) depending on the OS.
The script is already so tightly tied to what version of ruby you are using, what gems you have installed and so on. My scripts tend not to be like an ORM, (in this case) independent of OS.
I will never run this on Windows (the other dependencies will make the script not to work anyway).
So seems not to be a strong reason for using it, right?
Any of the following :
File.join("first","second")
File.join("first/","second")
File.join("first","/second")
File.join("first/","/second")
Will return
=> "first/second"
Could it be a good reason for you ?
That's only one example I can think of.
Actually, your goal is not to concatenate 2 strings, your goal is creating a path. This looks like a strong reason to use File.join to me.
Haven't used Ruby, but I expect a Path.join to handle corner cases, like paths ending with or without directory separators. Besides, it expresses intent a bit more clearly than string concatenation, and clarity is IMHO almost always a good idea.
I expect join to handle corner cases gracefully, like when ENV[HOME] is empty for some weird reason.
In addition to the other answers your code will be more portable, the correct separator will be used regardless of unix/windows/etc.
be aware of difference between RUBY and PYTHON
RUBY: File.join("","somthing") → "/something"
PYTHON: os.path.join("","somthing") → "something"
RUBY treat empty string as path → I call this a BUG
Does ruby allow you to treat warnings as errors?
One reason I'd like to do this is to ensure that if heckle removing a line of code means that a warning occurs, I have the option of ensuring that the mutant get killed.
There is unfortunately no real way of doing this, at least not on most versions of Ruby out there (variations may exist), short of monitoring the program output and aborting it when a warning appears on standard error. Here's why:
Ruby defines Kernel.warn, which you can redefine to do whatever you wish (including exiting), and which you'd expect (hope) to be used consistently by Ruby to report warnings (including internal e.g. parsing warning), but
methods implemented natively (in C) inside Ruby will in turn directly invoke a native method called rb_warn from source/server.c, completely bypassing your redefinition of Kernel.warn (e.g. the "string literal in condition" warning, for example, issued when doing something like: do_something if 'string', is printed via the native rb_warn from source/parse.c)
to make things even worse, there is an additional, rb_warning native method, which can be used by Ruby to log warnings if -w or -v is specified.
So, if you need to take action solely on warnings generated by your application code's calling Kernel.warn then simply redefine Kernel.warn. Otherwise, you have exactly two options:
alter source/error.c to exit in rb_warn and rb_warning (and rb_warn_m?), and rebuild Ruby
monitor your program's standard error output for ': warning:', and abort it on match
You can finally do that by overriding Warning.warn like
module Warning
def warn(msg)
raise msg
end
end
This will turn the warning into an exception. This solution works at least since 2.4 branch.
You could also potentially use DTrace, and intercept the calls to rb_warn and rb_warning, though that's not going to produce exceptions you can rescue from somewhere. Rather, it'll just put them somewhere you can easily log them.