Feature #5690

Module#qualified_const_get

Added by Yehuda Katz over 3 years ago. Updated over 2 years ago.

[ruby-core:41404]
Status:Closed
Priority:Normal
Assignee:Aaron Patterson

Description

It would be great if there was a way to dynamically load a constant path:

module Foo
module Bar
module Baz
end
end
end

Foo.qualified_const_get("Bar::Baz") => Foo::Bar::Baz

noname (500 Bytes) Anonymous, 12/01/2011 10:23 AM

bug5690.pdf (615 KB) Aaron Patterson, 07/01/2012 01:29 AM

noname (500 Bytes) Anonymous, 07/02/2012 11:53 PM

p2c.diff Magnifier (4.86 KB) Aaron Patterson, 10/24/2012 02:40 AM


Related issues

Related to Ruby trunk - Feature #5666: Make rb_path2class public Closed 11/24/2011
Duplicated by Ruby trunk - Feature #767: Module#const_getでネストした定数の値を得られてもよいと思う Rejected 11/21/2008

History

#1 Updated by Yui NARUSE over 3 years ago

  • Category set to core
  • Status changed from Open to Assigned
  • Assignee set to Yukihiro Matsumoto
  • Target version set to 2.0.0

#2 Updated by Yukihiro Matsumoto over 3 years ago

  • ruby -v changed from ruby 1.9.3p0 (2011-10-30 revision 33570) [x86_64-darwin11.2.0] to -

Hi,

In message "Re: [ruby-trunk - Bug #5690][Open] Module#qualified_const_get"
on Wed, 30 Nov 2011 12:10:02 +0900, Yehuda Katz wycats@gmail.com writes:

|It would be great if there was a way to dynamically load a constant path:
|
| module Foo
| module Bar
| module Baz
| end
| end
| end
|
| Foo.qualified_const_get("Bar::Baz") => Foo::Bar::Baz

Interesting.

  • is there any use-case for this method?
  • if Bar is not a class nor module, what kind error should be raised?
  • is qualified_const_get an appropriate name for the function?

                        matz.
    

#3 Updated by Yukihiro Matsumoto over 3 years ago

Hi,

In message "Re: Re: [ruby-trunk - Bug #5690][Open] Module#qualified_const_get"
on Wed, 30 Nov 2011 16:52:36 +0900, Yehuda Katz wycats@gmail.com writes:

|> * is there any use-case for this method?
|
|We use it often in Rails.

Could you show us the concrete example? It is difficult for me to
imagine.

|> * if Bar is not a class nor module, what kind error should be raised?
|>
|
|TypeError: "(…) is not a class/module"

Understood.

|> * is qualified_const_get an appropriate name for the function?
|>
|
|Unknown. It's what we call it in Rails ;)

How about

Foo.const_get("Bar::Baz")

or

Foo.const_get(:Bar, :Baz)

?

                        matz.

#4 Updated by Yehuda Katz over 3 years ago

Yehuda Katz
(ph) 718.877.1325

2011/11/30 Yukihiro Matsumoto matz@ruby-lang.org

Hi,

In message "Re: Re: [ruby-trunk - Bug #5690][Open]
Module#qualified_const_get"
on Wed, 30 Nov 2011 16:52:36 +0900, Yehuda Katz wycats@gmail.com
writes:

|> * is there any use-case for this method?
|
|We use it often in Rails.

Could you show us the concrete example? It is difficult for me to
imagine.

One case is to convert path names (file system paths) into constants.

In general, Rails uses inflections to convert names from different
contexts. In the router, for instance, "foo/bar" is used to represent
namespaces, which are then converted into "Foo::Bar" and then Foo::Bar.

|> * if Bar is not a class nor module, what kind error should be raised?
|>
|
|TypeError: "(…) is not a class/module"

Understood.

|> * is qualified_const_get an appropriate name for the function?
|>
|
|Unknown. It's what we call it in Rails ;)

How about

Foo.const_get("Bar::Baz")

Sounds good to me. Currently, trying to const_set or const_get a name with
:: in it produces a NameError, so it should not introduces new issues.

or

Foo.const_get(:Bar, :Baz)

This doesn't satisfy the use-case in question.

?

                                                   matz.

#5 Updated by Anonymous over 3 years ago

On Wed, Nov 30, 2011 at 04:52:36PM +0900, Yehuda Katz wrote:

On Tue, Nov 29, 2011 at 10:04 PM, Yukihiro Matsumoto matz@ruby-lang.orgwrote:

Hi,

In message "Re: [ruby-trunk - Bug #5690][Open]
Module#qualified_const_get"
on Wed, 30 Nov 2011 12:10:02 +0900, Yehuda Katz wycats@gmail.com
writes:

|It would be great if there was a way to dynamically load a constant path:
|
| module Foo
| module Bar
| module Baz
| end
| end
| end
|
| Foo.qualified_const_get("Bar::Baz") => Foo::Bar::Baz

Interesting.

  • is there any use-case for this method?

We use it often in Rails.

When serializing objects to yaml, the fully qualified class name is
emitted. When reviving those objects, the class name comes back as a
string, so psych uses rb_path2class to find the constant.

  • if Bar is not a class nor module, what kind error should be raised?

TypeError: "(…) is not a class/module"

Agreed.

  • is qualified_const_get an appropriate name for the function? Unknown. It's what we call it in Rails ;)

Yes.

--
Aaron Patterson
http://tenderlovemaking.com/

#6 Updated by Yui NARUSE over 3 years ago

(2011/12/01 5:59), Yehuda Katz wrote:

Yehuda Katz
(ph) 718.877.1325

2011/11/30 Yukihiro Matsumoto >

Hi,

In message "Re:  Re: [ruby-trunk - Bug #5690][Open] Module#qualified_const_get"
   on Wed, 30 Nov 2011 16:52:36 +0900, Yehuda Katz <wycats@gmail.com <mailto:wycats@gmail.com>> writes:

|> * is there any use-case for this method?
|
|We use it often in Rails.

Could you show us the concrete example?  It is difficult for me to
imagine.

One case is to convert path names (file system paths) into constants.

In general, Rails uses inflections to convert names from different contexts. In the router, for instance, "foo/bar" is used to represent namespaces, which are then converted into "Foo::Bar" and then Foo::Bar.

"concrete exapmle" needs "what is the router?" and "when the router is used?".

--
NARUSE, Yui naruse@airemix.jp

#7 Updated by Koichi Sasada over 3 years ago

(2011/11/30 12:10), Yehuda Katz wrote:

It would be great if there was a way to dynamically load a constant path:

module Foo
module Bar
module Baz
end
end
end

Foo.qualified_const_get("Bar::Baz") => Foo::Bar::Baz

Any progress on it?

There are several similar proposals.
I think 2.0 is a good opportunity to finish such discussion.

BTW, is it a "BUG"? (on title)

--
// SASADA Koichi at atdot dot net

#8 Updated by Thomas Sawyer over 3 years ago

Please, can we avoid the use such abstract name.

Is the speed hit too great that Ruby's can't just use #const_get for this as well? I suspect the Rail's method name was chosen simply to avoid a monkey patch.

Of course, in Facets the method is just called #constant. Heaven forbid we use the obvious name ;-)

#9 Updated by Xavier Noria over 3 years ago

I wrote qualified_const_get in Active Support. The rationale for that name was: 1) I didn't want to touch const_get. const_get is supposed to raise an exception if the argument is not a valid constant name and "Foo::Bar" is not a valid constant name. I didn't want to change that expectation in such a fundamental method. And 2) I wanted a name that was obvious. Given that a Ruby programmer knows const_get, he will instantly know what qualified_const_get is going to do. In that sense I don't think this method is more abstract than const_get, it is in my view rather the natural companion of const_get.

That's for the current name. If this was going to be considered for Ruby 2.0 I think I'd prefer this behavior to be available in const_get itself.

#10 Updated by Thomas Sawyer over 3 years ago

I don't think the name is particularly obvious. Nevertheless, I am hopeful that it won't matter. I think const_get can accommodate. In fact, I think it can even be made to handle:

const_get('Foo', 'Bar::Baz') #=> Foo::Bar::Baz

It is backward compatible, after all.

#11 Updated by Thomas Sawyer over 3 years ago

@Xavier Let me "qualify" that ;-) I think your reasoning made very good sense for adding to ActiveSupport. But becoming part of Ruby, there is the opportunity to do it "right", as your last statement concurs.

#12 Updated by Marc-Andre Lafortune about 3 years ago

Is anyone producing a slide-show for this?

#13 Updated by Aaron Patterson about 3 years ago

  • File bug5690.pdf added

Adding a slide for this feature.

#14 Updated by Aaron Patterson about 3 years ago

  • File deleted (bug5690.pdf)

#15 Updated by Aaron Patterson about 3 years ago

updating the pdf

#16 Updated by Marc-Andre Lafortune about 3 years ago

Cool slide :-)

+1 from me, but modifying existing const_get. No incompatibility as "Foo::Bar" are currently invalid constant names. And const_set should be modified as well (raises a Name if the path up to the last constant name does not exist or is not a Module/Class).

#17 Updated by Thomas Sawyer about 3 years ago

I agree with marcandre. Why bother with an additional (and long named) method, when current method can suffice?

#18 Updated by Yusuke Endoh about 3 years ago

Aaron, your slide is received. Thank you.

marcandre (Marc-Andre Lafortune) wrote:

+1 from me, but modifying existing const_get.

I'll add it verbally during the presentation, if I recall.

Yusuke Endoh mame@tsg.ne.jp

#19 Updated by Anonymous about 3 years ago

On Mon, Jul 02, 2012 at 01:54:32AM +0900, mame (Yusuke Endoh) wrote:

Issue #5690 has been updated by mame (Yusuke Endoh).

Aaron, your slide is received. Thank you.

marcandre (Marc-Andre Lafortune) wrote:

+1 from me, but modifying existing const_get.

I'll add it verbally during the presentation, if I recall.

Thank you!

--
Aaron Patterson
http://tenderlovemaking.com/

#20 Updated by Koichi Sasada almost 3 years ago

  • Tracker changed from Bug to Feature

#21 Updated by Yusuke Endoh almost 3 years ago

  • Assignee changed from Yukihiro Matsumoto to Aaron Patterson

Yehuda Katz and Aaron Patterson,

I'm happy to inform you that matz has accepted your proposal,
as one to extend existing const_get' (notqualified_const_get').

Aaron, could you implement a patch?

Yusuke Endoh mame@tsg.ne.jp

#22 Updated by Clifford Heath almost 3 years ago

Aaron,

On 24/07/2012, at 4:14 AM, Aaron Patterson wrote:

On Mon, Jul 23, 2012 at 09:39:38PM +0900, mame (Yusuke Endoh) wrote:

Issue #5690 has been updated by mame (Yusuke Endoh).
Assignee changed from matz (Yukihiro Matsumoto) to tenderlovemaking (Aaron Patterson)
Yehuda Katz and Aaron Patterson,
I'm happy to inform you that matz has accepted your proposal,
as one to extend existing const_get' (notqualified_const_get').

Aaron, could you implement a patch?
Yes I will. Thanks! :-)

Please ensure that if the lookup fails, the exception indicates which part of the name caused the failure.

It's waaay past time that the industry moves past "ENOENT: No such file or directory" in its exception reporting :)

Thank-you.

Clifford Heath.

#23 Updated by Aaron Patterson over 2 years ago

I've written a patch this this feature, and I've attached it here. Will someone review so that we can figure out what needs to change before it's applied? Thank you.

#24 Updated by Koichi Sasada over 2 years ago

I got SEGV.
could you run "make test-rubyspec" on your environment?

/mnt/sdb1/ruby/trunk/spec/rubyspec/core/module/const_get_spec.rb:34: [BUG] Segmentation fault
ruby 2.0.0dev (2012-10-27 trunk 37336) [x86_64-linux]

-- Control frame information -----------------------------------------------
c:0031 p:---- s:0108 e:000107 CFUNC :const_get
c:0030 p:0038 s:0104 e:000103 BLOCK /mnt/sdb1/ruby/trunk/spec/rubyspec/core/module/const_get_spec.rb:34 [FINISH]
c:0029 p:---- s:0101 e:000100 CFUNC :instance_eval
c:0028 p:0014 s:0098 e:000097 METHOD /mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:69
c:0027 p:0017 s:0092 e:000091 BLOCK /mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/context.rb:179 [FINISH]
c:0026 p:---- s:0089 e:000088 IFUNC
c:0025 p:---- s:0087 e:000086 CFUNC :each
c:0024 p:---- s:0085 e:000084 CFUNC :all?
c:0023 p:0044 s:0082 e:000081 METHOD /mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/context.rb:179
c:0022 p:0077 s:0076 e:000075 BLOCK /mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/context.rb:208
c:0021 p:0004 s:0072 e:000071 BLOCK /mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:239 [FINISH]
c:0020 p:---- s:0070 e:000069 CFUNC :times
c:0019 p:0017 s:0067 e:000066 METHOD /mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:238
c:0018 p:0011 s:0064 e:000063 BLOCK /mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/context.rb:200 [FINISH]
c:0017 p:---- s:0061 e:000060 CFUNC :each
c:0016 p:0091 s:0058 e:000057 METHOD /mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/context.rb:199
c:0015 p:0087 s:0055 e:000054 METHOD /mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:38
c:0014 p:0030 s:0048 e:000047 METHOD /mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/object.rb:11
c:0013 p:0045 s:0041 e:000c28 TOP /mnt/sdb1/ruby/trunk/spec/rubyspec/core/module/const_get_spec.rb:4 [FINISH]
c:0012 p:---- s:0039 e:000038 CFUNC :load
c:0011 p:0014 s:0035 e:000034 BLOCK /mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:57 [FINISH]
c:0010 p:---- s:0033 e:000032 CFUNC :instance_eval
c:0009 p:0014 s:0030 e:000029 METHOD /mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:69
c:0008 p:0061 s:0024 e:0000e0 BLOCK /mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:57 [FINISH]
c:0007 p:---- s:0021 e:000020 CFUNC :each
c:0006 p:0042 s:0018 e:001450 METHOD /mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:51
c:0005 p:0015 s:0014 e:000013 METHOD /mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:43
c:0004 p:0047 s:0011 e:000010 METHOD /mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/commands/mspec-run.rb:91
c:0003 p:0077 s:0008 e:000007 METHOD /mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/utils/script.rb:218
c:0002 p:0047 s:0004 e:000ac8 EVAL /mnt/sdb1/ruby/trunk/spec/mspec/bin/mspec-run:8 [FINISH]
c:0001 p:0000 s:0002 e:0007f8 TOP [FINISH]

/mnt/sdb1/ruby/trunk/spec/mspec/bin/mspec-run:8:in <main>'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/utils/script.rb:218:in
main'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/commands/mspec-run.rb:91:in run'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:43:in
process'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:51:in files'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:51:in
each'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:57:in block in files'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:69:in
protect'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:69:in instance_eval'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:57:in
block (2 levels) in files'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:57:in load'
/mnt/sdb1/ruby/trunk/spec/rubyspec/core/module/const_get_spec.rb:4:in
'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/object.rb:11:in describe'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:38:in
describe'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/context.rb:199:in process'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/context.rb:199:in
each'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/context.rb:200:in block in process'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:238:in
repeat'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:238:in times'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:239:in
block in repeat'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/context.rb:208:in block (2 levels) in process'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/context.rb:179:in
protect'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/context.rb:179:in all?'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/context.rb:179:in
each'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/context.rb:179:in block in protect'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:69:in
protect'
/mnt/sdb1/ruby/trunk/spec/mspec/lib/mspec/runner/mspec.rb:69:in instance_eval'
/mnt/sdb1/ruby/trunk/spec/rubyspec/core/module/const_get_spec.rb:34:in
block (2 levels) in '
/mnt/sdb1/ruby/trunk/spec/rubyspec/core/module/const_get_spec.rb:34:in `const_get'

-- C level backtrace information -------------------------------------------
make: *** [test-rubyspec] セグメンテーション違反です

#25 Updated by Anonymous over 2 years ago

On Sat, Oct 27, 2012 at 09:11:51AM +0900, ko1 (Koichi Sasada) wrote:

Issue #5690 has been updated by ko1 (Koichi Sasada).

I got SEGV.
could you run "make test-rubyspec" on your environment?

I can't seem to run make test-rubyspec in my environment. It always
does this:

[aaron@higgins ruby (trunk)]$ make test-rubyspec
./miniruby -I./lib -I. -I.ext/common ./tool/runruby.rb --extout=.ext -- --disable-gems ./spec/mspec/bin/mspec run -B ./spec/default.mspec
ruby 2.0.0dev (2012-10-27 trunk 37339) [x86_64-darwin12.2.0]
/Users/aaron/git/ruby/spec/mspec/lib/mspec/guards/guard.rb:156:in block in os?': uninitialized constant SpecGuard::Config (NameError)
from /Users/aaron/git/ruby/spec/mspec/lib/mspec/guards/guard.rb:155:in
each'
from /Users/aaron/git/ruby/spec/mspec/lib/mspec/guards/guard.rb:155:in any?'
from /Users/aaron/git/ruby/spec/mspec/lib/mspec/guards/guard.rb:155:in
os?'
from /Users/aaron/git/ruby/spec/mspec/lib/mspec/guards/platform.rb:22:in `block in match?'

I'm not sure why. Anyway, I can run mspec by hand, and this should be
fixed in r37340.

--
Aaron Patterson
http://tenderlovemaking.com/

#26 Updated by Aaron Patterson over 2 years ago

  • Status changed from Assigned to Closed
  • % Done changed from 0 to 100

I committed this in r37340 and r37335

Also available in: Atom PDF