gmail-britta 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "shoulda", ">= 0"
10
+ gem "rdoc", "~> 3.12"
11
+ gem "bundler", "~> 1.1.0"
12
+ gem "jeweler", "~> 1.8.4"
13
+ gem "rcov", ">= 0"
14
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,35 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activesupport (3.2.8)
5
+ i18n (~> 0.6)
6
+ multi_json (~> 1.0)
7
+ git (1.2.5)
8
+ i18n (0.6.1)
9
+ jeweler (1.8.4)
10
+ bundler (~> 1.0)
11
+ git (>= 1.2.5)
12
+ rake
13
+ rdoc
14
+ json (1.7.5)
15
+ multi_json (1.3.6)
16
+ rake (0.9.2.2)
17
+ rcov (1.0.0)
18
+ rdoc (3.12)
19
+ json (~> 1.4)
20
+ shoulda (3.1.1)
21
+ shoulda-context (~> 1.0)
22
+ shoulda-matchers (~> 1.2)
23
+ shoulda-context (1.0.0)
24
+ shoulda-matchers (1.3.0)
25
+ activesupport (>= 3.0.0)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ bundler (~> 1.1.0)
32
+ jeweler (~> 1.8.4)
33
+ rcov
34
+ rdoc (~> 3.12)
35
+ shoulda
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Andreas Fuchs
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,20 @@
1
+ = gmail-britta
2
+
3
+ This gem assists in writing complex gmail filters. You probably have a
4
+ lot of questions, and I'm sorry this README currently answers so few
5
+ of them )-:
6
+
7
+ == Contributing to gmail-britta
8
+
9
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
10
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
11
+ * Fork the project.
12
+ * Start a feature/bugfix branch.
13
+ * Commit and push until you are happy with your contribution.
14
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
15
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
16
+
17
+ == Copyright
18
+
19
+ Copyright (c) 2012 Andreas Fuchs. See LICENSE.txt for
20
+ further details.
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "gmail-britta"
18
+ gem.homepage = "http://github.com/antifuchs/gmail-britta"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Create complex gmail filtersets with a ruby DSL.}
21
+ gem.description = %Q{This gem helps create large (>50) gmail filter chains by writing xml compatible with gmail's "import/export filters" feature.} #'
22
+ gem.email = "asf@boinkor.net"
23
+ gem.authors = ["Andreas Fuchs"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ require 'rcov/rcovtask'
36
+ Rcov::RcovTask.new do |test|
37
+ test.libs << 'test'
38
+ test.pattern = 'test/**/test_*.rb'
39
+ test.verbose = true
40
+ test.rcov_opts << '--exclude "gems/*"'
41
+ end
42
+
43
+ task :default => :test
44
+
45
+ require 'rdoc/task'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "gmail-britta #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,60 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "gmail-britta"
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Andreas Fuchs"]
12
+ s.date = "2012-09-10"
13
+ s.description = "This gem helps create large (>50) gmail filter chains by writing xml compatible with gmail's \"import/export filters\" feature."
14
+ s.email = "asf@boinkor.net"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "gmail-britta.gemspec",
28
+ "lib/gmail-britta.rb"
29
+ ]
30
+ s.homepage = "http://github.com/antifuchs/gmail-britta"
31
+ s.licenses = ["MIT"]
32
+ s.require_paths = ["lib"]
33
+ s.rubygems_version = "1.8.24"
34
+ s.summary = "Create complex gmail filtersets with a ruby DSL."
35
+
36
+ if s.respond_to? :specification_version then
37
+ s.specification_version = 3
38
+
39
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
40
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
41
+ s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
42
+ s.add_development_dependency(%q<bundler>, ["~> 1.1.0"])
43
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
44
+ s.add_development_dependency(%q<rcov>, [">= 0"])
45
+ else
46
+ s.add_dependency(%q<shoulda>, [">= 0"])
47
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
48
+ s.add_dependency(%q<bundler>, ["~> 1.1.0"])
49
+ s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
50
+ s.add_dependency(%q<rcov>, [">= 0"])
51
+ end
52
+ else
53
+ s.add_dependency(%q<shoulda>, [">= 0"])
54
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
55
+ s.add_dependency(%q<bundler>, ["~> 1.1.0"])
56
+ s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
57
+ s.add_dependency(%q<rcov>, [">= 0"])
58
+ end
59
+ end
60
+
@@ -0,0 +1,262 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Google mail exclusive filter generator
4
+ # Docs: http://groups.google.com/group/gmail-labs-help-filter-import-export/browse_thread/thread/518a7b1634f20cdb#
5
+ # http://code.google.com/googleapps/domain/email_settings/developers_guide_protocol.html#GA_email_filter_main
6
+
7
+ require 'rubygems'
8
+ require 'time'
9
+ gem 'haml'
10
+ require 'haml'
11
+ require 'logger'
12
+
13
+ $log = Logger.new(STDERR)
14
+ $log.level = Logger::DEBUG
15
+
16
+ module SingleWriteAccessors
17
+ module ClassMethods
18
+ def ivar_name(name)
19
+ "@#{name}".intern
20
+ end
21
+
22
+ def single_write_accessors
23
+ @single_write_accessors ||= {}
24
+ end
25
+
26
+ def single_write_accessor(name, gmail_name, &block)
27
+ single_write_accessors[name] = gmail_name
28
+ ivar_name = self.ivar_name(name)
29
+ define_method(name) do |words|
30
+ if instance_variable_get(ivar_name)
31
+ raise "Only one use of #{name} is permitted per filter"
32
+ end
33
+ instance_variable_set(ivar_name, words)
34
+ end
35
+ define_method("get_#{name}") do
36
+ instance_variable_get(ivar_name)
37
+ end
38
+ if block_given?
39
+ define_method("output_#{name}") do
40
+ instance_variable_get(ivar_name) && block.call(instance_variable_get(ivar_name))
41
+ end
42
+ else
43
+ define_method("output_#{name}") do
44
+ instance_variable_get(ivar_name)
45
+ end
46
+ end
47
+ end
48
+
49
+ def single_write_boolean_accessor(name, gmail_name)
50
+ single_write_accessors[name] = gmail_name
51
+ ivar_name = self.ivar_name(name)
52
+ define_method(name) do |*args|
53
+ value = args.length > 0 ? args[0] : true
54
+ if instance_variable_get(ivar_name)
55
+ raise "Only one use of #{name} is permitted per filter"
56
+ end
57
+ instance_variable_set(ivar_name, value)
58
+ end
59
+ define_method("get_#{name}") do
60
+ instance_variable_get(ivar_name)
61
+ end
62
+ define_method("output_#{name}") do
63
+ instance_variable_get(ivar_name)
64
+ end
65
+ end
66
+ end
67
+
68
+ def self.included(base)
69
+ base.extend(ClassMethods)
70
+ end
71
+ end
72
+
73
+ class GmailBritta
74
+ def initialize(opts={})
75
+ @filters = []
76
+ @me = opts[:me] || 'me'
77
+ end
78
+
79
+ attr_accessor :filters
80
+ attr_accessor :me
81
+
82
+ def self.filterset(opts={}, &block)
83
+ (britta = GmailBritta.new(opts)).rules(&block)
84
+ britta
85
+ end
86
+
87
+ def rules(&block)
88
+ Delegate.new(self).perform(&block)
89
+ end
90
+
91
+ class Delegate
92
+ def initialize(britta)
93
+ @britta = britta
94
+ @filter = nil
95
+ end
96
+
97
+ def filter(&block)
98
+ Filter.new(@britta).perform(&block)
99
+ end
100
+
101
+ def perform(&block)
102
+ instance_eval(&block)
103
+ end
104
+ end
105
+
106
+ class Filter
107
+ include SingleWriteAccessors
108
+ single_write_accessor :has, 'hasTheWord' do |list|
109
+ emit_filter_spec(list)
110
+ end
111
+ single_write_accessor :has_not, 'doesNotHaveTheWord' do |list|
112
+ emit_filter_spec(list)
113
+ end
114
+ single_write_boolean_accessor :archive, 'shouldArchive'
115
+ single_write_boolean_accessor :delete_it, 'shouldTrash'
116
+ single_write_boolean_accessor :mark_read, 'shouldMarkAsRead'
117
+ single_write_boolean_accessor :mark_important, 'shouldAlwaysMarkAsImportant'
118
+ single_write_boolean_accessor :mark_unimportant, 'shouldNeverMarkAsImportant'
119
+ single_write_boolean_accessor :star, 'shouldStar'
120
+ single_write_boolean_accessor :never_spam, 'shouldNeverSpam'
121
+ single_write_accessor :label, 'label'
122
+ single_write_accessor :forward_to, 'forwardTo'
123
+
124
+ def generate_xml
125
+ engine = Haml::Engine.new(<<-ATOM)
126
+ %entry
127
+ %category{:term => 'filter'}
128
+ %title Mail Filter
129
+ %content
130
+ - self.class.single_write_accessors.keys.each do |name|
131
+ - gmail_name = self.class.single_write_accessors[name]
132
+ - if value = self.send("output_\#{name}".intern)
133
+ %apps:property{:name => gmail_name, :value => value.to_s}
134
+ ATOM
135
+ engine.render(self)
136
+ end
137
+
138
+ def self.emit_filter_spec(filter, infix=' ')
139
+ str = ''
140
+ case filter
141
+ when String
142
+ str << filter
143
+ when Hash
144
+ filter.keys.each do |key|
145
+ case key
146
+ when :or
147
+ str << '('
148
+ str << emit_filter_spec(filter[key], ' OR ')
149
+ str << ')'
150
+ when :not
151
+ str << '-('
152
+ str << emit_filter_spec(filter[key], ' ')
153
+ str << ')'
154
+ end
155
+ end
156
+ when Array
157
+ str << filter.map {|elt| emit_filter_spec(elt, ' ')}.join(infix)
158
+ end
159
+ $log.debug " Filter spec #{filter.inspect} + #{infix.inspect} => #{str.inspect}"
160
+ str
161
+ end
162
+
163
+ def me
164
+ @britta.me
165
+ end
166
+
167
+ def initialize(britta)
168
+ @britta=britta
169
+ end
170
+
171
+ def log_definition
172
+ $log.debug "Filter: #{self}"
173
+ Filter.single_write_accessors.each do |name|
174
+ val = instance_variable_get(Filter.ivar_name(name))
175
+ $log.debug " #{name}: #{val}" if val
176
+ end
177
+ self
178
+ end
179
+
180
+ def perform(&block)
181
+ instance_eval(&block)
182
+ @britta.filters << self
183
+ self
184
+ end
185
+
186
+ def merge_negated_criteria(filter)
187
+ old_has_not = Marshal.load(Marshal.dump((filter.get_has_not || []).reject { |elt|
188
+ @has.member?(elt)
189
+ }))
190
+ old_has = Marshal.load( Marshal.dump((filter.get_has || []).reject { |elt|
191
+ @has.member?(elt)
192
+ }))
193
+ $log.debug(" M: oh #{old_has.inspect}")
194
+ $log.debug(" M: ohn #{old_has_not.inspect}")
195
+
196
+ @has_not ||= []
197
+ @has_not += case
198
+ when old_has_not.first.is_a?(Hash) && old_has_not.first[:or]
199
+ old_has_not.first[:or] += old_has
200
+ old_has_not
201
+ when old_has_not.length > 0
202
+ [{:or => old_has_not + old_has}]
203
+ else
204
+ old_has
205
+ end
206
+ $log.debug(" M: h #{@has.inspect}")
207
+ $log.debug(" M: nhn #{@has_not.inspect}")
208
+ end
209
+
210
+ def otherwise(&block)
211
+ filter = Filter.new(@britta).perform(&block)
212
+ filter.merge_negated_criteria(self)
213
+ filter.log_definition
214
+ filter
215
+ end
216
+
217
+ def merge_positive_criteria(filter)
218
+ new_has = (@has || []) + (filter.get_has || [])
219
+ new_has_not = (@has_not || []) + (filter.get_has_not || [])
220
+ @has = new_has
221
+ @has_not = new_has_not
222
+ end
223
+
224
+ def also(&block)
225
+ filter = Filter.new(@britta).perform(&block)
226
+ filter.merge_positive_criteria(self)
227
+ filter.log_definition
228
+ filter
229
+ end
230
+
231
+ def archive_unless_directed(options={})
232
+ mark_as_read=options[:mark_read]
233
+ tos=(options[:to] || me).to_a
234
+ filter = Filter.new(@britta).perform do
235
+ has_not [{:or => tos.map {|to| "to:#{to}"}}]
236
+ archive
237
+ if mark_as_read
238
+ mark_read
239
+ end
240
+ end
241
+ filter.merge_positive_criteria(self)
242
+ filter.log_definition
243
+ self
244
+ end
245
+ end
246
+
247
+ def generate
248
+ engine = Haml::Engine.new(<<-ATOM)
249
+ !!! XML
250
+ %feed{:xmlns => 'http://www.w3.org/2005/Atom', 'xmlns:apps' => 'http://schemas.google.com/apps/2006'}
251
+ %title Mail Filters
252
+ %id tag:mail.google.com,2008:filters:
253
+ %updated #{Time.now.utc.iso8601}
254
+ %author
255
+ %name Andreas Fuchs
256
+ %email asf@boinkor.net
257
+ - filters.each do |filter|
258
+ != filter.generate_xml
259
+ ATOM
260
+ engine.render(self)
261
+ end
262
+ end
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gmail-britta
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Andreas Fuchs
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-09-10 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ requirement: &id001 !ruby/object:Gem::Requirement
22
+ none: false
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ hash: 3
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ version_requirements: *id001
31
+ name: shoulda
32
+ prerelease: false
33
+ type: :development
34
+ - !ruby/object:Gem::Dependency
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ hash: 31
41
+ segments:
42
+ - 3
43
+ - 12
44
+ version: "3.12"
45
+ version_requirements: *id002
46
+ name: rdoc
47
+ prerelease: false
48
+ type: :development
49
+ - !ruby/object:Gem::Dependency
50
+ requirement: &id003 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ hash: 19
56
+ segments:
57
+ - 1
58
+ - 1
59
+ - 0
60
+ version: 1.1.0
61
+ version_requirements: *id003
62
+ name: bundler
63
+ prerelease: false
64
+ type: :development
65
+ - !ruby/object:Gem::Dependency
66
+ requirement: &id004 !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ~>
70
+ - !ruby/object:Gem::Version
71
+ hash: 63
72
+ segments:
73
+ - 1
74
+ - 8
75
+ - 4
76
+ version: 1.8.4
77
+ version_requirements: *id004
78
+ name: jeweler
79
+ prerelease: false
80
+ type: :development
81
+ - !ruby/object:Gem::Dependency
82
+ requirement: &id005 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ hash: 3
88
+ segments:
89
+ - 0
90
+ version: "0"
91
+ version_requirements: *id005
92
+ name: rcov
93
+ prerelease: false
94
+ type: :development
95
+ description: This gem helps create large (>50) gmail filter chains by writing xml compatible with gmail's "import/export filters" feature.
96
+ email: asf@boinkor.net
97
+ executables: []
98
+
99
+ extensions: []
100
+
101
+ extra_rdoc_files:
102
+ - LICENSE.txt
103
+ - README.rdoc
104
+ files:
105
+ - .document
106
+ - Gemfile
107
+ - Gemfile.lock
108
+ - LICENSE.txt
109
+ - README.rdoc
110
+ - Rakefile
111
+ - VERSION
112
+ - gmail-britta.gemspec
113
+ - lib/gmail-britta.rb
114
+ homepage: http://github.com/antifuchs/gmail-britta
115
+ licenses:
116
+ - MIT
117
+ post_install_message:
118
+ rdoc_options: []
119
+
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ none: false
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ hash: 3
128
+ segments:
129
+ - 0
130
+ version: "0"
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ none: false
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ hash: 3
137
+ segments:
138
+ - 0
139
+ version: "0"
140
+ requirements: []
141
+
142
+ rubyforge_project:
143
+ rubygems_version: 1.8.24
144
+ signing_key:
145
+ specification_version: 3
146
+ summary: Create complex gmail filtersets with a ruby DSL.
147
+ test_files: []
148
+