sortah 0.5.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/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .rspec
6
+ tmp/*
7
+
8
+ coverage/
data/.rvmrc ADDED
@@ -0,0 +1,2 @@
1
+ RVM_STRING="rvm use ruby-1.9.2-p290@sortah --create --verbose"
2
+ [ -e './.rvmrc_local' ] && source './.rvmrc_local' || $RVM_STRING
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
data/CONTRIBUTION.md ADDED
@@ -0,0 +1,23 @@
1
+ #Version rules
2
+
3
+ Version 1.0 comes when I'm pretty sure there are no major outstanding bugs from
4
+ release. After that, SemVer applies.
5
+
6
+ Let me (jfredett) control the Version, please don't submit a version change in
7
+ any pull request. This is merely out of concern for sanity.
8
+
9
+ #Contribution
10
+
11
+ Easy peasy, fork, pull request. Don't submit something with broken tests in it.
12
+
13
+ If you find a bug, filing an issue is cool, fixing it is cooler. I will love you
14
+ forever if you fix it.
15
+
16
+ ##Contributing sortah definitions:
17
+
18
+ For now, there is no "sortah contrib" repo, if there are many people interested
19
+ in sharing some common set of sortah definitions, then one will be created.
20
+
21
+ For now, if you have something cool, make a page in the wiki.
22
+
23
+
data/FUTURE_PLANS.md ADDED
@@ -0,0 +1,73 @@
1
+ #Ideas
2
+
3
+ - proxy destinations
4
+
5
+ Difficulty: middling
6
+
7
+ To allow for non-filesystem storage locations (say, redis, elasticsearch, w/e),
8
+ it would be nice to have "pseudo" destinations, like:
9
+
10
+ destination :redis do
11
+ RedisHandler.acquire_conn do |redis|
12
+ redis.put email.key, email.to_s
13
+ end
14
+ end
15
+
16
+ lens :key do
17
+ email.key = SHA1_of(email)
18
+ end
19
+
20
+ router :root, :lenses => [:key] do
21
+ send_to :redis
22
+ end
23
+
24
+ - multi-target `send_to`
25
+
26
+ Difficulty: easy
27
+
28
+ It would be nice to say:
29
+
30
+ router do
31
+ send_to [:foo, :bar, :baz]
32
+ end
33
+
34
+ and have a copy be sent to each destination, optionally, it could take a
35
+ parameter, "linked", so that the others would only be soft-links to the
36
+ canonical one (specifed by link), eg:
37
+
38
+ router do
39
+ send_to [:foo, :bar, :baz], :link => :foo
40
+ end
41
+
42
+ In the above, :bar and :baz would be softlinks to :foo
43
+
44
+ - contrib
45
+
46
+ Difficulty: easy
47
+ Prereq: Having a community.
48
+
49
+ Set up an easy-to-use 'contrib' repo for community sortah libraries.
50
+
51
+ - getmail integration
52
+
53
+ Difficulty: easy | hard
54
+
55
+ This comes in two flavors, the easy flavor is to just provide a wrapper
56
+ for defining a getmailrc. Perhaps making it easy to define a gmail rc and
57
+ also allowing for safe password injections (eg, not storing the password
58
+ plaintext in the rc file).
59
+
60
+ The harder version would be to just implement getmail as part of sortah.
61
+ This would allow for fine-grained control over how sortah gets fired, allowing
62
+ for better concurrency.
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org"
2
+
3
+ # development deps:
4
+ gem 'ruby-debug19', :require => 'rubydebug'
5
+ gem 'rspec'
6
+ gem 'rake'
7
+ gem 'pry', :require => 'pry'
8
+
9
+ # Specify your gem's dependencies in sortah.gemspec
10
+ gemspec
data/KNOWN_BUGS.md ADDED
@@ -0,0 +1,12 @@
1
+ doing
2
+
3
+ sortah.sort(email)
4
+ sortah.metadata#blahblahblah
5
+
6
+ does not behave as expected (it doens't preserve metadata to the next line)
7
+ this has something to do with how I pull the data in the Kernel patch. it works
8
+ if you do
9
+
10
+ sortah.sort(email).metadata#blahblah
11
+
12
+
data/LICENSE ADDED
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2011, Joseph Fredette
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ Redistributions of source code must retain the above copyright notice, this list
8
+ of conditions and the following disclaimer.
9
+
10
+ Redistributions in binary form must reproduce the above copyright notice, this
11
+ list of conditions and the following disclaimer in the documentation and/or
12
+ other materials provided with the distribution.
13
+
14
+ Neither the name of "sortah" nor the names of its contributors may be used to
15
+ endorse or promote products derived from this software without specific prior
16
+ written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,216 @@
1
+ #sortah
2
+
3
+ ##For sortin' your friggin' mail.
4
+
5
+ --------------------------------------------------------------------------------
6
+
7
+ [![Build Status](https://secure.travis-ci.org/jfredett/sortah.png)](http://travis-ci.org/jfredett/sortah)
8
+
9
+ --------------------------------------------------------------------------------
10
+
11
+ Sortah sort's mail. It provides a ruby [EDSL](# Embedded DSL) for manipulating
12
+ email objects. The DSL allows the definition of three principle components:
13
+
14
+ - Destinations
15
+
16
+ A destination takes in an Email object, and returns a system path. This is where
17
+ the email object passed into it will be saved. Ex:
18
+
19
+ destination :spam, "spam/"
20
+ destination :ham, "/"
21
+
22
+ These are -- in essence -- simple delcarations of the structure of your sorting
23
+ system. They may take one of several forms. The examples above are relative to
24
+ the mail directory, and will transparently manage organization south of that.
25
+ They can also be absolute paths, eg:
26
+
27
+ destination :devnull, :abs => "/dev/null"
28
+
29
+ which is regarded as an absolutely qualified path. It may also alias another
30
+ path:
31
+
32
+ destination :tldr, :devnull
33
+
34
+ - Lenses
35
+
36
+ Lenses are functions which produce a value given an input email. This value is
37
+ interpreted as metadata to be used by the routers. Ex:
38
+
39
+ lens :spam_value do
40
+ x = 0
41
+ email.text.each_line do |line|
42
+ x += (line =~ /extension/) ? 1 : 0
43
+ end
44
+ x
45
+ end
46
+
47
+ lens :word_count do
48
+ email.text.split.size
49
+ end
50
+
51
+ lenses can also depend on other lenses.
52
+
53
+ lens :spam_ratio :lenses => [:spam_value, :word_count] do
54
+ email.spam_value / email.word_count
55
+ end
56
+
57
+ You may specify the `pass_through` option to cause the lens to not set any
58
+ metadata. This is useful in two cases, updating old metadata, and interaction
59
+ (typically creational) with other services. Eg:
60
+
61
+ lens :example_update, :pass_through => true do
62
+ email.spam_value = 1000000 if email.sender == "annoying_guy0022493@hotmail.com"
63
+ end
64
+
65
+ lens :example_interaction, :pass_through => true, lenses => [:spam_value] do
66
+ return unless email.spam_value >= 1000000
67
+ HTTParty.post "http://spamblacklist.net/spammer/new", :body => email.sender
68
+ end
69
+
70
+ - Routers
71
+
72
+ This is the core of the language, a router is an object which produce either a
73
+ router object, or a destination. If it produces a destination, then the email is
74
+ delivered to that destination. If it produces another router, then the email is
75
+ passed along to the router produced. A router also 'depends' on lenses. These
76
+ lenses get applied when the router is called. There is one router which is
77
+ special, the "root" router, this is the first router which gets called. To
78
+ declare it, simply declare a router without a name. Ex:
79
+
80
+ router :spam_filter, :lenses => [:spam_value] do
81
+ send_to :ham if email.spam_value < 10
82
+ send_to :spam
83
+ end
84
+
85
+ router :root, :lenses => [:word_count] do
86
+ send_to :tldr if email.word_count > 100
87
+ send_to :spam_filter
88
+ end
89
+
90
+ `send_to` will first search for a destination with the given name, if it cannot
91
+ find one, it will send it search for the corresponding router. It also acts as
92
+ `return` -- halting execution of the block when it is called. This is
93
+ implemented via an exception, which means it _may_ cause performance issues on
94
+ things like the JVM, YMMV.
95
+
96
+ when defining a root router with lenses, you must specify ":root" as the title.
97
+
98
+ ## Common problems, and how to solve them:
99
+
100
+ ### Problem: Adding a mail to an external service, and then saving it.
101
+
102
+ As a user of sortah, you want to set up filters to save all email from the
103
+ address "searchable@somewhere.net" to the folder "foobar/", as well as register
104
+ it with the external service "RubberBandSearch".
105
+
106
+ ### Solution
107
+
108
+ destination :foobar, "foobar/"
109
+
110
+ lens :search_index , :pass_through => true do
111
+ #code to register the email in RubberBandSearch
112
+ email.indexed? = true
113
+ end
114
+
115
+ router :index_in_rubberband, :lenses => [:search_index] do
116
+ send_to :foobar
117
+ end
118
+
119
+ router :lenses => [:spam?] do
120
+ send_to :devnull if email.spam?
121
+ send_to :index_in_rubberband
122
+ end
123
+
124
+ Here we've used a `pass_through` lens to do the actual indexing, and the router
125
+ is left as more of a proxy to call the lens.
126
+
127
+ ### Problem
128
+
129
+ As a user of sortah, you want to maintain a whitelist of people who should have
130
+ their own folders, and you want those people to be subsorted in some arbitrarily
131
+ deep parent folders, eg:
132
+
133
+ family/
134
+ mom/
135
+ dad/
136
+ uncle_timmy/
137
+ coworkers/
138
+ pointy_hair/
139
+ dilbert/
140
+ old_coworkers/
141
+ jim/
142
+ personal/
143
+ wife/
144
+ friends/
145
+ bob/
146
+ mike/
147
+ jack/
148
+
149
+ etc. Further, you'd like to only maintain the above file (or something like it), and
150
+ not have to write new sortah code every time you move jobs or make new friends.[1]
151
+
152
+ [1] Ideally, this code would maintain a directory structure for you. But as of right
153
+ now, sortah has no aspirations to do such a thing. Each edition which _moves_ files
154
+ in the yaml definition file will simply create new folders, it is up to the author
155
+ of that yaml file to keep the directory coherent with the yaml file.
156
+
157
+ ## Solution
158
+
159
+ First, define a yaml file like the following:
160
+
161
+ personal:
162
+ - name: wife
163
+ sender:
164
+ - pretty-lady-who-feeds-me@scary.com
165
+ family:
166
+ - name: mom
167
+ sender:
168
+ - mom@hotmail.com
169
+ - mom@gmail.com
170
+ - name: dad
171
+ sender:
172
+ - dad@work.org
173
+ nested:
174
+ - name: example
175
+ reply-to: some_list@place.com
176
+ - deeper-nesting:
177
+ - name: deeper-nested-example
178
+ - reply-to: somewhere_else@overtherainbow.biz.co.uk
179
+ #...
180
+
181
+ This yaml file will represent the directory structure, as well as provide information
182
+ about how to determine whether the email is from that person or not.
183
+
184
+ Next, you could define a class `Contact`, which could be built with the following methods:
185
+
186
+ class Contact
187
+ # ... contains a definition for 'path' -- which is built from the yaml file.
188
+
189
+ def destination
190
+ destination name, path
191
+ end
192
+
193
+ def wants?(email)
194
+ search_fields.any? { |f,v| email[f] =~ /#{v}/ }
195
+ end
196
+
197
+ def search_fields
198
+ #these are the key/value pairs from the YAML file which are of the form:
199
+ # email-field: content_string
200
+ #eg:
201
+ # sender: 'me@place.net'
202
+ # reply-to: 'mailing-list@majordomo.com'
203
+ #etc
204
+ end
205
+ # ...
206
+ end
207
+
208
+ All of this code could be bound up in a router, eg:
209
+
210
+ router :contacts do
211
+ contacts = Contact.load_from_file('contacts.yml')
212
+ contacts.select { |c| c.wants?(email) }.first.destination
213
+ end
214
+
215
+ Much of this is left to pseudocode, but you can see how being able to use pure-ruby
216
+ allows for complex routes to be expressed simply.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ task :default => :spec
5
+
6
+ desc "Run specs"
7
+ RSpec::Core::RakeTask.new
8
+
9
+
10
+ task :c => :console
11
+ desc "start up a irb console"
12
+ task :console do
13
+ system 'bundle exec irb'
14
+ end
data/TUTORIAL.md ADDED
@@ -0,0 +1,43 @@
1
+ #How to use Sortah
2
+
3
+ First, I reccommend the following setup. In your `$HOME` directory, create a file
4
+ called '.sortah/', and beneath that, create 'rc', then execute the command
5
+
6
+ ln -s .sortah/rc .sortahrc
7
+
8
+ from your `$HOME` directory to link the `.sortahrc` file to the "real" rc file.
9
+ Next, I would initialize a git repo (or whatever VCS you prefer) in the
10
+ `.sortah` directory, and add your rc to it.
11
+
12
+ Next, create an rvmrc file in the sortah directory with a gemset of 'sortahrc'
13
+ (or whatever you prefer), this is where you will marshall all your dependencies
14
+ for sortah -- at the moment, it's only going to be one. You can use bundler for
15
+ this if you like, but if you just need vanilla sortah, it may be worth just
16
+ using `gem` to install sortah and eliminate the bundler overhead, YMMV.
17
+
18
+ Now that we've done that, we can wire up our getmailrc to point to sortah, as
19
+ follows:
20
+
21
+ [destination]
22
+ type = MDA_external
23
+ path = $HOME/.sortah/sortah.sh
24
+ arguments = ("--log-errors", )
25
+
26
+ Where `path` should point to your `.sortah` directory. Next, we need to create
27
+ the starter script, `sortah.sh`, this should look like:
28
+
29
+ #!/bin/sh
30
+ rvm 1.9.2@sortahrc exec sortah $@
31
+
32
+ then run
33
+
34
+ chmod a+x ~/.sortah/sortah.sh
35
+
36
+ This wraps the sortah executable so that we can always call it in the context of
37
+ the sortahrc gemset -- if you install this directly to your system, then this
38
+ shouldn't be necessary.
39
+
40
+ Once you've done this, getmail should automatically use sortah to sort your
41
+ email, now you just need to write your sortah definitions in the `~/.sortah/rc`
42
+ file!
43
+