bulldog 0.0.5 → 0.0.6

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.
data/CHANGELOG CHANGED
@@ -1,3 +1,8 @@
1
+ == 0.0.6 2009-11-17
2
+
3
+ * Add attachment#reload for when the file changes behind Bulldog's
4
+ back.
5
+
1
6
  == 0.0.5 2009-11-17
2
7
 
3
8
  * Fix @original_attachments not being set in after save.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.5
1
+ 0.0.6
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{bulldog}
8
- s.version = "0.0.5"
8
+ s.version = "0.0.6"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["George Ogata"]
@@ -62,6 +62,7 @@ Flexible file attachments for active record.
62
62
  "spec/data/empty.txt",
63
63
  "spec/data/test.jpg",
64
64
  "spec/data/test.mov",
65
+ "spec/data/test.ogg",
65
66
  "spec/data/test.pdf",
66
67
  "spec/data/test.png",
67
68
  "spec/data/test2.jpg",
@@ -118,6 +118,11 @@ module Bulldog
118
118
  end
119
119
  end
120
120
 
121
+ def unload
122
+ super
123
+ @examination_result = nil
124
+ end
125
+
121
126
  protected # ---------------------------------------------------
122
127
 
123
128
  #
@@ -27,6 +27,11 @@ module Bulldog
27
27
 
28
28
  include HasDimensions
29
29
 
30
+ def unload
31
+ super
32
+ @original_dimensions = nil
33
+ end
34
+
30
35
  protected # ---------------------------------------------------
31
36
 
32
37
  #
@@ -66,9 +66,12 @@ module Bulldog
66
66
  end
67
67
 
68
68
  #
69
- # Set the stored attributes in the record.
69
+ # Load the attachment.
70
70
  #
71
- def set_stored_attributes
71
+ # Called just after initializing the attachment, or after a
72
+ # reload on the new attachment.
73
+ #
74
+ def load
72
75
  storable_attributes.each do |name, storable_attribute|
73
76
  if (column_name = reflection.column_name_for_stored_attribute(name))
74
77
  value = storable_attribute.value_for(self, :original)
@@ -78,16 +81,46 @@ module Bulldog
78
81
  end
79
82
 
80
83
  #
81
- # Clear the stored attributes in the record.
84
+ # Unload the attachment.
85
+ #
86
+ # Called before a reload on the old attachment, or before a
87
+ # destroy.
82
88
  #
83
- def clear_stored_attributes
84
- storable_attributes.each do |name, callback|
89
+ def unload
90
+ storable_attributes.each do |name, storable_attribute|
85
91
  if (column_name = reflection.column_name_for_stored_attribute(name))
86
92
  record.send("#{column_name}=", nil)
87
93
  end
94
+ if storable_attribute.memoize?
95
+ send("memoized_#{name}").clear
96
+ end
88
97
  end
89
98
  end
90
99
 
100
+ #
101
+ # Refresh the attachment. This includes anything read from the
102
+ # file, such as stored attributes, and dimensions.
103
+ #
104
+ # Use this to update the record if the underlying file has been
105
+ # modified outside of Bulldog. Note that the file has to be
106
+ # initialized from a saved file for this to work.
107
+ #
108
+ # Example:
109
+ #
110
+ # article = Article.create(:photo => photo_file)
111
+ # article = Article.find(article.id)
112
+ #
113
+ # # ... file is modified by an outside force ...
114
+ #
115
+ # article.photo.reload
116
+ # article.photo.dimensions # now reflects the new dimensions
117
+ #
118
+ def reload
119
+ unload
120
+ stream.reload
121
+ load
122
+ end
123
+
91
124
  #
92
125
  # Set the stored attributes in the attachment from the values in
93
126
  # the record.
@@ -80,6 +80,13 @@ module Bulldog
80
80
 
81
81
  storable_attribute :duration , :per_style => true, :memoize => true
82
82
 
83
+ def unload
84
+ super
85
+ instance_variables.grep(/@original_/).each do |name|
86
+ instance_variable_set(name, nil)
87
+ end
88
+ end
89
+
83
90
  protected # ---------------------------------------------------
84
91
 
85
92
  #
@@ -99,9 +99,9 @@ module Bulldog
99
99
  def assign_attachment(name, value)
100
100
  old_attachment = _attachment_for(name)
101
101
  unless old_attachment.value == value
102
- old_attachment.clear_stored_attributes
102
+ old_attachment.unload
103
103
  new_attachment = make_attachment_for(name, value)
104
- new_attachment.set_stored_attributes
104
+ new_attachment.load
105
105
  write_attribute(name, new_attachment)
106
106
  end
107
107
  end
@@ -88,6 +88,13 @@ module Bulldog
88
88
  FileUtils.cp src, dst
89
89
  end
90
90
  end
91
+
92
+ #
93
+ # Reload data from the target.
94
+ #
95
+ def reload
96
+ @content_type = nil
97
+ end
91
98
  end
92
99
 
93
100
  class ForTempfile < Base
Binary file
@@ -125,4 +125,40 @@ describe Attachment::Image do
125
125
  end
126
126
  end
127
127
  end
128
+
129
+ describe "#reload" do
130
+ before do
131
+ thing = Thing.create(:photo => test_image_file('test.jpg'))
132
+ @thing = Thing.find(thing.id)
133
+ end
134
+
135
+ it "should update the stored attributes from the file" do
136
+ # Prime the cached values.
137
+ @thing.photo_width.should == 40
138
+ @thing.photo_height.should == 30
139
+ @thing.photo_aspect_ratio.should be_close(4.0/3, 1e-5)
140
+ @thing.photo_dimensions.should == '40x30'
141
+
142
+ FileUtils.cp(test_path('test2.jpg'), @thing.photo.path(:original))
143
+ @thing.photo.reload
144
+ @thing.photo_width.should == 2
145
+ @thing.photo_height.should == 2
146
+ @thing.photo_aspect_ratio.should == 1
147
+ @thing.photo_dimensions.should == '2x2'
148
+ end
149
+
150
+ it "should update the original dimensions from the file" do
151
+ @thing.photo.dimensions(:original).should == [40, 30]
152
+ FileUtils.cp(test_path('test2.jpg'), @thing.photo.path(:original))
153
+ @thing.photo.reload
154
+ @thing.photo.dimensions(:original).should == [2, 2]
155
+ end
156
+
157
+ it "should update the dimensions for each style from the file" do
158
+ @thing.photo.dimensions(:double).should == [80, 60]
159
+ FileUtils.cp(test_path('test2.jpg'), @thing.photo.path(:original))
160
+ @thing.photo.reload
161
+ @thing.photo.dimensions(:double).should == [60, 60]
162
+ end
163
+ end
128
164
  end
@@ -1,7 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Attachment::Maybe do
4
- use_model_class(:Thing, :photo_file_name => :string)
4
+ use_model_class(:Thing,
5
+ :photo_file_name => :string,
6
+ :photo_file_size => :integer,
7
+ :photo_content_type => :string)
5
8
 
6
9
  before do
7
10
  spec = self
@@ -123,4 +126,23 @@ describe Attachment::Maybe do
123
126
  end
124
127
  end
125
128
  end
129
+
130
+ describe "#reload" do
131
+ before do
132
+ thing = Thing.create(:photo => test_file('test.png'))
133
+ @thing = Thing.find(thing.id)
134
+ end
135
+
136
+ it "should update the file size stored attribute from the file" do
137
+ FileUtils.cp(test_path('test2.jpg'), @thing.photo.path(:original))
138
+ @thing.photo.reload
139
+ @thing.photo_file_size.should == File.size(test_path('test2.jpg'))
140
+ end
141
+
142
+ it "should reload the content type stored attribute from the file" do
143
+ FileUtils.cp(test_path('test.png'), @thing.photo.path(:original))
144
+ @thing.photo.reload
145
+ @thing.photo_content_type.should =~ %r'image/png'
146
+ end
147
+ end
126
148
  end
@@ -173,4 +173,40 @@ describe Attachment::Video do
173
173
  end
174
174
  end
175
175
  end
176
+
177
+ describe "#reload" do
178
+ before do
179
+ thing = Thing.create(:video => test_video_file('test.mov'))
180
+ @thing = Thing.find(thing.id)
181
+ end
182
+
183
+ it "should update the stored attributes from the file" do
184
+ # Prime the cached values.
185
+ @thing.video_width.should == 640
186
+ @thing.video_height.should == 480
187
+ @thing.video_aspect_ratio.should be_close(4.0/3, 1e-5)
188
+ @thing.video_dimensions.should == '640x480'
189
+
190
+ FileUtils.cp(test_path('test.ogg'), @thing.video.path(:original))
191
+ @thing.video.reload
192
+ @thing.video_width.should == 176
193
+ @thing.video_height.should == 144
194
+ @thing.video_aspect_ratio.should == 176.0/144
195
+ @thing.video_dimensions.should == '176x144'
196
+ end
197
+
198
+ it "should update the original dimensions from the file" do
199
+ @thing.video.dimensions(:original).should == [640, 480]
200
+ FileUtils.cp(test_path('test.ogg'), @thing.video.path(:original))
201
+ @thing.video.reload
202
+ @thing.video.dimensions(:original).should == [176, 144]
203
+ end
204
+
205
+ it "should update the dimensions for each style from the file" do
206
+ @thing.video.dimensions(:half).should == [320, 240]
207
+ FileUtils.cp(test_path('test.ogg'), @thing.video.path(:original))
208
+ @thing.video.reload
209
+ @thing.video.dimensions(:half).should == [292, 240]
210
+ end
211
+ end
176
212
  end
@@ -82,6 +82,40 @@ describe Stream do
82
82
  end
83
83
  end
84
84
  end
85
+
86
+ describe "#reload" do
87
+ if options[:reloadable]
88
+ it "should make #size return the new size of the file" do
89
+ stream = stream('content')
90
+ update_target(stream, 'new content')
91
+ stream.reload
92
+ stream.size.should == 'new content'.size
93
+ end
94
+
95
+ it "should make #content_type return the new content type of the file" do
96
+ jpg_data = File.read(test_path('test.jpg'))
97
+ png_data = File.read(test_path('test.png'))
98
+ stream = stream(jpg_data)
99
+ stream.content_type.should =~ %r'\Aimage/jpeg'
100
+ update_target(stream, png_data)
101
+ stream.reload
102
+ stream.content_type.should =~ %r'\Aimage/png'
103
+ end
104
+ else
105
+ it "should not change the result of #size" do
106
+ stream = stream('content')
107
+ stream.reload
108
+ stream.size.should == 'content'.size
109
+ end
110
+
111
+ it "should not change the result of #content_type" do
112
+ jpg_data = File.read(test_path('test.jpg'))
113
+ stream = stream(jpg_data)
114
+ stream.reload
115
+ stream.content_type.should =~ %r'\Aimage/jpeg'
116
+ end
117
+ end
118
+ end
85
119
  end
86
120
 
87
121
  describe 'for a small uploaded file' do
@@ -100,28 +134,28 @@ describe Stream do
100
134
  it_should_behave_like_all_streams :file_name => :original_path
101
135
 
102
136
  def object(content)
103
- stringio = StringIO.new(content)
104
- class << stringio
137
+ tempfile = Tempfile.new('bulldog-spec')
138
+ tempfile.print(content)
139
+ class << tempfile
105
140
  attr_accessor :original_path
106
141
  end
107
- stringio
142
+ tempfile
108
143
  end
109
144
  end
110
145
 
111
- describe 'for a StringIO' do
146
+ describe Stream::ForStringIO do
112
147
  it_should_behave_like_all_streams
113
148
 
114
149
  def object(content)
115
- tempfile = Tempfile.new('bulldog-spec')
116
- tempfile.print(content)
117
- class << tempfile
150
+ stringio = StringIO.new(content)
151
+ class << stringio
118
152
  attr_accessor :original_path
119
153
  end
120
- tempfile
154
+ stringio
121
155
  end
122
156
  end
123
157
 
124
- describe 'for a Tempfile' do
158
+ describe Stream::ForTempfile do
125
159
  it_should_behave_like_all_streams
126
160
 
127
161
  def object(content)
@@ -131,7 +165,7 @@ describe Stream do
131
165
  end
132
166
  end
133
167
 
134
- describe 'for a File opened for reading' do
168
+ describe "Stream::ForFile (opened for writing)" do
135
169
  it_should_behave_like_all_streams :file_name => :basename
136
170
 
137
171
  def object(content)
@@ -152,7 +186,7 @@ describe Stream do
152
186
  end
153
187
  end
154
188
 
155
- describe 'for a File opened for writing' do
189
+ describe "Stream::ForFile (opened for writing)" do
156
190
  it_should_behave_like_all_streams :file_name => :basename
157
191
 
158
192
  def object(content)
@@ -162,17 +196,21 @@ describe Stream do
162
196
  end
163
197
  end
164
198
 
165
- describe 'for an SavedFile' do
166
- it_should_behave_like_all_streams :file_name => :file_name
199
+ describe Stream::ForSavedFile do
200
+ it_should_behave_like_all_streams :file_name => :file_name, :reloadable => true
167
201
 
168
202
  def object(content)
169
203
  path = "#{temporary_directory}/file"
170
204
  open(path, 'w'){|f| f.print content}
171
205
  SavedFile.new(path)
172
206
  end
207
+
208
+ def update_target(stream, content)
209
+ open(stream.target.path, 'w'){|f| f.print content}
210
+ end
173
211
  end
174
212
 
175
- describe 'for a MissingFile' do
213
+ describe Stream::ForMissingFile do
176
214
  it_should_behave_like_all_streams :file_name => :file_name
177
215
 
178
216
  def object(content)
@@ -210,12 +248,14 @@ describe Stream do
210
248
  end
211
249
  end
212
250
 
213
- describe 'for an IO' do
251
+ describe Stream::ForIO do
214
252
  it_should_behave_like_all_streams
215
253
 
216
254
  def object(content)
217
- io = IO.popen("echo -n #{content}")
218
- autoclose_stream(io)
255
+ readable, writable = IO.pipe
256
+ writable.print content
257
+ writable.close
258
+ autoclose_stream(readable)
219
259
  end
220
260
 
221
261
  describe "#path" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bulldog
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - George Ogata
@@ -98,6 +98,7 @@ files:
98
98
  - spec/data/empty.txt
99
99
  - spec/data/test.jpg
100
100
  - spec/data/test.mov
101
+ - spec/data/test.ogg
101
102
  - spec/data/test.pdf
102
103
  - spec/data/test.png
103
104
  - spec/data/test2.jpg