-
Notifications
You must be signed in to change notification settings - Fork 322
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use method_defined? instead of instance_methods lookup #337
Conversation
It's also worth to not convert every string to symbol — there's no need to do it now. |
the behavior of method_defined? vs instance_methods.include? aren't equivalent. method_defined? would also return true if the method were defined by one of it's superclasses, while instance_methods.include? would not. from my reading of the code, the latter behavior is what we want. i took a quick look at Module and don't see an equivalent. do you know of something that i missed? |
No, class X
def m
end
end
class Y < X; end
puts Y.instance_methods.include?(:m) # outputs 'true'
# However:
puts Y.instance_methods(false).include?(:m) # outputs 'false' But
I don't think so. What if I want to inherit my model class from another? |
I created a benchmark branch in my fork — it just contains a So we have 4 variants:
Here the benchmarks from my laptop:
This one performs better! |
Also we can see that @faisalmansoor's solution (#336 — that constructs a |
should have tried it out rather than just reading the doc, which lead to me overlooking the diff between modules and classes. re: what the behavior should be
it's a corner case, but here, i'd expect the attribute foo to overwrite the foo= method. that said, the behavior is not changing so no need to try and deal with now. the PR code gets exercised indirectly in specs, but could you add specs to verify the attributes method is doing what we expect in the case where there isn't a user defined version of the setter and predicate method? GTG once that's in place. |
💘 for the benchmark |
ok I will add it soon. |
@marshall-lee can you explain your benchmark a little bit more, how many objects did you test with and how many attributes each object had? array include is implemented using a linear search, I don't think just memorizing the array will solve the problem, even with memorization we will end up with a quadratic runtime for this function. I think we should use a set or method_defined function, they both will result in at max O(n lg n) runtime for this function.
|
benchmark code is open, i provided the link above ( In this benchmark I emulate a case when fetching from server an array of 1000 objects. Each object has three properties: |
O(lgN * N) vs O(N * N) just tells us about a rate of growth. Sure, solution with |
By the way, |
Sometimes directly and sometimes indirectly. Specs for setters/getters are already in spec/model/dirty_spec.rb. Are they sufficient? I just wrote my spec for UPD: context "attributes class method" do
before do
spawn_model 'Foo::User' do
attributes :fullname, :document
end
end
context "instance" do
subject { Foo::User.new }
it { should respond_to(:fullname) }
it { should respond_to(:fullname=) }
it { should respond_to(:fullname?) }
end
it "defines setter that affects attributes" do
user = Foo::User.new
user.fullname = 'Vladimir Kochnev'
user.attributes[:fullname].should eq('Vladimir Kochnev')
end
end |
About a corner case discovered by you: yeah it's a big problem. I think in next major version we should split |
@marshall-lee the reason i asked for specs is the behavior of the attributes method is i think it will be clearer to gem users if the behavior is spec-ed explicitly. the current specs also don't check that the predicate method gets defined. the attribute.to_sym call is also unnecessary since it gets converted back to a string when it gets interpolated. we can deal with that separately if you prefer. |
It's not necessary now since method_defined? accept both Symbol and String argument.
I wanted to do it separately because it's not the only place to fix string <=> symbol transformation. It's also not necessary to do interpolation like |
I added specs. Are they ok? |
Use method_defined? instead of instance_methods lookup
yep. looks good. thanks for your contribution 👍 |
This one is related to #336 and #307 and solves problem more efficient and simple (no memoization).
There is no need to call
instance_methods
at all because Ruby already hasmethod_defined?
that internally uses hash lookup (st_lookup
fromst.c
). After all, it doesn't create new array/set objects that is good too.