How does require always load into the top-level scope? - ruby

Consider this Ruby code:
class A
require 'json'
end
Because Ruby is interpreted and executes code line-by-line, I would expect JSON to be scoped inside A.
However:
irb(main):005:0> A::JSON
Traceback (most recent call last):
4: from /usr/bin/irb:23:in `<main>'
3: from /usr/bin/irb:23:in `load'
2: from /usr/lib/ruby/gems/2.7.0/gems/irb-1.2.1/exe/irb:11:in `<top (required)>'
1: from (irb):5
NameError (uninitialized constant A::JSON)
irb(main):004:0> JSON
=> JSON
How does require always load gems into the top-level scope?

require is implemented inside the VM, so it has access to internal functionality that normal Ruby code does not. For example it can manually escape its current scope and execute code at the top level. That is "how".
As for "why"? Imagining if you could require into a specific scope, this would be extremely prone to breakage since it would change top-level self from main (which is an Object) to... anything (in your example it would be A, which is a Class). It would be very hard to predict in general what would happen when your code is required.
By always executing loaded code at the top-level, the result is always consistent. And you can use the built-in hook mechanisms (included, extended, prepended, inherited) to access specific selfs from the scope that loaded you.

Related

What is the purpose of Ruby's unknown class?

I was looking through the Ruby Core API and noticed "unknown" at the bottom of the Classes list. When you go to that class's page at https://ruby-doc.org/core-2.6.1/unknown.html, you will notice that there is no content.
What is the purpose of this class, i.e. when and how would you use it? If there is no purpose, why is it documented on ruby-doc.org?
It looks like this is some kind of artifact of the documentation system and not an actual class in Ruby.
The documentation is produced by parsing the Ruby source code and can often get a little confused about what it's seeing. There's ways of coaching it with additional comments to ignore things it shouldn't document and so on, but tracking down which particular file or line produced this "unknown" reference is not necessarily easy.
You may want to report this as a bug in the documentation. That page isn't helpful, and is more confusing than anything.
There is no unknown class in Ruby. To prove #tadman's point simply run an IRB console:
irb(main):006:0> NilClass
=> NilClass
irb(main):007:0> Object
=> Object
irb(main):008:0> ZeroDivisionError
=> ZeroDivisionError
irb(main):009:0> Unknown
NameError: uninitialized constant Unknown
irb(main):010:0> unknown
NameError: undefined local variable or method `unknown' for main:Object

NameError: undefined local variable or method `watir' for main:Object

I am using the following code in irb:
irb(main):002:0>driver = watir::Browser.new :chrome
But it gives me this error:
NameError: undefined local variable or method `watir' for main:Object
from (irb):2
from C:/Ruby24-x64/bin/irb.cmd:19:in `<main>'
It looks like you're attempting to access a nested class, which should refer to capitalised variable names:
driver = Watir::Browser.new :chrome
By using the lowercase watir, you're looking for a variable watir defined in the local scope. You could, for instance, be storing the top level class / module in a variable:
watir = Watir
And then call the code as you have it, though that's pointless and an antipattern.
Capitalise the first letter as above, and this should work for you.
Edit: as Justin Ko pointed out, it sounds like that is now looking for the correct class, though it needs to be available to the file. Add this to the top and everything should work:
require 'watir'

Error uninitialized constant with a class inheritance

Clearly there is something off with how I understand it in ruby. I have 3 ruby files main, base, and derived. I have two classes Derived specializes from Base.
/bin/main.rb
require './lib/base'
/lib/base.rb
require './lib/derived'
class Base
end
/lib/derived.rb
require './lib/base'
class Derived < Base
end
running with rake ruby './bin/main.rb'
`<top (required)>': uninitialized constant Base (NameError)
What is causing the error?
Edit:
I just realized one point I was missing was that I forgot that require is a Kernel#method, that I do not need to keep on top of my code every time like I normally do with other languages.
The issue here is that the require './lib/derived' in the /lib/base.rb file is actually causing /lib/derived.rb to be parsed before Base is declared in /lib/base.rb. Try this, instead:
/bin/main.rb
require './lib/base'
require './lib/derived'
puts 'Success!'
/lib/base.rb
class Base
end
/lib/derived.rb
require './lib/base'
class Derived < Base
end
This allows the declarations to occur in the proper order.
As a side note, it isn't technically necessary to require './lib/base' in lib/main.rb, since it's actually successfully included in lib/derived.rb, but it is good form if Base is used directly in the body of lib/main.rb.

Rails: How do I require NumberHelper and make it work?

I'm trying to write a simple Sinatra thingy but I need ActionView::Helpers::NumberHelper from action pack.
http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html
The question is, how do I install and use it?
irb(main):001:0> require 'action_view/helpers/number_helper'
irb(main):002:0> number_with_precision(1)
NoMethodError: undefined method `number_with_precision' for main:Object
irb(main):004:0> ActionView::Helpers::NumberHelper.number_with_precision(1)
NoMethodError: undefined method `number_with_precision' for ActionView::Helpers::NumberHelper:Module
Why doesn't this simple step work?
Moreover, if I require all the crap:
irb(main):001:0> require 'action_pack'
irb(main):004:0> require 'action_view'
irb(main):005:0> include ActionView::Helpers::NumberHelper
irb(main):006:0> number_to_phone(12345)
NoMethodError: undefined method `starts_with?' for "12345":String
How to make sense from all of this? Why does not this module work? Why doesn't it require whatever it needs? What does it need? Where is starts_with?
Google is utterly silent on those questions.
UPD: And now I get the following
number_with_precision(1, :locale => 'fr')
TypeError: wrong argument type nil (expected Fixnum)
It seems to me that my NumberHelper is broken. This isn't a good behavior.
So, after doing a bit of research, I found the following pull request on the master branch of Rails
https://github.com/rails/rails/pull/6315
It pretty much aims to move ActionView::Helpers::NumberHelper from ActionView to ActiveSupport
I also saw a few closed issues that aimed to fix a few problems with allowing inclusion of NumberHelper as standalone. This means require fixes and such. I didn't find an open issue with the number_to_phone but the problem roots at the fact that ActiveSupport adds an alias starts_with? to the String class. I'm not sure if they have caught that bug there yet or not.
In any case, with ActionView version 3.2.13 you can do the following
require 'action_view'
include ActionView::Helpers::NumberHelper
number_with_precision 3.1
#=> "3.100"
As for the number_to_phone, that will still break with the current version. I'm making a PR to fix that issue at this moment.
EDIT
As for the locale issue, it seems that if you specify a local you need to have set the right options in the I18n for it to work. If you don't provide a locale the defaults will look like this {:separator=>".", :delimiter=>"", :precision=>3, :significant=>false, :strip_insignificant_zeros=>false}, otherwise, the hash will be empty and it will cause issues. I can't seem to find any issues about it on Rails though.
Again, this was fixed on a PR on master https://github.com/carlosantoniodasilva/rails/commit/f6b71499e967e03c65d53cc890585f42f3b8aaa2
UPDATE
You can use ActiveSupport now to use these helpers
http://api.rubyonrails.org/classes/ActiveSupport/NumberHelper.html
It changed recently:
require "active_support/all"
module Helpers
extend ActiveSupport::NumberHelper
end
Helpers.number_to_currency(10.23) # => "$10.23"

can't load require_dependency

I'm trying to use require_dependency from ActiveSupport library in pry:
require 'active_support' #=> true
require_dependency 'test' #=> NoMethodError: undefined method
#=> `require_dependency' for main:Object
What could be the problem?
ActiveSupport used to be pretty evil by loading a ton of stuff on require. The "kitchen sink" approach opened a lot of core classes up and changed their behavior (like JSON). This caused incompatibilities/problems with other gems and code that expected core ruby functions to behave like their vanilla selves.
So now requiring just active_support does not load anything.
see http://edgeguides.rubyonrails.org/active_support_core_extensions.html
in your case you probably will need require 'active_support/core_ext'

Resources