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,139 @@
1
+ # encoding: utf-8
2
+ require "rb.rotate/state"
3
+
4
+ module RbRotate
5
+ module StateModule
6
+
7
+ ##
8
+ # Represents file state record.
9
+ #
10
+
11
+ class File
12
+
13
+ ##
14
+ # Holds the file data.
15
+ #
16
+
17
+ @data
18
+
19
+ ##
20
+ # Holds path of the appropriate file.
21
+ #
22
+
23
+ @path
24
+
25
+ ##
26
+ # Constructor.
27
+ #
28
+
29
+ def initialize(path, data)
30
+ @path = path
31
+ @data = data
32
+ end
33
+
34
+ ##
35
+ # Indicates tate record for file exists.
36
+ #
37
+
38
+ def exists?
39
+ not @data.empty?
40
+ end
41
+
42
+ ##
43
+ # Returns last archival date.
44
+ #
45
+
46
+ def date
47
+ @data[:date]
48
+ end
49
+
50
+ ##
51
+ # Touches date to current date.
52
+ #
53
+
54
+ def touch!
55
+ @data[:date] = Time::now
56
+ end
57
+
58
+ ##
59
+ # Returns extension.
60
+ #
61
+
62
+ def extension
63
+ @data[:filename][:extension]
64
+ end
65
+
66
+ ##
67
+ # Returns basename.
68
+ #
69
+
70
+ def name
71
+ @data[:filename][:name]
72
+ end
73
+
74
+ ##
75
+ # Returns items list.
76
+ #
77
+
78
+ def items
79
+ @data[:items]
80
+ end
81
+
82
+ ##
83
+ # Returns directory specification.
84
+ #
85
+
86
+ def directory
87
+ if @data.has_key? :directory
88
+ @data[:directory].to_sym
89
+ else
90
+ nil
91
+ end
92
+ end
93
+
94
+ ##
95
+ # Sets items list.
96
+ #
97
+
98
+ def items=(value)
99
+ @data[:items].replace(value)
100
+ end
101
+
102
+ ##
103
+ # Creates the state record.
104
+ #
105
+
106
+ def create(file)
107
+ extension = ::File.extname(file.path)[1..-1]
108
+ if extension.nil?
109
+ extension = nil
110
+ cut = 0..-1
111
+ else
112
+ cut = 0..-2
113
+ end
114
+
115
+ new = {
116
+ :date => Time::now,
117
+ :items => { },
118
+ :directory => file.directory.identifier,
119
+ :filename => {
120
+ :name => ::File.basename(file.path, extension.to_s)[cut],
121
+ :extension => extension
122
+ }
123
+ }
124
+
125
+ @data.replace(new)
126
+ end
127
+
128
+ ##
129
+ # Destroys the state record.
130
+ #
131
+
132
+ def destroy!
133
+ State::files.delete(@path.to_sym)
134
+ @data = nil
135
+ end
136
+
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,208 @@
1
+ # encoding: utf-8
2
+
3
+ require "rb.rotate/storage/entry"
4
+ require "rb.rotate/file"
5
+ require "rb.rotate/configuration"
6
+
7
+ module RbRotate
8
+
9
+ ##
10
+ # Represents storage for archived files.
11
+ #
12
+
13
+ class Storage
14
+
15
+ ##
16
+ # Directory for which storage is aimed.
17
+ #
18
+
19
+ @directory
20
+ attr_reader :directory
21
+
22
+ ##
23
+ # Constructor.
24
+ #
25
+
26
+ def initialize(directory)
27
+ @directory = directory
28
+ end
29
+
30
+ ##
31
+ # Alias for new.
32
+ #
33
+
34
+ def self.get(directory)
35
+ self::new(directory)
36
+ end
37
+
38
+ ##
39
+ # Creates storage according to file and puts it to it.
40
+ #
41
+
42
+ def self.put(file)
43
+ self::get(file.directory).put(file)
44
+ end
45
+
46
+ ##
47
+ # Puts file to storage.
48
+ #
49
+
50
+ def put(file)
51
+ self.do_actions! file
52
+ end
53
+
54
+ ##
55
+ # Indicates, storage is compressed.
56
+ #
57
+
58
+ def compressed?
59
+ self.directory.compressable?
60
+ end
61
+
62
+ ##
63
+ # Runs file actions.
64
+ #
65
+
66
+ def do_actions!(file)
67
+ entry = StorageModule::Entry::new(self, file)
68
+
69
+ # Loads them
70
+ actions = @directory.configuration[:action].split("+")
71
+ actions.map! do |i|
72
+ i.strip!
73
+ k, v = i.split(":", 2)
74
+ [k.to_sym, v]
75
+ end
76
+
77
+ # Does them
78
+ variables = { }
79
+
80
+ actions.each do |action, arguments|
81
+ case action
82
+ when :move, :copy, :append
83
+ variables[:filepath] = entry.put! action
84
+ when :remove
85
+ variables[:filepath] = file.remove!
86
+ when :create, :truncate
87
+ variables[:filepath] = file.create!
88
+ when :mail
89
+ variables[:filepath] = entry.mail! arguments
90
+ when :hook
91
+ name, arguments = arguments.split(":", 2)
92
+ variables.merge! Hook::new(name.to_sym, arguments, variables).run!
93
+ end
94
+ end
95
+ end
96
+
97
+ ##
98
+ # Cleanups expired items from the storage.
99
+ #
100
+
101
+ def cleanup!
102
+ self.each_entry do |entry|
103
+ entry.cleanup!
104
+ end
105
+ end
106
+
107
+ ##
108
+ # Returns item identifier of the storage.
109
+ #
110
+
111
+ def item_identifier
112
+ self.directory.configuration[:identifier]
113
+ end
114
+
115
+ ##
116
+ # Indicates numeric identifier.
117
+ #
118
+
119
+ def numeric_identifier?
120
+ self.item_identifier.to_sym == :numeric
121
+ end
122
+
123
+ ##
124
+ # Removes orphans.
125
+ #
126
+
127
+ def self.remove_orphans!
128
+ self::each_entry do |entry|
129
+ items_count = 0
130
+
131
+ entry.each_item do |item|
132
+ if item.expired?
133
+ item.remove!
134
+ elsif not item.exists?
135
+ item.unregister!
136
+ else
137
+ items_count += 1
138
+ end
139
+ end
140
+
141
+ file = entry.file
142
+ if (not file.exists?) and (items_count <= 0)
143
+ file.state.destroy!
144
+ end
145
+ end
146
+ end
147
+
148
+ ##
149
+ # Traverses through each item in current storage.
150
+ #
151
+
152
+ def each_item(&block)
153
+ self.each_entry do |entry|
154
+ entry.each_item(&block)
155
+ end
156
+ end
157
+
158
+ ##
159
+ # Traverses through all entries of this directory storage.
160
+ #
161
+
162
+ def each_entry
163
+ State::each_file do |path, state|
164
+ if state.directory == self.directory.identifier
165
+ file = File::new(path)
166
+ entry = StorageModule::Entry::new(self, file)
167
+
168
+ yield entry
169
+ end
170
+ end
171
+ end
172
+
173
+ ##
174
+ # Traverses through all items in global storage.
175
+ #
176
+
177
+ def self.each_item(&block)
178
+ self.each_entry do |entry|
179
+ entry.each_item(&block)
180
+ end
181
+ end
182
+
183
+ ##
184
+ # Traverses through all entries.
185
+ #
186
+
187
+ def self.each_entry
188
+ State::each_file do |path, state|
189
+ file = File::new(path)
190
+ storage = self::new(file.directory)
191
+ entry = StorageModule::Entry::new(storage, file)
192
+
193
+ yield entry
194
+ end
195
+ end
196
+
197
+ ##
198
+ # Traverses through all directories in storage.
199
+ #
200
+
201
+ def self.each_directory
202
+ Configuration::each_directory do |directory|
203
+ yield self::get(directory)
204
+ end
205
+ end
206
+
207
+ end
208
+ end
@@ -0,0 +1,120 @@
1
+ # encoding: utf-8
2
+
3
+ require "rb.rotate/storage/item"
4
+ require "rb.rotate/mail"
5
+
6
+ module RbRotate
7
+ module StorageModule
8
+
9
+ ##
10
+ # Represents an entry of the storage.
11
+ # Entry is archived items of one file.
12
+ #
13
+
14
+ class Entry
15
+
16
+ ##
17
+ # Holds file of the entry.
18
+ #
19
+
20
+ @file
21
+ attr_reader :file
22
+
23
+ ##
24
+ # Holds parent storage.
25
+ #
26
+
27
+ @storage
28
+ attr_reader :storage
29
+
30
+ ##
31
+ # Constructor.
32
+ #
33
+
34
+ def initialize(storage, file)
35
+ @storage = storage
36
+ @file = file
37
+ end
38
+
39
+ ##
40
+ # Puts current version of the file to items.
41
+ #
42
+
43
+ def put!(method)
44
+
45
+ # If necessary, creates the state record
46
+ if not self.file.state.exists?
47
+ self.file.state.create(@file)
48
+ end
49
+
50
+ # Rotates other items
51
+ new_list = { }
52
+ self.each_item do |item|
53
+ if item.exists?
54
+ item.rotate!
55
+ new_list[item.identifier] = item.path
56
+ else
57
+ item.unregister!
58
+ end
59
+ end
60
+
61
+ # Puts new item
62
+ item = Item::new(self).allocate(method)
63
+ new_list[item.identifier] = item.path
64
+
65
+ self.file.state.touch!
66
+ self.file.state.items = new_list
67
+
68
+ return self.file.path
69
+ end
70
+
71
+ ##
72
+ # Cleanups the items.
73
+ #
74
+
75
+ def cleanup!
76
+ self.each_item do |item|
77
+ if item.expired?
78
+ item.remove!
79
+ end
80
+ end
81
+ end
82
+
83
+ ##
84
+ # Mail file to specified address.
85
+ #
86
+
87
+ def mail!(to)
88
+ if to.nil?
89
+ to = @storage.directory.configuration[:mail]
90
+ end
91
+ if to.nil?
92
+ raise Exception("No e-mail address specified for sending log to.")
93
+ end
94
+
95
+ require "etc"
96
+ require "socket"
97
+
98
+ Mail::send(
99
+ :from => Etc.getlogin.dup << "@" << Socket.gethostname,
100
+ :to => to,
101
+ :subject => Socket.gethostname.dup << " : log : " << self.file.path,
102
+ :body => ::File.read(self.file.path)
103
+ )
104
+
105
+ return self.file.path
106
+ end
107
+
108
+ ##
109
+ # Traverses through all items.
110
+ #
111
+
112
+ def each_item
113
+ self.file.state.items.each_pair do |identifier, path|
114
+ yield Item::new(self, identifier, path)
115
+ end
116
+ end
117
+
118
+ end
119
+ end
120
+ end