hi All,
I''ve been trying to wrestle dynamic code generation into place for a
week now with no success. What I want is a module or class (started
trying to use module but finished up trying to use a class) which can
inject a method into its consumer which will allow:
user.image = #Ruby:File
as you would get from a file upload. (This eliminates any need for
special image handling code in the view or controller and lets the image
field act like any other). It uses the RMagick library to convert all
images to jpg''s and resize them to a consumer-defined size. What I was
hoping the group could help with was moving all the code out of the
model and into my ImageBlob class (which will inject necessary code into
the model (i.e., consumer)). Here''s what I currently have (which
works):
model.rb
----------
def image=( file )
write_attribute( ''image'', ImageBlob.getBlob( file, 225 ))
end
def thumbnailImage=( file )
write_attribute( ''thumbnailImage'', ImageBlob.assignAttrib(
file, 125
))
end
ImageBlob.rb
------------
require ''RMagick''
include Magick
class ImageBlob
def ImageBlob.getBlob( file, maxImageSize )
#omitted code which resizes and reformats image and returns blob
end
end
I was hoping to have an initialize function in ImageBlob which would
take the consumer class and inject the necessary methods into it (image=
and thumbnailImage=). Here''s my best attempt at that (which fails for
an unknown reason):
def initialize( attributesAndSizesHash, consumerClass )
attributesAndSizesHash.each_pair {|attributeName, maxImageSize|
code = "class #{consumerClass}
def #{attributeName}=( file )
write_attribute( ''#{attributeName}'',
ImageBlob.getBlob(
file, #{maxImageSize} ))
end
end"
eval( code ) }
end
I also tried making code define a module and calling
consumerClass.extend( moduleName ). Neither of these approaches are
working.
Of course, if this implemention could work, then consumers of the
ImageBlob class could simply call:
ImageBlob.new( { "image" => 225, "thumbnailImage" =>
125 }, self )
anywhere in their class definition to have the functionality added (with
any number of image fields each with their own size).
Can anyone out there provide the last piece of this puzzle? If it is
provided, I will post the entire source for this module and it should
prove to be useful.
Regards,
Jonathan
--
Posted via http://www.ruby-forum.com/.
Oops. The thumbnailImage= method should have been defined as this: def thumbnailImage=( file ) write_attribute( ''thumbnailImage'', ImageBlob.getBlob( file, 125 )) end -- Posted via http://www.ruby-forum.com/.
Anyone have an idea? A simple example of how to inject code into a generic class would probably suffice. Can you possibly do a mixin dynamically and, if so, how? Much thanks. -- Posted via http://www.ruby-forum.com/.
Francois Beausoleil
2005-Dec-04 04:25 UTC
Re: Re: injecting image= method into model classes
Hi ! 2005/12/3, Jonathan <zjll9@imail.etsu.edu> <zjll9@imail.etsu. <zjll9@imail.etsu.edu>:> Anyone have an idea? A simple example of how to inject code into a > generic class would probably suffice. Can you possibly do a mixin > dynamically and, if so, how?Have you looked at FileColumn ? It does what you want, except it uses the file system. If you were to create a way to store in the DB instead, I'm sure Sebastian would include that in a future release. See http://www.kanthak.net/opensource/file_column/ Bye ! -- François Beausoleil http://blog.teksol.info/ _______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails
Sebastian Kanthak
2005-Dec-04 13:02 UTC
Re: Re: injecting image= method into model classes
On 12/4/05, Jonathan <zjll9-qOlZ2LGq26weF4Ui59n7hQ@public.gmane.org> <zjll9-qOlZ2LGq26wYnX86y03XkA@public.gmane.org <zjll9-qOlZ2LGq26weF4Ui59n7hQ@public.gmane.org> wrote:> Anyone have an idea? A simple example of how to inject code into a > generic class would probably suffice. Can you possibly do a mixin > dynamically and, if so, how?well, I don''t have to plug my file_column plugin anymore since Francois was already so nice to do so :) However, you might want to take a look at the plugin''s source code as it does exactly this: adding methods to a model dynamically. Instead of creating strings and evaluating them, I would suggest you take a look at Module#define_method that let''s you dynamically define methods using ruby''s nice block syntax. A small example from file_column, that defines the "image=" method. (where the name of the attribute is stored in the local variable attr). define_method "#{attr}=" do |file| state = send(state_method).assign(file) instance_variable_set state_attr, state if state.options[:after_upload] and state.just_uploaded? state.options[:after_upload].each do |sym| self.send sym end end end "state_method" is a Symbol that contains the name of another generated method that will return an object that does all the heavy lifting for file uploads/image manipulation. As a last note I wanted to give the reason why I decided to store files in the filesystem and not in the database: Simply put, rails'' support for large binary columns is very poor and inefficient (somebody correct me please if that has changed since I last checked) and since the filesystem is very good at storing files I decided to leverage this. Sebastian
skanthak wrote:> On 12/4/05, Jonathan <zjll9-qOlZ2LGq26weF4Ui59n7hQ@public.gmane.org> <zjll9-qOlZ2LGq26wYnX86y03XkA@public.gmane.org > <zjll9-qOlZ2LGq26weF4Ui59n7hQ@public.gmane.org> wrote: >> Anyone have an idea? A simple example of how to inject code into a >> generic class would probably suffice. Can you possibly do a mixin >> dynamically and, if so, how? > > well, I don''t have to plug my file_column plugin anymore since > Francois was already so nice to do so :) However, you might want to > take a look at the plugin''s source code as it does exactly this: > adding methods to a model dynamically. > > Instead of creating strings and evaluating them, I would suggest you > take a look at Module#define_method that let''s you dynamically define > methods using ruby''s nice block syntax. A small example from > file_column, that defines the "image=" method. (where the name of the > attribute is stored in the local variable attr). > > define_method "#{attr}=" do |file| > state = send(state_method).assign(file) > instance_variable_set state_attr, state > if state.options[:after_upload] and state.just_uploaded? > state.options[:after_upload].each do |sym| > self.send sym > end > end > end > > "state_method" is a Symbol that contains the name of another generated > method that will return an object that does all the heavy lifting for > file uploads/image manipulation. > > As a last note I wanted to give the reason why I decided to store > files in the filesystem and not in the database: Simply put, rails'' > support for large binary columns is very poor and inefficient > (somebody correct me please if that has changed since I last checked) > and since the filesystem is very good at storing files I decided to > leverage this. > > SebastianThank you very much. :) define_method should do the trick. In my case, the client has requested that files be stored in the database to eliminate file permission complexities. How does your plugin work with file permissions (i.e., which user owns the files and where are they stored)? -- Posted via http://www.ruby-forum.com/.
I actually decided try class_eval first (which David Black suggested on
the Ruby list). It worked, so I had no need to investigate further. I
figured I''d share the results with everyone in case someone else can
find a use for this (it actually should be a fairly common task).
Here it is:
ImageBlob.rb
------------
require ''RMagick''
include Magick
class ImageBlob
def ImageBlob.inject_attrib_assign_methods( attr_and_sizes_hash,
consumer_class )
attr_and_sizes_hash.each {|attr_name, max_size|
code = %{
def #{attr_name}=( file )
write_attribute( ''#{attr_name}'', ImageBlob.getBlob(
file,
#{max_size} ))
end
}
consumer_class.class_eval code }
end
def ImageBlob.getBlob( file, max_image_size )
begin
img = Image.from_blob( file.read ).first
if not img
raise
end
img.format = "JPG"
if img.rows > max_image_size or img.columns > max_image_size
if img.rows > img.columns
factor = max_image_size / img.rows.to_f
else
factor = max_image_size / img.columns.to_f
end
img = img.scale( factor )
end
retVal = img.to_blob
GC.start
return retVal
rescue
return nil
end
end
end
To use the class, from your model do:
ImageBlob.inject_attrib_assign_methods( { "image" => 225,
"thumbnailImage" => 125 }, self )
--Jonathan
--
Posted via http://www.ruby-forum.com/.