Skip to content
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

Segfault when binding command to a method #63

Closed
rongcuid opened this issue Jan 8, 2025 · 19 comments
Closed

Segfault when binding command to a method #63

rongcuid opened this issue Jan 8, 2025 · 19 comments

Comments

@rongcuid
Copy link

rongcuid commented Jan 8, 2025

The following is modified from the TkDocs feet to meter example, using OOP code:

require "tk"
require "tkextlib/tile"

class App < Tk::Tile::Frame
  def initialize(foo, *args)
    super(*args) { padding "3 3 12 12" }
    grid(sticky: "nsew")

    @feet = TkVariable.new
    @meters = TkVariable.new
    @f = Tk::Tile::Entry.new(self) {
      width 7
      textvariable @feet
    }.grid(column: 2, row: 1, sticky: "we")

    Tk::Tile::Label.new(self) { textvariable @meters }.grid(column: 2, row: 2, sticky: "we")
    Tk::Tile::Button.new(self) {
      text "Calculate"
      command { calculate }
    }.grid(column: 3, row: 3, sticky: "w")

    Tk::Tile::Label.new(self) { text "feet" }.grid(column: 3, row: 1, sticky: "w")
    Tk::Tile::Label.new(self) { text "is equivalent to" }.grid(column: 1, row: 2, sticky: "e")
    Tk::Tile::Label.new(self) { text "meters" }.grid(column: 3, row: 2, sticky: "w")

    TkWinfo.children(self).each { |w| TkGrid.configure w, padx: 5, pady: 5 }
    @f.focus
    root.bind("Return") { calculate }
  end

  def calculate
    @meters.value = (0.3048 * @feet * 10000.0).round / 10000.0
  rescue
    @meters.value = ""
  end
end

root = TkRoot.new { title "Feet to Meters" }
content = App.new(42, root)
TkGrid.columnconfigure root, 0, weight: 1
TkGrid.rowconfigure root, 0, weight: 1

Tk.mainloop

On command execution, Ruby crashes due to a segfault.

  • OS: Mac OS 15.2 (24C101), M2
  • Ruby version: 3.4.1
  • Ruby tk: 0.5.0
  • Tcl/tk: 8.6

Crash log:
crash.log

@jeremyevans
Copy link
Contributor

Can you see if #60 fixes this?

@rongcuid
Copy link
Author

rongcuid commented Jan 8, 2025

How do I test this?

@rongcuid
Copy link
Author

rongcuid commented Jan 8, 2025

Having some problem building it:

Fetching https://github.com/jeremyevans/tk
Fetching gem metadata from https://rubygems.org/.........
Resolving dependencies...
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    current directory: /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/ext/tk
/Users/rongcuid/.rbenv/versions/3.4.1/bin/ruby extconf.rb
check functions.checking for ruby_native_thread_p() in ruby.h... *** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
        --with-opt-dir
        --without-opt-dir
        --with-opt-include=${opt-dir}/include
        --without-opt-include
        --with-opt-lib=${opt-dir}/lib
        --without-opt-lib
        --with-make-prog
        --without-make-prog
        --srcdir=.
        --curdir
        --ruby=/Users/rongcuid/.rbenv/versions/3.4.1/bin/$(RUBY_BASE_NAME)
        --enable-shared
        --with-tk-old-extconf
        --without-tk-old-extconf
        --with-tk-old-extconf
        --without-tk-old-extconf
        --with-ActiveTcl
        --without-ActiveTcl
        --with-tk-shlib-search-path
        --without-tk-shlib-search-path
        --with-tcltkversion
        --without-tcltkversion
        --with-tcl-build-dir
        --without-tcl-build-dir
        --with-tk-build-dir
        --without-tk-build-dir
        --with-tcl-config
        --without-tcl-config
        --with-tk-config
        --without-tk-config
        --with-tclConfig-dir
        --without-tclConfig-dir
        --with-tkConfig-dir
        --without-tkConfig-dir
        --with-tclConfig-file
        --without-tclConfig-file
        --with-tkConfig-file
        --without-tkConfig-file
        --with-tcllib
        --without-tcllib
        --with-tklib
        --without-tklib
        --with-tcl-dir
        --without-tcl-dir
        --with-tk-dir
        --without-tk-dir
        --with-tcl-include
        --without-tcl-include
        --with-tk-include
        --without-tk-include
        --with-tcl-lib
        --without-tcl-lib
        --with-tk-lib
        --without-tk-lib
        --with-tcltk-framework
        --without-tcltk-framework
        --with-tcl-framework-dir
        --without-tcl-framework-dir
        --with-tk-framework-dir
        --without-tk-framework-dir
        --with-tcl-framework-header
        --without-tcl-framework-header
        --with-tk-framework-header
        --without-tk-framework-header
        --with-X11
        --without-X11
        --with-X11-dir
        --without-X11-dir
        --with-X11-include
        --without-X11-include
        --with-X11-lib
        --without-X11-lib
        --enable-tcltk-stubs
        --disable-tcltk-stubs
        --enable-tcl-h-ver-check
        --disable-tcl-h-ver-check
        --enable-tk-h-ver-check
        --disable-tk-h-ver-check
        --enable-mac-tcltk-framework
        --disable-mac-tcltk-framework
        --enable-tcltk-framework
        --disable-tcltk-framework
        --enable-pthread
        --disable-pthread
        --enable-tcl-thread
        --disable-tcl-thread
        --enable-space-on-tk-libpath
        --disable-space-on-tk-libpath
/Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/mkmf.rb:528:in 'MakeMakefile#try_do': The compiler failed to generate an executable file. (RuntimeError)
You have to install development tools first.

        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/mkmf.rb:621:in 'MakeMakefile#try_link0'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/mkmf.rb:639:in 'MakeMakefile#try_link'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/mkmf.rb:881:in 'MakeMakefile#try_func'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/mkmf.rb:1201:in 'block in MakeMakefile#have_func'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/mkmf.rb:1070:in 'block in MakeMakefile#checking_for'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/mkmf.rb:370:in 'block (2 levels) in MakeMakefile::Logging.postpone'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/mkmf.rb:340:in 'MakeMakefile::Logging.open'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/mkmf.rb:370:in 'block in MakeMakefile::Logging.postpone'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/mkmf.rb:340:in 'MakeMakefile::Logging.open'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/mkmf.rb:366:in 'MakeMakefile::Logging.postpone'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/mkmf.rb:1069:in 'MakeMakefile#checking_for'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/mkmf.rb:1200:in 'MakeMakefile#have_func'
        from extconf.rb:1820:in 'block in <main>'
        from extconf.rb:1819:in 'Array#each'
        from extconf.rb:1819:in '<main>'

To see why this extension failed to compile, please check the mkmf.log which can be found here:

  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/extensions/arm64-darwin-23/3.4.0/tk-492b8f622de5/mkmf.log

extconf failed, exit code 1

Gem files will remain installed in /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5 for inspection.
Results logged to /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/extensions/arm64-darwin-23/3.4.0/tk-492b8f622de5/gem_make.out

  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/rubygems/ext/builder.rb:126:in 'Gem::Ext::Builder.run'
  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/rubygems/ext/ext_conf_builder.rb:30:in 'Gem::Ext::ExtConfBuilder.build'
  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/rubygems/ext/builder.rb:195:in 'Gem::Ext::Builder#build_extension'
  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/rubygems/ext/builder.rb:229:in 'block in Gem::Ext::Builder#build_extensions'
  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/rubygems/ext/builder.rb:226:in 'Array#each'
  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/rubygems/ext/builder.rb:226:in 'Gem::Ext::Builder#build_extensions'
  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/rubygems/installer.rb:844:in 'Gem::Installer#build_extensions'
  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/bundler/rubygems_gem_installer.rb:111:in 'Bundler::RubyGemsGemInstaller#build_extensions'
  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/bundler/source/path/installer.rb:28:in 'Bundler::Source::Path::Installer#post_install'
  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/bundler/source/path.rb:234:in 'Bundler::Source::Path#generate_bin'
  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/bundler/source/git.rb:212:in 'Bundler::Source::Git#install'
  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/bundler/installer/gem_installer.rb:55:in 'Bundler::GemInstaller#install'
  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/bundler/installer/gem_installer.rb:17:in 'Bundler::GemInstaller#install_from_spec'
  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/bundler/installer/parallel_installer.rb:133:in 'Bundler::ParallelInstaller#do_install'
  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/bundler/installer/parallel_installer.rb:124:in 'block in Bundler::ParallelInstaller#worker_pool'
  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/bundler/worker.rb:62:in 'Bundler::Worker#apply_func'
  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/bundler/worker.rb:57:in 'block in Bundler::Worker#process_queue'
  <internal:kernel>:168:in 'Kernel#loop'
  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/bundler/worker.rb:54:in 'Bundler::Worker#process_queue'
  /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/3.4.0/bundler/worker.rb:90:in 'block (2 levels) in Bundler::Worker#create_threads'

An error occurred while installing tk (0.5.0), and Bundler cannot continue.

In Gemfile:
  tk

@jeremyevans
Copy link
Contributor

Check out the repository, apply the patch from the pull request, build the extension, then test:

git clone https://github.com/ruby/tk.git
cd tk
git pull https://github.com/jeremyevans/tk.git fix-ruby-3.4
rake compile # may need -- --with-tcl-*/--with-tk-* options depending on platform
ruby -I lib /path/to/your/file.rb

@rongcuid
Copy link
Author

rongcuid commented Jan 8, 2025

I can't build it:

/Users/rongcuid/.rbenv/versions/3.4.1/include/ruby-3.4.0/ruby/internal/stdckdint.h:48:11: error: 'stdckdint.h' file not found with <angled> include; use "quotes
" instead
   48 | # include <stdckdint.h>
      |           ^~~~~~~~~~~~~
      |           "stdckdint.h"
1 error generated.
checked program was:
/* begin */
1: #include "ruby.h"
2:
3: int main(int argc, char **argv)
4: {
5:   return !!argv[argc];
6: }
/* end */

Isn't this a C23 feature? Why is this required?

@jeremyevans
Copy link
Contributor

Not sure. That doesn't show up in my mkmf.log, but I'm not on a Mac. The stdckdint string doesn't show up in the ruby/tk repository, so it may be coming from Ruby itself.

Maybe instead of building from the repo, you could try building the gem and installing it:

# After applying the patch
gem build tk.gemspec
gem install --local tk-0.5.0.gem

@rongcuid
Copy link
Author

rongcuid commented Jan 8, 2025

Ok, the compile error was caused by my Ruby which was compiled under Mac 10.14, and after I rebuilt it the gem compiles.

However, now when I press "Calculate", some weird exception/error dialog box throw:

2025-01-07 20:27:25.028 ruby[55735:15270071] +[IMKClient subclass]: chose IMKClient_Modern
2025-01-07 20:27:25.028 ruby[55735:15270071] +[IMKInputSession subclass]: chose IMKInputSession_Modern
2025-01-07 20:27:44.341 ruby[55735:15270071] TSM AdjustCapsLockLEDForKeyTransitionHandling - _ISSetPhysicalKeyboardCapsLockLED Inhibit
/Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:4854:in 'TkObject#method_missing': NameError: unknown option 'calculate' for #<Tk::Tile::TButton:0x000000011ff5c060 @path=".w00000.w00003"> (deleted widget?) (NameError)

---< backtrace of Ruby side >-----
/Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:4854:in 'TkObject#method_missing'
test.rb:19:in 'block (2 levels) in App#initialize'
/Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1384:in 'TkUtil.eval_cmd'
/Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1384:in 'cb_eval'
/Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1339:in 'call'
/Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1536:in 'block in TkCore.callback'
/Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1535:in 'Kernel#catch'
/Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1535:in 'TkCore.callback'
/Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1752:in 'TclTkLib.mainloop'
/Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1752:in 'TkCore#mainloop'
test.rb:43:in '<main>'
---< backtrace of Tk side >-------

          fail NameError,
          ^^^^
        from test.rb:19:in 'block (2 levels) in App#initialize'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1384:in 'TkUtil.eval_cmd'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1384:in 'cb_eval'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1339:in 'call'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1536:in 'block in TkCore.callback'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1535:in 'Kernel#catch'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1535:in 'TkCore.callback'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1752:in 'TclTkLib.mainloop'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1752:in 'TkCore#mainloop'
        from test.rb:43:in '<main>'
/Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1932:in 'TclTkIp#_invoke': unknown option "-calculate" (RuntimeError)
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1932:in 'TkCore#_ip_invoke_core'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1967:in 'TkCore#_tk_call_core'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1994:in 'TkCore#tk_call_without_enc'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:3828:in 'TkConfigMethod#__cget_core'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:3835:in 'TkConfigMethod#cget'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:4851:in 'TkObject#method_missing'
        from test.rb:19:in 'block (2 levels) in App#initialize'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1384:in 'TkUtil.eval_cmd'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1384:in 'cb_eval'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1339:in 'call'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1536:in 'block in TkCore.callback'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1535:in 'Kernel#catch'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1535:in 'TkCore.callback'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1752:in 'TclTkLib.mainloop'
        from /Users/rongcuid/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/tk-492b8f622de5/lib/tk.rb:1752:in 'TkCore#mainloop'
        from test.rb:43:in '<main>'

@rongcuid
Copy link
Author

rongcuid commented Jan 8, 2025

Looks like command { calculate } gets translated to -calculate, which is invalid Tk command?

@jeremyevans
Copy link
Contributor

In App#initialize, try app = self, then switch the calculate calls to app.calculate. Not sure if that works, but considering you are getting a NameError, it might.

@rongcuid
Copy link
Author

rongcuid commented Jan 8, 2025

It doesn't work. I don't think it's a name error from Ruby, but one from Tk.

@jeremyevans
Copy link
Contributor

Well, at least we got some more evidence that #60 is needed on Ruby 3.4. I'll merge that. I'm not sure whether you tested the application on an earlier version of Ruby, but if you can do that, it would be helpful.

@rongcuid
Copy link
Author

rongcuid commented Jan 8, 2025

This happens on 3.3.6 as well. I just tested. Maybe I can't just translate directly from the TkDocs example?

https://tkdocs.com/tutorial/firstexample.html

@jeremyevans
Copy link
Contributor

Here's a working example:

require "tk"
require "tkextlib/tile"

class App < Tk::Tile::Frame
  def initialize(foo, *args)
    super(*args) { padding "3 3 12 12" }
    grid(sticky: "nsew")
    app = self

    feet = @feet = TkVariable.new
    meters = @meters = TkVariable.new
    @f = Tk::Tile::Entry.new(self) {
      width 7
      textvariable feet
    }.grid(column: 2, row: 1, sticky: "we")

    Tk::Tile::Label.new(self) { textvariable meters }.grid(column: 2, row: 2, sticky: "we")
    Tk::Tile::Button.new(self) {
      text "Calculate"
      command { app.calculate }
    }.grid(column: 3, row: 3, sticky: "w")

    Tk::Tile::Label.new(self) { text "feet" }.grid(column: 3, row: 1, sticky: "w")
    Tk::Tile::Label.new(self) { text "is equivalent to" }.grid(column: 1, row: 2, sticky: "e")
    Tk::Tile::Label.new(self) { text "meters" }.grid(column: 3, row: 2, sticky: "w")

    TkWinfo.children(self).each { |w| TkGrid.configure w, padx: 5, pady: 5 }
    @f.focus
    root.bind("Return") { app.calculate }
  end

  def calculate
    @meters.value = (0.3048 * @feet.value.to_i * 10000.0).round / 10000.0
  rescue
    @meters.value = ""
  end
end

root = TkRoot.new { title "Feet to Meters" }
content = App.new(42, root)
TkGrid.columnconfigure root, 0, weight: 1
TkGrid.rowconfigure root, 0, weight: 1

Tk.mainloop

You need to be careful regarding scope (for instance variables and methods), since most of the blocks you are passing change the scope.

@rongcuid
Copy link
Author

rongcuid commented Jan 8, 2025

You are right, but shouldn't self be automatically captured?

@rongcuid
Copy link
Author

rongcuid commented Jan 8, 2025

Ok, I got it. self is changed in that block

@jeremyevans
Copy link
Contributor

I'm not sure what you mean by "automatically captured". By default, blocks do not change scope, but in Ruby, they can, and I believe ruby-tk uses that extensively. Blocks you pass to new in ruby-tk are evaluated in the scope of the created object. They have to work that way, as otherwise, code such as:

    Tk::Tile::Button.new(self) {
      text "Calculate"
      command { app.calculate }
    }.grid(column: 3, row: 3, sticky: "w")

Would have text and command called on the App instance, instead of the Tk::Tile::Button you are creating. Since the self in that block is the Tk::Tile::Button, you need to specify the receiver for the calculate method.

@jeremyevans
Copy link
Contributor

I'm going to close this, since the issue was fixed as #60 was merged. I'll try to put out a 0.5.1 release later this week.

If you have additional questions, it's fine to ask them here.

@rongcuid
Copy link
Author

rongcuid commented Jan 8, 2025

Thanks for your help. Yeah, I didn't really thought about how Ruby DSLs worked despite using a few of them.

There are very few recent resources on how to use Ruby Tk, so I'm not very sure when it's a bug and when it's me making a mistake.

In fact, is it even a good idea to subclass directly instead of just putting a Frame in a variable? I was thinking that subclass allows me to use the layout commands directly on the App instance

@jeremyevans
Copy link
Contributor

I agree that there aren't a lot of recent resources on ruby-tk. I think I learned from https://ruby-doc.com/docs/ProgrammingRuby/html/ext_tk.html

I'm not sure I can opine on what constitutes good design with ruby-tk. I don't have that much experience with it, and no experience with it in large production applications. However, subclassing Frame as you did to add your own methods seems fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants