maildir 1.0.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +20 -10
- data/lib/maildir.rb +26 -0
- data/lib/maildir/message.rb +8 -12
- data/lib/maildir/serializer/base.rb +30 -28
- data/lib/maildir/serializer/json.rb +12 -10
- data/lib/maildir/serializer/mail.rb +13 -9
- data/lib/maildir/serializer/marshal.rb +13 -9
- data/lib/maildir/serializer/yaml.rb +13 -9
- data/lib/maildir/version.rb +1 -1
- data/test/test_maildir.rb +2 -2
- data/test/test_message.rb +1 -5
- metadata +61 -105
- data/lib/maildir/keywords.rb +0 -110
- data/lib/maildir/subdirs.rb +0 -70
- data/test/test_keywords.rb +0 -16
- data/test/test_subdirs.rb +0 -46
data/README.rdoc
CHANGED
@@ -95,10 +95,17 @@ The following serializers are included:
|
|
95
95
|
|
96
96
|
Maildir::Serializer::Base simply reads and writes strings to disk.
|
97
97
|
|
98
|
-
|
98
|
+
`Maildir.serializer` and `Maildir.serializer=` allow you to set default serializer.
|
99
|
+
|
100
|
+
Maildir.serializer # => Maildir::Serializer::Base.new (default)
|
99
101
|
message = maildir.add("Hello World!") # writes "Hello World!" to disk
|
100
102
|
message.data # => "Hello World!"
|
101
103
|
|
104
|
+
You can also set the serializer per maildir:
|
105
|
+
|
106
|
+
maildir = Maildir.new 'Maildir'
|
107
|
+
maildir.serializer = Maildir::Serializer::JSON.new
|
108
|
+
|
102
109
|
As of version 1.0.0, the Maildir::Serializer::Base can write IO streams as well as strings. For example:
|
103
110
|
|
104
111
|
message.add(STDIN)
|
@@ -106,18 +113,18 @@ As of version 1.0.0, the Maildir::Serializer::Base can write IO streams as well
|
|
106
113
|
This will use Ruby 1.9's more efficient IO.copy_stream method if available,
|
107
114
|
and degrade gracefully in Ruby 1.8.
|
108
115
|
|
116
|
+
As of version 1.0.2, serializers are autoloaded. Thus it is no longer necessary to manually require them.
|
117
|
+
|
109
118
|
The Mail serializer takes a ruby Mail object (http://github.com/mikel/mail) and writes RFC2822 email messages.
|
110
119
|
|
111
|
-
|
112
|
-
Maildir::Message.serializer = Maildir::Serializer::Mail.new
|
120
|
+
maildir.serializer = Maildir::Serializer::Mail.new
|
113
121
|
mail = Mail.new(...)
|
114
122
|
message = maildir.add(mail) # writes an RFC2822 message to disk
|
115
123
|
message.data == mail # => true; data is parsed as a Mail object
|
116
124
|
|
117
125
|
The Marshal, JSON, and YAML serializers work similarly. E.g.:
|
118
126
|
|
119
|
-
|
120
|
-
Maildir::Message.serializer = Maildir::Serializer::JSON.new
|
127
|
+
maildir.serializer = Maildir::Serializer::JSON.new
|
121
128
|
my_data = {"foo" => nil, "my_array" => [1,2,3]}
|
122
129
|
message = maildir.add(my_data) # writes {"foo":null,"my_array":[1,2,3]}
|
123
130
|
message.data == my_data # => true
|
@@ -127,13 +134,16 @@ It's trivial to create a custom serializer. Implement the following two methods:
|
|
127
134
|
load(path)
|
128
135
|
dump(data, path)
|
129
136
|
|
137
|
+
== Author
|
138
|
+
|
139
|
+
* Aaron Suggs (github[http://github.com/ktheory])
|
140
|
+
|
130
141
|
== Contributors
|
131
142
|
|
132
|
-
*
|
133
|
-
*
|
134
|
-
*
|
135
|
-
*
|
136
|
-
* Alexander Flatter (github[http://github.com/aflatter]) for improving serializers
|
143
|
+
* Niklas E. Cathor (github[http://github.com/nilclass])
|
144
|
+
* Ali Polatel (github[http://github.com/alip])
|
145
|
+
* Harry Vangberg (github[http://github.com/vangberg])
|
146
|
+
* Alexander Flatter (github[http://github.com/aflatter])
|
137
147
|
|
138
148
|
== Copyright
|
139
149
|
|
data/lib/maildir.rb
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
require 'fileutils' # For create_directories
|
2
2
|
class Maildir
|
3
3
|
|
4
|
+
module Serializer
|
5
|
+
autoload :Base, 'maildir/serializer/base'
|
6
|
+
autoload :Mail, 'maildir/serializer/mail'
|
7
|
+
autoload :Marshal, 'maildir/serializer/marshal'
|
8
|
+
autoload :JSON, 'maildir/serializer/json'
|
9
|
+
autoload :YAML, 'maildir/serializer/yaml'
|
10
|
+
end
|
11
|
+
|
4
12
|
SUBDIRS = [:tmp, :new, :cur].freeze
|
5
13
|
|
6
14
|
include Comparable
|
@@ -8,6 +16,19 @@ class Maildir
|
|
8
16
|
attr_reader :path
|
9
17
|
attr_accessor :serializer
|
10
18
|
|
19
|
+
# Default serializer.
|
20
|
+
@@serializer = Maildir::Serializer::Base.new
|
21
|
+
|
22
|
+
# Gets the default serializer.
|
23
|
+
def self.serializer
|
24
|
+
@@serializer
|
25
|
+
end
|
26
|
+
|
27
|
+
# Sets the default serializer.
|
28
|
+
def self.serializer=(serializer)
|
29
|
+
@@serializer = serializer
|
30
|
+
end
|
31
|
+
|
11
32
|
# Create a new maildir at +path+. If +create+ is true, will ensure that the
|
12
33
|
# required subdirectories exist.
|
13
34
|
def initialize(path, create = true)
|
@@ -17,6 +38,11 @@ class Maildir
|
|
17
38
|
create_directories if create
|
18
39
|
end
|
19
40
|
|
41
|
+
# Returns own serializer or falls back to default.
|
42
|
+
def serializer
|
43
|
+
@serializer || @@serializer
|
44
|
+
end
|
45
|
+
|
20
46
|
# Compare maildirs by their paths.
|
21
47
|
# If maildir is a different class, return nil.
|
22
48
|
# Otherwise, return 1, 0, or -1.
|
data/lib/maildir/message.rb
CHANGED
@@ -17,19 +17,16 @@ class Maildir::Message
|
|
17
17
|
message
|
18
18
|
end
|
19
19
|
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# Default serializer
|
23
|
-
@@serializer = Maildir::Serializer::Base.new
|
24
|
-
|
25
|
-
# Get the serializer
|
20
|
+
# DEPRECATED: Get the serializer.
|
21
|
+
# @see Maildir.serializer
|
26
22
|
def self.serializer
|
27
|
-
|
23
|
+
Maildir.serializer
|
28
24
|
end
|
29
25
|
|
30
|
-
# Set the serializer
|
26
|
+
# DEPRECATED: Set the serializer.
|
27
|
+
# @see Maildir.serializer=
|
31
28
|
def self.serializer=(serializer)
|
32
|
-
|
29
|
+
Maildir.serializer = serializer
|
33
30
|
end
|
34
31
|
|
35
32
|
attr_reader :dir, :unique_name, :info
|
@@ -70,10 +67,9 @@ class Maildir::Message
|
|
70
67
|
"#<#{self.class} key=#{key} maildir=#{@maildir.inspect}>"
|
71
68
|
end
|
72
69
|
|
73
|
-
#
|
74
|
-
# back to the default serializer.
|
70
|
+
# Helper to get serializer.
|
75
71
|
def serializer
|
76
|
-
@maildir.serializer
|
72
|
+
@maildir.serializer
|
77
73
|
end
|
78
74
|
|
79
75
|
# Writes data to disk. Can only be called on messages instantiated without
|
@@ -1,36 +1,38 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
1
|
+
class Maildir
|
2
|
+
module Serializer
|
3
|
+
# The Maildir::Serializer::Base class reads & writes data to disk as a
|
4
|
+
# string. Other serializers (e.g. Maildir::Serializer::Mail) can extend this
|
5
|
+
# class to do some pre- and post-processing of the string.
|
6
|
+
#
|
7
|
+
# The Serializer API has two methods:
|
8
|
+
# load(path) # => returns data
|
9
|
+
# dump(data, path) # => returns number of bytes written
|
10
|
+
class Base
|
11
|
+
# Reads the file at path. Returns the contents of path.
|
12
|
+
def load(path)
|
13
|
+
File.read(path)
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
# Writes data to path. Returns number of bytes written.
|
17
|
+
# If data acts like an IO object (i.e., data responds to the read method),
|
18
|
+
# we call data.read or the more efficient IO.copy_stream available in
|
19
|
+
# ruby 1.9.1.
|
20
|
+
def dump(data, path)
|
21
|
+
if data.respond_to?(:read)
|
22
|
+
if IO.respond_to?(:copy_stream)
|
23
|
+
IO.copy_stream(data, path)
|
24
|
+
else
|
25
|
+
write(data.read, path)
|
26
|
+
end
|
23
27
|
else
|
24
|
-
write(data
|
28
|
+
write(data, path)
|
25
29
|
end
|
26
|
-
else
|
27
|
-
write(data, path)
|
28
30
|
end
|
29
|
-
end
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
32
|
+
protected
|
33
|
+
def write(data, path)
|
34
|
+
File.open(path, "w") {|file| file.write(data)}
|
35
|
+
end
|
34
36
|
end
|
35
37
|
end
|
36
38
|
end
|
@@ -5,17 +5,19 @@ rescue LoadError
|
|
5
5
|
require 'json'
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
module
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
8
|
+
class Maildir
|
9
|
+
module Serializer
|
10
|
+
# Serialize messages as JSON
|
11
|
+
class JSON < Base
|
12
|
+
# Read data from path and parse it as JSON.
|
13
|
+
def load(path)
|
14
|
+
::JSON.load(super(path))
|
15
|
+
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
# Dump data as JSON and writes it to path.
|
18
|
+
def dump(data, path)
|
19
|
+
super(data.to_json, path)
|
20
|
+
end
|
19
21
|
end
|
20
22
|
end
|
21
23
|
end
|
@@ -1,13 +1,17 @@
|
|
1
1
|
require 'mail'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
class Maildir
|
3
|
+
module Serializer
|
4
|
+
# Serialize messages as a ruby Mail object
|
5
|
+
class Mail < Maildir::Serializer::Base
|
6
|
+
# Build a new Mail object from the data at path.
|
7
|
+
def load(path)
|
8
|
+
::Mail.new(super(path))
|
9
|
+
end
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
11
|
+
# Write data to path as a Mail message.
|
12
|
+
def dump(data, path)
|
13
|
+
super(data.to_s, path)
|
14
|
+
end
|
15
|
+
end
|
12
16
|
end
|
13
17
|
end
|
@@ -1,12 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
class Maildir
|
2
|
+
module Serializer
|
3
|
+
# Serialize messages as Marshalled ruby objects
|
4
|
+
class Marshal < Maildir::Serializer::Base
|
5
|
+
# Read data from path and unmarshal it.
|
6
|
+
def load(path)
|
7
|
+
::Marshal.load(super(path))
|
8
|
+
end
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
|
10
|
+
# Marshal data and write it to path.
|
11
|
+
def dump(data, path)
|
12
|
+
super(::Marshal.dump(data), path)
|
13
|
+
end
|
14
|
+
end
|
11
15
|
end
|
12
16
|
end
|
@@ -1,13 +1,17 @@
|
|
1
1
|
require 'yaml'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
class Maildir
|
3
|
+
module Serializer
|
4
|
+
# Serialize messages as YAML
|
5
|
+
class YAML < Maildir::Serializer::Base
|
6
|
+
# Read data from path and parse it as YAML.
|
7
|
+
def load(path)
|
8
|
+
::YAML.load(super(path))
|
9
|
+
end
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
11
|
+
# Dump data as YAML and writes it to path.
|
12
|
+
def dump(data, path)
|
13
|
+
super(data.to_yaml, path)
|
14
|
+
end
|
15
|
+
end
|
12
16
|
end
|
13
17
|
end
|
data/lib/maildir/version.rb
CHANGED
data/test/test_maildir.rb
CHANGED
@@ -17,8 +17,8 @@ class TestMaildir < Test::Unit::TestCase
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
should "have
|
21
|
-
|
20
|
+
should "have default serializer" do
|
21
|
+
assert_equal temp_maildir.serializer, Maildir::Message.serializer
|
22
22
|
end
|
23
23
|
|
24
24
|
should "set serializer" do
|
data/test/test_message.rb
CHANGED
@@ -8,11 +8,7 @@ class TestMessage < Test::Unit::TestCase
|
|
8
8
|
@message = Maildir::Message.new(@maildir)
|
9
9
|
end
|
10
10
|
|
11
|
-
should "use
|
12
|
-
assert_equal @message.serializer, Maildir::Message.serializer
|
13
|
-
end
|
14
|
-
|
15
|
-
should "prefer serializer of its maildir" do
|
11
|
+
should "use serializer of its maildir" do
|
16
12
|
@maildir.serializer = :foo
|
17
13
|
assert_equal @message.serializer, :foo
|
18
14
|
end
|
metadata
CHANGED
@@ -1,113 +1,84 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: maildir
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.0
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 0
|
9
|
-
- 1
|
10
|
-
version: 1.0.1
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Aaron Suggs
|
14
|
-
- Niklas E. Cathor
|
15
9
|
autorequire:
|
16
10
|
bindir: bin
|
17
11
|
cert_chain: []
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
dependencies:
|
22
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2011-09-09 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
23
15
|
name: rake
|
24
|
-
|
25
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: &70101941878220 !ruby/object:Gem::Requirement
|
26
17
|
none: false
|
27
|
-
requirements:
|
28
|
-
- -
|
29
|
-
- !ruby/object:Gem::Version
|
30
|
-
|
31
|
-
segments:
|
32
|
-
- 0
|
33
|
-
version: "0"
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
34
22
|
type: :development
|
35
|
-
version_requirements: *id001
|
36
|
-
- !ruby/object:Gem::Dependency
|
37
|
-
name: shoulda
|
38
23
|
prerelease: false
|
39
|
-
|
24
|
+
version_requirements: *70101941878220
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: shoulda
|
27
|
+
requirement: &70101941877660 !ruby/object:Gem::Requirement
|
40
28
|
none: false
|
41
|
-
requirements:
|
42
|
-
- -
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
|
45
|
-
segments:
|
46
|
-
- 0
|
47
|
-
version: "0"
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
48
33
|
type: :development
|
49
|
-
version_requirements: *id002
|
50
|
-
- !ruby/object:Gem::Dependency
|
51
|
-
name: mail
|
52
34
|
prerelease: false
|
53
|
-
|
35
|
+
version_requirements: *70101941877660
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: mail
|
38
|
+
requirement: &70101941877140 !ruby/object:Gem::Requirement
|
54
39
|
none: false
|
55
|
-
requirements:
|
56
|
-
- -
|
57
|
-
- !ruby/object:Gem::Version
|
58
|
-
|
59
|
-
segments:
|
60
|
-
- 0
|
61
|
-
version: "0"
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
62
44
|
type: :development
|
63
|
-
version_requirements: *id003
|
64
|
-
- !ruby/object:Gem::Dependency
|
65
|
-
name: json
|
66
45
|
prerelease: false
|
67
|
-
|
46
|
+
version_requirements: *70101941877140
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: json
|
49
|
+
requirement: &70101941876660 !ruby/object:Gem::Requirement
|
68
50
|
none: false
|
69
|
-
requirements:
|
70
|
-
- -
|
71
|
-
- !ruby/object:Gem::Version
|
72
|
-
|
73
|
-
segments:
|
74
|
-
- 0
|
75
|
-
version: "0"
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
76
55
|
type: :development
|
77
|
-
version_requirements: *id004
|
78
|
-
- !ruby/object:Gem::Dependency
|
79
|
-
name: fakefs
|
80
56
|
prerelease: false
|
81
|
-
|
57
|
+
version_requirements: *70101941876660
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: fakefs
|
60
|
+
requirement: &70101941876060 !ruby/object:Gem::Requirement
|
82
61
|
none: false
|
83
|
-
requirements:
|
84
|
-
- -
|
85
|
-
- !ruby/object:Gem::Version
|
86
|
-
hash: 23
|
87
|
-
segments:
|
88
|
-
- 0
|
89
|
-
- 3
|
90
|
-
- 2
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
91
65
|
version: 0.3.2
|
92
66
|
type: :development
|
93
|
-
|
94
|
-
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70101941876060
|
69
|
+
description: A ruby library for reading and writing arbitrary messages in DJB's maildir
|
70
|
+
format
|
95
71
|
email: aaron@ktheory.com
|
96
72
|
executables: []
|
97
|
-
|
98
73
|
extensions: []
|
99
|
-
|
100
74
|
extra_rdoc_files: []
|
101
|
-
|
102
|
-
files:
|
103
|
-
- lib/maildir/keywords.rb
|
75
|
+
files:
|
104
76
|
- lib/maildir/message.rb
|
105
77
|
- lib/maildir/serializer/base.rb
|
106
78
|
- lib/maildir/serializer/json.rb
|
107
79
|
- lib/maildir/serializer/mail.rb
|
108
80
|
- lib/maildir/serializer/marshal.rb
|
109
81
|
- lib/maildir/serializer/yaml.rb
|
110
|
-
- lib/maildir/subdirs.rb
|
111
82
|
- lib/maildir/unique_name.rb
|
112
83
|
- lib/maildir/version.rb
|
113
84
|
- lib/maildir.rb
|
@@ -115,53 +86,38 @@ files:
|
|
115
86
|
- README.rdoc
|
116
87
|
- Rakefile
|
117
88
|
- test/test_helper.rb
|
118
|
-
- test/test_keywords.rb
|
119
89
|
- test/test_maildir.rb
|
120
90
|
- test/test_message.rb
|
121
91
|
- test/test_serializers.rb
|
122
|
-
- test/test_subdirs.rb
|
123
92
|
- test/test_unique_name.rb
|
124
|
-
has_rdoc: true
|
125
93
|
homepage: http://github.com/ktheory/maildir
|
126
94
|
licenses: []
|
127
|
-
|
128
95
|
post_install_message:
|
129
|
-
rdoc_options:
|
96
|
+
rdoc_options:
|
130
97
|
- --charset=UTF-8
|
131
|
-
require_paths:
|
98
|
+
require_paths:
|
132
99
|
- lib
|
133
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
134
101
|
none: false
|
135
|
-
requirements:
|
136
|
-
- -
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
|
139
|
-
|
140
|
-
- 0
|
141
|
-
version: "0"
|
142
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
107
|
none: false
|
144
|
-
requirements:
|
145
|
-
- -
|
146
|
-
- !ruby/object:Gem::Version
|
147
|
-
hash: 17
|
148
|
-
segments:
|
149
|
-
- 1
|
150
|
-
- 3
|
151
|
-
- 5
|
108
|
+
requirements:
|
109
|
+
- - ! '>='
|
110
|
+
- !ruby/object:Gem::Version
|
152
111
|
version: 1.3.5
|
153
112
|
requirements: []
|
154
|
-
|
155
113
|
rubyforge_project:
|
156
|
-
rubygems_version: 1.
|
114
|
+
rubygems_version: 1.8.7
|
157
115
|
signing_key:
|
158
116
|
specification_version: 3
|
159
117
|
summary: Read & write messages in the maildir format
|
160
|
-
test_files:
|
118
|
+
test_files:
|
161
119
|
- test/test_helper.rb
|
162
|
-
- test/test_keywords.rb
|
163
120
|
- test/test_maildir.rb
|
164
121
|
- test/test_message.rb
|
165
122
|
- test/test_serializers.rb
|
166
|
-
- test/test_subdirs.rb
|
167
123
|
- test/test_unique_name.rb
|
data/lib/maildir/keywords.rb
DELETED
@@ -1,110 +0,0 @@
|
|
1
|
-
# implements IMAP Keywords as used by the Courier Mail Server
|
2
|
-
# see http://www.courier-mta.org/imap/README.imapkeywords.html for details
|
3
|
-
|
4
|
-
require 'maildir'
|
5
|
-
module Maildir::Keywords
|
6
|
-
def self.included(base)
|
7
|
-
Maildir::Message.send(:include, MessageExtension)
|
8
|
-
end
|
9
|
-
|
10
|
-
def keyword_dir
|
11
|
-
@keyword_dir ||= File.join(path, 'courierimapkeywords')
|
12
|
-
Dir.mkdir(@keyword_dir) unless File.directory?(@keyword_dir)
|
13
|
-
return @keyword_dir
|
14
|
-
end
|
15
|
-
|
16
|
-
# process contents of courierimapkeywords/ directory as described in README.imapkeywords
|
17
|
-
def read_keywords
|
18
|
-
messages = (list(:cur) + list(:new)).inject({}) { |m, msg| m[msg.unique_name] = msg ; m }
|
19
|
-
t = Time.now.to_i / 300
|
20
|
-
keywords = []
|
21
|
-
state = :head
|
22
|
-
# process :list
|
23
|
-
list_file = File.join(keyword_dir, ':list')
|
24
|
-
File.open(list_file).each_line do |line|
|
25
|
-
line.strip!
|
26
|
-
if state == :head
|
27
|
-
if line.empty?
|
28
|
-
state = :messages
|
29
|
-
next
|
30
|
-
end
|
31
|
-
keywords << line
|
32
|
-
else
|
33
|
-
key, ids = line.split(':')
|
34
|
-
if msg = messages[key]
|
35
|
-
msg.set_keywords(ids.split(/\s/).map {|id| keywords[id.to_i - 1] })
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end if File.exist?(list_file)
|
39
|
-
# collect keyword files
|
40
|
-
keyword_files = (Dir.entries(keyword_dir) - %w(. .. :list)).inject({}) do |keyword_files, file|
|
41
|
-
if file =~ /^\.(\d+)\.(.*)$/
|
42
|
-
n = $1
|
43
|
-
key = $2
|
44
|
-
else
|
45
|
-
n = t + 1
|
46
|
-
key = file
|
47
|
-
FileUtils.mv(File.join(keyword_dir, file), File.join(keyword_dir, ".#{n}.#{key}"))
|
48
|
-
end
|
49
|
-
if msg = messages[key]
|
50
|
-
(keyword_files[key] ||= []) << [n, key]
|
51
|
-
else # message doesn't exist
|
52
|
-
fname = File.join(keyword_dir, file)
|
53
|
-
if File.stat(fname).ctime < (Time.now - (15 * 60))
|
54
|
-
File.unlink(fname)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
next(keyword_files)
|
58
|
-
end
|
59
|
-
# process keyword files
|
60
|
-
keyword_files.each_pair do |key, files|
|
61
|
-
files.sort! { |a, b| a[0] <=> b[0] }
|
62
|
-
files[0..-2].each { |f| File.unlink(File.join(keyword_dir, ".#{f.join('.')}")) } if files.last[0] < t
|
63
|
-
msg = messages[key]
|
64
|
-
file = (File.exist?(File.join(keyword_dir, files.last[1])) ? files.last[1] : ".#{files.last.join('.')}")
|
65
|
-
current_keywords = File.read(File.join(keyword_dir, file)).split(/\s+/)
|
66
|
-
msg.set_keywords(current_keywords)
|
67
|
-
if (add = (current_keywords - keywords)).any?
|
68
|
-
keywords += add
|
69
|
-
end
|
70
|
-
end
|
71
|
-
# rebuild :list
|
72
|
-
@keywords = {}
|
73
|
-
tmp_file = File.join(path, 'tmp', ':list')
|
74
|
-
File.open(tmp_file, 'w') { |f|
|
75
|
-
f.write(keywords.join("\n")+"\n\n")
|
76
|
-
messages.each_pair do |key, msg|
|
77
|
-
next unless msg.keywords
|
78
|
-
f.puts([key, msg.keywords.map{|kw| keywords.index(kw) + 1 }.sort.join(' ')].join(':'))
|
79
|
-
@keywords[key] = msg.keywords
|
80
|
-
end
|
81
|
-
}
|
82
|
-
FileUtils.mv(tmp_file, list_file)
|
83
|
-
end
|
84
|
-
|
85
|
-
def keywords(key)
|
86
|
-
read_keywords unless @keywords
|
87
|
-
@keywords[key] || []
|
88
|
-
end
|
89
|
-
|
90
|
-
module MessageExtension
|
91
|
-
def keywords
|
92
|
-
return @keywords if @keywords
|
93
|
-
@maildir.keywords(unique_name)
|
94
|
-
end
|
95
|
-
|
96
|
-
# sets given keywords on the message.
|
97
|
-
def keywords=(list)
|
98
|
-
tmp_fname = File.join(@maildir.path, 'tmp', unique_name)
|
99
|
-
File.open(tmp_fname, 'w') { |f| f.write(list.join("\n")) }
|
100
|
-
FileUtils.mv(tmp_fname, File.join(@maildir.keyword_dir, unique_name))
|
101
|
-
end
|
102
|
-
|
103
|
-
# sets @keywords to the given list
|
104
|
-
def set_keywords(list)
|
105
|
-
@keywords = list
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
Maildir.send(:include, Maildir::Keywords)
|
data/lib/maildir/subdirs.rb
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
# implements subdirs as used by the Courier Mail Server (courier-mta.org)
|
2
|
-
require 'maildir'
|
3
|
-
module Maildir::Subdirs
|
4
|
-
ROOT_NAME = 'INBOX'
|
5
|
-
DELIM = '.'
|
6
|
-
|
7
|
-
def self.included(base)
|
8
|
-
base.instance_eval do
|
9
|
-
alias_method :inspect_without_subdirs, :inspect
|
10
|
-
alias_method :inspect, :inspect_with_subdirs
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def name
|
15
|
-
root? ? ROOT_NAME : subdir_parts(path).last
|
16
|
-
end
|
17
|
-
|
18
|
-
def create_subdir(name)
|
19
|
-
raise ArgumentError.new("'name' may not contain delimiter character (#{DELIM})") if name.include?(DELIM)
|
20
|
-
full_name = (root? ? [] : subdir_parts(File.basename(path))).push(name).unshift('').join(DELIM)
|
21
|
-
md = Maildir.new(File.join(path, full_name), true)
|
22
|
-
@subdirs << md if @subdirs
|
23
|
-
md
|
24
|
-
end
|
25
|
-
|
26
|
-
# returns the logical mailbox path
|
27
|
-
def mailbox_path
|
28
|
-
@mailbox_path ||= root? ? ROOT_NAME : subdir_parts(File.basename(path)).unshift(ROOT_NAME).join(DELIM)
|
29
|
-
end
|
30
|
-
|
31
|
-
# returns an array of Maildir objects representing the direct subdirectories of this Maildir
|
32
|
-
def subdirs(only_direct=true)
|
33
|
-
if root?
|
34
|
-
@subdirs ||= (Dir.entries(path) - %w(. ..)).select {|e|
|
35
|
-
e =~ /^\./ && File.directory?(File.join(path, e)) && (only_direct ? subdir_parts(e).size == 1 : true)
|
36
|
-
}.map { |e| Maildir.new(File.join(path, e), false) }
|
37
|
-
else
|
38
|
-
my_parts = subdir_parts(File.basename(path))
|
39
|
-
@subdirs ||= root.subdirs(false).select { |md| subdir_parts(File.basename(md.path))[0..-2] == my_parts }
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# Friendly inspect method
|
44
|
-
def inspect_with_subdirs
|
45
|
-
"#<#{self.class} path=#{@path} mailbox_path=#{mailbox_path}>"
|
46
|
-
end
|
47
|
-
|
48
|
-
# returns the Maildir representing the root directory
|
49
|
-
def root
|
50
|
-
root? ? self : Maildir.new(File.dirname(path), false)
|
51
|
-
end
|
52
|
-
|
53
|
-
# returns true if the parent directory doesn't look like a maildir
|
54
|
-
def root?
|
55
|
-
! ((Dir.entries(File.dirname(path)) & %w(cur new tmp)).size == 3)
|
56
|
-
end
|
57
|
-
|
58
|
-
private
|
59
|
-
|
60
|
-
def subdir_parts(path)
|
61
|
-
path.sub!(/\/$/, '') # remove trailing slash
|
62
|
-
parts = (path.split(DELIM) - [''])
|
63
|
-
# some clients (e.g. Thunderbird) mess up namespaces so subdirs
|
64
|
-
# end up looking like '.INBOX.Trash' instead of '.Trash'
|
65
|
-
parts.shift if parts.first == ROOT_NAME
|
66
|
-
parts
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
Maildir.send(:include, Maildir::Subdirs)
|
data/test/test_keywords.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
require 'maildir/keywords'
|
3
|
-
class TestKeywords < Test::Unit::TestCase
|
4
|
-
context "A message" do
|
5
|
-
setup do
|
6
|
-
@data = "foo\n"
|
7
|
-
@msg = Maildir::Message.create(temp_maildir, @data)
|
8
|
-
end
|
9
|
-
|
10
|
-
should "remember keywords" do
|
11
|
-
kw = %w(Junk Seen)
|
12
|
-
@msg.keywords = kw
|
13
|
-
assert (@msg.keywords & kw).size == 2
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
data/test/test_subdirs.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
require 'maildir/subdirs'
|
3
|
-
class TestSubdirs < Test::Unit::TestCase
|
4
|
-
context "A maildir" do
|
5
|
-
setup do
|
6
|
-
FakeFS::FileSystem.clear
|
7
|
-
@maildir = temp_maildir
|
8
|
-
setup_subdirs(@maildir)
|
9
|
-
end
|
10
|
-
|
11
|
-
should "have subdirs" do
|
12
|
-
assert @maildir.subdirs.any?
|
13
|
-
end
|
14
|
-
|
15
|
-
should "be called INBOX" do
|
16
|
-
assert @maildir.name == 'INBOX'
|
17
|
-
end
|
18
|
-
|
19
|
-
should "include direct subdirs" do
|
20
|
-
subdir_names = @maildir.subdirs.map(&:name)
|
21
|
-
assert subdir_names.include?('a') && subdir_names.include?('b')
|
22
|
-
end
|
23
|
-
|
24
|
-
should "not include deeper subdirs" do
|
25
|
-
subdir_names = @maildir.subdirs.map(&:name)
|
26
|
-
assert ! subdir_names.include?('x') && ! subdir_names.include?('a.x')
|
27
|
-
end
|
28
|
-
|
29
|
-
should "create more subdirs" do
|
30
|
-
@maildir.create_subdir("test")
|
31
|
-
assert @maildir.subdirs.map(&:name).include?("test")
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
context "A subdir" do
|
36
|
-
setup do
|
37
|
-
FakeFS::FileSystem.clear
|
38
|
-
@maildir = temp_maildir
|
39
|
-
setup_subdirs(@maildir)
|
40
|
-
end
|
41
|
-
|
42
|
-
should "include more subdirs" do
|
43
|
-
assert_not_empty @maildir.subdirs.select{ |sd| sd.name == 'a'}.first.subdirs
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|