cronedit 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,9 +3,15 @@
3
3
  #
4
4
  # Allows to manipulate crontab from comfortably from ruby code.
5
5
  # You can add/modify/remove (aka CRUD) named crontab entries individually with no effect on the rest of your crontab.
6
- # You can define cron entry definitions as standart text definitions <tt>'10 * * * * echo 42'</tt> or using Hash notation <tt>{:minute=>10, :command=>'echo 42'}</tt> (see CronEntry ::DEFAULTS)
7
- # Additionaly you can parse cron text definitions to Hash.
6
+ # You can define cron entry definitions as standard text definitions <tt>'10 * * * * echo 42'</tt> or using Hash notation <tt>{:minute=>10, :command=>'echo 42'}</tt> (see CronEntry ::DEFAULTS)
7
+ # Additionally you can parse cron text definitions to Hash.
8
+ #
9
+ # From other features: CronEdit allows you to make bulk updates of crontab; the same way you manipulate live crontab you can edit file or in-memory definitions and combine them arbitrarily.
8
10
  #
11
+ # ==Install
12
+ # * Available as gem: <tt>gem install cronedit</tt>
13
+ # * Project development page, downloads, forum, svn: http://rubyforge.org/projects/cronedit/
14
+ #
9
15
  # ==Usage
10
16
  #
11
17
  # Class methods offer quick crontab operations. Three examples:
@@ -13,55 +19,88 @@
13
19
  # CronEdit::Crontab.Add 'agent2', {:minute=>5, :command=>'echo 42'}
14
20
  # CronEdit::Crontab.Remove 'someId'
15
21
  #
16
- # or define a batch update and list the current content:
22
+ # Define a batch update and list the current content:
17
23
  #
18
- # cm = CronEdit:Crontab.new 'user'
24
+ # cm = CronEdit::Crontab.new 'user'
19
25
  # cm.add 'agent1', '5,35 0-23/2 * * * echo agent1'
20
26
  # ...
21
27
  # cm.add 'agent2', {:minute=>5, :command=>'echo 42'}
22
28
  # cm.commit
23
29
  # p cm.list
30
+ #
31
+ # see CronEdit::Crontab class for all available methods
32
+ #
33
+ # You can do a bulk merge (or removal) of definitions from a file using CronEdit::FileCrontab
34
+ # fc = FileCrontab.new 'example1.cron'
35
+ # Crontab.Merge fc
36
+ # p Crontab.List
37
+ # Crontab.Subtract fc
38
+ #
39
+ # Similary to CronEdit::FileCrontab you can you also CronEdit::DummyCrontab for in-memory crontabs.
40
+ # Above all, you can combine all three crontab implementations Crontab, FileCrontab, DummyCrontab arbitrarily.
41
+ #
42
+ # see <tt>test/examples/examples.rb</tt> for more examples !
24
43
  #
25
- # see Crontab for all available methods
26
44
  # ==Author
27
- # Viktor Zigo, http://7inf.com, All rights reserved. You can redistribute it and/or modify it under the same terms as Ruby.
28
- # (parts of the cron definition parsing code originaly by gotoken@notwork.org)
45
+ # Viktor Zigo, http://alephzarro.com, All rights reserved. You can redistribute it and/or modify it under the same terms as Ruby.
46
+ # (parts of the cronentry definition parsing code originally by gotoken@notwork.org)
47
+ #
48
+ # Sponsored by: http://7inf.com
29
49
  # ==History
30
- # version: 0.2.0
50
+ # * version: 0.3.0 2008-02-02
51
+ # ** keeps/survives full fromatting; FileCrontab; DummyCrontab; bulk addition and removal; clear crontab; more testcases; examples; and other
52
+ # * version: 0.2.0
31
53
  #
32
54
  # ==TODO
33
- # add more commands (clean crontab); lift some methods to class; put tests to separate file; make rake, make gem; implement addAllfrom
34
- # add Utils: getNext execution
55
+ # * add Utils: getNext execution
56
+ # * platform specific options (headers; vixiecron vs. dilloncron)
35
57
  module CronEdit
36
- self::VERSION = '0.2.0' unless defined? self::VERSION
37
-
58
+ self::VERSION = '0.3.0' unless defined? self::VERSION
59
+
60
+ # Main class that manipulates actual system cron. Additionally, it is the base class for other types of Crontab utils (FileCrontab, DummyCrontab)
38
61
  class Crontab
39
62
  # Use crontab for user aUser
40
63
  def initialize aUser = nil
41
64
  @user = aUser
65
+ @opts = {:close_input=>true, :close_output=>true}
42
66
  rollback()
43
67
  end
44
68
 
45
69
  class <<self
46
- # Add a new crontab entry definition. See instance add method
70
+ # Add a new crontab entry definition. (see instance method add).
47
71
  def Add anId, aDef
48
- cm = Crontab.new
72
+ cm = self.new
49
73
  entry = cm.add anId, aDef
50
74
  cm.commit
51
75
  entry
52
76
  end
53
77
 
54
- # Remove a crontab entry definition identified by anId from current crontab.
55
- def Remove anId
56
- cm = Crontab.new
57
- cm.remove anId
78
+ # Remove a crontab entry definitions identified by anIds from current crontab (see instance method remove).
79
+ def Remove *anIds
80
+ cm = self.new
81
+ cm.remove *anIds
58
82
  cm.commit
59
83
  end
60
84
 
61
85
  # List current crontab.
62
86
  def List
63
- Crontab.new.list
64
- end
87
+ self.new.list
88
+ end
89
+
90
+ #Merges this crontab with another one (see instance method merge).
91
+ def Merge anotherCrontab
92
+ cm=self.new
93
+ cm.merge anotherCrontab
94
+ cm.commit
95
+ end
96
+
97
+ #Removes crontab definitions of another crontab (see instance method subtract).
98
+ def Subtract anotherCrontab
99
+ cm=self.new
100
+ cm.subtract anotherCrontab
101
+ cm.commit
102
+ end
103
+
65
104
  end
66
105
 
67
106
  # Add a new crontab entry definition. Becomes effective only after commit().
@@ -70,51 +109,75 @@ module CronEdit
70
109
  # returns newly added CronEntry
71
110
  def add anId, aDef
72
111
  @adds[anId.to_s] = CronEntry.new( aDef )
112
+ @removals.delete anId.to_s
73
113
  end
74
114
 
75
- # Bulk addition of crontab entry definitions from stream (String, File, IO)
76
- def addAllFrom anIO
77
- #TODO: load file, parse it
78
- raise 'Not implemented yet'
115
+ # Bulk addition/merging of crontab definitions from another Crontab (FileCrontab, DummyCrontab, or a Crontab of another user)
116
+ def merge aCrontab
117
+ entries, lines = aCrontab.listFull
118
+ #todo: we throw the incoming lines away
119
+ #merge original data
120
+ @adds.merge! entries
121
+ #merge noncommited data as well
122
+ aCrontab.adds.each {|k,v| @adds[k]=v}
123
+ aCrontab.removals.each {|k,v| remove k}
124
+ self
79
125
  end
80
126
 
81
- # Remove a crontab entry definition identified by anId. Becomes effective only after commit().
82
- def remove anId
83
- @adds.delete anId.to_s
84
- @removals[anId.to_s]=anId.to_s
127
+ # Bulk subtraction/removal of crontab definitions from another Crontab (FileCrontab, DummyCrontab, or a Crontab of another user)
128
+ def subtract aCrontab
129
+ entries, lines = aCrontab.listFull
130
+ entries.each {|id,entry| remove id}
131
+ aCrontab.adds.each {|id,entry| remove id}
85
132
  end
86
133
 
134
+ # Remove a crontab entry definitions identified by anIds. Becomes effective only after commit().
135
+ def remove *anIds
136
+ anIds.each { |id|
137
+ @adds.delete id.to_s
138
+ @removals[id.to_s]=id.to_s
139
+ }
140
+ end
141
+
87
142
  # Merges the existing crontab with all modifications and installs the new crontab.
88
143
  # returns the merged parsed crontab hash
89
144
  def commit
90
145
  # merge crontab
91
- current = list()
92
- current.delete_if {|id, entry| @removals.include? id}
93
- merged = current.merge @adds
94
-
146
+ currentEntries, lines = listFull()
147
+ mergedEntries = nil
95
148
  # install it
96
- cmd = @user ? "crontab -u #{@user} -" : "crontab -"
97
- IO.popen(cmd,'w') {|io|
98
- merged.each {|id, entry|
99
- io.puts "# #{id}"
100
- io.puts entry
101
- }
102
- io.close
103
- }
149
+ io = getOutput
150
+ begin
151
+ mergedEntries = dumpCron currentEntries, lines, io
152
+ ensure
153
+ io.close if @opts[:close_output]
154
+ end
104
155
  # No idea why but without this any wait crontab reads and writes appears not synchronizes
105
156
  sleep 0.01
106
157
  #clean changes :)
107
158
  rollback()
108
- merged
159
+ mergedEntries
109
160
  end
110
-
161
+
111
162
  # Discards all modifications (since last commit, or creation)
112
163
  def rollback
113
164
  @adds = {}
114
165
  @removals = {}
115
166
  end
116
167
 
117
- # Prints out the items to be added and removed
168
+ # Clear crontab completely and immediately. Warning: no commit needed (no rollback anymore)
169
+ def clear!
170
+ rollback
171
+ # we dont do it using crontab command (-r , -d) because it differs on various platfoms (vixiecron vs. dilloncron)
172
+ io = getOutput
173
+ begin
174
+ io << ''
175
+ ensure
176
+ io.close if @opts[:close_output]
177
+ end
178
+ end
179
+
180
+ # A helper method that prints out the items to be added and removed
118
181
  def review
119
182
  puts "To be added: #{@adds.inspect}"
120
183
  puts "To be removed: #{@removals.keys.inspect}"
@@ -123,43 +186,225 @@ module CronEdit
123
186
  # Read the current crontab and parse it
124
187
  # returns a Hash (entry id or index)=>CronEntry
125
188
  def list
126
- cmd = @user ? "crontab -u #{@user} -l" : "crontab -l"
127
- IO.popen(cmd) {|io|
128
- return parseCrontab(io)
129
- }
189
+ res = listFull
190
+ res ? res[0] : nil
130
191
  end
131
192
 
193
+ # Read the current crontab and parse it, keeping the format
194
+ # returns a [entires, lines] where entries = Hash (entry id or index)=>CronEntry; lines = Array of Strings of EntryPlaceholders
195
+ def listFull
196
+ io = getInput
197
+ begin
198
+ return parseCrontabFull(io)
199
+ ensure
200
+ io.close if @opts[:close_input]
201
+ end
202
+ end
203
+
132
204
  # Lists raw content from crontab
133
205
  # returns array of text lines
134
206
  def listRaw
135
- cmd = @user ? "crontab -u #{@user} -l" : "crontab -l"
136
- IO.popen(cmd) {|io|
137
- entries = io.readlines
138
- return (entries.first =~ /^no/).nil? ? entries : []
139
- }
207
+ io = getInput
208
+ begin
209
+ entries = io.readlines
210
+ return (entries.first =~ /^no/).nil? ? entries : [] #returns empty list if no entry
211
+ ensure
212
+ io.close if @opts[:close_input]
213
+ end
140
214
  end
141
-
215
+
216
+ #Set alternative I/O for reading/writing cron entries. If set, the respective stream will be used instead of calling system 'crontab'
217
+ def setIO anInput, anOutput
218
+ @input = anInput
219
+ @output = anOutput
220
+ self
221
+ end
222
+ protected
223
+ # Parses cron definition from a stream
224
+ # returns Hash of id->CrontEntries
142
225
  def parseCrontab anIO
226
+ res = parseCrontabFull(anIO)
227
+ res ? res[0] : nil
228
+ end
229
+
230
+ # Parses cron definition from a stream
231
+ # returns [hash of CronEntries, Array of all lines (and placeholders)]
232
+ def parseCrontabFull anIO
233
+ lines=[]
143
234
  entries = {}
144
235
  idx = 0
145
236
  id = nil
146
237
  anIO.each_line { |l|
147
238
  l.strip!
239
+ lines << l if l.empty? # keep empty lines and opts[:keepformat]
148
240
  next if l.empty?
149
- return {} unless (l =~ /^no/).nil?
150
- if l=~/^#/
151
- id = l[1, l.size-1].strip
152
- else
153
- key = id.nil? ? (idx+=1) : id
241
+ return [entries, lines] unless (l =~ /^no crontab/).nil? # if crontab contains no schedule it returns 'no crontab for ...'
242
+
243
+ if l=~/^#/ #is it comment or ID ?
244
+ id = EntryPlaceholder.Create l
245
+ lines << l unless id #add the raw line to the lines if no id
246
+ elsif not (l =~ /^\w+\ *=/).nil? # is it variable definition ?
247
+ lines << l
248
+ id = nil
249
+ else # it should be cron entry definition
250
+ id = EntryPlaceholder.new(idx+=1) unless id # if there is no id generate an anonymous id
251
+ lines << id #add placeholder to lines
252
+ key = id.id
154
253
  entries[key.to_s]=l
155
254
  id = nil
156
255
  end
157
256
  }
158
- entries
257
+ [entries, lines]
159
258
  end
259
+
260
+ private
261
+ def dumpCron anEntries, aLines, anIO
262
+ currentEntries=anEntries.clone
263
+ currentEntries.delete_if {|id, entry| @removals.include? id}
264
+ mergedEntries = currentEntries.merge @adds
265
+
266
+ usedIds=[]
267
+ # dump lines and placeholders
268
+ aLines.each { |line|
269
+ if line.respond_to? :mergedump
270
+ usedIds << line.mergedump( anIO, mergedEntries )
271
+ else
272
+ anIO.puts line.to_s
273
+ end
274
+ }
275
+ #dump new entries
276
+ restIds = mergedEntries.keys - usedIds
277
+ restLines = restIds.map {|id| EntryPlaceholder.new id}
278
+ restLines .each {|line| line.mergedump anIO, mergedEntries }
279
+ mergedEntries
280
+ end
281
+
282
+ protected
283
+ # override this if you want to get the cron definition from other sources
284
+ def getInput
285
+ if @input
286
+ @input
287
+ else
288
+ cmd = @user ? "crontab -u #{@user} -l" : "crontab -l"
289
+ IO.popen(cmd)
290
+ end
291
+ end
292
+
293
+ # override this if you want to write the cron definition from other destination
294
+ def getOutput
295
+ if @output
296
+ @output
297
+ else
298
+ cmd = @user ? "crontab -u #{@user} -" : "crontab -"
299
+ IO.popen(cmd,'w')
300
+ end
301
+ end
302
+
303
+ attr_reader :adds, :removals
160
304
  end
161
305
 
306
+ # FileCrontab allows you to read in/write out crontab definitions from/to files. You might want to use it for merging some enire file to crontab.
307
+ # If '-' is given instead of input file / output file the definitions are streamed from/into STDIN/STDOUT.
308
+ #
309
+ # Example:
310
+ # fc = FileCrontab.new 'crontemplate.txt', '/tmp/crontest.txt'
311
+ # fc.add 'agent', '59 * * * * echo "modified"'
312
+ # fc.remove 'agent2'
313
+ # fc.commit
314
+ #
315
+ # or create a crontab fron scratch and output to STDOUT
316
+ # fc = FileCrontab.new nil, '-'
317
+ # fc.add 'agent', '59 * * * * echo "modified"'
318
+ # fc.commit
319
+
320
+
321
+ class FileCrontab < Crontab
322
+ # anInputFile and anOutputFile can be either filename - to r/w the file, '-' to use STDIN/STDOUT, or nil for no input/output
323
+ def initialize anInputFile = nil, anOutputFile = nil
324
+ super nil
325
+ @input = anInputFile
326
+ @opts[:close_input] = false if @input=='-'
327
+ @output = anOutputFile
328
+ @opts[:close_output] = false if @output=='-'
329
+ end
330
+ protected
331
+ def getInput
332
+ case @input
333
+ when nil then StringIO.new
334
+ when '-' then STDIN
335
+ else File.open( @input, 'r' )
336
+ end
337
+ end
338
+
339
+ def getOutput
340
+ case @output
341
+ when nil then StringIO.new
342
+ when '-' then STDOUT
343
+ else File.open( @output, 'w' )
344
+ end
345
+ end
346
+ end
347
+
348
+ # DummyCrontab allows you to create crontab definitions from scratch and dump them out as string. (Or combine them with other Crontab definitions)
349
+ #
350
+ # Example:
351
+ # fc = DummyCrontab.new
352
+ # fc.add 'agent1', '59 * * * * echo "agent1"'
353
+ # fc.add 'agent2', {:hour=>'2',:command=>'echo "huh"'}
354
+ # fc.commit
355
+ # puts fc
356
+ class DummyCrontab < Crontab
357
+ require 'stringio'
358
+ def initialize
359
+ super nil
360
+ @buffer = StringIO.new
361
+ end
362
+
363
+ def to_s
364
+ @buffer.string
365
+ end
366
+
367
+ protected
368
+ def getInput
369
+ #loopback - for multiple use
370
+ StringIO.new @buffer.string
371
+ end
372
+
373
+ def getOutput
374
+ @buffer = StringIO.new
375
+ end
376
+ end
377
+ # When the Crontab object reads in (parse) all lines, placeholder appears where a Crontab entry with an id was detected
378
+ class EntryPlaceholder
379
+ attr_reader :id
380
+ def self.Create(aLine)
381
+ id = ScanId aLine
382
+ return id ? EntryPlaceholder.new(id) : nil
383
+ end
384
+ def self.ScanId aStr
385
+ aStr.scan(/^\#\#__(.*)__/).flatten.first
386
+ end
387
+ def self.GenerateId anId
388
+ "##__#{anId}__"
389
+ end
390
+ def initialize anId
391
+ @id = anId.to_s
392
+ end
393
+
394
+ #returns used id
395
+ def mergedump anIO, anEntryHash
396
+ entry = anEntryHash[@id]
397
+ if entry
398
+ anIO.puts "#{EntryPlaceholder.GenerateId @id}\n"
399
+ anIO.puts "#{entry}\n"
400
+ return @id
401
+ else
402
+ return nil
403
+ end
404
+ end
405
+ end
162
406
 
407
+ #A cron entry ...
163
408
  class CronEntry
164
409
  DEFAULTS = {
165
410
  :minute => '*',
@@ -262,73 +507,4 @@ end
262
507
 
263
508
  if __FILE__ == $0
264
509
  include CronEdit
265
-
266
-
267
-
268
-
269
- ents = [
270
- CronEntry.new( "5,35 0-23/2 * * * echo 123" ),
271
- CronEntry.new,
272
- CronEntry.new( {:minute=>5, :command=>'echo 42'} ),
273
- ]
274
- ents.each { |e| puts e }
275
-
276
- e= CronEntry.new( "5,35 0-23/2 * * * echo 123" )
277
- puts "Minute: #{e[:minute].inspect}"
278
- puts "Hour: #{e[:hour].inspect}"
279
- puts "Day: #{e[:day].inspect}"
280
- p e.to_s
281
- p e.to_hash
282
- p e.to_raw
283
-
284
- begin
285
- CronEntry.new( {:minuteZ=>5, :command=>'echo 42'} )
286
- puts "Failure failed"
287
- rescue
288
- end
289
-
290
- begin
291
- CronEntry.new( "1-85 2 * * * echo 123" )
292
- puts "Failure failed"
293
- rescue CronEntry::FormatError
294
- end
295
-
296
- crontabTest=%Q{
297
- 5,35 0-23/2 * * * echo 123
298
- #agent1
299
- 3 * * * * echo agent1
300
-
301
-
302
- #agent2
303
- 3 * * * * echo agent2
304
- #ignored comment
305
- #agent1
306
- 3 * * * * echo agent3
307
- }
308
- puts "Crontab raw: #{Crontab.new.listRaw.inspect}"
309
- puts "Crontab: #{Crontab.new.list.inspect}"
310
- puts "Crontab test: #{Crontab.new.parseCrontab(crontabTest).inspect }"
311
-
312
- #rollback test
313
- cm = Crontab.new
314
- cm. add 'agent1', '5,35 0-23/2 * * * "echo 123" '
315
- cm.remove "agent2"
316
- cm.review
317
- cm.rollback
318
-
319
- #commit test
320
- cm = Crontab.new
321
- cm. add "agent1", "5,35 0-23/2 * * * echo agent1"
322
- cm. add "agent2", "0 2 * * * echo agent2"
323
- cm.commit
324
- current=cm.list
325
- puts "New crontab 1: #{current.inspect}"
326
-
327
- cm = Crontab.new
328
- cm. add "agent1", '59 * * * * echo "modified agent1"'
329
- cm.remove "agent2"
330
- cm.commit
331
- current=Crontab.List
332
- puts "New crontab 2: #{current.inspect}"
333
- raise "Assertion" unless current=={'agent1'=> '59 * * * * echo "modified agent1"'}
334
510
  end
@@ -2,14 +2,37 @@ $:.unshift File.join(File.dirname(__FILE__),'..','lib')
2
2
 
3
3
  require 'test/unit'
4
4
  require 'cronedit.rb'
5
+ require 'stringio'
5
6
  include CronEdit
6
7
 
8
+ class Class
9
+ def publicize_methods
10
+ saved_private_instance_methods = self.private_instance_methods
11
+ saved_protected_instance_methods = self.protected_instance_methods
12
+ self.class_eval { public(*saved_private_instance_methods) }
13
+ self.class_eval { public(*saved_protected_instance_methods) }
14
+ yield
15
+ self.class_eval { protected(*saved_protected_instance_methods) }
16
+ self.class_eval { private(*saved_private_instance_methods) }
17
+ end
18
+ end
19
+
7
20
  class CronEdit_test < Test::Unit::TestCase
8
- # def setup
9
- # end
10
- #
11
- # def teardown
12
- # end
21
+ def setup
22
+ #backup crontab
23
+ `crontab -l > /tmp/crontab.bak`
24
+ end
25
+
26
+ def teardown
27
+ #restore crontab
28
+ `crontab /tmp/crontab.bak`
29
+ end
30
+
31
+ def test_idParsing
32
+ idin=['##__id1__', ' ##__id3__', '#the end comment','kuk','##___id4___','###__id5__']
33
+ out = idin.map {|l| CronEdit::EntryPlaceholder.ScanId(l)}
34
+ assert_equal(['id1',nil,nil,nil,'_id4_',nil], out, "idParsing")
35
+ end
13
36
 
14
37
  def test_creation
15
38
  e = CronEntry.new( "5,35 0-23/2 * * * echo 123" )
@@ -29,11 +52,12 @@ class CronEdit_test < Test::Unit::TestCase
29
52
  assert_equal( "0,2,4,6,8,10,12,14,16,18,20,22", e[:hour])
30
53
  assert_equal( "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31", e[:day])
31
54
  end
32
- def test_wrongformat
55
+
56
+ def test_wrongformat
33
57
  assert_raise(CronEntry::FormatError){
34
58
  CronEntry.new( "1-85 2 * * * echo 123" )
35
59
  }
36
- end
60
+ end
37
61
 
38
62
  def test_wrongconfog
39
63
  assert_raise(RuntimeError){
@@ -42,29 +66,39 @@ class CronEdit_test < Test::Unit::TestCase
42
66
  end
43
67
 
44
68
  def test_zip
45
- crontabTest=%Q{
46
- 5,35 0-23/2 * * * echo 123
47
- #agent1
48
- 3 * * * * echo agent1
69
+ Crontab.publicize_methods {
70
+ crontabTest=%Q{
71
+ 5,35 0-23/2 * * * echo 123
72
+ ##__agent1__
73
+ 3 * * * * echo agent1
49
74
 
50
75
 
51
- #agent2
52
- 3 * * * * echo agent2
53
- #ignored comment
54
- #agent1
55
- 3 * * * * echo agent3
56
- }
57
- expected = {"agent1"=>"3 * * * * echo agent3", "agent2"=>"3 * * * * echo agent2", "1"=>"5,35 0-23/2 * * * echo 123"}
58
- assert_equal( expected, Crontab.new.parseCrontab(crontabTest), 'parsing of crontab file')
76
+ ##__agent2__
77
+ 3 * * * * echo agent2
78
+ #ignored comment
79
+ ##__agent1__
80
+ 3 * * * * echo agent3
81
+ }
82
+ expected = {"agent1"=>"3 * * * * echo agent3", "agent2"=>"3 * * * * echo agent2", "1"=>"5,35 0-23/2 * * * echo 123"}
83
+ assert_equal( expected, Crontab.new.parseCrontab(crontabTest), 'parsing of crontab file')
84
+ }
59
85
  end
60
86
 
61
87
  def test_emptycrontab
62
- assert_equal( [], Crontab.new.listRaw )
63
- assert_equal( {}, Crontab.new.list )
88
+ input = 'no crontab for user'
89
+ assert_equal( {}, Crontab.new.setIO(StringIO.new(input),nil).list )
90
+ end
91
+
92
+ def test_clear
93
+ Crontab.new.clear!
94
+ entries, lines = Crontab.new.listFull
95
+ assert_equal( {}, entries, "Entries" )
96
+ Crontab.new.list
64
97
  end
65
98
 
66
99
  def test_rollback
67
100
  #rollback test
101
+ `crontab -r`; `crontab -d`
68
102
  assert_equal( {}, Crontab.new.list, 'precondition' )
69
103
  cm = Crontab.new
70
104
  cm. add 'agent1', '5,35 0-23/2 * * * "echo 123" '
@@ -74,7 +108,68 @@ class CronEdit_test < Test::Unit::TestCase
74
108
  assert_equal( {}, Crontab.new.list )
75
109
  end
76
110
 
111
+ def test_complex
112
+ Crontab.publicize_methods {
113
+ File.open( File.join(File.dirname(__FILE__), 'testcron.txt') ) { |f|
114
+ content = f.read
115
+ f.rewind
116
+ entries, lines = Crontab.new.parseCrontabFull f
117
+ assert_equal 12, lines.length, "Lines"
118
+ assert_equal ['1','2','3','test'].sort, entries.keys.sort, "Entries"
119
+ #puts "--------------Lines: \n#{lines.join("\n") }"
120
+ #puts "--------------entries: \n#{entries.map {|k,v| "#{k}=>#{v}\n"} }"
121
+ output = StringIO.new
122
+ Crontab.new.dumpCron entries, lines, output
123
+ #puts "OUT: #{output.string}"
124
+ #loop
125
+ output.rewind
126
+ entries2, lines2 = Crontab.new.parseCrontabFull output
127
+ assert_equal 12, lines2.length, "Lines2"
128
+ assert_equal ['1','2','3','test'].sort, entries2.keys.sort, "Entries2"
129
+ }
130
+ }
131
+ end
132
+
133
+ def test_complex2
134
+ crontabTest=%Q{
135
+ MAIL = user
136
+ ##__agent1__
137
+ 3 * * * * echo agent1
138
+
139
+
140
+ #comment
141
+ 3 * * * * echo agent2
142
+
143
+ ##__blankId__
144
+ #anonymous
145
+ 3 * * * * echo anonymous
146
+ }
147
+ ct = Crontab.new
148
+ ct.setIO StringIO.new(crontabTest), nil
149
+ entries, lines = ct.listFull
150
+ assert_equal 11, lines.length, "Lines"
151
+ assert_equal ['agent1','1','2'].sort, entries.keys.sort, "Entries"
152
+ ## puts "--------------Lines: \n#{lines.join("\n") }"
153
+ ## puts "--------------entries: \n#{entries.map {|k,v| "#{k}=>#{v}\n"} }"
154
+
155
+ ct.add 'agent1', '5,35 0-23/2 * * * echo agent1' #overwriting
156
+ ct.add 'agent3', '0 2 * * * echo agent3' #new agent
157
+ ct.remove '2'
158
+ output = StringIO.new
159
+ ct.setIO StringIO.new(crontabTest), output
160
+ ct.commit
161
+ ## puts '-'*40 + "\n#{output.string}\n" + '-'*40
162
+ # loop
163
+ ct.setIO StringIO.new(output.string), nil
164
+ entries, lines = ct.listFull
165
+ assert_equal 11, lines.length, "Lines"
166
+ assert_equal ['agent1','agent3','1'].sort, entries.keys.sort, "Entries"
167
+ ## puts "--------------Lines: \n#{lines.join("\n") }"
168
+ ## puts "--------------entries: \n#{entries.map {|k,v| "#{k}=>#{v}\n"} }"
169
+ end
170
+
77
171
  def test_commit
172
+ `crontab -r`; `crontab -d`
78
173
  assert_equal( {}, Crontab.new.list, 'precondition' )
79
174
  cm = Crontab.new
80
175
  cm. add "agent1", "5,35 0-23/2 * * * echo agent1"
@@ -96,4 +191,105 @@ class CronEdit_test < Test::Unit::TestCase
96
191
  assert_equal( {}, Crontab.List, 'precondition' )
97
192
  end
98
193
 
194
+ def test_classMethods
195
+ Crontab.List
196
+ end
197
+
198
+ def test_filecrontab
199
+ fc = FileCrontab.new File.join(File.dirname(__FILE__), 'testcron.txt'), '/tmp/crontest.txt'
200
+ fc.add 'file', '59 * * * * echo "file"'
201
+ fc.remove 2, 3
202
+ fc.commit
203
+ # check it
204
+ entries, lines = FileCrontab.new( '/tmp/crontest.txt',nil).listFull
205
+ assert_equal 11, lines.length, "Lines"
206
+ assert_equal ['1','test','file'].sort, entries.keys.sort, "Entries"
207
+ end
208
+
209
+ def test_filecrontab2
210
+ fc = FileCrontab.new nil, '-'
211
+ fc.add 'agent', '59 * * * * echo 123'
212
+ fc.commit
213
+ #todo: how to test STDOUT
214
+
215
+ fc = FileCrontab.new nil, '/tmp/crontest.txt'
216
+ fc.add 'agent', '59 * * * * echo 123'
217
+ fc.commit
218
+ File.open( '/tmp/crontest.txt') {
219
+ |f|
220
+ assert_equal f.read, "##__agent__\n59 * * * * echo 123\n"
221
+ }
222
+ end
223
+
224
+ def test_dummycrontab
225
+ fc = DummyCrontab.new
226
+ fc.add 'agent1', '59 * * * * echo "agent1"'
227
+ fc.add 'agent2', {:hour=>'2',:command=>'echo "huh"'}
228
+ fc.commit
229
+ #check 1
230
+ output = fc.to_s
231
+ entries, lines = Crontab.new.setIO(StringIO.new(output),nil).listFull
232
+ assert_equal 2, lines.length, "Lines"
233
+ assert_equal ['agent1','agent2'].sort, entries.keys.sort, "Entries"
234
+ #continue editing
235
+ fc.remove 'agent2'
236
+ fc.commit
237
+ #check 2
238
+ output = fc.to_s
239
+ entries, lines = Crontab.new.setIO(StringIO.new(output),nil).listFull
240
+ assert_equal 1, lines.length, "Lines"
241
+ assert_equal ['agent1'].sort, entries.keys.sort, "Entries"
242
+ end
243
+
244
+ def test_merge
245
+ fc1 = DummyCrontab.new
246
+ fc1.add 'agent1', '59 * * * * echo "agent1"'
247
+ fc1.add 'agent2', {:hour=>'2',:command=>'echo "huh"'}
248
+ fc1.commit
249
+
250
+ fc2 = DummyCrontab.new
251
+ fc2.add 'agent3', '59 * * * * echo "agent3"'
252
+ fc2.remove 'agent2'
253
+
254
+ fc1.merge fc2
255
+ fc1.commit
256
+ output = fc1.to_s
257
+ entries, lines = Crontab.new.setIO(StringIO.new(output),nil).listFull
258
+ assert_equal 2, lines.length, "Lines"
259
+ assert_equal ['agent1','agent3'].sort, entries.keys.sort, "Entries"
260
+ end
261
+
262
+ def test_mergeWithFile
263
+ fc1 = FileCrontab.new File.join(File.dirname(__FILE__), 'testcron.txt')
264
+
265
+ fc2 = DummyCrontab.new
266
+ fc2.add 'agent3', '59 * * * * echo "agent3"'
267
+
268
+ fc2.merge fc1
269
+ fc2.commit
270
+ output = fc2.to_s
271
+ ## puts "\n\n"+output
272
+ entries, lines = Crontab.new.setIO(StringIO.new(output),nil).listFull
273
+ assert_equal 5, lines.length, "Lines"
274
+ assert_equal ['1','2','3','test','agent3'].sort, entries.keys.sort, "Entries"
275
+ end
276
+
277
+ def test_subtractFile
278
+ fc1 = DummyCrontab.new
279
+ fc1.add 'test', '59 * * * * echo "whatever"'
280
+ fc1.add 'test2', '59 * * * * echo "whatever"'
281
+ fc1.add '3', '59 * * * * echo "whatever"'
282
+ fc1.commit
283
+
284
+
285
+ fc2= FileCrontab.new File.join(File.dirname(__FILE__), 'testcron.txt')
286
+ fc1.subtract fc2
287
+ fc1.commit
288
+ output = fc1.to_s
289
+ ## puts "\n\n"+output
290
+ entries, lines = Crontab.new.setIO(StringIO.new(output),nil).listFull
291
+ assert_equal 1, lines.length, "Lines"
292
+ assert_equal ['test2'].sort, entries.keys.sort, "Entries"
293
+ end
294
+
99
295
  end
@@ -0,0 +1,4 @@
1
+ ##__echo1__
2
+ * * * * * echo 1 >> /tmp/echo.log
3
+ ##__echo2__
4
+ * * * * * echo 2 >> /tmp/echo.log
@@ -0,0 +1,51 @@
1
+ $:.unshift File.join(File.dirname(__FILE__),'../..','lib')
2
+ require 'cronedit.rb'
3
+ include CronEdit
4
+
5
+ # 1. Direct cron modifications
6
+ Crontab.Add 'agent1', '5,35 0-23/2 * * * echo agent1'
7
+ Crontab.Add 'agent2', {:minute=>5, :command=>'echo 42'}
8
+ p Crontab.List
9
+ Crontab.Remove 'agent1', 'agent2'
10
+
11
+ # 2. Batch modification
12
+ cm = Crontab.new
13
+ cm.add :agent1, '5,35 0-23/2 * * * echo "agent1" >> /tmp/agent/log'
14
+ cm.add :agent2, {:minute=>5, :command=>'echo 42'}
15
+ cm.commit
16
+ p cm.list
17
+
18
+
19
+ #3. Delete crontab completely
20
+ cm.clear!
21
+
22
+ # 4. Bulk merge from file using FileCrontab
23
+ fc = FileCrontab.new File.join(File.dirname(__FILE__), 'example1.cron')
24
+ # Add all entries from file
25
+ Crontab.Merge fc
26
+ p Crontab.List
27
+ # An an entry manually
28
+ Crontab.Add :test, '* * * * * test'
29
+ # Remove all entries defined in the file
30
+ Crontab.Subtract fc
31
+ p Crontab.List
32
+
33
+ # 5. Read from file - output to STDOUT (you can use a file instead of STDOUT)
34
+ fc = FileCrontab.new File.join(File.dirname(__FILE__), 'example1.cron'), '-'
35
+ fc.add :agent2, {:minute=>5, :command=>'echo 42'}
36
+ fc.commit
37
+
38
+
39
+ # 6. DummyCrontab - in memory crontab
40
+ dc = DummyCrontab.new
41
+ dc.add 'agent1', '59 * * * * echo "agent1"'
42
+ dc.add 'agent2', {:hour=>'2',:command=>'echo "huh"'}
43
+ dc.commit
44
+ puts dc
45
+
46
+ # 7. Of course you can combine (merge/subtract) all types of Crontabs, ie. Crontab, FileCrontab, DummyCrontab
47
+
48
+ # 8. In case you need to read/write crontab definition from/to yet another source (stream) use setIO method
49
+ require 'stringio'
50
+ output = "#read from DB"
51
+ p Crontab.new.setIO(StringIO.new(output),nil).list
@@ -0,0 +1,14 @@
1
+ MAILTO=user
2
+ SHELL = /bin/bash
3
+
4
+ # * * * * wed,sat /usr/bin/ruby -C /sync test.rb run
5
+ 0 20 * * * /tmp/agent1
6
+ # this is a comment - to be preserved
7
+
8
+ ##__test__
9
+ 2 * * * * /verycomplicated/script 123 >> /tmp/xml_server.log 2>&1
10
+
11
+ ##__test2__
12
+ # id is lost?
13
+ 0 21 * * * /sync/exec_with_env.sh jobstream_generate.rb --lang en --fulladmin run
14
+ 0 21 * * * /sync/exec_with_env.sh sync_master.rb --monitconf /etc/monitrc-200 --downtimeFrom '22:00' --downtimeTo '23:00' run
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: cronedit
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.2.0
7
- date: 2007-12-09 00:00:00 +01:00
6
+ version: 0.3.0
7
+ date: 2008-02-04 00:00:00 +01:00
8
8
  summary: CronEdit is a Ruby editor library for crontab.
9
9
  require_paths:
10
10
  - lib
@@ -30,6 +30,11 @@ authors:
30
30
  - Viktor Zigo
31
31
  files:
32
32
  - lib/cronedit.rb
33
+ - test/testcron.txt
34
+ - test/examples
35
+ - test/examples/example1.cron
36
+ - test/examples/examples.rb
37
+ - test/cronedit_test.rb
33
38
  test_files:
34
39
  - test/cronedit_test.rb
35
40
  rdoc_options: []