「LSEP」という不可視文字の削除方法

[バヅクリ](https://buzzkuri.com)CTOの合原です。
今回、非エンジニアからの依頼で、「LSEP」という記号？文字？の除去作業をシュッと行ったので、まとめたいと思います。

## 前提
当社[バヅクリ](https://buzzkuri.com)では、下記のように、オンラインで、
チームビルディングや研修を目的としてオリジナルのプログラムを提供しています。

とても、とても簡単に説明すると、、、

契約企業から特定のプログラムでのイベント開催の希望が発生すると、
アプリケーション上では、プログラムデータからイベントデータを自動生成しています。

プログラム、イベントそれぞれのデータについてCRUD処理が存在します。

![スクリーンショット 2022-07-20 17.04.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64537/b616cc8f-9030-d4a2-6b4c-1f822830e232.png)

通常企画チームにて、プログラムの入稿や更新、運営担当によるイベントデータの更新などが適宜行われています。

## 調査
さて、今回、見たこともない「LSEP」なるものがなぜ混入したのか。。。。？
そもそも、「LSEP」とは？？BOM的なモノ？

調べてみると、
[こちら](https://artabi.net/notes/wordpress/lsep)にあるように、どうも不可視文字なんだとか。

どうもエディタ固有の「行区切り」らしいです。

実際今回、本件の依頼者に入稿手順を確認してみたところ、
彼は、　mac標準の「メモ」を利用していました。

具体的には、
1. パワポから「メモ」アプリへコピペ
1. 「メモ」アプリ内で少し整形して、当社の社内管理サイトでプログラム入稿

をしていたとことで、目の前で作業をしてもらったところ再現しました :sweat: 

実態と原因が判明しました。
あとは、
1. LSEPが混入しないようすること
1. すでに混入してしまっているデータのクリーニング

をすればよさそうです。

## 1. LSEPが混入しないようする
所詮は文字コードなので、、、と調べてみると、[こちら](https://usortblog.com/lsep_problem/#toc2)にphpでの実装例がありましたので、こちらをrubyで実装すれば、除去できそうです。
また、「文字列」の操作であることと、当社ではすでに、Ruby標準ライブラリであるStringを拡張したライブラリを使っていることからそこに組み込みば、より簡潔に対応できそうです。

...ということで、以下のように拡張してみました。

```rb
class String
  :

  def remove_lsep # 
    self.delete("\u2028")
  end
end

```


あとはこれを使って、プログラム、イベントそれぞれの　modelでbefore_validation処理として、
上記を用いて、デフォルトで除去する形にします。

```rb:program.rb
class Clubs::Program < ApplicationRecord
  include MediaPathUsable
  include ImageResizable
  include MediaStorable
  mount_uploader :main_image_file_path, ::Clubs::ProgramUploader
  
  :

  before_validation do
    self.content = content.remove_lsep if content
    self.md_content = md_content.remove_lsep if md_content
    self.participants_content = participants_content.remove_lsep if participants_content
    self.md_participants_content = md_participants_content.remove_lsep if md_participants_content
  end

   :
end

```



## すでに混入してしまっているデータのクリーニング
ここまでで、プログラム、イベントそれぞれのデータの追加、更新時に自動でLSEP除去が行われる状態になりました。
つまり、対象のデータをシンプルにそのままsaveすれば、更新がかかり、callback処理＝~~before_save~~before_validation
に用意した処理が呼ばれるので、シンプルに以下のようにしてみました。

```rb
namespace :lesp_string do
  desc 'remove'
  task cleanup: :setup_logger do
    Rake::Task['lesp_string:remove:programs'].execute
    Rake::Task['lesp_string:remove:events'].execute
  end


  namespace :remove do
    desc 'cleanup programs'
    task programs: :setup_logger do
      begin
        ActiveRecord::Base.transaction do
          updating_programs.each do |p|
            p.save(validate: false)
            p.preparations.each(&:save!)
          end
        end
      rescue StandardError => e
        puts e.message
      end
    end

    desc 'cleanup events'
    task events: :setup_logger do
      begin
        ActiveRecord::Base.transaction do
          updating_events.each do |p|
            p.save(validate: false)
            p.preparations.each(&:save!)
          end
        end
      rescue StandardError => e
        puts e.message
      end
    end
  end

  def updating_programs
    @programs ||= ::Clubs::Program.order(:created_at)
  end

  def updating_events
    @events ||= ::Event.order(:created_at)
  end

  task setup_logger: :environment do
    Rails.logger = Logger.new(STDOUT)
  end

end

```

こちらをデプロイして、動作確認して、今回は無事、問題解消できました。



## 所感

最初はLSEPってなんだ？って驚きましたが、冷静に文字列処理で解決できることがわかれば、あとは、
Rails wayに則ったやりかたで、スリム対応ができるなと、実感した事案でした。
正直、Stringクラスを拡張していたこと自体忘れていましたが :sweat: 今回はとても助かりました :sweat: 

今回のような例に限らず、弊社では、Railsを実直に利用し、できる限りRailに乗せた設計、コーディングを行なっています。結果的にそのほうが、新規開発、既存改修などあらゆる面で柔軟性があり、スピーディな実装が可能となるからです。OOPの実践についても、今後、ちょうどいい事案がありましたらまた、披露できたらと思います。 :bow:  
（要は、フレームワークやデザインパターンといった先人の知恵は積極的使うほうがbetter!という発想です w）

### 今回も恒例のやつですが :sweat: 
当社では、バックエンドサーバーとして主にRails、フロントエンドでは、Nextjsを利用しています。
新しい物（コト）好きの当社では、絶賛、エンジニアを募集中です！

気になる方はお気軽にご連絡いただけましたらと思います！

https://www.wantedly.com/projects/1050133

https://buzzkuri.co.jp/recruit/engineer


https://buzzkuri.co.jp

### 編集後記
コメントにあるとおり、@scivola さんからのご指摘を受け、一部、対応内容修正させていただきました。
ありがとうございます。’