chronicle-email 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e37ee5bdabd254e03a06962503f5875ab75d2aa0b271aff0b946c73f673d5cc3
4
- data.tar.gz: c351c35f1b5545f0d0f9e6b4375076b159dab88eb454cf3e946fdab84ddc03a4
3
+ metadata.gz: e0d66a6d2109f4f46f23fdf0c157085aed6f757f962339a422e4e38ca86ed129
4
+ data.tar.gz: e4136e05cf2827f64a07657e9442f9a8cdcac1ab88ed589c52ea76f62fd0d315
5
5
  SHA512:
6
- metadata.gz: fedb95cf29c21d0dac17d6cbeca81da0a5318ab41ea8a5eaf355e8996606b03398ba70c4233e4591a78ceaef5ce5242b59c5e227e79a6310ba4474c245476e57
7
- data.tar.gz: 7a06834ea16425d456da704b3272c96d6e0f427044e46e60a477dbce1cab7f9621056f4f18f7b90a964f83e3e85281a981fdf155155ebf4cb8b06871cd9884ab
6
+ metadata.gz: 5d58d42a6db25951b46a08ad44302dab836fc3063fae2b9074e5156b4db59ab147b8401b60860665a0a27394ddca98a48c58a6abdf87ce474439e4d7b179604f
7
+ data.tar.gz: b11bea8171510544c35742cb7ab72c162b7774d0bd5603a7453f065d270a230fa8b2a17d77d5bfc1205a60710f1b7d076ec4f6521630754006a5efe4e6b18fcc
data/.rubocop.yml ADDED
@@ -0,0 +1,2 @@
1
+ inherit_gem:
2
+ chronicle-core: .rubocop-plugin.yml
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
- source "https://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
 
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  # Specify your gem's dependencies in chronicle-email.gemspec
6
6
  gemspec
data/README.md CHANGED
@@ -21,11 +21,11 @@ $ chronicle-etl secrets:set imap username foo@gmail.com
21
21
  $ chronicle-etl secrets:set imap password APPPASSWORD
22
22
 
23
23
  # Then, retrieve your email from the last five days
24
- $ chronicle-etl --extractor email:imap --transformer email --since 5d --loader json
24
+ $ chronicle-etl --extractor email:imap --schema chronicle --since 5d
25
25
 
26
26
  # If you don't want to save your credentials as a secret, you can just pass
27
27
  # them to the extractor directly
28
- $ chronicle-etl --extractor email:imap --transformer email --since 5d --loader json \
28
+ $ chronicle-etl --extractor email:imap --schema chronicle --since 5d --loader json \
29
29
  --extractor-opts username:foo@gmail.com --password:APPPASSWORD
30
30
  ```
31
31
 
@@ -34,7 +34,7 @@ The MBOX format is used to archive an email mailbox. [Google Takeout](https://ta
34
34
 
35
35
  ```sh
36
36
  # Retrieve the subject lines of all emails in test.mbox
37
- $ chronicle-etl --extractor email:mbox --input inbox.mbox --transformer email --fields subject
37
+ $ chronicle-etl --extractor email:mbox --input ~/Downloads/inbox.mbox --fields subject
38
38
  ```
39
39
 
40
40
  ## Available Connectors
data/Rakefile CHANGED
@@ -1,6 +1,2 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task :default => :spec
1
+ require 'bundler/gem_tasks'
2
+ task default: %i[]
data/bin/console CHANGED
@@ -1,14 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "chronicle/email"
3
+ require 'bundler/setup'
4
+ require 'chronicle/email'
5
5
 
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
8
-
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require "irb"
14
- IRB.start(__FILE__)
6
+ require 'pry'
7
+ Pry.start
@@ -1,47 +1,54 @@
1
-
2
- lib = File.expand_path("../lib", __FILE__)
1
+ lib = File.expand_path('lib', __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "chronicle/email/version"
3
+ require 'chronicle/email/version'
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = "chronicle-email"
6
+ spec.name = 'chronicle-email'
8
7
  spec.version = Chronicle::Email::VERSION
9
- spec.authors = ["Andrew Louis"]
10
- spec.email = ["andrew@hyfen.net"]
8
+ spec.authors = ['Andrew Louis']
9
+ spec.email = ['andrew@hyfen.net']
11
10
 
12
- spec.summary = "Email importer for Chronicle"
13
- spec.description = "Various email importers for chronicle-etl"
14
- spec.homepage = "https://github.com/chronicle-app/chronicle-email"
15
- spec.license = "MIT"
11
+ spec.summary = 'Email importer for Chronicle'
12
+ spec.description = 'Various email importers for chronicle-etl'
13
+ spec.homepage = 'https://github.com/chronicle-app/chronicle-email'
14
+ spec.license = 'MIT'
16
15
 
17
16
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
17
  # to allow pushing to a single host or delete this section to allow pushing to any host.
19
18
  if spec.respond_to?(:metadata)
20
19
  # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
21
20
 
22
- spec.metadata["homepage_uri"] = spec.homepage
23
- spec.metadata["source_code_uri"] = "https://github.com/chronicle-app/chronicle-email"
24
- spec.metadata["changelog_uri"] = "https://github.com/chronicle-app/chronicle-email"
21
+ spec.metadata['homepage_uri'] = spec.homepage
22
+ spec.metadata['source_code_uri'] = 'https://github.com/chronicle-app/chronicle-email'
23
+ spec.metadata['changelog_uri'] = 'https://github.com/chronicle-app/chronicle-email'
25
24
  else
26
- raise "RubyGems 2.0 or newer is required to protect against " \
27
- "public gem pushes."
25
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
26
+ 'public gem pushes.'
28
27
  end
29
28
 
30
29
  # Specify which files should be added to the gem when it is released.
31
30
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
32
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
31
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
33
32
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
34
33
  end
35
- spec.bindir = "exe"
34
+ spec.bindir = 'exe'
36
35
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
37
- spec.require_paths = ["lib"]
36
+ spec.require_paths = ['lib']
37
+ spec.metadata['rubygems_mfa_required'] = 'true'
38
+ spec.required_ruby_version = '>= 3.1'
38
39
 
39
- spec.add_dependency "chronicle-etl", "~> 0.5"
40
- spec.add_dependency "mail", "~> 2.7"
41
40
  spec.add_dependency 'email_reply_parser', '~> 0.5'
41
+ spec.add_dependency 'mail', '~> 2.7'
42
42
  spec.add_dependency 'reverse_markdown', '~> 2.0'
43
-
44
- spec.add_development_dependency "bundler", "~> 2.1"
45
- spec.add_development_dependency "rake", "~> 13.0"
46
- spec.add_development_dependency "rspec", "~> 3.9"
43
+ # Needed for Ruby 3.1 compatibility (https://github.com/mikel/mail/pull/1439#issuecomment-1002801221)
44
+ # TODO: check back for new version of `mail`
45
+ spec.add_dependency 'chronicle-core', '~> 0.3'
46
+ spec.add_dependency 'chronicle-etl', '~> 0.6'
47
+ spec.add_dependency 'net-imap'
48
+ spec.add_dependency 'net-smtp'
49
+
50
+ spec.add_development_dependency 'bundler', '~> 2.3'
51
+ spec.add_development_dependency 'pry-byebug', '~> 3.10'
52
+ spec.add_development_dependency 'rake', '~> 13.0'
53
+ spec.add_development_dependency 'rubocop', '~> 1.63'
47
54
  end
@@ -1,5 +1,5 @@
1
1
  require 'chronicle/etl'
2
- require 'mail'
2
+ require 'chronicle/models'
3
3
  require 'timeout'
4
4
  require 'email_reply_parser'
5
5
  require 'reverse_markdown'
@@ -8,98 +8,97 @@ module Chronicle
8
8
  module Email
9
9
  class EmailTransformer < Chronicle::ETL::Transformer
10
10
  register_connector do |r|
11
+ r.source = :email
12
+ r.type = :message
11
13
  r.description = 'an email object'
12
- r.provider = 'email'
13
- r.identifier = 'email'
14
+ r.from_schema = :extraction
15
+ r.to_schema = :chronicle
14
16
  end
15
17
 
16
18
  setting :body_as_markdown, default: false
17
19
  setting :remove_signature, default: true
18
20
 
19
- def transform
20
- build_messaged
21
+ def transform(record)
22
+ build_messaged(record.data[:raw])
21
23
  end
22
24
 
23
- def id
24
- message.message_id || raise(Chronicle::ETL::UntransformableRecordError, "Email doesn't have an ID")
25
- end
25
+ private
26
26
 
27
- def timestamp
28
- message.date&.to_time || raise(Chronicle::ETL::UntransformableRecordError, "Email doesn't have a timestamp")
29
- end
27
+ def build_messaged(email)
28
+ timestamp = email.date&.to_time || raise(Chronicle::ETL::UntransformableRecordError,
29
+ "Email doesn't have a timestamp")
30
30
 
31
- private
31
+ email.message_id || raise(Chronicle::ETL::UntransformableRecordError, "Email doesn't have an ID")
32
32
 
33
- def message
34
- @message ||= Mail.new(@extraction.data[:email])
33
+ Chronicle::Models::CommunicateAction.new do |r|
34
+ r.end_time = timestamp
35
+ r.agent = build_agent(email[:from])
36
+ r.source = 'email'
37
+ r.source_id = email.message_id
38
+ r.object = build_message(email)
39
+ end
35
40
  end
36
41
 
37
- def build_messaged
38
- record = ::Chronicle::ETL::Models::Activity.new
39
- record.verb = 'messaged'
40
- record.provider = 'email'
41
- record.provider_id = id
42
- record.end_at = timestamp
42
+ def build_agent(from)
43
+ raise(Chronicle::ETL::UntransformableRecordError, "Can't determine email sender") unless from&.addrs&.any?
43
44
 
44
- record.dedupe_on << [:verb, :provider, :provider_id]
45
-
46
- record.actor = build_actor
47
- record.involved = build_message
48
- record
45
+ build_person(from.addrs.first)
49
46
  end
50
47
 
51
- def build_actor
52
- # sometimes From: fields are malformed and we can't build an
53
- # actor out of it.
54
- raise(Chronicle::ETL::UntransformableRecordError, "Can't determine email sender") unless message[:from]&.addrs&.any?
48
+ def build_message(email)
49
+ Chronicle::Models::Message.new do |r|
50
+ r.name = clean_subject(email.subject)
51
+ r.text = clean_body(email)
52
+ r.source = 'email'
53
+ r.source_id = email.message_id
55
54
 
56
- record = ::Chronicle::ETL::Models::Entity.new
57
- record.represents = 'identity'
58
- record.provider = 'email'
59
- record.slug = message[:from].addrs.first.address
60
- record.title = message[:from].addrs.first.display_name
55
+ r.recipient = email[:to]&.addrs&.map { |addr| build_person(addr) }
61
56
 
62
- record.dedupe_on << [:represents, :provider, :slug]
57
+ # TODO: handle email references
58
+ # TODO: handle email account owner
59
+ # TODO: handle attachments
63
60
 
64
- record
61
+ r.dedupe_on << %i[source source_id type]
62
+ end
65
63
  end
66
64
 
67
- def build_message
68
- record = ::Chronicle::ETL::Models::Entity.new
69
- record.represents = 'message'
70
- record.title = clean_subject(message.subject)
71
- record.body = clean_body(message)
72
- record.provider = 'email'
73
- record.provider_id = id
74
-
75
- # TODO: handle consumer
76
- # TODO: handle email references
77
- # TODO: handle email account owner
78
- # TODO: handle attachments
79
-
80
- record
65
+ def build_person(addr)
66
+ Chronicle::Models::Person.new do |r|
67
+ r.source = 'email'
68
+ r.slug = addr.address
69
+ r.name = addr.display_name
70
+ r.dedupe_on << %i[represents provider slug]
71
+ end
81
72
  end
82
73
 
83
74
  def clean_subject(subject)
84
- subject&.encode("UTF-8", invalid: :replace, undef: :replace)
75
+ subject&.encode('UTF-8', invalid: :replace, undef: :replace)
85
76
  end
86
77
 
87
- def clean_body message
88
- # FIXME: this all needs to be refactored
78
+ def clean_body(message)
79
+ # FIXME: this all needs to be refactored
89
80
  if message.multipart?
90
- body = message.text_part&.decoded rescue Mail::UnknownEncodingType
81
+ body = begin
82
+ message.text_part&.decoded
83
+ rescue StandardError
84
+ Mail::UnknownEncodingType
85
+ end
91
86
  else
92
- body = message.body&.decoded rescue Mail::UnknownEncodingType
87
+ body = begin
88
+ message.body&.decoded
89
+ rescue StandardError
90
+ Mail::UnknownEncodingType
91
+ end
93
92
  body = body_to_markdown if @config.body_as_markdown
94
93
  end
95
94
 
96
95
  return if body == Mail::UnknownEncodingType
97
- return unless body && body != ""
96
+ return unless body && body != ''
98
97
 
99
98
  body = body_without_signature(body) if @config.remove_signature
100
99
 
101
100
  # Force UTF-8 encoding
102
- body.encode("UTF-8", invalid: :replace, undef: :replace)
101
+ body.encode('UTF-8', invalid: :replace, undef: :replace)
103
102
  end
104
103
 
105
104
  def body_to_markdown(body)
@@ -111,11 +110,11 @@ module Chronicle
111
110
  def body_without_signature(body)
112
111
  # FIXME: regex in EmailReplyParse gem seems to get into infinite loops
113
112
  # with certain long bodies that have binary data
114
- parsed_body = Timeout::timeout(5) do
113
+ Timeout.timeout(5) do
115
114
  EmailReplyParser.parse_reply(body)
116
115
  end
117
- rescue Timeout::Error, StandardError => e
118
- return body
116
+ rescue Timeout::Error, StandardError
117
+ body
119
118
  end
120
119
  end
121
120
  end
@@ -1,12 +1,14 @@
1
1
  require 'net/imap'
2
+ require 'mail'
2
3
 
3
4
  module Chronicle
4
5
  module Email
5
6
  class IMAPExtractor < Chronicle::ETL::Extractor
6
7
  register_connector do |r|
7
- r.provider = 'email'
8
- r.description = 'imap server'
9
- r.identifier = 'imap'
8
+ r.source = :email
9
+ r.type = :message
10
+ r.strategy = :imap
11
+ r.description = 'IMAP server'
10
12
  end
11
13
 
12
14
  setting :host, required: true, default: 'imap.gmail.com'
@@ -28,7 +30,15 @@ module Chronicle
28
30
  def extract
29
31
  @message_ids.each do |message_id|
30
32
  message = fetch_message(message_id)
31
- yield Chronicle::ETL::Extraction.new(data: { email: message.attr["BODY[]"]} )
33
+ email = Mail.new(message.attr['BODY[]'])
34
+ data = {
35
+ raw: email,
36
+ time: email.date&.to_time,
37
+ subject: email.subject,
38
+ from: email&.from&.join(', '),
39
+ to: email&.to&.join(', ')
40
+ }
41
+ yield build_extraction(data:)
32
42
  end
33
43
  end
34
44
 
@@ -39,31 +49,32 @@ module Chronicle
39
49
  connection.login(@config.username, @config.password)
40
50
  connection.select(@config.mailbox)
41
51
  connection
42
- rescue Net::IMAP::NoResponseError => e
43
- raise(Chronicle::ETL::ExtractionError, "Error connecting to IMAP server. Please check username and password")
52
+ rescue Net::IMAP::NoResponseError
53
+ raise(Chronicle::ETL::ExtractionError, 'Error connecting to IMAP server. Please check username and password')
44
54
  end
45
55
 
46
56
  def fetch_message_ids
47
57
  keys = gmail_mode? ? search_keys_gmail : search_keys_default
48
- @connection.search(keys)
49
- rescue Net::IMAP::BadResponseError => e
50
- raise(Chronicle::ETL::ExtractionError, "Error searching IMAP server for messages")
58
+ message_ids = @connection.search(keys)
59
+ message_ids = message_ids.first(@config.limit) if @config.limit
60
+ message_ids
61
+ rescue Net::IMAP::BadResponseError
62
+ raise(Chronicle::ETL::ExtractionError, 'Error searching IMAP server for messages')
51
63
  end
52
64
 
53
65
  def fetch_message(message_id)
54
- response = @connection.fetch(3100020, "BODY.PEEK[]")
55
- raise(Chronicle::ETL::ExtractionError, "Error loading message") unless response
66
+ response = @connection.fetch(message_id, 'BODY.PEEK[]')
67
+ raise(Chronicle::ETL::ExtractionError, 'Error loading message') unless response
56
68
 
57
- return response[0]
69
+ response[0]
58
70
  end
59
71
 
60
72
  def search_keys_gmail
61
73
  # Gmail offers an extension to IMAP that lets us use gmail queries
62
- q = ""
63
74
 
64
75
  # First, we ignore drafts beacuse they break a lot of assumptions we
65
76
  # make when when processing emails (lack of timestamps, ids, etc)
66
- q = "-label:draft"
77
+ q = '-label:draft'
67
78
 
68
79
  # We use UNIX timestamps in gmail filters which let us do more precise
69
80
  # since/until compared with date-based imap filters
@@ -71,14 +82,14 @@ module Chronicle
71
82
  q += " before:#{@config.until.to_i}" if @config.until
72
83
  q += " #{@config.search_query}" if @config.search_query
73
84
 
74
- ["X-GM-RAW", q]
85
+ ['X-GM-RAW', q]
75
86
  end
76
87
 
77
88
  def search_keys_default
78
89
  keys = []
79
90
  # TODO: test out non-gmail IMAP searching (for @config.search_query)
80
91
  keys += ['SINCE', Net::IMAP.format_date(@config.since)] if @config.since
81
- keys += ['BEFORE', Net::IMAP.format_date(@config.until)] if @config.until
92
+ keys + ['BEFORE', Net::IMAP.format_date(@config.until)] if @config.until
82
93
  end
83
94
 
84
95
  def gmail_mode?
@@ -86,4 +97,4 @@ module Chronicle
86
97
  end
87
98
  end
88
99
  end
89
- end
100
+ end
@@ -6,9 +6,10 @@ module Chronicle
6
6
  module Email
7
7
  class MboxExtractor < Chronicle::ETL::Extractor
8
8
  register_connector do |r|
9
- r.provider = 'email'
9
+ r.source = :email
10
+ r.type = :message
11
+ r.strategy = :mbox
10
12
  r.description = 'an .mbox file'
11
- r.identifier = 'mbox'
12
13
  end
13
14
 
14
15
  setting :input, required: true
@@ -40,14 +41,20 @@ module Chronicle
40
41
  #
41
42
  # TODO: make this thread-safe (one tmp file per email?)
42
43
  file.each do |line|
43
- if line =~ NEW_EMAIL_REGEX
44
- if File.size(tmp) > 0
45
- tmp.rewind
46
- email = tmp.read
47
- yield Chronicle::ETL::Extraction.new(data: { email: email} )
48
- tmp.truncate(0)
49
- tmp.rewind
50
- end
44
+ if line =~ (NEW_EMAIL_REGEX) && File.size(tmp).positive?
45
+ tmp.rewind
46
+
47
+ email = Mail.new(tmp.read)
48
+ data = {
49
+ raw: email,
50
+ time: email.date&.to_time,
51
+ subject: email.subject,
52
+ from: email&.from&.join(', '),
53
+ to: email&.to&.join(', ')
54
+ }
55
+ yield build_extraction(data:)
56
+ tmp.truncate(0)
57
+ tmp.rewind
51
58
  end
52
59
  tmp.write(line)
53
60
  end
@@ -1,5 +1,5 @@
1
1
  module Chronicle
2
2
  module Email
3
- VERSION = "0.2.2"
3
+ VERSION = '0.3.0'.freeze
4
4
  end
5
5
  end
@@ -1,7 +1,7 @@
1
- require "chronicle/email/version"
2
- require "chronicle/email/email_transformer"
3
- require "chronicle/email/mbox_extractor"
4
- require "chronicle/email/imap_extractor"
1
+ require 'chronicle/email/version'
2
+ require 'chronicle/email/email_transformer'
3
+ require 'chronicle/email/mbox_extractor'
4
+ require 'chronicle/email/imap_extractor'
5
5
 
6
6
  module Chronicle
7
7
  module Email
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chronicle-email
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Louis
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-31 00:00:00.000000000 Z
11
+ date: 2024-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: chronicle-etl
14
+ name: email_reply_parser
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
@@ -39,47 +39,103 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '2.7'
41
41
  - !ruby/object:Gem::Dependency
42
- name: email_reply_parser
42
+ name: reverse_markdown
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0.5'
47
+ version: '2.0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0.5'
54
+ version: '2.0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: reverse_markdown
56
+ name: chronicle-core
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '2.0'
61
+ version: '0.3'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '2.0'
68
+ version: '0.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: chronicle-etl
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.6'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.6'
83
+ - !ruby/object:Gem::Dependency
84
+ name: net-imap
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: net-smtp
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
69
111
  - !ruby/object:Gem::Dependency
70
112
  name: bundler
71
113
  requirement: !ruby/object:Gem::Requirement
72
114
  requirements:
73
115
  - - "~>"
74
116
  - !ruby/object:Gem::Version
75
- version: '2.1'
117
+ version: '2.3'
76
118
  type: :development
77
119
  prerelease: false
78
120
  version_requirements: !ruby/object:Gem::Requirement
79
121
  requirements:
80
122
  - - "~>"
81
123
  - !ruby/object:Gem::Version
82
- version: '2.1'
124
+ version: '2.3'
125
+ - !ruby/object:Gem::Dependency
126
+ name: pry-byebug
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.10'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.10'
83
139
  - !ruby/object:Gem::Dependency
84
140
  name: rake
85
141
  requirement: !ruby/object:Gem::Requirement
@@ -95,19 +151,19 @@ dependencies:
95
151
  - !ruby/object:Gem::Version
96
152
  version: '13.0'
97
153
  - !ruby/object:Gem::Dependency
98
- name: rspec
154
+ name: rubocop
99
155
  requirement: !ruby/object:Gem::Requirement
100
156
  requirements:
101
157
  - - "~>"
102
158
  - !ruby/object:Gem::Version
103
- version: '3.9'
159
+ version: '1.63'
104
160
  type: :development
105
161
  prerelease: false
106
162
  version_requirements: !ruby/object:Gem::Requirement
107
163
  requirements:
108
164
  - - "~>"
109
165
  - !ruby/object:Gem::Version
110
- version: '3.9'
166
+ version: '1.63'
111
167
  description: Various email importers for chronicle-etl
112
168
  email:
113
169
  - andrew@hyfen.net
@@ -117,6 +173,7 @@ extra_rdoc_files: []
117
173
  files:
118
174
  - ".gitignore"
119
175
  - ".rspec"
176
+ - ".rubocop.yml"
120
177
  - ".travis.yml"
121
178
  - CODE_OF_CONDUCT.md
122
179
  - Gemfile
@@ -138,6 +195,7 @@ metadata:
138
195
  homepage_uri: https://github.com/chronicle-app/chronicle-email
139
196
  source_code_uri: https://github.com/chronicle-app/chronicle-email
140
197
  changelog_uri: https://github.com/chronicle-app/chronicle-email
198
+ rubygems_mfa_required: 'true'
141
199
  post_install_message:
142
200
  rdoc_options: []
143
201
  require_paths:
@@ -146,14 +204,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
146
204
  requirements:
147
205
  - - ">="
148
206
  - !ruby/object:Gem::Version
149
- version: '0'
207
+ version: '3.1'
150
208
  required_rubygems_version: !ruby/object:Gem::Requirement
151
209
  requirements:
152
210
  - - ">="
153
211
  - !ruby/object:Gem::Version
154
212
  version: '0'
155
213
  requirements: []
156
- rubygems_version: 3.3.3
214
+ rubygems_version: 3.4.10
157
215
  signing_key:
158
216
  specification_version: 4
159
217
  summary: Email importer for Chronicle