Ruby Issue Tracking System: Issueshttps://bugs.ruby-lang.org/https://bugs.ruby-lang.org/favicon.ico?17113305112014-01-26T17:55:58ZRuby Issue Tracking System
Redmine Ruby master - Feature #9453 (Rejected): Return symbols of defined methods for `attr` and friendshttps://bugs.ruby-lang.org/issues/94532014-01-26T17:55:58Zjballanc (Joshua Ballanco)jballanc@gmail.com
<p>With Ruby 2.1 returning a symbol from <code>def</code> and <code>define_method</code>, that leaves <code>attr</code>, <code>attr_reader</code>, <code>attr_writer</code>, and <code>attr_accessor</code> as ways to define methods that still return nil. This is unfortunate, because it prevents the use of method decorators developed to work with <code>def</code> from also working with the <code>attr*</code> methods. Because these mechanisms can define more than one method, the return values would need to be arrays of symbols.</p>
<p>For an example of how this could be useful in real-world code, consider this sample from James Edward Gray II's Warehouse Keeper example (<a href="https://github.com/JEG2/warehouse_keeper" class="external">https://github.com/JEG2/warehouse_keeper</a>):</p>
<pre><code>attr_reader :images, :key_map, :window, :screen_manager, :animations
private :images, :key_map, :window, :screen_manager, :animations
</code></pre>
<p>if <code>attr_reader</code> returned symbols, then this could be simplified to:</p>
<pre><code>private *attr_reader(:images, :key_map, :window, :screen_manager, :animations)
</code></pre>
<p>I've attached a patch that implements this change and includes a few tests. For those who use git, I've also submitted this as a pull request here: <a href="https://github.com/ruby/ruby/pull/517" class="external">https://github.com/ruby/ruby/pull/517</a></p> Ruby master - Feature #7747 (Open): Expanded API for Binding semanticshttps://bugs.ruby-lang.org/issues/77472013-01-28T08:08:12Zjballanc (Joshua Ballanco)jballanc@gmail.com
<p>=begin<br>
Currently, the only way to create a new instance of Binding is to get a copy of the current scope's binding, or ask some other object for its binding. In either case, the binding object returned always has semantics identical to the original binding. In other words, a binding object used with eval is capable of (not necessarily an exhaustive list):</p>
<ul>
<li>redefining methods on the binding target</li>
<li>defining new methods on the binding target</li>
<li>getting/setting instance variables</li>
<li>getting/creating new constants</li>
</ul>
<p>This feature proposal would introduce a new mechanism for creating a binding and adjusting its relationship with the source binding. For example, if you have a class (({Foo})) defined like so:</p>
<pre><code>class Foo
def say
puts "hello!"
end
end
</code></pre>
<p>Then you would be able to create a new binding that ((<em>won't</em>)) propagate new definitions to the parent binding like so:</p>
<pre><code>new_binding = Binding.new(Foo)
new_binding.propagate = false
new_binding.eval("def shout; puts 'HI!'; end")
Foo.new.say #=> "hello!"
Foo.new.shout #=> No Method Error
new_binding.eval("Foo.new.say") #=> "hello"
new_binding.eval("Foo.new.shout") #=> "HI!"
</code></pre>
<p>If, additionally, we introduce a way to merge or combine bindings, then this API could actually be used to implement refinements in pure Ruby like so (note I am also assuming that (({Binding#eval})) gains the ability to take a block):</p>
<pre><code>class Module
def refine(klass, &block)
@refined_bindings ||= {}
refinement_binding = Binding.new(klass)
refinement_binding.propagate = false
refinement_binding.shadow = true
refinement_binding.eval &block
(@refined_bindings[self.name] ||= []) << refinement_binding
end
def using(mod)
@refined_bindings[mod].each { |refinement| self.binding.merge(refinement) }
end
end
</code></pre>
<p>Following is the preliminary list of additional APIs I am tentatively proposing (though I expect this to change with additional discussion):</p>
<ul>
<li>(({Binding.new(an_object)})) - creates a new Binding object that "inherits" the binding of the argument; essentially equivalent to (({an_object.send(:binding).dup}))</li>
<li>(({Binding#propagate}))/(({Binding#propagate=})) - boolean; determines whether new method, class, or Constant defs are propagated into the "parent" binding's scope</li>
<li>(({Binding#shadow}))/(({Binding#shadow=})) - boolean; sets whether or not new values for existing methods/constants/variables can be set</li>
<li>(({Binding#freeze})) - causes the Binding to capture all existing methods/constants/variables in scope at call time, and "disconnect" from the parent binding, so that any updates to definitions or values are no longer reflected into the "child" binding</li>
<li>(({Binding#merge(other_binding)})) - combines the method/constant/variable bindings in (({other_binding})) with the receiver; effectively shadows the "parent" of (({other_binding})) within the receiver binding<br>
=end</li>
</ul> Ruby master - Feature #7702 (Open): Remove Proc#bindinghttps://bugs.ruby-lang.org/issues/77022013-01-16T09:09:14Zjballanc (Joshua Ballanco)jballanc@gmail.com
<p>=begin<br>
As discussed in the most recent Ruby implementer's meeting and elsewhere over the years, being able to get a handle to the binding of any block/proc has a number of problems:</p>
<ul>
<li>Code execution after passing a block/proc in unpredictable, as the binding of said proc can be used to redefine any local, instance variable, method, class, module, etc.</li>
<li>Potentially sensitive data can leak out of the scope of a method via a proc binding</li>
<li>Large object graphs may need to be retained for the lifetime of a proc, since any identifier in scope at the time of proc creation must remain live in the event that the binding of the proc is used to evaluate code</li>
</ul>
<p>Additionally, removal of Proc#binding would enable a number of very useful optimizations and performance improvements.<br>
=end</p> Ruby master - Bug #7522 (Closed): Non-core "Type()" Kernel methods return new objectshttps://bugs.ruby-lang.org/issues/75222012-12-06T06:17:50Zjballanc (Joshua Ballanco)jballanc@gmail.com
<p>The methods Array(), String(), Float(), Integer(), Hash(), and Rational() all return their argument when the argument is already an instance of the type in question. For example:</p>
<p>a = []<br>
a.equal? Array(a) #=> true</p>
<p>However, the similar methods Pathname(), BigDecimal(), and Complex() do not do this:</p>
<p>p = Pathname.new('/tmp')<br>
p.equal? Pathname(p) #=> false</p>
<p>I had the impression that the "Type()" methods were intended as "safe" coercion methods. That is, if no type conversion is required, then the system is left unchanged (and no new objects are created). The attached patch fixes the three methods mentioned above to adhere to this same invariant.</p> Backport193 - Backport #7353 (Closed): Backport #6171https://bugs.ruby-lang.org/issues/73532012-11-15T10:14:46Zjballanc (Joshua Ballanco)jballanc@gmail.com
<p>Please backport r35080 to fix <a class="issue tracker-4 status-5 priority-4 priority-default closed" title="Backport: Segfault in rb_free_method_entry (Closed)" href="https://bugs.ruby-lang.org/issues/6171">#6171</a> on 1.9.3.</p> Ruby master - Bug #7342 (Closed): String#<=> checks for a #to_str method on other but never uses it?https://bugs.ruby-lang.org/issues/73422012-11-13T11:24:05Zjballanc (Joshua Ballanco)jballanc@gmail.com
<p>=begin<br>
This isn't exactly a bug, as much as a request for clarification. I was looking at the semantics of the (({<=>})) operator and noticed something curious. For most classes, when evaluating (({thing <=> other})), if (({other})) is not of a compatible type, then (({nil})) is returned.</p>
<p>The exceptions (as far as I can find) are String and Time. For the Time class, if (({other})) is not a kind of (({Time})), then the reverse comparison (({other <=> thing})) is tried and the inverse of this result is returned (if not nil). For String, the reverse comparison is only tried IF (({other.respond_to?(:to_str)})), HOWEVER the referenced (({other.to_str})) method is never called. For example:</p>
<pre><code>class NotAString
def <=>(other)
1
end
def to_str
raise "I'm not a string!"
end
end
"test" <=> NotAString.new #=> -1
</code></pre>
<p>This seems very counterintuitive to me. I would expect that if my class implemented (({to_str})), that the return value of this would be used for comparison.<br>
=end</p> Ruby master - Feature #6253 (Rejected): Implement a way to pass keyword options to curried procshttps://bugs.ruby-lang.org/issues/62532012-04-04T14:27:11Zjballanc (Joshua Ballanco)jballanc@gmail.com
<p>=begin<br>
(See original discussion in issue <a class="issue tracker-2 status-6 priority-4 priority-default closed" title="Feature: Proc#curry behavior is inconsistent with lambdas containing default argument values (Rejected)" href="https://bugs.ruby-lang.org/issues/4610">#4610</a>)</p>
<p>With the introduction of keyword arguments in Ruby 2.0, it would be useful to have a way to set a keyword-based option on a curried proc. The example below demonstrates a Rack-like system where a curried proc is passed to a helper, then after partial application of the arguments, the returned curried proc is stored and later evaluated:</p>
<pre><code>class NicknameTranslator
def initialize(app)
@app = app
end
def call(name)
case name
when 'Robert'
@app.call('Bob')
when 'James'
@app.call('Jimmy')
else
@app.call(name)
end
end
end
class NightGreeting
def initialize(app)
@app = app
@app.pass_option(greeting: 'Goodnight')
end
def call(name)
@app.call(name)
end
end
class Greeter
def initialize(helper)
@helper = helper
@app = lambda do |sender, receiver, greeting: 'Hello'|
puts "#{sender} says \"#{greeting}\" to #{receiver}"
end.curry
@greetings = {}
end
def call(sender, receiver)
@greetings[sender] ||= @helper.new(@app).call(sender)
@greetings[sender].call(receiver)
end
end
Greeter.new(NicknameTranslator).call('Josh', 'Joe')
Greeter.new(NicknameTranslator).call('Robert', 'Joe')
Greeter.new(NicknameTranslator).call('Josh', 'Robert')
</code></pre>
<p>If we wanted, however, to be able to set a keyword-based option in the helper, there is currently no way in Ruby 2.0 to do so. Currently, keyword arguments can only be used at the same time as the final non-keyword, non-default, non-rest argument to the proc is applied. So, for example, there is no way to do the above with NightGreeting in place of NicknameTranslator.</p>
<p>Currying is really only useful when it can be used with partial application. However, Ruby currently limits how you can achieve partial application of curried procs. In particular, there is no way to manage partial application of parameters with default values. As such, it is not surprising that Proc#curry does not seem to have been adopted very widely. In my personal survey of ~600 gems that I use in various projects, I did not find any usage of Proc#curry.</p>
<p>So, I would request a method like Proc#pass_option (or some other, better name) that allows for setting keyword arguments on a curried proc at any time.<br>
=end</p> Backport193 - Backport #6171 (Closed): Segfault in rb_free_method_entryhttps://bugs.ruby-lang.org/issues/61712012-03-18T10:38:57Zjballanc (Joshua Ballanco)jballanc@gmail.com
<p>=begin<br>
Running the following script in both Ruby 1.9.3p125 and trunk causes a segfault:</p>
<pre><code>class Bug
def initialize(target)
define_singleton_method(:reverse, target.method(:reverse).to_proc)
end
end
1000.times { p = Bug.new('test'); 10000.times { p.reverse } }
</code></pre>
<p>and the corresponding backtrace:</p>
<pre><code>(gdb) bt
#0 0x00007fff9337a6c1 in tiny_free_list_remove_ptr ()
#1 0x00007fff9337e55d in szone_free_definite_size ()
#2 0x00007fff933b7789 in free ()
#3 0x000000010007373c in vm_xfree (objspace=0x10081a800, ptr=0x100460470) at gc.c:880
#4 0x0000000100073ae6 in ruby_xfree (x=0x100460470) at gc.c:944
#5 0x00000001002079f1 in rb_free_method_entry (me=0x100460470) at vm_method.c:157
#6 0x0000000100207920 in rb_sweep_method_entry (pvm=0x100401780) at vm_method.c:127
#7 0x0000000100077abd in before_gc_sweep (objspace=0x10081a800) at gc.c:2296
#8 0x00000001000781f5 in gc_lazy_sweep (objspace=0x10081a800) at gc.c:2385
#9 0x0000000100074b63 in rb_newobj () at gc.c:1324
#10 0x00000001000066c1 in ary_alloc (klass=4304249320) at array.c:301
#11 0x0000000100006869 in ary_new (klass=4304249320, capa=0) at array.c:320
#12 0x0000000100006955 in rb_ary_new2 (capa=0) at array.c:334
#13 0x0000000100006cbf in rb_ary_new4 (n=0, elts=0x7fff5fbfa2e0) at array.c:370
#14 0x00000001001f6350 in vm_yield_with_cfunc (th=0x100401b60, block=0x100499f00, self=4304013680, argc=0, argv=0x7fff5fbfa2e0, blockargptr=0x0) at vm_insnhelper.c:763
#15 0x00000001002126b5 in invoke_block_from_c (th=0x100401b60, block=0x100499f00, self=4304013680, argc=0, argv=0x7fff5fbfa2e0, blockptr=0x0, cref=0x0) at vm.c:609
#16 0x0000000100212844 in rb_vm_invoke_proc (th=0x100401b60, proc=0x100499f00, self=4304013680, argc=0, argv=0x7fff5fbfa2e0, blockptr=0x0) at vm.c:652
#17 0x000000010020680a in vm_call_bmethod (th=0x100401b60, recv=4304013680, argc=0, argv=0x7fff5fbfa2e0, blockptr=0x0, me=0x100499f80) at vm_insnhelper.c:479
#18 0x000000010020524e in vm_call_method (th=0x100401b60, cfp=0x1006ffce8, num=0, blockptr=0x0, flag=0, id=2112, me=0x100499f80, recv=4304013680) at vm_insnhelper.c:608
#19 0x00000001001fd465 in vm_exec_core (th=0x100401b60, initial=0) at insns.def:1018
#20 0x00000001002143eb in vm_exec (th=0x100401b60) at vm.c:1223
#21 0x0000000100212662 in invoke_block_from_c (th=0x100401b60, block=0x1006ffe18, self=4304315600, argc=1, argv=0x7fff5fbfbbb8, blockptr=0x0, cref=0x0) at vm.c:606
#22 0x0000000100212730 in vm_yield (th=0x100401b60, argc=1, argv=0x7fff5fbfbbb8) at vm.c:636
#23 0x000000010020daec in rb_yield_0 (argc=1, argv=0x7fff5fbfbbb8) at vm_eval.c:780
#24 0x000000010020daa8 in rb_yield (val=13317) at vm_eval.c:790
#25 0x00000001000c8a8a in int_dotimes (num=20001) at numeric.c:3410
#26 0x0000000100206c28 in call_cfunc (func=0x1000c89e0 <int_dotimes>, recv=20001, len=0, argc=0, argv=0x100600078) at vm_insnhelper.c:370
#27 0x000000010020666c in vm_call_cfunc (th=0x100401b60, reg_cfp=0x1006ffdf0, num=0, recv=20001, blockptr=0x1006ffe18, me=0x100426c00) at vm_insnhelper.c:454
#28 0x0000000100204dfe in vm_call_method (th=0x100401b60, cfp=0x1006ffdf0, num=0, blockptr=0x1006ffe18, flag=0, id=3376, me=0x100426c00, recv=20001) at vm_insnhelper.c:580
#29 0x00000001001fd465 in vm_exec_core (th=0x100401b60, initial=0) at insns.def:1018
#30 0x00000001002143eb in vm_exec (th=0x100401b60) at vm.c:1223
#31 0x0000000100212662 in invoke_block_from_c (th=0x100401b60, block=0x1006fff20, self=4304315600, argc=1, argv=0x7fff5fbfd808, blockptr=0x0, cref=0x0) at vm.c:606
#32 0x0000000100212730 in vm_yield (th=0x100401b60, argc=1, argv=0x7fff5fbfd808) at vm.c:636
#33 0x000000010020daec in rb_yield_0 (argc=1, argv=0x7fff5fbfd808) at vm_eval.c:780
#34 0x000000010020daa8 in rb_yield (val=11) at vm_eval.c:790
#35 0x00000001000c8a8a in int_dotimes (num=2001) at numeric.c:3410
#36 0x0000000100206c28 in call_cfunc (func=0x1000c89e0 <int_dotimes>, recv=2001, len=0, argc=0, argv=0x100600038) at vm_insnhelper.c:370
#37 0x000000010020666c in vm_call_cfunc (th=0x100401b60, reg_cfp=0x1006ffef8, num=0, recv=2001, blockptr=0x1006fff20, me=0x100426c00) at vm_insnhelper.c:454
#38 0x0000000100204dfe in vm_call_method (th=0x100401b60, cfp=0x1006ffef8, num=0, blockptr=0x1006fff20, flag=0, id=3376, me=0x100426c00, recv=2001) at vm_insnhelper.c:580
#39 0x00000001001fd465 in vm_exec_core (th=0x100401b60, initial=0) at insns.def:1018
#40 0x00000001002143eb in vm_exec (th=0x100401b60) at vm.c:1223
#41 0x0000000100215106 in rb_iseq_eval_main (iseqval=4304147120) at vm.c:1463
#42 0x0000000100059e4a in ruby_exec_internal (n=0x1008c12b0) at eval.c:204
#43 0x0000000100059fc4 in ruby_exec_node (n=0x1008c12b0) at eval.c:251
#44 0x0000000100059f76 in ruby_run_node (n=0x1008c12b0) at eval.c:244
#45 0x00000001000008d2 in main (argc=2, argv=0x7fff5fbff4a0) at main.c:38
</code></pre>
<p>Running also occasionally results in the following error:</p>
<pre><code>malloc: *** error for object 0x7fe658c8e9c0: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
</code></pre>
<p>This is on OS X 10.7.3. I've tried compiling with gcc and clang, and get the same results (also the crash occurs at both -O3 and -O0).<br>
=end</p> Ruby master - Bug #5544 (Closed): Lookup scope for class variables in class_eval'd procs changed ...https://bugs.ruby-lang.org/issues/55442011-11-02T05:45:11Zjballanc (Joshua Ballanco)jballanc@gmail.com
<p>When using class_eval with a proc, references to class variables scoped to the receiver in 1.9.2, but in 1.9.3 class variables are now lexically scoped to the environment of the proc. For example, this code:</p>
<pre><code>class Foo
def initialize
@@value = "Set from Foo initialize"
end
def report
@@value
end
end
class Bar
def initialize
@@value = "Set from Bar initialize"
end
def report
@@value
end
def monkey
Foo.class_eval do
def set(value)
@@value = value
end
end
end
end
b = Bar.new
b.monkey
f = Foo.new
puts "Before monkeying:"
puts "Bar's class var: #{b.report}"
puts "Foo's class var: #{f.report}"
f.set("Set through Monkey")
puts "After monkeying:"
puts "Bar's class var: #{b.report}"
puts "Foo's class var: #{f.report}"
</code></pre>
<p>Running under 1.9.2-p290:</p>
<pre><code>Before monkeying:
Bar's class var: Set from Bar initialize
Foo's class var: Set from Foo initialize
After monkeying:
Bar's class var: Set from Bar initialize
Foo's class var: Set through Monkey
</code></pre>
<p>Running under 1.9.3-p0:</p>
<pre><code>Before monkeying:
Bar's class var: Set from Bar initialize
Foo's class var: Set from Foo initialize
After monkeying:
Bar's class var: Set through Monkey
Foo's class var: Set from Foo initialize
</code></pre> Ruby master - Bug #5542 (Rejected): Ruby 1.9.3-p0 changed arity on default initialization methodhttps://bugs.ruby-lang.org/issues/55422011-11-02T05:32:56Zjballanc (Joshua Ballanco)jballanc@gmail.com
<p>The following code worked under 1.9.2-p290 but breaks with an Argument Error (1 for 0) under 1.9.3-p0:</p>
<pre><code>class Foo; end
Foo.new(nil)
</code></pre>
<p>Furthermore, the reported arity for Object's initialize method has changed:</p>
<pre><code>ruby-1.9.2-p290 :001 > Object.instance_method(:initialize).arity
=> -1
ruby-1.9.3-p0 :001 > Object.instance_method(:initialize).arity
=> 0
</code></pre> Ruby master - Bug #5540 (Rejected): Final comma in argument list before block causes syntax errorhttps://bugs.ruby-lang.org/issues/55402011-11-02T05:24:54Zjballanc (Joshua Ballanco)jballanc@gmail.com
<p>The following code parses and runs correctly under 1.9.2-p290 but causes a syntax error under 1.9.3-p0:</p>
<pre><code>def test(foo, &block); end
test 'hello', do; end
</code></pre> Ruby master - Feature #4610 (Rejected): Proc#curry behavior is inconsistent with lambdas containi...https://bugs.ruby-lang.org/issues/46102011-04-25T23:56:43Zjballanc (Joshua Ballanco)jballanc@gmail.com
<p>If I curry a lambda with 3 arguments, then I can call three times with one argument each time to get the desired results:</p>
<p>ruby-1.9.2-p180 :001 > l = ->(a, b, c) { puts "#{a}, #{b}, #{c}" }<br>
#<Proc:0x00000100963650@(irb):1 (lambda)><br>
ruby-1.9.2-p180 :002 > c = l.curry<br>
#<Proc:0x0000010095c9e0 (lambda)><br>
ruby-1.9.2-p180 :003 > c.('one').('two').('three')<br>
one, two, three<br>
nil</p>
<p>However, if the lambda has default values and I curry it, the entire lambda is evaluated after the first #call:</p>
<p>ruby-1.9.2-p180 :004 > l = ->(a = 'ichi', b = 'ni', c = 'san') { puts "#{a}, #{b}, #{c}" }<br>
#<Proc:0x00000100877b88@(irb):4 (lambda)><br>
ruby-1.9.2-p180 :005 > c = l.curry<br>
#<Proc:0x0000010086b338 (lambda)><br>
ruby-1.9.2-p180 :006 > c.('one').('two').('three')<br>
one, ni, san<br>
NoMethodError: undefined method `call' for nil:NilClass</p>
<p>This behavior seem very inconsistent. Ideally, if I wanted to use the default argument at a certain position in a currie proc, I would just #call with no arguments, like so:</p>
<p>ruby-1.9.2-p180 :007 > c.('one').().('three')<br>
#=> Propose that this result in: "one, ni, three"</p>