Rails 4 Engine: Method on Eigenclass of Module not found - ruby

I have updated my mountable engine from Ruby 1.9.3 / Rails 3.2 to Ruby 2.0.0 / Rails 4. Now I am having a strange problem with my namespaced models. It can't find the correct table name. I've figured that the reason for this is a problem with the tabele_name_prefix method on the parent module. It can't be found.
module DynaState::AttributeAccessors
def self.table_name_prefix
'dyna_state_attribute_accessors_'
end
def self.foo
'bar'
end
end
If I test this in the rails console:
2.0.0p0 :004 > DynaState::AttributeAccessors.table_name_prefix
NoMethodError: undefined method `table_name_prefix' for DynaState::AttributeAccessors:Module
2.0.0p0 :005 > DynaState::AttributeAccessors.foo
NoMethodError: undefined method `foo' for DynaState::AttributeAccessors:Module
Whereas when I define it on the fly in the console:
2.0.0p0 :002 > module Foo; def self.bar; 'argh!' end; end;
2.0.0p0 :003 > Foo.bar
=> "argh!"
My guess is that during initialization it is not evaluating all the files that compose the module. Especially not the file where the methods are defined. But I don't know how to fix it or if it is even the problem.
Do you have any Idea how I could debug this or what the problem is?
EDIT: This problem is only occuring with the ONE module. The AttributeAccessors module.

You cannot call methods on a Module until you've included or extended them in a class, if in your console you do
extend DynaState::AttributeAccessors
and then
DynaState::AttributeAccessors.table_name_prefix
That should most likely do it for you. I'm really not sure but the module defined on the fly in the console might be available/included in the main scope of the console context.

Related

Ruby NoMethodError exception calls #inspect by default

I have found a weird problem in Ruby and I'm not quite sure if it is an issue or it's a feature introduced in recent Ruby versions.
Basically when we call an undefined method, we get an undefined method error as expected in Ruby. The issue is that it also calls inspect and prints out the object and all of its attributes/values. If my object is a complex one, it takes really long time to finish printing everything out and in many times it causes my local rails server to hang, especially when there is an attribute holding binary data. This issue does not seem to happen with Ruby 2.6 but Ruby >= 2.7.
For simple objects, that's not a problem but I'm specifically having the issue with this gem: puppeteer-ruby. Each object in this gem has lots of attributes and dependencies.
Here is an example, I run the following code in IRB with different Ruby version and getting different exceptions:
require 'puppeteer-ruby'
browser = Puppeteer.launch(headless: true)
browser.foo
Ruby 2.6.6
NoMethodError: undefined method `foo' for #<Puppeteer::Browser:0x00007fce2d553b30>
Did you mean? for
Ruby 3.0.0
(irb):9:in `<main>': undefined method `foo' for #<Puppeteer::Browser:0x00007f9300550740 #ignore_https_errors=false, #default_viewport=#<Puppeteer::Viewport:0x00007f92ffbf8e90 #width=800, #height=600, #device_scale_factor=1.0, #is_mobile=false, #has_touch=false, #is_landscape=false>, #process=#<Puppeteer::BrowserRunner::BrowserProcess:0x00007f92fc5ae448 #spawnargs=["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "--disable-background-networking", "--enable-features=NetworkService,NetworkServiceInProcess", "--disable-background-timer-throttling", "--disable-backgrounding-occluded-windows", "--disable-breakpad", "--disable-client-side-phishing-detection", "--disable-component-extensions-with-background-pages", "--disable-default-apps", "--disable-dev-shm-usage", "--disable-extensions", "--disable-features=Translate", "--disable-hang-monitor", "--disable-ipc-flooding-protection", "--disable-popup-blocking", "--disable-prompt-on-repost", "--disable-renderer-backgrounding", "--disable-sync", "--force-color-profile=srgb", "--metrics-recording-only", "--no-first-run", "--enable-automation", "--password-store=basic", "--use-mock-keychain", "--enable-blink-features=IdleDetection", "--headless", "--hide-scrollbars", "--mute-audio", "about:blank", "--remote-debugging-port=0", "--user-data-dir=/var/folders/qn/kx4kb8xx5x13gx77yy2q56fr0000gn/T/puppeteer_dev_chrome_profile-20210910-25051-m7mnxb"], #stdout=#<IO:fd 24>, #stderr=#<IO:fd 26>,......very long text after this..
Thats interesting - inspect has always printed internal variables.
In the blame the last change to inspect was 9 years ago: rb_object_inspect#704
A simple script using 2.6:
> docker run -it ruby:2.6.6-alpine sh
#> ruby -v
ruby 2.6.6p146 (2020-03-31 revision 67876) [x86_64-linux-musl]
#> irb
irb(main):001:0> class Foo; def initialize(message); #message = message; end; end
=> :initialize
irb(main):002:0> Foo.new('hello').foo
NoMethodError (undefined method `foo' for #<Foo:0x00007f35e20e0280 #message="hello">)
Did you mean? for
Though looking at the source for Puppeteer::Browser it seems they changed to a pure ruby implementation of the browser around 2 years ago. When they did that they introduced a lot of ivars to the class. Perhaps the version you're using changed between updating ruby from 2.6 to 3.0?
A simple fix would be to monkey patch inspect on Puppeteer::Browser, or send a PR to the maintainer - looks like the project is active on GitHub.

Not able to call method in a gem

This might be an easy question but I was unfortunately not able to find the answer on Google.
Context:
I am working on a project of my own, and I am externalizing some code in a gem (FrenchTaxSystem). It is the first I create a gem and I have difficulties using it properly.
Problem:
When calling a method (like testit) defined in the main file (french_tax_system.rb) of my gem I get a "NoMethodError: undefined method `testit' for FrenchTaxSystem:Module", though I can call constants from this same file (like FISCAL_NB_PARTS_FOR_MARRIED_COUPLE) and it puzzles me.
E.g in IRB I get that when calling a method:
[
And it is the same in my Rspecs tests inside my gem
However when calling a constant I have no error:
Main file in my gem:
french_tax_system.rb
module FrenchTaxSystem
class Error < StandardError; end
# Constants
...
FISCAL_NB_PARTS_FOR_MARRIED_COUPLE = 2
...
# Methods
## Main method
def testit
"test me"
end
end
Gem file structure:
Thank you in advance for your help,
Mth0158
This should work:
module FrenchTaxSystem
def self.testit
"test me"
end
end

NoMethodError when trying to access a "nested method" (or def) in the same module

The piece of code below gives me a NoMethodError. I'm a little confused why it gives me an error, and why I can't find anything about nesting methods in modules. Could someone please explain why this isn't working? Can I nest "defs" in modules?
module HowToBasic
module_function
def say_id_and_say_name(id)
# nested method
def say_id(id)
p id
end
# errors here with `say_id_and_say_name':
# undefined method `say_id' for HowToBasic:Module (NoMethodError)
# from teststuff.rb:24:in `<main>'
say_id(id)
end
end
HowToBasic.say_id_and_say_name("99999")
Version:
ruby 2.3.1p112
I had a look and couldn't find anything about this:
relates to includes NoMethodError when trying to access method defined in included module
relates to classes https://bugs.ruby-lang.org/issues/11665
seems weird Access a Ruby module's method within same module
you're missing self keyword in method definition - without it say_id_and_say_name is just an instance method, thus it can't be invoked on Module.
module HowToBasic
module_function
def self.say_id(id)
p id
end
def self.say_id_and_say_name(id)
say_id(id)
end
end
HowToBasic.say_id_and_say_name("99999")

Upgraded to minitest 5.4.0. Now must* and won't* doesn't work within Minitest::Test subclass, but assert* still works

I have been using the MiniTest that comes with Ruby 2.1 without a problem. I would subclass MiniTest::Unit:TestCase create a couple methods like 'test_simple', and everything just worked. I would use Expectations and Asserts without a problem.
I have upgraded Minitest to 5.4.0 using a gem. Everywhere I use Expectations (musts and wonts) I get a strangle error. Example of the test class.
gem 'minitest'
require "minitest/autorun"
require "rest-client"
require "json"
require "pp"
# require './testcase_addins'
class TestUserKey < Minitest::Test
def test_simple
data = 0
assert( data >= 0 )
data.must_be :>=,0
end
end
When I run this, the assert line passes without a problem, but must_be line throws this error:
1) Error:
TestUserKey#test_simple:
NoMethodError: undefined method `assert_operator' for nil:NilClass
(eval):4:in `must_be'
user_key_testcase.rb:14:in `test_simple'
The strange part is what is the nil:NilClass in the error can't be nil; it's 0. Even I change the Fixnum to a String, I still get the same error.
If I change the test to a spec test, everything works again. So I can't use Expectations with in Unit Tests? IF that is the case, could someone explain why?
The short answer is that with 5.4.0 your test class must inherit from MiniTest::Spec in order to use expectations.
I tested this on a new ubuntu machine with ruby 2.1.2 installed via RVM:
rvm install ruby-2.1.2
This code works with the stock ruby 2.1.2 (no 5.4.0 minitest gem installed, slightly cleaned up from your example code above):
#!/usr/bin/env ruby
require 'minitest/unit'
require "minitest/autorun"
class TestUserKey < MiniTest::Unit::TestCase
def test_simple
data = 0
assert( data >= 0 )
data.must_be :>=,0
end
end
Running this code works fine. To reproduce the error listed above, install minitest 5.4.0:
gem install minitest -v 5.4.0
Now the code fails with "NoMethodError: undefined method `assert_operator' for nil:NilClass". You now have both versions of minitest installed:
~/.rvm/rubies/ruby-2.1.2/lib/ruby/2.1.0/minitest/ # stock ruby version of minitest
~/.rvm/gems/ruby-2.1.2/gems/minitest-5.4.0/ # minitest v5.4.0 installed via rubygems
Now that everything is set up, we can dig into what exactly is happening. The expectations are defined with a call to infect_an_assertion, like this:
infect_an_assertion :assert_operator, :must_be, :reverse
For 5.4.0 that call happens in ~/.rvm/gems/ruby-2.1.2/gems/minitest-5.4.0/lib/minitest/expectations.rb. It's roughly the same in both versions, it just happens in a different place.
infect_an_assertion is also roughly the same for both versions. For :must_be it winds up making this call, which is identical between the two versions of minitest:
MiniTest::Spec.current.#{meth}(args.first, self, *args[1..-1])
They're doing some metaprogramming here, the call at runtime will look more like this since meth is set to assert_operator:
MiniTest::Spec.current.assert_operator(...)
The important part is MiniTest::Spec.current. In 5.4.0 this method returns nil, which results in a NoMethodError exception when it tries to call assert_operator on nil.
In the stock minitest from ruby 2.1.2:
Here MiniTest::Spec inherits from MiniTest::Unit::TestCase. TestCase defines the method current and returns a value that was set in the initialize method. You can see this all happening around line 1303 of ~/.rvm/rubies/ruby-2.1.2/lib/ruby/2.1.0/minitest/unit.rb:
def initialize name # :nodoc:
#__name__ = name
#__io__ = nil
#passed = nil
##current = self # FIX: make thread local
end
def self.current # :nodoc:
##current # FIX: make thread local
end
Therefore, when you inherit from MiniTest::Unit::TestCase in your test class with the stock minitest, current is defined as a method and is guaranteed to return a value when the above call to MiniTest::Spec.current is made. This is why it works in the stock ruby 2.1.2 minitest.
In minitest 5.4.0
In 5.4.0, Minitest::Spec inherits from Minitest::Test, which does not define current (nor do any of its parents). The current method is defined directly on Minitest::Spec. It simply returns Thread.current[:current_spec]:
# line 83 of ~/.rvm/gems/ruby-2.1.2/gems/minitest-5.4.0/lib/minitest/spec.rb
def self.current # :nodoc:
Thread.current[:current_spec]
end
The value of Thread.current[:current_spec] is set in the Minitest::Spec constructor on line 87 of the same file:
def initialize name # :nodoc:
super
Thread.current[:current_spec] = self
end
The problem is that when your test class inherits from Minitest::Test, the Minitest::Spec constructor never gets called and Thread.current[:current_spec] is never initialized. That means the call in infect_an_assertion to Minitest::Spec.current returns nil, which results in the NoMethodError you're seeing when it tries to call assert_operator on nil. The solution is to make your test class inherit from Minitest::Spec so that the constructor is called and Thread.current[:current_spec] gets a value.
Here is a slightly modified version of the original code that works with minitest 5.4.0:
#!/usr/bin/env ruby
gem 'minitest'
require "minitest/autorun"
class TestUserKey < Minitest::Spec
def test_simple
data = 0
assert( data >= 0 )
data.must_be :>=,0
end
end
Hope this helps!
From the minitest readme, it seems like the must style syntax is a part of the spec syntax, which would require you to use methods like describe and it instead of defining your own unit test methods.

ruby 2.0 undefined method ObjectSpace.trace_object_allocations

tried this on ruby 2.0.0 / 247 or head:
require 'objspace'
ObjectSpace.trace_object_allocations -> undefined method `trace_object_allocations'
for ObjectSpace:Module
Docs say it should work http://www.ruby-doc.org/stdlib-2.0/libdoc/objspace/rdoc/ObjectSpace.html any idea what I'm missing ?
For higher ruby versions you still may get errors like:
undefined method `memsize_of' for ObjectSpace:Module
To solve this issue -> You need to require 'objspace'. According to https://ruby-doc.org/stdlib-2.3.1/libdoc/objspace/rdoc/ObjectSpace.html#method-c-memsize_of
Only available when installing ruby-head aka ruby 2.1
From the output of puts ObjectSpace.methods.sort after requiring objspace, it looks like that method doesn't exist.
irb(main):005:0> puts ObjectSpace.methods.sort
...
reachable_objects_from
remove_class_variable
remove_instance_variable
respond_to?
send
singleton_class
singleton_methods
taint
tainted?
tap
to_enum
to_s
trust
undefine_finalizer
untaint
untrust
untrusted?
=> nil
irb(main):009:0> ObjectSpace.methods.include? :trace_object_allocations
=> false
You can see that it contains the ::reachable_objects_from method, which is mentioned in the docs, but not the one you're looking for, unfortunately.

Resources