syncevolution 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e66f23c24c893a5c3f65f05bb4c01b68969e9480
4
+ data.tar.gz: 72711fca4d295007d661967514962f9628435f56
5
+ SHA512:
6
+ metadata.gz: 6bc97d40e0e1f7385c91e3c46c5834d7e9e17b661fa8e50af1db63516b70b26c9ff0ec4ac6548f47ecd35ed49f0910d363fee0430fcf7e16dcfdce3494933dac
7
+ data.tar.gz: 77ecb22582ae6c2669bb5180fdd90848fcd072350b2e979d1ce56de1c3cb208be74c09619ac2a5c7db861566168ba6e374b329cfeab76aadb32a77e92a79d003
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ #ruby=2.1
2
+ #ruby-gemset=syncevolution
3
+
4
+ source 'https://rubygems.org'
5
+
6
+ # Specify your gem's dependencies in syncevolution.gemspec
7
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 friflaj
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # SyncEvolution
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'syncevolution'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install syncevolution
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'test'
7
+ end
8
+
9
+ desc "Run tests"
10
+ task :default => :test
data/bin/se-config ADDED
@@ -0,0 +1,326 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'inifile'
4
+ require 'nokogiri'
5
+ require 'pp'
6
+ require 'trollop'
7
+
8
+ module SyncEvolution
9
+ class SyncEvolution
10
+ def to_params(params, indent, postfix = " \\\n")
11
+ return '' if params.size == 0
12
+ sep = indent ? "\\\n " : ''
13
+ return sep + params.keys.select{|k| params[k]}.collect{|k| "#{k}=#{params[k]}"}.collect{|kv| escape(kv) }.join(" #{sep}") + (indent ? postfix : '')
14
+ end
15
+
16
+ def escape(s)
17
+ return s if s !~ / / && s != "\"#{s}\""
18
+ return _inspect(s)
19
+ end
20
+
21
+ def to_gv
22
+ gv = "digraph syncevolution {\n"
23
+
24
+ objects = {}
25
+ syncs = 0
26
+
27
+ @doc.xpath('//context').each{|context|
28
+ objects[context['name']] ||= "o#{objects.size}"
29
+ gv << " subgraph cluster_#{objects[context['name']]} {\n"
30
+ gv << " label = #{context['name'].inspect};\n"
31
+
32
+ context.xpath('./store').each{|store|
33
+ objects[context['name'] + ':' + store['name']] ||= "o#{objects.size}"
34
+ gv << " #{objects[context['name'] + ':' + store['name']]} [ shape = folder, label = #{store['name'].inspect}];\n"
35
+ }
36
+
37
+ context.xpath('./sync').each{|sync|
38
+ objects[context['name'] + ':' + sync['name']] ||= "o#{objects.size}"
39
+ gv << " #{objects[context['name'] + ':' + sync['name']]} [ shape = component, label = #{sync['name'].inspect}];\n"
40
+
41
+
42
+ sync.xpath('./store').each{|store|
43
+ }
44
+ }
45
+
46
+ gv << " }\n"
47
+ }
48
+
49
+ @doc.xpath('//sync/store').each{|store|
50
+ sync = store.parent
51
+ context = sync.parent
52
+
53
+ if sync['syncURL'] =~ /^local:\/\/@(.+)/
54
+ targetcontext = $1.downcase
55
+ else
56
+ next
57
+ end
58
+
59
+ syncs += 1
60
+
61
+ objects["#{targetcontext}:target-config"] ||= "o#{objects.size}"
62
+ objects["#{targetcontext}:#{store['uri']}"] ||= "o#{objects.size}"
63
+
64
+ dir = case store['sync']
65
+ when 'two-way' then 'both'
66
+ else throw store['sync'].inspect
67
+ end
68
+
69
+ gv << " #{objects[context['name'] + ':' + store['name']]} -> #{objects[context['name'] + ':' + sync['name']]} [label=\"#{syncs}\", dir=#{dir}, style=dashed];\n"
70
+ gv << " #{objects[context['name'] + ':' + sync['name']]} -> #{objects["#{targetcontext}:target-config"]} [label=\"#{syncs}\", dir=#{dir}, style=dashed];\n"
71
+ gv << " #{objects["#{targetcontext}:target-config"]} -> #{objects["#{targetcontext}:#{store['uri']}"]} [label=\"#{syncs}\", dir=#{dir}, style=dashed];\n"
72
+ }
73
+
74
+ gv << "}\n"
75
+
76
+ gv
77
+ end
78
+
79
+ def import(xml)
80
+ doc = Nokogiri::XML(open(xml))
81
+
82
+ set(@doc.root, doc.root.attributes)
83
+
84
+ doc.xpath('//context').each {|ctx_s|
85
+ ctx_t = @doc.at("//context[@name=#{_inspect(ctx_s['name'])}]")
86
+ if ctx_s.at('./clear') && ctx_t
87
+ ctx_t.unlink
88
+ ctx_t = add(context: ctx_s['name'])
89
+ ctx_t << '<clear/>'
90
+ end
91
+ set(add(context: ctx_s['name']), ctx_s.attributes) if ctx_s.attributes.size > 0
92
+
93
+ ctx_s.xpath('./store').each{|st_s|
94
+ st_t = @doc.at("//context[@name=#{_inspect(ctx_s['name'])}]/store[@name=#{_inspect(st_s['name'])}]")
95
+ if st_s.at('./clear') && st_t
96
+ st_t.unlink
97
+ st_t = add(context: ctx_s['name'], store: st_s['name'])
98
+ st_t << '<clear/>'
99
+ end
100
+ set(add(context: ctx_s['name'], store: st_s['name']), st_s.attributes) if st_s.attributes.size > 0
101
+ }
102
+
103
+ ctx_s.xpath('./sync').each{|sn_s|
104
+ sn_t = @doc.at("//context[@name=#{_inspect(ctx_s['name'])}]/store[@name=#{_inspect(sn_s['name'])}]")
105
+ if sn_s.at('./clear') && sn_t
106
+ sn_t.unlink
107
+ sn_t = add(context: ctx_s['name'], sync: sn_s['name'])
108
+ sn_t << '<clear/>'
109
+ end
110
+ set(add(context: ctx_s['name'], sync: sn_s['name']), sn_s.attributes) if sn_s.attributes.size > 0
111
+
112
+ sn_s.xpath('./store').each{|st_s|
113
+ st_t = @doc.at("//context[@name=#{_inspect(ctx_s['name'])}]/sync[@name=#{_inspect(sn_s['name'])}]/store[@name=#{_inspect(st_s['name'])}]")
114
+ if st_s.at('./clear') && st_t
115
+ st_t.unlink
116
+ st_t = add(context: ctx_s['name'], sync: sn_s['name'], store: st_s['name'])
117
+ st_t << '<clear/>'
118
+ end
119
+ set(add(context: ctx_s['name'], sync: sn_s['name'], store: st_s['name']), st_s.attributes) if st_s.attributes.size > 0
120
+ }
121
+ }
122
+ }
123
+ end
124
+
125
+ def to_bash(indent)
126
+ bash = ''
127
+
128
+ params = to_params(@doc.root.attributes.reject{|k, v| k == 'name'}, indent, '')
129
+ bash << "syncevolution --configure --template none #{params}\n" if params != ''
130
+
131
+ @doc.xpath('//context').each{|context|
132
+ bash << "syncevolution --remove #{escape('@' + context['name'])}\n" if context.at('./clear')
133
+
134
+ params = to_params(context.attributes.reject{|k, v| k == 'name'}, indent)
135
+ bash << "syncevolution --configure --template none #{params} @#{escape(context['name'])}\n" if params != ''
136
+
137
+ context.xpath('./store').each{|store|
138
+ bash << "syncevolution --remove-database #{escape('@' + context['name'])} #{escape(store['name'])}\n" if store.at('./clear')
139
+
140
+ params = to_params(store.attributes.reject{|k, v| k == 'name'}, indent)
141
+ bash << "syncevolution --configure --template none #{params} @#{escape(context['name'])} #{escape(store['name'])}\n" if params != ''
142
+ }
143
+
144
+ context.xpath('./sync').each{|sync|
145
+ bash << "syncevolution --remove #{escape(sync['name'] + '@' + context['name'])}\n" if sync.at('./clear')
146
+ params = to_params(sync.attributes.reject{|k, v| k == 'name'}, indent)
147
+ bash << "syncevolution --configure --template none #{params} #{escape(sync['name'] + '@' + context['name'])}\n" if params != ''
148
+ sync.xpath('./store').each{|store|
149
+ params = to_params(store.attributes.reject{|k, v| k == 'name'}, indent)
150
+ bash << "syncevolution --configure --template none #{params} #{escape(sync['name'] + '@' + context['name'])} #{escape(store['name'])}\n" if params != ''
151
+ }
152
+ }
153
+ }
154
+
155
+ bash
156
+ end
157
+
158
+ def _inspect(s)
159
+ "'#{s.gsub("'", %q(\\\'))}'"
160
+ end
161
+
162
+ def add(options={})
163
+ context = @doc.at("//context[@name=#{_inspect(options[:context].downcase)}]")
164
+ if !context
165
+ context = Nokogiri::XML::Node.new('context', @doc)
166
+ context['name'] = options[:context].downcase
167
+ @doc.root.add_child(context)
168
+ end
169
+ return context if !(options[:store] || options[:sync])
170
+
171
+ if options[:sync]
172
+ sync = context.at("./*[@name=#{_inspect(options[:sync].downcase)}]")
173
+ if !sync
174
+ sync = Nokogiri::XML::Node.new('sync', @doc)
175
+ sync['name'] = options[:sync].downcase
176
+ context.add_child(sync)
177
+ elsif sync.name != 'sync'
178
+ throw "#{options[:context].downcase}/#{options[:sync].downcase} added as a sync, but exists as a #{sync.name}"
179
+ end
180
+
181
+ return sync unless options[:store]
182
+ context = sync
183
+ end
184
+
185
+ store = context.at("./*[@name=#{_inspect(options[:store].downcase)}]")
186
+ if !store
187
+ store = Nokogiri::XML::Node.new('store', @doc)
188
+ store['name'] = options[:store].downcase
189
+ context.add_child(store)
190
+ elsif store.name != 'store'
191
+ throw "#{options[:context].downcase}/#{options[:sync] ? options[:sync] + '/' : ''}#{options[:store].downcase} added as a store, but exists as a #{store.name}"
192
+ end
193
+ return store
194
+ end
195
+
196
+ def set(node, params)
197
+ params.each_pair{|k, v|
198
+ node[k] = v
199
+ }
200
+ end
201
+
202
+ def initialize(root=nil)
203
+ root = '~/.config/syncevolution/' if root == '~'
204
+ root = File.expand_path(root)
205
+ @doc = Nokogiri::XML('<syncevolution/>')
206
+
207
+ Dir["#{root}/**/config.ini"].sort.reverse.each{|ini|
208
+ params = {}
209
+ settings = IniFile.load(ini)
210
+ settings.each { |section, parameter, value|
211
+ next if parameter == 'synthesisID'
212
+ raise section unless section == 'global'
213
+
214
+ value = 'eas-events' if parameter == 'backend' && value == 'ActiveSync Events'
215
+ value = 'eas-contacts' if parameter == 'backend' && value == 'ActiveSync Address Book'
216
+
217
+ params[parameter] = value.to_s.strip == '' ? nil : value
218
+ }
219
+
220
+ node = nil
221
+ case File.dirname(ini)[root.size, ini.size].gsub(/^\//, '')
222
+ when ''
223
+ node = @doc.root
224
+
225
+ when /^([^\/]+)$/
226
+ node = add(context: $1)
227
+
228
+ when /^([^\/]+)\/sources\/([^\/]+)$/
229
+ node = add(context: $1, store: $2)
230
+
231
+ when /^([^\/]+)\/peers\/([^\/]+)$/
232
+ node = add(context: $1, sync: $2)
233
+
234
+ when /^([^\/]+)\/peers\/([^\/]+)\/sources\/([^\/]+)$/
235
+ node = add(context: $1, sync: $2, store: $3)
236
+
237
+ else throw ini
238
+ end
239
+
240
+ set(node, params)
241
+ }
242
+ end
243
+
244
+ def to_xml
245
+ return @doc.to_xml
246
+ end
247
+
248
+ def backend_type(store)
249
+ case store['backend']
250
+ when 'file'
251
+ case store['databaseFormat']
252
+ when 'text/vcard' then return :contacts
253
+ when 'text/calendar' then return :events
254
+ end
255
+ when 'eas-events', 'ActiveSync Events' then return :events
256
+ when 'eas-contacts', 'ActiveSync Address Book' then return :contacts
257
+ end
258
+ throw "Cannot determine backend type from #{store.inspect}"
259
+ end
260
+
261
+ def verify
262
+ @doc.xpath("//sync[@name='target-config']/store[@sync != 'none' and @sync != 'disabled']").each{|store|
263
+ puts "target-config@#{store.parent.parent['name']} has set sync of #{store['name']} to #{store['sync']}, but sync is not used in a target-config"
264
+ }
265
+
266
+ @doc.xpath("//sync[@name='target-config' and @syncURL]").each{|sync|
267
+ puts "target-config@#{sync.parent['name']} has set an syncURL, but syncURL is not used in a target-config"
268
+ }
269
+
270
+ @doc.xpath("//sync").each{|sync|
271
+ if !sync['syncURL']
272
+ puts "sync-config #{sync['name']}@#{sync.parent['name']} has no syncURL" unless sync['name'] == 'target-config'
273
+ else
274
+ targetcontext = sync['syncURL'].gsub(/^local:\/\//i, '')
275
+
276
+ if targetcontext != sync['syncURL'].to_s
277
+ puts "sync-config #{sync['name']}@#{sync.parent['name']} set up for local sync with #{targetcontext}, which doesn't start with '@'" if targetcontext !~ /^@/
278
+ targetcontext.sub!(/^@/, '')
279
+ targetcontext.downcase!
280
+ if !@doc.at("//context[@name=#{_inspect(targetcontext)}]")
281
+ puts "sync-config #{sync['name']}@#{sync.parent['name']} set up for local sync with non-existent context #{targetcontext}"
282
+ elsif !@doc.at("//context[@name=#{_inspect(targetcontext)}]/sync[@name='target-config']")
283
+ puts "sync-config #{sync['name']}@#{sync.parent['name']} set up for local sync with non-existent target-config@#{targetcontext}"
284
+ else
285
+ sync.xpath('./store').each{|store|
286
+ local = @doc.at("//context[@name=#{_inspect(sync.parent['name'])}]/store[@name=#{_inspect(store['name'])}]")
287
+ if local
288
+ remote = @doc.at("//context[@name=#{_inspect(targetcontext)}]/store[@name=#{_inspect(store['uri'])}]")
289
+ if remote
290
+ lbe = backend_type(local)
291
+ rbe = backend_type(remote)
292
+ puts "sync-config #{sync['name']}@#{sync.parent['name']} connects store #{local['name']}@#{sync.parent['name']} of type #{lbe} to incompatible #{remote['name']}@#{targetcontext} of type #{rbe}" if lbe != rbe
293
+ else
294
+ puts "sync-config #{sync['name']}@#{sync.parent['name']} connects store #{store['name']}@#{sync.parent['name']} to non-existent #{store['uri']}@#{targetcontext}"
295
+ end
296
+ end
297
+ }
298
+ end
299
+ end
300
+ end
301
+ }
302
+
303
+ @doc.xpath("//sync/store").each{|store|
304
+ if !store.parent.at(".//store[@name=#{_inspect(store['name'])}]")
305
+ puts "sync-config #{store.parent['name']}@#{store.parent.parent['name']} references non-existent store #{store['name']}"
306
+ end
307
+ }
308
+ end
309
+ end
310
+ end
311
+
312
+ OPTS = Trollop::options do
313
+ opt :xml, "Output xml"
314
+ opt :import, "Output xml", :type => :string
315
+ opt :bash, "Output bash code"
316
+ opt :graphviz, "Output graphviz code"
317
+ opt :indent, "Indent bash code"
318
+ opt :verify, "Verify configuration"
319
+ end
320
+
321
+ SE = SyncEvolution::SyncEvolution.new('~')
322
+ SE.verify
323
+ SE.import(OPTS[:import]) if OPTS[:import]
324
+ puts SE.to_xml if OPTS[:xml]
325
+ puts SE.to_bash(OPTS[:indent]) if OPTS[:bash]
326
+ puts SE.to_gv if OPTS[:graphviz]
@@ -0,0 +1,3 @@
1
+ module SyncEvolution
2
+ VERSION = "0.1.5"
3
+ end
data/publish.sh ADDED
@@ -0,0 +1,11 @@
1
+ #!/bin/sh
2
+
3
+ set -e
4
+
5
+ git tag `ruby -e 'require "./lib/syncevolution/version.rb"; puts SyncEvolution::VERSION'`
6
+ git push
7
+ rm -f pkg/*
8
+ rake install
9
+ for f in pkg/*.gem; do
10
+ gem push $f
11
+ done
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'syncevolution/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "syncevolution"
8
+ spec.version = SyncEvolution::VERSION
9
+ spec.executables << 'se-config'
10
+ spec.authors = ["Emiliano Heyns"]
11
+ spec.email = ["Emiliano.Heyns@iris-advies.com"]
12
+ spec.description = %q{Manage SyncEvolution configuration}
13
+ spec.summary = %q{Manage SyncEvolution configuration}
14
+ spec.homepage = ""
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files`.split($/)
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency 'nokogiri'
23
+ spec.add_dependency 'inifile'
24
+ spec.add_dependency 'trollop'
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.3"
27
+ spec.add_development_dependency "rake"
28
+ end
@@ -0,0 +1,10 @@
1
+ require 'test/unit'
2
+ require 'syncevolution'
3
+
4
+ class TALTest < Test::Unit::TestCase
5
+ def test_basics
6
+ assert_equal "$".to_latex, '{\\textdollar}'
7
+ assert_equal "\u0027".to_latex, "'"
8
+ assert_equal "\u00D6".to_latex, '\"{O}'
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: syncevolution
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.5
5
+ platform: ruby
6
+ authors:
7
+ - Emiliano Heyns
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: inifile
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: trollop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Manage SyncEvolution configuration
84
+ email:
85
+ - Emiliano.Heyns@iris-advies.com
86
+ executables:
87
+ - se-config
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - Gemfile
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - bin/se-config
97
+ - lib/syncevolution/version.rb
98
+ - publish.sh
99
+ - syncevolution.gemspec
100
+ - test/syncevolution.rb
101
+ homepage: ''
102
+ licenses:
103
+ - MIT
104
+ metadata: {}
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubyforge_project:
121
+ rubygems_version: 2.2.2
122
+ signing_key:
123
+ specification_version: 4
124
+ summary: Manage SyncEvolution configuration
125
+ test_files:
126
+ - test/syncevolution.rb