sortah 0.5.0 → 0.5.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.
@@ -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