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,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+
5
+ gem "pony", ">= 1.1"
6
+ gem "sys-uname", ">= 0.8.5"
7
+
8
+ # Add dependencies to develop your gem here.
9
+ # Include everything needed to run rake, tests, features, etc.
10
+ group :development do
11
+ gem "bundler", "~> 1.0.0"
12
+ gem "jeweler", "~> 1.5.2"
13
+ end
@@ -0,0 +1,32 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activesupport (3.0.3)
5
+ git (1.2.5)
6
+ i18n (0.5.0)
7
+ jeweler (1.5.2)
8
+ bundler (~> 1.0.0)
9
+ git (>= 1.2.5)
10
+ rake
11
+ mail (2.2.12)
12
+ activesupport (>= 2.3.6)
13
+ i18n (>= 0.4.0)
14
+ mime-types (~> 1.16)
15
+ treetop (~> 1.4.8)
16
+ mime-types (1.16)
17
+ polyglot (0.3.1)
18
+ pony (1.1)
19
+ mail (> 2.0)
20
+ rake (0.8.7)
21
+ sys-uname (0.8.5)
22
+ treetop (1.4.9)
23
+ polyglot (>= 0.3.1)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ bundler (~> 1.0.0)
30
+ jeweler (~> 1.5.2)
31
+ pony (>= 1.1)
32
+ sys-uname (>= 0.8.5)
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Martin Kozák
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.
@@ -0,0 +1,81 @@
1
+ rb.rotate
2
+ =========
3
+
4
+ **rb.rotate** is an alternative to classical `logrotate` tool.
5
+ It implements very similar functionallity, features openess and
6
+ flexibility of the scripting environment and removes some most known
7
+ `logrotate` limitations.
8
+
9
+ And of sure, it adds some features and it doesn't implement some
10
+ features of the original for now.
11
+
12
+ ### Removed `logrotate` Limitations
13
+
14
+ * log directory and archive directory can be on **different drives**,
15
+ * it can **follow symbolic links** both for directories and files,
16
+ * it can **respect the subdirectories structure** of the log directory,
17
+ so archive directory can have the same structure as original log
18
+ directory.
19
+
20
+ ### Additional Features
21
+
22
+ * elegant, well documented and easy-to-understand [YAML][2] configuration
23
+ file,
24
+ * flexible and in fact unlimited possibility to define hooks while
25
+ "put file to archive" action and run them in mix with pre-build
26
+ actions in whatever required order.
27
+
28
+ Installation
29
+ ------------
30
+
31
+ After installing the `rb.rotate` gem, simply type following as root and
32
+ then add the `rb.rotate` entry to cron.
33
+
34
+ rb.rotate install
35
+
36
+ Be warn, it works for *Linux* and *FreeBSD* only. On other platforms,
37
+ please:
38
+
39
+ 1. copy initial configuration files in `<gem-path>/lib/rb.rotate/install`
40
+ to appropriate location on your platform (remove the `.initial` extension,
41
+ of sure),
42
+ 2. create the `<gem-path>/lib/paths.conf` file with path to
43
+ `rotate.yaml` file,
44
+ 3. change paths in `rotate.yaml` file to directories appropriate for
45
+ your platfom,
46
+ 4. [report back][1] to the project output of the `rb.rotate sysname`
47
+ command and appropriate configuration locations for including them in
48
+ next release.
49
+
50
+
51
+ Status
52
+ ------
53
+ Currently in **pre-beta** version. All features are implemented, some
54
+ of them such as *hooks* are untested.
55
+
56
+ ### Documentation
57
+
58
+ See the configuration file. It's pretty well documented with possible values
59
+ listed and a lot of explaining.
60
+
61
+
62
+ Contributing
63
+ ------------
64
+
65
+ 1. Fork it.
66
+ 2. Create a branch (`git checkout -b 20101220-my-change`).
67
+ 3. Commit your changes (`git commit -am "Added something"`).
68
+ 4. Push to the branch (`git push origin 20101220-my-change`).
69
+ 5. Create an [Issue][1] with a link to your branch.
70
+ 6. Enjoy a refreshing Diet Coke and wait.
71
+
72
+
73
+ Copyright
74
+ ---------
75
+
76
+ Copyright (c) 2010 [Martin Kozák][3]. See `LICENSE.txt` for
77
+ further details.
78
+
79
+ [1]: http://github.com/martinkozak/rb.rotate/issues
80
+ [2]: http://www.yaml.org/
81
+ [3]: http://www.martinkozak.net/
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+ require 'rubygems'
3
+ require 'bundler'
4
+ begin
5
+ Bundler.setup(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+ require 'rake'
12
+
13
+ require 'jeweler'
14
+ Jeweler::Tasks.new do |gem|
15
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
16
+ gem.name = "rb.rotate"
17
+ gem.homepage = "http://github.com/martinkozak/rb.rotate"
18
+ gem.license = "MIT"
19
+ gem.summary = %Q{More modern alternative to 'logrotate' with more features and less limitations.}
20
+ gem.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.}
21
+ gem.email = "martinkozak@martinkozak.net"
22
+ gem.authors = ["Martin Kozák"]
23
+ gem.executables = ["rb.rotate"]
24
+ gem.post_install_message = "\nINSTALLATION DONE!\nFor remaining part of installation run 'rb.rotate install'\nand then eventually setup running the 'rb.rotate' by cron.\n\nBe warn, it's still BETA VERSION.\n\n"
25
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
26
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
27
+ # gem.add_runtime_dependency 'sys-uname', '>= 0.8.5'
28
+ # gem.add_runtime_dependency 'pony', '>= 1.1'
29
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
30
+ end
31
+ Jeweler::RubygemsDotOrgTasks.new
32
+
33
+ require 'rake/rdoctask'
34
+ Rake::RDocTask.new do |rdoc|
35
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
36
+
37
+ rdoc.rdoc_dir = 'rdoc'
38
+ rdoc.title = "rb.rotate #{version}"
39
+ rdoc.rdoc_files.include('README*')
40
+ rdoc.rdoc_files.include('lib/**/*.rb')
41
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ require "rb.rotate"
3
+ if ARGV[0]
4
+ case ARGV[0].to_sym
5
+ when :install
6
+ RbRotate::install!
7
+ when :sysname
8
+ RbRotate::sysname!
9
+ end
10
+ else
11
+ RbRotate::run!
12
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+ require "rb.rotate/dispatcher"
3
+
4
+ module RbRotate
5
+
6
+ ##
7
+ # Runs the application.
8
+ #
9
+
10
+ def self.run!
11
+ Dispatcher::new::run!
12
+ end
13
+
14
+ ##
15
+ # Installs the application.
16
+ #
17
+
18
+ def self.install!
19
+ Dispatcher::new::install!
20
+ end
21
+
22
+ ##
23
+ # Prints out the system name.
24
+ #
25
+
26
+ def self.sysname!
27
+ Dispatcher::new::sysname!
28
+ end
29
+
30
+ end
@@ -0,0 +1,312 @@
1
+ # encoding: utf-8
2
+
3
+ require "yaml"
4
+ require "rb.rotate/directory"
5
+
6
+ module RbRotate
7
+
8
+ ##
9
+ # Directory configuration.
10
+ #
11
+
12
+ class DirectoryConfiguration
13
+
14
+ ##
15
+ # Holds currently selected directory section.
16
+ #
17
+
18
+ @directory
19
+
20
+ ##
21
+ # Holds configuration data.
22
+ #
23
+
24
+ @data
25
+
26
+ ##
27
+ # Constructor.
28
+ #
29
+ # Expects file path and directory section specification
30
+ # as symbol.
31
+ #
32
+
33
+ def initialize(directory)
34
+ # TODO: this should handle configuration inheritance
35
+ @directory = directory
36
+ end
37
+
38
+ ##
39
+ # Returns configuration file setting.
40
+ #
41
+
42
+ def configuration
43
+ Configuration::get
44
+ end
45
+
46
+ ##
47
+ # Returns configuration data.
48
+ #
49
+
50
+ def data
51
+ if @data.nil?
52
+ data = self.configuration[:dirs][@directory]
53
+ @data = { }
54
+
55
+ # Converts strings to symbols
56
+ data.each_pair do |key, value|
57
+ @data[key.to_sym] = value
58
+ end
59
+
60
+ self.handle_inheritance!
61
+ end
62
+
63
+ return @data
64
+ end
65
+
66
+ ##
67
+ # Handles configuration inheritance.
68
+ #
69
+
70
+ def handle_ihneritance!
71
+ if @data.include? :parent
72
+ @data.merge! self.class::new(@data[:parent]).data
73
+ end
74
+ end
75
+
76
+ ##
77
+ # Returns some item.
78
+ #
79
+
80
+ def [](name)
81
+ self.data[name]
82
+ end
83
+
84
+ ##
85
+ # Handles method calls.
86
+ #
87
+
88
+ def method_missing(name)
89
+ self[name]
90
+ end
91
+
92
+ end
93
+
94
+ ##
95
+ # General configuration file.
96
+ #
97
+
98
+ class Configuration
99
+
100
+ ##
101
+ # Brings self instance. (Class is singletone.)
102
+ #
103
+
104
+ @@self = nil
105
+
106
+ ##
107
+ # Holds data.
108
+ #
109
+
110
+ @data
111
+
112
+ ##
113
+ # Opens the file. (Shortcut for instance call.)
114
+ #
115
+
116
+ def self.read(file)
117
+ self::get.read(file)
118
+ end
119
+
120
+ ##
121
+ # Returns the single instance.
122
+ #
123
+
124
+ def self.get
125
+ if @@self.nil?
126
+ @@self = self::new
127
+ end
128
+
129
+ return @@self
130
+ end
131
+
132
+ ##
133
+ # Traverses through each directory configuration.
134
+ # (Shortcut for instace call.)
135
+
136
+ def self.each_directory(&block)
137
+ self::get.each_directory(&block)
138
+ end
139
+
140
+ ##
141
+ # Alias for #find_path.
142
+ #
143
+
144
+ def self.find_path(path)
145
+ self::get.find_path(path)
146
+ end
147
+
148
+ ##
149
+ # Opens the file.
150
+ #
151
+
152
+ def read(file)
153
+ data = YAML.load(::File.read(file))
154
+ @data = { }
155
+
156
+ # Converts strings to symbols
157
+ data.each_pair do |name, section|
158
+ section_data = { }
159
+ @data[name.to_sym]= section_data
160
+
161
+ if section.kind_of? Hash
162
+ section.each_pair do |key, value|
163
+ section_data[key.to_sym] = value
164
+ end
165
+ end
166
+ end
167
+
168
+ defaults = YAML.load(::File.read(@data[:paths][:"defaults file"]))
169
+
170
+ # Merges with defaults
171
+ defaults.each_pair do |name, section|
172
+ name = name.to_sym
173
+ if not @data.has_key? name
174
+ @data[name] = value
175
+ elsif section.kind_of? Hash
176
+ section.each_pair do |key, value|
177
+ key = key.to_sym
178
+ if not @data[name].has_key? key
179
+ @data[name][key] = value
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
185
+
186
+ ##
187
+ # Returns an item.
188
+ #
189
+
190
+ def [](name)
191
+ @data[name]
192
+ end
193
+
194
+ ##
195
+ # Handles method calls.
196
+ #
197
+
198
+ def method_missing(name)
199
+ self[name]
200
+ end
201
+
202
+ ##
203
+ # Traverses through each directory configuration.
204
+ #
205
+
206
+ def each_directory
207
+ @data[:dirs].each_pair do |name, dir|
208
+ yield Directory::new(name, dir)
209
+ end
210
+ end
211
+
212
+ ##
213
+ # Looks for directory in configuration.
214
+ #
215
+
216
+ def find_path(path)
217
+ @data.each_pair do |name, dir|
218
+ if dir[:directory] == path
219
+ return Directory::new(name, dir)
220
+ end
221
+ end
222
+
223
+ return nil
224
+ end
225
+
226
+ end
227
+
228
+ end
229
+
230
+ # String extensions
231
+
232
+ class String
233
+
234
+ TO_TIME_MATCHER = /(\d+)\s*((?:year|month|week|day|hour|minute|second))s?/i
235
+
236
+ ##
237
+ # Converts number with units to bytes count.
238
+ #
239
+
240
+ def to_bytes
241
+ value = self.to_i
242
+ if value == 0
243
+ raise Exception::new("Invalid size specification: " << self << ".")
244
+ end
245
+
246
+ exponent = nil
247
+ case self[-1]
248
+ when ?M
249
+ exponent = 2
250
+ when ?G
251
+ exponent = 3
252
+ when ?K
253
+ exponent = 1
254
+ when ?0, ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9
255
+ exponent = 0
256
+ else
257
+ raise Exception::new("Invalid unit in size specification: " << self << ".")
258
+ end
259
+
260
+ return value * (1024 ** exponent)
261
+ end
262
+
263
+ ##
264
+ # Converts string identifier of time period to seconds.
265
+ #
266
+
267
+ def to_seconds
268
+
269
+ period = nil
270
+ case self.to_sym
271
+ when :yearly
272
+ period = "1 year"
273
+ when :monthly
274
+ period = "1 month"
275
+ when :weekly
276
+ period = "1 week"
277
+ when :daily
278
+ period = "1 day"
279
+ when :hourly
280
+ period = "1 hour"
281
+ else
282
+ period = self
283
+ end
284
+
285
+ matches = period.match(self.class::TO_TIME_MATCHER)
286
+ if matches.nil?
287
+ raise Exception::new("Invalid period specification: " << self << ".")
288
+ end
289
+
290
+ count = matches[1].to_i
291
+ unit = matches[2].to_sym
292
+ seconds = nil
293
+
294
+ case unit
295
+ when :year
296
+ seconds = 365 * 24 * 60 * 60
297
+ when :month
298
+ seconds = 30 * 24 * 60 * 60
299
+ when :week
300
+ seconds = 7 * 24 * 60 * 60
301
+ when :day
302
+ seconds = 24 * 60 * 60
303
+ when :hour
304
+ seconds = 60 * 60
305
+ when :second
306
+ seconds = 1
307
+ end
308
+
309
+ return seconds * count
310
+ end
311
+
312
+ end