mournmail 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/README.md +46 -27
- data/lib/mournmail.rb +2 -0
- data/lib/mournmail/commands.rb +7 -4
- data/lib/mournmail/config.rb +2 -4
- data/lib/mournmail/draft_mode.rb +56 -13
- data/lib/mournmail/mail_encoded_word_patch.rb +73 -0
- data/lib/mournmail/message_mode.rb +3 -2
- data/lib/mournmail/message_rendering.rb +22 -15
- data/lib/mournmail/search_result_mode.rb +144 -0
- data/lib/mournmail/summary.rb +63 -8
- data/lib/mournmail/summary_mode.rb +406 -38
- data/lib/mournmail/utils.rb +279 -38
- data/lib/mournmail/version.rb +1 -1
- data/mournmail.gemspec +1 -1
- metadata +6 -5
    
        data/lib/mournmail/utils.rb
    CHANGED
    
    | @@ -1,11 +1,13 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            require "mail"
         | 
| 4 | 
            -
            require "mail-iso-2022-jp"
         | 
| 5 4 | 
             
            require "net/imap"
         | 
| 6 5 | 
             
            require "time"
         | 
| 6 | 
            +
            require "tempfile"
         | 
| 7 7 | 
             
            require "fileutils"
         | 
| 8 8 | 
             
            require "timeout"
         | 
| 9 | 
            +
            require "digest"
         | 
| 10 | 
            +
            require "groonga"
         | 
| 9 11 |  | 
| 10 12 | 
             
            module Mournmail
         | 
| 11 13 | 
             
              begin
         | 
| @@ -15,23 +17,37 @@ module Mournmail | |
| 15 17 | 
             
                HAVE_MAIL_GPG = false
         | 
| 16 18 | 
             
              end
         | 
| 17 19 |  | 
| 18 | 
            -
              def self.define_variable(name,  | 
| 20 | 
            +
              def self.define_variable(name, initial_value: nil, attr: nil)
         | 
| 19 21 | 
             
                var_name = "@" + name.to_s
         | 
| 20 22 | 
             
                if !instance_variable_defined?(var_name)
         | 
| 21 | 
            -
                  instance_variable_set(var_name,  | 
| 23 | 
            +
                  instance_variable_set(var_name, initial_value)
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
                case attr
         | 
| 26 | 
            +
                when :accessor
         | 
| 27 | 
            +
                  singleton_class.send(:attr_accessor, name)
         | 
| 28 | 
            +
                when :reader
         | 
| 29 | 
            +
                  singleton_class.send(:attr_reader, name)
         | 
| 30 | 
            +
                when :writer
         | 
| 31 | 
            +
                  singleton_class.send(:attr_writer, name)
         | 
| 22 32 | 
             
                end
         | 
| 23 | 
            -
                singleton_class.send(:attr_accessor, name)
         | 
| 24 33 | 
             
              end
         | 
| 25 34 |  | 
| 26 | 
            -
              define_variable :current_mailbox
         | 
| 27 | 
            -
              define_variable :current_summary
         | 
| 28 | 
            -
              define_variable :current_uid
         | 
| 29 | 
            -
              define_variable :current_mail
         | 
| 30 | 
            -
              define_variable :background_thread
         | 
| 35 | 
            +
              define_variable :current_mailbox, attr: :accessor
         | 
| 36 | 
            +
              define_variable :current_summary, attr: :accessor
         | 
| 37 | 
            +
              define_variable :current_uid, attr: :accessor
         | 
| 38 | 
            +
              define_variable :current_mail, attr: :accessor
         | 
| 39 | 
            +
              define_variable :background_thread, attr: :accessor
         | 
| 40 | 
            +
              define_variable :keep_alive_thread, attr: :accessor
         | 
| 41 | 
            +
              define_variable :imap
         | 
| 42 | 
            +
              define_variable :imap_mutex, initial_value: Mutex.new
         | 
| 43 | 
            +
              define_variable :mailboxes, initial_value: []
         | 
| 44 | 
            +
              define_variable :current_account
         | 
| 45 | 
            +
              define_variable :account_config
         | 
| 46 | 
            +
              define_variable :groonga_db
         | 
| 31 47 |  | 
| 32 | 
            -
              def self.background
         | 
| 48 | 
            +
              def self.background(skip_if_busy: false)
         | 
| 33 49 | 
             
                if background_thread&.alive?
         | 
| 34 | 
            -
                   | 
| 50 | 
            +
                  return if skip_if_busy
         | 
| 35 51 | 
             
                end
         | 
| 36 52 | 
             
                self.background_thread = Utils.background {
         | 
| 37 53 | 
             
                  begin
         | 
| @@ -42,6 +58,33 @@ module Mournmail | |
| 42 58 | 
             
                }
         | 
| 43 59 | 
             
              end
         | 
| 44 60 |  | 
| 61 | 
            +
              def self.start_keep_alive_thread
         | 
| 62 | 
            +
                if keep_alive_thread
         | 
| 63 | 
            +
                  raise EditorError, "Keep alive thread already running"
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
                self.keep_alive_thread = Thread.start {
         | 
| 66 | 
            +
                  loop do
         | 
| 67 | 
            +
                    sleep(CONFIG[:mournmail_keep_alive_interval])
         | 
| 68 | 
            +
                    background(skip_if_busy: true) do
         | 
| 69 | 
            +
                      begin
         | 
| 70 | 
            +
                        imap_connect do |imap|
         | 
| 71 | 
            +
                          imap.noop
         | 
| 72 | 
            +
                        end
         | 
| 73 | 
            +
                      rescue => e
         | 
| 74 | 
            +
                        message("Error in IMAP NOOP: #{e.class}: #{e.message}")
         | 
| 75 | 
            +
                      end
         | 
| 76 | 
            +
                    end
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
                }
         | 
| 79 | 
            +
              end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
              def self.stop_keep_alive_thread
         | 
| 82 | 
            +
                if keep_alive_thread
         | 
| 83 | 
            +
                  keep_alive_thread&.kill
         | 
| 84 | 
            +
                  self.keep_alive_thread = nil
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
              end
         | 
| 87 | 
            +
             | 
| 45 88 | 
             
              def self.message_window
         | 
| 46 89 | 
             
                if Window.list.size == 1
         | 
| 47 90 | 
             
                  split_window
         | 
| @@ -74,19 +117,46 @@ module Mournmail | |
| 74 117 | 
             
                escape_binary(s)
         | 
| 75 118 | 
             
              end
         | 
| 76 119 |  | 
| 77 | 
            -
               | 
| 78 | 
            -
             | 
| 120 | 
            +
              def self.current_account
         | 
| 121 | 
            +
                init_current_account
         | 
| 122 | 
            +
                @current_account
         | 
| 123 | 
            +
              end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
              def self.account_config
         | 
| 126 | 
            +
                init_current_account
         | 
| 127 | 
            +
                @account_config
         | 
| 128 | 
            +
              end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
              def self.init_current_account
         | 
| 131 | 
            +
                if @current_account.nil?
         | 
| 132 | 
            +
                  @current_account, @account_config = CONFIG[:mournmail_accounts].first
         | 
| 133 | 
            +
                end
         | 
| 134 | 
            +
              end
         | 
| 135 | 
            +
             | 
| 136 | 
            +
              def self.current_account=(name)
         | 
| 137 | 
            +
                unless CONFIG[:mournmail_accounts].key?(name)
         | 
| 138 | 
            +
                  raise ArgumentError, "No such account: #{name}"
         | 
| 139 | 
            +
                end
         | 
| 140 | 
            +
                @current_account = name
         | 
| 141 | 
            +
                @account_config = CONFIG[:mournmail_accounts][name]
         | 
| 142 | 
            +
              end
         | 
| 79 143 |  | 
| 80 144 | 
             
              def self.imap_connect
         | 
| 81 145 | 
             
                @imap_mutex.synchronize do
         | 
| 146 | 
            +
                  if keep_alive_thread.nil?
         | 
| 147 | 
            +
                    start_keep_alive_thread
         | 
| 148 | 
            +
                  end
         | 
| 82 149 | 
             
                  if @imap.nil? || @imap.disconnected?
         | 
| 150 | 
            +
                    conf = account_config
         | 
| 83 151 | 
             
                    Timeout.timeout(CONFIG[:mournmail_imap_connect_timeout]) do
         | 
| 84 | 
            -
                      @imap = Net::IMAP.new( | 
| 85 | 
            -
                                             | 
| 86 | 
            -
                      @imap.authenticate( | 
| 87 | 
            -
                                          | 
| 88 | 
            -
                                          | 
| 89 | 
            -
             | 
| 152 | 
            +
                      @imap = Net::IMAP.new(conf[:imap_host],
         | 
| 153 | 
            +
                                            conf[:imap_options])
         | 
| 154 | 
            +
                      @imap.authenticate(conf[:imap_options][:auth_type] || "PLAIN",
         | 
| 155 | 
            +
                                         conf[:imap_options][:user_name],
         | 
| 156 | 
            +
                                         conf[:imap_options][:password])
         | 
| 157 | 
            +
                      @mailboxes = @imap.list("", "*").map { |mbox|
         | 
| 158 | 
            +
                        Net::IMAP.decode_utf7(mbox.name)
         | 
| 159 | 
            +
                      }
         | 
| 90 160 | 
             
                      if Mournmail.current_mailbox
         | 
| 91 161 | 
             
                        @imap.select(Mournmail.current_mailbox)
         | 
| 92 162 | 
             
                      end
         | 
| @@ -101,6 +171,7 @@ module Mournmail | |
| 101 171 |  | 
| 102 172 | 
             
              def self.imap_disconnect
         | 
| 103 173 | 
             
                @imap_mutex.synchronize do
         | 
| 174 | 
            +
                  stop_keep_alive_thread
         | 
| 104 175 | 
             
                  if @imap
         | 
| 105 176 | 
             
                    @imap.disconnect rescue nil
         | 
| 106 177 | 
             
                    @imap = nil
         | 
| @@ -116,6 +187,17 @@ module Mournmail | |
| 116 187 | 
             
                  else
         | 
| 117 188 | 
             
                    summary = Mournmail::Summary.load_or_new(mailbox)
         | 
| 118 189 | 
             
                  end
         | 
| 190 | 
            +
                  uidvalidity = imap.responses["UIDVALIDITY"].last
         | 
| 191 | 
            +
                  if summary.uidvalidity.nil?
         | 
| 192 | 
            +
                    summary.uidvalidity = uidvalidity
         | 
| 193 | 
            +
                  elsif uidvalidity && uidvalidity != summary.uidvalidity
         | 
| 194 | 
            +
                    clear = next_tick {
         | 
| 195 | 
            +
                      yes_or_no?("UIDVALIDITY has been changed; Clear cache?")
         | 
| 196 | 
            +
                    }
         | 
| 197 | 
            +
                    if clear
         | 
| 198 | 
            +
                      summary = Mournmail::Summary.new(mailbox)
         | 
| 199 | 
            +
                    end
         | 
| 200 | 
            +
                  end
         | 
| 119 201 | 
             
                  first_uid = (summary.last_uid || 0) + 1
         | 
| 120 202 | 
             
                  data = imap.uid_fetch(first_uid..-1, ["UID", "ENVELOPE", "FLAGS"])
         | 
| 121 203 | 
             
                  summary.synchronize do
         | 
| @@ -134,33 +216,192 @@ module Mournmail | |
| 134 216 | 
             
              end
         | 
| 135 217 |  | 
| 136 218 | 
             
              def self.mailbox_cache_path(mailbox)
         | 
| 137 | 
            -
                 | 
| 138 | 
            -
             | 
| 139 | 
            -
             | 
| 219 | 
            +
                File.expand_path("cache/#{current_account}/mailboxes/#{mailbox}",
         | 
| 220 | 
            +
                                 CONFIG[:mournmail_directory])
         | 
| 221 | 
            +
              end
         | 
| 222 | 
            +
             | 
| 223 | 
            +
              def self.mail_cache_path(cache_id)
         | 
| 224 | 
            +
                dir = cache_id[0, 2]
         | 
| 225 | 
            +
                File.expand_path("cache/#{current_account}/mails/#{dir}/#{cache_id}",
         | 
| 226 | 
            +
                                 CONFIG[:mournmail_directory])
         | 
| 140 227 | 
             
              end
         | 
| 141 228 |  | 
| 142 | 
            -
              def self. | 
| 143 | 
            -
                path =  | 
| 229 | 
            +
              def self.read_mail_cache(cache_id)
         | 
| 230 | 
            +
                path = Mournmail.mail_cache_path(cache_id)
         | 
| 231 | 
            +
                File.read(path)
         | 
| 232 | 
            +
              end
         | 
| 233 | 
            +
             | 
| 234 | 
            +
              def self.write_mail_cache(s)
         | 
| 235 | 
            +
                header = s.slice(/.*\r\n\r\n/m)
         | 
| 236 | 
            +
                cache_id = Digest::SHA256.hexdigest(header)
         | 
| 237 | 
            +
                path = mail_cache_path(cache_id)
         | 
| 238 | 
            +
                dir = File.dirname(path)
         | 
| 239 | 
            +
                base = File.basename(path)
         | 
| 144 240 | 
             
                begin
         | 
| 145 | 
            -
                   | 
| 146 | 
            -
             | 
| 147 | 
            -
                     | 
| 241 | 
            +
                  f = Tempfile.create(["#{base}-", ".tmp"], dir)
         | 
| 242 | 
            +
                  begin
         | 
| 243 | 
            +
                    f.write(s)
         | 
| 244 | 
            +
                  ensure
         | 
| 245 | 
            +
                    f.close
         | 
| 148 246 | 
             
                  end
         | 
| 149 247 | 
             
                rescue Errno::ENOENT
         | 
| 150 | 
            -
                   | 
| 151 | 
            -
             | 
| 152 | 
            -
             | 
| 153 | 
            -
             | 
| 154 | 
            -
             | 
| 248 | 
            +
                  FileUtils.mkdir_p(File.dirname(path))
         | 
| 249 | 
            +
                  retry
         | 
| 250 | 
            +
                end
         | 
| 251 | 
            +
                File.rename(f.path, path)
         | 
| 252 | 
            +
                cache_id
         | 
| 253 | 
            +
              end
         | 
| 254 | 
            +
             | 
| 255 | 
            +
              def self.index_mail(cache_id, mail)
         | 
| 256 | 
            +
                messages_db = Groonga["Messages"]
         | 
| 257 | 
            +
                unless messages_db.has_key?(cache_id)
         | 
| 258 | 
            +
                  thread_id = find_thread_id(mail, messages_db)
         | 
| 259 | 
            +
                  list_id = (mail["List-Id"] || mail["X-ML-Name"])
         | 
| 260 | 
            +
                  messages_db.add(cache_id,
         | 
| 261 | 
            +
                                  message_id: header_text(mail.message_id),
         | 
| 262 | 
            +
                                  thread_id: header_text(thread_id),
         | 
| 263 | 
            +
                                  date: mail.date&.to_time,
         | 
| 264 | 
            +
                                  subject: header_text(mail.subject),
         | 
| 265 | 
            +
                                  from: header_text(mail["From"]),
         | 
| 266 | 
            +
                                  to: header_text(mail["To"]),
         | 
| 267 | 
            +
                                  cc: header_text(mail["Cc"]),
         | 
| 268 | 
            +
                                  list_id: header_text(list_id),
         | 
| 269 | 
            +
                                  body: body_text(mail))
         | 
| 270 | 
            +
                end
         | 
| 271 | 
            +
              end
         | 
| 272 | 
            +
             | 
| 273 | 
            +
              class << self
         | 
| 274 | 
            +
                private
         | 
| 275 | 
            +
             | 
| 276 | 
            +
                def find_thread_id(mail, messages_db)
         | 
| 277 | 
            +
                  references = Array(mail.references) | Array(mail.in_reply_to)
         | 
| 278 | 
            +
                  if references.empty?
         | 
| 279 | 
            +
                    mail.message_id
         | 
| 280 | 
            +
                  else
         | 
| 281 | 
            +
                    parent = messages_db.select { |m|
         | 
| 282 | 
            +
                      references.inject(nil) { |cond, ref|
         | 
| 283 | 
            +
                        if cond.nil?
         | 
| 284 | 
            +
                          m.message_id == ref
         | 
| 285 | 
            +
                        else
         | 
| 286 | 
            +
                          cond | (m.message_id == ref)
         | 
| 287 | 
            +
                        end
         | 
| 288 | 
            +
                      }
         | 
| 289 | 
            +
                    }.first
         | 
| 290 | 
            +
                    if parent
         | 
| 291 | 
            +
                      parent.thread_id
         | 
| 292 | 
            +
                    else
         | 
| 293 | 
            +
                      mail.message_id
         | 
| 155 294 | 
             
                    end
         | 
| 156 | 
            -
             | 
| 157 | 
            -
             | 
| 158 | 
            -
             | 
| 159 | 
            -
             | 
| 160 | 
            -
             | 
| 295 | 
            +
                  end
         | 
| 296 | 
            +
                end
         | 
| 297 | 
            +
             | 
| 298 | 
            +
                def header_text(s)
         | 
| 299 | 
            +
                  force_utf8(s.to_s)
         | 
| 300 | 
            +
                end
         | 
| 301 | 
            +
                
         | 
| 302 | 
            +
                def body_text(mail)
         | 
| 303 | 
            +
                  if mail.multipart?
         | 
| 304 | 
            +
                    mail.parts.map { |part|
         | 
| 305 | 
            +
                      part_text(part)
         | 
| 306 | 
            +
                    }.join("\n")
         | 
| 307 | 
            +
                  else
         | 
| 308 | 
            +
                    s = mail.body.decoded
         | 
| 309 | 
            +
                    to_utf8(s, mail.charset).gsub(/\r\n/, "\n")
         | 
| 310 | 
            +
                  end
         | 
| 311 | 
            +
                rescue
         | 
| 312 | 
            +
                  ""
         | 
| 313 | 
            +
                end
         | 
| 314 | 
            +
                
         | 
| 315 | 
            +
                def part_text(part)
         | 
| 316 | 
            +
                  if part.multipart?
         | 
| 317 | 
            +
                    part.parts.map { |part|
         | 
| 318 | 
            +
                      part_text(part)
         | 
| 319 | 
            +
                    }.join("\n")
         | 
| 320 | 
            +
                  elsif part.main_type == "message" && part.sub_type == "rfc822"
         | 
| 321 | 
            +
                    mail = Mail.new(part.body.raw_source)
         | 
| 322 | 
            +
                    body_text(mail)
         | 
| 323 | 
            +
                  elsif part.attachment?
         | 
| 324 | 
            +
                    force_utf8(part.filename.to_s)
         | 
| 325 | 
            +
                  else
         | 
| 326 | 
            +
                    if part.main_type == "text" && part.sub_type == "plain"
         | 
| 327 | 
            +
                      force_utf8(part.decoded).sub(/(?<!\n)\z/, "\n").gsub(/\r\n/, "\n")
         | 
| 328 | 
            +
                    else
         | 
| 329 | 
            +
                      ""
         | 
| 161 330 | 
             
                    end
         | 
| 162 | 
            -
                    [s, true]
         | 
| 163 331 | 
             
                  end
         | 
| 332 | 
            +
                rescue
         | 
| 333 | 
            +
                  ""
         | 
| 334 | 
            +
                end
         | 
| 335 | 
            +
              end
         | 
| 336 | 
            +
             | 
| 337 | 
            +
              def self.read_mailbox_name(prompt, **opts)
         | 
| 338 | 
            +
                f = ->(s) {
         | 
| 339 | 
            +
                  complete_for_minibuffer(s, @mailboxes)
         | 
| 340 | 
            +
                }
         | 
| 341 | 
            +
                mailbox = read_from_minibuffer(prompt, completion_proc: f, **opts)
         | 
| 342 | 
            +
                Net::IMAP.encode_utf7(mailbox)
         | 
| 343 | 
            +
              end
         | 
| 344 | 
            +
             | 
| 345 | 
            +
              def self.force_utf8(s)
         | 
| 346 | 
            +
                s.force_encoding(Encoding::UTF_8).scrub("?")
         | 
| 347 | 
            +
              end
         | 
| 348 | 
            +
             | 
| 349 | 
            +
              def self.to_utf8(s, charset)
         | 
| 350 | 
            +
                if /\Autf-8\z/i =~ charset
         | 
| 351 | 
            +
                  force_utf8(s)
         | 
| 352 | 
            +
                else
         | 
| 353 | 
            +
                  begin
         | 
| 354 | 
            +
                    s.encode(Encoding::UTF_8, charset, replace: "?")
         | 
| 355 | 
            +
                  rescue Encoding::ConverterNotFoundError
         | 
| 356 | 
            +
                    force_utf8(s)
         | 
| 357 | 
            +
                  end
         | 
| 358 | 
            +
                end.gsub(/\r\n/, "\n")
         | 
| 359 | 
            +
              end
         | 
| 360 | 
            +
             | 
| 361 | 
            +
              def self.open_groonga_db
         | 
| 362 | 
            +
                db_path = File.expand_path("groonga/#{current_account}/messages.db",
         | 
| 363 | 
            +
                                           CONFIG[:mournmail_directory])
         | 
| 364 | 
            +
                if File.exist?(db_path)
         | 
| 365 | 
            +
                  @groonga_db = Groonga::Database.open(db_path)
         | 
| 366 | 
            +
                else
         | 
| 367 | 
            +
                  @groonga_db = create_groonga_db(db_path)
         | 
| 368 | 
            +
                end
         | 
| 369 | 
            +
              end
         | 
| 370 | 
            +
             | 
| 371 | 
            +
              def self.create_groonga_db(db_path)
         | 
| 372 | 
            +
                FileUtils.mkdir_p(File.dirname(db_path), mode: 0700)
         | 
| 373 | 
            +
                db = Groonga::Database.create(path: db_path)
         | 
| 374 | 
            +
             | 
| 375 | 
            +
                Groonga::Schema.create_table("Messages", :type => :hash) do |table|
         | 
| 376 | 
            +
                  table.short_text("message_id")
         | 
| 377 | 
            +
                  table.short_text("thread_id")
         | 
| 378 | 
            +
                  table.time("date")
         | 
| 379 | 
            +
                  table.short_text("subject")
         | 
| 380 | 
            +
                  table.short_text("from")
         | 
| 381 | 
            +
                  table.short_text("to")
         | 
| 382 | 
            +
                  table.short_text("cc")
         | 
| 383 | 
            +
                  table.short_text("list_id")
         | 
| 384 | 
            +
                  table.text("body")
         | 
| 385 | 
            +
                end
         | 
| 386 | 
            +
                
         | 
| 387 | 
            +
                Groonga::Schema.create_table("Terms",
         | 
| 388 | 
            +
                                             type: :patricia_trie,
         | 
| 389 | 
            +
                                             normalizer: :NormalizerAuto,
         | 
| 390 | 
            +
                                             default_tokenizer: "TokenBigram") do |table|
         | 
| 391 | 
            +
                  table.index("Messages.subject")
         | 
| 392 | 
            +
                  table.index("Messages.from")
         | 
| 393 | 
            +
                  table.index("Messages.to")
         | 
| 394 | 
            +
                  table.index("Messages.cc")
         | 
| 395 | 
            +
                  table.index("Messages.list_id")
         | 
| 396 | 
            +
                  table.index("Messages.body")
         | 
| 397 | 
            +
                end
         | 
| 398 | 
            +
             | 
| 399 | 
            +
                db
         | 
| 400 | 
            +
              end
         | 
| 401 | 
            +
             | 
| 402 | 
            +
              def self.close_groonga_db
         | 
| 403 | 
            +
                if @groonga_db
         | 
| 404 | 
            +
                  @groonga_db.close
         | 
| 164 405 | 
             
                end
         | 
| 165 406 | 
             
              end
         | 
| 166 407 | 
             
            end
         | 
    
        data/lib/mournmail/version.rb
    CHANGED
    
    
    
        data/mournmail.gemspec
    CHANGED
    
    | @@ -23,7 +23,7 @@ Gem::Specification.new do |spec| | |
| 23 23 |  | 
| 24 24 | 
             
              spec.add_runtime_dependency "textbringer"
         | 
| 25 25 | 
             
              spec.add_runtime_dependency "mail"
         | 
| 26 | 
            -
              spec.add_runtime_dependency " | 
| 26 | 
            +
              spec.add_runtime_dependency "rroonga"
         | 
| 27 27 |  | 
| 28 28 | 
             
              spec.add_development_dependency "bundler", "~> 1.14"
         | 
| 29 29 | 
             
              spec.add_development_dependency "rake", "~> 10.0"
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: mournmail
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.2.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Shugo Maeda
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2018-05-03 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: textbringer
         | 
| @@ -39,7 +39,7 @@ dependencies: | |
| 39 39 | 
             
                  - !ruby/object:Gem::Version
         | 
| 40 40 | 
             
                    version: '0'
         | 
| 41 41 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 42 | 
            -
              name:  | 
| 42 | 
            +
              name: rroonga
         | 
| 43 43 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 44 44 | 
             
                requirements:
         | 
| 45 45 | 
             
                - - ">="
         | 
| @@ -99,8 +99,10 @@ files: | |
| 99 99 | 
             
            - lib/mournmail/config.rb
         | 
| 100 100 | 
             
            - lib/mournmail/draft_mode.rb
         | 
| 101 101 | 
             
            - lib/mournmail/faces.rb
         | 
| 102 | 
            +
            - lib/mournmail/mail_encoded_word_patch.rb
         | 
| 102 103 | 
             
            - lib/mournmail/message_mode.rb
         | 
| 103 104 | 
             
            - lib/mournmail/message_rendering.rb
         | 
| 105 | 
            +
            - lib/mournmail/search_result_mode.rb
         | 
| 104 106 | 
             
            - lib/mournmail/summary.rb
         | 
| 105 107 | 
             
            - lib/mournmail/summary_mode.rb
         | 
| 106 108 | 
             
            - lib/mournmail/utils.rb
         | 
| @@ -127,9 +129,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 127 129 | 
             
                  version: '0'
         | 
| 128 130 | 
             
            requirements: []
         | 
| 129 131 | 
             
            rubyforge_project: 
         | 
| 130 | 
            -
            rubygems_version: 2.6 | 
| 132 | 
            +
            rubygems_version: 2.7.6
         | 
| 131 133 | 
             
            signing_key: 
         | 
| 132 134 | 
             
            specification_version: 4
         | 
| 133 135 | 
             
            summary: A message user agent for Textbringer.
         | 
| 134 136 | 
             
            test_files: []
         | 
| 135 | 
            -
            has_rdoc: 
         |