Load only what classes are being used in Ruby? - ruby

If I load x.rb, then all the classes in that file are loaded. Is it possible to check and see what classes are being used and load those only?
Assuming x.rb contains both Hello and Goodbye classes, and my program only uses the Hello Class, is it possible to load only the Hello Class?
Happy with a script that checks the document, and outputs an .rb that has only the Hello Class and the code that uses it... Would be an interesting github project if doesn't exist, but I think it's out of my skillset atm.

When classes are defined in their own separate file, you could use the autoload¹² method:
autoload :Hello, 'x/hello'
autoload :Goodbye, 'x/goodbye'
When you write Hello, you are actually accessing the Hello constant. autoload uses const_missing to automatically require a file if the constant is not defined.
Note that we are still dealing with files here. The contents of x/hello.rb will simply be read and evaluated. That code can run any operation. It can require other files. It could define a million other classes.
That's because source code is really just text. This is especially true for interpreted languages. For example, in Java, you can usually only declare one public type per "compilation unit". In Ruby, there is no such thing.
¹ Matz strongly discourages the practice
² Ruby Inside article on autoload

NOTE: I misread part of the question and the other part somehow evaded me... I must have been multitasking big time. Anyway, this answer only answers half of the question, and incorrectly; just saying.
Okay, here's a possibly expensive way, but try something like this:
$ cat definer.rb
class Foo
def greet(person)
puts "Hello, #{person}!"
end
end
Bar = Class.new
$ cat finder.rb
$LOAD_PATH << File.dirname "."
before = Object.constants
require 'definer'
after = Object.constants
print (before - after).join(" ")
$ cat looker.rb
differences = `ruby finder.rb`.split(" ")
puts "The following constants will be defined when finder.rb is loaded: #{differences.join(", ")}"
Now for a cleaner and way, you could use sockets. I'll get an example of this up.

Related

Convert Strings to Class Names in ruby

I am trying to write a script in ruby which involves converting a string to Class Names. Since I am using pure ruby I cannot use .constantize.
I have tried using Object.const_get('String') but not sure why it is throwing a uninitialized constant String (NameError)
I have require 'active_support' on the top of the file
The conventional way of assigning a name to an anonymous class is as follows.
bub = Class.new do
def b
'hi'
end
end
str = 'Bubba'
Object.const_set(str, bub)
Bubba.is_a?(Class)
#=> true
Bubba.new.b
#=> "hi"
Is that what you want to do? If so, as you see, you need to use Module#const_set.
Do you try to use const_get only for a class or it is under a namespace like ModuleA::ModuleB::ClassName?
Also converting a string to a class name makes a new class or assigns the value to it?
I am asking these questions because the answer will affect the method you have to use. Maybe const_set instead of const_get is the correct approach, I don't know.
From the comments you gave it looks like 'String' is just an example and not the value that you literally pass to const_get. The actual value apparently is 'Assignment', is this correct?
When you execute Object.const_get('Assignment') and you receive the uninitialized constant error it indicates that at this point the class Assignment has not been loaded yet.
When you are using Rails then a lot of autoloading takes place if the files are in the right folder and the classes follow the naming conventions. Since you are running a "standalone" ruby script, autoloading does not take place and you will need to load the file yourself.
Adding a line like
require_relative "somepath/assignment"
should work. somepath needs to be adapted to the directory/file layout you have. It will load the file and execute the ruby code in that file.
If assignment.rb defines something like
class Assignment
end
Then the const_get will work.
That being said: what is your exact use case for this? Why do you need to dynamically find classes? Also Note that this opens up your app to (an unlikely) potential security issues if you let user input define what classes are loaded.

Capturing the spec filename in a ruby DSL implementation?

I am writing a ruby DSL that will be used to code-generate a number of Objective-C++ functions. I would like the name of each function to be derived from the name of its ruby DSL source file.
For example, given this source file clusterOptions.rb:
require './vMATCodeMonkey'
VMATCodeMonkey.new(:print).options_processor <<EOS
-cutoff: flag: set('useCutoff', true), arg: vector('double')
-depth: flag: set('useInconsistent', true), arg: scalar('double', default: 2.0)
-maxclust: flag: set('useCutoff', false), arg: vector('index')
EOS
When the VMATCodeMonkey.new(:print) expression is evaluated I would ideally somehow like the new object to capture the clusterOptions.rb source filename. Is that possible?
And if (as I suspect) it is not, is there a good idiom for accomplishing this functionality [e.g. making the source file name effectively part of the specification captured by a DSL] in ruby?
[While I suspect it's not possible to do exactly as I've described, I ask anyway, because I've been surprised by ruby's obscure capabilities more than once.]
EDIT: I'm aware of __FILE__; what I'm looking for is some DSL-centric way of capturing the name of a DSL source file without explicitly mentioning __FILE__ in the DSL source. Hmm, and now that I'm trying to explain it, maybe crawling up a stack trace from the class initialize method?
Solution
With thanks to tadman, here is my VMATCodeMonkey#initialize method:
def initialize(out_opt = :print)
#caller_file = caller(1)[0].split(':')[0]
case out_opt
when :pbcopy
#out = IO.popen('pbcopy', 'w')
when :print
#out = $stdout
else
raise ArgumentError, "#{out_opt} is not an option!"
end
#out.puts "// vMATCodeMonkey's work; do not edit by hand!\n\n"
initialize_options_processor
end
And here's what it captures:
#caller_file = "/Users/Shared/Source/vMAT/ruby/clusterOptions.rb"
The full path to the source file being evaluated is stored in __FILE__. If you want just the filename, you'd use:
File.basename(__FILE__)
The __FILE__ constant is common to C, C++, Perl and Python, among others.
If you need to know what file made the call to the currently running routine, this could work:
caller(1)[0].split(':')[0]
This presumes your filenames do not have : in them, but in most cases that should be a fairly safe assumption. You'll also need to call this at the entry point into your library. If it's a method deeper in the stack, test caller(2) and so on.

From an included file, how can I get the filename of the file doing the including?

Apologies for the poorly worded question title - no idea how to put it better!
In the following code, when I execute ruby bar.rb, how can I make it output bar.rb, rather than foo.rb?
In foo.rb:
module Foo
def filename
__FILE__
end
end
In bar.rb:
require_relative 'foo'
include Foo
puts filename # outputs 'foo.rb'
This is for a library function that, each time some code is executed, records the location (and git ref) of that code.
Your question stimulated me to crack open the Ruby interpreter source and see how __FILE__ actually works. The answer is pretty interesting: it's implemented right inside the parser. The lexer has a special token type for __FILE__. When the parser sees that token, it converts it to a string constant, which contains the name of the file the parser is working on.
From line 14948 of ext/ripper/ripper.c:
case keyword__FILE__:
return NEW_STR(rb_external_str_new_with_enc(ruby_sourcefile, strlen(ruby_sourcefile),
rb_filesystem_encoding()));
I think this should make it clear that trying to make __FILE__ return the name of the including file is completely impossible, unless you hack the Ruby interpreter source, or write your own preprocessor which transforms __FILE__ to something else before passing the Ruby source to the interpreter!
There is a trick you might be a able to use. If you pass a block to the method you could use the blocks closure to determine it's source. Something like:
def filename(&blk)
blk.eval "__FILE__"
end
But again, that means you have to pass a block.
Honestly I wonder what you are trying to accomplish, b/c outside of make some common core extension method, this is probably something you really don't want to do.

When is `eval` in Ruby justified?

"Is 'eval' supposed to be nasty?" inspired this one:
Mostly everybody agrees that eval is bad, and in most cases there is more elegant/safer replacement.
So I wanted to ask: if eval is misused that often, is it really needed as a language feature? Is it doing more evil than good?
Personally, the only place I find it useful is to interpolate strings provided in config file.
Edit: The intention of this question is to get as many real-life cases as possible when eval is the only or the best solution. So please, don't go into "should a language limit a programmer's creativity" direction.
Edit2: And when I say eval, of course I refer to evaling string, not passing ruby block to instance_eval or class_eval.
The only case I know of (other than "I have this string and I want to execute it") is dynamically dealing with local and global variables. Ruby has methods to get the names of local and global variables, but it lacks methods to get or set their values based on these names. The only way to do AFAIK is with eval.
Any other use is almost certainly wrong. I'm no guru and can't state categorically that there are no others, but every other use case I've ever seen where somebody said "You need eval for this," I've found a solution that didn't.
Note that I'm talking about string eval here, by the way. Ruby also has instance_eval, which can take either a string or a block to execute in the context of the receiver. The block form of this method is fast, safe and very useful.
When is it justified? I'd say when there's no reasonable alternative. I was able to think of one use where I can't think of an alternative: irb, which, if you dig deep enough (to workspace.rb, around line 80 in my copy if you're interested) uses eval to execute your input:
def evaluate(context, statements, file = __FILE__, line = __LINE__)
eval(statements, #binding, file, line)
end
That seems pretty reasonable to me - a situation where you specifically don't know what code you're going to have to execute until the very moment that you're asked to do so. Something dynamic and interactive seems to fit the bill.
The reason eval is there is because when you need it, when you really need it, there are no substitutes. There's only so much you can do with creative method dispatching, after all, and at some point you need to execute arbitrary code.
Just because a language has a feature that might be dangerous doesn't mean it's inherently a bad thing. When a language presumes to know more than its user, that's when there's trouble.
I'd argue that when you find a programming language devoid of danger, you've found one that's not very useful.
When is eval justified? In pragmatic terms, when you say it is. If it's your program and you're the programmer, you set the parameters.
There is one very important use-case for eval() which cannot (AFAIK) be achieved using anything else, and that is to find the corresponding object reference for a binding.
Say you have been passed a block but (for some reason) you need access to object context of the binding, you would do the following:
obj = eval('self', block.binding)
It is also useful to define the following:
class Proc
def __context__
eval('self', self.binding)
end
end
IMO mostly for Domain Specific Languages.
"Evaluation Options in Ruby" is an article by Jay Fields about it on InfoQ.
eval is a tool, it is neither inherently good nor evil. It is justified whenever you are certain it is the right tool for what you are trying to accomplish.
A tool like eval is about evaluating code at runtime vs. "compile" time. Do you know what the code is when you launch Ruby? Then you probably don't need eval. Is your code generating code during runtime? then you probably need to eval it.
For example, the methods/functions needed in a recursive decent parser depend on the language being parsed. If your application builds such a parser on-the-fly, then it might make sense to use eval. You could write a generalized parser, but it might not be as elegant a solution.
"Programatically filling in a letrec in Scheme. Macros or eval?" is a question I posted about eval in Scheme, where its use is mostly unavoidable.
In general eval is a useful language feature when you want to run arbitrary code. This should be a rare thing but maybe you are making your own REPL or you want to expose the ruby run-time to the end user for some reason. It could happen and that is why the feature exists. If you are using it to work around some part of the language (e.g. global variables) then either the language is flawed or your understanding of the language is flawed. The solution is typically not to use eval but to either better understand the language or pick a different language.
It's worth noting that in ruby particulary instance_eval and class_eval have other uses.
You very likely use eval on a regular basis without even realizing it; it's how rubygems loads the contents of a Gemspec. Via rubygems/lib/specification.rb:
# Note: I've removed some lines from that listing to illustrate the core concept
def self.load(file)
code = File.read(file)
begin
_spec = eval code, binding, file # <-------- EVAL HAPPENS HERE
if Gem::Specification === _spec
return _spec
end
warn "[#{file}] isn't a Gem::Specification (#{_spec.class} instead)."
rescue SignalException, SystemExit
raise
rescue SyntaxError, Exception => e
warn "Invalid gemspec in [#{file}]: #{e}"
end
nil
end
Typically, a gem specification would look like this:
Gem::Specification.new do |s|
s.name = 'example'
s.version = '0.1.0'
s.licenses = ['MIT']
s.summary = "This is an example!"
s.description = "Much longer explanation of the example!"
s.authors = ["Ruby Coder"]
s.email = 'rubycoder#example.com'
s.files = ["lib/example.rb"]
s.homepage = 'https://rubygems.org/gems/example'
s.metadata = { "source_code_uri" => "https://github.com/example/example" }
end
Note that the gemspec file simply creates a new object but does not assign it nor send it anywhere.
Trying to load or require this file (or even executing it with Ruby) will not return the Gem::Specification value. eval is the only way to extract the value defined by an external ruby file.
One use of eval is compiling another language to ruby:
ruby_code = "(def foo (f a b) (mapv f (cons a b)))".compile_to_ruby
# "foo_proc = ->(f a b) { mapv_proc.call(f, (cons_proc.call(a, b)) }"
eval ruby_code
I use a 3D modeling software that implemented Ruby for writing custom text macros. In that software we are given access to model data in the form of name:value pairs accessed using the following format:
owner.name
#=> value
So for a 36 inch tall cabinet, I could access the height and convert its value to feet like so:
owner.height.to_f / 12
The main problem is that objects in that software have no unique identifiers aside from something called their schedule_number. If I want to name a variable using the schedule_number in the variable name so that I can call and use that value elsewhere, the only possible way I know to do that is by using eval:
eval "#{owner.schedule_number} = owner.height"

Why don't modules always honor 'require' in ruby?

(sorry I should have been clearer with the code the first time I posted this. Hope this makes sense)
File "size_specification.rb"
class SizeSpecification
def fits?
end
end
File "some_module.rb"
require 'size_specification'
module SomeModule
def self.sizes
YAML.load_file(File.dirname(__FILE__) + '/size_specification_data.yml')
end
end
File "size_specification_data.yml
---
- !ruby/object:SizeSpecification
height: 250
width: 300
Then when I call
SomeModule.sizes.first.fits?
I get an exception because "sizes" are Object's not SizeSpecification's so they don't have a "fits" function.
Are your settings and ruby installation ok? I created those 3 files and wrote what follows in "test.rb"
require 'yaml'
require "some_module"
SomeModule.sizes.first.fits?
Then I ran it.
$ ruby --version
ruby 1.8.6 (2008-06-20 patchlevel 230) [i486-linux]
$ ruby -w test.rb
$
No errors!
On second reading I'm a little confused, you seem to want to mix the class into module, which is porbably not so advisable. Also is the YAML supposed to load an array of the SizeSpecifications?
It appears to be that you're not mixing the Module into your class. If I run the test in irb then the require throws a LoadError. So I assume you've put two files together, if not dump it.
Normally you'd write the functionality in the module, then mix that into the class. so you may modify your code like this:
class SizeSpecification
include SomeModule
def fits?
end
end
Which will allow you to then say:
SizeSpecification::SomeModule.sizes
I think you should also be able to say:
SizeSpecification.sizes
However that requires you to take the self off the prefix of the sizes method definition.
Does that help?
The question code got me a little confused.
In general with Ruby, if that happens it's a good sign that I am trying to do things the wrong way.
It might be better to ask a question related to your actual intended outcome, rather than the specifics of a particular 'attack' on your problem. They we can say 'nonono, don't do that, do THIS' or 'ahhhhh, now I understand what you wanna do'

Resources