Module Compu::Acts::MbsFiles::ClassMethods
In: lib/acts_as_mbs_files.rb

The MbsFiles module allows you to easily handle multiple file uploads. You can designate one or more columns of your model‘s table as "file columns" like this:

  class SomeItem < ActiveRecord::Base
    acts_as_mbs_files :mbs_files, :files
    # or, for more than one column
    # acts_as_mbs_files :mbs_files, [:files,:more_files]
  end

In this example, the :mbs_files argument tells the plugin that the files are stored with the MbsFile class. The second argument, files, can be either a single symol, or an array of symbols, and tells the plugins which properties of the SomeItem class are used to store the GUID that correlates to the files represented in the MbsFile class.

Validations can then be individually included as needed. Alternatively, it is possible to automatically include the appropriate validations and the acts_as_mbs_files plugin by including the automatic_mbs_validation in both the main model, and the model that represents the files, like this:

 class SomeItem < ActiveRecord::Base
   automatic_mbs_validation MbsValidationSettings.new("files")
 end

 class MbsFile < ActiveRecord::Base
   file_column :file_name
   automatic_mbs_validation MbsValidationSettings.new("files")
 end

This makes it much easier to modify the validation settings, as they only have to be changed in one place. The MbsValidationSettings class itself must be generated and modified to suit your needs. It is essentially a static object, and can be generated using the command ruby script\generate multi_bit_shift MbsFile validation_settings. An example file is installed with that command, and can be customized.

The methods of this module are automatically included into ActiveRecord::Base as class methods, so that you can use them in your models.

Generated Methods

After calling "acts_as_mbs_files :mbs_files, :files" as in the example above, a number of instance methods will automatically be generated:

  • SomeItem#files: this will return an array of all MbsFiles associated with the current option.
  • SomeItem#files_guid: this will return the GUID associated with the column of this object. If none exists will create one and store it in the "files" column.

You can access the raw value of the "files" column (which will contain the guid, or nil if "files_guid" has never been called) via the ActiveRecord::Base#attributes or ActiveRecord::Base#[] methods like this:

  some_item['files']    # e.g."200f3a6f-8977-012a-5cc8-00044b05bd87"

This could potentially be useful in determining if the "files_guid" function has ever been called.

The guid can be modified with the standard SomeItem#files= method, although we don‘t provide a mechanism to do so, and we generally treat it as immutable.

Dependencies

The uuid gem must be installed to use this plugin. It can be installed using gem install uuid.

Notes

This documentation is based on the open source file_column documentation.

Methods

Constants

DEFAULT_VALIDATION_OPTIONS = { :on => :save, :allow_nil => false, :message => nil }.freeze   default options that will be used for the validation
ALL_RANGE_OPTIONS = [ :is, :within, :in, :minimum, :maximum ].freeze   possible options for the range of the validates_number_of_mbs_files_in validation

Public Instance methods

handle the symbols in the guid_fields array as "mbs_files" columns, addtional method as decribed above are generated. The first attribute, association_id is the model that is associated with the files. Example:

  class SomeItem < ActiveRecord::Base
    acts_as_mbs_files :mbs_files, :files
    # or, for more than one column
    # acts_as_mbs_files :mbs_files, [:files,:more_files]
  end

[Source]

     # File lib/acts_as_mbs_files.rb, line 99
 99:         def acts_as_mbs_files(association_id, guid_fields = [])
100:           if guid_fields.class.to_s != "Array"
101:             guid_fields = [guid_fields]
102:           end
103:           
104:           for field in guid_fields
105:             define_method "#{field.to_s}" do
106:               Object.const_get(association_id.to_s.camelize.singularize).find_all_by_associated_with(self.method("#{field.to_s}_guid").call)
107:             end
108:             define_method "#{field.to_s}_guid" do
109:               if read_attribute(field).blank?
110:                 write_attribute(field, UUID.new.to_s)
111:               end
112:               read_attribute(field)
113:             end
114:           end
115:         end

This method should be included in both the MBS Files as described above, and automatically includes the appropriate validations. This is mainly a convenience method, so that validation settings are editable from one file, that file being the validation_object.

[Source]

    # File lib/acts_as_mbs_files.rb, line 81
81:         def automatic_mbs_validation(validation_object, options = {})
82:           if self.class_name.to_s == validation_object.main_class
83:             acts_as_mbs_files(validation_object.file_class.tableize.intern, validation_object.main_column)
84:             validates_number_of_mbs_files_in(validation_object.main_column,options.merge({:in => validation_object.number_of_files_range}))
85:           elsif self.class_name.to_s == validation_object.file_class
86:             validates_total_maximum_filecount_of(validation_object.file_column, options.merge({:maximum => validation_object.maximum_files}))
87:             validates_total_filesize_of(validation_object.file_column, options.merge({:in => Range.new(0, validation_object.maximum_total_file_size)}))
88:           end
89:         end

Validates that the specified attribute has the number of uploaded files that match the length restrictions supplied. Only one option can be used at a time:

  class Person < ActiveRecord::Base
    # an exception will almost certainly been throws if acts_as_mbs_files isn't declared for the attribute that is being validated.
    acts_as_mbs_files :multibit_files, :files
    # Only ONE of these can be used at a time.
    validates_number_of_mbs_files_in :files, :maximum=>30
    validates_number_of_mbs_files_in :files, :maximum=>30, :message=>"upload less than %d if you don't mind"
    validates_number_of_mbs_files_in :files, :in => 7..32, :allow_nil => true
    validates_number_of_mbs_files_in :files, :within => 6..20, :too_long => "upload fewer files", :too_short => "upload more files"
    validates_number_of_mbs_files_in :files, :minimum=>1, :too_short=>"please upload at least %d file"
    validates_number_of_mbs_files_in :files, :is=>4, :message=>"%d files must be uploaded."
  end

Configuration options:

  • minimum - The minimum size of the attribute
  • maximum - The maximum size of the attribute
  • is - The exact size of the attribute
  • within - A range specifying the minimum and maximum size of the attribute
  • in - A synonym(or alias) for :within
  • allow_nil - Attribute may be nil; skip validation.
  • too_long - The error message if the attribute goes over the maximum (default is: "has too many files (maximum is %d files)")
  • too_short - The error message if the attribute goes under the minimum (default is: "has too few files (minimum is %d files)")
  • wrong_length - The error message if using the :is method and the attribute is the wrong size (default is: "has the wrong number of files (should be %d files)")
  • message - The error message to use for a :minimum, :maximum, or :is violation. An alias of the appropriate too_long/too_short/wrong_length message
  • on - Specifies when this validation is active (default is :save, other options :create, :update)
  • if - Specifies a method, proc or string to call to determine if the validation should occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The method, proc or string should return or evaluate to a true or false value.

Note: This function and the supporting documentation is derived in nearly its entirety from the validates_length_of function built into rails.

[Source]

     # File lib/acts_as_mbs_files.rb, line 151
151:         def validates_number_of_mbs_files_in(*attrs)
152:           # Merge given options with defaults.
153:           options = {
154:             :too_long     => "has too many files (maximum is %d files)",
155:             :too_short    => "has too few files (minimum is %d files)",
156:             :wrong_length => "has the wrong number of files (should be %d files)"
157:           }.merge(DEFAULT_VALIDATION_OPTIONS)
158:           options.update(attrs.pop.symbolize_keys) if attrs.last.is_a?(Hash)
159:   
160:           # Ensure that one and only one range option is specified.
161:           range_options = ALL_RANGE_OPTIONS & options.keys
162:           case range_options.size
163:             when 0
164:               raise ArgumentError, 'Range unspecified.  Specify the :within, :maximum, :minimum, or :is option.'
165:             when 1
166:               # Valid number of options; do nothing.
167:             else
168:               raise ArgumentError, 'Too many range options specified.  Choose only one.'
169:           end
170:   
171:           # Get range option and value.
172:           option = range_options.first
173:           option_value = options[range_options.first]
174:   
175:           case option
176:             when :within, :in
177:               raise ArgumentError, ":#{option} must be a Range" unless option_value.is_a?(Range)
178:   
179:               too_short = options[:too_short] % option_value.begin
180:               too_long  = options[:too_long]  % option_value.end
181:   
182:               validates_each(attrs, options) do |record, attr, value|
183:                 # Get number of files
184:                 length = record.method("#{attr.to_s}").call.length
185:                 if value.nil? or length < option_value.begin
186:                   record.errors.add(attr, too_short)
187:                 elsif length > option_value.end
188:                   record.errors.add(attr, too_long)
189:                 end
190:               end
191:             when :is, :minimum, :maximum
192:               raise ArgumentError, ":#{option} must be a nonnegative Integer" unless option_value.is_a?(Integer) and option_value >= 0
193:   
194:               # Declare different validations per option.
195:               validity_checks = { :is => "==", :minimum => ">=", :maximum => "<=" }
196:               message_options = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }
197:   
198:               message = (options[:message] || options[message_options[option]]) % option_value
199:   
200:               validates_each(attrs, options) do |record, attr, value|
201:                 # Get number of files
202:                 length = record.method("#{attr.to_s}").call.length
203:                 record.errors.add(attr, message) unless !value.nil? and length.method(validity_checks[option])[option_value]
204:               end
205:           end
206:         end

This validates the total file size of all the files sharing the same associated_with value in one or more columns. It is assumed that the columns that are passed in contain the path to the file. A list of columns should be given followed by an options hash. This validation is designed to be used in the model that represents the uploaded files.

Required options:

  • :in => A size range. Note that you can use ActiveSupport‘s numeric extensions for kilobytes, etc.

Examples:

   validates_total_filesize_of :field, :in => 0..100.megabytes
   validates_total_filesize_of :field, :in => 15.kilobytes..1.megabyte

Note: This function and the supporting documentation is derived in nearly its entirety from the validates_filesize_of function from the open source FileColumn plugin.

[Source]

     # File lib/acts_as_mbs_files.rb, line 224
224:         def validates_total_filesize_of(*attrs)  
225:         
226:           options = attrs.pop if attrs.last.is_a?Hash
227:           raise ArgumentError, "Please include the :in option." if !options || !options[:in]
228:           raise ArgumentError, "Invalid value for option :in" unless options[:in].is_a?Range
229:         
230:           validates_each(attrs, options) do |record, attr, value|
231:             unless value.blank?
232:               size = File.size(value)
233:               for file in record.class.find_all_by_associated_with(record.associated_with)
234:                 size = size + File.size(file.method(attr).call) unless record == file
235:               end
236:               record.errors.add attr, "is smaller than the allowed size range." if size < options[:in].first
237:               record.errors.add attr, "is larger than the allowed size range." if size > options[:in].last
238:             end
239:           end
240:         
241:         end

Validates that the specified attribute has the number of uploaded files that match the length restrictions supplied. THIS PLUGIN IS DESIGNED TO BE USED IN THE MODEL THAT REPRESENTS THE UPLOADED FILE. It is assumed that the columns that are passed in contain the path to the file. Only one option can be used at a time:

  class MbsFile < ActiveRecord::Base
    # this method should be used in the model representing the uploaded file.
    validates_total_filecount_of :files, :maximum=>30
    validates_total_filecount_of :files, :maximum=>30, :message=>"upload less than %d if you don't mind"
  end

Configuration options:

  • maximum - The maximum size of the attribute
  • allow_nil - Attribute may be nil; skip validation.
  • too_long - The error message if the attribute goes over the maximum (default is: "has too many files (maximum is %d files)")
  • message - The error message to use for a :minimum, :maximum, or :is violation. An alias of the appropriate too_long/too_short/wrong_length message
  • on - Specifies when this validation is active (default is :save, other options :create, :update)
  • if - Specifies a method, proc or string to call to determine if the validation should occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The method, proc or string should return or evaluate to a true or false value.

Note: This function and the supporting documentation is derived in nearly its entirety from the validates_length_of function built into rails.

[Source]

     # File lib/acts_as_mbs_files.rb, line 266
266:         def validates_total_maximum_filecount_of(*attrs)
267:           # Merge given options with defaults.
268:           options = {
269:             :too_long     => "has too many files (maximum is %d files)",
270:           }.merge(DEFAULT_VALIDATION_OPTIONS)
271:           options.update(attrs.pop.symbolize_keys) if attrs.last.is_a?(Hash)
272:   
273:           # Ensure that one and only one range option is specified.
274:           range_options = [:maximum] & options.keys
275:           case range_options.size
276:             when 0
277:               raise ArgumentError, 'Range unspecified.  Specify the :maximum option.'
278:             when 1
279:               # Valid number of options; do nothing.
280:             else
281:               raise ArgumentError, 'Too many range options specified.  Choose only one.'
282:           end
283:   
284:           # Get range option and value.
285:           option = range_options.first
286:           option_value = options[range_options.first]
287:   
288:           case option
289:             when :maximum
290:               raise ArgumentError, ":#{option} must be a nonnegative Integer" unless option_value.is_a?(Integer) and option_value >= 0
291:   
292:               # Declare different validations per option.
293:               validity_checks = { :is => "==", :minimum => ">=", :maximum => "<=" }
294:               message_options = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }
295:   
296:               message = (options[:message] || options[message_options[option]]) % option_value
297:   
298:               validates_each(attrs, options) do |record, attr, value|
299:                 # Get number of files
300:                 length = 1
301:                 for file in record.class.find_all_by_associated_with(record.associated_with)
302:                   length = length + 1 unless record == file
303:                 end
304:                 record.errors.add(attr, message) unless !value.nil? and length.method(validity_checks[option])[option_value]
305:               end
306:           end
307:         end

[Validate]