Injecting dynamic methods into a class


#1

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


#2

Hi –

On Sun, 4 Dec 2005, johanatan wrote:

user.image = #Ruby:File
model.rb

take the consumer class and inject the necessary methods into it (image=
end"
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


#3

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.


#4

transfire wrote:

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. :slight_smile:

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? :slight_smile:
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.


#5

Ok. The class is ready for consumption. :slight_smile: 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


#6

johanatan removed_email_address@domain.invalid removed_email_address@domain.invalid wrote:

transfire wrote:

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. :slight_smile:

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.

  consumer_class.class_eval code

end

I didn’t try class_eval. Did you test this or is it just a theory? :slight_smile:
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.

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.

If ImagaBob is not instantiable then a module would be better than a
class.

Much thanks for the answers.

No problemo.

T.


#7

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.


#8

An example of using this is:

in a class definition:

ImageBlob.inject_attrib_assign_methods( { “image” => 225,
“thumbnailImage” => 125 }, self )


#9

transfire wrote:

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


#10

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


#11

transfire wrote:

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?


#12

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?


#13

Jonathan removed_email_address@domain.invalid <removed_email_address@domain.invalid. wrote:

robust or was it something else?
Nope, that’s it exactly.

T.


#14

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.


#15

On Tue, 06 Dec 2005 02:10:51 -0000, Trans removed_email_address@domain.invalid wrote:

… singleton methods, or adhoc methods (my new prefered
term).

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’?


#16

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.


#17

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,

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


#18

Ross B. wrote:

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


#19

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


#20

Ross B. wrote:

On Tue, 06 Dec 2005 02:10:51 -0000, Trans removed_email_address@domain.invalid wrote:

… singleton methods, or adhoc methods (my new prefered
term).

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.