maildir 1.0.1 → 2.0.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.
- 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
|