backup-remote 0.0.2

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.
Files changed (135) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +112 -0
  3. data/bin/backup-remote +5 -0
  4. data/lib/backup.rb +155 -0
  5. data/lib/backup/archive.rb +170 -0
  6. data/lib/backup/binder.rb +22 -0
  7. data/lib/backup/cleaner.rb +116 -0
  8. data/lib/backup/cli.rb +374 -0
  9. data/lib/backup/cloud_io/base.rb +41 -0
  10. data/lib/backup/cloud_io/cloud_files.rb +298 -0
  11. data/lib/backup/cloud_io/s3.rb +260 -0
  12. data/lib/backup/compressor/base.rb +35 -0
  13. data/lib/backup/compressor/bzip2.rb +39 -0
  14. data/lib/backup/compressor/custom.rb +53 -0
  15. data/lib/backup/compressor/gzip.rb +74 -0
  16. data/lib/backup/config.rb +121 -0
  17. data/lib/backup/config/dsl.rb +106 -0
  18. data/lib/backup/config/helpers.rb +143 -0
  19. data/lib/backup/database/base.rb +85 -0
  20. data/lib/backup/database/mongodb.rb +187 -0
  21. data/lib/backup/database/mysql.rb +192 -0
  22. data/lib/backup/database/openldap.rb +95 -0
  23. data/lib/backup/database/postgresql.rb +133 -0
  24. data/lib/backup/database/redis.rb +179 -0
  25. data/lib/backup/database/remote_mysql.rb +248 -0
  26. data/lib/backup/database/riak.rb +82 -0
  27. data/lib/backup/database/sqlite.rb +57 -0
  28. data/lib/backup/encryptor/base.rb +29 -0
  29. data/lib/backup/encryptor/gpg.rb +747 -0
  30. data/lib/backup/encryptor/open_ssl.rb +77 -0
  31. data/lib/backup/errors.rb +58 -0
  32. data/lib/backup/logger.rb +199 -0
  33. data/lib/backup/logger/console.rb +51 -0
  34. data/lib/backup/logger/fog_adapter.rb +29 -0
  35. data/lib/backup/logger/logfile.rb +133 -0
  36. data/lib/backup/logger/syslog.rb +116 -0
  37. data/lib/backup/model.rb +479 -0
  38. data/lib/backup/notifier/base.rb +128 -0
  39. data/lib/backup/notifier/campfire.rb +63 -0
  40. data/lib/backup/notifier/command.rb +102 -0
  41. data/lib/backup/notifier/datadog.rb +107 -0
  42. data/lib/backup/notifier/flowdock.rb +103 -0
  43. data/lib/backup/notifier/hipchat.rb +118 -0
  44. data/lib/backup/notifier/http_post.rb +117 -0
  45. data/lib/backup/notifier/mail.rb +249 -0
  46. data/lib/backup/notifier/nagios.rb +69 -0
  47. data/lib/backup/notifier/pagerduty.rb +81 -0
  48. data/lib/backup/notifier/prowl.rb +68 -0
  49. data/lib/backup/notifier/pushover.rb +74 -0
  50. data/lib/backup/notifier/ses.rb +105 -0
  51. data/lib/backup/notifier/slack.rb +148 -0
  52. data/lib/backup/notifier/twitter.rb +58 -0
  53. data/lib/backup/notifier/zabbix.rb +63 -0
  54. data/lib/backup/package.rb +55 -0
  55. data/lib/backup/packager.rb +107 -0
  56. data/lib/backup/pipeline.rb +128 -0
  57. data/lib/backup/remote/command.rb +82 -0
  58. data/lib/backup/splitter.rb +76 -0
  59. data/lib/backup/storage/base.rb +69 -0
  60. data/lib/backup/storage/cloud_files.rb +158 -0
  61. data/lib/backup/storage/cycler.rb +75 -0
  62. data/lib/backup/storage/dropbox.rb +212 -0
  63. data/lib/backup/storage/ftp.rb +112 -0
  64. data/lib/backup/storage/local.rb +64 -0
  65. data/lib/backup/storage/qiniu.rb +65 -0
  66. data/lib/backup/storage/rsync.rb +248 -0
  67. data/lib/backup/storage/s3.rb +156 -0
  68. data/lib/backup/storage/scp.rb +67 -0
  69. data/lib/backup/storage/sftp.rb +82 -0
  70. data/lib/backup/syncer/base.rb +70 -0
  71. data/lib/backup/syncer/cloud/base.rb +179 -0
  72. data/lib/backup/syncer/cloud/cloud_files.rb +83 -0
  73. data/lib/backup/syncer/cloud/local_file.rb +100 -0
  74. data/lib/backup/syncer/cloud/s3.rb +110 -0
  75. data/lib/backup/syncer/rsync/base.rb +54 -0
  76. data/lib/backup/syncer/rsync/local.rb +31 -0
  77. data/lib/backup/syncer/rsync/pull.rb +51 -0
  78. data/lib/backup/syncer/rsync/push.rb +205 -0
  79. data/lib/backup/template.rb +46 -0
  80. data/lib/backup/utilities.rb +224 -0
  81. data/lib/backup/version.rb +5 -0
  82. data/templates/cli/archive +28 -0
  83. data/templates/cli/compressor/bzip2 +4 -0
  84. data/templates/cli/compressor/custom +7 -0
  85. data/templates/cli/compressor/gzip +4 -0
  86. data/templates/cli/config +123 -0
  87. data/templates/cli/databases/mongodb +15 -0
  88. data/templates/cli/databases/mysql +18 -0
  89. data/templates/cli/databases/openldap +24 -0
  90. data/templates/cli/databases/postgresql +16 -0
  91. data/templates/cli/databases/redis +16 -0
  92. data/templates/cli/databases/riak +17 -0
  93. data/templates/cli/databases/sqlite +11 -0
  94. data/templates/cli/encryptor/gpg +27 -0
  95. data/templates/cli/encryptor/openssl +9 -0
  96. data/templates/cli/model +26 -0
  97. data/templates/cli/notifier/zabbix +15 -0
  98. data/templates/cli/notifiers/campfire +12 -0
  99. data/templates/cli/notifiers/command +32 -0
  100. data/templates/cli/notifiers/datadog +57 -0
  101. data/templates/cli/notifiers/flowdock +16 -0
  102. data/templates/cli/notifiers/hipchat +16 -0
  103. data/templates/cli/notifiers/http_post +32 -0
  104. data/templates/cli/notifiers/mail +24 -0
  105. data/templates/cli/notifiers/nagios +13 -0
  106. data/templates/cli/notifiers/pagerduty +12 -0
  107. data/templates/cli/notifiers/prowl +11 -0
  108. data/templates/cli/notifiers/pushover +11 -0
  109. data/templates/cli/notifiers/ses +15 -0
  110. data/templates/cli/notifiers/slack +22 -0
  111. data/templates/cli/notifiers/twitter +13 -0
  112. data/templates/cli/splitter +7 -0
  113. data/templates/cli/storages/cloud_files +11 -0
  114. data/templates/cli/storages/dropbox +20 -0
  115. data/templates/cli/storages/ftp +13 -0
  116. data/templates/cli/storages/local +8 -0
  117. data/templates/cli/storages/qiniu +12 -0
  118. data/templates/cli/storages/rsync +17 -0
  119. data/templates/cli/storages/s3 +16 -0
  120. data/templates/cli/storages/scp +15 -0
  121. data/templates/cli/storages/sftp +15 -0
  122. data/templates/cli/syncers/cloud_files +22 -0
  123. data/templates/cli/syncers/rsync_local +20 -0
  124. data/templates/cli/syncers/rsync_pull +28 -0
  125. data/templates/cli/syncers/rsync_push +28 -0
  126. data/templates/cli/syncers/s3 +27 -0
  127. data/templates/general/links +3 -0
  128. data/templates/general/version.erb +2 -0
  129. data/templates/notifier/mail/failure.erb +16 -0
  130. data/templates/notifier/mail/success.erb +16 -0
  131. data/templates/notifier/mail/warning.erb +16 -0
  132. data/templates/storage/dropbox/authorization_url.erb +6 -0
  133. data/templates/storage/dropbox/authorized.erb +4 -0
  134. data/templates/storage/dropbox/cache_file_written.erb +10 -0
  135. metadata +1122 -0
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ module Backup
4
+ module Compressor
5
+ class Base
6
+ include Utilities::Helpers
7
+ include Config::Helpers
8
+
9
+ ##
10
+ # Yields to the block the compressor command and filename extension.
11
+ def compress_with
12
+ log!
13
+ yield @cmd, @ext
14
+ end
15
+
16
+ private
17
+
18
+ ##
19
+ # Return the compressor name, with Backup namespace removed
20
+ def compressor_name
21
+ self.class.to_s.sub('Backup::', '')
22
+ end
23
+
24
+ ##
25
+ # Logs a message to the console and log file to inform
26
+ # the client that Backup is using the compressor
27
+ def log!
28
+ Logger.info "Using #{ compressor_name } for compression.\n" +
29
+ " Command: '#{ @cmd }'\n" +
30
+ " Ext: '#{ @ext }'"
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+
3
+ module Backup
4
+ module Compressor
5
+ class Bzip2 < Base
6
+
7
+ ##
8
+ # Specify the level of compression to use.
9
+ #
10
+ # Values should be a single digit from 1 to 9.
11
+ # Note that setting the level to either extreme may or may not
12
+ # give the desired result. Be sure to check the documentation
13
+ # for the compressor being used.
14
+ #
15
+ # The default `level` is 9.
16
+ attr_accessor :level
17
+
18
+ ##
19
+ # Creates a new instance of Backup::Compressor::Bzip2
20
+ def initialize(&block)
21
+ load_defaults!
22
+
23
+ @level ||= false
24
+
25
+ instance_eval(&block) if block_given?
26
+
27
+ @cmd = "#{ utility(:bzip2) }#{ options }"
28
+ @ext = '.bz2'
29
+ end
30
+
31
+ private
32
+
33
+ def options
34
+ " -#{ @level }" if @level
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ module Backup
4
+ module Compressor
5
+ class Custom < Base
6
+
7
+ ##
8
+ # Specify the system command to invoke a compressor,
9
+ # including any command-line arguments.
10
+ # e.g. @compressor.command = 'pbzip2 -p2 -4'
11
+ #
12
+ # The data to be compressed will be piped to the command's STDIN,
13
+ # and it should write the compressed data to STDOUT.
14
+ # i.e. `cat file.tar | %command% > file.tar.%extension%`
15
+ attr_accessor :command
16
+
17
+ ##
18
+ # File extension to append to the compressed file's filename.
19
+ # e.g. @compressor.extension = '.bz2'
20
+ attr_accessor :extension
21
+
22
+ ##
23
+ # Initializes a new custom compressor.
24
+ def initialize(&block)
25
+ load_defaults!
26
+
27
+ instance_eval(&block) if block_given?
28
+
29
+ @cmd = set_cmd
30
+ @ext = set_ext
31
+ end
32
+
33
+ private
34
+
35
+ ##
36
+ # Return the command line using the full path.
37
+ # Ensures the command exists and is executable.
38
+ def set_cmd
39
+ parts = @command.to_s.split(' ')
40
+ parts[0] = utility(parts[0])
41
+ parts.join(' ')
42
+ end
43
+
44
+ ##
45
+ # Return the extension given without whitespace.
46
+ # If extension was not set, return an empty string
47
+ def set_ext
48
+ @extension.to_s.strip
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,74 @@
1
+ # encoding: utf-8
2
+
3
+ module Backup
4
+ module Compressor
5
+ class Gzip < Base
6
+ class Error < Backup::Error; end
7
+ extend Utilities::Helpers
8
+
9
+ ##
10
+ # Specify the level of compression to use.
11
+ #
12
+ # Values should be a single digit from 1 to 9.
13
+ # Note that setting the level to either extreme may or may not
14
+ # give the desired result. Be sure to check the documentation
15
+ # for the compressor being used.
16
+ #
17
+ # The default `level` is 6.
18
+ attr_accessor :level
19
+
20
+ ##
21
+ # Use the `--rsyncable` option with `gzip`.
22
+ #
23
+ # This option directs `gzip` to compress data using an algorithm that
24
+ # allows `rsync` to efficiently detect changes. This is especially useful
25
+ # when used to compress `Archive` or `Database` backups that will be
26
+ # stored using Backup's `RSync` Storage option.
27
+ #
28
+ # The `--rsyncable` option is only available on patched versions of `gzip`.
29
+ # While most distributions apply this patch, this option may not be
30
+ # available on your system. If it's not available, Backup will log a
31
+ # warning and continue to use the compressor without this option.
32
+ attr_accessor :rsyncable
33
+
34
+ ##
35
+ # Determine if +--rsyncable+ is supported and cache the result.
36
+ def self.has_rsyncable?
37
+ return @has_rsyncable unless @has_rsyncable.nil?
38
+ cmd = "#{ utility(:gzip) } --rsyncable --version >/dev/null 2>&1; echo $?"
39
+ @has_rsyncable = %x[#{ cmd }].chomp == '0'
40
+ end
41
+
42
+ ##
43
+ # Creates a new instance of Backup::Compressor::Gzip
44
+ def initialize(&block)
45
+ load_defaults!
46
+
47
+ @level ||= false
48
+ @rsyncable ||= false
49
+
50
+ instance_eval(&block) if block_given?
51
+
52
+ @cmd = "#{ utility(:gzip) }#{ options }"
53
+ @ext = '.gz'
54
+ end
55
+
56
+ private
57
+
58
+ def options
59
+ opts = ''
60
+ opts << " -#{ @level }" if @level
61
+ if self.class.has_rsyncable?
62
+ opts << ' --rsyncable'
63
+ else
64
+ Logger.warn Error.new(<<-EOS)
65
+ 'rsyncable' option ignored.
66
+ Your system's 'gzip' does not support the `--rsyncable` option.
67
+ EOS
68
+ end if @rsyncable
69
+ opts
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,121 @@
1
+ # encoding: utf-8
2
+ require 'backup/config/dsl'
3
+ require 'backup/config/helpers'
4
+
5
+ module Backup
6
+ module Config
7
+ class Error < Backup::Error; end
8
+
9
+ DEFAULTS = {
10
+ :config_file => 'config.rb',
11
+ :data_path => '.data',
12
+ :tmp_path => '.tmp'
13
+ }
14
+
15
+ class << self
16
+ include Utilities::Helpers
17
+
18
+ attr_reader :user, :root_path, :config_file, :data_path, :tmp_path
19
+
20
+ # Loads the user's +config.rb+ and all model files.
21
+ def load(options = {})
22
+ update(options) # from the command line
23
+
24
+ unless File.exist?(config_file)
25
+ raise Error, "Could not find configuration file: '#{config_file}'."
26
+ end
27
+
28
+ config = File.read(config_file)
29
+ version = Backup::VERSION.split('.').first
30
+ unless config =~ /^# Backup v#{ version }\.x Configuration$/
31
+ raise Error, <<-EOS
32
+ Invalid Configuration File
33
+ The configuration file at '#{ config_file }'
34
+ does not appear to be a Backup v#{ version }.x configuration file.
35
+ If you have upgraded to v#{ version }.x from a previous version,
36
+ you need to upgrade your configuration file.
37
+ Please see the instructions for upgrading in the Backup documentation.
38
+ EOS
39
+ end
40
+
41
+ dsl = DSL.new
42
+
43
+ dsl.instance_eval(config, config_file)
44
+
45
+ update(dsl._config_options) # from config.rb
46
+ update(options) # command line takes precedence
47
+
48
+
49
+ Dir[File.join(File.dirname(config_file), 'models', '*.rb')].each do |model|
50
+ dsl.instance_eval(File.read(model), model)
51
+ end
52
+ end
53
+
54
+ def hostname
55
+ @hostname ||= run(utility(:hostname))
56
+ end
57
+
58
+ private
59
+
60
+ # If :root_path is set in the options, all paths will be updated.
61
+ # Otherwise, only the paths given will be updated.
62
+ def update(options = {})
63
+ root_path = options[:root_path].to_s.strip
64
+ new_root = root_path.empty? ? false : set_root_path(root_path)
65
+
66
+ DEFAULTS.each do |name, ending|
67
+ set_path_variable(name, options[name], ending, new_root)
68
+ end
69
+ end
70
+
71
+ # Sets the @root_path to the given +path+ and returns it.
72
+ # Raises an error if the given +path+ does not exist.
73
+ def set_root_path(path)
74
+ # allows #reset! to set the default @root_path,
75
+ # then use #update to set all other paths,
76
+ # without requiring that @root_path exist.
77
+ return @root_path if path == @root_path
78
+
79
+ path = File.expand_path(path)
80
+ unless File.directory?(path)
81
+ raise Error, <<-EOS
82
+ Root Path Not Found
83
+ When specifying a --root-path, the path must exist.
84
+ Path was: #{ path }
85
+ EOS
86
+ end
87
+ @root_path = path
88
+ end
89
+
90
+ def set_path_variable(name, path, ending, root_path)
91
+ # strip any trailing '/' in case the user supplied this as part of
92
+ # an absolute path, so we can match it against File.expand_path()
93
+ path = path.to_s.sub(/\/\s*$/, '').lstrip
94
+ new_path = false
95
+ # If no path is given, the variable will not be set/updated
96
+ # unless a root_path was given. In which case the value will
97
+ # be updated with our default ending.
98
+ if path.empty?
99
+ new_path = File.join(root_path, ending) if root_path
100
+ else
101
+ # When a path is given, the variable will be set/updated.
102
+ # If the path is relative, it will be joined with root_path (if given),
103
+ # or expanded relative to PWD.
104
+ new_path = File.expand_path(path)
105
+ unless path == new_path
106
+ new_path = File.join(root_path, path) if root_path
107
+ end
108
+ end
109
+ instance_variable_set(:"@#{name}", new_path) if new_path
110
+ end
111
+
112
+ def reset!
113
+ @user = ENV['USER'] || Etc.getpwuid.name
114
+ @root_path = File.join(File.expand_path(ENV['HOME'] || ''), 'Backup')
115
+ update(:root_path => @root_path)
116
+ end
117
+ end
118
+
119
+ reset! # set defaults on load
120
+ end
121
+ end
@@ -0,0 +1,106 @@
1
+ # encoding: utf-8
2
+
3
+ module Backup
4
+ module Config
5
+ # Context for loading user config.rb and model files.
6
+ class DSL
7
+ class Error < Backup::Error; end
8
+ Model = Backup::Model
9
+
10
+ class << self
11
+ private
12
+
13
+ # List the available database, storage, syncer, compressor, encryptor
14
+ # and notifier constants. These are used to define constant names within
15
+ # Backup::Config::DSL so that users may use a constant instead of a string.
16
+ # Nested namespaces are represented using Hashs. Deep nesting supported.
17
+ #
18
+ # Example, instead of:
19
+ # database "MySQL" do |mysql|
20
+ # sync_with "RSync::Local" do |rsync|
21
+ #
22
+ # You can do:
23
+ # database MySQL do |mysql|
24
+ # sync_with RSync::Local do |rsync|
25
+ #
26
+ def add_dsl_constants
27
+ create_modules(
28
+ DSL,
29
+ [ # Databases
30
+ ['MySQL', 'PostgreSQL', 'MongoDB', 'Redis', 'Riak', 'OpenLDAP', 'SQLite',
31
+ 'RemoteMySQL'
32
+ ],
33
+
34
+ # Storages
35
+ ['S3', 'CloudFiles', 'Ninefold', 'Dropbox', 'FTP',
36
+ 'SFTP', 'SCP', 'RSync', 'Local', 'Qiniu'],
37
+ # Compressors
38
+ ['Gzip', 'Bzip2', 'Custom', 'Pbzip2', 'Lzma'],
39
+ # Encryptors
40
+ ['OpenSSL', 'GPG'],
41
+ # Syncers
42
+ [
43
+ { 'Cloud' => ['CloudFiles', 'S3'] },
44
+ { 'RSync' => ['Push', 'Pull', 'Local'] }
45
+ ],
46
+ # Notifiers
47
+ ['Mail', 'Twitter', 'Campfire', 'Prowl',
48
+ 'Hipchat', 'PagerDuty', 'Pushover', 'HttpPost', 'Nagios',
49
+ 'Slack', 'FlowDock', 'Zabbix', 'Ses', 'DataDog', 'Command']
50
+ ]
51
+ )
52
+ end
53
+
54
+ def create_modules(scope, names)
55
+ names.flatten.each do |name|
56
+ if name.is_a?(Hash)
57
+ name.each do |key, val|
58
+ create_modules(get_or_create_empty_module(scope, key), [val])
59
+ end
60
+ else
61
+ get_or_create_empty_module(scope, name)
62
+ end
63
+ end
64
+ end
65
+
66
+ def get_or_create_empty_module(scope, const)
67
+ if scope.const_defined?(const)
68
+ scope.const_get(const)
69
+ else
70
+ scope.const_set(const, Module.new)
71
+ end
72
+ end
73
+ end
74
+
75
+ add_dsl_constants # add constants on load
76
+
77
+ attr_reader :_config_options
78
+
79
+ def initialize
80
+ @_config_options = {}
81
+ end
82
+
83
+ # Allow users to set command line path options in config.rb
84
+ [:root_path, :data_path, :tmp_path].each do |name|
85
+ define_method name, lambda {|path| _config_options[name] = path }
86
+ end
87
+
88
+ # Allows users to create preconfigured models.
89
+ def preconfigure(name, &block)
90
+ unless name.is_a?(String) && name =~ /^[A-Z]/
91
+ raise Error, "Preconfigured model names must be given as a string " +
92
+ "and start with a capital letter."
93
+ end
94
+
95
+ if DSL.const_defined?(name)
96
+ raise Error, "'#{ name }' is already in use " +
97
+ "and can not be used for a preconfigured model."
98
+ end
99
+
100
+ DSL.const_set(name, Class.new(Model))
101
+ DSL.const_get(name).preconfigure(&block)
102
+ end
103
+
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,143 @@
1
+ # encoding: utf-8
2
+ require 'ostruct'
3
+
4
+ module Backup
5
+ module Config
6
+ module Helpers
7
+
8
+ def self.included(klass)
9
+ klass.extend ClassMethods
10
+ end
11
+
12
+ module ClassMethods
13
+
14
+ def defaults
15
+ @defaults ||= Config::Defaults.new
16
+
17
+ if block_given?
18
+ yield @defaults
19
+ else
20
+ @defaults
21
+ end
22
+ end
23
+
24
+ # Used only within the specs
25
+ def clear_defaults!
26
+ defaults.reset!
27
+ end
28
+
29
+ def deprecations
30
+ @deprecations ||= {}
31
+ end
32
+
33
+ def log_deprecation_warning(name, deprecation)
34
+ msg = "#{ self }##{ name } has been deprecated as of " +
35
+ "backup v.#{ deprecation[:version] }"
36
+ msg << "\n#{ deprecation[:message] }" if deprecation[:message]
37
+ Logger.warn Config::Error.new(<<-EOS)
38
+ [DEPRECATION WARNING]
39
+ #{ msg }
40
+ EOS
41
+ end
42
+
43
+ protected
44
+
45
+ ##
46
+ # Method to deprecate an attribute.
47
+ #
48
+ # :version
49
+ # Must be set to the backup version which will first
50
+ # introduce the deprecation.
51
+ #
52
+ # :action
53
+ # If set, this Proc will be called with a reference to the
54
+ # class instance and the value set on the deprecated accessor.
55
+ # e.g. deprecation[:action].call(klass, value)
56
+ # This should perform whatever action is neccessary, such as
57
+ # transferring the value to a new accessor.
58
+ #
59
+ # :message
60
+ # If set, this will be appended to #log_deprecation_warning
61
+ #
62
+ # Note that this replaces the `attr_accessor` method, or other
63
+ # method previously used to set the accessor being deprecated.
64
+ # #method_missing will handle any calls to `name=`.
65
+ #
66
+ def attr_deprecate(name, args = {})
67
+ deprecations[name] = {
68
+ :version => nil,
69
+ :message => nil,
70
+ :action => nil
71
+ }.merge(args)
72
+ end
73
+
74
+ end # ClassMethods
75
+
76
+ private
77
+
78
+ ##
79
+ # Sets any pre-configured default values.
80
+ # If a default value was set for an invalid accessor,
81
+ # this will raise a NameError.
82
+ def load_defaults!
83
+ self.class.defaults._attributes.each do |name|
84
+ val = self.class.defaults.send(name)
85
+ val = val.dup rescue val
86
+ send(:"#{ name }=", val)
87
+ end
88
+ end
89
+
90
+ ##
91
+ # Check missing methods for deprecated attribute accessors.
92
+ #
93
+ # If a value is set on an accessor that has been deprecated
94
+ # using #attr_deprecate, a warning will be issued and any
95
+ # :action (Proc) specified will be called with a reference to
96
+ # the class instance and the value set on the deprecated accessor.
97
+ # See #attr_deprecate and #log_deprecation_warning
98
+ #
99
+ # Note that OpenStruct (used for setting defaults) does not allow
100
+ # multiple arguments when assigning values for members.
101
+ # So, we won't allow it here either, even though an attr_accessor
102
+ # will accept and convert them into an Array. Therefore, setting
103
+ # an option value using multiple values, whether as a default or
104
+ # directly on the class' accessor, should not be supported.
105
+ # i.e. if an option will accept being set as an Array, then it
106
+ # should be explicitly set as such. e.g. option = [val1, val2]
107
+ #
108
+ def method_missing(name, *args)
109
+ deprecation = nil
110
+ if method = name.to_s.chomp!('=')
111
+ if (len = args.count) != 1
112
+ raise ArgumentError,
113
+ "wrong number of arguments (#{ len } for 1)", caller(1)
114
+ end
115
+ deprecation = self.class.deprecations[method.to_sym]
116
+ end
117
+
118
+ if deprecation
119
+ self.class.log_deprecation_warning(method, deprecation)
120
+ deprecation[:action].call(self, args[0]) if deprecation[:action]
121
+ else
122
+ super
123
+ end
124
+ end
125
+
126
+ end # Helpers
127
+
128
+ # Store for pre-configured defaults.
129
+ class Defaults < OpenStruct
130
+ # Returns an Array of all attribute method names
131
+ # that default values were set for.
132
+ def _attributes
133
+ @table.keys
134
+ end
135
+
136
+ # Used only within the specs
137
+ def reset!
138
+ @table.clear
139
+ end
140
+ end
141
+
142
+ end
143
+ end