changing method behavior depending on context

M

Marat Kamenschikov

Assume there is class A with method save which takes one param with
default value (e.g. true). Class A instance gets save and behaves
depending on true value.
Assume there is class B with a field of type A. Calling save on this
field it should behave as if save(false) was called.
Outside of class B scope save method should behave originally.
Something like override, but just inside of one scope.

Here is some piece of code:
class A
def save(validate = true)
validate? «INVALID»: «VALID»
end
end

class B
# something very crazy
def initialize
@a = A.new
end
def save
@a.save
end
end

A.new.save # => «INVALID»
B.new.save # => «VALID»
A.new.save # => «INVALID»

It is meant to be used in Rails application with ActiveRecord objects,
but it really doesn't matter much.
The most simple way here is probably examining caller list, but is there
a way to redef a method inside of other class' scope?
 
J

Jesús Gabriel y Galán

Assume there is class A with method save which takes one param with
default value (e.g. true). Class A instance gets save and behaves
depending on true value.
Assume there is class B with a field of type A. Calling save on this
field it should behave as if save(false) was called.
Outside of class B scope save method should behave originally.
Something like override, but just inside of one scope.

Here is some piece of code:
class A
=A0def save(validate =3D true)
=A0 =A0validate? =ABINVALID=BB: =ABVALID=BB
=A0end
end

class B
=A0# something very crazy
=A0def initialize
=A0 =A0@a =3D A.new
=A0end
=A0def save
=A0 [email protected]
=A0end
end

A.new.save # =3D> =ABINVALID=BB
B.new.save # =3D> =ABVALID=BB
A.new.save # =3D> =ABINVALID=BB

It is meant to be used in Rails application with ActiveRecord objects,
but it really doesn't matter much.
The most simple way here is probably examining caller list, but is there
a way to redef a method inside of other class' scope?

I have no clue if this works or not with ActiveRecord objects, but in
case it does, I would do this:

irb(main):006:0> class A
irb(main):007:1> def save(validate=3Dtrue)
irb(main):008:2> validate ? "INVALID" : "VALID"
irb(main):009:2> end
irb(main):010:1> end
=3D> nil
irb(main):011:0> class B
irb(main):012:1> def initialize
irb(main):013:2> @a =3D A.new
irb(main):014:2> def @a.save(validate=3Dtrue)
irb(main):015:3> super false
irb(main):016:3> end
irb(main):017:2> end
irb(main):018:1> def save
irb(main):019:2> @a.save
irb(main):020:2> end
irb(main):021:1> end
=3D> nil
irb(main):022:0> B.new.save
=3D> "VALID"
irb(main):023:0> A.new.save
=3D> "INVALID"


Although if this works for you, you could also change the save method
in class B to pass a false...

irb(main):001:0> class A
irb(main):002:1> def save(validate=3Dtrue)
irb(main):003:2> validate ? "INVALID" : "VALID"
irb(main):004:2> end
irb(main):005:1> end
=3D> nil
irb(main):006:0> class B
irb(main):007:1> def initialize
irb(main):008:2> @a =3D A.new
irb(main):009:2> end
irb(main):010:1> def save
irb(main):011:2> @a.save false
irb(main):012:2> end
irb(main):013:1> end
=3D> nil
irb(main):014:0> A.new.save
=3D> "INVALID"
irb(main):015:0> B.new.save
=3D> "VALID"

Jesus.
 
R

Robert Klemme

Assume there is class A with method save which takes one param with
default value (e.g. true). Class A instance gets save and behaves
depending on true value.
Assume there is class B with a field of type A. Calling save on this
field it should behave as if save(false) was called.
Outside of class B scope save method should behave originally.
Something like override, but just inside of one scope.

Here is some piece of code:
class A
def save(validate = true)
validate? «INVALID»: «VALID»
end
end

class B
# something very crazy
def initialize
@a = A.new
end
def save
@a.save
end
end

A.new.save # => «INVALID»
B.new.save # => «VALID»
A.new.save # => «INVALID»

It is meant to be used in Rails application with ActiveRecord objects,
but it really doesn't matter much.
The most simple way here is probably examining caller list, but is there
a way to redef a method inside of other class' scope?

What's wrong with doing

class B
# something very crazy
def initialize
@a = A.new
end
def save
@a.save false
end
end

as Jesus suggested? Even if you don't do that you could do

class A
def initialize(owner = nil)
@owner = owner
end

def save
B === @owner ? "VALID" : "INVALID"
end
end

class B
# something not so crazy
def initialize
@a = A.new self
end
def save
@a.save
end
end

Note: meta programming magic also makes code harder to read.

Kind regards

robert
 
M

Marat Kamenschikov

Thanks Jesus and Robert! But the case is quite more complex because (I
forgot to mention in original message) I'm not calling B#save directly -
it's a part of a big class which builds many objects and the option is
either replace all .save with .save(false) or to make some monkey magic.
The B class might go into some lib or plugin (but not sure yet) so I
need to find a way for this case also.

My friend and I came to this kind of solution:
class B
def initialize
@a=A.new
@a.instance_eval <<-EOF
alias :eek:ld_save :save
def save(validate = false)
old_save(validate)
end
EOF
end

The only thing that this would save without validation only one time, if
there'd be smth like
class B
def initialize
@a1 = A.new
@a2 = A.new
end
def save
@a1.save
@a2.save # it wouldn't work here or I'd need to define same methods
n times
end
end

I will agree if you say that doesn't worth that but that becomes a ruby
riddle - is it powerful enough to change behaviour depending on context?
I think it is. Curious how.
 

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,773
Messages
2,569,594
Members
45,121
Latest member
LowellMcGu
Top