Referencing the global namespace with Ruby's Scope Resolution Operator
Tags: ruby, rails
Published: 2020-06-03 20:03:37

This `::` that you see prefixing a class name from time to time

```ruby
::Account.find_by("123abc")
```

is called the **Scope Resolution Operator**.

You can think about it like this. The thing on the right-hand side of the `::` operator resolves to the scope of the thing on the left-hand side.

In the case of something like `Net::HTTP`, the `HTTP` resolves to being scoped to the `Net` namespace.

As for our above example, `::Account`, there is nothing on the left-hand side which means `Account` will be scoped to the _global_ namespace.

## How to use it

### Referencing a class on the global namespace

Consider this paired down banking software example.

```ruby
class Account
  attr_accessor :first_name, :last_name, :email

  def self.find_by(account_number)
    # ...
  end
end

module Bank
  class Account
    def withdraw(amount)
      if would_overdraw(amount)
        user_account = Account.find_by(account_number)
        # send alert or email to user_account
      end

      # ...
    end
  end
end
```

In this example, I have the user's `Account` within the web app as well as a `Bank::Account` of which one or more could be associated with a user `Account`.

In `#withdraw`, I try to look up the user's `Account` in the event of an overdraft so that they can be alerted.

Here's what happens when that overdraft code path is executed:

```
`withdraw': undefined method `find_by' for Bank::Account:Class (NoMethodError)
```

Take notice that it is looking for `find_by` in `Bank::Account`, not `Account`.

Ruby applies the current namespace context when referencing `Account` in `#withdraw`. Because the call is within the `Bank::Account` namespace, there is a `NoMethodError`.

The `find_by` method it is looking for is instead on `Account`. And that's where the _scope resolution operator_ comes into play.

```ruby
def withdraw(amount)
  if would_overdraw(amount)
    user_account = ::Account.find_by(account_number)
    # send alert or email to user_account
  end

  # ...
end
```

Prepending `Account` with `::` tells Ruby that I want to reference that class from the global namespace. And now Ruby knows that I am talking about the other account, the one that defines `find_by`.

We can use this same operator when defining a class if we'd like.

### Defining a class on the global namespace

```ruby
class SomethingDoer < BaseService

  class SomethingIsAfootError < StandardError; end

  def self.run(thing)
    # do stuff
  end

end
```

I've defined an error class within a service object. Because of that, the error class is now namespaced under that service object.

If I were to inspect that error class from inside the `#run` method, I'd see this namespacing:

```ruby
SomethingIsAfootError.ancestors
=> [SomethingDoer::SomethingIsAfootError, StandardError, Exception, Object, ...]
```

Notice how `SomethingIsAfootError` is prefixed with `SomethingDoer::`.

If however I want the error class defined at the global scope, I could apply the _scope resolution operator_ when defining it.


```ruby
class SomethingDoer < BaseService

  class ::SomethingIsAfootError < StandardError; end

  def self.run(thing)
    # do stuff
  end

end
```

Inspecting it this time, I see the error class is globally namespaced.

```ruby
SomethingIsAfootError.ancestors
=> [SomethingIsAfootError, StandardError, Exception, Object, ...]
```

_Note: in this second example, I could also define the error class outside of the service object. The example is meant to be instructive so as to convey how this operator can be used as part of a class definition._

_If you enjoyed this post, [join my newsletter](https://crafty-builder-6996.ck.page/e169c61186
) to get a direct line on more stuff like this._

## References

- [Ruby: what does `::` prefix do?](https://stackoverflow.com/questions/5032844/ruby-what-does-prefix-do)
- [Scope Resolution Operator, Wikipedia](https://en.wikipedia.org/wiki/Scope_resolution_operator)

## Acknowledgements

Thanks to Jack Christensen for providing feedback on an early draft of this post.