Hi,
I am trying to create a model (BatchExternalBooking) which has the
following methods:
BatchExternalBooking.message_thread_1=
BatchExternalBooking.message_thread_1
BatchExternalBooking.message_thread_2=
BatchExternalBooking.message_thread_2
... etc all the way upto
BatchExternalBooking.message_thread_x=
BatchExternalBooking.message_thread_x
I assume that I need to do this via method_missing because I don't know
how many of these methods I will actually need.
My code currently looks like this:
class BatchExternalBooking
def method_missing(method_sym, *args)
if method_sym.to_s =~ /^message_thread_([0-9]*)=?(\w*)?$/
BatchExternalBooking.instance_eval "attr_accessor
:message_thread_#{$1}"
self.send("message_thread_#{$1}=", $2)
else
super
end
end
end
However, this is not working. See below:
b = BatchExternalBooking.new
=> #<BatchExternalBooking:0x3e54040>
b.message_thread_1 = 45
=> 45
b.message_thread_1
=> ""
The value wasn't actually set but the attr_accessor correctly created
the setter method. If I try and set the variable again, it works:
b.message_thread_1 = 45
=> 45
b.message_thread_1
=> 45
Why isn't the setter working the first time round?
Hi,
I am trying to create a model (BatchExternalBooking) which has the
following methods:
BatchExternalBooking.message_thread_1=
BatchExternalBooking.message_thread_1
BatchExternalBooking.message_thread_2=
BatchExternalBooking.message_thread_2
... etc all the way upto
BatchExternalBooking.message_thread_x=
BatchExternalBooking.message_thread_x
I assume that I need to do this via method_missing because I don't know
how many of these methods I will actually need.
My code currently looks like this:
class BatchExternalBooking
def method_missing(method_sym, *args)
if method_sym.to_s =~ /^message_thread_([0-9]*)=?(\w*)?$/
BatchExternalBooking.instance_eval "attr_accessor
:message_thread_#{$1}"
self.send("message_thread_#{$1}=", $2)
else
super
end
end
end
However, this is not working. See below:
b = BatchExternalBooking.new
=> #<BatchExternalBooking:0x3e54040>
b.message_thread_1 = 45
=> 45
b.message_thread_1
=> ""
The value wasn't actually set but the attr_accessor correctly created
the setter method. If I try and set the variable again, it works:
b.message_thread_1 = 45
=> 45
b.message_thread_1
=> 45
Why isn't the setter working the first time round?
Thanks a lot.
Two thoughts. First, why not use an array? Then
BatchExternalBooking.message_thread[n] = whatever
If that doesn't work for you, then investigate OpenStruct (i.e. 'ostruct').
Hi,
I am trying to create a model (BatchExternalBooking) which has the
following methods:
BatchExternalBooking.message_thread_1=
BatchExternalBooking.message_thread_1
BatchExternalBooking.message_thread_2=
BatchExternalBooking.message_thread_2
.. etc all the way upto
BatchExternalBooking.message_thread_x=
BatchExternalBooking.message_thread_x
I assume that I need to do this via method_missing because I don't know
how many of these methods I will actually need.
My code currently looks like this:
class BatchExternalBooking
def method_missing(method_sym, *args)
if method_sym.to_s =~ /^message_thread_([0-9]*)=?(\w*)?$/
BatchExternalBooking.instance_eval "attr_accessor
:message_thread_#{$1}"
You might rather want to use class_eval here.
Also, as Robert pointed out already, using $1 only once and storing the value in a local variable is safer because it can be changed by any method you invoke.
self.send("message_thread_#{$1}=", $2)
else
super
end
end
end
However, this is not working. See below:
b = BatchExternalBooking.new
=> #<BatchExternalBooking:0x3e54040>
b.message_thread_1 = 45
=> 45
b.message_thread_1
=> ""
The value wasn't actually set but the attr_accessor correctly created
the setter method. If I try and set the variable again, it works:
b.message_thread_1 = 45
=> 45
b.message_thread_1
=> 45
Why isn't the setter working the first time round?
Thanks a lot.
Is there a reason that you do not use OpenStruct or an ordinary Array for this? It seems you are indexing by number anyway so why not use an Array?
Sorry, I'm a pedant about things like that... Also, you might want to check
that this does the right thing when the getter is called before the setter. At
least, your method_missing regex seems to assume that this might be the case,
but your code doesn't.
···
On Sunday 26 April 2009 12:40:05 Tim Conner wrote:
The reason that I am trying to use methods/attributes to store the
information rather than an array is because this model is used to create
a batch update form in a Rails project. If one of the updates fails
then I want to display the errors on the form next to the relevant
fields. For this to work, I will need to use the error_messages_for
helper to which I need to pass the attribute.
My code currently looks like this:
class BatchExternalBooking
def method_missing(method_sym, *args)
if method_sym.to_s =~ /^message_thread_([0-9]*)=?(\w*)?$/
BatchExternalBooking.instance_eval "attr_accessor
:message_thread_#{$1}"
You might rather want to use class_eval here.
No instance_eval works just as well for sending attr_accessor to a
class/module. The only difference with class_eval is when using def to
define a method.
Both do the evaluation in the context of the receive but class_eval
sets the current class to the receiver.
Class.class_eval("def foo;end") defines an instance method while
Class.instance_eval("def foo;end") defines a class method.
This seems a bit less odd when you consider that a class method is
just a singleton method on the class (albeit one which can be
inherited by subclasses).
Also, as Robert pointed out already, using $1 only once and storing the
value in a local variable is safer because it can be changed by any method
you invoke.
That's not true either. The $n variables are frame local, in a given
invocation frame they will only change when another regex match is
done in the same invocation. The bug here is that this line should
have been
self.send("message_thread_#{$1}=", *args) # or args.first if you must.
Since the regexp only had a single capture, $2 is always nil, so even
though the newly created setter is geting called, it's setting the
instance variable to nil.
···
On Sun, Apr 26, 2009 at 3:30 PM, Robert Klemme <shortcutter@googlemail.com> wrote:
My code currently looks like this:
class BatchExternalBooking
def method_missing(method_sym, *args)
if method_sym.to_s =~ /^message_thread_([0-9]*)=?(\w*)?$/
BatchExternalBooking.instance_eval "attr_accessor
:message_thread_#{$1}"
You might rather want to use class_eval here.
No instance_eval works just as well for sending attr_accessor to a
class/module. The only difference with class_eval is when using def to
define a method.
I did not want to state that instance_eval is the issue. Sorry for being imprecise. A simple
Also, as Robert pointed out already, using $1 only once and storing the
value in a local variable is safer because it can be changed by any method
you invoke.
self.send("message_thread_#{$1}=", $2)
That's not true either. The $n variables are frame local, in a given
invocation frame they will only change when another regex match is
done in the same invocation.
A simple test verifies this to be true. But now I wonder how I have come to this misconception. I am pretty sure I stored $1 in a local variable to avoid issues with changing values. Maybe I just had another match in the same method and extended the overwriting problem to method calls.
Thank you for the education, Rick!
Kind regards
robert
···
On 26.04.2009 21:55, Rick DeNatale wrote:
On Sun, Apr 26, 2009 at 3:30 PM, Robert Klemme > <shortcutter@googlemail.com> wrote:
The reason that I am trying to use methods/attributes to store the
information rather than an array is because this model is used to create
a batch update form in a Rails project. If one of the updates fails
then I want to display the errors on the form next to the relevant
fields. For this to work, I will need to use the error_messages_for
helper to which I need to pass the attribute.
Am I approaching this in the correct manner?
I believe that ostruct by itself will do the job, or you can use
method_missing to delegate to a hash without actually defining any
methods.
At least, I know this works for form helpers. You'd have to test it with
validations and error_messages_for.