# What to do when two mixins need to hook into attribute= of a

**URL:** <https://www.ruby-forum.com/t/what-to-do-when-two-mixins-need-to-hook-into-attribute-of-a/77500>\
**Category:** Rails\
**Created:** [November 28, 2006, 2:40pm UTC](https://www.ruby-forum.com/t/what-to-do-when-two-mixins-need-to-hook-into-attribute-of-a/77500 "2006-11-28T14:40:57Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [November 28, 2006, 2:40pm UTC](https://www.ruby-forum.com/t/what-to-do-when-two-mixins-need-to-hook-into-attribute-of-a/77500/1 "2006-11-28T14:40:57Z")

</div>

I can override an ActiveRecord attribute in an mixin as follows:

module MyMixin  
def attribute=(new\_value)  
write\_attribute :attribute, new\_value  
end  
end

But what do I do if I have two mixins which may be included in  
arbitrary order and are somewhat orthogonal in purpose and who both  
need to customize attribute=?

Cheers,  
Michael

(specifically I am trying to figure out how to make act\_as\_attachment  
pass all its tests. It is broken right now because two mixins –  
InstanceMethods and FileSystemMethods – both override filename= )

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [November 28, 2006, 3:11pm UTC](https://www.ruby-forum.com/t/what-to-do-when-two-mixins-need-to-hook-into-attribute-of-a/77500/2 "2006-11-28T15:11:02Z")

</div>

Ok, this actually easy, because they can both do:

def attribute=(new\_value)  
# stuff  
super  
end

and all mixed in definitions will get called.

I was led astray by the fact that all the examples of overriding an  
attribute writer have it calling write\_attribute so I sillily thought  
that super would return a nomethoderror in the last mixin in the  
chain. But in actual fact you can just call super in an overridden  
attribute write, you don’t have to call write\_attribute (unless  
someone knows a reason you do???).

However, figuring out what to do if you couldn’t count on the method  
being mixed\_in already having a definition in the superclass helped  
me to learn more about ruby. Here is my example, in case anyone is  
curious:

class Superthing #this stands in for ActiveRecord::Base  
def dohook  
puts “whatever happens, this must happen”  
end  
end

module Doer #this is in one file in a plugin  
module Mary  
def doit  
puts “Mary says doit”  
super  
end  
end  
end

module Doer # this is in another file in a plugin, and is somewhat  
orthogonal to “Mary”  
module Bob  
def doit  
puts “Bob says doit”  
super  
end  
end  
end

module Doer # this initializes the plugin

def self.included(base)  
base.extend SetupMethods  
end

module MakeSureTheresASuper # this is necessary because the  
superclass only defines “dohook” not “doit”  
def doit  
dohook  
end  
end

module SetupMethods  
def setmeup  
include MakeSureTheresASuper  
if rand(2) == 1  
puts “Bob first”  
include Bob  
include Mary  
else  
puts “Mary first”  
include Mary  
include Bob  
end  
end  
end  
end

Superthing.send(:include, Doer::SetupMethods)

class Thing \< Superthing  
include Doer

setmeup

def initialize()  
end  
end

#will always do Bob & will always do Mary, but order is random  
Thing.new.doit

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [November 28, 2006, 3:11pm UTC](https://www.ruby-forum.com/t/what-to-do-when-two-mixins-need-to-hook-into-attribute-of-a/77500/3 "2006-11-28T15:11:02Z")

</div>

Michael J. wrote:

> need to customize attribute=?  
> Would a method chain work. The new release candidate has a method built  
> to make this easy (I think called alias\_method\_chain) but to simple  
> alias\_method calls will work as well.

Eric

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [November 28, 2006, 3:17pm UTC](https://www.ruby-forum.com/t/what-to-do-when-two-mixins-need-to-hook-into-attribute-of-a/77500/4 "2006-11-28T15:17:05Z")

</div>

Michael J. wrote:

> But what do I do if I have two mixins which may be included in  
> arbitrary order and are somewhat orthogonal in purpose and who both  
> need to customize attribute=?

Would a method chain work. The new release candidate has a method built  
to make this easy (I think called alias\_method\_chain) but two simple  
alias\_method calls will work as well.

Eric

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [November 28, 2006, 3:43pm UTC](https://www.ruby-forum.com/t/what-to-do-when-two-mixins-need-to-hook-into-attribute-of-a/77500/5 "2006-11-28T15:43:10Z")

</div>

On 28-Nov-06, at 6:07 AM, Eric A. wrote:

> Would a method chain work. The new release candidate has a method  
> built  
> to make this easy (I think called alias\_method\_chain) but two simple  
> alias\_method calls will work as well.

On 28-Nov-06, at 6:17 AM, Rob S. wrote:

> def my\_method\_with\_bar\_feature  
> # do stuff  
> end  
> end
> 
> This is completely untested. If the order in which these things  
> happens is important, you’ll have to be more careful and maybe  
> explicitly require things.
> 
> - rob

Thanks Eric & Rob. This does seem like a more robust way of doing it,  
although I wouldn’t want to break the downward compatibility just yet.

I’m curious to know if there is anything inherently wrong with my way  
of doing it, by calling super in each overloaded method, and letting  
the ancestors callchain take care of it?

I found one wrinkle in my solution, which is that I do need to define  
a bare filename= method because sometimes the model objects do not  
persist the filename attribute to the database, in which case super  
is of course not defined for filename= (but write\_attribute still works)

Cheers,  
Michael

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [November 28, 2006, 3:19pm UTC](https://www.ruby-forum.com/t/what-to-do-when-two-mixins-need-to-hook-into-attribute-of-a/77500/6 "2006-11-28T15:19:05Z")

</div>

On 11/28/06, Michael J. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> arbitrary order and are somewhat orthogonal in purpose and who both  
> need to customize attribute=?
> 
> Cheers,  
> Michael
> 
> (specifically I am trying to figure out how to make act\_as\_attachment  
> pass all its tests. It is broken right now because two mixins –  
> InstanceMethods and FileSystemMethods – both override filename= )

You’ll have to do a little metaprogramming and method chaining. This  
is assuming rails edge, so you can use alias\_method\_chain.

So in mixin one:

module Foo  
def self.included(klass)  
klass.alias\_method\_chain :my\_method, :with\_foo\_feature  
end

def my\_method\_with\_foo\_feature  
# do stuff  
end  
end

module Bar  
def self.included(klass)  
klass.alias\_method\_chain :my\_method, :with\_bar\_feature  
end

def my\_method\_with\_bar\_feature  
# do stuff  
end  
end

This is completely untested. If the order in which these things  
happens is important, you’ll have to be more careful and maybe  
explicitly require things.

- rob

–

> **[Home](https://robsanheim.com/)**
>
> Just another WordPress site

> **[Seeking Alpha | Stock Market Analysis & Tools for Investors](https://seekingalpha.com/)**
>
> Join Seeking Alpha, the largest investing community in the world. Get stock market news and analysis, investing ideas, earnings calls, charts and portfolio analysis tools.

[http://www.ajaxian.com](http://www.ajaxian.com)

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [November 28, 2006, 4:15pm UTC](https://www.ruby-forum.com/t/what-to-do-when-two-mixins-need-to-hook-into-attribute-of-a/77500/7 "2006-11-28T16:15:18Z")

</div>

> - rob  
> Hmmm, I see one disadvantage with the aliases: the consumer class  
> can’t override attribute=, unless they make sure the def stays  
> physically before the inclusion of the modules.

Using the callchain method, the consumer class can override  
attribute= as long as they call super instead of write\_attribute.

Unless I did something wrong. Here is my test:

class Superthing #this stands in for ActiveRecord::Base  
def dohook  
puts “whatever happens, this must happen”  
end  
end

module Doer #this is in one file in a plugin  
module Mary  
def self.included(base)  
base.class\_eval do  
alias\_method :doit\_without\_mary, :doit unless method\_defined?  
(:doit\_without\_mary)  
alias\_method :doit, :doit\_with\_mary  
end  
end  
def doit\_with\_mary  
puts “Mary says doit”  
doit\_without\_mary  
end  
end  
end

module Doer # this is in another file in a plugin, and is somewhat  
orthogonal to “Mary”  
module Bob  
def self.included(base)  
base.class\_eval do  
alias\_method :doit\_without\_bob, :doit unless method\_defined?  
(:doit\_without\_bob)  
alias\_method :doit, :doit\_with\_bob  
end  
end  
def doit\_with\_bob  
puts “Bob says doit”  
doit\_without\_bob  
end  
end  
end

module Doer # this initializes the plugin

def self.included(base)  
puts “I just got included”  
base.extend SetupMethods  
end

module MakeSureTheresASuper # this is necessary in my example  
def doit  
puts “I’m just standing in”  
dohook  
end  
end

module SetupMethods  
def setmeup  
include MakeSureTheresASuper  
if rand(2) == 1  
puts “Bob first”  
include Bob  
include Mary  
else  
puts “Mary first”  
include Mary  
include Bob  
end  
end  
end  
end

Superthing.send(:include, Doer::SetupMethods)

class Thing \< Superthing  
include Doer

setmeup

end

class CustomThing \< Superthing  
include Doer

setmeup

def doit()  
puts “CustomThing says doit”  
super  
end

# if we do setmeup here instead, it will work. but that is yucky

end

#will always do Bob & will always do Mary, but order is random  
puts “Thing:”  
Thing.new.doit  
puts  
puts “OOPS!!! CustomThing loses all the good love”  
puts “CustomThing:”  
CustomThing.new.doit

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [November 28, 2006, 3:49pm UTC](https://www.ruby-forum.com/t/what-to-do-when-two-mixins-need-to-hook-into-attribute-of-a/77500/8 "2006-11-28T15:49:12Z")

</div>

> persist the filename attribute to the database, in which case super  
> is of course not defined for filename= (but write\_attribute still works)
> 
> Cheers,  
> Michael

I don’t think there is anything wrong with your implementation,  
strictly. Its just more code, which means more to test and to  
maintain. Using a Rails feature like alias\_method\_chain will be a  
more obvious idiom to other experienced Rails developers, and will  
also have the advantage of using a tested, supported api from Rails.

OTOH, doing it yourself did help you learn more about what is going on  
underneath, which is a good thing. 🙂

- rob

–

> **[Home](https://robsanheim.com/)**
>
> Just another WordPress site

> **[Seeking Alpha | Stock Market Analysis & Tools for Investors](https://seekingalpha.com/)**
>
> Join Seeking Alpha, the largest investing community in the world. Get stock market news and analysis, investing ideas, earnings calls, charts and portfolio analysis tools.

[http://www.ajaxian.com](http://www.ajaxian.com)
