message_dir 0.0.1

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/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in message.gemspec
4
+ gemspec
5
+
6
+ gem "simple_uuid"
7
+
8
+ group :development do
9
+ gem "rspec"
10
+ end
data/README.rdoc ADDED
@@ -0,0 +1,64 @@
1
+ = Message dir
2
+
3
+ A directory, with messages. Like your Maildir/
4
+
5
+ Meant to be used to safely store incoming messages from any slow connection
6
+
7
+ == Message dir DSL
8
+
9
+ # create
10
+ dir = MessageDir.new("/path/to/store")
11
+ msg = dir.new do |fh|
12
+ fh.puts info_from_slow_connection
13
+ end
14
+
15
+ # move a message to cur/
16
+ dir.cur(msg)
17
+ msg.cur!
18
+
19
+ # get a list of all messages
20
+ dir.messages
21
+
22
+ # get a list of all messages in new/
23
+ dir.messages(:new)
24
+
25
+ # or cur
26
+ dir.messages(:cur)
27
+
28
+ # work on a message (moves it to tmp/ for the duration of a block)
29
+ dir.process(msg, File::WRONLY|File::APPEND) do |fh|
30
+ fh.puts more_info
31
+ end
32
+
33
+ msg.process(File::WRONLY|File::TRUNC) do |fh|
34
+ fh.puts new_contents
35
+ end
36
+
37
+ # remove a message
38
+ msg = dir.messages(:new).first
39
+ dir.rm(msg) # => raise exception, we dont like to remove 'new' messages
40
+
41
+ msg = dir.msgs(:cur).first
42
+ msg.rm!
43
+
44
+ # lock a message
45
+ msg.lock! do
46
+ # something with the locked message
47
+ end
48
+ msg.locked? # => false
49
+
50
+ dir.lock(msg) do
51
+ end
52
+
53
+ == Message acts as file
54
+
55
+ A message is not an implementation of IO or File but it acts in the same way.
56
+ Use that to your advantage.
57
+
58
+ == Message safety
59
+
60
+ A lot is done to ensure the safe handling of messages
61
+
62
+ * Messages are created and processed in tmp/
63
+ * A message fh is always opened with File::SYNC - slow but trustworthy
64
+ * Locks are carried over processes (and crashes) using .lock files
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,131 @@
1
+ class MessageDir
2
+ class Message
3
+ def initialize(master, path, uuid)
4
+ @master = master
5
+ @path = path
6
+ @uuid = uuid
7
+ @io = nil
8
+
9
+ self.create if !File.exists?(self.path)
10
+ @locked = File.exists?(self.lock_file)
11
+ open
12
+ end
13
+
14
+ def self.load(master, path)
15
+ self.new(master, File.dirname(path), SimpleUUID::UUID.new(File.basename(path)))
16
+ end
17
+
18
+ def path
19
+ File.join(@path, @uuid.to_guid)
20
+ end
21
+
22
+ def guid
23
+ @uuid.to_guid
24
+ end
25
+
26
+ def spot
27
+ File.basename(@path).to_sym
28
+ end
29
+
30
+ def create
31
+ open(File::CREAT|File::WRONLY) do |f|
32
+ # write 1 byte and truncate to force existance of the file
33
+ f.syswrite("i")
34
+ f.truncate(0)
35
+ end
36
+ end
37
+
38
+ def process(mode=File::RDONLY, &block)
39
+ @master.process(self, mode, &block)
40
+ end
41
+
42
+ def open(mode=File::RDONLY, &block)
43
+ close
44
+
45
+ # make sure them files are synced
46
+ mode |= File::SYNC unless mode & File::SYNC == File::SYNC
47
+
48
+ @io = File.open(self.path, mode, &block)
49
+ end
50
+
51
+ def fh
52
+ @io
53
+ end
54
+
55
+ def move(path)
56
+ raise "Can't move a locked file" if locked?
57
+
58
+ close
59
+ FileUtils.mv(self.path, File.join(path, self.guid))
60
+ @path = path
61
+ open
62
+ end
63
+
64
+ def rm
65
+ @master.rm(self)
66
+ end
67
+
68
+ def close
69
+ @io.close unless closed?
70
+ rescue
71
+ true
72
+ end
73
+
74
+ def closed?
75
+ @io.closed?
76
+ rescue
77
+ return true
78
+ end
79
+
80
+ def cur!
81
+ @master.cur(self)
82
+ end
83
+
84
+ def ==(other)
85
+ other.path == self.path && self.guid == other.guid
86
+ end
87
+
88
+ def locked?
89
+ @locked == true ? true : false || File.exists?(lock_file)
90
+ end
91
+
92
+ def lock_file
93
+ "#{path}.lock"
94
+ end
95
+
96
+ def lock
97
+ return if locked?
98
+
99
+ File.open(lock_file, File::CREAT|File::WRONLY) do |lf|
100
+ res = lf.flock(File::LOCK_EX|File::LOCK_NB)
101
+ raise "Could not obtain a lock on the lock file" if res == false
102
+ lf.flock(File::LOCK_UN)
103
+ end
104
+
105
+ if block_given?
106
+ yield
107
+ unlock
108
+ else
109
+ @locked = true
110
+ end
111
+ end
112
+ alias_method :lock!, :lock
113
+
114
+ def unlock
115
+ File.open(lock_file, File::CREAT|File::WRONLY) do |lf|
116
+ res = lf.flock(File::LOCK_EX|File::LOCK_NB)
117
+ raise "Could not obtain a lock on the lock file" if res == false
118
+ lf.flock(File::LOCK_UN)
119
+ File.unlink(lock_file)
120
+ end
121
+ @locked = false
122
+ end
123
+
124
+ def method_missing(method, *args, &block)
125
+ super
126
+ rescue NameError, NoMethodError => e
127
+ @io.send(method, *args, &block)
128
+ end
129
+
130
+ end
131
+ end
@@ -0,0 +1,3 @@
1
+ class MessageDir
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,110 @@
1
+ require "message_dir/version"
2
+ require "message_dir/message"
3
+ require 'simple_uuid'
4
+ require 'fileutils'
5
+
6
+ class MessageDir
7
+ SPOTS = %w(tmp new cur)
8
+
9
+ attr_reader :path
10
+
11
+ def initialize(path)
12
+ @path = MessageDir::Path.new(path)
13
+
14
+ create_tree
15
+ end
16
+
17
+ def create_tree
18
+ SPOTS.each do |spot|
19
+ FileUtils.mkdir_p @path.send(spot)
20
+ end
21
+ end
22
+
23
+ def new(&block)
24
+ uuid = SimpleUUID::UUID.new
25
+ msg = Message.new(self, path.new, uuid)
26
+
27
+ if block_given?
28
+ begin
29
+ msg.move(path.tmp)
30
+
31
+ msg.open(File::TRUNC|File::WRONLY) do |fh|
32
+ yield fh
33
+ end
34
+
35
+ msg.move(path.new)
36
+ rescue Exception => ex
37
+ # silently ignore errors
38
+ ensure
39
+ # make sure the message is written
40
+ msg.close
41
+ end
42
+ end
43
+
44
+ msg.open(File::RDONLY)
45
+ msg.seek(0,0)
46
+
47
+ return msg
48
+ end
49
+
50
+ def cur(msg)
51
+ return if msg.spot == :cur
52
+ msg.move(path.cur)
53
+ end
54
+
55
+ def messages(where=nil)
56
+ spots = where.nil? ? SPOTS : [ where ]
57
+
58
+ # find all the wanted paths
59
+ paths = spots.collect do |spot|
60
+ Dir[File.join(path.send(spot), '*')].select do |path|
61
+ path !~ /\.lock$/ && File.file?(path)
62
+ end
63
+ end.flatten
64
+
65
+ # load all the wanted messages
66
+ paths.collect do |path|
67
+ Message.load(self, path)
68
+ end
69
+ end
70
+ alias_method :msgs, :messages
71
+
72
+ def message(uuid)
73
+ uuid = uuid.to_guid if uuid.is_a? SimpleUUID::UUID
74
+ messages.select { |msg| msg.guid == uuid }.first
75
+ end
76
+
77
+ def process(msg, mode=File::RDONLY, &block)
78
+ raise "No block given" if !block_given?
79
+
80
+ # move to tmp and open with block for the given mode
81
+ before = msg.spot
82
+ msg.move(path.tmp) unless before == :tmp
83
+ msg.lock do
84
+ msg.open(mode, &block)
85
+ end
86
+
87
+ # move back re-open with default mode
88
+ msg.move(path.send(before)) unless before == :tmp
89
+ msg.open
90
+ end
91
+
92
+ def rm(msg)
93
+ return false if msg.locked?
94
+ File.unlink(msg.path) == 1 ? true : false
95
+ end
96
+
97
+ class Path
98
+ def initialize(root)
99
+ @root = root
100
+ end
101
+
102
+ def method_missing(method, *args, &block)
103
+ super
104
+ rescue NoMethodError => e
105
+ if [ :new, :cur, :tmp ].include?(method)
106
+ return File.join(@root, method.to_s)
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "message_dir/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "message_dir"
7
+ s.version = MessageDir::VERSION
8
+ s.authors = ["Hartog C. de Mik"]
9
+ s.email = ["hartog.de.mik@gmail.com"]
10
+ s.homepage = "https://github.com/coffeeaddict/message_dir"
11
+ s.summary = %q{Maildir like messages}
12
+ s.description = %q{Handle slow network messages with care}
13
+
14
+ s.rubyforge_project = "message_dir"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency(%q<simple_uuid>, ['~> 0.2'])
22
+ end
@@ -0,0 +1,301 @@
1
+ require 'spec_helper'
2
+ require 'tmpdir'
3
+
4
+ describe MessageDir do
5
+ before :each do
6
+ @path = Dir.mktmpdir(nil, '/tmp')
7
+ end
8
+
9
+ after :each do
10
+ FileUtils.remove_entry_secure @path
11
+ end
12
+
13
+ it "should create a tree structure at a designated directory" do
14
+ expect {
15
+ MessageDir.new(@path)
16
+ }.to change {
17
+ %w(tmp new cur).collect do |spot|
18
+ File.directory?(File.join(@path, spot))
19
+ end
20
+ }
21
+ end
22
+
23
+ subject { MessageDir.new(@path) }
24
+
25
+ it "should create a new message" do
26
+ msg = subject.new
27
+ File.exists?(msg.path).should == true
28
+ msg.path.should =~ Regexp.new(subject.path.new)
29
+ end
30
+
31
+ it "should provide new messages open for reading" do
32
+ msg = subject.new
33
+
34
+ msg.size.should == 0
35
+
36
+ expect { msg.syswrite("failure") }.to raise_error(IOError)
37
+ end
38
+
39
+ it "should provide new messages in block style" do
40
+ msg = subject.new do |fh|
41
+ fh.puts "I is a message"
42
+ end
43
+
44
+ msg.path.should =~ Regexp.new(subject.path.new)
45
+
46
+ msg.pos.should == 0
47
+
48
+ expect { msg.syswrite("failure") }.to raise_error(IOError)
49
+
50
+ msg.read.should == "I is a message\n"
51
+ end
52
+
53
+ it "should handle exceptions when creating messages" do
54
+ expect {
55
+ subject.new do |fh|
56
+ raise "an error"
57
+ end
58
+ }.to_not raise_error
59
+
60
+ msg = subject.new do |fh|
61
+ fh.puts "some info"
62
+ raise
63
+ end
64
+
65
+ msg.should_not be_nil
66
+ msg.read.should == "some info\n"
67
+ end
68
+
69
+ it "should place a new message in tmp during creation" do
70
+ msg = subject.new do |fh|
71
+ fh.puts "I am not in new"
72
+ raise
73
+ end
74
+
75
+ msg.path.should =~ Regexp.new(subject.path.tmp)
76
+ end
77
+
78
+ it "should move a message to cur" do
79
+ msg = subject.new do |fh|
80
+ fh.puts "I am now in cur"
81
+ end
82
+
83
+ msg.read.should == "I am now in cur\n"
84
+
85
+ subject.cur(msg)
86
+
87
+ msg.should_not be_closed
88
+ msg.path.should =~ Regexp.new(subject.path.cur)
89
+
90
+ msg.read.should == "I am now in cur\n"
91
+ end
92
+
93
+ it "should move a message to cur over the message" do
94
+ msg = subject.new do |fh|
95
+ fh.puts "i am to be curred"
96
+ end
97
+
98
+ msg.cur!
99
+
100
+ msg.should_not be_closed
101
+ msg.path.should =~ Regexp.new(subject.path.cur)
102
+ msg.read.should == "i am to be curred\n"
103
+ end
104
+
105
+ describe "listing messages" do
106
+ it "should list no messages when empty" do
107
+ subject.messages
108
+ subject.messages.should be_empty
109
+ end
110
+
111
+ it "should list all messages" do
112
+ 3.times do |i|
113
+ msg = subject.new do |fh|
114
+ fh.puts "i am message #{i}"
115
+ end
116
+ end
117
+
118
+ subject.messages.count.should == 3
119
+ end
120
+
121
+ it "should list all messages in new" do
122
+ 3.times do |i|
123
+ msg = subject.new do |fh|
124
+ fh.puts "i am message #{i}"
125
+ end
126
+
127
+ msg.cur! if i == 1
128
+ end
129
+
130
+ subject.msgs(:new).count.should == 2
131
+ end
132
+
133
+ it "should list all messages in cur" do
134
+ 3.times do |i|
135
+ msg = subject.new do |fh|
136
+ fh.puts "i am message #{i}"
137
+ end
138
+
139
+ msg.cur! if i != 1
140
+ end
141
+
142
+ subject.msgs(:cur).count.should == 2
143
+ end
144
+
145
+ it "should list all messages in tmp" do
146
+ 3.times do |i|
147
+ msg = subject.new do |fh|
148
+ fh.puts "i am message #{i}"
149
+ end
150
+ msg.move(subject.path.tmp)
151
+ end
152
+
153
+ subject.msgs(:tmp).count.should == 3
154
+ end
155
+ end
156
+
157
+ describe "accessing messages" do
158
+ before :each do
159
+ 3.times { subject.new }
160
+ 3.times { subject.new.cur! }
161
+ end
162
+
163
+ it "should provide access to a message in new" do
164
+ subject.msgs(:new).should be_any
165
+ subject.msgs(:new).first.should be_kind_of(MessageDir::Message)
166
+ end
167
+
168
+ it "should provide access to a message in cur" do
169
+ subject.msgs(:cur).should be_any
170
+ subject.msgs(:cur).first.should be_kind_of(MessageDir::Message)
171
+ end
172
+
173
+ it "should provide access to a message based on UUID" do
174
+ msg = subject.msgs(:new).first
175
+ same = subject.message(msg.guid)
176
+
177
+ same.should be_kind_of(MessageDir::Message)
178
+
179
+ msg.should == same
180
+ end
181
+ end
182
+
183
+ describe "locking" do
184
+ before :each do
185
+ 3.times { subject.new }
186
+ end
187
+
188
+ it "should not lock messages by default" do
189
+ msg = subject.msgs.first
190
+ msg.should_not be_locked
191
+ end
192
+
193
+ it "should lock messages" do
194
+ msg = subject.msgs.first
195
+ msg.lock
196
+ msg.should be_locked
197
+ end
198
+
199
+ it "should unlock messages" do
200
+ msg = subject.msgs.first
201
+ msg.lock
202
+ msg.should be_locked
203
+
204
+ msg.unlock
205
+ msg.should_not be_locked
206
+ end
207
+
208
+ it "should lock message in block style" do
209
+ msg = subject.msgs.first
210
+ msg.should_not be_locked
211
+
212
+ msg.lock do
213
+ msg.should be_locked
214
+
215
+ msg.open(File::WRONLY|File::APPEND) do |fh|
216
+ fh.puts "Placed in a locked file"
217
+ end
218
+ end
219
+
220
+ msg.should_not be_locked
221
+ end
222
+ end
223
+
224
+
225
+ describe "processing" do
226
+ before :each do
227
+ 3.times do |i|
228
+ msg = subject.new do |fh|
229
+ fh.puts "I am new"
230
+ end
231
+
232
+ msg.cur! if i == 1
233
+ end
234
+ end
235
+
236
+ it "should process messages" do
237
+ msg = subject.msgs.first
238
+ subject.process(msg, File::WRONLY|File::TRUNC) do |fh|
239
+ fh.puts "I am processed"
240
+ end
241
+
242
+ msg.read.should == "I am processed\n"
243
+ end
244
+
245
+ it "should process messages read-only per default" do
246
+ msg = subject.msgs.first
247
+ msg.process do |fh|
248
+ expect { fh.syswrite(" ") }.to raise_error(IOError)
249
+ end
250
+ end
251
+
252
+ it "should move messages to tmp for processing" do
253
+ msg = subject.msgs(:cur).first
254
+
255
+ msg.process do |fh|
256
+ fh.path.should =~ Regexp.new(subject.path.tmp)
257
+ msg.path.should =~ Regexp.new(subject.path.tmp)
258
+ end
259
+
260
+ msg.path.should =~ Regexp.new(subject.path.cur)
261
+ end
262
+
263
+ it "should lock messages when processing" do
264
+ msg = subject.msgs(:cur).first
265
+ msg.should_not be_locked
266
+ msg.process do |fh|
267
+ msg.should be_locked
268
+ msg.lock_file.should =~ Regexp.new(subject.path.tmp)
269
+ end
270
+
271
+ msg.should_not be_locked
272
+ end
273
+ end
274
+
275
+ describe "removing" do
276
+ before :each do
277
+ 3.times do |i|
278
+ msg = subject.new do |fh|
279
+ fh.puts "I am new"
280
+ end
281
+
282
+ msg.cur! if i == 1
283
+ end
284
+ end
285
+
286
+ it "should remove a message" do
287
+ msg = subject.msgs.last
288
+
289
+ subject.rm(msg).should be_true
290
+ File.exists?(msg.path).should be_false
291
+ end
292
+
293
+ it "should not remove a message when locked" do
294
+ msg = subject.msgs.last
295
+ msg.lock
296
+
297
+ msg.rm.should_not be_true
298
+ File.exists?(msg.path).should_not be_false
299
+ end
300
+ end
301
+ end
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+ require 'simple_uuid'
3
+
4
+ describe MessageDir::Message do
5
+ before :all do
6
+ @dir = MessageDir.new(Dir.mktmpdir(nil, '/tmp'))
7
+ end
8
+
9
+ after :all do
10
+ FileUtils.remove_entry_secure @dir.path
11
+ end
12
+
13
+ describe "On initialize" do
14
+ before :each do
15
+ uuid = SimpleUUID::UUID.new
16
+ File.open(File.join(@dir.path, "new", uuid.to_guid), "w") do |f|
17
+ f.puts "hello"
18
+ end
19
+ @new = uuid.to_guid
20
+
21
+ uuid = SimpleUUID::UUID.new
22
+ File.open(File.join(@dir.path, "cur", uuid.to_guid), "w") do |f|
23
+ f.puts "world"
24
+ end
25
+ @cur = uuid.to_guid
26
+ end
27
+
28
+ it "should not open a message without a guid" do
29
+ msg = MessageDir::Message.new(@dir)
30
+ msg.fh.should be_nil
31
+ end
32
+
33
+ it "should not create a message with a non existing guid" do
34
+ msg = MessageDir::Message.new(@dir, "24319b5c-5cc8-11e1-8312-67c6faefd181")
35
+ msg.fh.should be_nil
36
+ end
37
+
38
+ it "should open a message with an existing guid" do
39
+ msg = MessageDir::Message.new(@dir, @new)
40
+ msg.fh.should be_kind_of(File)
41
+ msg.fh.should_not be_closed
42
+ end
43
+
44
+ it "should find a message in cur as well" do
45
+ msg = MessageDir::Message.new(@dir, @cur)
46
+ msg.fh.should be_kind_of(File)
47
+ msg.fh.should_not be_closed
48
+ end
49
+ end
50
+
51
+ describe "With new" do
52
+ before :each do
53
+ @msg = MessageDir::Message.new(@dir)
54
+ end
55
+
56
+ it "should create a message in new" do
57
+ @msg.new do |fh|
58
+ fh.puts "i am the new message"
59
+ end
60
+
61
+ @msg.uuid.should be_kind_of(SimpleUUID::UUID)
62
+ @msg.guid.should be_kind_of(String)
63
+ @msg.guid.should_not be_empty
64
+
65
+ @msg.fh.should_not be_closed
66
+ @msg.fh.read.should =~ /i am the new message/
67
+
68
+ @msg.spot.should == :new
69
+ @msg.path.should =~ /new\//
70
+ end
71
+
72
+ it "should keep a message in tmp while not done" do
73
+ Thread.new do
74
+ @msg.new do |fh|
75
+ fh.puts "a part"
76
+ sleep 4
77
+ fh.puts "and another"
78
+ end
79
+ end
80
+
81
+ sleep 1
82
+
83
+ files = Dir[File.join(@dir.path, "tmp", "*")]
84
+ files.count.should == 1
85
+ end
86
+ end
87
+
88
+ describe "Current" do
89
+ it "should move a message to cur/" do
90
+ msg = MessageDir::Message.new(@dir).new do |fh|
91
+ fh.puts "i am the message"
92
+ end
93
+
94
+ msg.spot.should == :new
95
+
96
+ msg.cur
97
+ msg.spot.should == :cur
98
+ msg.path.should =~ /cur\//
99
+ end
100
+
101
+ it "should keep an open filehandle" do
102
+ msg = MessageDir::Message.new(@dir).new do |fh|
103
+ fh.puts "i am the message"
104
+ end.cur
105
+
106
+ msg.spot.should == :cur
107
+ msg.fh.should_not be_closed
108
+ end
109
+ end
110
+
111
+ end
@@ -0,0 +1,2 @@
1
+ require 'message_dir'
2
+ require 'fileutils'
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: message_dir
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Hartog C. de Mik
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2012-03-07 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: simple_uuid
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: "0.2"
24
+ type: :runtime
25
+ version_requirements: *id001
26
+ description: Handle slow network messages with care
27
+ email:
28
+ - hartog.de.mik@gmail.com
29
+ executables: []
30
+
31
+ extensions: []
32
+
33
+ extra_rdoc_files: []
34
+
35
+ files:
36
+ - .gitignore
37
+ - Gemfile
38
+ - README.rdoc
39
+ - Rakefile
40
+ - lib/message_dir.rb
41
+ - lib/message_dir/message.rb
42
+ - lib/message_dir/version.rb
43
+ - message_dir.gemspec
44
+ - spec/objects/message_dir_spec.rb
45
+ - spec/objects/message_spec_old.rb
46
+ - spec/spec_helper.rb
47
+ homepage: https://github.com/coffeeaddict/message_dir
48
+ licenses: []
49
+
50
+ post_install_message:
51
+ rdoc_options: []
52
+
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ requirements: []
68
+
69
+ rubyforge_project: message_dir
70
+ rubygems_version: 1.8.16
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: Maildir like messages
74
+ test_files: []
75
+