sortah 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ##11/02/11
2
+
3
+ - fixed the "sortah hangs when redirecting to another router" bug
4
+ - this required changing cleanroom to be a normal Object subclass, instead of
5
+ a BasicObject subclass, I need to investigate re-adding certain methods to
6
+ Object so that the throw/catch machinery will be in place for BasicObject
7
+ - made bin/sortah more verbose
@@ -1,5 +1,64 @@
1
1
  #Ideas
2
2
 
3
+ - nested destinations --- overhaul of destinations
4
+
5
+ two things, first, change the way destinations are defined, eg:
6
+
7
+ maildir "/path/to/root/mail/dir/" do
8
+ destination :personal do
9
+ dir "gmail/"
10
+ type :maildir
11
+
12
+ destination :contact do
13
+ dir "contacts/"
14
+ type :maildir
15
+
16
+ destination :joe
17
+ destination :jenn
18
+ destination :tim
19
+
20
+ end
21
+
22
+ destination :mailing_lists do
23
+ type :mbox
24
+ #... snip ...
25
+ end
26
+ end
27
+
28
+ #... snip ...
29
+
30
+ abs :bitbucket, "/dev/null"
31
+
32
+ proxy :search_index do
33
+ #code to inject into search index
34
+ end
35
+
36
+ #... snip ...
37
+ end
38
+
39
+ This would make defining destinations a bit nicer, in addition to providing a
40
+ way for multi-type mailboxen.
41
+
42
+ - 'scaffold' functionality
43
+
44
+ should build all the scaffolding described by the destinations given in the
45
+ sortah configuration
46
+
47
+ - 'resource' type
48
+
49
+ A resource is some block of code which returns an object which is cached, like
50
+ lenses, a router, lens, or resource may depend on other resources. Resources
51
+ differ from lenses in that they are not bound to an email, and -- in the event
52
+ of multiple incoming emails, are executed only once across the whole session
53
+
54
+ - speed
55
+
56
+ it's pretty slow, as it stands, could definitely use some speed up. Major
57
+ improvement would be a sortah server, which could run persistently, so that
58
+ starting/stopping a ruby vm wouldn't be necessary. Also, parallizing sortah
59
+ would be nice, potential for that could be batch execution or the server idea
60
+ from above.
61
+
3
62
  - proxy destinations
4
63
 
5
64
  Difficulty: middling
@@ -9,4 +9,6 @@ if you do
9
9
 
10
10
  sortah.sort(email).metadata#blahblah
11
11
 
12
+ --------
12
13
 
14
+ if you try to metaprogram routers or w/e, it doesn't actually define them.
data/bin/sortah CHANGED
@@ -17,6 +17,7 @@ email = Mail.new do
17
17
  body ARGF.read
18
18
  end
19
19
 
20
+ puts "Loading sortah rc" if opts[:verbose]
20
21
  load File.expand_path(opts[:rc])
21
22
 
22
23
  dest = sortah.sort(email).full_destination
@@ -4,15 +4,13 @@ module Sortah
4
4
  end
5
5
 
6
6
 
7
- class CleanRoom < BasicObject
7
+ class CleanRoom < Object
8
8
  def self.sort(email, context)
9
9
  new(email, context).sort
10
10
  end
11
11
 
12
12
  def sort
13
- until @pointer.is_a?(Destination) do
14
- run!(@pointer) rescue FinishedExecution
15
- end
13
+ catch(:finished_execution) { run!(@pointer) } until @pointer.is_a?(Destination)
16
14
  self
17
15
  end
18
16
 
@@ -35,7 +33,7 @@ module Sortah
35
33
 
36
34
  def send_to(dest)
37
35
  @pointer = @__context__.routers[dest] || @__context__.destinations[dest]
38
- throw FinishedExecution
36
+ throw :finished_execution
39
37
  end
40
38
 
41
39
  def initialize(email, context)
@@ -1,3 +1,3 @@
1
1
  module Sortah
2
- VERSION = "0.5.0"
2
+ VERSION = "0.5.1"
3
3
  end
@@ -0,0 +1,99 @@
1
+ require 'yaml'
2
+ require 'sortah'
3
+
4
+ class Contact
5
+ def want?(email)
6
+ @emails.include?(email.sender) ||
7
+ email.from.any? { |me| is?(me) }
8
+ end
9
+
10
+ def build_destination(prefix)
11
+ local_dest = @destination #this gets around a weirdness -- the ivar doesn't end up in the right scope
12
+ #otherwise
13
+ sortah { destination local_dest, "#{prefix}/#{local_dest}/new/" }
14
+ end
15
+
16
+ def is?(contact)
17
+ case contact
18
+ when Contact
19
+ @emails.any? { |e| contact.is? e }
20
+ when String
21
+ @emails.include? contact
22
+ else
23
+ raise "Tried to compare a contact to something that wasn't an email address or contact object"
24
+ end
25
+ end
26
+
27
+ def <<(email)
28
+ @emails << email
29
+ update_in_contacts
30
+ end
31
+
32
+ attr_reader :destination
33
+
34
+ def initialize(emails, destination, path = '~/.sortah/contacts.yml')
35
+ @emails = emails
36
+ @destination = destination
37
+ @path = path
38
+ register_with_contacts
39
+ end
40
+
41
+ def register_with_contacts
42
+ Contacts(@path).add_contact(self)
43
+ end
44
+
45
+ def update_in_contacts
46
+ Contacts(@path).update(self)
47
+ end
48
+ end
49
+
50
+ class Contacts
51
+ def build_destinations(prefix)
52
+ @contacts.map { |c| c.build_destination(prefix) }
53
+ end
54
+
55
+ def destination_for(email)
56
+ find(email).first.destination
57
+ end
58
+
59
+ def want?(email)
60
+ @contacts.any? { |c| c.want? email }
61
+ end
62
+
63
+ def find(email)
64
+ @contacts.select { |c| c.want? email }
65
+ end
66
+
67
+ def add_contact(c)
68
+ @contacts << c
69
+ end
70
+
71
+ def update(c)
72
+ @contacts = @contacts.delete_if { |k| k.is? c } #compares by email
73
+ add_contact(c)
74
+ end
75
+
76
+ def save
77
+ File.open(@path, 'w') { |f| f << @contacts.to_yaml }
78
+ end
79
+
80
+ private
81
+
82
+ def load_contacts
83
+ @contacts = []
84
+ @contacts = YAML.load(File.read(@path)) if File.exists?(@path)
85
+ end
86
+
87
+ def initialize(path = nil)
88
+ if path
89
+ @path = File.expand_path(path)
90
+ else
91
+ @path = "#{ENV["HOME"]}/.sortah/contacts.yml"
92
+ end
93
+ load_contacts
94
+ end
95
+ end
96
+
97
+ def Contacts(path = nil)
98
+ Contacts.new(path)
99
+ end
@@ -0,0 +1,10 @@
1
+ ---
2
+ - !ruby/object:Contact
3
+ destination: joe
4
+ emails:
5
+ - jfredett@place.com
6
+ - joe@other_place.com
7
+ - !ruby/object:Contact
8
+ destination: sarah
9
+ emails:
10
+ - sfredett@somewhere.com
@@ -0,0 +1,4 @@
1
+ - !ruby/object:Contact
2
+ destination: brian
3
+ emails:
4
+ - brian@work.com
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+ #^^ just to make syntax highlighting stick
3
+
4
+ require './spec/fixtures/semantic_acceptance/contact.rb'
5
+
6
+ #destinations and maildir
7
+ sortah do
8
+ maildir "/Users/jfredett/.mutt/mail/"
9
+
10
+ $contacts = {}
11
+ $contacts[:work] = Contacts("./spec/fixtures/semantic_acceptance/coworkers.yml")
12
+ $contacts[:personal] = Contacts("./spec/fixtures/semantic_acceptance/contacts.yml")
13
+
14
+ $contacts[:work].build_destinations(:work)
15
+ $contacts[:personal].build_destinations(:personal)
16
+
17
+ destination :unknown, 'new/'
18
+ destination :unknown_personal, 'personal/unknown/new'
19
+ destination :unknown_work, 'work/unknown/new'
20
+ destination :unknown_coworker, 'work/coworkers/unknown/new'
21
+ end
22
+
23
+ #routers
24
+ sortah do
25
+ router :personal do
26
+ contact = $contacts[:personal]
27
+ send_to contact.destination_for(email) if contact.want? email
28
+ send_to :unknown_personal
29
+ end
30
+
31
+ router :work do
32
+ contact = $contacts[:work]
33
+ send_to contact.destination_for(email) if contact.want? email
34
+ send_to :unknown_work
35
+ end
36
+
37
+ router do
38
+ send_to :personal if email.to.any? { |r| r =~ /jfredett@place.com/ }
39
+ send_to :work if email.to.any? { |r| r =~ /joe@work.com/ }
40
+ send_to :unknown
41
+ end
42
+
43
+ end
@@ -15,6 +15,7 @@ describe Sortah::Parser do
15
15
  sortah.should_not be_nil
16
16
  end
17
17
 
18
+
18
19
  it "should parse defined 'simple' destinations" do
19
20
  expect {
20
21
  sortah do
@@ -230,15 +231,30 @@ describe Sortah::Parser do
230
231
  sortah.maildir.should == "/home/user/.mail/personal"
231
232
  end
232
233
 
234
+ it "should pass over a nested #sortah block" do
235
+ expect {
236
+ sortah do
237
+ sortah do
238
+ destination :foo, "foo/"
239
+
240
+ sortah do
241
+ end
242
+ end
243
+ end
244
+ }.should_not raise_error
245
+ sortah.destinations[:foo].should == 'foo/'
246
+ end
233
247
  end
234
248
 
235
249
  #acceptance criteria
236
250
  it "should parse an example sortah file, which contains all of the language elements" do
237
251
  expect {
238
252
  sortah do
239
- destination :place, "somewhere"
240
- destination :devnull, :abs => "/dev/null"
241
- destination :bitbucket, :devnull
253
+ sortah do
254
+ destination :place, "somewhere"
255
+ destination :devnull, :abs => "/dev/null"
256
+ destination :bitbucket, :devnull
257
+ end
242
258
 
243
259
  lens :random_value do
244
260
  rand
@@ -18,45 +18,46 @@ def basic_sortah_definition
18
18
  end
19
19
 
20
20
  describe Sortah do
21
- context "when sorting an email" do
22
- before :all do
23
- Mail.defaults do
24
- delivery_method :test
25
- end
21
+ before :all do
22
+ Mail.defaults do
23
+ delivery_method :test
24
+ end
26
25
 
27
- @email = Mail.new do
28
- to 'testa@example.com'
29
- from 'chuck@nope.com'
30
- subject "Taximerdizin'"
31
- body <<-TXT
32
- OJAI VALLEY TAXIDERMY
26
+ @email = Mail.new do
27
+ to 'testa@example.com'
28
+ from 'chuck@nope.com'
29
+ subject "Taximerdizin'"
30
+ body <<-TXT
31
+ OJAI VALLEY TAXIDERMY
33
32
 
34
- BET YOU THOUGHT THIS EMAIL WAS REAL
33
+ BET YOU THOUGHT THIS EMAIL WAS REAL
35
34
 
36
- NOPE. CHUCK TESTA
37
- TXT
38
- end
39
-
40
- @reply_email = Mail.new do
41
- to 'chuck@nope.com'
42
- from 'jgf@somewhere.com'
43
- subject "Re: Taximerdizin'"
44
- reply_to 'chuck@nope.com'
45
- body <<-TXT
46
- > OJAI VALLEY TAXIDERMY
47
- >
48
- > BET YOU THOUGHT THIS EMAIL WAS REAL
49
- >
50
- > NOPE. CHUCK TESTA
51
-
52
- Do you taxidermize pets?
53
- TXT
54
- end
35
+ NOPE. CHUCK TESTA
36
+ TXT
55
37
  end
56
38
 
57
- before :each do
58
- Sortah::Parser.clear!
39
+ @reply_email = Mail.new do
40
+ to 'chuck@nope.com'
41
+ from 'jgf@somewhere.com'
42
+ subject "Re: Taximerdizin'"
43
+ reply_to 'chuck@nope.com'
44
+ body <<-TXT
45
+ > OJAI VALLEY TAXIDERMY
46
+ >
47
+ > BET YOU THOUGHT THIS EMAIL WAS REAL
48
+ >
49
+ > NOPE. CHUCK TESTA
50
+
51
+ Do you taxidermize pets?
52
+ TXT
59
53
  end
54
+ end
55
+
56
+ before :each do
57
+ Sortah::Parser.clear!
58
+ end
59
+
60
+ context "when sorting an email" do
60
61
 
61
62
  it "should provide a way to sort a single email" do
62
63
  sortah.should respond_to :sort
@@ -111,6 +112,44 @@ describe Sortah do
111
112
  sortah.sort(@reply_email).destination.should == "bar/"
112
113
  end
113
114
 
115
+ it "should defer to a second router using the more idiomatic ruby syntax" do
116
+ sortah do
117
+ destination :foo, "foo/"
118
+ destination :bar, "bar/"
119
+
120
+ router do
121
+ send_to :foo if email.from.any? { |sender| sender =~ /chuck/ }
122
+ send_to :secondary_router
123
+ end
124
+
125
+ router :secondary_router do
126
+ send_to :bar
127
+ end
128
+ end
129
+ sortah.sort(@email).destination.should == "foo/"
130
+ sortah.sort(@reply_email).destination.should == "bar/"
131
+ end
132
+
133
+ it "should allow me to set local variables in a router block" do
134
+ sortah do
135
+ destination :foo, "foo/"
136
+ destination :bar, "bar/"
137
+
138
+ router do
139
+ senders = email.from
140
+
141
+ send_to :foo if senders.any? { |sender| sender =~ /chuck/ }
142
+ send_to :secondary_router
143
+ end
144
+
145
+ router :secondary_router do
146
+ send_to :bar
147
+ end
148
+ end
149
+ sortah.sort(@email).destination.should == "foo/"
150
+ sortah.sort(@reply_email).destination.should == "bar/"
151
+ end
152
+
114
153
  it "should run dependent lenses for the root router" do
115
154
  sortah do
116
155
  destination :foo, "foo/"
@@ -305,6 +344,32 @@ describe Sortah do
305
344
 
306
345
  end
307
346
  end
347
+
348
+ end
349
+
350
+ end
351
+
352
+ describe "acceptance" do
353
+ it "should be able to parse and sort using a real live example sortah definition" do
354
+
355
+ sortah do
356
+ load './spec/fixtures/semantic_acceptance/rc'
357
+ end
358
+
359
+ personal_email = Mail.new do
360
+ To 'jfredett@place.com'
361
+ From 'sfredett@somewhere.com'
362
+ end
363
+ work_email = Mail.new do
364
+ To 'joe@work.com'
365
+ From 'brian@work.com'
366
+ Subject 'You get a raise, you brilliant bastard.'
367
+ #shuttup, I can dream.
368
+ end
369
+
370
+ sortah.sort(@email).destination.should == 'new/'
371
+ sortah.sort(personal_email).destination.should == 'personal/sarah/new/'
372
+ sortah.sort(work_email).destination.should == 'work/brian/new/'
308
373
  end
309
374
  end
310
375
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sortah
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-25 00:00:00.000000000Z
12
+ date: 2011-11-02 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mail
16
- requirement: &2153551920 !ruby/object:Gem::Requirement
16
+ requirement: &2164841700 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2153551920
24
+ version_requirements: *2164841700
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: trollop
27
- requirement: &2153551480 !ruby/object:Gem::Requirement
27
+ requirement: &2164841260 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *2153551480
35
+ version_requirements: *2164841260
36
36
  description: ! "\n Sortah provides a simple, declarative internal DSL for sorting
37
37
  \n your email. It provides an executable which may serve as an external\n mail
38
38
  delivery agent for such programs as `getmail`. Finally, since\n your sorting
@@ -50,6 +50,7 @@ files:
50
50
  - .gitignore
51
51
  - .rvmrc
52
52
  - .travis.yml
53
+ - CHANGELOG.md
53
54
  - CONTRIBUTION.md
54
55
  - FUTURE_PLANS.md
55
56
  - Gemfile
@@ -78,6 +79,10 @@ files:
78
79
  - spec/destination_spec.rb
79
80
  - spec/email_spec.rb
80
81
  - spec/fixtures/rc
82
+ - spec/fixtures/semantic_acceptance/contact.rb
83
+ - spec/fixtures/semantic_acceptance/contacts.yml
84
+ - spec/fixtures/semantic_acceptance/coworkers.yml
85
+ - spec/fixtures/semantic_acceptance/rc
81
86
  - spec/parser_spec.rb
82
87
  - spec/semantic_spec.rb
83
88
  - spec/sortah_handler_spec.rb
@@ -111,6 +116,10 @@ test_files:
111
116
  - spec/destination_spec.rb
112
117
  - spec/email_spec.rb
113
118
  - spec/fixtures/rc
119
+ - spec/fixtures/semantic_acceptance/contact.rb
120
+ - spec/fixtures/semantic_acceptance/contacts.yml
121
+ - spec/fixtures/semantic_acceptance/coworkers.yml
122
+ - spec/fixtures/semantic_acceptance/rc
114
123
  - spec/parser_spec.rb
115
124
  - spec/semantic_spec.rb
116
125
  - spec/sortah_handler_spec.rb