I have a multi line HEREDOC such as this:
c = <<-MYTEXT
{ 'Cache-Control' => "public, max-age=#{2.days.to_i}" }
MYTEXT
This raises the error
undefined method `days' for 2:Integer (NoMethodError)
I don't want Ruby to interpolate the string and write the value of 2 days in integer, but instead I want it to write exactly the string #{2.days.to_i}
If I escape the # and the \ write it like
{ 'Cache-Control' => "public, max-age=\#\{2.days.to_i\}\" }
it works, but imagine a long text with many #{} string interpolations, ugly.
Any smarter way of doing this?
You can "disable" interpolation by using single-quoted strings
c = <<-'MYTEXT'
{ 'Cache-Control' => "public, max-age=#{2.days.to_i}" }
MYTEXT
Related
I am attempting to replace a variable in a large search string I will be using to call Elastic Search, but I can't seem to get the variable into the string. Below is what I have attempted, using string interpolation to try and add my_var into the search string at 3 different places.
my_var = "foo"
search_string = '{"query":{"bool":{"must":[{"nested":{"path":"some_status", "query":{"bool":{"must":{"terms":{"status.code":["ACTIVE"]}}}}}}, {"bool":{"should":[{"nested":{"path":"acc", "query":{"bool":{"must":[{"match_phrase":{"acc.name.text":"{#my_var}"}}]}}}}, {"bool":{"must":[{"match_phrase":{"name.stuff":"{#my_var}"}}]}}, {"nested":{"path":"trade_names", "query":{"bool":{"must":[{"match_phrase":{"some_names.some_name.stuff":"{#my_var}"}}]}}}}]}}]}}}'
JSON.parse(search_string)
What am I doing wrong here?
First of all, it's #{my_var}, not {#my_var}.
Beside that typo, string interpolation needs "...", it is disabled in '...':
foo = 123
str = "foo = #{foo}" # <- turns #{foo} into 123
#=> "foo = 123"
str = 'foo = #{foo}' # <- keeps #{foo} literally
#=> "foo = \#{foo}"
Since your string contains many literal double quote characters, using "..." would add a lot of escape characters.
Apart from quotes, you could use the percent string %Q(...):
str = %Q({"foo":"#{foo}"})
#=> "{\"foo\":\"123\"}"
or a heredoc: (using JSON as the delimiter might enable syntax highlighting in your editor)
str = <<-JSON.chomp
{"foo":"#{foo}"}
JSON
#=> "{\"foo\":\"123\"}"
Another option is to construct a Ruby hash and turn that into JSON:
require 'json'
hash = { 'foo' => foo }
str = hash.to_json
#=> "{\"foo\":123}"
The JSON library also handles escaping for you.
String format works like this:
someString = "Example string %{key}"
result = someString % {key: newData}
I would like to retrieve the hash keys in the string without hardcoding them. Is there a method for doing this?
Also, is there any way to construct the format string using variables? I would like to do something like this:
variable = key
result = someString % {variable: newData}
You almost got it. Just a bit off with the syntax
variable = :key # get this one from whereever
someString = "Example string %{key}"
someString % { variable => 'foo' } # => "Example string foo"
One way to extract keys from the format string:
"Example string %{key1} %{key2}".scan /(?<=%{)[^{}]+?(?=})/
# => ["key1", "key2"]
The regex (?<=%{)[^{}]+?(?=}) matches one or more characters (non-greedy) if it's prefixed by %{ and followed by }.
To construct the format string, you can use string interpolation:
variable = 'key'
"Example string %{#{variable}}"
# => "Example string %{key}"
I have loaded a string with #{variable} references in it. How would I resolve those variables in the string like puts does?
name="jim"
str="Hi #{name}"
puts str
Instead of puts, I would like to have the result available to pass as a parameter or save into a variable.
you could eval it
name = "Patrick"
s = 'hello, #{name}'
s # => "hello, \#{name}"
# wrap the string in double quotes, making it a valid interpolatable ruby string
eval "\"#{s}\"" # => "hello, Patrick"
puts doesn't resolve the variables. The Ruby parser does when it creates the string. if you passed str to any other method, it would be the same as passing 'Hi jim', since the interpolation is already done.
String has a format option that appears as %. It can be used to pass arguments into a predefined string much like interpolation does.
message = "Hello, %s"
for_patrick = message % "Patrick" #=> "Hello, Patrick"
for_jessie = message % "Jessie" #=> "Hello, Jessie"
messages = "Hello, %s and %s"
for_p_and_j = messages % ["Patrick", "Jessie"] #=> "Hello, Patrick and Jessie"
It may not look "Rubyish" but I believe it is the functionality you are looking for.
So, if you have a string coming in from somewhere that contains these placeholders, you can then pass in values as arguments as so:
method_that_gets_hello_message % "Patrick"
This will also allow you to only accept values you are expecting.
message = "I can count to %d"
message % "eleven" #=> ArgumentError: invalid value for Integer()
There's a list on Wikipedia for possible placeholders for printf() that should also work in Ruby.
The eval seems to be the only solution for this particular task. But we can avoid this dirty-unsafe-dishonourable eval if we modify the task a bit: we can resolve not local, but instance variable without eval using instance_variable_get:
#name = "Patrick"
#id = 2 # Test that number is ok
#a_b = "oooo" # Test that our regex can eat underscores
s = 'hello, #{name} !!#{id} ??#{a_b}'
s.gsub(/#\{(\w+)\}/) { instance_variable_get '#'+$1 }
=> "hello, Patrick !!2 ??oooo"
In this case you even can use any other characters instead of #{} (for example, %name% etc), by only modifying the regex a bit.
But of course, all this smells.
It sounds like you want the basis for a template system, which Ruby does easily if you use String's gsub or sub methods.
replacements = { '%greeting%' => 'Hello', '%name%' => 'Jim' }
pattern = Regexp.union(replacements.keys)
'%greeting% %name%!'.gsub(pattern, replacements)
=> "Hello Jim!"
You could just as easily define the key as:
replacements = { '#{name}' => 'Jim' }
and use Ruby's normal string interpolation #{...} but I'd recommend not reusing that. Instead use something unique.
The advantage to this is the target => replacement map can easily be put into a YAML file, or a database table, and then you can swap them out with other languages, or different user information. The sky is the limit.
The benefit to this also, is there is no evaluation involved, it's only string substitution. With a bit of creative use you can actually implement macros:
macros = { '%salutation%' => '%greeting% %name%' }
replacements = { '%greeting%' => 'Hello', '%name%' => 'Jim' }
macro_pattern, replacement_pattern = [macros, replacements].map{ |h| Regexp.union(h.keys) }
'%salutation%!'.gsub(macro_pattern, macros).gsub(replacement_pattern, replacements)
=> "Hello Jim!"
I have a very large string that needs to escape all the single quotes in it, so I can feed it to JavaScript without upsetting it.
I have no control over the external string, so I can't change the source data.
Example:
Cote d'Ivoir -> Cote d\'Ivoir
(the actual string is very long and contains many single quotes)
I'm trying to this by using gsub on the string, but can't get this to work:
a = "Cote d'Ivoir"
a.gsub("'", "\\\'")
but this gives me:
=> "Cote dIvoirIvoir"
I also tried:
a.gsub("'", 92.chr + 39.chr)
but got the same result; I know it's something to do with regular expressions, but I never get those.
The %q delimiters come in handy here:
# %q(a string) is equivalent to a single-quoted string
puts "Cote d'Ivoir".gsub("'", %q(\\\')) #=> Cote d\'Ivoir
The problem is that \' in a gsub replacement means "part of the string after the match".
You're probably best to use either the block syntax:
a = "Cote d'Ivoir"
a.gsub(/'/) {|s| "\\'"}
# => "Cote d\\'Ivoir"
or the Hash syntax:
a.gsub(/'/, {"'" => "\\'"})
There's also the hacky workaround:
a.gsub(/'/, '\#').gsub(/#/, "'")
# prepare a text file containing [ abcd\'efg ]
require "pathname"
backslashed_text = Pathname("/path/to/the/text/file.txt").readlines.first.strip
# puts backslashed_text => abcd\'efg
unslashed_text = "abcd'efg"
unslashed_text.gsub("'", Regexp.escape(%q|\'|)) == backslashed_text # true
# puts unslashed_text.gsub("'", Regexp.escape(%q|\'|)) => abcd\'efg
Is there any way to prevent Ruby's JSON.pretty_generate() method from escaping a Unicode character?
I have a JSON object as follows:
my_hash = {"my_str" : "\u0423"};
Running JSON.pretty_generate(my_hash) returns the value as being \\u0423.
Is there any way to prevent this behaviour?
In your question you have a string of 6 unicode characters "\", "u", "0", "4", "2", "3" (my_hash = { "my_str" => '\u0423' }), not a string consisting of 1 "У" character ("\u0423", note double quotes).
According to RFC 4627, paragraph 2.5, backslash character in JSON string must be escaped, this is why your get double backslash from JSON.pretty_generate.
Alternatively, there are two-character sequence escape
representations of some popular characters. So, for example, a
string containing only a single reverse solidus character may be
represented more compactly as "\\".
char = unescaped /
escape (...
%x5C / ; \ reverse solidus U+005C
escape = %x5C ; \
Thus JSON ruby gem escape this character internally and there is no way to alter this behavior by parametrizing JSON or JSON.pretty_generate.
If you are interested in JSON gem implementation details - it defines internal mapping hash with explicit mapping of '' char:
module JSON
MAP = {
...
'\\' => '\\\\'
I took this code from a pure ruby variant of JSON gem gem install json_pure (note that there are also C extension variant that is distributed by gem install json).
Conclusion: If you need to unescape backslash after JSON genaration you need to implement it in your application logic, like in the code above:
my_hash = { "my_str" => '\u0423' }
# => {"my_str"=>"\\u0423"}
json = JSON.pretty_generate(my_hash)
# => "{\n \"my_str\": \"\\\\u0423\"\n}"
res = json.gsub "\\\\", "\\"
# => "{\n \"my_str\": \"\\u0423\"\n}"
Hope this helps!
Usually, hashes declared using rocket => rather than colon :. Also, there is alternative syntax for symbol-keyed hashes since 1.9: my_hash = {my_str: "\u0423"}. In this case, :my_str would be the key.
Anyway, on my computer JSON.pretty_generate works as expected:
irb(main):002:0> my_hash = {"my_str" => "\u0423"}
=> {"my_str"=>"У"}
irb(main):003:0> puts JSON.pretty_generate(my_hash)
{
"my_str": "У"
}
=> nil
Ruby 1.9.2p290, (built-in) json 1.4.2.