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 +4 -0
- data/Gemfile +10 -0
- data/README.rdoc +64 -0
- data/Rakefile +1 -0
- data/lib/message_dir/message.rb +131 -0
- data/lib/message_dir/version.rb +3 -0
- data/lib/message_dir.rb +110 -0
- data/message_dir.gemspec +22 -0
- data/spec/objects/message_dir_spec.rb +301 -0
- data/spec/objects/message_spec_old.rb +111 -0
- data/spec/spec_helper.rb +2 -0
- metadata +75 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
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
|
data/lib/message_dir.rb
ADDED
@@ -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
|
data/message_dir.gemspec
ADDED
@@ -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
|
data/spec/spec_helper.rb
ADDED
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
|
+
|