aboutsummaryrefslogtreecommitdiffstats
path: root/core/lib/retriever/entity/regexp_entity.rb
blob: c7c8433d9be66677be4f45bf9df558ad4b73ebf8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# -*- coding: utf-8 -*-
require_relative 'blank_entity'

module Retriever::Entity

=begin rdoc
特定の正規表現にマッチする部分を自動的にSegmentにするEntity。

==== Example

    class Sample < Retriever::Model
      entity_class Retriever::Entity::RegexpEntity.
        filter(/:(?:\w+):/, ->s{ s.merge(open: 'https://...') }). # :???: をクリックされたら対応する絵文字の画像(https://...)を開く
        filter(/@(?:\w+)/, ->s{ s.merge(open: "https://twitter.com/#{s[:url]}") }) # @??? をクリックされたらTwitterでユーザページを開く
    end

=end
  class RegexpEntity < BlankEntity
    class << self
      # 正規表現ルールを定義する
      # ==== Args
      # [regexp] Regexp Segmentを自動生成するための正規表現
      # [generator:]
      # Proc テキストを装飾する範囲を表わすHashを引数として受け取って、それを加工して返すProc。
      # 引数はHashひとつだけで、加工する必要がなければ、引数の内容をそのまま返す。
      # ===== Argument of generator
      # 引数 _generator:_ のProcに渡されるHashは、以下のキーを持つ
      # [:message] Retriever::Model このEntityが装飾する本文を持っているModel
      # [:range] Range 装飾する範囲(文字数)。
      # [:face] String _:message_ の本文中の _range_ の範囲にあるテキスト。これを書き換えると、実際の本文は変わらないが、表示上はこの内容に置き換わる。
      # [:url] String 歴史的経緯でこのような名前になっているがURLとは限らない。_:message_ の本文中の _range_ の範囲にあるテキスト。 _:face_ と違って、書き換わる前の内容が格納されている。
      # [:open] Retriever::Model|URI|nil デフォルトでは存在しない。内容を指定すると、UI上で本文の _:range_ の範囲がクリックされた時に、 :open イベントでそれを開くようになる。
      # [:callback] Proc|nil 利用は非推奨。できるだけ _:open_ を使う。デフォルトでは存在しない。内容を指定すると、UI上で本文の _:range_ の範囲がクリックされた時に、これが呼ばれるようになる。指定されている場合、 _:open_ より優先される。
      # ==== Return
      # Class その正規表現を自動でリンクにする新しいEntityクラス
      def filter(regexp, generator: ret_nth)
        Class.new(self) do
          @@autolink_condition = regexp.freeze
          @@generator = generator

          def initialize(*rest)
            super
            segments = Set.new(@generate_value)
            self.message.to_show.scan(@@autolink_condition) do
              match = Regexp.last_match
              pos = match.begin(0)
              body = match.to_s.freeze
              if not segments.any?{ |this| this[:range].include?(pos) }
                segments << @@generator.(
                  message: message,
                  from: :regexp,
                  slug: :urls,
                  range: Range.new(pos, pos + body.size, true),
                  face: body,
                  url: body).freeze
              end
            end
            @generate_value = segments.sort_by{ |r| r[:range].first }.freeze
          end
        end
      end
    end

  end
end