rb.rotate 0.1.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.
@@ -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
+