Can I make a new function like attr_accessor?

J

Jack, Paul

I'm writing a program that has a bunch of components I'd like
to link together using Java-style property change events.

So I created a mixin module:

module Emitter

def addListener(listener)
@listeners.push(listener)
end

def removeLister(listener)
@listeners.delete(listener)
end

def emit(sym, oldValue)
@listeners.each { |x| x.propertyChanged(self, sym, oldValue) }
end

end

In my component objects, I just make them include Emitter and invoke
"emit" on any mutator:

class ExampleComponent

include Emitter

def foo
@foo
end

def foo=(x)
old = @foo
@foo = x
emit:)foo, old)
end

end

I'd like to automate the creation of emitter properties, though. Just
like I can use attr_accessor to quickly create readable/writeable
attributes, I'd like a new attr_emitter function to quickly create
readable/writeable/emitable attributes.

But I can't figure out how to do that. :( Is it possible? It would
save me a LOT of redundant typing...

Thanks all,

-Paul
 
F

Francis Hwang

I'd like to automate the creation of emitter properties, though. Just
like I can use attr_accessor to quickly create readable/writeable
attributes, I'd like a new attr_emitter function to quickly create
readable/writeable/emitable attributes.

But I can't figure out how to do that. :( Is it possible? It would
save me a LOT of redundant typing...

It's quite possible, and a very useful way to add domain-specific
syntactic sugar. Basically you define it as a module-level or
class-level method.

module Emitter
def self.attr_emitter( args )
...
end
end

Remember that in Ruby there's no distinction between compile-time and
runtime. So when you define a class like this:

class ExampleComponent
include Emitter
attr_emitter arg1, arg2
end

The call to "attr_emitter" isn't actually a special compile-time flag;
it's a module- or class-level method that you're calling at runtime, as
you define the class.

BTW, if you're just coming from Ruby to Java, I wrote an article on my
blog that you might find really useful: http://fhwang.net/blog/40.html

Francis Hwang
http://fhwang.net/
 
R

Robert Klemme

Thomas Counsell said:
Hi Paul

Not tested, but something like this should do the trick:

class Class
def attr_emitter( *syms )
syms.each do |sym|
class_eval( "def #{sym}; @#{sym} end" )
class_eval( "def #{sym}=(x)
old, @#{sym} = @#{sym}, x
emit:)#{sym}, old)
end" )
end
end
end

Preferrable put that code into class Module. That way even modules can
use attr_emitter.
Note that I think you also need to define @listeners in your Emitter
module ..... (@listeners ||= [] ).push( listener) or some such.

Yeah. Also you can automate inclusion of the module. So this would be my
implementation:

module Emitter
def listeners; @listeners ||= [] end

def addListener(listener)
listeners.push(listener)
end

def removeLister(listener)
listeners.delete(listener)
end

def emit(sym, oldValue)
listeners.each { |x| x.propertyChanged(self, sym, oldValue) }
end
end

class Module
def attr_emitter(*syms)
syms.each do |sym|
class_eval( "def #{sym}; @#{sym} end; def #{sym}=(x) old, @#{sym} =
@#{sym}, x ; emit:)#{sym}, old) end" )
end

include Emitter
end
end

Kind regards

robert
 

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,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top