jswanner-carrierwave 0.5.0.beta3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +553 -0
- data/lib/carrierwave.rb +101 -0
- data/lib/carrierwave/compatibility/paperclip.rb +95 -0
- data/lib/carrierwave/mount.rb +359 -0
- data/lib/carrierwave/orm/activerecord.rb +79 -0
- data/lib/carrierwave/orm/datamapper.rb +37 -0
- data/lib/carrierwave/orm/mongoid.rb +23 -0
- data/lib/carrierwave/orm/sequel.rb +45 -0
- data/lib/carrierwave/processing/image_science.rb +116 -0
- data/lib/carrierwave/processing/mini_magick.rb +261 -0
- data/lib/carrierwave/processing/rmagick.rb +278 -0
- data/lib/carrierwave/sanitized_file.rb +273 -0
- data/lib/carrierwave/storage/abstract.rb +30 -0
- data/lib/carrierwave/storage/cloud_files.rb +168 -0
- data/lib/carrierwave/storage/file.rb +48 -0
- data/lib/carrierwave/storage/grid_fs.rb +108 -0
- data/lib/carrierwave/storage/right_s3.rb +3 -0
- data/lib/carrierwave/storage/s3.rb +206 -0
- data/lib/carrierwave/test/matchers.rb +164 -0
- data/lib/carrierwave/uploader.rb +44 -0
- data/lib/carrierwave/uploader/cache.rb +146 -0
- data/lib/carrierwave/uploader/callbacks.rb +41 -0
- data/lib/carrierwave/uploader/configuration.rb +135 -0
- data/lib/carrierwave/uploader/default_url.rb +19 -0
- data/lib/carrierwave/uploader/download.rb +75 -0
- data/lib/carrierwave/uploader/extension_whitelist.rb +38 -0
- data/lib/carrierwave/uploader/mountable.rb +39 -0
- data/lib/carrierwave/uploader/processing.rb +85 -0
- data/lib/carrierwave/uploader/proxy.rb +62 -0
- data/lib/carrierwave/uploader/remove.rb +23 -0
- data/lib/carrierwave/uploader/store.rb +90 -0
- data/lib/carrierwave/uploader/url.rb +33 -0
- data/lib/carrierwave/uploader/versions.rb +157 -0
- data/lib/generators/templates/uploader.rb +47 -0
- data/lib/generators/uploader_generator.rb +13 -0
- metadata +374 -0
@@ -0,0 +1,164 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module CarrierWave
|
4
|
+
module Test
|
5
|
+
|
6
|
+
##
|
7
|
+
# These are some matchers that can be used in RSpec specs, to simplify the testing
|
8
|
+
# of uploaders.
|
9
|
+
#
|
10
|
+
module Matchers
|
11
|
+
|
12
|
+
class BeIdenticalTo # :nodoc:
|
13
|
+
def initialize(expected)
|
14
|
+
@expected = expected
|
15
|
+
end
|
16
|
+
def matches?(actual)
|
17
|
+
@actual = actual
|
18
|
+
FileUtils.identical?(@actual, @expected)
|
19
|
+
end
|
20
|
+
def failure_message
|
21
|
+
"expected #{@actual.inspect} to be identical to #{@expected.inspect}"
|
22
|
+
end
|
23
|
+
def negative_failure_message
|
24
|
+
"expected #{@actual.inspect} to not be identical to #{@expected.inspect}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def be_identical_to(expected)
|
29
|
+
BeIdenticalTo.new(expected)
|
30
|
+
end
|
31
|
+
|
32
|
+
class HavePermissions # :nodoc:
|
33
|
+
def initialize(expected)
|
34
|
+
@expected = expected
|
35
|
+
end
|
36
|
+
|
37
|
+
def matches?(actual)
|
38
|
+
@actual = actual
|
39
|
+
# Satisfy expectation here. Return false or raise an error if it's not met.
|
40
|
+
(File.stat(@actual.path).mode & 0777) == @expected
|
41
|
+
end
|
42
|
+
|
43
|
+
def failure_message
|
44
|
+
"expected #{@actual.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode & 0777).to_s(8)}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def negative_failure_message
|
48
|
+
"expected #{@actual.inspect} not to have permissions #{@expected.to_s(8)}, but it did"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def have_permissions(expected)
|
53
|
+
HavePermissions.new(expected)
|
54
|
+
end
|
55
|
+
|
56
|
+
class BeNoLargerThan # :nodoc:
|
57
|
+
def initialize(width, height)
|
58
|
+
@width, @height = width, height
|
59
|
+
end
|
60
|
+
|
61
|
+
def matches?(actual)
|
62
|
+
@actual = actual
|
63
|
+
# Satisfy expectation here. Return false or raise an error if it's not met.
|
64
|
+
image = ImageLoader.load_image(@actual.current_path)
|
65
|
+
@actual_width = image.width
|
66
|
+
@actual_height = image.height
|
67
|
+
@actual_width <= @width && @actual_height <= @height
|
68
|
+
end
|
69
|
+
|
70
|
+
def failure_message
|
71
|
+
"expected #{@actual.current_path.inspect} to be no larger than #{@width} by #{@height}, but it was #{@actual_width} by #{@actual_height}."
|
72
|
+
end
|
73
|
+
|
74
|
+
def negative_failure_message
|
75
|
+
"expected #{@actual.current_path.inspect} to be larger than #{@width} by #{@height}, but it wasn't."
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
def be_no_larger_than(width, height)
|
81
|
+
BeNoLargerThan.new(width, height)
|
82
|
+
end
|
83
|
+
|
84
|
+
class HaveDimensions # :nodoc:
|
85
|
+
def initialize(width, height)
|
86
|
+
@width, @height = width, height
|
87
|
+
end
|
88
|
+
|
89
|
+
def matches?(actual)
|
90
|
+
@actual = actual
|
91
|
+
# Satisfy expectation here. Return false or raise an error if it's not met.
|
92
|
+
image = ImageLoader.load_image(@actual.current_path)
|
93
|
+
@actual_width = image.width
|
94
|
+
@actual_height = image.height
|
95
|
+
@actual_width == @width && @actual_height == @height
|
96
|
+
end
|
97
|
+
|
98
|
+
def failure_message
|
99
|
+
"expected #{@actual.current_path.inspect} to have an exact size of #{@width} by #{@height}, but it was #{@actual_width} by #{@actual_height}."
|
100
|
+
end
|
101
|
+
|
102
|
+
def negative_failure_message
|
103
|
+
"expected #{@actual.current_path.inspect} not to have an exact size of #{@width} by #{@height}, but it did."
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
def have_dimensions(width, height)
|
109
|
+
HaveDimensions.new(width, height)
|
110
|
+
end
|
111
|
+
|
112
|
+
class ImageLoader # :nodoc:
|
113
|
+
def self.load_image(filename)
|
114
|
+
if defined? ::MiniMagick
|
115
|
+
MiniMagickWrapper.new(filename)
|
116
|
+
else
|
117
|
+
unless defined? ::Magick
|
118
|
+
begin
|
119
|
+
require 'rmagick'
|
120
|
+
rescue LoadError
|
121
|
+
require 'RMagick'
|
122
|
+
rescue LoadError
|
123
|
+
puts "WARNING: Failed to require rmagick, image processing may fail!"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
MagickWrapper.new(filename)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class MagickWrapper # :nodoc:
|
132
|
+
attr_reader :image
|
133
|
+
def width
|
134
|
+
image.columns
|
135
|
+
end
|
136
|
+
|
137
|
+
def height
|
138
|
+
image.rows
|
139
|
+
end
|
140
|
+
|
141
|
+
def initialize(filename)
|
142
|
+
@image = ::Magick::Image.read(filename).first
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class MiniMagickWrapper # :nodoc:
|
147
|
+
attr_reader :image
|
148
|
+
def width
|
149
|
+
image[:width]
|
150
|
+
end
|
151
|
+
|
152
|
+
def height
|
153
|
+
image[:height]
|
154
|
+
end
|
155
|
+
|
156
|
+
def initialize(filename)
|
157
|
+
@image = ::MiniMagick::Image.from_file(filename)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end # Matchers
|
162
|
+
end # Test
|
163
|
+
end # CarrierWave
|
164
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module CarrierWave
|
4
|
+
|
5
|
+
##
|
6
|
+
# See CarrierWave::Uploader::Base
|
7
|
+
#
|
8
|
+
module Uploader
|
9
|
+
|
10
|
+
##
|
11
|
+
# An uploader is a class that allows you to easily handle the caching and storage of
|
12
|
+
# uploaded files. Please refer to the README for configuration options.
|
13
|
+
#
|
14
|
+
# Once you have an uploader you can use it in isolation:
|
15
|
+
#
|
16
|
+
# my_uploader = MyUploader.new
|
17
|
+
# my_uploader.cache!(File.open(path_to_file))
|
18
|
+
# my_uploader.retrieve_from_store!('monkey.png')
|
19
|
+
#
|
20
|
+
# Alternatively, you can mount it on an ORM or other persistence layer, with
|
21
|
+
# +CarrierWave::Mount#mount_uploader+. There are extensions for activerecord and datamapper
|
22
|
+
# these are *very* simple (they are only a dozen lines of code), so adding your own should
|
23
|
+
# be trivial.
|
24
|
+
#
|
25
|
+
class Base
|
26
|
+
attr_reader :file
|
27
|
+
|
28
|
+
include CarrierWave::Uploader::Callbacks
|
29
|
+
include CarrierWave::Uploader::Proxy
|
30
|
+
include CarrierWave::Uploader::Url
|
31
|
+
include CarrierWave::Uploader::Mountable
|
32
|
+
include CarrierWave::Uploader::Cache
|
33
|
+
include CarrierWave::Uploader::Store
|
34
|
+
include CarrierWave::Uploader::Download
|
35
|
+
include CarrierWave::Uploader::Remove
|
36
|
+
include CarrierWave::Uploader::ExtensionWhitelist
|
37
|
+
include CarrierWave::Uploader::Processing
|
38
|
+
include CarrierWave::Uploader::Versions
|
39
|
+
include CarrierWave::Uploader::DefaultUrl
|
40
|
+
include CarrierWave::Uploader::Configuration
|
41
|
+
end # Base
|
42
|
+
|
43
|
+
end # Uploader
|
44
|
+
end # CarrierWave
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module CarrierWave
|
4
|
+
|
5
|
+
class FormNotMultipart < UploadError
|
6
|
+
def message
|
7
|
+
"You tried to assign a String or a Pathname to an uploader, for security reasons, this is not allowed.\n\n If this is a file upload, please check that your upload form is multipart encoded."
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
# Generates a unique cache id for use in the caching system
|
13
|
+
#
|
14
|
+
# === Returns
|
15
|
+
#
|
16
|
+
# [String] a cache id in the format YYYYMMDD-HHMM-PID-RND
|
17
|
+
#
|
18
|
+
def self.generate_cache_id
|
19
|
+
Time.now.strftime('%Y%m%d-%H%M') + '-' + Process.pid.to_s + '-' + ("%04d" % rand(9999))
|
20
|
+
end
|
21
|
+
|
22
|
+
module Uploader
|
23
|
+
module Cache
|
24
|
+
extend ActiveSupport::Concern
|
25
|
+
|
26
|
+
include CarrierWave::Uploader::Callbacks
|
27
|
+
include CarrierWave::Uploader::Configuration
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
|
31
|
+
##
|
32
|
+
# Removes cached files which are older than one day. You could call this method
|
33
|
+
# from a rake task to clean out old cached files.
|
34
|
+
#
|
35
|
+
# You can call this method directly on the module like this:
|
36
|
+
#
|
37
|
+
# CarrierWave.clean_cached_files!
|
38
|
+
#
|
39
|
+
# === Note
|
40
|
+
#
|
41
|
+
# This only works as long as you haven't done anything funky with your cache_dir.
|
42
|
+
# It's recommended that you keep cache files in one place only.
|
43
|
+
#
|
44
|
+
def clean_cached_files!
|
45
|
+
Dir.glob(File.expand_path(File.join(cache_dir, '*'), CarrierWave.root)).each do |dir|
|
46
|
+
time = dir.scan(/(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})/).first.map { |t| t.to_i }
|
47
|
+
time = Time.utc(*time)
|
48
|
+
if time < (Time.now - (60*60*24))
|
49
|
+
FileUtils.rm_rf(dir)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Returns true if the uploader has been cached
|
57
|
+
#
|
58
|
+
# === Returns
|
59
|
+
#
|
60
|
+
# [Bool] whether the current file is cached
|
61
|
+
#
|
62
|
+
def cached?
|
63
|
+
@cache_id
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# Returns a String which uniquely identifies the currently cached file for later retrieval
|
68
|
+
#
|
69
|
+
# === Returns
|
70
|
+
#
|
71
|
+
# [String] a cache name, in the format YYYYMMDD-HHMM-PID-RND/filename.txt
|
72
|
+
#
|
73
|
+
def cache_name
|
74
|
+
File.join(cache_id, full_original_filename) if cache_id and original_filename
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Caches the given file. Calls process! to trigger any process callbacks.
|
79
|
+
#
|
80
|
+
# === Parameters
|
81
|
+
#
|
82
|
+
# [new_file (File, IOString, Tempfile)] any kind of file object
|
83
|
+
#
|
84
|
+
# === Raises
|
85
|
+
#
|
86
|
+
# [CarrierWave::FormNotMultipart] if the assigned parameter is a string
|
87
|
+
#
|
88
|
+
def cache!(new_file)
|
89
|
+
new_file = CarrierWave::SanitizedFile.new(new_file)
|
90
|
+
raise CarrierWave::FormNotMultipart if new_file.is_path?
|
91
|
+
|
92
|
+
unless new_file.empty?
|
93
|
+
with_callbacks(:cache, new_file) do
|
94
|
+
self.cache_id = CarrierWave.generate_cache_id unless cache_id
|
95
|
+
|
96
|
+
@filename = new_file.filename
|
97
|
+
self.original_filename = new_file.filename
|
98
|
+
|
99
|
+
@file = new_file.copy_to(cache_path, permissions)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
##
|
105
|
+
# Retrieves the file with the given cache_name from the cache.
|
106
|
+
#
|
107
|
+
# === Parameters
|
108
|
+
#
|
109
|
+
# [cache_name (String)] uniquely identifies a cache file
|
110
|
+
#
|
111
|
+
# === Raises
|
112
|
+
#
|
113
|
+
# [CarrierWave::InvalidParameter] if the cache_name is incorrectly formatted.
|
114
|
+
#
|
115
|
+
def retrieve_from_cache!(cache_name)
|
116
|
+
with_callbacks(:retrieve_from_cache, cache_name) do
|
117
|
+
self.cache_id, self.original_filename = cache_name.to_s.split('/', 2)
|
118
|
+
@filename = original_filename
|
119
|
+
@file = CarrierWave::SanitizedFile.new(cache_path)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def cache_path
|
126
|
+
File.expand_path(File.join(cache_dir, cache_name), root)
|
127
|
+
end
|
128
|
+
|
129
|
+
attr_reader :cache_id, :original_filename
|
130
|
+
|
131
|
+
# We can override the full_original_filename method in other modules
|
132
|
+
alias_method :full_original_filename, :original_filename
|
133
|
+
|
134
|
+
def cache_id=(cache_id)
|
135
|
+
raise CarrierWave::InvalidParameter, "invalid cache id" unless cache_id =~ /\A[\d]{8}\-[\d]{4}\-[\d]+\-[\d]{4}\z/
|
136
|
+
@cache_id = cache_id
|
137
|
+
end
|
138
|
+
|
139
|
+
def original_filename=(filename)
|
140
|
+
raise CarrierWave::InvalidParameter, "invalid filename" unless filename =~ /\A[a-z0-9\.\-\+_]+\z/i
|
141
|
+
@original_filename = filename
|
142
|
+
end
|
143
|
+
|
144
|
+
end # Cache
|
145
|
+
end # Uploader
|
146
|
+
end # CarrierWave
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module CarrierWave
|
4
|
+
module Uploader
|
5
|
+
module Callbacks
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
class_inheritable_accessor :_before_callbacks, :_after_callbacks
|
10
|
+
end
|
11
|
+
|
12
|
+
def with_callbacks(kind, *args)
|
13
|
+
self.class._before_callbacks_for(kind).each { |callback| self.send(callback, *args) }
|
14
|
+
yield
|
15
|
+
self.class._after_callbacks_for(kind).each { |callback| self.send(callback, *args) }
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
|
20
|
+
def _before_callbacks_for(kind) #:nodoc:
|
21
|
+
(self._before_callbacks || { kind => [] })[kind] || []
|
22
|
+
end
|
23
|
+
|
24
|
+
def _after_callbacks_for(kind) #:nodoc:
|
25
|
+
(self._after_callbacks || { kind => [] })[kind] || []
|
26
|
+
end
|
27
|
+
|
28
|
+
def before(kind, callback)
|
29
|
+
self._before_callbacks ||= {}
|
30
|
+
self._before_callbacks[kind] = _before_callbacks_for(kind) + [callback]
|
31
|
+
end
|
32
|
+
|
33
|
+
def after(kind, callback)
|
34
|
+
self._after_callbacks ||= {}
|
35
|
+
self._after_callbacks[kind] = _after_callbacks_for(kind) + [callback]
|
36
|
+
end
|
37
|
+
end # ClassMethods
|
38
|
+
|
39
|
+
end # Callbacks
|
40
|
+
end # Uploader
|
41
|
+
end # CarrierWave
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module CarrierWave
|
2
|
+
|
3
|
+
module Uploader
|
4
|
+
module Configuration
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
add_config :root
|
9
|
+
add_config :permissions
|
10
|
+
add_config :storage_engines
|
11
|
+
add_config :s3_access # for old aws/s3
|
12
|
+
add_config :s3_access_policy # for aws
|
13
|
+
add_config :s3_bucket
|
14
|
+
add_config :s3_access_key_id
|
15
|
+
add_config :s3_secret_access_key
|
16
|
+
add_config :s3_cnamed
|
17
|
+
add_config :s3_headers
|
18
|
+
add_config :s3_multi_thread
|
19
|
+
add_config :cloud_files_username
|
20
|
+
add_config :cloud_files_api_key
|
21
|
+
add_config :cloud_files_container
|
22
|
+
add_config :cloud_files_cdn_host
|
23
|
+
add_config :grid_fs_database
|
24
|
+
add_config :grid_fs_host
|
25
|
+
add_config :grid_fs_port
|
26
|
+
add_config :grid_fs_username
|
27
|
+
add_config :grid_fs_password
|
28
|
+
add_config :grid_fs_access_url
|
29
|
+
add_config :store_dir
|
30
|
+
add_config :cache_dir
|
31
|
+
add_config :enable_processing
|
32
|
+
|
33
|
+
# Mounting
|
34
|
+
add_config :ignore_integrity_errors
|
35
|
+
add_config :ignore_processing_errors
|
36
|
+
add_config :validate_integrity
|
37
|
+
add_config :validate_processing
|
38
|
+
add_config :mount_on
|
39
|
+
|
40
|
+
configure do |config|
|
41
|
+
config.permissions = 0644
|
42
|
+
config.storage_engines = {
|
43
|
+
:file => "CarrierWave::Storage::File",
|
44
|
+
:s3 => "CarrierWave::Storage::S3",
|
45
|
+
:grid_fs => "CarrierWave::Storage::GridFS",
|
46
|
+
:right_s3 => "CarrierWave::Storage::RightS3",
|
47
|
+
:cloud_files => "CarrierWave::Storage::CloudFiles"
|
48
|
+
}
|
49
|
+
config.storage = :file
|
50
|
+
#config.s3_access = :public_read
|
51
|
+
#config.s3_access_policy = 'public-read' # Now set in library
|
52
|
+
config.s3_headers = {}
|
53
|
+
config.s3_multi_thread = true
|
54
|
+
config.grid_fs_database = 'carrierwave'
|
55
|
+
config.grid_fs_host = 'localhost'
|
56
|
+
config.grid_fs_port = 27017
|
57
|
+
config.store_dir = 'uploads'
|
58
|
+
config.cache_dir = 'uploads/tmp'
|
59
|
+
config.ignore_integrity_errors = true
|
60
|
+
config.ignore_processing_errors = true
|
61
|
+
config.validate_integrity = true
|
62
|
+
config.validate_processing = true
|
63
|
+
config.root = CarrierWave.root
|
64
|
+
config.enable_processing = true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
module ClassMethods
|
69
|
+
|
70
|
+
##
|
71
|
+
# Sets the storage engine to be used when storing files with this uploader.
|
72
|
+
# Can be any class that implements a #store!(CarrierWave::SanitizedFile) and a #retrieve!
|
73
|
+
# method. See lib/carrierwave/storage/file.rb for an example. Storage engines should
|
74
|
+
# be added to CarrierWave::Uploader::Base.storage_engines so they can be referred
|
75
|
+
# to by a symbol, which should be more convenient
|
76
|
+
#
|
77
|
+
# If no argument is given, it will simply return the currently used storage engine.
|
78
|
+
#
|
79
|
+
# === Parameters
|
80
|
+
#
|
81
|
+
# [storage (Symbol, Class)] The storage engine to use for this uploader
|
82
|
+
#
|
83
|
+
# === Returns
|
84
|
+
#
|
85
|
+
# [Class] the storage engine to be used with this uploader
|
86
|
+
#
|
87
|
+
# === Examples
|
88
|
+
#
|
89
|
+
# storage :file
|
90
|
+
# storage CarrierWave::Storage::File
|
91
|
+
# storage MyCustomStorageEngine
|
92
|
+
#
|
93
|
+
def storage(storage = nil)
|
94
|
+
if storage.is_a?(Symbol)
|
95
|
+
@storage = eval(storage_engines[storage])
|
96
|
+
elsif storage
|
97
|
+
@storage = storage
|
98
|
+
elsif @storage.nil?
|
99
|
+
# Get the storage from the superclass if there is one
|
100
|
+
@storage = superclass.storage rescue nil
|
101
|
+
end
|
102
|
+
return @storage
|
103
|
+
end
|
104
|
+
alias_method :storage=, :storage
|
105
|
+
|
106
|
+
|
107
|
+
def add_config(name)
|
108
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
109
|
+
def self.#{name}(value=nil)
|
110
|
+
@#{name} = value if value
|
111
|
+
return @#{name} if self.object_id == #{self.object_id} || defined?(@#{name})
|
112
|
+
name = superclass.#{name}
|
113
|
+
return nil if name.nil? && !instance_variable_defined?("@#{name}")
|
114
|
+
@#{name} = name && !name.is_a?(Module) && !name.is_a?(Symbol) && !name.is_a?(Numeric) && !name.is_a?(TrueClass) && !name.is_a?(FalseClass) ? name.dup : name
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.#{name}=(value)
|
118
|
+
@#{name} = value
|
119
|
+
end
|
120
|
+
|
121
|
+
def #{name}
|
122
|
+
self.class.#{name}
|
123
|
+
end
|
124
|
+
RUBY
|
125
|
+
end
|
126
|
+
|
127
|
+
def configure
|
128
|
+
yield self
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|