griddle 0.0.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown ADDED
@@ -0,0 +1,112 @@
1
+ Griddle: GridFileSystem made simple
2
+ =======================================
3
+
4
+ Griddle is a file attachment gem for use with `mongo-ruby-driver`.
5
+
6
+ Installation
7
+ ------------------
8
+
9
+ Install the gem:
10
+
11
+ gem install griddle
12
+
13
+
14
+ Usage
15
+ ---------------------------------------
16
+
17
+ A class with a grid attachment:
18
+
19
+ class Document
20
+
21
+ include Griddle::HasGridAttachment
22
+
23
+ has_grid_attachment :image, :styles=>{
24
+ :thumb => "50x50#"
25
+ }
26
+
27
+ end
28
+
29
+ Or, alternately if you're using an object model
30
+
31
+ class Document
32
+
33
+ include MongoMapper::Document
34
+ include Griddle::HasGridAttachment
35
+
36
+ has_grid_attachment :image, :styles=>{
37
+ :thumb => "50x50#"
38
+ }
39
+
40
+ end
41
+
42
+ Create a document:
43
+
44
+ @document = Document.new
45
+ @document.image = File.new("attached_file.jpg", 'rb')
46
+ @document.save_attached_files
47
+
48
+ Or, if you're using an object model, `saved_attached_files` is called `after_save`:
49
+
50
+ image = File.new("attached_file.jpg", 'rb')
51
+ @document = Document.new(:image => image)
52
+ @document.save
53
+
54
+ Retrieving A File
55
+ -----------------
56
+
57
+ The contents of a file stored in GridFileSystem can be retrieved using `Mongo::GridIO` accessed by the `file` method:
58
+
59
+ @document.image.file
60
+ => <#Mongo::GridIO>
61
+
62
+ @document.image.file.read
63
+ => contents of file
64
+
65
+ Some other methods that may be helpful to know:
66
+
67
+ # does the attachment exist?
68
+ @document.image.exist?
69
+ => true
70
+
71
+ # attachment file name
72
+ @document.image.file_name
73
+ => attached_file.jpg
74
+
75
+ # attachment grid key
76
+ @document.image.grid_key
77
+ => document/12345/image/attached_file.jpg
78
+
79
+ Styles
80
+ ------
81
+
82
+ Griddle makes use of `ImageMagik` processor to fit and/or crop images and store different image `styles`:
83
+
84
+ @document.image.styles
85
+ => {:thumb => '50x50#'}
86
+
87
+ Each style is saved as a `Griddle::Attachment` as well:
88
+
89
+ @document.image.thumb.exist?
90
+ => true
91
+
92
+ @document.image.thumb.file_name
93
+ => attached_file.jpg
94
+
95
+ @document.image.thumb.grid_key
96
+ => documents/12345/images/attached_file.jpg
97
+
98
+ Note on Patches/Pull Requests
99
+ -----------------------------
100
+
101
+ * Fork the project.
102
+ * Make your feature addition or bug fix.
103
+ * Add tests for it. This is important so I don't break it in a
104
+ future version unintentionally.
105
+ * Commit, do not mess with rakefile, version, or history.
106
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
107
+ * Send me a pull request. Bonus points for topic branches.
108
+
109
+ Copyright
110
+ ---------
111
+
112
+ Copyright (c) 2010 Matt Matt san Mongeau. See LICENSE for details.
data/Rakefile CHANGED
@@ -9,9 +9,9 @@ begin
9
9
  gem.description = %Q{GridFS made simple...}
10
10
  gem.email = "matt@toastyapps.com"
11
11
  gem.homepage = "http://github.com/toastyapps/griddle"
12
- gem.authors = ["Matt Mongeau"]
12
+ gem.authors = ["Matt Mongeau","meanmarcus"]
13
13
  gem.add_development_dependency "shoulda", ">= 0"
14
- gem.add_dependency "mongo_mapper", ">= 0"
14
+ gem.add_development_dependency "mongo_mapper", ">= 0"
15
15
  end
16
16
  Jeweler::GemcutterTasks.new
17
17
  rescue LoadError
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.0
1
+ 0.1.2
data/griddle.gemspec CHANGED
@@ -5,37 +5,43 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{griddle}
8
- s.version = "0.0.0"
8
+ s.version = "0.1.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Matt Mongeau"]
12
- s.date = %q{2010-03-03}
11
+ s.authors = ["Matt Mongeau", "meanmarcus"]
12
+ s.date = %q{2010-05-30}
13
13
  s.description = %q{GridFS made simple...}
14
14
  s.email = %q{matt@toastyapps.com}
15
15
  s.extra_rdoc_files = [
16
16
  "LICENSE",
17
- "README.rdoc"
17
+ "README.markdown"
18
18
  ]
19
19
  s.files = [
20
20
  ".document",
21
21
  ".gitignore",
22
22
  "LICENSE",
23
- "README.rdoc",
23
+ "README.markdown",
24
24
  "Rakefile",
25
25
  "VERSION",
26
26
  "griddle.gemspec",
27
27
  "lib/griddle.rb",
28
28
  "lib/griddle/attachment.rb",
29
29
  "lib/griddle/has_grid_attachment.rb",
30
+ "lib/griddle/processor.rb",
31
+ "lib/griddle/processor/image_magick.rb",
30
32
  "lib/griddle/style.rb",
31
33
  "lib/griddle/upfile.rb",
32
34
  "rails/init.rb",
33
35
  "test/attachment_test.rb",
34
36
  "test/fixtures/baboon.jpg",
37
+ "test/fixtures/climenole.jpeg",
35
38
  "test/fixtures/fox.jpg",
36
39
  "test/fixtures/sample.pdf",
40
+ "test/fixtures/squid.png",
37
41
  "test/has_attachment_test.rb",
38
42
  "test/models.rb",
43
+ "test/mongo_mapper_models.rb",
44
+ "test/processor_test.rb",
39
45
  "test/style_test.rb",
40
46
  "test/test_helper.rb",
41
47
  "test/upfile_test.rb"
@@ -43,12 +49,14 @@ Gem::Specification.new do |s|
43
49
  s.homepage = %q{http://github.com/toastyapps/griddle}
44
50
  s.rdoc_options = ["--charset=UTF-8"]
45
51
  s.require_paths = ["lib"]
46
- s.rubygems_version = %q{1.3.5}
52
+ s.rubygems_version = %q{1.3.7}
47
53
  s.summary = %q{GridFS made simple.}
48
54
  s.test_files = [
49
55
  "test/attachment_test.rb",
50
56
  "test/has_attachment_test.rb",
51
57
  "test/models.rb",
58
+ "test/mongo_mapper_models.rb",
59
+ "test/processor_test.rb",
52
60
  "test/style_test.rb",
53
61
  "test/test_helper.rb",
54
62
  "test/upfile_test.rb"
@@ -58,9 +66,9 @@ Gem::Specification.new do |s|
58
66
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
59
67
  s.specification_version = 3
60
68
 
61
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
69
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
62
70
  s.add_development_dependency(%q<shoulda>, [">= 0"])
63
- s.add_runtime_dependency(%q<mongo_mapper>, [">= 0"])
71
+ s.add_development_dependency(%q<mongo_mapper>, [">= 0"])
64
72
  else
65
73
  s.add_dependency(%q<shoulda>, [">= 0"])
66
74
  s.add_dependency(%q<mongo_mapper>, [">= 0"])
@@ -1,71 +1,242 @@
1
1
  module Griddle
2
2
  class Attachment
3
- include MongoMapper::Document
4
3
 
5
- belongs_to :owner, :polymorphic => true
6
- key :name, String
7
- key :owner_id, ObjectId, :required => true
8
- key :owner_type, String, :required => true
9
- key :file_name, String
10
- key :file_size, Integer
11
- key :content_type, String
12
- key :styles, Hash
13
- key :options, Hash
14
-
15
- before_destroy :destroy_file
16
- before_save :save_file
4
+ include Mongo
5
+
6
+ def self.attachment_for(options)
7
+ options = self.clean_options(options)
8
+ options_for_search = {:name => options[:name], :owner_type => options[:owner_type], :owner_id => options[:owner_id]}
9
+ record = collection.find_one(options_for_search)
10
+ return new(record) unless record.nil?
11
+ return new(options)
12
+ end
13
+
14
+ def self.collection
15
+ @collection ||= Griddle.database.collection('griddle.attachments')
16
+ end
17
17
 
18
18
  def self.for(name, owner, options = {})
19
- a = Attachment.find_or_create_by_name_and_owner_type_and_owner_id(name, owner.class.to_s, owner.id)
20
- if options.has_key?(:styles)
21
- a.styles = (options[:styles] || {}).inject({}) do |h, value|
22
- h[value.first] = Style.new value.first, value.last, a
23
- h
19
+ attachment_for(options.merge({
20
+ :name => name,
21
+ :owner_type => owner.class,
22
+ :owner_id => owner.id
23
+ }))
24
+ end
25
+
26
+ def self.valid_attributes
27
+ [:name, :owner_id, :owner_type, :file_name, :file_size, :content_type, :styles, :options]
28
+ end
29
+ # belongs_to :owner, :polymorphic => true
30
+
31
+ attr_accessor :attributes
32
+
33
+ def initialize(attributes = {})
34
+ @grid = GridFileSystem.new(Griddle.database)
35
+ @attributes = attributes.symbolize_keys
36
+ initialize_processor
37
+ initialize_styles
38
+ create_attachments_for_styles
39
+ end
40
+
41
+ def assign(uploaded_file)
42
+ if valid_assignment?(uploaded_file)
43
+ self.file = uploaded_file
44
+ self.dirty!
45
+ end
46
+ end
47
+
48
+ def attributes
49
+ @attributes
50
+ end
51
+
52
+ def attributes=(attributes)
53
+ @attributes.merge!(attributes).symbolize_keys
54
+ end
55
+
56
+ def collection
57
+ @collection ||= self.class.collection
58
+ end
59
+
60
+ def destroy
61
+ destroy_file
62
+ collection.remove({:name => name, :owner_type => owner_type, :owner_id => owner_id})
63
+ end
64
+
65
+ def method_missing(method, *args, &block)
66
+ key = method.to_s.gsub(/\=$/, '').to_sym
67
+ if self.class.valid_attributes.include?(key)
68
+ if key != method
69
+ @attributes[key] = args[0]
70
+ else
71
+ @attributes[key]
24
72
  end
73
+ else
74
+ super
25
75
  end
26
- a
76
+ end
77
+
78
+ def destroy_file
79
+ @grid.delete(grid_key)
80
+ destroy_styles
81
+ end
82
+
83
+ def dirty?
84
+ @dirty ||= false
85
+ @dirty
86
+ end
87
+
88
+ def exists?
89
+ Griddle.database['fs.files'].find({'filename' => self.grid_key}).count > 0
27
90
  end
28
91
 
29
92
  def grid_key
30
- @grid_key ||= "#{owner_type.tableize}/#{owner_id}/#{name}/#{file_name}".downcase
93
+ "#{owner_type.tableize}/#{owner_id}/#{name}/#{self.file_name}".downcase
31
94
  end
32
95
 
33
- def assign(uploaded_file)
34
- return nil unless valid_assignment?(uploaded_file)
35
- @tmp_file = uploaded_file
96
+ def file
97
+ @grid.open(grid_key, 'r') if exists?
36
98
  end
37
99
 
38
100
  def file=(new_file)
39
- file_name = new_file.respond_to?(:original_filename) ? new_file.original_filename : File.basename(new_file.path)
40
- self.file_name = file_name
41
- self.file_size = File.size(new_file)
101
+ filename = clean_filename(new_file.respond_to?(:original_filename) ? new_file.original_filename : File.basename(new_file.path))
102
+ self.file_name = filename
103
+ self.file_size = File.size(new_file.path)
42
104
  self.content_type = new_file.content_type
43
-
44
- GridFS::GridStore.open(self.class.database, grid_key, 'w', :content_type => self.content_type) do |f|
45
- f.write new_file.read
105
+ @tmp_file = new_file
106
+ end
107
+
108
+ def name= name
109
+ @attributes[:name] = name.to_sym
110
+ end
111
+
112
+ def owner_id= id
113
+ @attributes[:owner_id] = id.to_s
114
+ end
115
+
116
+ def owner_type= str
117
+ @attributes[:owner_type] = str.to_s
118
+ end
119
+
120
+ def processor
121
+ @processor
122
+ end
123
+
124
+ def processor= processor
125
+ @attributes[:processor] = processor
126
+ initialize_processor
127
+ end
128
+
129
+ def save
130
+ if valid?
131
+ destroy
132
+ save_file
133
+ collection.insert(valid_attributes(@attributes).stringify_keys)
46
134
  end
47
135
  end
48
136
 
49
- def file
50
- GridFS::GridStore.new(self.class.database, grid_key, 'r') unless file_name.blank?
137
+ def styles
138
+ @styles
51
139
  end
52
140
 
53
- def destroy_file
54
- GridFS::GridStore.unlink(self.class.database, grid_key)
141
+ def styles= styles
142
+ @attributes[:styles] = styles
143
+ initialize_styles
55
144
  end
56
145
 
57
- def exists?
58
- !file_name.nil?
146
+ def valid?
147
+ dirty? && valid_assignment?(@tmp_file)
148
+ end
149
+
150
+ def valid_attributes(attributes)
151
+ Hash[*attributes.select{|key, value| self.class.valid_attributes.include?(key) }.flatten]
152
+ end
153
+
154
+ protected
155
+
156
+ def dirty!
157
+ @dirty = true
59
158
  end
60
159
 
61
160
  private
62
161
 
162
+ def self.clean_options(options)
163
+ options.symbolize_keys!
164
+ options.merge({
165
+ :name => options[:name].to_sym,
166
+ :owner_type => options[:owner_type].to_s,
167
+ :owner_id => options[:owner_id].to_s
168
+ })
169
+ end
170
+
171
+ def clean_filename str
172
+ tmp_file_reg = /\.([a-z]{2}[a-z0-9]{0,2})#{Time.now.strftime('%Y%m%d')}-.+/
173
+ str.gsub(tmp_file_reg,'.\1').gsub(/[?:\/*""<>|]+/,'_')
174
+ end
175
+
176
+ def create_attachments_for_styles
177
+ self.styles.each do |h|
178
+ create_style_attachment h[0]
179
+ end
180
+ end
181
+
182
+ def create_style_attachment style_name
183
+ raise "Invalid style name :#{style_name}. #{style_name} is a reserved word." if respond_to?(style_name) || !attributes[style_name.to_sym].nil?
184
+
185
+ attrs = attributes.merge({
186
+ :name => "#{name}/#{style_name}",
187
+ :owner_id => @attributes[:owner_id].to_s,
188
+ :styles => {}
189
+ })
190
+ self.class_eval do
191
+
192
+ define_method(style_name) do |*args|
193
+ Attachment.attachment_for(attrs)
194
+ end
195
+
196
+ define_method("#{style_name}=") do |file|
197
+ Attachment.attachment_for(attrs).assign(file)
198
+ end
199
+
200
+ end
201
+ end
202
+
203
+ def destroy_styles
204
+ styles.each{|s| send(s[0]).destroy }
205
+ end
206
+
207
+ def initialize_processor
208
+ @processor = Processor.new @attributes[:processor]
209
+ end
210
+
211
+ def initialize_styles
212
+ @styles = {}
213
+ if @attributes[:styles] && @attributes[:styles].is_a?(Hash)
214
+ @styles = @attributes[:styles].inject({}) do |h, value|
215
+ h[value.first.to_sym] = Style.new value.first, value.last, self
216
+ h
217
+ end
218
+ end
219
+ end
220
+
63
221
  def save_file
64
- self.file = @tmp_file if @tmp_file
222
+ @tmp_file.rewind
223
+ @grid.open(grid_key, 'w', :content_type => self.content_type) do |f|
224
+ f.write @tmp_file.read
225
+ end
226
+ save_styles
227
+ end
228
+
229
+ def save_styles
230
+ styles.each do |h|
231
+ processed_file = processor.process_image(@tmp_file, h[1])
232
+ style_attachment = send(h[0])
233
+ style_attachment.assign(processed_file)
234
+ style_attachment.save
235
+ end
65
236
  end
66
237
 
67
238
  def valid_assignment?(file)
68
- file.nil? || (file.respond_to?(:original_filename) && file.respond_to?(:content_type))
239
+ (file.respond_to?(:original_filename) && file.respond_to?(:content_type))
69
240
  end
70
241
 
71
242
  end
@@ -1,4 +1,3 @@
1
- require 'mongo/gridfs'
2
1
  module Griddle
3
2
  module HasGridAttachment
4
3
  def self.included(base)
@@ -13,14 +12,15 @@ module Griddle
13
12
  write_inheritable_attribute(:attachment_definitions, {}) if attachment_definitions.nil?
14
13
  attachment_definitions[name] = options
15
14
 
16
- after_save :save_attached_files
15
+ after_save :save_attached_files if respond_to? :after_save
16
+ after_destroy :destroy_attached_files if respond_to? :after_destroy
17
17
 
18
18
  define_method(name) do |*args|
19
- attachment_for(name)
19
+ attachment_for(name, options)
20
20
  end
21
21
 
22
22
  define_method("#{name}=") do |file|
23
- attachment_for(name).assign(file)
23
+ attachment_for(name, options).assign(file)
24
24
  end
25
25
  end
26
26
 
@@ -31,9 +31,13 @@ module Griddle
31
31
 
32
32
  module InstanceMethods
33
33
 
34
- def attachment_for name
34
+ def attachment_for name, options = {}
35
35
  @_gripster_attachments ||= {}
36
- @_gripster_attachments[name] ||= Attachment.for(name, self)
36
+ @_gripster_attachments[name] ||= Attachment.for(name, self, options)
37
+ end
38
+
39
+ def destroy_attached_files
40
+ each_attachment{|name, attachment| attachment.destroy }
37
41
  end
38
42
 
39
43
  def each_attachment
@@ -0,0 +1,74 @@
1
+ module Griddle
2
+ class Processor
3
+ class ImageMagick
4
+
5
+ def crop
6
+ `convert #{File.expand_path(@destination_file.path)} -crop #{geometry} -gravity Center #{@destination_file.path}`
7
+ end
8
+
9
+ def crop?
10
+ @style.geometry =~ /#/
11
+ end
12
+
13
+ def file_width
14
+ file_dimensions.first
15
+ end
16
+
17
+ def fit(geo = geometry)
18
+ `convert #{File.expand_path(@file.path)} -resize #{geo} #{@destination_file.path}`
19
+ end
20
+
21
+ def geometry
22
+ @geometry ||= @style.geometry.gsub(/#/,'')
23
+ end
24
+
25
+ def file_height
26
+ file_dimensions.last
27
+ end
28
+
29
+ def height
30
+ dimensions.last
31
+ end
32
+
33
+ def process_image file, style
34
+ @style = style
35
+ @file = file
36
+ @destination_file = Tempfile.new @file.original_filename
37
+ if crop?
38
+ fit(resize_geometry_for_crop)
39
+ crop
40
+ else
41
+ fit
42
+ end
43
+ @destination_file
44
+ end
45
+
46
+ def width
47
+ dimensions.first
48
+ end
49
+
50
+ private
51
+
52
+ def dimensions
53
+ @dimensions ||= dimensions_for(geometry)
54
+ end
55
+
56
+ def dimensions_for geo
57
+ geo.scan(/([0-9]*)x([0-9]*)/).flatten.collect{|v|v.to_f}
58
+ end
59
+
60
+ def file_dimensions
61
+ @file_dimensions ||= dimensions_for(`identify -format "%[fx:w]x%[fx:h]" #{File.expand_path(@file.path)}`)
62
+ end
63
+
64
+ def resize_for_width?
65
+ file_height*(width/file_width) >= height
66
+ end
67
+
68
+ def resize_geometry_for_crop
69
+ resize_for_width? ? "#{width}x" : "x#{height}"
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,23 @@
1
+ module Griddle
2
+ class Processor
3
+
4
+ attr_accessor :klass
5
+
6
+ def initialize( processor_class = :ImageMagick )
7
+ processor_class = :ImageMagick unless valid_processors.include? processor_class
8
+ self.klass = self.class.const_get "#{processor_class}"
9
+ end
10
+
11
+ def process_image file, style
12
+ processor = self.klass.new
13
+ raise "Define in subclass" unless processor.respond_to? :process_image
14
+ processor.process_image(file, style)
15
+ end
16
+
17
+ protected
18
+ def valid_processors
19
+ [:ImageMagick]
20
+ end
21
+
22
+ end
23
+ end
data/lib/griddle.rb CHANGED
@@ -3,16 +3,35 @@ if RUBY_VERSION =~ /1.9.[0-9]/
3
3
  else
4
4
  require 'ftools'
5
5
  end
6
+ require 'rubygems'
6
7
  require 'mime/types'
8
+ require 'tempfile'
7
9
  require 'griddle/has_grid_attachment'
8
10
  require 'griddle/attachment'
9
11
  require 'griddle/upfile'
10
12
  require 'griddle/style'
13
+ require 'griddle/processor/image_magick'
14
+ require 'griddle/processor'
11
15
 
12
16
  module Griddle
17
+
13
18
  def self.version
14
- "0.0.1"
19
+ @version ||= File.read(File.join(File.dirname(__FILE__), "..", "VERSION")).chomp
15
20
  end
21
+
22
+ def self.database
23
+ @database ||= if(defined?(MongoMapper))
24
+ MongoMapper.database
25
+ else
26
+ nil
27
+ end
28
+ end
29
+
30
+ def self.database=(database)
31
+ @database = database
32
+ end
33
+
16
34
  end
17
35
 
18
- File.send(:include, Griddle::Upfile)
36
+ File.send(:include, Griddle::Upfile)
37
+
@@ -1,6 +1,8 @@
1
1
  require "test_helper"
2
2
  require "models"
3
3
 
4
+ include Mongo
5
+
4
6
  class AttachmentTest < Test::Unit::TestCase
5
7
 
6
8
  context "An Attachment" do
@@ -19,8 +21,8 @@ class AttachmentTest < Test::Unit::TestCase
19
21
  should "#assign a valid assignment" do
20
22
  @attachment.assign(@image)
21
23
  @attachment.save
22
- assert @attachment.file.is_a? GridFS::GridStore
23
- assert GridFS::GridStore.exist?(DocNoAttachment.database, @attachment.grid_key)
24
+ assert_kind_of Mongo::GridIO, @attachment.file
25
+ assert @attachment.exists?
24
26
  end
25
27
 
26
28
  context "with a file" do
@@ -31,7 +33,7 @@ class AttachmentTest < Test::Unit::TestCase
31
33
 
32
34
  should "#destroy_file" do
33
35
  @attachment.destroy_file
34
- assert !GridFS::GridStore.exist?(DocNoAttachment.database, @attachment.grid_key)
36
+ assert !@attachment.exists?
35
37
  end
36
38
 
37
39
  end
Binary file
Binary file
Binary file
@@ -1,5 +1,6 @@
1
1
  require "test_helper"
2
2
  require "models"
3
+ include Mongo
3
4
 
4
5
  class HasAttachmentTest < Test::Unit::TestCase
5
6
  context "A Doc that has_grid_attachment :image" do
@@ -28,20 +29,83 @@ class HasAttachmentTest < Test::Unit::TestCase
28
29
  should "should return an Attachment" do
29
30
  assert_equal(Griddle::Attachment, @document.image.class)
30
31
  end
31
-
32
+
32
33
  should "read file from grid store" do
33
34
  assert_equal "image/jpeg", @file_system.find_one(:filename => @document.image.grid_key)['contentType']
34
35
  end
36
+
37
+ should "exist" do
38
+ assert @document.image.exists?
39
+ end
40
+
41
+ context "when assigned a new file" do
42
+
43
+ setup do
44
+ @new_file = File.new("#{@dir}/climenole.jpeg", 'rb')
45
+ @document.image = @new_file
46
+ @document.save!
47
+ @document = Doc.find(@document.id)
48
+ end
49
+
50
+ should "be the new file" do
51
+ assert_equal "climenole.jpeg", @document.image.file_name
52
+ @new_file.rewind
53
+ assert_equal @new_file.read.length, @document.image.file.read.length
54
+ end
55
+
56
+ end
57
+
58
+ context "when save is called without a new file" do
59
+
60
+ setup do
61
+ @document = Doc.find(@document.id)
62
+ @document.save!
63
+ end
64
+
65
+ should "be the original image" do
66
+ assert @document.image.exists?
67
+ assert_equal "baboon.jpg", @document.image.file_name
68
+ @image.rewind
69
+ assert_equal @image.read.length, @document.image.file.read.length
70
+ end
71
+
72
+ end
35
73
 
36
74
  end
37
75
 
76
+ context "when assigned nil" do
77
+
78
+ setup do
79
+ @document.image = nil
80
+ @document.save!
81
+ end
82
+
83
+ should "not exist" do
84
+ assert !@document.image.exists?
85
+ end
86
+
87
+ end
88
+
89
+ context "when assigned blank" do
90
+
91
+ setup do
92
+ @document.image = ""
93
+ @document.save!
94
+ end
95
+
96
+ should "not exist" do
97
+ assert !@document.image.exists?
98
+ end
99
+
100
+ end
101
+
38
102
  context "with styles" do
39
103
 
40
104
  setup do
41
105
  @document = DocWithStyles.new
42
106
  end
43
107
 
44
- context "when assigned a file" do
108
+ context "when assigned an image" do
45
109
 
46
110
  setup do
47
111
  @document.image = @image
@@ -50,23 +114,171 @@ class HasAttachmentTest < Test::Unit::TestCase
50
114
 
51
115
  should "have a styles" do
52
116
  assert_kind_of Hash, @document.image.styles
53
- end
117
+ end
118
+
119
+ should "have a method for each style" do
120
+ assert @document.image.respond_to? :cropped_wide
121
+ end
122
+
123
+ should "be a kind of attachment" do
124
+ assert_kind_of Griddle::Attachment, @document.image.cropped_wide
125
+ end
126
+
127
+ should "style should have a grid_key for cropped" do
128
+ assert_equal "doc_with_styles/#{@document.id}/image/cropped_wide/baboon.jpg", @document.image.cropped_wide.grid_key
129
+ end
130
+
131
+ should "style should have a file for cropped" do
132
+ assert @document.image.cropped_wide.exists?
133
+ assert !@document.image.cropped_wide.file.read.blank?
134
+ end
135
+
136
+ should "delete an image and its styles" do
137
+ grid_keys = @document.image.styles.inject([]) do |a,style|
138
+ a << @document.image.send(style[0]).grid_key
139
+ a
140
+ end
141
+
142
+ grid_keys << @document.image.grid_key
143
+
144
+ @document.image.destroy
145
+
146
+ grid_keys.each do |grid_key|
147
+ assert Griddle.database['fs.files'].find({'filename' => grid_key}).count == 0
148
+ end
149
+ end
150
+
151
+ end
152
+
153
+ image_varations = {
154
+ "wider than taller" => {
155
+ :file_name => "baboon.jpg",
156
+ :expected_dimensions => {
157
+ :resized => "150 x 100",
158
+ :fitted => "150 x 114",
159
+ :cropped_wide => '60 x 50',
160
+ :cropped_tall => '50 x 60',
161
+ :cropped_square => '50 x 50'
162
+ }
163
+ },
164
+ "taller than wider" => {
165
+ :file_name =>"climenole.jpeg",
166
+ :expected_dimensions => {
167
+ :resized => "150 x 100",
168
+ :fitted => "94 x 150",
169
+ :cropped_wide => '60 x 50',
170
+ :cropped_tall => '50 x 60',
171
+ :cropped_square => '50 x 50'
172
+ }
173
+ },
174
+ "square" => {
175
+ :file_name => "squid.png",
176
+ :expected_dimensions => {
177
+ :resized => "150 x 100",
178
+ :fitted => "150 x 150",
179
+ :cropped_wide => '60 x 50',
180
+ :cropped_tall => '50 x 60',
181
+ :cropped_square => '50 x 50'
182
+ }
183
+ },
184
+ }
185
+
186
+ image_varations.each do |variant|
187
+ description, variant = variant
188
+
189
+ context "when assigned an image that is #{description}" do
190
+
191
+ setup do
192
+ @document.image = File.new("#{@dir}/#{variant[:file_name]}", 'rb')
193
+ @document.save!
194
+ end
195
+
196
+ variant[:expected_dimensions].each do |style|
197
+ style_name, dimensions = style
198
+
199
+ should "have the correct dimensions for #{style_name}" do
200
+ temp = Tempfile.new "#{style[0]}.jpg"
201
+ style_attachment = @document.image.send(style_name)
202
+
203
+ file_path = File.dirname(temp.path) + '/' + style_attachment.file_name
204
+ File.open(file_path, 'w') do |f|
205
+ f.write style_attachment.file.read
206
+ end
207
+ cmd = %Q[identify -format "%[fx:w] x %[fx:h]" #{file_path}]
208
+ assert_equal dimensions, `#{cmd}`.chomp
209
+ end
210
+
211
+ end
212
+
213
+ end
54
214
 
55
215
  end
56
216
 
57
217
  end
58
-
59
- context "when multiple instances" do
218
+
219
+ context "with a invalid style name" do
220
+
60
221
  setup do
61
- @document2 = Doc.new
62
- @image2 = File.open("#{@dir}/fox.jpg", 'r')
63
- @document3 = Doc.new
64
- @image3 = File.open("#{@dir}/baboon.jpg", 'r')
65
- @document2.image = @image2
66
- @document3.image = @image3
222
+ @document = DocWithInvalidStyles.new
223
+ end
224
+
225
+ should "raise an error indicating an invalid style name" do
226
+ assert_raise RuntimeError do
227
+ @document.image = @image
228
+ end
67
229
  end
230
+
68
231
  end
69
-
232
+
233
+ end
234
+
235
+ context "A Document with multiple attachments" do
236
+
237
+ setup do
238
+ @document = DocWithMultipleAttachments.new
239
+ @dir = File.dirname(__FILE__) + '/fixtures'
240
+ @document.image = File.open("#{@dir}/baboon.jpg", 'r')
241
+ @document.pdf = File.open("#{@dir}/sample.pdf", 'r')
242
+ @document.save
243
+
244
+ @grid_keys = [@document.image.grid_key, @document.pdf.grid_key]
245
+ end
246
+
247
+ should "destroy_attached_files" do
248
+ @document.destroy_attached_files
249
+ @grid_keys.each do |grid_key|
250
+ assert Griddle.database['fs.files'].find({'filename' => grid_key}).count == 0
251
+ end
252
+ end
253
+
254
+ should "destroy on after_destroy" do
255
+ @document.destroy
256
+ @grid_keys.each do |grid_key|
257
+ assert Griddle.database['fs.files'].find({'filename' => grid_key}).count == 0
258
+ end
259
+ end
260
+
261
+ end
262
+
263
+ context "A Document with no object model" do
264
+
265
+ setup do
266
+ @document = Document.new
267
+ @dir = File.dirname(__FILE__) + '/fixtures'
268
+ @image = File.open("#{@dir}/baboon.jpg", 'r')
269
+ end
270
+
271
+ should "have a method for save_attached_files" do
272
+ assert @document.respond_to? :save_attached_files
273
+ end
274
+
275
+ should "save_attached_files" do
276
+ @document.save_attached_files
277
+ attachment = Griddle::Attachment.for(:image, @document)
278
+ attachment.attributes.delete(:_id)
279
+ assert_equal @document.image.attributes, attachment.attributes
280
+ end
281
+
70
282
  end
71
283
 
72
284
  end
data/test/models.rb CHANGED
@@ -1,19 +1,16 @@
1
- class Doc
2
- include MongoMapper::Document
3
- include Griddle::HasGridAttachment
4
- has_grid_attachment :image
5
- end
6
-
7
- class DocNoAttachment
8
- include MongoMapper::Document
9
- end
10
-
11
- class DocWithStyles
12
- include MongoMapper::Document
1
+ class Document
13
2
  include Griddle::HasGridAttachment
14
3
 
15
4
  has_grid_attachment :image, :styles => {
16
- :thumb => '50x50#'
5
+ :resized => "150x100!",
6
+ :fitted => "150x150",
7
+ :cropped_wide => '60x50#',
8
+ :cropped_tall => '50x60#',
9
+ :cropped_square => '50x50#'
17
10
  }
18
11
 
12
+ def id
13
+ object_id
14
+ end
15
+
19
16
  end
@@ -0,0 +1,46 @@
1
+ MongoMapper.database = Griddle.database.name
2
+
3
+ class Doc
4
+ include MongoMapper::Document
5
+ include Griddle::HasGridAttachment
6
+ has_grid_attachment :image
7
+ end
8
+
9
+ class DocNoAttachment
10
+ include MongoMapper::Document
11
+ end
12
+
13
+ class DocWithStyles
14
+ include MongoMapper::Document
15
+ include Griddle::HasGridAttachment
16
+
17
+ has_grid_attachment :image, :styles => {
18
+ :resized => "150x100!",
19
+ :fitted => "150x150",
20
+ :cropped_wide => '60x50#',
21
+ :cropped_tall => '50x60#',
22
+ :cropped_square => '50x50#'
23
+ }
24
+
25
+ end
26
+
27
+ # invalid because one of the styles is a reserved word
28
+ class DocWithInvalidStyles
29
+ include MongoMapper::Document
30
+ include Griddle::HasGridAttachment
31
+
32
+ has_grid_attachment :image, :styles => {
33
+ :file => "50x50#",
34
+ :thumb => '50x50#'
35
+ }
36
+
37
+ end
38
+
39
+ class DocWithMultipleAttachments
40
+ include MongoMapper::Document
41
+ include Griddle::HasGridAttachment
42
+
43
+ has_grid_attachment :image
44
+ has_grid_attachment :pdf
45
+
46
+ end
@@ -0,0 +1,56 @@
1
+ require "test_helper"
2
+ require "models"
3
+
4
+ class ProcessorTest < Test::Unit::TestCase
5
+
6
+ context "A Processor" do
7
+
8
+ setup do
9
+ @processor = Griddle::Processor.new
10
+ end
11
+
12
+ should "be a type of processor" do
13
+ assert_kind_of Griddle::Processor, @processor
14
+ end
15
+
16
+ end
17
+
18
+ context "An Attachment" do
19
+
20
+ setup do
21
+ @dir = File.dirname(__FILE__) + '/fixtures'
22
+ @image = File.open("#{@dir}/baboon.jpg", 'r')
23
+ @options = {
24
+ :processor => :ImageMagick
25
+ }
26
+ @doc = DocNoAttachment.create
27
+
28
+ end
29
+
30
+ context "without processor options" do
31
+
32
+ setup do
33
+ @attachment = Griddle::Attachment.for(:image, @doc)
34
+ end
35
+
36
+ should "have default to processor ImageMagick" do
37
+ assert_equal Griddle::Processor::ImageMagick, @attachment.processor.klass
38
+ end
39
+
40
+ end
41
+
42
+ context "and a file" do
43
+
44
+ setup do
45
+ @attachment = Griddle::Attachment.for(:image, @doc, @options)
46
+ end
47
+
48
+ should "have a processor of ImageMagick" do
49
+ assert_equal Griddle::Processor::ImageMagick, @attachment.processor.klass
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+
56
+ end
data/test/style_test.rb CHANGED
@@ -44,6 +44,14 @@ class StyleTest < Test::Unit::TestCase
44
44
  assert_equal @options[:styles][:thumb], @attachment.styles[:thumb].geometry
45
45
  end
46
46
 
47
+ should "save styles with the attachment" do
48
+ @attachment.save
49
+ options = {:name => @attachment.name, :owner_id => @attachment.owner_id, :owner_type => @attachment.owner_type }
50
+ record = Griddle::Attachment.collection.find_one(options)
51
+ attachment = Griddle::Attachment.new record
52
+ assert_equal @attachment.styles[:thumb][:geometry], attachment.styles[:thumb][:geometry]
53
+ end
54
+
47
55
  end
48
56
 
49
57
  end
data/test/test_helper.rb CHANGED
@@ -2,17 +2,22 @@ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
2
 
3
3
  require 'rubygems'
4
4
  require 'mongo_mapper'
5
+ require 'mongo'
6
+ gem 'activesupport', '2.3.5'
7
+ require 'active_support'
5
8
  require 'griddle'
6
9
  require 'test/unit'
7
10
  require 'shoulda'
11
+ require 'tempfile'
12
+ require 'rack'
8
13
 
9
14
  TEST_DB = 'griddle-test' unless Object.const_defined?("TEST_DB")
10
15
 
11
- MongoMapper.database = TEST_DB
16
+ Griddle.database = Mongo::Connection.new.db(TEST_DB)
12
17
 
13
18
  class Test::Unit::TestCase
14
19
  def teardown
15
- MongoMapper.database.collections.each do |coll|
20
+ Griddle.database.collections.each do |coll|
16
21
  coll.remove
17
22
  end
18
23
  end
@@ -24,4 +29,7 @@ class Test::Unit::TestCase
24
29
  super
25
30
  end
26
31
  end
27
- end
32
+ end
33
+
34
+ require 'models'
35
+ require 'mongo_mapper_models'
metadata CHANGED
@@ -1,37 +1,52 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: griddle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ hash: 31
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 2
10
+ version: 0.1.2
5
11
  platform: ruby
6
12
  authors:
7
13
  - Matt Mongeau
14
+ - meanmarcus
8
15
  autorequire:
9
16
  bindir: bin
10
17
  cert_chain: []
11
18
 
12
- date: 2010-03-03 00:00:00 -05:00
19
+ date: 2010-05-30 00:00:00 -04:00
13
20
  default_executable:
14
21
  dependencies:
15
22
  - !ruby/object:Gem::Dependency
16
23
  name: shoulda
17
- type: :development
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
20
27
  requirements:
21
28
  - - ">="
22
29
  - !ruby/object:Gem::Version
30
+ hash: 3
31
+ segments:
32
+ - 0
23
33
  version: "0"
24
- version:
34
+ type: :development
35
+ version_requirements: *id001
25
36
  - !ruby/object:Gem::Dependency
26
37
  name: mongo_mapper
27
- type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
30
41
  requirements:
31
42
  - - ">="
32
43
  - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
33
47
  version: "0"
34
- version:
48
+ type: :development
49
+ version_requirements: *id002
35
50
  description: GridFS made simple...
36
51
  email: matt@toastyapps.com
37
52
  executables: []
@@ -40,27 +55,33 @@ extensions: []
40
55
 
41
56
  extra_rdoc_files:
42
57
  - LICENSE
43
- - README.rdoc
58
+ - README.markdown
44
59
  files:
45
60
  - .document
46
61
  - .gitignore
47
62
  - LICENSE
48
- - README.rdoc
63
+ - README.markdown
49
64
  - Rakefile
50
65
  - VERSION
51
66
  - griddle.gemspec
52
67
  - lib/griddle.rb
53
68
  - lib/griddle/attachment.rb
54
69
  - lib/griddle/has_grid_attachment.rb
70
+ - lib/griddle/processor.rb
71
+ - lib/griddle/processor/image_magick.rb
55
72
  - lib/griddle/style.rb
56
73
  - lib/griddle/upfile.rb
57
74
  - rails/init.rb
58
75
  - test/attachment_test.rb
59
76
  - test/fixtures/baboon.jpg
77
+ - test/fixtures/climenole.jpeg
60
78
  - test/fixtures/fox.jpg
61
79
  - test/fixtures/sample.pdf
80
+ - test/fixtures/squid.png
62
81
  - test/has_attachment_test.rb
63
82
  - test/models.rb
83
+ - test/mongo_mapper_models.rb
84
+ - test/processor_test.rb
64
85
  - test/style_test.rb
65
86
  - test/test_helper.rb
66
87
  - test/upfile_test.rb
@@ -74,21 +95,27 @@ rdoc_options:
74
95
  require_paths:
75
96
  - lib
76
97
  required_ruby_version: !ruby/object:Gem::Requirement
98
+ none: false
77
99
  requirements:
78
100
  - - ">="
79
101
  - !ruby/object:Gem::Version
102
+ hash: 3
103
+ segments:
104
+ - 0
80
105
  version: "0"
81
- version:
82
106
  required_rubygems_version: !ruby/object:Gem::Requirement
107
+ none: false
83
108
  requirements:
84
109
  - - ">="
85
110
  - !ruby/object:Gem::Version
111
+ hash: 3
112
+ segments:
113
+ - 0
86
114
  version: "0"
87
- version:
88
115
  requirements: []
89
116
 
90
117
  rubyforge_project:
91
- rubygems_version: 1.3.5
118
+ rubygems_version: 1.3.7
92
119
  signing_key:
93
120
  specification_version: 3
94
121
  summary: GridFS made simple.
@@ -96,6 +123,8 @@ test_files:
96
123
  - test/attachment_test.rb
97
124
  - test/has_attachment_test.rb
98
125
  - test/models.rb
126
+ - test/mongo_mapper_models.rb
127
+ - test/processor_test.rb
99
128
  - test/style_test.rb
100
129
  - test/test_helper.rb
101
130
  - test/upfile_test.rb
data/README.rdoc DELETED
@@ -1,17 +0,0 @@
1
- = griddle
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 bump version in a commit by itself I can ignore when I pull)
13
- * Send me a pull request. Bonus points for topic branches.
14
-
15
- == Copyright
16
-
17
- Copyright (c) 2010 Matt Matt san Mongeau. See LICENSE for details.