bulldog 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/.gitignore +2 -0
  2. data/DESCRIPTION.txt +3 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +18 -0
  5. data/Rakefile +64 -0
  6. data/VERSION +1 -0
  7. data/bulldog.gemspec +157 -0
  8. data/lib/bulldog.rb +95 -0
  9. data/lib/bulldog/attachment.rb +49 -0
  10. data/lib/bulldog/attachment/base.rb +167 -0
  11. data/lib/bulldog/attachment/has_dimensions.rb +94 -0
  12. data/lib/bulldog/attachment/image.rb +63 -0
  13. data/lib/bulldog/attachment/maybe.rb +229 -0
  14. data/lib/bulldog/attachment/none.rb +37 -0
  15. data/lib/bulldog/attachment/pdf.rb +63 -0
  16. data/lib/bulldog/attachment/unknown.rb +11 -0
  17. data/lib/bulldog/attachment/video.rb +143 -0
  18. data/lib/bulldog/error.rb +5 -0
  19. data/lib/bulldog/has_attachment.rb +214 -0
  20. data/lib/bulldog/interpolation.rb +73 -0
  21. data/lib/bulldog/missing_file.rb +12 -0
  22. data/lib/bulldog/processor.rb +5 -0
  23. data/lib/bulldog/processor/argument_tree.rb +116 -0
  24. data/lib/bulldog/processor/base.rb +124 -0
  25. data/lib/bulldog/processor/ffmpeg.rb +172 -0
  26. data/lib/bulldog/processor/image_magick.rb +134 -0
  27. data/lib/bulldog/processor/one_shot.rb +19 -0
  28. data/lib/bulldog/reflection.rb +234 -0
  29. data/lib/bulldog/saved_file.rb +19 -0
  30. data/lib/bulldog/stream.rb +186 -0
  31. data/lib/bulldog/style.rb +38 -0
  32. data/lib/bulldog/style_set.rb +101 -0
  33. data/lib/bulldog/tempfile.rb +28 -0
  34. data/lib/bulldog/util.rb +92 -0
  35. data/lib/bulldog/validations.rb +68 -0
  36. data/lib/bulldog/vector2.rb +18 -0
  37. data/rails/init.rb +9 -0
  38. data/script/console +8 -0
  39. data/spec/data/empty.txt +0 -0
  40. data/spec/data/test.jpg +0 -0
  41. data/spec/data/test.mov +0 -0
  42. data/spec/data/test.pdf +0 -0
  43. data/spec/data/test.png +0 -0
  44. data/spec/data/test2.jpg +0 -0
  45. data/spec/helpers/image_creation.rb +8 -0
  46. data/spec/helpers/temporary_directory.rb +25 -0
  47. data/spec/helpers/temporary_models.rb +76 -0
  48. data/spec/helpers/temporary_values.rb +102 -0
  49. data/spec/helpers/test_upload_files.rb +108 -0
  50. data/spec/helpers/time_travel.rb +20 -0
  51. data/spec/integration/data/test.jpg +0 -0
  52. data/spec/integration/lifecycle_hooks_spec.rb +213 -0
  53. data/spec/integration/processing_image_attachments.rb +72 -0
  54. data/spec/integration/processing_video_attachments_spec.rb +82 -0
  55. data/spec/integration/saving_an_attachment_spec.rb +31 -0
  56. data/spec/matchers/file_operations.rb +159 -0
  57. data/spec/spec_helper.rb +76 -0
  58. data/spec/unit/attachment/base_spec.rb +311 -0
  59. data/spec/unit/attachment/image_spec.rb +128 -0
  60. data/spec/unit/attachment/maybe_spec.rb +126 -0
  61. data/spec/unit/attachment/pdf_spec.rb +137 -0
  62. data/spec/unit/attachment/video_spec.rb +176 -0
  63. data/spec/unit/attachment_spec.rb +61 -0
  64. data/spec/unit/has_attachment_spec.rb +700 -0
  65. data/spec/unit/interpolation_spec.rb +108 -0
  66. data/spec/unit/processor/argument_tree_spec.rb +159 -0
  67. data/spec/unit/processor/ffmpeg_spec.rb +467 -0
  68. data/spec/unit/processor/image_magick_spec.rb +260 -0
  69. data/spec/unit/processor/one_shot_spec.rb +70 -0
  70. data/spec/unit/reflection_spec.rb +338 -0
  71. data/spec/unit/stream_spec.rb +234 -0
  72. data/spec/unit/style_set_spec.rb +44 -0
  73. data/spec/unit/style_spec.rb +51 -0
  74. data/spec/unit/validations_spec.rb +491 -0
  75. data/spec/unit/vector2_spec.rb +27 -0
  76. data/tasks/bulldog_tasks.rake +4 -0
  77. metadata +193 -0
@@ -0,0 +1,2 @@
1
+ /pkg
2
+ /tmp
@@ -0,0 +1,3 @@
1
+ = Bulldog
2
+
3
+ Flexible file attachments for active record.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 George Ogata
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,18 @@
1
+ = Bulldog
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but
13
+ bump version in a commit by itself I can ignore when I pull)
14
+ * Send me a pull request. Bonus points for topic branches.
15
+
16
+ == Copyright
17
+
18
+ Copyright (c) 2009 George Ogata. See LICENSE for details.
@@ -0,0 +1,64 @@
1
+ require 'rake'
2
+ require 'spec/rake/spectask'
3
+ require 'rake/rdoctask'
4
+
5
+ PLUGIN_ROOT = File.dirname(__FILE__)
6
+
7
+ begin
8
+ require 'jeweler'
9
+ Jeweler::Tasks.new do |gem|
10
+ gem.name = "bulldog"
11
+ gem.summary = "A heavy-duty paperclip. File attachments for ActiveRecord."
12
+ gem.description = File.read("#{PLUGIN_ROOT}/DESCRIPTION.txt")
13
+ gem.email = "george.ogata@gmail.com"
14
+ gem.homepage = "http://github.com/oggy/bulldog"
15
+ gem.authors = ["George Ogata"]
16
+ gem.add_development_dependency "rspec"
17
+ gem.add_development_dependency "rspec_outlines"
18
+ gem.add_development_dependency "mocha"
19
+ end
20
+ rescue LoadError
21
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
22
+ end
23
+
24
+ require 'rake/rdoctask'
25
+ Rake::RDocTask.new do |rdoc|
26
+ if File.exist?('VERSION')
27
+ version = File.read('VERSION')
28
+ else
29
+ version = ""
30
+ end
31
+
32
+ rdoc.rdoc_dir = 'rdoc'
33
+ rdoc.title = "Bulldog #{version}"
34
+ rdoc.rdoc_files.include('README*')
35
+ rdoc.rdoc_files.include('lib/**/*.rb')
36
+ end
37
+
38
+ desc "Run all specs."
39
+ task :spec => ['check_dependencies', 'spec:unit', 'spec:integration']
40
+
41
+ namespace :spec do
42
+ Spec::Rake::SpecTask.new(:unit) do |t|
43
+ t.pattern = 'spec/unit/**/*_spec.rb'
44
+ t.libs << 'lib' << 'spec'
45
+ t.spec_opts = ['--options', "\"#{PLUGIN_ROOT}/spec/spec.opts\""]
46
+ end
47
+
48
+ Spec::Rake::SpecTask.new(:integration) do |t|
49
+ t.pattern = 'spec/integration/**/*_spec.rb'
50
+ t.libs << 'lib' << 'spec'
51
+ t.spec_opts = ['--options', "\"#{PLUGIN_ROOT}/spec/spec.opts\""]
52
+ end
53
+ end
54
+
55
+ desc "Run all specs in spec directory with RCov"
56
+ Spec::Rake::SpecTask.new(:rcov) do |t|
57
+ t.spec_opts = ['--options', "\"#{PLUGIN_ROOT}/spec/spec.opts\""]
58
+ t.rcov = true
59
+ t.rcov_opts = lambda do
60
+ IO.readlines(File.dirname(__FILE__) + "/spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
61
+ end
62
+ end
63
+
64
+ task :default => :spec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,157 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{bulldog}
8
+ s.version = "0.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["George Ogata"]
12
+ s.date = %q{2009-11-15}
13
+ s.description = %q{= Bulldog
14
+
15
+ Flexible file attachments for active record.
16
+ }
17
+ s.email = %q{george.ogata@gmail.com}
18
+ s.extra_rdoc_files = [
19
+ "LICENSE",
20
+ "README.rdoc"
21
+ ]
22
+ s.files = [
23
+ ".gitignore",
24
+ "DESCRIPTION.txt",
25
+ "LICENSE",
26
+ "README.rdoc",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "bulldog.gemspec",
30
+ "lib/bulldog.rb",
31
+ "lib/bulldog/attachment.rb",
32
+ "lib/bulldog/attachment/base.rb",
33
+ "lib/bulldog/attachment/has_dimensions.rb",
34
+ "lib/bulldog/attachment/image.rb",
35
+ "lib/bulldog/attachment/maybe.rb",
36
+ "lib/bulldog/attachment/none.rb",
37
+ "lib/bulldog/attachment/pdf.rb",
38
+ "lib/bulldog/attachment/unknown.rb",
39
+ "lib/bulldog/attachment/video.rb",
40
+ "lib/bulldog/error.rb",
41
+ "lib/bulldog/has_attachment.rb",
42
+ "lib/bulldog/interpolation.rb",
43
+ "lib/bulldog/missing_file.rb",
44
+ "lib/bulldog/processor.rb",
45
+ "lib/bulldog/processor/argument_tree.rb",
46
+ "lib/bulldog/processor/base.rb",
47
+ "lib/bulldog/processor/ffmpeg.rb",
48
+ "lib/bulldog/processor/image_magick.rb",
49
+ "lib/bulldog/processor/one_shot.rb",
50
+ "lib/bulldog/reflection.rb",
51
+ "lib/bulldog/saved_file.rb",
52
+ "lib/bulldog/stream.rb",
53
+ "lib/bulldog/style.rb",
54
+ "lib/bulldog/style_set.rb",
55
+ "lib/bulldog/tempfile.rb",
56
+ "lib/bulldog/util.rb",
57
+ "lib/bulldog/validations.rb",
58
+ "lib/bulldog/vector2.rb",
59
+ "rails/init.rb",
60
+ "script/console",
61
+ "spec/data/empty.txt",
62
+ "spec/data/test.jpg",
63
+ "spec/data/test.mov",
64
+ "spec/data/test.pdf",
65
+ "spec/data/test.png",
66
+ "spec/data/test2.jpg",
67
+ "spec/helpers/image_creation.rb",
68
+ "spec/helpers/temporary_directory.rb",
69
+ "spec/helpers/temporary_models.rb",
70
+ "spec/helpers/temporary_values.rb",
71
+ "spec/helpers/test_upload_files.rb",
72
+ "spec/helpers/time_travel.rb",
73
+ "spec/integration/data/test.jpg",
74
+ "spec/integration/lifecycle_hooks_spec.rb",
75
+ "spec/integration/processing_image_attachments.rb",
76
+ "spec/integration/processing_video_attachments_spec.rb",
77
+ "spec/integration/saving_an_attachment_spec.rb",
78
+ "spec/matchers/file_operations.rb",
79
+ "spec/spec_helper.rb",
80
+ "spec/unit/attachment/base_spec.rb",
81
+ "spec/unit/attachment/image_spec.rb",
82
+ "spec/unit/attachment/maybe_spec.rb",
83
+ "spec/unit/attachment/pdf_spec.rb",
84
+ "spec/unit/attachment/video_spec.rb",
85
+ "spec/unit/attachment_spec.rb",
86
+ "spec/unit/has_attachment_spec.rb",
87
+ "spec/unit/interpolation_spec.rb",
88
+ "spec/unit/processor/argument_tree_spec.rb",
89
+ "spec/unit/processor/ffmpeg_spec.rb",
90
+ "spec/unit/processor/image_magick_spec.rb",
91
+ "spec/unit/processor/one_shot_spec.rb",
92
+ "spec/unit/reflection_spec.rb",
93
+ "spec/unit/stream_spec.rb",
94
+ "spec/unit/style_set_spec.rb",
95
+ "spec/unit/style_spec.rb",
96
+ "spec/unit/validations_spec.rb",
97
+ "spec/unit/vector2_spec.rb",
98
+ "tasks/bulldog_tasks.rake"
99
+ ]
100
+ s.homepage = %q{http://github.com/oggy/bulldog}
101
+ s.rdoc_options = ["--charset=UTF-8"]
102
+ s.require_paths = ["lib"]
103
+ s.rubygems_version = %q{1.3.5}
104
+ s.summary = %q{A heavy-duty paperclip. File attachments for ActiveRecord.}
105
+ s.test_files = [
106
+ "spec/helpers/image_creation.rb",
107
+ "spec/helpers/temporary_directory.rb",
108
+ "spec/helpers/temporary_models.rb",
109
+ "spec/helpers/temporary_values.rb",
110
+ "spec/helpers/test_upload_files.rb",
111
+ "spec/helpers/time_travel.rb",
112
+ "spec/integration/lifecycle_hooks_spec.rb",
113
+ "spec/integration/processing_image_attachments.rb",
114
+ "spec/integration/processing_video_attachments_spec.rb",
115
+ "spec/integration/saving_an_attachment_spec.rb",
116
+ "spec/matchers/file_operations.rb",
117
+ "spec/spec_helper.rb",
118
+ "spec/unit/attachment/base_spec.rb",
119
+ "spec/unit/attachment/image_spec.rb",
120
+ "spec/unit/attachment/maybe_spec.rb",
121
+ "spec/unit/attachment/pdf_spec.rb",
122
+ "spec/unit/attachment/video_spec.rb",
123
+ "spec/unit/attachment_spec.rb",
124
+ "spec/unit/has_attachment_spec.rb",
125
+ "spec/unit/interpolation_spec.rb",
126
+ "spec/unit/processor/argument_tree_spec.rb",
127
+ "spec/unit/processor/ffmpeg_spec.rb",
128
+ "spec/unit/processor/image_magick_spec.rb",
129
+ "spec/unit/processor/one_shot_spec.rb",
130
+ "spec/unit/reflection_spec.rb",
131
+ "spec/unit/stream_spec.rb",
132
+ "spec/unit/style_set_spec.rb",
133
+ "spec/unit/style_spec.rb",
134
+ "spec/unit/validations_spec.rb",
135
+ "spec/unit/vector2_spec.rb"
136
+ ]
137
+
138
+ if s.respond_to? :specification_version then
139
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
140
+ s.specification_version = 3
141
+
142
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
143
+ s.add_development_dependency(%q<rspec>, [">= 0"])
144
+ s.add_development_dependency(%q<rspec_outlines>, [">= 0"])
145
+ s.add_development_dependency(%q<mocha>, [">= 0"])
146
+ else
147
+ s.add_dependency(%q<rspec>, [">= 0"])
148
+ s.add_dependency(%q<rspec_outlines>, [">= 0"])
149
+ s.add_dependency(%q<mocha>, [">= 0"])
150
+ end
151
+ else
152
+ s.add_dependency(%q<rspec>, [">= 0"])
153
+ s.add_dependency(%q<rspec_outlines>, [">= 0"])
154
+ s.add_dependency(%q<mocha>, [">= 0"])
155
+ end
156
+ end
157
+
@@ -0,0 +1,95 @@
1
+ require 'bulldog/util'
2
+ module Bulldog
3
+ extend Util
4
+ end
5
+
6
+ require 'bulldog/error'
7
+ require 'bulldog/has_attachment'
8
+ require 'bulldog/validations'
9
+ require 'bulldog/reflection'
10
+ require 'bulldog/attachment'
11
+ require 'bulldog/style'
12
+ require 'bulldog/style_set'
13
+ require 'bulldog/interpolation'
14
+ require 'bulldog/saved_file'
15
+ require 'bulldog/missing_file'
16
+ require 'bulldog/processor'
17
+ require 'bulldog/tempfile'
18
+ require 'bulldog/stream'
19
+ require 'bulldog/vector2'
20
+
21
+ module Bulldog
22
+ class << self
23
+ #
24
+ # Logger object to log to. Set to nil to omit logging.
25
+ #
26
+ attr_accessor :logger
27
+
28
+ #
29
+ # The default path template to use. See the #path configuration
30
+ # option for #has_attachment.
31
+ #
32
+ attr_accessor :default_path_template
33
+
34
+ #
35
+ # The default url template to use. See the #url configuration
36
+ # option for #has_attachment.
37
+ #
38
+ attr_accessor :default_url_template
39
+
40
+ #
41
+ # Define an interpolation token.
42
+ #
43
+ # If :<token> appears in a path or URL template, the block will be
44
+ # called to get the value to substitute in. The block will be
45
+ # called with 3 arguments: the record, the attachment name, and
46
+ # the style.
47
+ #
48
+ # Example:
49
+ #
50
+ # Bulldog.to_interpolate :datestamp do |record, name, style|
51
+ # Date.today.strftime("%Y%m%d")
52
+ # end
53
+ #
54
+ def to_interpolate(token, &block)
55
+ Interpolation.to_interpolate(token, &block)
56
+ end
57
+
58
+ #
59
+ # Register a custom type detector.
60
+ #
61
+ # When instantiating an attachment, Bulldog will use the
62
+ # configured type detector to work out which type of attachment to
63
+ # use. This method registers a custom type detector which may be
64
+ # selected in your attachment configuration by just using the
65
+ # name.
66
+ #
67
+ # The given block is passed the record, the attribute name, and
68
+ # the value as a Bulldog::Stream. The block should return a
69
+ # symbol representing the attachment type.
70
+ #
71
+ # Example:
72
+ #
73
+ # Bulldog.to_detect_type_by :file_extension do |record, name, stream|
74
+ # case File.extname(stream.file_name).sub(/\A\./)
75
+ # when 'jpg', 'png', 'gif', 'tiff'
76
+ # :image
77
+ # when 'mpg', 'avi', 'ogv'
78
+ # :video
79
+ # when 'pdf'
80
+ # :pdf
81
+ # end
82
+ # end
83
+ #
84
+ def to_detect_type_by(name, &block)
85
+ Reflection.to_detect_type_by(name, &block)
86
+ end
87
+ end
88
+
89
+ self.logger = nil
90
+ self.default_path_template = nil
91
+ self.default_url_template = "/assets/:class/:id.:style.:extension"
92
+ end
93
+
94
+ ActiveRecord::Base.send :include, Bulldog::HasAttachment
95
+ ActiveRecord::Base.send :include, Bulldog::Validations
@@ -0,0 +1,49 @@
1
+ module Bulldog
2
+ module Attachment
3
+ def self.types_to_classes #:nodoc:
4
+ @types_to_classes ||= {}
5
+ end
6
+ end
7
+ end
8
+
9
+ require 'bulldog/attachment/maybe'
10
+ require 'bulldog/attachment/has_dimensions'
11
+ require 'bulldog/attachment/base'
12
+ require 'bulldog/attachment/none'
13
+ require 'bulldog/attachment/image'
14
+ require 'bulldog/attachment/video'
15
+ require 'bulldog/attachment/pdf'
16
+ require 'bulldog/attachment/unknown'
17
+
18
+ module Bulldog
19
+ module Attachment
20
+ #
21
+ # Return a new attachment of the specified type.
22
+ #
23
+ # If +type+ is nil, then the returned attachment will be None if
24
+ # the stream is nil, or Unknown otherwise.
25
+ #
26
+ def self.of_type(type, record, name, stream)
27
+ if type.nil?
28
+ klass = stream ? Unknown : None
29
+ else
30
+ klass = class_from_type(type)
31
+ end
32
+ klass.new(record, name, stream)
33
+ end
34
+
35
+ #
36
+ # Return a None attachment for the given record and name.
37
+ #
38
+ def self.none(record, name)
39
+ None.new(record, name, nil)
40
+ end
41
+
42
+ #
43
+ # Return the class corresponding to the given type.
44
+ #
45
+ def self.class_from_type(type)
46
+ types_to_classes[type]
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,167 @@
1
+ module Bulldog
2
+ module Attachment
3
+ class Base < Maybe
4
+ #
5
+ # Return an instance of the record if the stream is valid, nil
6
+ # otherwise.
7
+ #
8
+ def self.try(record, name, stream)
9
+ attachment = new(record, name, stream)
10
+ attachment.send(:examine) ? attachment : nil
11
+ end
12
+
13
+ def initialize(record, name, stream)
14
+ @record = record
15
+ @name = name
16
+ @stream = stream
17
+ @value = stream && stream.target
18
+ @examination_result = nil
19
+ end
20
+
21
+ #
22
+ # Run the processors for the named event.
23
+ #
24
+ def process(event_name, *args)
25
+ reflection.events[event_name].each do |event|
26
+ if (types = event.attachment_types)
27
+ next unless types.include?(type)
28
+ end
29
+ processor_type = event.processor_type || default_processor_type
30
+ processor_class = Processor.const_get(processor_type.to_s.camelize)
31
+ processor = processor_class.new(self, styles_for_event(event), stream.path)
32
+ processor.process(*args, &event.callback)
33
+ end
34
+ end
35
+
36
+ #
37
+ # Return the path on the file system where the given style's
38
+ # image is to be stored.
39
+ #
40
+ def path(style_name = reflection.default_style)
41
+ interpolate_path(style_name)
42
+ end
43
+
44
+ #
45
+ # Return the URL where the given style's image is to be found.
46
+ #
47
+ def url(style_name = reflection.default_style)
48
+ interpolate_url(style_name)
49
+ end
50
+
51
+ #
52
+ # Return the size of the attached file.
53
+ #
54
+ def file_size
55
+ stream.size
56
+ end
57
+
58
+ #
59
+ # Return the original file name of the attached file.
60
+ #
61
+ def file_name
62
+ @file_name ||= stream.file_name
63
+ end
64
+
65
+ #
66
+ # Return the content type of the data.
67
+ #
68
+ def content_type
69
+ @content_type ||= stream.content_type
70
+ end
71
+
72
+ #
73
+ # Called when the attachment is saved.
74
+ #
75
+ def save
76
+ return if saved?
77
+ self.saved = true
78
+ original_path = interpolate_path(:original)
79
+ stream.write_to(original_path)
80
+ end
81
+
82
+ #
83
+ # Called when the attachment is destroyed, or just before
84
+ # another attachment is saved in its place.
85
+ #
86
+ def destroy
87
+ delete_files_and_empty_parent_directories
88
+ end
89
+
90
+ #
91
+ # Set any attributes that the attachment is configured to store.
92
+ #
93
+ storable_attribute :file_name
94
+ storable_attribute :file_size
95
+ storable_attribute :content_type
96
+
97
+ #
98
+ # Ensure the file examination has been run, and return the
99
+ # result.
100
+ #
101
+ def examine
102
+ if @examination_result.nil?
103
+ @examination_result = run_examination
104
+ else
105
+ @examination_result
106
+ end
107
+ end
108
+
109
+ protected # ---------------------------------------------------
110
+
111
+ #
112
+ # Return the default processor class to use for this attachment.
113
+ #
114
+ def default_processor_type
115
+ :base
116
+ end
117
+
118
+ #
119
+ # Examine the stream.
120
+ #
121
+ # Return true if the stream looks like a valid instance of this
122
+ # attachment type, false otherwise.
123
+ #
124
+ def run_examination
125
+ true
126
+ end
127
+
128
+ private # -------------------------------------------------------
129
+
130
+ def styles_for_event(event)
131
+ if event.styles
132
+ styles = reflection.styles.slice(*event.styles)
133
+ else
134
+ styles = reflection.styles
135
+ end
136
+ end
137
+
138
+ #
139
+ # Remove the files for this attachment, along with any parent
140
+ # directories.
141
+ #
142
+ def delete_files_and_empty_parent_directories
143
+ style_names = reflection.styles.map{|style| style.name} << :original
144
+ # If the attachment was set to nil, we need the original value
145
+ # to work out what to delete.
146
+ if column_name = reflection.column_name_for_stored_attribute(:file_name)
147
+ interpolation_params = {:basename => record.send("#{column_name}_was")}
148
+ else
149
+ interpolation_params = {}
150
+ end
151
+ style_names.each do |style_name|
152
+ path = interpolate_path(style_name, interpolation_params) or
153
+ next
154
+ FileUtils.rm_f(path)
155
+ begin
156
+ loop do
157
+ path = File.dirname(path)
158
+ FileUtils.rmdir(path)
159
+ end
160
+ rescue Errno::EEXIST, Errno::ENOTEMPTY, Errno::ENOENT, Errno::EINVAL, Errno::ENOTDIR
161
+ # Can't delete any further.
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end