How to List the Currently Available Objects in the Current Scope in Ruby

How do you list the currently available objects in the current scope in ruby?

ObjectSpace.each_object could be what you are looking for.

To get a list of included modules you could use Module.included_modules.

You can also check if an object responds to a method on a case-by-case basis using object.respond_to?.

Quick way to see variables that apply in current scope and which object self refers to, RAILS

I really like using the Better Errors gem, then I just "raise @somevar" where I need, that way I can use the in browser console and poke it with a stick. :) god I love that thing.

How to escape scope in IRB?

You don't seem to be returning all the values from your method show_top_locals. Also, the call to Kernel#local_variables inside a method will only lookup local variables from the method scope. You should pass the binding from the place you are calling the method so that you get list of local variables at the place where method was invoked.

require "pp"

# For backward compatibility with Ruby 2.1 and below
def local_vars(binding)
if binding.respond_to? :local_variables
binding.local_variables
else
eval "Kernel.local_variables", binding
end
end

def show_top_locals(class_param, binding)
available_objects_array = ObjectSpace.each_object(class_param).to_a
local_variables_array = local_vars(binding).delete_if {|y| binding.eval(y.to_s).class != class_param}
local_variables_array_reversed = local_variables_array.reverse.map {|z| z.to_s}
objects_and_variables_array = local_variables_array_reversed.zip(available_objects_array).flatten.compact
return {
available_objs: available_objects_array,
local_vars: local_variables_array,
local_vars_reversed: local_variables_array_reversed,
objs_and_vars: objects_and_variables_array
}
end

class Foo
end

a = Foo.new
b = Foo.new

pp show_top_locals(Foo, binding)
#=> {:available_objs=>[#<Foo:0x000000027825b0>, #<Foo:0x000000027825d8>],
# :local_vars=>[:a, :b],
# :local_vars_reversed=>["b", "a"],
# :objs_and_vars=>["b", #<Foo:0x000000027825b0>, "a", #<Foo:0x000000027825d8>]}

You can place the method show_top_locals in a .rb file and refer to "Can you 'require' ruby file in irb session, automatically, on every command?" to autoload it in IRB.

Rails - Refer to current object in model

It doesn't work in your model because @category is an instance variable that lives within your controllers. You can pass the category into the named scope using a lambda (anonymous function):

named_scope :in_tree, lambda { |category| { :include => :category,
:conditions => ['category in (?)', (subtree_of(category)) ] }}

or

named_scope :in_tree, lambda { |category| { :include => :category,
:conditions => ['category in (?)', (category.subtree_ids) ] }}

Now in your controllers/helpers you can use the named scope using Product.in_tree(@category).

Access variables programmatically by name in Ruby

What if you turn your problem around? Instead of trying to get names from variables, get the variables from the names:

["foo", "goo", "bar"].each { |param_name|
param = eval(param_name)
if param.class != Array
puts "#{param_name} wasn't an Array. It was a/an #{param.class}"
return "Error: #{param_name} wasn't an Array"
end
}

If there were a chance of one the variables not being defined at all (as opposed to not being an array), you would want to add "rescue nil" to the end of the "param = ..." line to keep the eval from throwing an exception...

Why can't I access a local variable inside a method in Ruby?

The reason ff is inaccessible inside the test method definition is simply that methods (created with the def keyword) create a new scope. Same with defining classes and modules using the class and module keywords respectively.

The role of main (the top-level object) is almost completely irrelevant to the question of scope in this situation.

Note that, if you DO want your test method to have access to locals defined in the definition context, then use the define_method (or in your case, the define_singleton_method method), see here:

ff = "hi"
define_singleton_method("test") { ff }
test #=> "hi"

Unlike the def keyword, the define_method family of methods do not create new scopes but instead close over the current scope, capturing any local variables.

The reason using @ff worked in the next example given by @soup, is not that main is somehow a "special case" it's just that an ivar defined at top-level is an ivar of main and so is accessible to a method invoked on main.

What, however, is the relationship of the test method to main? It is not a method on just main itself - it is actually a private instance method defined on the Object class. This means that the test method would be available (as a private method) to nearly every object in your ruby program. All methods defined at top-level (main) are actually defined as private instance methods on the Object class.

For more information on the Ruby top-level, see this article: http://banisterfiend.wordpress.com/2010/11/23/what-is-the-ruby-top-level/

Use an object from outside your modules scope

Like this:

::User.find(user_id)

How can I add a method to the global scope in Ruby?

TL;DR extend-ing a module at the top level adds its methods to the top level without adding them to every object.

There are three ways to do this:

Option 1: Write a top-level method in your gem

#my_gem.rb

def add_blog(blog_name)
puts "Adding blog #{blog_name}..."
end
#some_app.rb

require 'my_gem' #Assume your gem is in $LOAD_PATH
add_blog 'Super simple blog!'

This will work, but it's not the neatest way of doing it: it's impossible to require your gem without adding the method to the top level. Some users may want to use your gem without this.

Ideally we'd have some way of making it available in a scoped way OR at the top level, according to the user's preference. To do that, we'll define your method(s) within a module:

#my_gem.rb
module MyGem

#We will add methods in this module to the top-level scope
module TopLevel
def self.add_blog(blog_name)
puts "Adding blog #{blog_name}..."
end
end

#We could also add other, non-top-level methods or classes here
end

Now our code is nicely scoped. The question is how do we make it accessible from the top level, so we don't always need to call MyGem::TopLevel.add_blog ?

Let's look at what the top level in Ruby actually means. Ruby is a highly object oriented language. That means, amongst other things, that all methods are bound to an object. When you call an apparently global method like puts or require, you're actually calling a method on a "default" object called main.

Therefore if we want to add a method to the top-level scope we need to add it to main. There are a couple of ways we could do this.

Option 2: Adding methods to Object

main is an instance of the class Object. If we add the methods from our module to Object (the monkey patch the OP referred to), we'll be able to use them from main and therefore the top level. We can do that by using our module as a mixin:

#my_gem.rb

module MyGem
module TopLevel
def self.add_blog
#...
end
end
end

class Object
include MyGem::TopLevel
end

We can now call add_blog from the top level. However, this also isn't a perfect solution (as the OP points out), because we haven't just added our new methods to main, we've added them to every instance of Object! Unfortunately almost everything in Ruby is a descendant of Object, so we've just made it possible to call add_blog on anything! For example, 1.add_blog("what does it mean to add a blog to a number?!") .

This is clearly undesirable, but conceptually pretty close to what we want. Let's refine it so we can add methods to main only.

Option 3: Adding methods to main only

So if include adds the methods from a module into a class, could we call it directly on main? Remember if a method is called without an explicit receiver ("owning object"), it will be called on main.

#app.rb

require 'my_gem'
include MyGem::TopLevel

add_blog "it works!"

Looks promising, but still not perfect - it turns out that include adds methods to the receiver's class, not just the receiver, so we'd still be able to do strange things like 1.add_blog("still not a good thing!") .

So, to fix that, we need a method that will add methods to the receiving object only, not its class. extend is that method.

This version will add our gem's methods to the top-level scope without messing with other objects:

#app.rb

require 'my_gem'
extend MyGem::TopLevel

add_blog "it works!"
1.add_blog "this will throw an exception!"

Excellent! The last stage is to set up our Gem so that a user can have our top-level methods added to main without having to call extend themselves. We should also provide a way for users to use our methods in a fully scoped way.

#my_gem/core.rb

module MyGem
module TopLevel
def self.add_blog...

end
end

#my_gem.rb

require './my_gem/core.rb'
extend MyGem::TopLevel

That way users can have your methods automatically added to the top level:

require 'my_gem'
add_blog "Super simple!"

Or can choose to access the methods in a scoped way:

require 'my_gem/core'
MyGem::TopLevel.add_blog "More typing, but more structure"

Ruby acheives this through a bit of magic called the eigenclass. Each Ruby object, as well as being an instance of a class, has a special class of its own - its eigenclass. We're actually using extend to add the MyGem::TopLevel methods to main's eigenclass.


This is the solution I would use. It's also the pattern that Sinatra uses. Sinatra applies it in a slightly more complex way, but it's essentially the same:

When you call

require 'sinatra'

sinatra.rb is effectively prepended to your script. That file calls

require sinatra/main

sinatra/main.rb calls

extend Sinatra::Delegator

Sinatra::Delegator is the equivalent of the MyGem::TopLevel module I described above - it causes main to know about Sinatra-specific methods.

Here's where Delgator differs slightly - instead of adding its methods to main directly, Delegator causes main to pass specific methods on to a designated target class - in this case, Sinatra::Application.

You can see this for yourself in the Sinatra code. A search through sinatra/base.rb shows that, when the Delegator module is extended or included, the calling scope (the "main" object in this case) will delegate the following methods to Sinatra::Application:

:get, :patch, :put, :post, :delete, :head, :options, :link, :unlink,
:template, :layout, :before, :after, :error, :not_found, :configure,
:set, :mime_type, :enable, :disable, :use, :development?, :test?,
:production?, :helpers, :settings, :register


Related Topics



Leave a reply



Submit