siba 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (159) hide show
  1. data/.gitignore +7 -0
  2. data/Gemfile +4 -0
  3. data/Guardfile +18 -0
  4. data/LICENSE +22 -0
  5. data/README.md +47 -0
  6. data/Rakefile +27 -0
  7. data/bin/siba +5 -0
  8. data/lib/siba.rb +27 -0
  9. data/lib/siba/backup.rb +31 -0
  10. data/lib/siba/console.rb +196 -0
  11. data/lib/siba/errors.rb +8 -0
  12. data/lib/siba/generator.rb +115 -0
  13. data/lib/siba/globals.rb +19 -0
  14. data/lib/siba/helpers/encoding_helper.rb +38 -0
  15. data/lib/siba/helpers/file_helper.rb +89 -0
  16. data/lib/siba/helpers/gem_helper.rb +22 -0
  17. data/lib/siba/helpers/password_strength.rb +94 -0
  18. data/lib/siba/helpers/security_helper.rb +30 -0
  19. data/lib/siba/helpers/string_helper.rb +32 -0
  20. data/lib/siba/helpers/test/extend_test.rb +114 -0
  21. data/lib/siba/helpers/test/file_mock.rb +38 -0
  22. data/lib/siba/helpers/test/helper.rb +55 -0
  23. data/lib/siba/helpers/test/kernel_mock.rb +44 -0
  24. data/lib/siba/helpers/test/removable_constants.rb +18 -0
  25. data/lib/siba/helpers/test/require.rb +12 -0
  26. data/lib/siba/logger_plug.rb +36 -0
  27. data/lib/siba/options_backup.rb +37 -0
  28. data/lib/siba/options_loader.rb +38 -0
  29. data/lib/siba/plugins/archive/tar/archive.rb +117 -0
  30. data/lib/siba/plugins/archive/tar/init.rb +38 -0
  31. data/lib/siba/plugins/archive/tar/options.yml +1 -0
  32. data/lib/siba/plugins/destination/dir/dest_dir.rb +77 -0
  33. data/lib/siba/plugins/destination/dir/init.rb +31 -0
  34. data/lib/siba/plugins/destination/dir/options.yml +2 -0
  35. data/lib/siba/plugins/encryption/gpg/encryption.rb +140 -0
  36. data/lib/siba/plugins/encryption/gpg/init.rb +45 -0
  37. data/lib/siba/plugins/encryption/gpg/options.yml +2 -0
  38. data/lib/siba/plugins/installed_plugins.rb +77 -0
  39. data/lib/siba/plugins/plugin_loader.rb +100 -0
  40. data/lib/siba/plugins/plugins.rb +57 -0
  41. data/lib/siba/plugins/plugins.yml +9 -0
  42. data/lib/siba/plugins/source/files/files.rb +166 -0
  43. data/lib/siba/plugins/source/files/init.rb +33 -0
  44. data/lib/siba/plugins/source/files/options.yml +11 -0
  45. data/lib/siba/restore.rb +113 -0
  46. data/lib/siba/scaffold.rb +166 -0
  47. data/lib/siba/siba_check.rb +75 -0
  48. data/lib/siba/siba_file.rb +89 -0
  49. data/lib/siba/siba_kernel.rb +37 -0
  50. data/lib/siba/siba_logger.rb +172 -0
  51. data/lib/siba/tasks/siba_task.rb +42 -0
  52. data/lib/siba/tasks/siba_tasks.rb +120 -0
  53. data/lib/siba/test_files.rb +76 -0
  54. data/lib/siba/test_files/a_file +1 -0
  55. data/lib/siba/test_files/files_and_dirs/.hidden +1 -0
  56. data/lib/siba/test_files/files_and_dirs/.hidden_dir/file10 +1 -0
  57. data/lib/siba/test_files/files_and_dirs/File With Spaces +1 -0
  58. data/lib/siba/test_files/files_and_dirs/dir1/file10 +1 -0
  59. data/lib/siba/test_files/files_and_dirs/dir1/sub-dir/file111.txt +1 -0
  60. data/lib/siba/test_files/files_and_dirs/file1 +1 -0
  61. data/lib/siba/test_files/files_and_dirs/file2.txt +1 -0
  62. data/lib/siba/tmp_dir.rb +94 -0
  63. data/lib/siba/version.rb +5 -0
  64. data/scaffolds/archive.rb +26 -0
  65. data/scaffolds/destination.rb +20 -0
  66. data/scaffolds/encryption.rb +26 -0
  67. data/scaffolds/project/.gitignore +5 -0
  68. data/scaffolds/project/Gemfile +4 -0
  69. data/scaffolds/project/Guardfile +9 -0
  70. data/scaffolds/project/LICENSE +22 -0
  71. data/scaffolds/project/README.md +33 -0
  72. data/scaffolds/project/Rakefile +28 -0
  73. data/scaffolds/project/lib/siba-c6y-demo.rb +11 -0
  74. data/scaffolds/project/lib/siba-c6y-demo/options.yml +2 -0
  75. data/scaffolds/project/lib/siba-c6y-demo/version.rb +9 -0
  76. data/scaffolds/project/siba-c6y-demo.gemspec +26 -0
  77. data/scaffolds/project/test/helper/require_integration.rb +5 -0
  78. data/scaffolds/project/test/helper/require_unit.rb +4 -0
  79. data/scaffolds/project/test/integration/i9n_init.rb +35 -0
  80. data/scaffolds/project/test/unit/test_init.rb +43 -0
  81. data/scaffolds/project/test/unit/yml/valid.yml +8 -0
  82. data/scaffolds/shared/examples.rb +47 -0
  83. data/scaffolds/shared/init_example.rb +13 -0
  84. data/scaffolds/source.rb +25 -0
  85. data/siba.gemspec +30 -0
  86. data/test/helper/require_integration.rb +4 -0
  87. data/test/helper/require_unit.rb +4 -0
  88. data/test/integration/helpers/i9n_file_helper.rb +50 -0
  89. data/test/integration/i9n_backup.rb +53 -0
  90. data/test/integration/i9n_console.rb +16 -0
  91. data/test/integration/i9n_generator.rb +29 -0
  92. data/test/integration/i9n_options_backup.rb +22 -0
  93. data/test/integration/i9n_scaffold.rb +27 -0
  94. data/test/integration/i9n_siba_file.rb +30 -0
  95. data/test/integration/i9n_test_unicode_files.rb +40 -0
  96. data/test/integration/i9n_tmp_dir.rb +44 -0
  97. data/test/integration/plugins/archive/tar/i9n_archive.rb +18 -0
  98. data/test/integration/plugins/destination/dir/i9n_dest_dir.rb +52 -0
  99. data/test/integration/plugins/encryption/gpg/i9n_encryption.rb +87 -0
  100. data/test/integration/plugins/i9n_installed_plugins.rb +13 -0
  101. data/test/integration/plugins/source/files/i9n_files.rb +146 -0
  102. data/test/integration/tasks/i9n_siba_tasks.rb +30 -0
  103. data/test/integration/yml/valid.yml +16 -0
  104. data/test/unit/helpers/test_encoding_helper.rb +17 -0
  105. data/test/unit/helpers/test_gem_helper.rb +17 -0
  106. data/test/unit/helpers/test_security_helper.rb +21 -0
  107. data/test/unit/helpers/test_string_helper.rb +35 -0
  108. data/test/unit/plugins/archive/tar/test_archive.rb +41 -0
  109. data/test/unit/plugins/archive/tar/test_init.rb +36 -0
  110. data/test/unit/plugins/archive/tar/yml/archive/check_installed.yml +2 -0
  111. data/test/unit/plugins/archive/tar/yml/init/default_compression.yml +1 -0
  112. data/test/unit/plugins/archive/tar/yml/init/invalid_compression.yml +2 -0
  113. data/test/unit/plugins/archive/tar/yml/init/valid.yml +2 -0
  114. data/test/unit/plugins/destination/dir/test_dest_dir.rb +41 -0
  115. data/test/unit/plugins/destination/dir/test_init.rb +36 -0
  116. data/test/unit/plugins/destination/dir/yml/init/valid.yml +2 -0
  117. data/test/unit/plugins/encryption/gpg/test_encryption.rb +70 -0
  118. data/test/unit/plugins/encryption/gpg/test_init.rb +47 -0
  119. data/test/unit/plugins/source/files/test_files.rb +44 -0
  120. data/test/unit/plugins/source/files/test_init.rb +48 -0
  121. data/test/unit/plugins/source/files/test_path_match.rb +140 -0
  122. data/test/unit/plugins/source/files/yml/ignore_list.yml +8 -0
  123. data/test/unit/plugins/source/files/yml/ignore_not_array.yml +5 -0
  124. data/test/unit/plugins/source/files/yml/include_not_array.yml +3 -0
  125. data/test/unit/plugins/source/files/yml/include_subdirs_false.yml +6 -0
  126. data/test/unit/plugins/source/files/yml/include_subdirs_missing.yml +5 -0
  127. data/test/unit/plugins/source/files/yml/no_ignore.yml +4 -0
  128. data/test/unit/plugins/source/files/yml/no_include.yml +1 -0
  129. data/test/unit/plugins/source/files/yml/valid.yml +9 -0
  130. data/test/unit/plugins/test_installed_plugins.rb +32 -0
  131. data/test/unit/plugins/test_plugin_loader.rb +27 -0
  132. data/test/unit/plugins/test_plugins.rb +44 -0
  133. data/test/unit/tasks/test_siba_task.rb +30 -0
  134. data/test/unit/tasks/test_siba_tasks.rb +84 -0
  135. data/test/unit/tasks/yml/task/invalid.yml +4 -0
  136. data/test/unit/tasks/yml/task/valid.yml +7 -0
  137. data/test/unit/test_backup.rb +18 -0
  138. data/test/unit/test_console.rb +166 -0
  139. data/test/unit/test_generator.rb +21 -0
  140. data/test/unit/test_globals.rb +34 -0
  141. data/test/unit/test_log_message.rb +26 -0
  142. data/test/unit/test_logger_plug.rb +49 -0
  143. data/test/unit/test_options_backup.rb +21 -0
  144. data/test/unit/test_options_loader.rb +72 -0
  145. data/test/unit/test_password_strength.rb +76 -0
  146. data/test/unit/test_restore.rb +18 -0
  147. data/test/unit/test_scaffold.rb +26 -0
  148. data/test/unit/test_siba_check.rb +118 -0
  149. data/test/unit/test_siba_logger.rb +174 -0
  150. data/test/unit/test_tmp_dir.rb +21 -0
  151. data/test/unit/yml/options_loader/array.yml +2 -0
  152. data/test/unit/yml/options_loader/empty.yml +0 -0
  153. data/test/unit/yml/options_loader/invalid.yml +4 -0
  154. data/test/unit/yml/options_loader/string.yml +1 -0
  155. data/test/unit/yml/options_loader/utf8_with_bom.yml +2 -0
  156. data/test/unit/yml/options_loader/valid.yml +12 -0
  157. data/test/unit/yml/siba_options_backup.yml +20 -0
  158. data/test/unit/yml/valid.yml +18 -0
  159. metadata +240 -0
@@ -0,0 +1,77 @@
1
+ # encoding: UTF-8
2
+
3
+ module Siba::Destination
4
+ module Dir
5
+ class DestDir
6
+ include Siba::FilePlug
7
+ include Siba::LoggerPlug
8
+ attr_accessor :dir
9
+
10
+ def initialize(dir)
11
+ @dir = siba_file.file_expand_path dir
12
+ test_dir_access
13
+ end
14
+
15
+ def copy_backup_to_dest(path_to_backup)
16
+ siba_file.run_this "copy backup to dest" do
17
+ logger.info "Copying backup to destination directory: #{dir}"
18
+ unless siba_file.file_file? path_to_backup
19
+ raise Siba::Error, "Backup file '#{path_to_backup}' does not exist"
20
+ end
21
+ unless siba_file.file_directory? dir
22
+ raise Siba::Error, "Destination directory '#{dir}' does not exist"
23
+ end
24
+ siba_file.file_utils_cp(path_to_backup, dir)
25
+ end
26
+ end
27
+
28
+ def restore_backup_to_dir(backup_name, to_dir)
29
+ siba_file.run_this do
30
+ logger.info "Copying backup from destination directory: #{dir}"
31
+ path_to_backup = File.join dir, backup_name
32
+ unless siba_file.file_file? path_to_backup
33
+ raise Siba::Error, "Can not find backup #{path_to_backup}"
34
+ end
35
+
36
+ siba_file.file_utils_cp path_to_backup, to_dir
37
+ end
38
+ end
39
+
40
+ def test_dir_access
41
+ siba_file.run_this "test dir access" do
42
+ # create dest dir
43
+ begin
44
+ siba_file.file_utils_mkpath dir unless siba_file.file_directory? dir
45
+ rescue Exception
46
+ logger.error "Failed to create destination dir '#{dir}'."
47
+ raise
48
+ end
49
+
50
+ # copy a test file to dest dir
51
+ begin
52
+ test_file = Siba::TestFiles.prepare_test_file "destination_dir", dir
53
+ raise "Can not find the test file." unless siba_file.file_file? test_file
54
+ siba_file.file_utils_remove_entry_secure test_file
55
+ rescue Exception
56
+ logger.error "Could not write to destination dir '#{dir}'"
57
+ raise
58
+ end
59
+
60
+ logger.debug "Access to destination directory is verified"
61
+ end
62
+ end
63
+
64
+ # Returns an array of two-element arrays: [file_name, mtime]
65
+ def get_backups_list(backup_name)
66
+ siba_file.run_this do
67
+ Siba::FileHelper.entries(dir).select do |f|
68
+ f =~ /^#{backup_name}/
69
+ end.map do |f|
70
+ mtime = siba_file.file_mtime File.join dir, f
71
+ [f, mtime]
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'siba/plugins/destination/dir/dest_dir'
4
+
5
+ module Siba::Destination
6
+ module Dir
7
+ class Init
8
+ attr_accessor :dest_dir
9
+
10
+ def initialize(options)
11
+ dir = Siba::SibaCheck.options_string options, "dir"
12
+ @dest_dir = Siba::Destination::Dir::DestDir.new dir
13
+ end
14
+
15
+ def backup(path_to_backup_file)
16
+ @dest_dir.copy_backup_to_dest path_to_backup_file
17
+ end
18
+
19
+ # Put backup file into dir
20
+ def restore(backup_name, dir)
21
+ @dest_dir.restore_backup_to_dir backup_name, dir
22
+ end
23
+
24
+ # Returns an array of two-element arrays:
25
+ # [backup_file_name, modification_time]
26
+ def get_backups_list(backup_name)
27
+ @dest_dir.get_backups_list backup_name
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,2 @@
1
+ dir: destination_dir # relative to this options file
2
+ # or /absolute or ~/relative_to_home
@@ -0,0 +1,140 @@
1
+ # encoding: UTF-8
2
+
3
+ module Siba::Encryption
4
+ module Gpg
5
+ class Encryption
6
+ include Siba::LoggerPlug
7
+ include Siba::FilePlug
8
+
9
+ DEFAULT_CIPHER = "AES256"
10
+
11
+ attr_accessor :passphrase, :cipher
12
+
13
+ def initialize(passphrase, cipher=DEFAULT_CIPHER)
14
+ @passphrase = passphrase
15
+ @cipher = Encryption.check_cipher cipher
16
+ check_password_strength
17
+ test_encryption
18
+ end
19
+
20
+ def encrypt(path_to_archive)
21
+ path_to_encrypted_backup = "#{path_to_archive}.gpg"
22
+ siba_file.run_this("encrypt") do
23
+ if siba_file.file_file? path_to_encrypted_backup
24
+ raise Siba::Error, "Encrypted file #{path_to_encrypted_backup} already exists"
25
+ end
26
+ passphare_for_command = passphrase.gsub('"','\\"')
27
+ gpg_homedir = Siba::TestFiles.mkdir_in_tmp_dir "gpg-homedir"
28
+ command_without_password = %(gpg -c -q --batch --homedir="#{gpg_homedir}" --no-options --cipher-algo=#{cipher} --passphrase="****" --no-use-agent "#{path_to_archive}")
29
+ command = command_without_password.gsub "****", passphare_for_command
30
+ siba_file.run_shell command, "failed to encrypt: #{command_without_password}"
31
+ unless siba_file.file_file? path_to_encrypted_backup
32
+ raise siba::error, "failed to find encrypted backup file: #{command_without_password}"
33
+ end
34
+ end
35
+ path_to_encrypted_backup
36
+ end
37
+
38
+ def decrypt(path_to_encrypted_file, path_to_decrypted_file=nil)
39
+ path_to_decrypted_file = path_to_encrypted_file.gsub /\.gpg$/, "" if path_to_decrypted_file.nil?
40
+ siba_file.run_this("decrypt") do
41
+ if siba_file.file_file? path_to_decrypted_file
42
+ raise Siba::Error, "Decrypted file #{path_to_decrypted_file} already exists"
43
+ end
44
+ passphare_for_command = passphrase.gsub('"','\\"')
45
+ gpg_homedir = Siba::TestFiles.mkdir_in_tmp_dir "gpg-homedir"
46
+ command_without_password = %(gpg -d -q --batch --homedir="#{gpg_homedir}" --no-options --passphrase="****" -o "#{path_to_decrypted_file}" --no-use-agent "#{path_to_encrypted_file}")
47
+ command = command_without_password.gsub "****", passphare_for_command
48
+ siba_file.run_shell command, "failed to decrypt: #{command_without_password}"
49
+ unless siba_file.file_file? path_to_decrypted_file
50
+ raise siba::error, "failed to find decrypted backup file: #{command_without_password}"
51
+ end
52
+ end
53
+ path_to_decrypted_file
54
+ end
55
+
56
+ def test_encryption
57
+ begin
58
+ siba_file.run_this("test_encryption") do
59
+ # encrypt
60
+ path_to_source_file = Siba::TestFiles.prepare_test_file "encryption-gpg"
61
+ path_to_encrypted_file = encrypt path_to_source_file
62
+ raise unless path_to_encrypted_file == "#{path_to_source_file}.gpg"
63
+ raise unless siba_file.file_file? path_to_encrypted_file
64
+ raise if siba_file.file_utils_compare_file path_to_source_file, path_to_encrypted_file
65
+
66
+ # decrypt
67
+ path_to_output_file = "#{path_to_source_file}.decrypted"
68
+ path_to_decrypted_file = decrypt path_to_encrypted_file, path_to_output_file
69
+ raise unless path_to_decrypted_file == path_to_output_file
70
+ raise unless siba_file.file_utils_compare_file path_to_source_file, path_to_decrypted_file
71
+
72
+ logger.debug "GPG encryption is verified"
73
+ end
74
+ rescue Exception
75
+ logger.error "'gpg' encryption utility does not work correctly. Try reinstalling it."
76
+ raise
77
+ end
78
+ end
79
+
80
+ class << self
81
+ include Siba::FilePlug
82
+ include Siba::LoggerPlug
83
+
84
+ def check_cipher(cipher)
85
+ siba_file.run_this("check_cipher") do
86
+ supported_ciphers = nil
87
+ begin
88
+ supported_ciphers = Encryption.get_cipher_names
89
+ rescue Exception
90
+ logger.error "'gpg' encryption utility is not found. Please install it."
91
+ raise
92
+ end
93
+
94
+ supported_ciphers_msg = "Please use one of the following: #{supported_ciphers.join(', ')}."
95
+ if cipher.nil?
96
+ cipher = Encryption::DEFAULT_CIPHER
97
+ raise Siba::CheckError, "Default cipher '#{cipher}' is not supported.
98
+ #{supported_ciphers_msg}" unless supported_ciphers.include?(cipher)
99
+ else
100
+ cipher.upcase!
101
+ end
102
+
103
+ raise Siba::CheckError, "'#{cipher}' cipher is not supported.
104
+ #{supported_ciphers_msg}" unless supported_ciphers.include?(cipher)
105
+ end
106
+ cipher
107
+ end
108
+
109
+ def get_cipher_names
110
+ output = siba_file.run_shell "gpg --version"
111
+ cipher_names = parse_cipher_names output
112
+ raise Siba::Error, "Failed to get the list of supported ciphers" if cipher_names.empty?
113
+ cipher_names
114
+ end
115
+
116
+ def parse_cipher_names(version)
117
+ scan = version.scan /Cipher:(.*?)\n\w+:/m
118
+ scan = version.scan /Cipher:(.*)/m if scan.size == 0
119
+ if scan.size == 0 || !scan[0].is_a?(Array) || scan[0].size == 0 || !scan[0][0].is_a?(String)
120
+ raise "Failed to parse gpg version information"
121
+ end
122
+ scan = scan[0][0]
123
+ scan.gsub!(/ |\n/, "")
124
+ scan.split(",").each {|a| a.upcase!}
125
+ end
126
+ end
127
+
128
+ private
129
+
130
+ def check_password_strength
131
+ seconds_to_crack = Siba::PasswordStrength.seconds_to_crack(@passphrase)
132
+ if Siba::PasswordStrength.is_weak? seconds_to_crack
133
+ timespan_to_guess = Siba::PasswordStrength.seconds_to_timespan seconds_to_crack
134
+ guess_estimation_message = "It will take #{timespan_to_guess} to guess it."
135
+ logger.warn "Your encryption password is weak. #{guess_estimation_message}"
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'siba/helpers/password_strength'
4
+ require 'siba/plugins/encryption/gpg/encryption'
5
+
6
+ module Siba::Encryption
7
+ module Gpg
8
+ class Init
9
+ include Siba::LoggerPlug
10
+ include Siba::FilePlug
11
+ attr_accessor :encryption
12
+
13
+ def initialize(options)
14
+ passphrase = Siba::SibaCheck.options_string(options, "passphrase")
15
+ cipher = Siba::SibaCheck.options_string(options, "cipher", true)
16
+ @encryption = Siba::Encryption::Gpg::Encryption.new passphrase, cipher
17
+ end
18
+
19
+ # Encrypt backup archive file (path_to_archive) and put it to dest_dir.
20
+ # Return the name of encrypted file. It must begin with archive name
21
+ # and its ending must always be the same.
22
+ def backup(path_to_archive, dest_dir)
23
+ logger.info "Encrypting backup with 'gpg', cipher: '#{encryption.cipher}'"
24
+ path_to_encrypted_file = encryption.encrypt path_to_archive
25
+
26
+ # move encrypted file to dest_dir
27
+ file_name = File.basename path_to_encrypted_file
28
+ dest_file_path = File.join dest_dir, file_name
29
+ siba_file.file_utils_mv path_to_encrypted_file, dest_file_path
30
+ file_name
31
+ end
32
+
33
+ # Decrypt backup file (path_to_backup) to_dir.
34
+ # Return the name of decrypted file.
35
+ def restore(path_to_backup, to_dir)
36
+ logger.info "Decrypting backup with 'gpg', cipher: '#{encryption.cipher}'"
37
+ decrypted_file_name = File.basename path_to_backup
38
+ decrypted_file_name.gsub! /\.gpg$/, ""
39
+ path_to_decrypted_file = File.join to_dir, decrypted_file_name
40
+ encryption.decrypt path_to_backup, path_to_decrypted_file
41
+ decrypted_file_name
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,2 @@
1
+ passphrase: "<%= Siba::SecurityHelper.generate_password_for_yaml %>"
2
+ cipher: AES256 # or BLOWFISH, TWOFISH, 3DES...
@@ -0,0 +1,77 @@
1
+ # encoding: UTF-8
2
+
3
+ module Siba
4
+ class InstalledPlugins
5
+ GEM_PREFIX = "siba-"
6
+
7
+ class << self
8
+ include Siba::FilePlug
9
+
10
+ def installed?(category, type)
11
+ types = all_installed[category]
12
+ return false if types.nil?
13
+ types.include? type
14
+ end
15
+
16
+ def all_installed
17
+ @installed ||= find_installed
18
+ end
19
+
20
+ def plugin_path(category, type)
21
+ unless installed? category, type
22
+ raise Siba::Error, "Plugin #{plugin_category_and_type(category, type)} is not installed"
23
+ end
24
+ siba_file.run_this do
25
+ path = type_dir category, type
26
+ unless siba_file.file_directory? path
27
+ path = Siba::GemHelper.gem_path gem_name(category, type)
28
+ path = File.join path, "lib", gem_name(category, type)
29
+ end
30
+ unless siba_file.file_directory? path
31
+ raise Siba::Error, "Failed to get path to plugin #{plugin_category_and_type(category, type)}"
32
+ end
33
+ path
34
+ end
35
+ end
36
+
37
+ def category_dir(category)
38
+ File.expand_path "../#{category}/", __FILE__
39
+ end
40
+
41
+ def type_dir(category, type)
42
+ File.join category_dir(category), type
43
+ end
44
+
45
+ def gem_name(category, type)
46
+ "#{GEM_PREFIX}#{category}-#{type}"
47
+ end
48
+
49
+ def plugin_category_and_type(category, type)
50
+ "#{category}#{type.nil? ? "" : '-' + type}"
51
+ end
52
+
53
+ private
54
+
55
+ def find_installed
56
+ installed = {}
57
+ Siba::GemHelper.all_local_gems.map{|a| a.name}.each do |item|
58
+ Siba::Plugins::CATEGORIES.each do |category|
59
+ installed[category] ||= []
60
+ gem_prefix_full = /^#{GEM_PREFIX}#{category}-/
61
+ installed[category] << item.gsub(gem_prefix_full, '') if item =~ /#{gem_prefix_full}.*$/
62
+ end
63
+ end
64
+
65
+ Siba::Plugins::CATEGORIES.each do |category|
66
+ installed[category] += Dir.glob(File.join(category_dir(category),"*")).select { |entry|
67
+ File.directory? entry
68
+ }.select{|dir| File.file?(File.join(dir,"init.rb")) }
69
+ .map{|directory| File.basename(directory)}
70
+ end
71
+
72
+ installed
73
+ end
74
+
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,100 @@
1
+ # encoding: UTF-8
2
+
3
+ module Siba
4
+ class PluginLoader
5
+ include Siba::LoggerPlug
6
+ include Siba::FilePlug
7
+
8
+ InitClassName = "Init"
9
+
10
+ def self.loader
11
+ @loader ||= PluginLoader.new
12
+ end
13
+
14
+ def load(category, type, options)
15
+ unless Siba::Plugins.valid_category? category
16
+ raise PluginLoadError, "Incorrect plugin category '#{category}'. Available plugin categories are: #{Siba::Plugins.categories_str}"
17
+ end
18
+
19
+ raise PluginLoadError, "Options data is incorrect for #{plugin_category_and_type} plugin." unless options.is_a? Hash
20
+
21
+ @category=category
22
+ @type=type
23
+ @options = options
24
+ logger.debug "Loading #{plugin_category_and_type} plugin"
25
+
26
+ require_plugin
27
+ plugin_module = get_plugin_module
28
+ plugin_type_module = get_plugin_type_module plugin_module
29
+ init_class = get_plugin_init_class plugin_type_module
30
+ init_plugin(init_class)
31
+ end
32
+
33
+ private
34
+ attr_accessor :category, :type, :options
35
+
36
+ def require_plugin
37
+ path_to_init_file = File.join(plugin_dir, "init.rb")
38
+ if File.exists?(path_to_init_file)
39
+ require path_to_init_file
40
+ else
41
+ gem_name = Siba::InstalledPlugins.gem_name category, type
42
+ begin
43
+ Gem::Specification.find_by_name(gem_name)
44
+ rescue Gem::LoadError
45
+ raise PluginLoadError, "Unknown type '#{type}' for '#{category}' plugin. #{available_types_msg}"
46
+ end
47
+ require gem_name
48
+ end
49
+ end
50
+
51
+ def get_plugin_module
52
+ plugin_module_name = "#{category.capitalize}"
53
+ Siba.const_get(plugin_module_name)
54
+ rescue Exception
55
+ raise PluginLoadError, "Failed to load #{plugin_category_and_type} plugin: module 'Siba::#{plugin_module_name}' is undefined."
56
+ end
57
+
58
+ def get_plugin_type_module(plugin_module)
59
+ plugin_type_module_name = StringHelper.camelize type
60
+ plugin_module.const_get(plugin_type_module_name)
61
+ rescue
62
+ raise PluginLoadError, "Failed to load #{plugin_category_and_type} plugin: module 'Siba::#{category.capitalize}::#{plugin_type_module_name}' is undefined."
63
+ end
64
+
65
+ def get_plugin_init_class(plugin_type_module)
66
+ plugin_type_module.const_get InitClassName
67
+ rescue Exception => e
68
+ raise PluginLoadError, "#{InitClassName} class is undefined for #{plugin_category_and_type} plugin."
69
+ end
70
+
71
+ def init_plugin(plugin_init_class)
72
+ plugin_init_class.new options
73
+ rescue ArgumentError
74
+ logger.error "Failed to load #{plugin_category_and_type} plugin: 'initialize' method is not defined or has wrong agruments."
75
+ raise
76
+ end
77
+
78
+ def plugin_category_and_type
79
+ Siba::InstalledPlugins.plugin_category_and_type category, type
80
+ end
81
+
82
+ def plugins_root_dir
83
+ Siba::InstalledPlugins.category_dir category
84
+ end
85
+
86
+ def plugin_dir
87
+ Siba::InstalledPlugins.type_dir category, type
88
+ end
89
+
90
+ def available_types_msg
91
+ return "No #{category} plugins are installed." if find_all_installed.empty?
92
+ return "Available types are: #{find_all_installed.join(", ")}." if find_all_installed.size > 1
93
+ "Available type is '#{find_all_installed.join(", ")}'."
94
+ end
95
+
96
+ def find_all_installed
97
+ Siba::InstalledPlugins.all_installed[category]
98
+ end
99
+ end
100
+ end