griddle 0.0.0 → 0.1.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.
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.