Overriding random points in file - ruby

I want to overwrite portions of a file based on a regex pattern. The ruby script will look for camelCase variable names and convert them to something like background-color. I have a list of all variables to convert (the keys) and what they need to be changed to (the values):
variables = {
"backgroundColor" => "background-color",
"fontSize" => "font-size",
"fontFamily" => "font-family",
"fontColor" => "font-color",
"formFont" => "form-font",
"linkColor" => "link-color",
"linkHoverColor" => "link-hover-color",
"linkDecoration" => "link-decoration",
"headingFamily" => "heading-family",
"headingColor" => "heading-color",
"baseWidth" => "base-width",
"baseColWidth" => "base-col-width",
"baseGutterWidth" => "base-gutter-width",
"isFluid" => "is-fluid",
"baseColCount" => "base-col-count",
"tabletWidth" => "tablet-width",
"mobilePortraitWidth" => "mobile-portrait-width",
"mobileLandscapeWidth" => "mobile-landscape-width"
}
I have a working shell script:
sed -i '' "s/${keys[i]}/${values[i]}/g" _MYconfig.scss
I am trying to translate this into Ruby. I tried reading the file line by line, but the lines in the file don't correspond to the items in the collection. Something like this will not work:
File.open("_skeleton.config.scss", "r+") do |file|
file.each_line do |line|
# use gsub here
end
end
Then I drew inspiration from this gist, and tried:
variables.each do |key, value|
%x(ruby -p -e "gsub /#{key}/, '#{value}' #{Dir.pwd}#{filename}")
end
but I can't seem to get it to work. I can't figure out how to write at random points in a file like sed. I can't figure out how to iterate through variables using the ruby version of sed. Any ideas?

Use this for each key:
newkey = oldkey.gsub(/(?<=[a-z])(?=[A-Z])/, '-').downcase
Explanation
The regex matches positions (not characters) located between the change in case by using a lookbehind and a lookahead
(?<=[a-z]) asserts that the previous char is lowercase
(?=[A-Z]) asserts that the next char is uppercase
the gsub replaces that position with a -
we downcase the result
To see the replacement before the downcase, see the substitutions at the bottom of this regex demo.

Try something like this:
# read all lines of the file into an array
file_lines = File.readlines("_skeleton.config.scss", "r+")
new_file_lines = []
# gsub each line with all your matches
file_lines.each do |line|
new_line = line
variables.each do |key, value|
new_line = new_line.gsub /#{key}/, value
end
new_file_lines << new_line
end
# then truncate the old file and write back the set of newlines
File.open("_skeleton.config.scss", "w") do |f|
new_file_lines.each do |line|
f.write line
end
end
note: not tested, may contain bugs...

Related

how do you use multiple arguments with gsub? (Ruby)

I need to add multiple arguments to the gsub parenthesis, but whatever I try it doesn't seem to work.
# encoding: utf-8
# !/usr/bin/ruby
# create an empty array
original_contents = []
# open file to read and write
f = File.open("input.txt", "r")
# pass each line through the array
f.each_line do |line|
# push edited text to the array
original_contents << line.gsub(/[abc]/, '*')
end
f.close
new_file = File.new("output.txt", "r+")
new_file.puts(original_contents)
new_file.close
I need it so I can do a lot of different search and replaces like this:
original_contents << line.gsub(/[abc]/, '*' || /[def]/, '&' || /[ghi]/, '£')
Of course I know this code doesn't work but you get the idea. I've tried using an array for each argument but it ends up printing the text into the output file multiple times.
Any ideas?
As Holger Just said, I also suggest you run gsub multiple times. You can make the code a bit prettier when you store the replacements in a hash and then iteratively apply them to the string with Enumerable#reduce.
replacements = {
/[abc]/ => '*',
/[def]/ => '&',
/[ghi]/ => '£'
}
f = File.open("input.txt", "r")
original_contents = f.lines.map do |line|
replacements.reduce(line) do |memo, (pat, replace)|
memo.gsub(pat, replace)
end
end
f.close
new_file = File.new("output.txt", "r+")
new_file.puts(original_contents)
new_file.close

inserting variable value in regex in ruby script

I am having a ruby script file for patter match. my input string look like below
this.plugin = document.getElementById("pluginPlayer");
my regex look like
regxPlayerVariable = '(.*?)=.*?document\.getElementById\("#{Regexp.escape(pluginPlayeVariable)}"\)'
here pluginPlayeVariable is a variable but its not macthing with input string.
if i change my rege and replace variable with its value it's work fine but i can not do that as it's a run time value which change accordingly.
i also tried some more regex mention below
regxPlayerVariable = '(.*?)=.*?document\.getElementById\("#{pluginPlayeVariable}"\)'
so how can i solve this issue?
First of all, regxPlayerVariable is not a Regexp, it's a String. And the reason why your interpolation does not work is because you are using single quotes. Look:
foo = "bar"
puts '#{foo}' # => #{foo}
puts "#{foo}" # => bar
puts %q{#{foo}} # => #{foo}
puts %Q{#{foo}} # => bar
puts %{#{foo}} # => bar
puts /#{foo}/ # => (?-mix:bar)
puts %r{#{foo}} # => (?-mix:bar)
Only the last two are actually regular expressions, but here you can see which quoting expressions do interpolation, and which don't.

How to scan text/string for certain words and gsub them out with desired words

I want to create a 'swearscan' that can scan user text and swap the swear words out for 'censored'. I thought I coded it properly, but obviously not because I'll show you what's happening. Someone please help!
And since its stackflow we'll substitute swear words for something else
puts "Input your sentence here: "
text = gets.downcase.strip
swear_words = {'cat' => 'censored', 'dog' => 'censored', 'cow' => 'censored'}
clean_text = swear_words.each do |word, clean|
text.gsub(word,clean)
end
puts clean_text
When I ran this program (with the actual swearwords) all it would return is the hash like so: catcensoreddogcensoredcowcensored. What is wrong with my code that it's returning the hash and not the clean_text with everything substituted out?
This works for me:
puts "Input your sentence here: "
text = gets.downcase.strip
swear_words = {'cat' => 'censored', 'dog' => 'censored', 'cow' => 'censored'}
swear_words.each do |word, clean| # No need to copy here
text.gsub!(word,clean) # Changed from gsub
end
puts text # Changed from clean_text
What is wrong is that gsub does not change the original string, but you are expecting it to do so. Using gsub! will change the original string. You are also wrong to expect each to return something in it. Just refer to text in the end to get the replaced string.
By the way, if the replacement strings are all the same 'censored', then it does not make sense to use a hash there. You should just have an array of the swear words, and put the replacement string in the gsub! method directly (or define it as a constant in some other place).

Concatenating strings with << is adding unexpected new lines

As part of a data cleanup effort I need to append a string to the end of each line of data.
export = File.new('C:\clean_file.txt' , 'w+')
File.open('C:\dirty_file.txt').each_with_index do |line, index|
start_string = line.to_s
# start_string => "23-SEP-13","201309","208164","F5140"
# some gsub code on start_string...
# start_string => "09/23/2013","201309","208164","Customer1"
decoded_string = start_string
decoded_string << %q(,"Accounts")
export.puts decoded_string
end
However, when I attempt to append the string with the << operator, I'm getting an extra carriage return:
# clean_file.txt looks like this =>
line1: "09/23/2013","201309","208164","Customer1"
line2: ,"Accounts"
line3: "09/24/2013","201309","208165","Customer2"
line4: ,"Accounts"
# etc.
I tried:
decoded_string = start_string + %q("Accounts")
but got the same result, and it seems like << is the preferred way to concatenate strings in Ruby. How should I be appending strings to ensure 'clean_file.txt' looks like the below?
# clean_file.txt SHOULD love look like this =>
line1: "09/23/2013","201309","208164","Customer1","Accounts"
line2: "09/24/2013","201309","208165","Customer2","Accounts"
# etc.
Change
start_string = line.to_s
to
start_string = line.chomp
The newlines are coming from the lines read from the file.

Create regular expression from string

Is there any way to create the regex /func:\[sync\] displayPTS/ from string func:[sync] displayPTS?
The story behind this question is that I have serval string pattens to search against in a text file and I don't want to write the same thing again and again.
File.open($f).readlines.reject {|l| not l =~ /"#{string1}"/}
File.open($f).readlines.reject {|l| not l =~ /"#{string2}"/}
Instead , I want to have a function to do the job:
def filter string
#build the reg pattern from string
File.open($f).readlines.reject {|l| not l =~ pattern}
end
filter string1
filter string2
s = "func:[sync] displayPTS"
# => "func:[sync] displayPTS"
r = Regexp.new(s)
# => /func:[sync] displayPTS/
r = Regexp.new(Regexp.escape(s))
# => /func:\[sync\]\ displayPTS/
I like Bob's answer, but just to save the time on your keyboard:
string = 'func:\[sync] displayPTS'
/#{string}/
If the strings are just strings, you can combine them into one regular expression, like so:
targets = [
"string1",
"string2",
].collect do |s|
Regexp.escape(s)
end.join('|')
targets = Regexp.new(targets)
And then:
lines = File.readlines('/tmp/bar').reject do |line|
line !~ target
end
s !~ regexp is equivalent to not s =~ regexp, but easier to read.
Avoid using File.open without closing the file. The file will remain open until the discarded file object is garbage collected, which could be long enough that your program will run out of file handles. If you need to do more than just read the lines, then:
File.open(path) do |file|
# do stuff with file
end
Ruby will close the file at the end of the block.
You might also consider whether using find_all and a positive match would be easier to read than reject and a negative match. The fewer negatives the reader's mind has to go through, the clearer the code:
lines = File.readlines('/tmp/bar').find_all do |line|
line =~ target
end
How about using %r{}:
my_regex = "func:[sync] displayPTS"
File.open($f).readlines.reject { |l| not l =~ %r{#{my_regex}} }

Resources