I ran into an issue with inherited while trying to understand a very strange behaviour in my IDE of choice, NetBeans. When running my rails test cases from NetBeans, I noticed they were not transactional, but when run on the command line they were. After sending a flame-o-gram which I will soon need to retract to the nb ruby mailing list, I decided to poke around in their code. I finally narrowed the problem down to their testrunner code.
In the newest version of NetBeans they have added a nicer ruby testrunner. In order to provide some features this new testrunner keeps track of all descendants of Test::Unit::TestCase. It does this, by, you guessed it, using the inherited method. When I delved into this code the first problem I saw was pretty obvious. The author had violated what I will now credit my friend Jim by referring to as Weirich's Hook Method Implementation Commandment:
Thou Shalt Always Delegate to the Previous Implementation
The reason this is important is that when you implement a hook method, you can easily step on someone else who had implemented it for another reason. Using the rails alias_method_chain will normally take care of this for you, but because this code loads before rails they didn't have that option and had not done the right thing and delegated explicitly.
And sure enough, I noticed that when I commented out the inherited method in the testrunner my tests became transactional. I figured at this point I had it licked. I went ahead and changed the NB testrunner code to delegate correctly and tried again. Sadly, it had no affect on the transactional issue. Much head scratching ensued. After many hours of investigation, I finally tracked down the real problem. I noticed that the use_transactional_fixtures class attribute of Test::Unit::TestCase was not being set. This led me to investigate how class_inheritable_attributes works in rails. Eventually I was able to put together a failing test case which expresses the problem. Here it is:
require File.dirname(__FILE__) + '/inheritable_accessor'
require File.dirname(__FILE__) + '/../test_helper'
class A
class_inheritable_accessor :foo
self.foo = "bar"
end
class B < A
end
class InheritableAttributesTest < Test::Unit::TestCase
def test_foo
assert_equal "bar", B.foo
end
end
And here what's in inheritable_accessor.rb:
class A
class << self
alias_method :a_old_inherited, :inherited
def inherited(base)
puts "A inherited"
a_old_inherited(base)
end
end
end
To try this at home, simply make a new rails project and drop these two files into your test/units directory. It should fail. But why? The inherited method does delegates to the previous implementation correctly, right? Now try switching the order of those two require statements. Poof, it passes.
Turns out the problem happens because of how class_inheritable_accessor is implemented. As you probably have guessed, it uses inherited. And yes, it does correctly delegate to the previous implementation. But it's where its implemented that is the problem: in order to let all classes be able to use this method, it's implemented on Class. However, there is an unfortunate side effect to this decision: it breaks for any class loaded before this code which itself defines inherited. In our example, the A class defines the inherited method before the rails code is loaded. But since the A class extends the Class class it's definition of inherited overrides the version rails adds to Class. The net outcome is that for the A class, and any class loaded before rails that defines inherited, class_inheritable_accessor is broken.
What's the right way to fix this? I'm not honestly sure. It could be that in order to do something like this which effectively adds a feature to the language, you need to be loaded first. On the flip side, it could be considered a bug that class_inheritable_accessor is broken in cases like this. Well, I had hoped that by the time I got this point in the post a clear solution would emerge. I suppose I'll have to leave it, as they say, as an exercise for the reader :)
Update: I was about to post this, and I went to grab some dinner and finally realized the solution: A still does not entirely obey Weirich's law. It correctly delegates to a previous implementation in the same class, but not to the superclass. A call to super at the end of the inherited method causes the test to pass. Adding the super call to the NB test runner code also causes my tests to be transactional again. Yay!
8 comments:
Very interesting indeed -- great post, thanks a bunch for solving the problem!
who has started the last 12 games in the absence of Yi Jianlian. "I know I've improved
a ton defensively this season."
...................................................
.......
池袋 風俗
渋谷 風俗
新宿 風俗
コンドーム 激安
アダルトDVD
av 写真
大人のおもちゃ
おとなのおもちゃ
アダルト ブルーレイ
アダルトショップ
ペニス増大
電マ
TENGA
SM 通販
メンズセクシー下着
男性用下着
メンズTバック
大規模修繕
決済代行
SEO
SEO
Grid geat grop
カード決済
ブライダルエステ
FX 外国為替
クレジットカード 比較
仔犬
子ウサギ
仔ウサギ
アダルトショップ
アダルトグッツ
ゴールドカード年会費
浮気調査 探偵
身辺調査 探偵
企業調査 探偵
盗聴発見 探偵
特殊調査 探偵
妻の浮気 夫の浮気
尾行調査
企業調査
所在調査
身辺調査
追跡調査
素行調査
追跡調査
信用調査
身上調査
所在調査
妻の浮気 相談
素行調査 相談
身元調査 相談
離婚 相談
追跡調査 相談
ストーカー対策 相談
結婚詐欺 相談
オナホール
メール 英文
ビジネススクール 英語
I always wanted to write in my site something like that.
phentermine without prescription loose weight
phentermine 37.5 without prescription 56
phentermine 37.5 no prescription medicals
phentermine 37.5 mg medicine
phentermine 37.5mg pharmacy
phentermine 37 5 healthy weight
phentermine 37.5 fat
anxiety medication anti.
phentermine 37.5 meds
phentermine 37.5 mg no prescription womens
http://georgeseal.yolasite.com
http://benhasse.vox.com
http://vedegene.livejournal.com
aser de dertyo lolas
qaw frta huna ser taye re
http://johndic.biz.ly/cgi-bin/blog
http://georgemaz.blog.com
http://mavitrain.bravehost.com
Quick Weightloss
wellness troubles
My Fitness
Dieting
Obesity
Good Health
Obese Women
Fatness
slimming down
Healthy Body
Post a Comment