rb.rotate 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,415 @@
1
+ # encoding: utf-8
2
+
3
+ require "fileutils"
4
+ require "rb.rotate/state"
5
+ require "rb.rotate/mail"
6
+
7
+ module RbRotate
8
+ module StorageModule
9
+
10
+ ##
11
+ # Represents an item of some entry in storage.
12
+ #
13
+
14
+ class Item
15
+
16
+ ##
17
+ # Parent entry.
18
+ #
19
+
20
+ @entry
21
+
22
+ ##
23
+ # Indentifier of the item.
24
+ #
25
+
26
+ @identifier
27
+ attr_writer :identifier
28
+
29
+ ##
30
+ # Full path of the item.
31
+ #
32
+
33
+ @path
34
+
35
+ ##
36
+ # Data of the item.
37
+ #
38
+
39
+ @data
40
+
41
+ ##
42
+ # Constructor.
43
+ #
44
+
45
+ def initialize(entry, identifier = nil, path = nil)
46
+ @entry = entry
47
+ @identifier = identifier
48
+ @path = path
49
+
50
+ # Loads data
51
+ self.load_data!
52
+ end
53
+
54
+ ##
55
+ # Returns data.
56
+ #
57
+
58
+ def load_data!
59
+ if @data.nil?
60
+ if not @path.nil?
61
+ @data = State::get.archive.file(@path)
62
+ end
63
+
64
+ # Default
65
+ if @path.nil? or @data.nil?
66
+ @data = {
67
+ :date => Time::now,
68
+ :compression => false
69
+ }
70
+ end
71
+ end
72
+ end
73
+
74
+ ##
75
+ # Rotates itself.
76
+ #
77
+
78
+ def rotate!
79
+ if @entry.storage.numeric_identifier? and (self.identifier.kind_of? Numeric)
80
+ ##
81
+ # Unregisters old filename, increases counter
82
+ # and register it again.
83
+ #
84
+
85
+ self.unregister!
86
+
87
+ if self.exists?
88
+ old_path = self.path
89
+ self.identifier += 1
90
+
91
+ self.rebuild_path!
92
+ self.prepare_directory!
93
+ FileUtils.move(old_path, self.path)
94
+
95
+ self.register!
96
+ end
97
+ end
98
+
99
+ return self
100
+ end
101
+
102
+ ##
103
+ # Returns state object.
104
+ #
105
+
106
+ def state
107
+ @entry.file.state
108
+ end
109
+
110
+ ##
111
+ # Indicates, item still exists in storage.
112
+ #
113
+
114
+ def exists?
115
+ ::File.exists? self.path
116
+ end
117
+
118
+ ##
119
+ # Registers itself.
120
+ #
121
+
122
+ def register!
123
+ State::archive.register_file(self.path, @data)
124
+ end
125
+
126
+ ##
127
+ # Unregisters itself.
128
+ #
129
+
130
+ def unregister!
131
+ State::archive.unregister_file(self.path)
132
+ end
133
+
134
+ ##
135
+ # Removes itself.
136
+ #
137
+
138
+ def remove!
139
+ self.unregister!
140
+
141
+ # Eventually mails it if required
142
+ if @entry.storage.directory.configuration[:recycle].to_sym == :mail
143
+ self.mail!
144
+ end
145
+
146
+ FileUtils.remove(self.path)
147
+ end
148
+
149
+ ##
150
+ # Mails the file.
151
+ #
152
+
153
+ def mail!
154
+ to = @entry.storage.directory.configuration[:mail]
155
+ self.decompress!
156
+
157
+ require "etc"
158
+ require "socket"
159
+
160
+ Mail::send(
161
+ :from => Etc.getlogin.dup << "@" << Socket.gethostname,
162
+ :to => to,
163
+ :subject => Socket.gethostname << " : log : " << self.path,
164
+ :body => ::File.read(self.target_path)
165
+ )
166
+
167
+ self.compress!
168
+ end
169
+
170
+ ##
171
+ # Returns identifier.
172
+ #
173
+
174
+ def identifier
175
+ if @identifier.nil?
176
+ if @entry.storage.numeric_identifier?
177
+ @identifier = 1
178
+ else
179
+ item_identifier = @entry.storage.item_identifier
180
+
181
+ if item_identifier.to_sym == :date
182
+ format = "%Y%m%d.%H%M"
183
+ else
184
+ format = item_identifier
185
+ end
186
+
187
+ @identifier = Time::now.strftime(format)
188
+ end
189
+ end
190
+
191
+ return @identifier
192
+ end
193
+
194
+ ##
195
+ # Returns path.
196
+ #
197
+
198
+ def path
199
+ if @path.nil?
200
+ self.rebuild_path!
201
+ end
202
+
203
+ return @path
204
+ end
205
+
206
+ ##
207
+ # Generates target (without compression extension) path
208
+ # from path.
209
+ #
210
+
211
+ def target_path
212
+ if self.compressed?
213
+ extension = self.compression[:extension]
214
+ result = self.path[0...-(extension.length + 1)]
215
+ else
216
+ result = self.path
217
+ end
218
+
219
+ return result
220
+ end
221
+
222
+ ##
223
+ # Rebuilds path.
224
+ #
225
+
226
+ def rebuild_path!
227
+ directory = @entry.storage.directory
228
+ configuration = directory.configuration
229
+ @path = configuration[:storage].dup << "/"
230
+
231
+ # Adds archive subdirectories structure if necessary
232
+ recursive = configuration[:recursive]
233
+ if (recursive.kind_of? TrueClass) or (configuration[:recursive].to_sym != :flat)
234
+ relative_path = directory.relative_path
235
+ if relative_path != ?.
236
+ @path << relative_path << "/"
237
+ end
238
+ end
239
+
240
+ # Adds filename
241
+ @path << self.state.name.to_s << "." << self.identifier.to_s
242
+
243
+ # Adds extension if necessary
244
+ if not self.state.extension.nil?
245
+ @path << "." << self.state.extension
246
+ end
247
+
248
+ # Adds compression extension if necessary
249
+ if self.compressed?
250
+ @path << "." << self.compression[:extension]
251
+ end
252
+ end
253
+
254
+ ##
255
+ # Prepares directory.
256
+ #
257
+
258
+ def prepare_directory!
259
+ directory = FileUtils.mkdir_p(::File.dirname(self.path)).first
260
+ State::archive.register_directory(directory)
261
+ end
262
+
263
+ ##
264
+ # Allocates new record.
265
+ #
266
+
267
+ def allocate(method)
268
+
269
+ # Prepares directory
270
+ self.prepare_directory!
271
+
272
+ # Allocates by required action
273
+ case method
274
+ when :copy
275
+ FileUtils.copy(@entry.file.path, self.path)
276
+ when :move
277
+ FileUtils.move(@entry.file.path, self.path)
278
+ when :append
279
+ self.append!(:"no compress")
280
+ else
281
+ raise Exception::new("Invalid allocating method.")
282
+ end
283
+
284
+ self.compress!
285
+ self.register!
286
+
287
+ return self
288
+ end
289
+
290
+ ##
291
+ # Appends to item.
292
+ #
293
+
294
+ def append!(compress = :compress)
295
+ self.decompress!
296
+
297
+ ::File.open(self.path, "a") do |io|
298
+ io.write(::File.read(@entry.file.path))
299
+ end
300
+
301
+ if compress == :compress
302
+ self.compress!
303
+ end
304
+ end
305
+
306
+ ##
307
+ # Indicates file is or file should be compressed.
308
+ #
309
+
310
+ def compressed?
311
+ result = @data[:compression]
312
+ if result.kind_of? Array
313
+ result = true
314
+ end
315
+
316
+ return result
317
+ end
318
+
319
+ ##
320
+ # Compress the file.
321
+ #
322
+
323
+ def compress!
324
+
325
+ # Checking out configuration
326
+ configuration = @entry.storage.directory.configuration
327
+ command, extension = configuration[:compress]
328
+ decompress = configuration[:decompress]
329
+
330
+ if not command.kind_of? FalseClass
331
+
332
+ # Setting file settings according to current
333
+ # configuration parameters
334
+
335
+ if command.kind_of? TrueClass
336
+ command = "gzip --best"
337
+ extension = "gz"
338
+ end
339
+ if decompress.kind_of? TrueClass
340
+ decompress = "gunzip"
341
+ end
342
+
343
+ @data[:compression] = {
344
+ :decompress => decompress,
345
+ :extension => extension
346
+ }
347
+
348
+ # Compress
349
+ system(command.dup << " " << self.path)
350
+ self.rebuild_path!
351
+ end
352
+ end
353
+
354
+ ##
355
+ # Decompress file.
356
+ #
357
+
358
+ def decompress!
359
+ if self.compressed? and self.exists?
360
+ command = self.compression[:decompress]
361
+ system(command.dup << " " << self.path << " 2> /dev/null")
362
+ FileUtils.move(self.target_path, self.path)
363
+ end
364
+ end
365
+
366
+ ##
367
+ # Describes compression.
368
+ #
369
+
370
+ def compression
371
+ @data[:compression]
372
+ end
373
+
374
+ ##
375
+ # Returns the creation date.
376
+ #
377
+
378
+ def created_at
379
+ @data[:date]
380
+ end
381
+
382
+ ##
383
+ # Returns the expiration date.
384
+ #
385
+
386
+ def expiration_at
387
+ configuration = @entry.storage.directory.configuration
388
+ period = configuration[:period].to_seconds
389
+ multiplier = configuration[:rotate]
390
+
391
+ return self.created_at + (period * multiplier)
392
+ end
393
+
394
+ ##
395
+ # Indicates, item is expired.
396
+ #
397
+
398
+ def expired?
399
+ recycle = @entry.storage.directory.configuration[:recycle]
400
+ if recycle.kind_of? FalseClass
401
+ result = false
402
+ else
403
+ recycle = recycle.to_sym
404
+ end
405
+
406
+ if recycle and (recycle == :remove) or (recycle == :mail)
407
+ result = self.expiration_at < Time::now
408
+ end
409
+
410
+ return result
411
+ end
412
+
413
+ end
414
+ end
415
+ end
@@ -0,0 +1,85 @@
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 = %q{rb.rotate}
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 = ["Martin Kozák"]
12
+ s.date = %q{2010-12-21}
13
+ s.default_executable = %q{rb.rotate}
14
+ s.description = %q{An alternative to classical 'logrotate' tool. It implements very similar functionallity, features openess and flexibility of the scripting environment and removes some most known 'logrotate' limitations.}
15
+ s.email = %q{martinkozak@martinkozak.net}
16
+ s.executables = ["rb.rotate"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE.txt",
19
+ "README.md"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "LICENSE.txt",
26
+ "README.md",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "bin/rb.rotate",
30
+ "lib/rb.rotate.rb",
31
+ "lib/rb.rotate/configuration.rb",
32
+ "lib/rb.rotate/directory.rb",
33
+ "lib/rb.rotate/dispatcher.rb",
34
+ "lib/rb.rotate/file.rb",
35
+ "lib/rb.rotate/hook.rb",
36
+ "lib/rb.rotate/install/defaults.yaml.initial",
37
+ "lib/rb.rotate/install/rotate.yaml.initial",
38
+ "lib/rb.rotate/log.rb",
39
+ "lib/rb.rotate/mail.rb",
40
+ "lib/rb.rotate/reader.rb",
41
+ "lib/rb.rotate/state.rb",
42
+ "lib/rb.rotate/state/archive.rb",
43
+ "lib/rb.rotate/state/file.rb",
44
+ "lib/rb.rotate/storage.rb",
45
+ "lib/rb.rotate/storage/entry.rb",
46
+ "lib/rb.rotate/storage/item.rb",
47
+ "rb.rotate.gemspec"
48
+ ]
49
+ s.homepage = %q{http://github.com/martinkozak/rb.rotate}
50
+ s.licenses = ["MIT"]
51
+ s.post_install_message = %q{
52
+ INSTALLATION DONE!
53
+ For remaining part of installation run 'rb.rotate install'
54
+ and then eventually setup running the 'rb.rotate' by cron.
55
+
56
+ Be warn, it's still BETA VERSION.
57
+
58
+ }
59
+ s.require_paths = ["lib"]
60
+ s.rubygems_version = %q{1.3.7}
61
+ s.summary = %q{More modern alternative to 'logrotate' with more features and less limitations.}
62
+
63
+ if s.respond_to? :specification_version then
64
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
65
+ s.specification_version = 3
66
+
67
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
68
+ s.add_runtime_dependency(%q<pony>, [">= 1.1"])
69
+ s.add_runtime_dependency(%q<sys-uname>, [">= 0.8.5"])
70
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
71
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
72
+ else
73
+ s.add_dependency(%q<pony>, [">= 1.1"])
74
+ s.add_dependency(%q<sys-uname>, [">= 0.8.5"])
75
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
76
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
77
+ end
78
+ else
79
+ s.add_dependency(%q<pony>, [">= 1.1"])
80
+ s.add_dependency(%q<sys-uname>, [">= 0.8.5"])
81
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
82
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
83
+ end
84
+ end
85
+