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/.