Module: Fleximage::Model::ClassMethods

Defined in:
lib/fleximage/model.rb

Overview

Provides class methods for Fleximage for use in model classes. The only class method is acts_as_fleximage which integrates Fleximage functionality into a model class.

The following class level accessors also get inserted.

  • image_directory: (String, no default) Where the master images are stored, directory path relative to your app root.

  • s3_bucket: Name of the bucket on Amazon S3 where your master images are stored. To use this you must call establish_connection! on the aws/s3 gem form your app’s initilization to authenticate with your S3 account.

  • use_creation_date_based_directories: (Boolean, default true) If true, master images will be stored in directories based on creation date. For example: "#{image_directory}/2007/11/24/123.png" for an image with an id of 123 and a creation date of November 24, 2007. Turing this off would cause the path to be “#image_directory/123.png” instead. This helps keep the OS from having directories that are too full.

  • image_storage_format: (:png or :jpg, default :png) The format of your master images. Using :png will give you the best quality, since the master images as stored as lossless version of the original upload. :jpg will apply lossy compression, but the master image file sizes will be much smaller. If storage space is a concern, us :jpg.

  • require_image: (Boolean, default true) The model will raise a validation error if no image is uploaded with the record. Setting to false allows record to be saved with no images.

  • missing_image_message: (String, default “is required”) Validation message to display when no image was uploaded for a record.

  • invalid_image_message: (String default “was not a readable image”) Validation message when an image is uploaded, but is not an image format that can be read by RMagick.

  • output_image_jpg_quality: (Integer, default 85) When rendering JPGs, this represents the amount of compression. Valid values are 0-100, where 0 is very small and very ugly, and 100 is near lossless but very large in filesize.

  • default_image_path: (String, nil default) If no image is present for this record, the image at this path will be used instead. Useful for a placeholder graphic for new content that may not have an image just yet.

  • default_image: A hash which defines an empty starting image. This hash look like: :size => '123x456', :color => :transparent, where :size defines the dimensions of the default image, and :color defines the fill. :color can be a named color as a string (‘red’), :transparent, or a Magick::Pixel object.

  • preprocess_image: (Block, no default) Call this class method just like you would call operate in a view. The image transoformation in the provided block will be run on every uploaded image before its saved as the master image.

Example:

class Photo < ActiveRecord::Base
  acts_as_fleximage do 
    image_directory 'public/images/uploaded'
    use_creation_date_based_directories true
    image_storage_format      :png
    require_image             true
    missing_image_message     'is required'
    invalid_image_message     'was not a readable image'
    default_image_path        'public/images/no_photo_yet.png'
    default_image             nil
    output_image_jpg_quality  85

    preprocess_image do |image|
      image.resize '1024x768'
    end
  end

  # normal model methods...
end

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.db_store?Boolean

Internal method to ask this class if it stores image in the DB.

Returns:

  • (Boolean)


91
92
93
94
95
96
97
98
99
100
# File 'lib/fleximage/model.rb', line 91

def self.db_store?
  return false if s3_store?
  if respond_to?(:columns)
    columns.find do |col|
      col.name == 'image_file_data'
    end
  else
    false
  end
end

.file_store?Boolean

Returns:

  • (Boolean)


106
107
108
# File 'lib/fleximage/model.rb', line 106

def self.file_store?
  !db_store? && !s3_store?
end

.has_store?Boolean

Returns:

  • (Boolean)


110
111
112
# File 'lib/fleximage/model.rb', line 110

def self.has_store?
  respond_to?(:columns) && (db_store? || image_directory)
end

.image_too_small_message(str = nil) ⇒ Object

Image too small message Should include {minimum}



179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/fleximage/model.rb', line 179

def self.image_too_small_message(str = nil)
  fb = "is too small (Minimum: {{minimum}})"
  if str.nil?
    minimum_size = Fleximage::Operator::Base.size_to_xy(validates_image_size).join('x')
    if @image_too_small_message
      @image_too_small_message.gsub("{{minimum}}", minimum_size)
    else
      translate_error_message("image_too_small", fb.gsub("{{minimum}}", minimum_size), :minimum => minimum_size)
    end
  else
    @image_too_small_message = str
  end
end

.invalid_image_message(str = nil) ⇒ Object

Invalid image message dsl_accessor :invalid_image_message, :default => ‘was not a readable image’



165
166
167
168
169
170
171
172
173
174
175
# File 'lib/fleximage/model.rb', line 165

def self.invalid_image_message(str = nil)
  if str.nil?
    if @invalid_image_message
      @invalid_image_message
    else
      translate_error_message("invalid_image", "was not a readable image")
    end
  else
    @invalid_image_message = str
  end
end

.missing_image_message(str = nil) ⇒ Object

Missing image message dsl_accessor :missing_image_message, :default => ‘is required’



149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/fleximage/model.rb', line 149

def self.missing_image_message(str = nil)
  if str.nil?
    if @missing_image_message
      @missing_image_message
    else
      translate_error_message("missing_image", "is required")
    end
    
  else
    @missing_image_message = str
  end
end

.preprocess_image(&block) ⇒ Object

Call this class method just like you would call operate in a view. The image transoformation in the provided block will be run on every uploaded image before its saved as the master image.



86
87
88
# File 'lib/fleximage/model.rb', line 86

def self.preprocess_image(&block)
  preprocess_image_operation(block)
end

.s3_store?Boolean

Returns:

  • (Boolean)


102
103
104
# File 'lib/fleximage/model.rb', line 102

def self.s3_store?
  !!s3_bucket
end

.translate_error_message(name, fallback, options = {}) ⇒ Object



140
141
142
143
144
145
# File 'lib/fleximage/model.rb', line 140

def self.translate_error_message(name, fallback, options = {})
  translation = I18n.translate "activerecord.errors.models.#{self.model_name.underscore}.#{name}", options
  if translation.match /translation missing:/
    I18n.translate "activerecord.errors.messages.#{name}", options.merge({ :default => fallback })
  end
end

Instance Method Details

#acts_as_fleximage(options = {}) ⇒ Object

Use this method to include Fleximage functionality in your model. It takes an options hash with a single required key, :image_directory. This key should point to the directory you want your images stored on your server. Or configure with a nice looking block.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/fleximage/model.rb', line 78

def acts_as_fleximage(options = {})
  
  # Include the necesary instance methods
  include Fleximage::Model::InstanceMethods
  
  # Call this class method just like you would call +operate+ in a view.
  # The image transoformation in the provided block will be run on every uploaded image before its saved as the 
  # master image.
  def self.preprocess_image(&block)
    preprocess_image_operation(block)
  end
  
  # Internal method to ask this class if it stores image in the DB.
  def self.db_store?
    return false if s3_store?
    if respond_to?(:columns)
      columns.find do |col|
        col.name == 'image_file_data'
      end
    else
      false
    end
  end
  
  def self.s3_store?
    !!s3_bucket
  end
  
  def self.file_store?
    !db_store? && !s3_store?
  end
  
  def self.has_store?
    respond_to?(:columns) && (db_store? || image_directory)
  end
  
  # validation callback
  validate :validate_image if respond_to?(:validate)
  
  # The filename of the temp image.  Used for storing of good images when validation fails
  # and the form needs to be redisplayed.
  attr_reader :image_file_temp
  
  # Setter for jpg compression quality at the instance level
  attr_accessor :jpg_compression_quality
  
  # Where images get stored
  dsl_accessor :image_directory
  
  # Amazon S3 bucket where the master images are stored
  dsl_accessor :s3_bucket
  
  # Put uploads from different days into different subdirectories
  dsl_accessor :use_creation_date_based_directories, :default => true
  
  # The format are master images are stored in
  dsl_accessor :image_storage_format, :default => Proc.new { :png }
  
  # Require a valid image.  Defaults to true.  Set to false if its ok to have no image for
  dsl_accessor :require_image, :default => true
  
  
  def self.translate_error_message(name, fallback, options = {})
    translation = I18n.translate "activerecord.errors.models.#{self.model_name.underscore}.#{name}", options
    if translation.match /translation missing:/
      I18n.translate "activerecord.errors.messages.#{name}", options.merge({ :default => fallback })
    end
  end
  
  # Missing image message
  #dsl_accessor :missing_image_message, :default => 'is required'
  def self.missing_image_message(str = nil)
    if str.nil?
      if @missing_image_message
        @missing_image_message
      else
        translate_error_message("missing_image", "is required")
      end
      
    else
      @missing_image_message = str
    end
  end
  
  
  # Invalid image message
  #dsl_accessor :invalid_image_message, :default => 'was not a readable image'
  def self.invalid_image_message(str = nil)
    if str.nil?
      if @invalid_image_message
        @invalid_image_message
      else
        translate_error_message("invalid_image", "was not a readable image")
      end
    else
      @invalid_image_message = str
    end
  end
  
  # Image too small message
  # Should include {{minimum}}
  def self.image_too_small_message(str = nil)
    fb = "is too small (Minimum: {{minimum}})"
    if str.nil?
      minimum_size = Fleximage::Operator::Base.size_to_xy(validates_image_size).join('x')
      if @image_too_small_message
        @image_too_small_message.gsub("{{minimum}}", minimum_size)
      else
        translate_error_message("image_too_small", fb.gsub("{{minimum}}", minimum_size), :minimum => minimum_size)
      end
    else
      @image_too_small_message = str
    end
  end
  
  # Sets the quality of rendered JPGs
  dsl_accessor :output_image_jpg_quality, :default => 85
  
  # Set a default image to use when no image has been assigned to this record
  dsl_accessor :default_image_path
  
  # Set a default image based on a a size and fill
  dsl_accessor :default_image
  
  # A block that processes an image before it gets saved as the master image of a record.
  # Can be helpful to resize potentially huge images to something more manageable. Set via
  # the "preprocess_image { |image| ... }" class method.
  dsl_accessor :preprocess_image_operation
  
  # Set a minimum size ([x, y] e.g. 200, '800x600', [800, 600])
  # Set '0x600' to just enforce y size or
  # '800x0' to just validate x size.
  dsl_accessor :validates_image_size
  
  # Image related save and destroy callbacks
  if respond_to?(:before_save)
    after_destroy :delete_image_file
    before_save   :pre_save
    after_save    :post_save
  end
  
  # execute configuration block
  yield if block_given?
  
  # Create S3 bucket if it's not present
  if s3_bucket
    begin
      AWS::S3::Bucket.find(s3_bucket)
    rescue AWS::S3::NoSuchBucket
      AWS::S3::Bucket.create(s3_bucket)
    end
  end
  
  # set the image directory from passed options
  image_directory options[:image_directory] if options[:image_directory]
  
  # Require the declaration of a master image storage directory
  if respond_to?(:validate) && !image_directory && !db_store? && !s3_store? && !default_image && !default_image_path
    raise "No place to put images!  Declare this via the :image_directory => 'path/to/directory' option\n"+
          "Or add a database column named image_file_data for DB storage\n"+
          "Or set :virtual to true if this class has no image store at all\n"+
          "Or set a default image to show with :default_image or :default_image_path"
  end
end

#image_file_exists(file) ⇒ Object



243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/fleximage/model.rb', line 243

def image_file_exists(file)
  # File must be a valid object
  return false if file.nil?
  
  # Get the size of the file.  file.size works for form-uploaded images, file.stat.size works
  # for file object created by File.open('foo.jpg', 'rb').  It must have a size > 0.
  return false if (file.respond_to?(:size) ? file.size : file.stat.size) <= 0
  
  # object must respond to the read method to fetch its contents.
  return false if !file.respond_to?(:read)
  
  # file validation passed, return true
  true
end