ruby_clone 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ruby_clone.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,10 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ notification :off
5
+
6
+ guard 'rspec', :cli => "--colour" do
7
+ watch(%r{^spec/.+_spec\.rb$})
8
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
9
+ watch('spec/spec_helper.rb') { "spec" }
10
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Frederico Benevides
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,329 @@
1
+ # **RubyClone** - Simplifying the use of Rsync
2
+
3
+ **RubyClone** was designed to be simple, easy, clean and a high level script for RSync.
4
+
5
+ ## Installation
6
+
7
+ Install the gem:
8
+
9
+ gem install ruby_clone
10
+
11
+ Create a file that is named '.ruby_clone' in your home folder and set up a profile.
12
+ **NOTE**: Don't forget to use the dot in the file.
13
+
14
+ And then execute:
15
+
16
+ $ ruby_clone my_profile
17
+
18
+ You can also specify another path of your **RubyClone** file:
19
+
20
+ $ ruby_clone -b /my/ruby_clone_file my_profile
21
+
22
+ ## Usage
23
+
24
+ ### Basic Configuration
25
+
26
+ To start using it, you'll need to create the following configuration in your **RubyClone** file.
27
+
28
+ **NOTE**: The folder of from configurations needs to have the last slash. If it doesn't have, it will copy the
29
+ 'source_folder' inside the 'destination_folder'. (This behaviour is from RSync)
30
+
31
+ **NOTE 2**: If your source folder/destination_folder has spaces as "My Source Folder", you need to set up it as
32
+ "My\\ Source\\ Folder" with **two back slashes** for **each space**. Spaces and only one back slash will not work.
33
+
34
+
35
+ profile :my_profile do
36
+ from "/my/source_folder/"
37
+ to "/my/destination_folder"
38
+ end
39
+
40
+ If you set up this configuration in your $HOME/.ruby_clone, you can run with:
41
+
42
+ $ ruby_clone my_profile
43
+
44
+ If is in another path, run with:
45
+
46
+ $ ruby_clone -b /my/backup_file my_profile
47
+
48
+ **NOTE**: This basic setup doesn't sync deleted files from the source folder. If you want to delete them, you need
49
+ to setup the option 'delete: true'.
50
+
51
+ ### SSH
52
+
53
+ **RubyClone** offers the option to use SSH in a easy way. You just need to use the option ':ssh' with the 'user@host'.
54
+ Example for SSH that is on source folder.
55
+
56
+ profile :my_profile do
57
+ from "/my/source_folder/", ssh: "user@source_server"
58
+ to "/my/destination_folder"
59
+ end
60
+
61
+ Example for SSH that is on destination folder:
62
+
63
+ profile :my_profile do
64
+ from "/my/source_folder/"
65
+ to "/my/destination_folder", ssh: "user@destination_server"
66
+ end
67
+
68
+ After running one of theses profiles, you just need to type the password.
69
+
70
+ ## Improving your **RubyClone** file
71
+
72
+ **RubyClone** aims to be simple and be readable for you when you set up it. All the new configurations that you
73
+ can set up was created with this in mind.
74
+
75
+ Here is all the configuration you can set up and improve your **RubyClone** file.
76
+
77
+ ### Excluding a Pattern - exclude_pattern "pattern"
78
+
79
+ If you need to exclude folders or files matching a pattern, use the 'exclude_pattern command.
80
+ This command can be used in two ways: On the top of **RubyClone** file and/or inside the 'from'.
81
+ Here a complete example:
82
+
83
+ exclude_pattern "top_pattern"
84
+
85
+ profile :my_profile1 do
86
+ from "/my/source_folder/"
87
+ to "/my/destination_folder"
88
+ end
89
+
90
+ profile :my_profile2 do
91
+ from "/my/source_folder/" do
92
+ exclude_pattern "from_pattern"
93
+ end
94
+ to "/my/destination_folder"
95
+ end
96
+
97
+ 1. 'my_profile1' will exclude only the "top_pattern".
98
+ 2. 'my_profile2' will exclude the "top_pattern" and "from_pattern"
99
+
100
+ ### Including a Pattern - include_pattern "pattern"
101
+
102
+ Same as the exclude_pattern. If you need to include folders or files matching a pattern, use the
103
+ include_pattern command. This command can be use in two ways: On the top of **RubyClone** file and/or
104
+ inside the 'from' block. Example:
105
+
106
+ include_pattern "top_pattern"
107
+
108
+ profile :my_profile1 do
109
+ from "/my/source_folder/"
110
+ to "/my/destination_folder"
111
+ end
112
+
113
+ profile :my_profile2 do
114
+ from "/my/source_folder/" do
115
+ include_pattern "from_pattern"
116
+ end
117
+ to "/my/destination_folder"
118
+ end
119
+
120
+ 1. 'my_profile1' will include only the "top_pattern".
121
+ 2. 'my_profile2' will include the "top_pattern" and "from_pattern"
122
+
123
+ Here an example using exclude_pattern and include_pattern together. In this example, :my_profile will exclude
124
+ all folders and will sync/include only the 'from_pattern'
125
+
126
+ profile :my_profile do
127
+ from "/my/source_folder/" do
128
+ exclude_pattern "*"
129
+ include_pattern "from_pattern"
130
+ end
131
+ to "/my/destination_folder"
132
+ end
133
+
134
+ ### Deleting files
135
+
136
+ 1. Deleting files that don't exist in source_folder but in destination folder - delete: true
137
+
138
+ The basic configuration was created to be a secure configuration. So if you really want to delete files/folders
139
+ in your destination folder that doesn't exist anymore in your source folder, you'll need to set up 'delete: true' in
140
+ your **RubyClone** file. Below an example how to do it:
141
+
142
+ profile :my_profile do
143
+ from "/my/source_folder/"
144
+ to "/my/destination_folder", delete: true
145
+ end
146
+
147
+ Now all files that exists in "/my/destination_folder/" but "/my/source_folder" will be deleted. If for some
148
+ reason you want to keep some files and delete others, just set up the exclude_pattern inside 'from' block.
149
+
150
+ 2. Deleting files that are excluded from source folder to destination folder - delete_excluded: true
151
+
152
+ If you decided to exclude files from the syncronization but they still in your destination folder, you can use
153
+ the 'delete_excluded' to delete this files inside destination folder that are excluded from the source_folder.
154
+ Example:
155
+
156
+ profile :my_profile do
157
+ from "/my/source_folder/" do
158
+ exclude_pattern "my_pattern"
159
+ end
160
+ to "/my/destination_folder", delete_excluded: true
161
+ end
162
+
163
+ Now all files that are inside destination_folder that have the pattern 'my_pattern' will be deleted.
164
+
165
+ **NOTE**: When you use 'delete_excluded :true' you don't need to set up 'delete: true' even the from
166
+ configuration doesn't have the 'exclude_pattern'
167
+
168
+ ### Backuping files - backup "folder"
169
+
170
+ 1. If you want to save all the files that get **updated** and **deleted**, you need to set up the 'backup'
171
+ configuration inside the 'to' block. Example:
172
+
173
+ profile :my_profile do
174
+ from "/my/source_folder/"
175
+ to "/my/destination_folder", delete: true do
176
+ backup "/my/backup_folder"
177
+ end
178
+ end
179
+
180
+ Now all the folders/files that get **updated** or **deleted** in the "/my/destination_folder" will be moved to
181
+ "/my/backup_folder". The files moved to "/my/destination_folder" will have a default suffix that is a **RubyClone**
182
+ control key plus the Date with the format: "yyyyMMdd".
183
+
184
+ All files that are saved in the "/my/backup_folder" will have a limit of 5 files. So if any file that passed this
185
+ limit, the old file will be deleted. You can change this limit using the option "limit".
186
+
187
+ **NOTE**: The **RubyClone** control key is a special key to use as a control to remove old duplicated files.
188
+
189
+ 2. Changing the suffix of the files inside the backup folder - backup "folder", suffix: "my_suffix"
190
+
191
+ If you want to change the Date suffix for the files that you backup, you need to set the 'suffix' as a parameter to 'backup'.
192
+ Example:
193
+
194
+ profile :my_profile do
195
+ from "/my/source_folder/"
196
+ to "/my/destination_folder", delete: true do
197
+ backup "/my/backup_folder", suffix: "my_suffix"
198
+ end
199
+ end
200
+
201
+ Another example using suffix with the Time class of Ruby to generate the date with timestamp "yyyyMMdd_hhmmss" :
202
+
203
+ profile :my_profile do
204
+ from "/my/source_folder/"
205
+ to "/my/destination_folder", delete: true do
206
+ backup "/my/backup_folder", suffix: "_#{Time.now.strftime("%Y%m%d_%H%M%S")}"
207
+ end
208
+ end
209
+
210
+ **NOTE**: Changing the suffix will not change the **RubyClone** control key. Since this key control how to remove the
211
+ old duplicated files.
212
+
213
+ 3. Disabling the default suffix - backup "folder", disable_suffix: true
214
+
215
+ If for some reason you want to disable the file suffix in the backup folder you can set true for 'default_true'. Doing
216
+ that all the files that are moved to "/my/backup_folder" will have the same name and it will get override by a new file.
217
+ Example:
218
+
219
+ profile :my_profile do
220
+ from "/my/source_folder/"
221
+ to "/my/destination_folder", delete: true do
222
+ backup "/my/backup_folder", disable_suffix: true
223
+ end
224
+ end
225
+
226
+ **NOTE**: This kind of setup will **backup only one file** and nothing more.
227
+
228
+ 4. Changing how many files you want to backup - backup "folder", limit: 10
229
+
230
+ **RubyClone** have a default to save 5 files "/my/backup_folder" that have the same name. If you want to change the
231
+ limit you just need to set up the option limit with the number of files to save.
232
+ Example:
233
+
234
+ profile :my_profile do
235
+ from "/my/source_folder/"
236
+ to "/my/destination_folder", delete: true do
237
+ backup "/my/backup_folder", limit: 20
238
+ end
239
+ end
240
+
241
+ Now "/my/backup_folder" will save 20 files with the same name.
242
+
243
+ 5. Disabling the limit of files to save. Save unlimited files - backup "folder", limit: :unlimited
244
+
245
+ If for some reason you want to backup all files that are **updated** or **deleted** you need to set up the 'limit'
246
+ option with ':unlimited'
247
+ Example:
248
+
249
+ profile :my_profile do
250
+ from "/my/source_folder/"
251
+ to "/my/destination_folder", delete: true do
252
+ backup "/my/backup_folder", limit: :unlimited
253
+ end
254
+ end
255
+
256
+ Now all files will be saved, until you change the 'limit' to a specific number to erased all the old files that passed
257
+ the new limit.
258
+
259
+ ### Disabling the output commands of RSync - config options
260
+
261
+ 1. **RubyClone** offers the possibility to not show the rsync command generated (show_command) and rsync output
262
+ (show_output). To use it, you need to set up in the top of your **RubyClone** file the 'config' and the commands you
263
+ want to disable. Example:
264
+
265
+ config show_command: false, show_output: false
266
+
267
+ profile :my_profile do
268
+ from "/my/source_folder/"
269
+ to "/my/destination_folder"
270
+ end
271
+
272
+ The above config will not show anymore the rsync command and rsync outputs. But errors will keep showing if
273
+ happen.
274
+
275
+ 2. Overriding the default configuration of Rsync command - config options: 'override_options'
276
+
277
+ If you need to override the default configurations for RSync you can set up the "config options: 'my_options'".
278
+ Example:
279
+
280
+ config options: '-Cav --stats'
281
+
282
+ profile :my_profile do
283
+ from "/my/source_folder/"
284
+ to "/my/destination_folder"
285
+ end
286
+
287
+ ## Running
288
+
289
+ **RubyClone** as default will try to read the **RubyClone** file in your home folder: $HOME/.ruby_clone. If you
290
+ need to specify another path of your **RubyClone** file don't forget to add the option -b
291
+
292
+ $ ruby_clone -b /my/ruby_clone_file profile
293
+
294
+ RSync offers the options to dry-run the command and just run the sincronization as a simulation. You can do this
295
+ too with **RubyClone**. Just pass the option '-d' to ruby_clone
296
+
297
+ $ ruby_clone -d profile
298
+
299
+ ## More interesting **RubyClone** file
300
+
301
+ Here a interesting **RubyClone** file that you can improve.
302
+
303
+ **NOTE**: Don't forget that the folder of from configuration needs to have the last slash. If it doesn't have,
304
+ it will copy the 'source_folder' inside the 'destination_folder. (This behaviour is from RSync)
305
+
306
+ **NOTE 2**: If your source folder/destination_folder has spaces as "/My Source Folder", you need to set up it as
307
+ "My\\ Source\\ Folder" with **two back slashes** for **each space**. Spaces and only one back slash will not work.
308
+
309
+ profile :my_profile do
310
+ from "/my/source_folder/" do
311
+ exclude_pattern "pattern"
312
+ end
313
+ to "/my/destination_folder", delete_excluded: true do
314
+ backup "/my/backup"
315
+ end
316
+ end
317
+
318
+ ## **WARNINGS**
319
+
320
+ Even this script were has been tested many times, you run this script entirely at your own risk.
321
+ **RubyClone** accepts no responsibility for any damage this script may cause.
322
+
323
+ ## Contributing to **RubyClone**
324
+
325
+ * Fork, fix, then send me a pull request.
326
+
327
+ ## Copyright
328
+
329
+ Copyright (c) 2012 Frederico Benevides. See MIT-LICENSE for further details.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/bin/ruby_clone ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ruby_clone'
4
+
5
+ extend RubyClone::DSL
6
+
7
+ options = RubyClone::Options.new(STDOUT)
8
+ options.parse(ARGV)
9
+
10
+ if !ARGV.empty?
11
+ load File.expand_path options.configurations[:backup_file]
12
+
13
+ rsync = current_rsync
14
+ rsync.dry_run = options.configurations[:dry_run]
15
+ run_backup ARGV[0]
16
+ end
@@ -0,0 +1,41 @@
1
+ require 'fileutils'
2
+ require 'find'
3
+
4
+ module RubyClone
5
+
6
+ module BackupUtils
7
+
8
+ def self.delete_files(path, ruby_clone_suffix, limit)
9
+
10
+ if File.exists?(path)
11
+ paths = find_files_with_same_name path, ruby_clone_suffix
12
+ delete_files_reached_the_limit paths, limit
13
+ end
14
+ end
15
+
16
+ def self.find_files_with_same_name(path, ruby_clone_suffix)
17
+ paths = {}
18
+
19
+ Find.find(path) do |path|
20
+ path_without_suffix = path.sub(/#{ruby_clone_suffix}.*/, '')
21
+
22
+ if FileTest.file? path
23
+ paths[path_without_suffix] ||= []
24
+ paths[path_without_suffix] << path
25
+ end
26
+ end
27
+
28
+ paths
29
+ end
30
+
31
+ def self.delete_files_reached_the_limit(paths, limit)
32
+ paths.each_value do |files|
33
+ if files.size > limit
34
+ files.slice(0, files.size - limit).each do |file|
35
+ FileUtils.remove_entry file
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,79 @@
1
+ module RubyClone
2
+
3
+ module DSL
4
+
5
+ lambda {
6
+
7
+ rsync = RubyClone::RSync.new(STDOUT)
8
+ current_object = rsync
9
+
10
+ define_method :current_rsync do
11
+ rsync
12
+ end
13
+
14
+ define_method :rsync_new_instance do
15
+ rsync = RubyClone::RSync.new(STDOUT)
16
+ current_object = rsync
17
+ end
18
+
19
+ define_method :run_backup do |profile|
20
+ rsync.run profile
21
+ end
22
+
23
+ define_method :profile do |name, &block|
24
+ profile = Profile.new(name)
25
+ current_object = profile
26
+
27
+ if block
28
+ rsync.profiles = profile
29
+ called_block = block.call
30
+
31
+ raise SyntaxError, 'Empty Profile not allowed' if not called_block
32
+ else
33
+ raise SyntaxError, 'Empty Profile not allowed'
34
+ end
35
+
36
+ current_object = rsync
37
+ end
38
+
39
+ define_method :from do |folder, options = {}, &block|
40
+ from_folder = FromFolder.new(folder, options)
41
+ current_object = from_folder
42
+
43
+ rsync.last_profile.from_folder = from_folder
44
+
45
+ block.call if block
46
+ current_object = rsync
47
+ end
48
+
49
+ define_method :to do |folder, options = {}, &block|
50
+ to_folder = ToFolder.new(folder, options)
51
+ current_object = to_folder
52
+
53
+ rsync.last_profile.to_folder = to_folder
54
+
55
+ block.call if block
56
+ current_object = rsync
57
+ end
58
+
59
+ define_method :backup do |path, options = {}|
60
+ backup = Backup.new(path, options)
61
+ current_object.backup = backup
62
+ end
63
+
64
+ define_method :exclude_pattern do |path|
65
+ current_object.exclude_pattern = path
66
+ end
67
+
68
+ define_method :include_pattern do |path|
69
+ current_object.include_pattern = path
70
+ end
71
+
72
+ define_method :config do |configurations|
73
+ current_object.update_configurations configurations
74
+ end
75
+
76
+ }.call
77
+
78
+ end
79
+ end
@@ -0,0 +1,38 @@
1
+ require 'optparse'
2
+
3
+ module RubyClone
4
+
5
+ class Options
6
+ attr_reader :configurations
7
+ attr_reader :profile
8
+
9
+ def initialize(output)
10
+ @output = output
11
+ @configurations = {}
12
+ @configurations[:backup_file] = '~/.ruby_clone'
13
+ end
14
+
15
+ def parse(argv)
16
+ opts = OptionParser.new do |opts|
17
+ opts.banner = "Usage: ruby_clone [options] profile"
18
+
19
+ opts.on("-b", "--backup-file path", String, "Change the path to backup file (default is #{ENV['HOME']}/.ruby_clone)") do |backup_file|
20
+ @configurations[:backup_file] = backup_file
21
+ end
22
+
23
+ opts.on("-d", "--dry-run", "Show what would have been transferred") do
24
+ @configurations[:dry_run] = true
25
+ end
26
+
27
+ opts.on("-h", "--help", "This message") do
28
+ @output.puts opts
29
+ end
30
+
31
+ argv = %w[-h] if argv.empty?
32
+ opts.parse!(argv)
33
+ end
34
+ opts
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,128 @@
1
+ module RubyClone
2
+
3
+ class FromFolder
4
+ attr_accessor :path
5
+
6
+ def initialize(path, options = {})
7
+ @exclude_patterns = []
8
+ @include_patterns = []
9
+ @options = options
10
+ @path = path
11
+ end
12
+
13
+ def exclude_pattern=(path)
14
+ @exclude_patterns << path
15
+ end
16
+
17
+ def include_pattern=(path)
18
+ @include_patterns << path
19
+ end
20
+
21
+ def to_command
22
+ command = ""
23
+ command << @include_patterns.map { |e| "--include=#{e}" }.join(" ") + " "
24
+ command << @exclude_patterns.map { |e| "--exclude=#{e}" }.join(" ")
25
+ command.strip
26
+ end
27
+
28
+ def ssh?
29
+ if @options[:ssh]
30
+ true
31
+ else
32
+ false
33
+ end
34
+ end
35
+
36
+ def to_s
37
+ return "#{@options[:ssh]}:#{@path}" if @options[:ssh]
38
+ return @path if not @options[:ssh]
39
+ end
40
+ end
41
+
42
+ class ToFolder
43
+ attr_accessor :backup
44
+ attr_accessor :path
45
+
46
+ def initialize(path, options = {})
47
+ @path = path
48
+ @options = options
49
+ end
50
+
51
+ def delete_files
52
+ @backup.delete_files if @backup
53
+ end
54
+
55
+ def to_command
56
+ command = ""
57
+ command << "--delete " if @options[:delete]
58
+ command << "--delete-excluded " if @options[:delete_excluded]
59
+
60
+ command << backup.to_command if backup
61
+ command.strip
62
+ end
63
+
64
+ def ssh?
65
+ if @options[:ssh]
66
+ true
67
+ else
68
+ false
69
+ end
70
+ end
71
+
72
+ def to_s
73
+ return "#{@options[:ssh]}:#{@path}" if @options[:ssh]
74
+ return @path if not @options[:ssh]
75
+ end
76
+ end
77
+
78
+ class Profile
79
+ attr_accessor :name
80
+ attr_accessor :from_folder
81
+ attr_accessor :to_folder
82
+
83
+ def initialize(name)
84
+ @name = name.to_s
85
+ end
86
+
87
+ def to_s
88
+ @name
89
+ end
90
+ end
91
+
92
+ class Backup
93
+ attr_accessor :path
94
+
95
+ def initialize(path, options = { })
96
+ @default_suffix = '_rbcl'
97
+ @options = { disable_suffix: false, limit: 5 }.merge(options)
98
+ @path = path
99
+
100
+ end
101
+
102
+ def delete_files
103
+ BackupUtils.delete_files @path, @default_suffix, @options[:limit] if @options[:limit] != :unlimited
104
+ end
105
+
106
+ def to_command
107
+ command = "-b "
108
+ command << "#{create_suffix} " if not @options[:disable_suffix]
109
+ command << "--backup-dir=#{path} "
110
+ command.strip
111
+ end
112
+
113
+ private
114
+
115
+ def create_suffix
116
+ time = @time || Time.now.strftime("%Y%m%d")
117
+ command = "--suffix="
118
+ command << "#{@default_suffix}_#{time}" if not @options[:suffix]
119
+ command << "#{@default_suffix}#{@options[:suffix]}" if @options[:suffix]
120
+ command
121
+ end
122
+
123
+ def to_s
124
+ @path
125
+ end
126
+ end
127
+
128
+ end