injecting dynamic methods into a class

J

johanatan

hi All,

I posted this message to the Ruby on Rails group yesterday, but I
suppose it belongs here instead.

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.getBlob( 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 )

in their class definition to have the functionality added (with
any number of image fields each having their own size).

Can anyone out there provide the last piece of this puzzle?

Thanks much,
Jonathan
 
D

David A. Black

Hi --

hi All,

I posted this message to the Ruby on Rails group yesterday, but I
suppose it belongs here instead.

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.getBlob( 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 )

in their class definition to have the functionality added (with
any number of image fields each having their own size).

Can anyone out there provide the last piece of this puzzle?

I don't know if this is an exact fit, but I've tried to model it on what
your example. The basic idea here is to call class_eval on the class
that's passed in, and proceed from there (defining the methods).

class ImageBlob
def self.get_blob(*args)
puts "In ImageBlob.get_blob"
end
end

class MyClass
def initialize(attrs_and_sizes, klass)
attrs_and_sizes.each_pair do |attr_name,max_image_size|
klass.class_eval do
define_method("#{attr_name}=") do |file|
write_attribute(attr_name,
ImageBlob.get_blob(file,max_image_size))
end
end
end
end
end

class OtherClass
def write_attribute(*args)
puts "In write_attribute"
end
end

m = MyClass.new({"a" => 10, "b" => 20}, OtherClass)
OtherClass.new.a = 2

(If you're passing in a classname string, rather than a class, you'd also
want to do some variant of const_get to get the class itself before
class_eval'ing from it.)


David Black
 
T

Trans

If you don't mind while I'm at this I'm going to touch up the code to
follow ruby conventions.

def initialize( 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

BTW it seems odd to use the #initialize method of ImageBob to do this.

HTH,
T.
 
J

johanatan

transfire said:
If you don't mind while I'm at this I'm going to touch up the code to
follow ruby conventions.

I don't mind at all. I'm new to ruby so I haven't picked up on the
extensions yet. :)
def initialize( 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

I didn't try class_eval. Did you test this or is it just a theory? :)
Seemed like regular eval and extend should work.
BTW it seems odd to use the #initialize method of ImageBob to do this.

It is odd, but the ImageBlob class contains only one class method (i.e.,
static method) and no data members, so no actual memory should be
defined for it. I suppose it could have contained two class methods
instead of using initialize (that is better so I'll change that). At
first, I was trying to store a ptr to the ImageBlob instance in the
consumer class but realized it wasn't necessary.

Much thanks for the answers.
 
J

johanatan

Ok. The class is ready for consumption. :) Here it is:

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
 
T

Trans

johanatan said:
I don't mind at all. I'm new to ruby so I haven't picked up on the
extensions yet. :)

Understandable. As you've noticed Matz prefers underscores and
lowercase over camelcase for methd names, so that's become the general
convention. There's alos a tendency to get them as small as possible
while still being human readable/self-documenting.
I didn't try class_eval. Did you test this or is it just a theory? :)
Seemed like regular eval and extend should work.

I didn't test, but I've used it enough times to know that the way to go
about it (even I made a booboo in my code) You'll notice David used it
in his variation too.
It is odd, but the ImageBlob class contains only one class method (i.e.,
static method) and no data members, so no actual memory should be
defined for it. I suppose it could have contained two class methods
instead of using initialize (that is better so I'll change that). At
first, I was trying to store a ptr to the ImageBlob instance in the
consumer class but realized it wasn't necessary.

If ImagaBob is not instantiable then a module would be better than a
class.
Much thanks for the answers.

No problemo.

T.
 
T

Trans

Couple suggestions:

def ImageBlob.inject_attrib_assign_methods

to

def self.inject_accessors

Using 'self' is more robust. Also in Ruby attribute methods are
generally refered to as accessors.

def ImageBlob.getBlob( file, max_image_size )
begin
img = Image.from_blob( file.read ).first
if not img
raise
end

to

def self.get_blob( file, max_image_size )
begin
img = Image.from_blob( file.read ).first
return nil if not img

Don't use camelcase (unless you're passionate about it) and there's no
need to raise if the rescue clause is just going ot return nil anyway.

T.
 
J

Jonathan

transfire said:
Couple suggestions:

def ImageBlob.inject_attrib_assign_methods

to

def self.inject_accessors

Using 'self' is more robust. Also in Ruby attribute methods are
generally refered to as accessors.

I agree with the name change. However, the class isn't instantiable, so
using self won't work. I will change this to a module though. (you can
call methods of a module without mixing it in right?)
def ImageBlob.getBlob( file, max_image_size )
begin
img = Image.from_blob( file.read ).first
if not img
raise
end

to

def self.get_blob( file, max_image_size )
begin
img = Image.from_blob( file.read ).first
return nil if not img

Don't use camelcase (unless you're passionate about it) and there's no
need to raise if the rescue clause is just going ot return nil anyway.

Thanks for point that out. I overlooked getBlob.

--Jonathan
 
J

Jonathan

Here's the updated code:

require 'RMagick'
include Magick

module ImageBlob

def ImageBlob.inject_assigners( 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.get_blob( file,
#{max_size} ))
end
}
consumer_class.class_eval code }
end

def ImageBlob.get_blob( file, max_image_size )
begin
img = Image.from_blob( file.read ).first
return nil if not img

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

ret_val = img.to_blob
GC.start
return ret_val
rescue
return nil
end
end
end
 
J

Jonathan

I agree with the name change. However, the class isn't instantiable, so
using self won't work. I will change this to a module though. (you can
call methods of a module without mixing it in right?)

Oops. I guess 'self' in that context refers to the module or class??
Yea, that would be more robust (or at least more consistent with
write-once. changing the name of a class or module would require
changing all static method names (are these called class methods in
ruby??) unless self is used. Is that the reason you think it's more
robust or was it something else?
 
J

Jonathan

transfire said:
Using 'self' is more robust. Also in Ruby attribute methods are
generally refered to as accessors.

I've always heard 'accessor' refers to a get_prop method and 'assigner'
refers to a set_prop method. Does accessor refer to both in Ruby?
 
T

Trans

However, the class isn't instantiable, so using self won't work.

Sure it will. 'self' just refers to the current context, in either case
whether class or module that's what it is. So it should work fine.
(you can call methods of a module without mixing it in right?)

Yes, if they are "module methods" as opposed to instance methods.
Modules methods, (also called class methods but usually in the context
of class) are singleton methods, or adhoc methods (my new prefered
term). You can tell this by the way they are defined --the name of the
object proceeds the method name in the def statement (eg. 'def
ImageBob.get_blob'). Another way to write them:

module ImageBlob

class << self # opens adhoc context

def get_blob
...
end

end

end


T.
 
T

Trans

I've always heard 'accessor' refers to a get_prop method and 'assigner'
refers to a set_prop method. Does accessor refer to both in Ruby?

Yes, becuase of the common convenience method:

attr_accessor :a

which creates both a "reader" and a "writer", which are the terms Ruby
generally uses. What I mean by *convenience* method is that the above
is simply a shortcut for doing:

def a
@a
end

def a=(x)
@a = x
end

And is the same as doing

attr_reader :a
attr_writer :a

T.
 
T

Trans

Jonathan said:
Oops. I guess 'self' in that context refers to the module or class??
Yea, that would be more robust (or at least more consistent with
write-once. changing the name of a class or module would require
changing all static method names (are these called class methods in
ruby??) unless self is used. Is that the reason you think it's more
robust or was it something else?

Nope, that's it exactly.

T.
 
R

Ross Bamford


I know I'm new, but humour me if you will ?

Firstly, what's wrong with 'singleton'? It seems to me that it fits well,
and makes the usage obvious to the reader (well, me, but I assume others
too?). Secondly, 'Ad-hoc' seems to be a really odd choice to me. I am
aware that it can be taken to mean 'Specific to a given problem', but it
is also defined along the lines of 'impromtu', 'temporary' or
'disorganized'. Science uses it to mean a quick fix for a flawed theory.
Upon seeing the word ad-hoc I tend to imagine 'jury rigged' - stuck
together with duct tape to last us out the storm.

Indeed, someone given to 'adhocism' (IMHO an awful word) exhibits 'the
tendency to use temporary, provisional, or improvised methods to deal with
a particular problem'. I wouldn't want my solutions to be seen as
'temporary' or 'provisional', whether tailor-made or otherwise.

I'm sure this is an ongoing debate, and I don't want to tread on any
beliefs, but I just thought I'd offer a perspective from a fresh pair of
eyes. Is there a serious movement to replace 'singleton'?
 
H

Hal Fulton

Ross said:
I know I'm new, but humour me if you will ?

Firstly, what's wrong with 'singleton'?

[snippage]

It doesn't really bother me. But there is some confusion with
the Singleton pattern, and with other similar usages.
I'm sure this is an ongoing debate, and I don't want to tread on any
beliefs, but I just thought I'd offer a perspective from a fresh pair
of eyes. Is there a serious movement to replace 'singleton'?

Some people want to change it, I think. If it must be changed, I
would favor something like "singular class" (and I agree with your
assessment of "ad hoc"). Some have suggested "eigenclass" -- and I
admit this is a cool-sounding word, reminding me of my math and physics
(and German) in college. But I can't really advocate it seriously.


Hal
 
J

Jonathan

It doesn't really bother me. But there is some confusion with
the Singleton pattern, and with other similar usages.


Some people want to change it, I think. If it must be changed, I
would favor something like "singular class" (and I agree with your
assessment of "ad hoc"). Some have suggested "eigenclass" -- and I
admit this is a cool-sounding word, reminding me of my math and physics
(and German) in college. But I can't really advocate it seriously.

Hal,

I think transfire (and rosco) were referring to singleton methods, not
classes. And, actually I totally agree with the points made by rosco.
'Ad hoc' has too many negative connotations and singleton has a fairly
unambiguous meaning.

A singleton class should appear to the consumer as a regular class, but
under the skin always return the same instance. How is this
accomplished in Ruby? It couldn't be done in initialize, could it
(because initialize doesn't actually return an instance)?

--Jonathan
 
T

Trans

Ross said:
I know I'm new, but humour me if you will ?

Firstly, what's wrong with 'singleton'? It seems to me that it fits well,
and makes the usage obvious to the reader (well, me, but I assume others
too?).

There has been much discussion about this. The problem with the term
singleton is that it clashes with singleton design pattern, which is
something different altogether. B/c of this much discussion has been
given to finding a better term.

Now, The original term I believe was "virtual class", indeed you will
still find an error or two in core that uses that phrase. I'm not sure
you will find the term "singleton" anywhere in the source though. Also
the the term "metaclass" was used when the context is of a class' or
module's singleton class. But metaclass fell out of favor --I think
b/c matz said he didn't like it. So "singleton" came along to sort of
fill the void, not so much for its particular merits, but more for the
lack of something better. Also I point out we have other terms that can
cause confusion, although they too refer to the same thing just in a
particular usage, namely "class methods" and "module methods".

About a year ago, I was having to decide what term to use in Facets, I
included the ususal fair but wan't happy about having to have so many
methods all for the same thing. So I tried to find a more suitable term
that we all could generally agree on. I've tried out a few ideas, but
none of them really worked. Around that time _why the lucky stiff came
up with the term "eigenclass", and that has had some sticking power, no
doubt in part due to the ingenius humor he can bring to things. I think
it a fairly good term, and I think we should keep using it and even get
a bit more serious about it, tough obviously it still lacks in certain
respects. I had all but given up on finding a better term until I
accidently came acrosss "ad hoc". And I've actually been surprised and
delighted at just how well that term works.
Secondly, 'Ad-hoc' seems to be a really odd choice to me. I am
aware that it can be taken to mean 'Specific to a given problem', but it
is also defined along the lines of 'impromtu', 'temporary' or
'disorganized'. Science uses it to mean a quick fix for a flawed theory.
Upon seeing the word ad-hoc I tend to imagine 'jury rigged' - stuck
together with duct tape to last us out the storm.

You see that's not the actual definition of the term. That's more of
the vague understanding one gathers from not actually knowing the
meaning. Although that vague idea has become widespread enough to be
acknolwedged, it is still a secondary usage. I understand where you're
coming from though, b/c I thought much the same way until I had used
the word inproperly and my Grandmother corrected me. I wasn't so sure,
so we looked it up in the dictionary and sure enough she was right. The
definition is quite clear. From Websters (and others):

Main Entry: 1ad hoc
Pronunciation: 'ad-'häk, -'hOk; 'äd-'hOk
Function: adverb
Etymology: Latin, for this
: for the particular end or case at hand without consideration of
wider application

That's how I realized the term would make a good fit.
Indeed, someone given to 'adhocism' (IMHO an awful word)
exhibits 'the tendency to use temporary, provisional, or improvised methods to deal with
a particular problem'. I wouldn't want my solutions to be seen as
'temporary' or 'provisional', whether tailor-made or otherwise.

Yep, "adhocism" is a bad term, it's fairly recent, hardly known, and
really a misuse of the term adhoc. One gets this shade of meaning from
applying the term to a particular *moment* --its the purpose itself
that is temporary or provisional, not the "for" of it.
I'm sure this is an ongoing debate, and I don't want to tread on any
beliefs, but I just thought I'd offer a perspective from a fresh pair of
eyes. Is there a serious movement to replace 'singleton'?

I did a survey once and people's opinions are all over the map. I
personaly would like to see a solid term. I think 'adhoc' works well
becuase it is small, ponient and has the added advantage (which none of
the other choices have) of being an adverb, so it has very flexible
usage. I guess my end preference to all this is that we migrate to the
terms 'adhoc' and 'eigenclass' as is suitable. But I think this will
happen naturally if the terms work.

T.
 
T

Trans

'Ad hoc' has too many negative connotations and singleton has a fairly
unambiguous meaning.

I felt the same way at first, until I started using it, keeping in mind
the strict definition --even the Latin definition: *for this*.

T.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top