Can I parse long arguments with Elixir's built-in OptionParser? - arguments

I am using Elixir's OptionParser, but I am having problems parsing long dashed arguments.
Preferably, I would like to do something like this:
OptionParser.parse(argv, strict: ["db-username": :string, "db-password": :string])
But this does not seem to work. I'm getting Unknown option.
Anybody knows if it is possible somehow?

OptionParser has this (slightly weird) behaviour, where it corrects your option names from using underscores to using dashes and doesn't let you specify the arguments with dashes yourself.
iex(1)> argv = ["--db-username", "foo"]
["--db-username", "foo"]
iex(2)> OptionParser.parse(argv, strict: ["db-username": :string, "db-password": :string])
{[], ["foo"], [{"--db-username", nil}]}
iex(3)> OptionParser.parse(argv, strict: ["db_username": :string, "db_password": :string])
{[db_username: "foo"], [], []}
This makes it a bit more natural when you provide the arguments spec as atoms instead of strings, you can just do :db_username, instead of :"db-username".
This behaviour is documented, but it's easy to miss.

Related

Difference between Encoding::BINARY and Encoding::ASCII-8BIT?

Ruby says that Encoding::BINARY and Encoding::ASCII-8BIT are the same.
Encoding::BINARY == Encoding::ASCII_8BIT
#=> true
We explicitly create a binary string and ruby still says it's ASCII_8BIT.
String.new("ABC", encoding: Encoding::BINARY).encoding
#=> #<Encoding:ASCII-8BIT>
Likewise, force_encoding cannot create a BINARY, it just creates an ASCII-8BIT string.
It seems that BINARY is simply an alias for ASCII-8BIT. Are there any differences?
Your observation is correct: BINARY and ASCII-8BIT are indeed aliases and being an alias implies there are no differences as it's just another name for the same encoding, method, etc.
Looking at the source code is the most reliable way to confirm this. CRuby's character encodings can be found in the enc directory. The ASCII-8BIT encoding is defined in the ascii.c file containing the following line (in 2.5.0, it's line 61):
ENC_ALIAS("BINARY", "ASCII-8BIT")
ENC_ALIAS works like Ruby's alias keyword (alias, original name).
Confirming that BINARY or another encoding name is an alias can be done in pure Ruby too. One possibility is calling the Encoding.aliases method which returns a hash (alias => original):
Encoding.aliases['BINARY'] # => "ASCII-8BIT"
Other useful methods are Encoding#name which returns the original name and Encoding#names which also returns all aliases:
Encoding::BINARY.names # => ["ASCII-8BIT", "BINARY"]
Encoding::US_ASCII.names # => ["US-ASCII", "ASCII", "ANSI_X3.4-1968", "646"]
Or a way without any Encoding methods:
Encoding::BINARY.equal?(Encoding::ASCII_8BIT)
As the == method is often overwritten and may return true even if both operands are two different objects, BasicObject#equal? should be called to check if they are the same object. E.g. 1 and 1.0 have the same value (== returns true) but not the same object identity (equal? returns false).

Ruby get strings from array which contain substring

I've got an array of strings. A few of the strings in this array contain a certain substring I'm looking for. I want to get an array of those strings containing the substring.
I would hope to do it like this:
a = ["abc", "def", "ghi"]
o.select(&:include?("c"))
But that gives me this error:
(repl):2: syntax error, unexpected ')', expecting end-of-input
o.select(&:include?("c"))
^
If your array was a file lines.txt
abc
def
ghi
Then you would select the lines containing c with the grep command-line utility:
$ grep c lines.txt
abc
Ruby has adopted this as Enumerable#grep. You can pass a regular expression as the pattern and it returns the strings matching this pattern:
['abc', 'def', 'ghi'].grep(/c/)
#=> ["abc"]
More specifically, the result array contains all elements for which pattern === element is true:
/c/ === 'abc' #=> true
/c/ === 'def' #=> false
/c/ === 'ghi' #=> false
You can use the &-shorthand here. It's rather irrational (don't do this), but possible.
If you do manage to find an object and a method so you can make checks in your select like so:
o.select { |e| some_object.some_method(e) }
(the important part is that some_object and some_method need to be the same in all iterations)
...then you can use Object#method to get a block like that. It returns something that implements to_proc (a requirement for &-shorthand) and that proc, when called, calls some_method on some_object, forwarding its arguments to it. Kinda like:
o.m(a, b, c) # <=> o.method(:m).to_proc.call(a, b, c)
Here's how you use this with the &-shorthand:
collection.select(&some_object.method(:some_method))
In this particular case, /c/ and its method =~ do the job:
["abc", "def", "ghi"].select(&/c/.method(:=~))
Kinda verbose, readability is relatively bad.
Once again, don't do this here. But the trick can be helpful in other situations, particularly where the proc is passed in from the outside.
Note: you may have heard of this shorthand syntax in a pre-release of Ruby 2.7, which was, unfortunately, reverted and didn't make it to 2.7:
["abc", "def", "ghi"].select(&/c/.:=~)
You are almost there, you cannot pass parameter in &:. You can do something like:
o.select{ |e| e.include? 'c' }

Generate string for Regex pattern in Ruby

In Python language I find rstr that can generate a string for a regex pattern.
Or in Python we have this method that can return range of string:
re.sre_parse.parse(pattern)
#..... ('range', (97, 122)) ....
But In Ruby I didn't find any thing.
So how to generate string for a regex pattern in Ruby(reverse regex)?
I wanna to some thing like this:
"/[a-z0-9]+/".example
#tvvd
"/[a-z0-9]+/".example
#yt
"/[a-z0-9]+/".example
#bgdf6
"/[a-z0-9]+/".example
#564fb
"/[a-z0-9]+/" is my input.
The outputs must be correct string that available in my regex pattern.
Here outputs were: tvvd , yt , bgdf6 , 564fb that "example" method generated them.
I need that method.
Thanks for your advice.
You can also use the Faker gem https://github.com/stympy/faker and then use this call:
Faker::Base.regexify(/[a-z0-9]{10}/)
In Ruby:
/qweqwe/.to_s
# => "(?-mix:qweqwe)"
When you declare a Regexp, you've got the Regexp class object, to convert it to String class object, you may use Regexp's method #to_s. During conversion the special fields will be expanded, as you may see in the example., using:
(using the (?opts:source) notation. This string can be fed back in to Regexp::new to a regular expression with the same semantics as the original.
Also, you can use Regexp's method #inspect, which:
produces a generally more readable version of rxp.
/ab+c/ix.inspect #=> "/ab+c/ix"
Note: that the above methods are only use for plain conversion Regexp into String, and in order to match or select set of string onto an other one, we use other methods. For example, if you have a sourse array (or string, which you wish to split with #split method), you can grep it, and get result array:
array = "test,ab,yr,OO".split( ',' )
# => ['test', 'ab', 'yr', 'OO']
array = array.grep /[a-z]/
# => ["test", "ab", "yr"]
And then convert the array into string as:
array.join(',')
# => "test,ab,yr"
Or just use #scan method, with slightly changed regexp:
"test,ab,yr,OO".scan( /[a-z]+/ )
# => ["test", "ab", "yr"]
However, if you really need a random string matched the regexp, you have to write your own method, please refer to the post, or use ruby-string-random library. The library:
generates a random string based on Regexp syntax or Patterns.
And the code will be like to the following:
pattern = '[aw-zX][123]'
result = StringRandom.random_regex(pattern)
A bit late to the party, but - originally inspired by this stackoverflow thread - I have created a powerful ruby gem which solves the original problem:
https://github.com/tom-lord/regexp-examples
/this|is|awesome/.examples #=> ['this', 'is', 'awesome']
/https?:\/\/(www\.)?github\.com/.examples #=> ['http://github.com', 'http://www.github.com', 'https://github.com', 'https://www.github.com']
UPDATE: Now regular expressions supported in string_pattern gem and it is 30 times faster than other gems
require 'string_pattern'
/[a-z0-9]+/.generate
To see a comparison of speed https://repl.it/#tcblues/Comparison-generating-random-string-from-regular-expression
I created a simple way to generate strings using a pattern without the mess of regular expressions, take a look at the string_pattern gem project: https://github.com/MarioRuiz/string_pattern
To install it: gem install string_pattern
This is an example of use:
# four characters. optional: capitals and numbers, required: lower
"4:XN/x/".gen # aaaa, FF9b, j4em, asdf, ADFt
Maybe you can find what you are looking for over here.

Clarifications on YAML syntax and Ruby parsing

I am new to YAML and Ruby. I am using the following Ruby code to parse a YAML file:
obj = YAML::load_file('test.yml')
Are the following YAML file contents for 'test.yml' valid?
Case 1:
test
In this case, I don't specify the value of test (something like test : true) but my Ruby parsing code does not throw an error. I thought this was an invalid YAML syntax.
Case 2:
:test : true
In this case, the Ruby code treats test as a symbol instead of a string and when I do puts obj[:test], it returns the result to be "true". Is this a Ruby thing? Other languages will interpret it as a string ":test"?
Case 3:
:test : true
:test : false
In this case, instead of throwing up an error for redefinition of :test, my Ruby code takes the latest value for :test (which is false). Why is this? Does YAML syntax allow for re-definition of elements and in which case only the latest value gets taken?
Case 1: YAML allows unquoted scalars, or "bare" strings not enclosed in quotes. Compared to quoted strings they are less flexible, since you can't use certain characters without creating ambiguous syntax, but the Ruby parser does support them.
1.9.3-p448 > YAML::parse('test').to_ruby
=> "test"
Case 2: As you've guessed, this is Ruby-specific since YAML has no concept of "symbols". When converting a YAML mapping to a Ruby hash, scalar keys that start with a colon are interpreted as symbols instead of strings.
Case 3: Under YAML's definition of a mapping, keys must be unique, so a strict parser should throw an error when given your example. It seems the Ruby parser is more lenient, and allows the same key to be defined multiple times with a last-value-wins rule. This is also allowed in native Ruby hashes.
1.9.3-p448 > YAML::parse("test: true\ntest: false").to_ruby
=> {"test"=>false}
1.9.3-p448 > { 'test' => true, 'test' => false }
=> {"test"=>false}
A great way to learn how the YAML parser converts to/from Ruby structures, is to write Ruby code that outputs YAML, and look at what it's doing:
Here's a basic hash:
require 'yaml'
foo = {'test' => true} # => {"test"=>true}
foo.to_yaml # => "---\ntest: true\n"
A hash using a symbol as a key:
foo = {test: true}
foo.to_yaml # => "---\n:test: true\n"
A hash with conflicting keys, causing the first to be stomped-on by the last:
foo = {test: true, test: false}
foo # => {:test=>false}
foo.to_yaml # => "---\n:test: false\n"
YAML is creating the hash, but hashes can't have duplicated keys; If they do, the collision results in the second replacing the first.
"Yaml Cookbook
at the YamlForRuby site" is also a great resource.

How does Ruby's replace work?

I'm looking at ruby's replace: http://www.ruby-doc.org/core/classes/String.html#M001144
It doesn't seem to make sense to me, you call replace and it replaces the entire string.
I was expecting:
replace(old_value, new_value)
Is what I am looking for gsub then?
replace seems to be different than in most other languages.
I agree that replace is generally used as some sort of pattern replace in other languages, but Ruby is different :)
Yes, you are thinking of gsub:
ruby-1.9.2-p136 :001 > "Hello World!".gsub("World", "Earth")
=> "Hello Earth!"
One thing to note is that String#replace may seem pointeless, however it does remove 'taintediness". You can read more up on tained objects here.
I suppose the reason you feel that replace does not make sense is because there is assigment operator = (not much relevant to gsub).
The important point is that String instances are mutable objects. By using replace, you can change the content of the string while retaining its identity as an object. Compare:
a = 'Hello' # => 'Hello'
a.object_id # => 84793190
a.replace('World') # => 'World'
a.object_id # => 84793190
a = 'World' # => 'World'
a.object_id # => 84768100
See that replace has not changed the string object's id, whereas simple assignment did change it. This difference has some consequences. For example, suppose you assigned some instance variables to the string instance. By replace, that information will be retained, but if you assign the same variable simply to a different string, all that information is gone.
Yes, it is gsub and it is taken from awk syntax. I guess replace stands for the internal representation of the string, since, according to documentation, tainted-ness is removed too.

Resources