From 0342324b4721ca0d152125a6c583cddb218c0c79 Mon Sep 17 00:00:00 2001 From: Kyle Zhao Date: Tue, 3 Oct 2017 20:47:53 -0400 Subject: [PATCH] FEATURE: support regex in rake post:remap (#5201) --- app/models/post.rb | 10 +++++++ lib/tasks/posts.rake | 58 +++++++++++++++++++++++++--------------- spec/tasks/posts_spec.rb | 30 ++++++++++++++++++++- 3 files changed, 76 insertions(+), 22 deletions(-) diff --git a/app/models/post.rb b/app/models/post.rb index 992e3a83a9c..a70c69a915a 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -91,6 +91,16 @@ class Post < ActiveRecord::Base q.order('posts.created_at ASC') } + scope :raw_match, -> (pattern, type = 'string') { + type = type&.downcase + + case type + when 'string' + where('raw ILIKE ?', "%#{pattern}%") + when 'regex' + where('raw ~ ?', pattern) + end + } delegate :username, to: :user diff --git a/lib/tasks/posts.rake b/lib/tasks/posts.rake index 6814c5c0932..8a4bc69b6a3 100644 --- a/lib/tasks/posts.rake +++ b/lib/tasks/posts.rake @@ -42,23 +42,18 @@ end desc 'Rebake all posts matching string/regex and optionally delay the loop' task 'posts:rebake_match', [:pattern, :type, :delay] => [:environment] do |_, args| + args.with_defaults(type: 'string') pattern = args[:pattern] - type = args[:type] - type = type.downcase if type - delay = args[:delay].to_i if args[:delay] + type = args[:type]&.downcase + delay = args[:delay]&.to_i + if !pattern puts "ERROR: Expecting rake posts:rebake_match[pattern,type,delay]" exit 1 elsif delay && delay < 1 puts "ERROR: delay parameter should be an integer and greater than 0" exit 1 - end - - if type == "regex" - search = Post.where("raw ~ ?", pattern) - elsif type == "string" || !type - search = Post.where("raw ILIKE ?", "%#{pattern}%") - else + elsif type != 'string' && type != 'regex' puts "ERROR: Expecting rake posts:rebake_match[pattern,type] where type is string or regex" exit 1 end @@ -66,7 +61,7 @@ task 'posts:rebake_match', [:pattern, :type, :delay] => [:environment] do |_, ar rebaked = 0 total = search.count - search.find_each do |post| + Post.raw_match(pattern, type).find_each do |post| rebake_post(post) print_status(rebaked += 1, total) sleep(delay) if delay @@ -130,11 +125,15 @@ task 'posts:normalize_code' => :environment do puts "#{i} posts normalized!" end -def remap_posts(find, replace = "") +def remap_posts(find, type, replace = "") i = 0 - Post.where("raw LIKE ?", "%#{find}%").each do |p| - new_raw = p.raw.dup - new_raw = new_raw.gsub!(/#{Regexp.escape(find)}/, replace) || new_raw + + Post.raw_match(find, type).find_each do |p| + new_raw = + case type + when 'string' then p.raw.gsub(/#{Regexp.escape(find)}/, replace) + when 'regex' then p.raw.gsub(/#{find}/, replace) + end if new_raw != p.raw p.revise(Discourse.system_user, { raw: new_raw }, bypass_bump: true, skip_revision: true) @@ -142,42 +141,59 @@ def remap_posts(find, replace = "") i += 1 end end + i end desc 'Remap all posts matching specific string' -task 'posts:remap', [:find, :replace] => [:environment] do |_, args| +task 'posts:remap', [:find, :replace, :type] => [:environment] do |_, args| + require 'highline/import' + args.with_defaults(type: 'string') find = args[:find] replace = args[:replace] + type = args[:type]&.downcase + if !find puts "ERROR: Expecting rake posts:remap['find','replace']" exit 1 elsif !replace puts "ERROR: Expecting rake posts:remap['find','replace']. Want to delete a word/string instead? Try rake posts:delete_word['word-to-delete']" exit 1 + elsif type != 'string' && type != 'regex' + puts "ERROR: Expecting rake posts:delete_word[pattern, type] where type is string or regex" + exit 1 + else + confirm_replace = ask("Are you sure you want to replace all #{type} occurrences of '#{find}' with '#{replace}'? (Y/n)") + exit 1 unless (confirm_replace == "" || confirm_replace.downcase == 'y') end puts "Remapping" - total = remap_posts(find, replace) + total = remap_posts(find, type, replace) puts "", "#{total} posts remapped!", "" end desc 'Delete occurrence of a word/string' -task 'posts:delete_word', [:find] => [:environment] do |_, args| +task 'posts:delete_word', [:find, :type] => [:environment] do |_, args| require 'highline/import' + args.with_defaults(type: 'string') find = args[:find] + type = args[:type]&.downcase + if !find puts "ERROR: Expecting rake posts:delete_word['word-to-delete']" exit 1 + elsif type != 'string' && type != 'regex' + puts "ERROR: Expecting rake posts:delete_word[pattern, type] where type is string or regex" + exit 1 else - confirm_replace = ask("Are you sure you want to remove all occurrences of '#{find}'? (Y/n) ") - exit 1 unless (confirm_replace == "" || confirm_replace.downcase == 'y') + confirm_delete = ask("Are you sure you want to remove all #{type} occurrences of '#{find}'? (Y/n)") + exit 1 unless (confirm_delete == "" || confirm_delete.downcase == 'y') end puts "Processing" - total = remap_posts(find) + total = remap_posts(find, type) puts "", "#{total} posts updated!", "" end diff --git a/spec/tasks/posts_spec.rb b/spec/tasks/posts_spec.rb index f69a3bbf685..7f3e307f9e1 100644 --- a/spec/tasks/posts_spec.rb +++ b/spec/tasks/posts_spec.rb @@ -1,18 +1,46 @@ require 'rails_helper' +require 'highline/import' +require 'highline/simulate' RSpec.describe "Post rake tasks" do before do + Rake::Task.clear Discourse::Application.load_tasks IO.any_instance.stubs(:puts) end describe 'remap' do + let!(:tricky_post) { Fabricate(:post, raw: 'Today ^Today') } + it 'should remap posts' do post = Fabricate(:post, raw: "The quick brown fox jumps over the lazy dog") - Rake::Task['posts:remap'].invoke("brown", "red") + HighLine::Simulate.with('y') do + Rake::Task['posts:remap'].invoke("brown", "red") + end + post.reload expect(post.raw).to eq('The quick red fox jumps over the lazy dog') end + + context 'when type == string' do + it 'remaps input as string' do + HighLine::Simulate.with('y') do + Rake::Task['posts:remap'].invoke('^Today', 'Yesterday', 'string') + end + + expect(tricky_post.reload.raw).to eq('Today Yesterday') + end + end + + context 'when type == regex' do + it 'remaps input as regex' do + HighLine::Simulate.with('y') do + Rake::Task['posts:remap'].invoke('^Today', 'Yesterday', 'regex') + end + + expect(tricky_post.reload.raw).to eq('Yesterday ^Today') + end + end end end