Ruby - substituting characters in a string with sequential elements of an array - ruby-on-rails

I have a string:
a = 'bla \n bla \n bla \n'
And an array:
b = ['1', '2', '3']
I want to search through the string, and replace every nth instance of \n with the (n-1)th element from the array, resulting in:
a = 'bla 1 bla 2 bla 3'
What is the simplest way for me to do this?

String#gsub with a block makes short work of this:
a.gsub('\n') { b.shift }
Note that Array#shift modifies the original array. Make a copy of it first (b.dup) if that's a problem.

You can use method sub
a = 'bla \n bla \n bla \n'
b = ['1', '2', '3']
b.each { |i| a.sub!('\n', i) }
#> a
#=> "bla 1 bla 2 bla 3"

Just one more way using String#split and Array#zip
a.split('\n').zip(b).join
#=> "bla 1 bla 2 bla 3"

Related

Parse looping forever when replacing %% by <!-- -->

I want to transform
test: "bla bla %bla bla% bla bla bla bla %bla% bla"
into
test: "bla bla <!--bla bla--> bla bla bla bla <!--bla--> bla"
which I thought would be easy as it was a slight variation of red parsing and replacing double % with <> doesn't work
but my code loops forever though I have "to end" rule:
test: "bla bla %bla bla% bla bla bla bla %bla% bla"
toggle: -1
rules: [
any [
to "%" mark: (
toggle: negate toggle
either toggle = 1 [change mark {} insert mark {<!--}][change mark {}
insert mark {-->}]
)
]
|
to end
]
parse test rules
test
I would not use to (or thru), they are bit dangerous IMO. I would use something like:
toggle: false
parse test [
some [
change #"%" (either toggle: not toggle ["<!--"]["-->"])
| skip
]
]
Also, you can get rid of the toggle, if you want to:
parse test [
some [
change #"%" (first reverse ["-->" "<!--"])
| skip
]
]
The problem comes from you trying to exchange "%" with an empty string. "%" stays there and gets always a hit. Your rule works with these modifications
rules: [
any [
to "%" mark: (
toggle: negate toggle
either toggle = 1 [
change/part mark {<!--} 1
][
change/part mark {-->} 1
]
)
]
to end
]
Without using parse, though there is no advantage, just another way:
until [none? attempt [insert remove find test "%" first reverse ["-->" "<!--"]]]

Parse doesn't work for insertion after last line of some keyword

I want to insert new sentence under last line where keyword is found, but it doesn't work, seems simple at first:
source: {
bla bla bla bla
bla bla bla bla
bla bla keyword bla bla
bla bla keyword bla bla
bla bla keyword bla bla
bla bla bla bla
bla bla bla bla
bla bla bla bla
}
rules: [
some [
thru "keyword" to newline skip
]
mark: ([insert mark "stranger"])
to end
]
parse source rules
Your block evaluates to the same block. You have to use
mark: (insert mark "stranger")
without the block.
And don't use source as source is already defined as a mezzanine function.

Can't get rid of some characters when pushing string to array

I'm creating some kind of custom tags that I'll use later to filter some datas. However, when I add the tags inside an array, I get the following:
"[\"witcher 3\", \"badass\", \"epic\"]"
#tags = []
params[:tags].split(', ').map do |tag|
#tags.push(tag.strip)
end
# About 5 lines under
FileDetail.create!(path: path, creation_date: date, tags: #tags)
Why do these \ show up, and why don't .strip work?
Thank you in advance
You are setting an array of strings in #tag, and \" represents an escaped character, in this case " which is used by ruby to represent String objects.
Consider the following code (an try it on IRB):
foo = ["bar", "baz"]
#=> ["bar", "baz"]
foo.inspect
#=> "[\"bar\", \"baz\"]"
foo.each { |f| puts "tag: #{f}" }
# tag: bar
# tag: baz
As you can see, there is really no \ character to strip from the string, its just how ruby outputs a String representation. So your code doesn't need .strip method:
#tags = []
params[:tags].split(', ').map do |tag|
#tags.push(tag)
end
Not related to your question, but still relevant: split method will return an array, so there is no need to create one before and then push items to it; just assign the returned array to #tags.
For example:
params[:tags] = "witcher 3, badass, epic"
#=> "witcher 3, badass, epic"
#tags = params[:tags].split(', ')
#=> ["witcher 3", "badass", "epic"]
If you want, you can still use map and strip to remove leading and trailing spaces:
params[:tags] = "witcher 3, badass , epic "
#=> "witcher 3, badass , epic "
params[:tags].split(",").map(&:strip)
#=> ["witcher 3", "badass", "epic"]

Extract env variables from file using regex

# code
ENV['VAR_1'] = 'HELLO 1'
ENV['VAR_2'] = 'HELLO 2'
ENV['VAR_3'] = 'HELLO 3'
# code
How do I extract using ruby and regex each variable and it's value?
Currently I'm doing line by line which is stupid.
S3_SECRET = line.split(' = ').last.delete("'") if line =~ /ENV\['S3_SECRET'\]/
S3_KEY = line.split(' = ').last.delete("'") if line =~ /ENV\['S3_KEY'\]/
S3_BUCKET = line.split(' = ').last.delete("'") if line =~ /ENV\['S3_BUCKET'\]/
You may have quite a verbose regex like
/^ENV\['(.*?)'\] *= *'(.*?)'$/
See the regex demo.
Details:
^ - start of line
ENV\[' - a literal ENV[' substring
(.*?) - Group 1 capturing 0+ chars other than a newline as few as possible up to the first
'\] - literal '] text
*= * - a = sign enclosed with optional (0 or more) spaces
' - a single quote
(.*?) - Group 2 capturing 0+ chars other than a newline as few as possible up to the
' - final ' at...
$ - the end of the line.
Here is a Ruby demo:
s = <<DATA
# code
ENV['VAR_1'] = 'HELLO 1'
ENV['VAR_2'] = 'HELLO 2'
ENV['VAR_3'] = 'HELLO 3'
# code
DATA
puts s.scan(/^ENV\['(.*?)'\] *= *'(.*?)'$/).to_h
Output: {"VAR_1"=>"HELLO 1", "VAR_2"=>"HELLO 2", "VAR_3"=>"HELLO 3"}
Suppose you've read the file into an array of lines (using, say, IO#readlines).
arr = ["ENV['VAR_1'] = 'HELLO 1'",
"ENV['VAR_2'] = 'HELLO 2'",
"ENV['VAR_3'] = 'HELLO 3'"]
Rather than using a complex regex straight away, we can remove the text we don't want, split the slimed-down strings on "=", surrounded by spaces, and then convert the resultant array to a hash.
bad_bits = %w| ENV[ ] ' |
#=> ["ENV[", "]", "'"]
r = Regexp.union(bad_bits)
#=> /ENV\[|\]|'/
arr.map { |str| str.gsub(r, '') }.map { |s| s.split(/\s+=\s+/) }.to_h
#=> {"VAR_1"=>"HELLO 1", "VAR_2"=>"HELLO 2", "VAR_3"=>"HELLO 3"}
Notice that Regexp::union does the escaping of regex's special characters for you.

String interpolation with subhashes

In my code I want to use string interpolation for an email subject I am generating.
output = "this is my %{title}" % {title: "Text here"}
This works as expected, but is there a way to use hashes inside of hashes and still be able to use string interpolation?
It would be awesome if I could do something like:
output = "this is my %{title.text}" % {title: {text: "text here"}}
In Ruby 2.3, sprintf checks the hash's default value, so you could provide a default_proc to dig up the nested value:
hash = {title: {text: "text here"}}
hash.default_proc = proc { |h, k| h.dig(*k.to_s.split('.').map(&:to_sym)) }
"this is my %{title.text}" % hash
#=> "this is my text here"
Kind of hacky, but it seems to work.
I don't think this is possible with % method. You'd have to use regular Ruby interpolation with "#{}".
I'd also point out that you can use OpenStruct.
title = OpenStruct.new(text: 'text here')
output = "this is my #{title.text}"
It's actually not hard to make this work if you write a simple utility method to "squash" a nested Hash's keys, e.g.:
def squash_hash(hsh, stack=[])
hsh.reduce({}) do |res, (key, val)|
next_stack = [ *stack, key ]
if val.is_a?(Hash)
next res.merge(squash_hash(val, next_stack))
end
res.merge(next_stack.join(".").to_sym => val)
end
end
hsh = { foo: { bar: 1, baz: { qux: 2 } }, quux: 3 }
p squash_hash(hsh)
# => { :"foo.bar" => 1, :"foo.baz.qux" => 2, :quux => 3 }
puts <<END % squash_hash(hsh)
foo.bar: %{foo.bar}
foo.baz.qux: %{foo.baz.qux}
quux: %{quux}
END
# => foo.bar: 1
# foo.baz.qux: 2
# quux: 3

Resources