selbackup 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.
- checksums.yaml +7 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +48 -0
- data/LICENSE +14 -0
- data/README.fr.md +82 -0
- data/README.md +83 -0
- data/Rakefile +26 -0
- data/doc/apple-touch-icon.png +0 -0
- data/doc/classes/Object.html +121 -0
- data/doc/classes/SelBackup.html +1387 -0
- data/doc/created.rid +6 -0
- data/doc/css/github.css +129 -0
- data/doc/css/main.css +333 -0
- data/doc/css/panel.css +384 -0
- data/doc/css/reset.css +48 -0
- data/doc/favicon.ico +0 -0
- data/doc/files/Gemfile.html +84 -0
- data/doc/files/Guardfile.html +147 -0
- data/doc/files/LICENSE.html +86 -0
- data/doc/files/Rakefile.html +101 -0
- data/doc/files/backup-to-s3_rb.html +85 -0
- data/doc/files/lib/selbackup_rb.html +89 -0
- data/doc/files/lib-backup_rb.html +89 -0
- data/doc/files/mv_files_rb.html +89 -0
- data/doc/files/spec/selbackup_spec_rb.html +76 -0
- data/doc/files/test/lib-backup_test_rb.html +76 -0
- data/doc/i/arrows.png +0 -0
- data/doc/i/results_bg.png +0 -0
- data/doc/i/tree_bg.png +0 -0
- data/doc/index.html +13 -0
- data/doc/js/highlight.pack.js +1 -0
- data/doc/js/jquery-1.3.2.min.js +19 -0
- data/doc/js/jquery-effect.js +593 -0
- data/doc/js/main.js +24 -0
- data/doc/js/navigation.js +142 -0
- data/doc/js/search_index.js +1 -0
- data/doc/js/searchdoc.js +449 -0
- data/doc/js/searcher.js +228 -0
- data/doc/panel/index.html +73 -0
- data/doc/panel/links.html +16 -0
- data/doc/panel/tree.js +1 -0
- data/lib/selbackup.rb +376 -0
- data/selbackup.gemspec +13 -0
- data/spec/selbackup_spec.rb +240 -0
- data/upload_test/create_files.sh +13 -0
- metadata +88 -0
data/doc/js/searcher.js
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
Searcher = function(data) {
|
|
2
|
+
this.data = data;
|
|
3
|
+
this.handlers = [];
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
Searcher.prototype = new function() {
|
|
7
|
+
// search is performed in chunks of 1000 for non-blocking user input
|
|
8
|
+
var CHUNK_SIZE = 1000;
|
|
9
|
+
// do not try to find more than 100 results
|
|
10
|
+
var MAX_RESULTS = 100;
|
|
11
|
+
var huid = 1;
|
|
12
|
+
var suid = 1;
|
|
13
|
+
var runs = 0;
|
|
14
|
+
|
|
15
|
+
this.find = function(query) {
|
|
16
|
+
var queries = splitQuery(query);
|
|
17
|
+
var regexps = buildRegexps(queries);
|
|
18
|
+
var highlighters = buildHilighters(queries);
|
|
19
|
+
var state = { from: 0, pass: 0, limit: MAX_RESULTS, n: suid++};
|
|
20
|
+
var _this = this;
|
|
21
|
+
|
|
22
|
+
this.currentSuid = state.n;
|
|
23
|
+
|
|
24
|
+
if (!query) return;
|
|
25
|
+
|
|
26
|
+
var run = function() {
|
|
27
|
+
// stop current search thread if new search started
|
|
28
|
+
if (state.n != _this.currentSuid) return;
|
|
29
|
+
|
|
30
|
+
var results =
|
|
31
|
+
performSearch(_this.data, regexps, queries, highlighters, state);
|
|
32
|
+
var hasMore = (state.limit > 0 && state.pass < 4);
|
|
33
|
+
|
|
34
|
+
triggerResults.call(_this, results, !hasMore);
|
|
35
|
+
if (hasMore) {
|
|
36
|
+
setTimeout(run, 2);
|
|
37
|
+
}
|
|
38
|
+
runs++;
|
|
39
|
+
};
|
|
40
|
+
runs = 0;
|
|
41
|
+
|
|
42
|
+
// start search thread
|
|
43
|
+
run();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* ----- Events ------ */
|
|
47
|
+
this.ready = function(fn) {
|
|
48
|
+
fn.huid = huid;
|
|
49
|
+
this.handlers.push(fn);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* ----- Utilities ------ */
|
|
53
|
+
function splitQuery(query) {
|
|
54
|
+
return jQuery.grep(query.split(/(\s+|::?|\(\)?)/), function(string) {
|
|
55
|
+
return string.match(/\S/)
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function buildRegexps(queries) {
|
|
60
|
+
return jQuery.map(queries, function(query) {
|
|
61
|
+
return new RegExp(query.replace(/(.)/g, '([$1])([^$1]*?)'), 'i')
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function buildHilighters(queries) {
|
|
66
|
+
return jQuery.map(queries, function(query) {
|
|
67
|
+
return jQuery.map(query.split(''), function(l, i) {
|
|
68
|
+
return '\u0001$' + (i*2+1) + '\u0002$' + (i*2+2);
|
|
69
|
+
}).join('');
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// function longMatchRegexp(index, longIndex, regexps) {
|
|
74
|
+
// for (var i = regexps.length - 1; i >= 0; i--){
|
|
75
|
+
// if (!index.match(regexps[i]) && !longIndex.match(regexps[i])) return false;
|
|
76
|
+
// };
|
|
77
|
+
// return true;
|
|
78
|
+
// }
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
/* ----- Mathchers ------ */
|
|
82
|
+
|
|
83
|
+
/*
|
|
84
|
+
* This record matches if the index starts with queries[0] and the record
|
|
85
|
+
* matches all of the regexps
|
|
86
|
+
*/
|
|
87
|
+
function matchPassBeginning(index, longIndex, queries, regexps) {
|
|
88
|
+
if (index.indexOf(queries[0]) != 0) return false;
|
|
89
|
+
for (var i=1, l = regexps.length; i < l; i++) {
|
|
90
|
+
if (!index.match(regexps[i]) && !longIndex.match(regexps[i]))
|
|
91
|
+
return false;
|
|
92
|
+
};
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/*
|
|
97
|
+
* This record matches if the longIndex starts with queries[0] and the
|
|
98
|
+
* longIndex matches all of the regexps
|
|
99
|
+
*/
|
|
100
|
+
function matchPassLongIndex(index, longIndex, queries, regexps) {
|
|
101
|
+
if (longIndex.indexOf(queries[0]) != 0) return false;
|
|
102
|
+
for (var i=1, l = regexps.length; i < l; i++) {
|
|
103
|
+
if (!longIndex.match(regexps[i]))
|
|
104
|
+
return false;
|
|
105
|
+
};
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/*
|
|
110
|
+
* This record matches if the index contains queries[0] and the record
|
|
111
|
+
* matches all of the regexps
|
|
112
|
+
*/
|
|
113
|
+
function matchPassContains(index, longIndex, queries, regexps) {
|
|
114
|
+
if (index.indexOf(queries[0]) == -1) return false;
|
|
115
|
+
for (var i=1, l = regexps.length; i < l; i++) {
|
|
116
|
+
if (!index.match(regexps[i]) && !longIndex.match(regexps[i]))
|
|
117
|
+
return false;
|
|
118
|
+
};
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/*
|
|
123
|
+
* This record matches if regexps[0] matches the index and the record
|
|
124
|
+
* matches all of the regexps
|
|
125
|
+
*/
|
|
126
|
+
function matchPassRegexp(index, longIndex, queries, regexps) {
|
|
127
|
+
if (!index.match(regexps[0])) return false;
|
|
128
|
+
for (var i=1, l = regexps.length; i < l; i++) {
|
|
129
|
+
if (!index.match(regexps[i]) && !longIndex.match(regexps[i]))
|
|
130
|
+
return false;
|
|
131
|
+
};
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
/* ----- Highlighters ------ */
|
|
137
|
+
function highlightRegexp(info, queries, regexps, highlighters) {
|
|
138
|
+
var result = createResult(info);
|
|
139
|
+
for (var i=0, l = regexps.length; i < l; i++) {
|
|
140
|
+
result.title = result.title.replace(regexps[i], highlighters[i]);
|
|
141
|
+
result.namespace = result.namespace.replace(regexps[i], highlighters[i]);
|
|
142
|
+
};
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function hltSubstring(string, pos, length) {
|
|
147
|
+
return string.substring(0, pos) + '\u0001' + string.substring(pos, pos + length) + '\u0002' + string.substring(pos + length);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function highlightQuery(info, queries, regexps, highlighters) {
|
|
151
|
+
var result = createResult(info);
|
|
152
|
+
var pos = 0;
|
|
153
|
+
var lcTitle = result.title.toLowerCase();
|
|
154
|
+
|
|
155
|
+
pos = lcTitle.indexOf(queries[0]);
|
|
156
|
+
if (pos != -1) {
|
|
157
|
+
result.title = hltSubstring(result.title, pos, queries[0].length);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
result.namespace = result.namespace.replace(regexps[0], highlighters[0]);
|
|
161
|
+
for (var i=1, l = regexps.length; i < l; i++) {
|
|
162
|
+
result.title = result.title.replace(regexps[i], highlighters[i]);
|
|
163
|
+
result.namespace = result.namespace.replace(regexps[i], highlighters[i]);
|
|
164
|
+
};
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function createResult(info) {
|
|
169
|
+
var result = {};
|
|
170
|
+
result.title = info[0];
|
|
171
|
+
result.namespace = info[1];
|
|
172
|
+
result.path = info[2];
|
|
173
|
+
result.params = info[3];
|
|
174
|
+
result.snippet = info[4];
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* ----- Searching ------ */
|
|
179
|
+
function performSearch(data, regexps, queries, highlighters, state) {
|
|
180
|
+
var searchIndex = data.searchIndex;
|
|
181
|
+
var longSearchIndex = data.longSearchIndex;
|
|
182
|
+
var info = data.info;
|
|
183
|
+
var result = [];
|
|
184
|
+
var i = state.from;
|
|
185
|
+
var l = searchIndex.length;
|
|
186
|
+
var togo = CHUNK_SIZE;
|
|
187
|
+
var matchFunc, hltFunc;
|
|
188
|
+
|
|
189
|
+
while (state.pass < 4 && state.limit > 0 && togo > 0) {
|
|
190
|
+
if (state.pass == 0) {
|
|
191
|
+
matchFunc = matchPassBeginning;
|
|
192
|
+
hltFunc = highlightQuery;
|
|
193
|
+
} else if (state.pass == 1) {
|
|
194
|
+
matchFunc = matchPassLongIndex;
|
|
195
|
+
hltFunc = highlightQuery;
|
|
196
|
+
} else if (state.pass == 2) {
|
|
197
|
+
matchFunc = matchPassContains;
|
|
198
|
+
hltFunc = highlightQuery;
|
|
199
|
+
} else if (state.pass == 3) {
|
|
200
|
+
matchFunc = matchPassRegexp;
|
|
201
|
+
hltFunc = highlightRegexp;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
for (; togo > 0 && i < l && state.limit > 0; i++, togo--) {
|
|
205
|
+
if (info[i].n == state.n) continue;
|
|
206
|
+
if (matchFunc(searchIndex[i], longSearchIndex[i], queries, regexps)) {
|
|
207
|
+
info[i].n = state.n;
|
|
208
|
+
result.push(hltFunc(info[i], queries, regexps, highlighters));
|
|
209
|
+
state.limit--;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
if (searchIndex.length <= i) {
|
|
213
|
+
state.pass++;
|
|
214
|
+
i = state.from = 0;
|
|
215
|
+
} else {
|
|
216
|
+
state.from = i;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return result;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function triggerResults(results, isLast) {
|
|
223
|
+
jQuery.each(this.handlers, function(i, fn) {
|
|
224
|
+
fn.call(this, results, isLast)
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
|
3
|
+
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
|
4
|
+
|
|
5
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
|
6
|
+
<head>
|
|
7
|
+
<title>search index</title>
|
|
8
|
+
<link rel="stylesheet" href="../css/reset.css" type="text/css" media="screen" charset="utf-8" />
|
|
9
|
+
<link rel="stylesheet" href="../css/panel.css" type="text/css" media="screen" charset="utf-8" />
|
|
10
|
+
<script src="../js/search_index.js" type="text/javascript" charset="utf-8"></script>
|
|
11
|
+
<script src="../js/searcher.js" type="text/javascript" charset="utf-8"></script>
|
|
12
|
+
<script src="tree.js" type="text/javascript" charset="utf-8"></script>
|
|
13
|
+
<script src="../js/jquery-1.3.2.min.js" type="text/javascript" charset="utf-8"></script>
|
|
14
|
+
<script src="../js/searchdoc.js" type="text/javascript" charset="utf-8"></script>
|
|
15
|
+
<script type="text/javascript" charset="utf-8">
|
|
16
|
+
function placeholder() {
|
|
17
|
+
if ($('<input type="text">')[0].placeholder !== undefined) return;
|
|
18
|
+
|
|
19
|
+
$('#search-label').click(function() {
|
|
20
|
+
$('#search').focus();
|
|
21
|
+
$('#search-label').hide();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
$('#search').focus(function() {
|
|
25
|
+
$('#search-label').hide();
|
|
26
|
+
});
|
|
27
|
+
$('#search').blur(function() {
|
|
28
|
+
this.value == '' && $('#search-label').show()
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
$('#search')[0].value == '' && $('#search-label').show();
|
|
32
|
+
}
|
|
33
|
+
$(function() {
|
|
34
|
+
placeholder();
|
|
35
|
+
$('#links').hide();
|
|
36
|
+
var panel = new Searchdoc.Panel($('#panel'), search_data, tree, window.parent.frames[1]);
|
|
37
|
+
$('#search').focus();
|
|
38
|
+
|
|
39
|
+
var s = window.parent.location.search.match(/\?q=([^&]+)/);
|
|
40
|
+
if (s) {
|
|
41
|
+
s = decodeURIComponent(s[1]).replace(/\+/g, ' ');
|
|
42
|
+
if (s.length > 0)
|
|
43
|
+
{
|
|
44
|
+
$('#search').val(s);
|
|
45
|
+
panel.search(s, true);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
</script>
|
|
50
|
+
</head>
|
|
51
|
+
<body>
|
|
52
|
+
<div class="panel panel_tree" id="panel">
|
|
53
|
+
<div class="header">
|
|
54
|
+
<div>
|
|
55
|
+
<label for="search" id="search-label" style="display: none">Search</label>
|
|
56
|
+
<table>
|
|
57
|
+
<tr><td>
|
|
58
|
+
<input type="Search" placeholder="Search" autosave="searchdoc" results="10" id="search" autocomplete="off"/>
|
|
59
|
+
</td></tr>
|
|
60
|
+
</table></div>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="tree">
|
|
63
|
+
<ul>
|
|
64
|
+
</ul>
|
|
65
|
+
</div>
|
|
66
|
+
<div class="result">
|
|
67
|
+
<ul>
|
|
68
|
+
</ul>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
<a href="links.html" id="links">index</a>
|
|
72
|
+
</body>
|
|
73
|
+
</html>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
<head>File index</head>
|
|
3
|
+
<body>
|
|
4
|
+
|
|
5
|
+
<a href="../files/Gemfile.html">Gemfile</a>
|
|
6
|
+
|
|
7
|
+
<a href="../files/LICENSE.html">LICENSE</a>
|
|
8
|
+
|
|
9
|
+
<a href="../files/Rakefile.html">Rakefile</a>
|
|
10
|
+
|
|
11
|
+
<a href="../files/lib/selbackup_rb.html">lib/selbackup.rb</a>
|
|
12
|
+
|
|
13
|
+
<a href="../files/spec/selbackup_spec_rb.html">spec/selbackup_spec.rb</a>
|
|
14
|
+
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|
data/doc/panel/tree.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var tree = [["","","files",[["Gemfile","files/Gemfile.html","",[]],["LICENSE","files/LICENSE.html","",[]],["Rakefile","files/Rakefile.html","",[]],["","","lib",[["selbackup.rb","files/lib/selbackup_rb.html","",[]]]],["","","spec",[["selbackup_spec.rb","files/spec/selbackup_spec_rb.html","",[]]]]]],["SelBackup","classes/SelBackup.html"," < Object",[]]]
|
data/lib/selbackup.rb
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
require 'active_support/core_ext'
|
|
2
|
+
require 'fog'
|
|
3
|
+
|
|
4
|
+
# SelBackup Class
|
|
5
|
+
#
|
|
6
|
+
# Enables connection to s3 server and uploads files
|
|
7
|
+
class SelBackup
|
|
8
|
+
# Attributes: informations required for connection to s3 server
|
|
9
|
+
attr_accessor :access_key, :access_secret, :bucket_name, :region
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Public: Initialize a new SelBackup instance.
|
|
13
|
+
#
|
|
14
|
+
# key:: A String representing the AWS ACCESS KEY ID.
|
|
15
|
+
#
|
|
16
|
+
# secret:: A String representing the AWS ACCESS KEY SECRET.
|
|
17
|
+
#
|
|
18
|
+
# bucket:: A String representing the name of the bucket ot use.
|
|
19
|
+
#
|
|
20
|
+
# :region (optional):: A String representing the region to conect to
|
|
21
|
+
# (default: 'eu-west-1').
|
|
22
|
+
#
|
|
23
|
+
# Examples
|
|
24
|
+
#
|
|
25
|
+
# SelectraBackup.new('key', 'secret', 'bucket', region: 'us-east-1')
|
|
26
|
+
#
|
|
27
|
+
# Returns the newly instanciated object.
|
|
28
|
+
def initialize(key, secret, bucket, region = 'eu-west-1')
|
|
29
|
+
@access_key = key
|
|
30
|
+
@access_secret = secret
|
|
31
|
+
@bucket_name = bucket
|
|
32
|
+
@region = region
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# Public: Returns the S3 bucket.
|
|
37
|
+
#
|
|
38
|
+
# Returns a Fog::Storage::AWS::Directory instance.
|
|
39
|
+
def bucket
|
|
40
|
+
@bucket ||= connection.directories.get(bucket_name)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# Public: daily check algorythm
|
|
45
|
+
#
|
|
46
|
+
# filename:: String which contains a filename. If you upload '2013-01-01-file.tgz', filename will be 'file.tgz'
|
|
47
|
+
#
|
|
48
|
+
# * If 7 daily files or less: does nothing
|
|
49
|
+
#
|
|
50
|
+
# * If more than 7 daily files but less than one week datediff between the latest daily file and the most recent weekly file: deletes the latest daily file
|
|
51
|
+
#
|
|
52
|
+
# * If more than 7 daily files but less than one week datediff between the latest daily file and the most recent weekly file: pushes the latest daily file to weekly file
|
|
53
|
+
# 2013-01-01-daily-file.tgz -> 2013-01-01-weekly-file.tgz
|
|
54
|
+
#
|
|
55
|
+
# Returns true if a file as been push. False in other cases
|
|
56
|
+
def check_daily(filename, nb_day)
|
|
57
|
+
return_value = false
|
|
58
|
+
daily_directory = give_me_files(filename, "daily")
|
|
59
|
+
if ! daily_good?(daily_directory, nb_day)
|
|
60
|
+
weekly_directory = give_me_files(filename, "weekly")
|
|
61
|
+
latest_daily_file = give_me_latest_file(daily_directory)
|
|
62
|
+
recent_weekly_file = give_me_recent_file(weekly_directory)
|
|
63
|
+
if weekly_directory == [] || should_weekly?(latest_daily_file.key, recent_weekly_file.key)
|
|
64
|
+
weekly_file = gen_weekly_file({ key: latest_daily_file.key, body: latest_daily_file.body})
|
|
65
|
+
push_file(weekly_file)
|
|
66
|
+
return_value = true
|
|
67
|
+
end
|
|
68
|
+
delete_file(latest_daily_file)
|
|
69
|
+
end
|
|
70
|
+
return return_value
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# Public: montlhy check algorythm
|
|
75
|
+
#
|
|
76
|
+
# filename:: String which contains a filename. If you upload '2013-01-01-file.tgz', filename will be 'file.tgz'
|
|
77
|
+
#
|
|
78
|
+
# * If 3 monthly files or less: does nothing
|
|
79
|
+
#
|
|
80
|
+
# * if more than 3 monthly files: deletes the latest monthly file
|
|
81
|
+
#
|
|
82
|
+
# Returns true if a file as been delete. False in other cases
|
|
83
|
+
def check_monthly(filename, nb_month)
|
|
84
|
+
return_value = false
|
|
85
|
+
monthly_directory = give_me_files(filename, "monthly")
|
|
86
|
+
if ! monthly_good?(monthly_directory, nb_month)
|
|
87
|
+
latest_monthly_file = give_me_latest_file(monthly_directory)
|
|
88
|
+
delete_file(latest_monthly_file)
|
|
89
|
+
return_value = true
|
|
90
|
+
end
|
|
91
|
+
return return_value
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# Public: weekly check algorythm
|
|
96
|
+
#
|
|
97
|
+
# filename:: String which contains a filename. If you upload '2013-01-01-file.tgz', filename will be 'file.tgz'
|
|
98
|
+
#
|
|
99
|
+
# * If 4 weekly files or less: does nothing
|
|
100
|
+
#
|
|
101
|
+
# * If more than 4 weekly files but less than one month datediff between the latest weekly file and the most recent monthly file: deletes the latest weekly file
|
|
102
|
+
#
|
|
103
|
+
# * If more than 4 weekly files but less than one week datediff between the latest weekly file and the most recent monthly file: pushes the latest weekly file to monthly file
|
|
104
|
+
# 2013-01-01-weekly-file.tgz -> 2013-01-01-monthly-file.tgz
|
|
105
|
+
#
|
|
106
|
+
# Returns true if a file as been push. False in other cases
|
|
107
|
+
def check_weekly(filename, nb_week)
|
|
108
|
+
return_value = false
|
|
109
|
+
weekly_directory = give_me_files(filename, "weekly")
|
|
110
|
+
if ! weekly_good?(weekly_directory, nb_week)
|
|
111
|
+
monthly_directory = give_me_files(filename, "monthly")
|
|
112
|
+
latest_weekly_file = give_me_latest_file(weekly_directory)
|
|
113
|
+
recent_monthly_file = give_me_recent_file(monthly_directory)
|
|
114
|
+
if monthly_directory == [] || should_monthly?(latest_weekly_file.key, recent_monthly_file.key)
|
|
115
|
+
monthly_file = gen_monthly_file({ key: latest_weekly_file.key, body: latest_weekly_file.body})
|
|
116
|
+
push_file(monthly_file)
|
|
117
|
+
return_value = true
|
|
118
|
+
end
|
|
119
|
+
delete_file(latest_weekly_file)
|
|
120
|
+
end
|
|
121
|
+
return return_value
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# Public: Returns the S3 connection.
|
|
126
|
+
#
|
|
127
|
+
# Returns a Fog::Storage::AWS::Real instance.
|
|
128
|
+
def connection
|
|
129
|
+
@connection ||= Fog::Storage.new(provider: 'AWS', aws_access_key_id: access_key, aws_secret_access_key: access_secret, region: region)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# Public: Check daily files number
|
|
134
|
+
#
|
|
135
|
+
# directoryFiles:: Array of daily files
|
|
136
|
+
# ['file/2013-01-01-daily-file.tgz', 'file/2013-01-02-daily-file.tgz', ...]
|
|
137
|
+
#
|
|
138
|
+
# Returns false if there is more than 7 daily files. False in other cases
|
|
139
|
+
def daily_good?(directoryFiles, nb_day)
|
|
140
|
+
return directoryFiles.length > nb_day ? false : true;
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
# Public: deletes a file
|
|
145
|
+
#
|
|
146
|
+
# file:: Fog::Storage::AWS::Files file
|
|
147
|
+
#
|
|
148
|
+
# returns nothing
|
|
149
|
+
def delete_file(file)
|
|
150
|
+
file.destroy
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# Public: Returns a virtual S3 directory.
|
|
155
|
+
#
|
|
156
|
+
# S3 doesn't know such a thing as a directory but it is possible to use a prefix to select only the files having a key starting with this prefix.
|
|
157
|
+
#
|
|
158
|
+
# path:: A String representing the prefix to use.
|
|
159
|
+
#
|
|
160
|
+
# Examples
|
|
161
|
+
#
|
|
162
|
+
# sb = SelectraBackup.new('key', 'secret', 'bucket')
|
|
163
|
+
# backup = sb.directory('backup/')
|
|
164
|
+
# backup.files
|
|
165
|
+
# # => <Fog::Storage::AWS::Files ...>
|
|
166
|
+
#
|
|
167
|
+
# Returns a Fog::Storage::AWS::Directory instance.
|
|
168
|
+
def directory(path)
|
|
169
|
+
connection.directories.get(bucket_name, prefix: path)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
# Public: Returns the list of files.
|
|
174
|
+
#
|
|
175
|
+
# Returns a Fog::Storage::AWS::Files instances.
|
|
176
|
+
def files
|
|
177
|
+
bucket.files
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# Public: returns lists of file
|
|
182
|
+
#
|
|
183
|
+
# filename: "file.tgz" for example
|
|
184
|
+
#
|
|
185
|
+
# type: specify if you want daily, weekly or monthly files
|
|
186
|
+
#
|
|
187
|
+
# Returns a sorted array
|
|
188
|
+
def give_me_files(filename, type)
|
|
189
|
+
full_directory = directory("#{filename}/")
|
|
190
|
+
files = []
|
|
191
|
+
full_directory.files.each do | file |
|
|
192
|
+
if file.key =~ /#{filename}\/\d{4}-\d{2}-\d{2}-#{type}-[\w+.]+/ ||
|
|
193
|
+
file.key =~ /#{filename}\/#{type}-\d{4}-\d{2}-\d{2}-[\w+.]+/
|
|
194
|
+
files.push(file)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
return files
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
# Public: returns latest file
|
|
202
|
+
#
|
|
203
|
+
# files: array of AWS object file
|
|
204
|
+
#
|
|
205
|
+
# Returns the latest file, based on the date
|
|
206
|
+
def give_me_latest_file(files)
|
|
207
|
+
latest_file = files.first
|
|
208
|
+
files.each do | file |
|
|
209
|
+
if Date.parse(file.key.gsub('-', '')) < Date.parse(latest_file.key.gsub('-', ''))
|
|
210
|
+
latest_file = file
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
return latest_file
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
# Public: returns most recent file
|
|
218
|
+
#
|
|
219
|
+
# files: array of AWS object file
|
|
220
|
+
#
|
|
221
|
+
# Returns the most recent file, based on the date
|
|
222
|
+
def give_me_recent_file(files)
|
|
223
|
+
recent_file = files.first
|
|
224
|
+
files.each do | file |
|
|
225
|
+
if Date.parse(file.key.gsub('-', '')) > Date.parse(recent_file.key.gsub('-', ''))
|
|
226
|
+
recent_file = file
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
return recent_file
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
# Public: Check monthly files number
|
|
234
|
+
#
|
|
235
|
+
# files:: Array of monthly files
|
|
236
|
+
# ['file/2013-01-01-monthly-file.tgz', 'file/2013-01-02-monthly-file.tgz', ...]
|
|
237
|
+
#
|
|
238
|
+
# Returns false if there is more than 3 monthly files. False in other cases
|
|
239
|
+
def monthly_good?(files, nb_month)
|
|
240
|
+
return files.length > nb_month ? false : true
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
# Public: gen a daily file
|
|
245
|
+
#
|
|
246
|
+
# file:: file you want to upload
|
|
247
|
+
#
|
|
248
|
+
# Returns an hash containing the filename, the key and the body of the daily file
|
|
249
|
+
def gen_daily_file(file)
|
|
250
|
+
name = File.basename(file)
|
|
251
|
+
if name =~ /[0-9]{4}-[0-9]{2}-[0-9]{2}(\.\w*)?$/
|
|
252
|
+
return false
|
|
253
|
+
elsif !(name =~ /[0-9]{4}-[0-9]{2}-[0-9]{2}-/)
|
|
254
|
+
name = "#{Date.today.to_s}-#{name}"
|
|
255
|
+
end
|
|
256
|
+
myname = name.split(/[0-9]{4}-[0-9]{2}-[0-9]{2}-/).last
|
|
257
|
+
mydate = name.split("-#{myname}").first
|
|
258
|
+
key = "#{myname}/#{mydate}-daily-#{myname}"
|
|
259
|
+
return { myname: myname, key: key, body: File.open(file) }
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
# Public: gen a monthly file
|
|
264
|
+
#
|
|
265
|
+
# file:: hash containing the key and the body of the weekly file you want to push to monthly file
|
|
266
|
+
#
|
|
267
|
+
# Returns an hash containing the key and the body of the monthly file
|
|
268
|
+
def gen_monthly_file(file)
|
|
269
|
+
if file[:key] =~ /[\w+.-]+\/[\w+.-]+-\d{4}-\d{2}-\d{2}-[\w+.-]+/
|
|
270
|
+
monthly_name = file[:key].sub("/weekly-", "/monthy-")
|
|
271
|
+
else
|
|
272
|
+
monthly_name = file[:key].sub("-weekly-", "-monthly-")
|
|
273
|
+
end
|
|
274
|
+
return { key: monthly_name, body: file[:body] }
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
# Public: gen a weekly file
|
|
279
|
+
#
|
|
280
|
+
# file:: hash containing the key and the body of the daily file you want to push to weekly file
|
|
281
|
+
#
|
|
282
|
+
# Returns an hash containing the key and the body of the weekly file
|
|
283
|
+
def gen_weekly_file(file)
|
|
284
|
+
if file[:key] =~ /[\w+.-]+\/[\w+.-]+-\d{4}-\d{2}-\d{2}-[\w+.-]+/
|
|
285
|
+
weekly_name = file[:key].sub("/daily-", "/weekly-")
|
|
286
|
+
else
|
|
287
|
+
weekly_name = file[:key].sub("-daily-", "-weekly-")
|
|
288
|
+
end
|
|
289
|
+
return { key: weekly_name, body: file[:body] }
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
# Public: pushes a file to s3 server
|
|
294
|
+
#
|
|
295
|
+
# file:: hash containing the key and the body of the file you want to upload
|
|
296
|
+
#
|
|
297
|
+
# Returns nothing
|
|
298
|
+
def push_file(file)
|
|
299
|
+
if file[:key] =~ /[\w+.-]+\/[\w+.-]+-\d{4}-\d{2}-\d{2}-[\w+.-]+/
|
|
300
|
+
filename = file[:key].split('/').first
|
|
301
|
+
name_parts = file[:key].split('/').last.split('-')
|
|
302
|
+
file[:key] = "#{filename}/#{name_parts[1]}-#{name_parts[2]}-#{name_parts[3]}-#{name_parts[0]}-#{filename}"
|
|
303
|
+
end
|
|
304
|
+
bucket.files.create(key: file[:key], body: file[:body])
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
# Public: date diff
|
|
309
|
+
#
|
|
310
|
+
# file1:: latest daily file
|
|
311
|
+
#
|
|
312
|
+
# file2:: most recent weekly file
|
|
313
|
+
#
|
|
314
|
+
# Returns true if datediff between the two files is upper than 7 days. False in other cases
|
|
315
|
+
def should_weekly?(file1, file2)
|
|
316
|
+
begin
|
|
317
|
+
date1 = Date.parse(file1.gsub('-', ''))
|
|
318
|
+
date2 = Date.parse(file2.gsub('-', ''))
|
|
319
|
+
rescue
|
|
320
|
+
print "Wrong date (Date.parse in should_monthly)."
|
|
321
|
+
return false
|
|
322
|
+
end
|
|
323
|
+
date_diff = date1 - date2
|
|
324
|
+
return date_diff.abs >= 7 ? true : false
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
# Public: date diff
|
|
329
|
+
#
|
|
330
|
+
# file1:: latest weekly file
|
|
331
|
+
#
|
|
332
|
+
# file2:: most recent monthly file
|
|
333
|
+
#
|
|
334
|
+
# Returns true if datediff between the two files is upper than 1 month. False in other cases
|
|
335
|
+
def should_monthly?(file1, file2)
|
|
336
|
+
begin
|
|
337
|
+
date1 = Date.parse(file1.gsub('-', '')).prev_month
|
|
338
|
+
date2 = Date.parse(file2.gsub('-', ''))
|
|
339
|
+
rescue
|
|
340
|
+
print "Wrong date (Date.parse in should_monthly)."
|
|
341
|
+
return false
|
|
342
|
+
end
|
|
343
|
+
return date1 >= date2 ? true : false
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
# Public: upload file algorythm
|
|
347
|
+
#
|
|
348
|
+
# file:: file you want to upload
|
|
349
|
+
#
|
|
350
|
+
# Uploads file as daily file and check daily, weekly and monthly full.
|
|
351
|
+
#
|
|
352
|
+
# Returns nothing
|
|
353
|
+
def upload_file(file, nb_day = 7, nb_week = 4, nb_month = 3)
|
|
354
|
+
daily_file = gen_daily_file(file)
|
|
355
|
+
if daily_file == false
|
|
356
|
+
return false
|
|
357
|
+
end
|
|
358
|
+
push_file(daily_file)
|
|
359
|
+
if check_daily(daily_file[:myname], nb_day)
|
|
360
|
+
if check_weekly(daily_file[:myname], nb_week)
|
|
361
|
+
check_monthly(daily_file[:myname], nb_month)
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
# Public: Check weekly files number
|
|
368
|
+
#
|
|
369
|
+
# files:: Array of daily files
|
|
370
|
+
# ['file/2013-01-01-weekly-file.tgz', 'file/013-01-02-weekly-2file.tgz', ...]
|
|
371
|
+
#
|
|
372
|
+
# Returns false if there is more than 4 weekly files. False in other cases
|
|
373
|
+
def weekly_good?(directoryFiles, nb_week)
|
|
374
|
+
return directoryFiles.length > nb_week ? false : true
|
|
375
|
+
end
|
|
376
|
+
end
|