I'm using URI.encode to generate HTML data URLs:
visit "data:text/html,#{URI::encode(html)}"
After upgrading to Ruby 2.7.1, interpreter started warning:
warning: URI.escape is obsolete
Recommended replacements of this are CGI.escape and URI.encode_www_form_component. However, they're not doing same thing:
2.7.1 :007 > URI.escape '<html>this and that</html>'
(irb):7: warning: URI.escape is obsolete
=> "%3Chtml%3Ethis%20and%20that%3C/html%3E"
2.7.1 :008 > CGI.escape '<html>this and that</html>'
=> "%3Chtml%3Ethis+and+that%3C%2Fhtml%3E"
2.7.1 :009 > URI.encode_www_form_component '<html>this and that</html>'
=> "%3Chtml%3Ethis+and+that%3C%2Fhtml%3E"
Result of these slight encoding differences - html page where spaces are replaced by +. My question is - what's a good replacement of URI.encode for this use case?
There is actually a drop in replacement.
s = '<html>this and that</html>'
p = URI::Parser.new
p.escape(s)
=> "%3Chtml%3Ethis%20and%20that%3C/html%3E"
Docs: https://docs.w3cub.com/ruby~3/uri/rfc2396_parser
Found this through a comment under this article
https://docs.knapsackpro.com/2020/uri-escape-is-obsolete-percent-encoding-your-query-string
Also tested this against some other strings in my setup, this also seems to retain commas the same way URI.escape does, in contrast to ERB::Util.url_encode.
NOTE:
As this answer became so popular now, it's probably worth to mention that you should not blindly change your code to use URI::Parser unless you are certain your project doesn't need a standards compliant encoder. As URI.escape was actually deprecated for a reason. So before simply switching to URI::Parser make sure you have read and understood https://stackoverflow.com/a/13059657/6376353
There is no official RFC 3986-compliant URI escaper in the Ruby standard library today.
See Why is URI.escape() marked as obsolete and where is this REGEXP::UNSAFE constant? for background.
There are several methods that have various issues with them as you have discovered and pointed out in the comment:
They produce deprecation warnings
They do not claim standards compliance
They are not escaping in accordance with RFC 3986
They are implemented in tangentially related libraries
From Apidock.com
require "erb"
include ERB::Util
puts url_encode("Programming Ruby: The Pragmatic Programmer's Guide")
Generates
Programming%20Ruby%3A%20%20The%20Pragmatic%20Programmer%27s%20Guide
Related
I'm doing a Ruby REPL (just a hobby, won't be big and professional like pry).
I wrote a very simple REPL that works fine if the input it's just a single valid line of Ruby:
loop do
print "ruby> "
input = gets
puts "=> #{eval(input)}"
end
I want to support multiline inputs.
One approach that I'm thinking is to check for each input line if the code is a correct Ruby expression, part of a Ruby expression or invalid code.
valid_expression?("def foo; end") # => true, complete expression
valid_expression?("def foo") # => true, partial expression
valid_expression?("def ::foo") # => false
Anyway, I tried to understand other implementations1,2,3 but is really difficult/undocumented code. Maybe I can use RubyLex or Ripper.
Ideally, I'm interesting to use Ruby standard libraries without any external gem. It doesn't matter if only target for Ruby 2.x versions, but if there is a gem to do the work, I'll happy to use it.
Since you noticed that there is pry, you should look its source and learn from it. Actually, pry does internally use such method as you described. It is: MethodSource::CodeHelpers#complete_expression?.
Long story short, I was writing a method that included an options argument, that will do certain stuff if the value for the key :if, evaluated to true. When I trying the hash in IRB using the new syntax I got a syntax error in IRB, the prompt stays open:
1.9.3p374 :010 > {if: true}
1.9.3p374 :011?>
Using the old syntax, works just fine:
1.9.3p374 :011 > {:if => true}
=> {:if=>true}
All keywords that start a statement, exhibit the same behavior. E.g. def, do, module, case
Other reserved words that occur in the middle and class work just fine: else, end
My question is: Is this expected behavior, a bug or a limitation?
It's challenging to reliably and unambiguously parse things in any language. This is especially true when you start using reserved words. And irb has to go beyond that and provide an interactive model on top of the parser, which is even harder. I personally don't think there's too much value in worrying about cases like this, either as a user of the language or as a maintainer. In my mind, it's better to simply figure out what works and avoid getting into these situations if possible.
You can see some similar behaviors in plain Ruby, outside irb. For example:
puts({if: true}) # no problem, behaves as expected in Ruby 1.9.3.
puts {if: true} # raises a syntax error in Ruby 1.9.3
To answer your question, is it "expected behavior, a bug or a limitation", I'd say you should ignore irb and compare it to plain Ruby, and if you do this, it works fine. That means it has to be an irb bug.
But is it possible or worthwhile to solve? #coreyward makes a good point in his comment that irb has to delay execution in most cases when it encounters an if. You'd have to look further to know for sure, but you may not be able to unambiguously interpret all cases like this.
My advice: avoid this construct altogether if you can, and don't use reserved words for labels if you can avoid it!
Here's a file you can run with plain Ruby (eg MRI). You should see {:if=>true} in the output to confirm it works.
{if: true}
foo = {if: true}
# if MRI is working, should be able to execute this file without trouble.
p foo
The question is: Can I define my own custom operator in Ruby, except for the ones found in
"Operator Expressions"?
For example: 1 %! 2
Yes, custom operators can be created, although there are some caveats. Ruby itself doesn't directly support it, but the superators gem does a clever trick where it chains operators together. This allows you to create your own operators, with a few limitations:
$ gem install superators19
Then:
require 'superators19'
class Array
superator "%~" do |operand|
"#{self} percent-tilde #{operand}"
end
end
puts [1] %~ [2]
# Outputs: [1] percent-tilde [2]
Due to the aforementioned limitations, I couldn't do your 1 %! 2 example. The Documentation has full details, but Fixnums can't be given a superator, and ! can't be in a superator.
No. You can only define operators already specified in ruby, +,-,!,/,%, etc. (you saw the list)
You can see for yourself this won't work
def HI
def %!
puts "wow"
end
end
This is largely due to the fact that the syntax parser would have to be extended to accept any code using your new operator.
As Darshan mentions this example alone may not be enough to realize the underlying problem. Instead let us take a closer look at how the parser could possibly handle some example code using this operator.
3 %! 0
While with my spacing it may seem obvious that this should be 3.%!(0) without spacing it becomes harder to see.
3%! can also be seen as 3.%(0.!) The parser has no idea which to chose. Currently, there is no way easy way to tell it. Instead, we could possibly hope to override the meaning of 3.%(0.!) but this isn't exactly defining a new operator, as we are still only limited to ruby's parsable symbols
You probably can't do this within Ruby, but only by modifying Ruby itself. I think modifying parse.y would be your best bet. parse.y famtour
I've only seen something like rand(1..5) work in MRI ruby 1.9.3 (haven't tried 1.9.2). Jruby doesn't support it, even in 1.9 mode - it raises a TypeError.
Even ruby-doc doesn't mention that Ranges are supported. What's the official behavior?
UPDATE
Well as the answers and comments point out, only 1.9.3 supports it. Jruby is only at 1.9.2.
The docs do say that it is supported
If max is Range, returns a pseudorandom number where range.member(number) == true.
It's also in the 1.9.3 changelog
Verbatim Copy-paste from docs
If max is Range, returns a pseudorandom number where range.member(number) == true.
Or else converts max to an integer using max1 = max.to_i.abs.
so, yes. it is supported from ruby 1.9.3
array.include? 'foo' or array.include? 'bar'
is a syntax error (unexpected keyword_or). Parentheses solve the problem, but as I'm new to Ruby I've no idea which of the following is considered more idiomatic:
Option 1
array.include?('foo') or array.include?('bar')
Option 2
(array.include? 'foo') or (array.include? 'bar')
Does this come down to personal preference, or is one approach considered more "correct"?
I'd suggest you take a look at the community-driven Ruby coding style guide, here particularly the section on Syntax.
Omit parentheses around parameters for methods that are part of an internal DSL (e.g. Rake, Rails, RSpec), methods that are with "keyword" status in Ruby (e.g. attr_reader, puts) and attribute access methods. Use parentheses around the arguments of all other method invocations. - excerpt from the guide
class Person
attr_reader :name, :age
# omitted
end
temperance = Person.new('Temperance', 30)
temperance.name
puts temperance.age
x = Math.sin(y)
array.delete(e)
Are you sure that is failing? Your initial example works fine for me.
ruby-1.9.2-p290 :002 > array = ['bar']
=> ["bar"]
ruby-1.9.2-p290 :003 > array.include? 'foo' or array.include? 'bar'
=> true
As a matter of fact, if anything could be considered idiomatic it would be that one. The low precedence of or allows this to work when you leave the parens off. This characteristic is something that should make it idiomatic to Ruby (and even Perl, which or is a hold over from).
Option 1 is super clear, but considering you included the parens you really have no need to use or. It's probably better to use ||, which has a high precedence like other operators and is just more common. I think using or for the sake of it looking like english is not a great practice. It has a semantic meaning within the language and is probably best used for those qualities.
Option 2 is silly of course. If you're going to include parens, you might as well use them for the method signature.
Hope this helps.
Avdi Grimm reckons you shouldn't use and or or for boolean logic. You should only and or or for control flow (analogous to if or unless)
According to his recommendation, you should use || instead:
array.include?('foo') || array.include?('bar')
Option 1 is preferred since it's common to other languages as well. Option 2 looks like LISP, which is not popular nowadays.