Ruby: reading files in turn - ruby

I have 2 files which should be run in order. I create run.rb file:
files =[
'./file-name-1.rb',
'./file-name-2.rb',
]
files.each do |file|
require file
end
And run it. Are there more correct methods of solving this problem?

Since it is file-name-2.rb that depends on file-name-1.rb, then it should be the one to require "file-name-1.rb". It should not be run.rb's job to know what another file's dependencies are. The correct way to solve this, then, is:
file-name-2.rb
require "file-name-1.rb"
# ...
run.rb
require "file-name-2.rb
# ....
And, as Frederick suggests in the comments above, it's unorthodox for a file to do work other than declaring constants (classes, modules) and/or methods at the time it's required. Instead, it should define methods to do that work, and then other files that require it can invoke those methods. This way you always know exactly when the work will be done, even if your application has a complex dependency structure.

Related

Ruby require multiple files without knowing their class names

Let's say I have an Application that requires different files. Each file contains a single purpose "thing". I call it "thing" because I'm unsure if it will be a class or a module.
The First Thing
# things/one.rb
class One
def self.do_it
"I'm the one"
end
end
And the second
# things/two.rb
class Two
def self.do_it
"I'm not the only one!"
end
end
Now I wanna require all files in the thingsdirectory and execute the do_it method. But I wanna do this dynamically - for every thing in things folder without even knowing their Classname.
# app.rb
Dir["things/*.rb"].each {|file| require file }
Keep in Mind: I simply wanna execute the code in the things files - it's not necessary that they are Classes or Modules
You can't reliably do this for a number of reasons, but the most important is that you're under no obligations to declare a single class in any given file, or a class at all. You've got the right idea with loading in all files in a particular directory, but if you need to operate on those there's two ways to crack that nut.
The easiest way is to declare these classes as a subclass of some already known parent. ActiveSupport has the descendents method to reflect on a particular class, that's part of Rails, but you can also do it the hard way if you have to.
The second easiest way is to correlate the names with the classes in them, and then convert filenames to class-names algorithmically. This is how the Rails autoloader works. cat.rb contains Cat, bad_dog.rb contains BadDog and so on.
If you look more closely at how things like Test::Unit work they take the first approach, any declared test subclasses are executed before the Ruby process terminates. It's a fairly simple system that puts no constraints on what the files are called or where and how the classes are declared.
Rails leans towards the second approach where the path communicates intent. This makes finding files more predictable but requires more discipline on the part of the developer to conform to that standard.
They both have merit. Pick the one that works for your use case.

Odd behavior from Ruby's require

I'm writing a Ruby gem with lots of nested classes and such. I'd like to keep a huge list of require statements out of my main Ruby file in the /lib directory, so instead I used the following:
Dir[ File.join( File.dirname(__FILE__), "**", "*.rb" ) ].each {|f| require f}
which totally worked fine until this morning when I added a new file (helper module) and now the library is acting like that file isn't loaded, even though it is. I checked with
puts "loaded" if defined?(RealtimeArgHelpers)
I also duplicated the require statement to check to see if my new file is getting returned
Dir[ File.join( File.dirname(__FILE__), "**", "*.rb" ) ].each {|f| puts f}
and it is. I have to manually require this one file. Out of nowhere. I have 101 other files currently being gathered by this statement and everything works perfectly fine. But not this one file. I don't have any name conflicts besides
/path/arg_helpers.rb
/path/realtime/agents.rb
/path/realtime/queues.rb
/path/realtime.rb
/path/realtime_arg_helpers.rb
which still shouldn't be a 'conflict.' I'm completely baffled by the seemingly random behavior, unless I'm doing something illegal in the language. I tried renaming the module, renaming the file, no dice. Why is this one file not getting loaded?
The problem comes from the way require works in Ruby. Whenever a file is required (let's only consider the case of a .rb source file to keep it simple), Ruby parses the file and immediately executes the instructions that are not in methods. So if you use classes that are defined in files yet to be loaded, Ruby won't find them.
I don't really know if there is a recommended way used by the gem authors to deal with that, but coming from a C/C++ background I tend to favour explicitly requiring the dependencies in each source file, rather than trying to maintain a giant sorted list in the main.rb (or equivalent).

load 'file.rb' versus require 'Module' in Ruby

I am confused about the difference between load 'file.rb' and require 'Module'. At Learn Ruby the Hard Way, the example of how to use a module is set up with two files (mystuff.rb and apple.rb):
mystuff.rb
module MyStuff
def MyStuff.apple()
puts "I AM APPLES!"
end
end
apple.rb
require 'mystuff'
MyStuff.apple()
However, when I run apple.rb, either in the Sublime Text console, or with ruby apple.rb, I get a Load Error. I have also tried require 'MyStuff', and require 'mystuff.rb', but I still get the Load Error.
So, I switched the first line of apple.rb to load 'mystuff.rb', which allows it to run. However, if I edit 'mystuff.rb' to be a definition of class MyStuff as opposed to a module MyStuff, there is no difference.
For reference, the Load Error is:
/Users/David/.rvm/rubies/ruby-2.0.0-p353/lib/ruby/site_ruby/2.0.0/rubygems/core_ext/kernel_require.rb:55:in require': cannot load such file -- mystuff (LoadError)`
I've peeked into kernel_require.rb and looked at the require definition, but since I'm a Ruby Nuby (indeed, a programming newbie), it was a little overwhelming. Since Learn Ruby the Hard Way hasn't been updated since 2012-10-05, there've probably been some syntax changes for modules. Yes?
require searches a pre-defined list of directories, as discussed in What are the paths that "require" looks up by default?. It's failing because it can't find the mystuff.rb in any of those directories.
load, on the other hand, will look for files in the current directory.
As for:
However, if I edit 'mystuff.rb' to be a definition of class MyStuff as
opposed to a module MyStuff, there is no difference.
I'm not sure I understand what you mean by "no difference". If you mean that the require and load continue to fail and succeed, respectively, that makes sense, as the require failure is independent of the content of the file contents and the code you're testing behaves the same independent of whether Mystuff is a class or a vanilla module.
You can solve this easily by changing
require 'mystuff'
to
require_relative './mystuff'

Ruby, including module in current directory

I am currently working through the Well Grounded Rubyist. Great book so far. I am stuck on something I don't quite get with ruby. I have two files
In ModuleTester.rb
class ModuleTester
include MyFirstModule
end
mt = ModuleTester.new
mt.say_hello
In MyFirstModule.rb
module MyFirstModule
def say_hello
puts "hello"
end
end
When I run 'ruby ModuleTester.rb', I get the following message:
ModuleTester.rb:2:in <class:ModuleTester>': uninitialized constant ModuleTester::MyFirstModule (NameError)
from ModuleTester.rb:1:in'
From what I have found online, the current directory isn't in the the namespace, so it can't see the file. But, the include statement doesn't take a string to let me give the path. Since the include statement and require statements do different things, I am absolutely lost
as to how to get the include statement to recognize the module. I looked through other questions, but they all seem to be using the require statement. Any hints are greatly appreciated.
You use require to load a file, not include. :-)
First, you have to require the file containing the module you want to include. Then you can include the module.
If you're using Rails, it has some magic to automagically require the right file first. But otherwise, you have to do it yourself.
You need to require the file before you can use types defined in it. *
# my_first_module.rb
module MyFirstModule
def say_hello
puts 'hello'
end
end
Note the require at the beginning of the following:
# module_tester.rb
require 'my_first_module'
class ModuleTester
include MyFirstModule
end
mt = ModuleTester.new
mt.say_hello
The require method actually loads and executes the script specified, using the Ruby VM's load path ($: or $LOAD_PATH) to find it when the argument is not an absolute path.
The include method, on the other hand actually mixes in a Module's methods into the current class. It's closely related to extend. The Well Grounded Rubyist does a great job of covering all this, though, so I encourage you to continue plugging through it.
See the #require, #include and #extend docs for more information.
* Things work a bit differently when using Rubygems and/or Bundler, but getting into those details is likely to confuse you more than it's worth at this point.

Adding a directory to $LOAD_PATH (Ruby)

I have seen two commonly used techniques for adding the directory of the file currently being executed to the $LOAD_PATH (or $:). I see the advantages of doing this in case you're not working with a gem. One seems more verbose than the other, obviously, but is there a reason to go with one over the other?
The first, verbose method (could be overkill):
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
and the more straightforward, quick-and-dirty:
$:.unshift File.dirname(__FILE__)
Any reason to go with one over the other?
The Ruby load path is very commonly seen written as $: , but just because it is short, does not make it better. If you prefer clarity to cleverness, or if brevity for its own sake makes you itchy, you needn't do it just because everyone else is.
Say hello to ...
$LOAD_PATH
... and say goodbye to ...
# I don't quite understand what this is doing...
$:
I would say go with $:.unshift File.dirname(__FILE__) over the other one, simply because I've seen much more usage of it in code than the $LOAD_PATH one, and it's shorter too!
I'm not too fond on the 'quick-and-dirty' way.
Anyone new to Ruby will be pondering what $:. is.
I find this more obvious.
libdir = File.dirname(__FILE__)
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
Or if I care about having the full path...
libdir = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
UPDATE 2009/09/10
As of late I've been doing the following:
$:.unshift(File.expand_path(File.dirname(__FILE__))) unless
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
I've seen it in a whole bunch of different ruby projects while browsing GitHub.
Seems to be the convention?
If you type script/console in your Rails project and enter $:, you'll get an array that includes all the directories needed to load Ruby. The take-away from this little exercise is that $: is an array. That being so, you can perform functions on it like prepending other directories with the unshift method or the << operator. As you implied in your statement $: and $LOAD_PATH are the same.
The disadvantage with doing it the quick and dirty way as you mentioned is this: if you already have the directory in your boot path, it will repeat itself.
Example:
I have a plugin I created called todo. My directory is structured like so:
/---vendor
|
|---/plugins
|
|---/todo
|
|---/lib
|
|---/app
|
|---/models
|---/controllers
|
|---/rails
|
|---init.rb
In the init.rb file I entered the following code:
## In vendor/plugins/todo/rails/init.rb
%w{ models controllers models }.each do |dir|
path = File.expand_path(File.join(File.dirname(__FILE__), '../lib', 'app', dir))
$LOAD_PATH << path
ActiveSupport::Dependencies.load_paths << path
ActiveSupport::Dependencies.load_once_paths.delete(path)
end
Note how I tell the code block to perform the actions inside the block to the strings 'models', 'controllers', and 'models', where I repeat 'models'. (FYI, %w{ ... } is just another way to tell Ruby to hold an array of strings). When I run script/console, I type the following:
>> puts $:
And I type this so that it is easier to read the contents in the string. The output I get is:
...
...
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/controllers
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models
As you can see, though this is as simple an example I could create while using a project I'm currently working on, if you're not careful the quick and dirty way will lead to repeated paths. The longer way will check for repeated paths and make sure they don't occur.
If you're an experienced Rails programmer, you probably have a very good idea of what you're doing and likely not make the mistake of repeating paths. If you're a newbie, I would go with the longer way until you understand really what you're doing.
Best I have come across for adding a dir via relative path when using Rspec. I find it verbose enough but also still a nice one liner.
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
There is a gem which will let you setup your load path with nicer and cleaner code. Check this out: https://github.com/nayyara-samuel/load-path.
It also has good documentation
My 2ยข: I like $LOAD_PATH rather than $:. I'm getting old... I've studied 92,000 languages. I find it hard to keep track of all the customs and idioms.
I've come to abhor namespace pollution.
Last, when I deal with paths, I always delete and then either append or prepend -- depending upon how I want the search to proceed. Thus, I do:
1.times do
models_dir = "#{File.expand_path(File.dirname(__FILE__))}/models"
$LOAD_PATH.delete(models_dir)
$LOAD_PATH.unshift(models_dir)
end
I know it's been a long time since this question was first asked, but I have an additional answer that I want to share.
I have several Ruby applications that were developed by another programmer over several years, and they re-use the same classes in the different applications although they might access the same database. Since this violates the DRY rule, I decided to create a class library to be shared by all of the Ruby applications. I could have put it in the main Ruby library, but that would hide custom code in the common codebase which I didn't want to do.
I had a problem where I had a name conflict between an already defined name "profile.rb", and a class I was using. This conflict wasn't a problem until I tried to create the common code library. Normally, Ruby searches application locations first, then goes to the $LOAD_PATH locations.
The application_controller.rb could not find the class I created, and threw an error on the original definition because it is not a class. Since I removed the class definition from the app/models section of the application, Ruby could not find it there and went looking for it in the Ruby paths.
So, I modified the $LOAD_PATH variable to include a path to the library directory I was using. This can be done in the environment.rb file at initialization time.
Even with the new directory added to the search path, Ruby was throwing an error because it was preferentially taking the system-defined file first. The search path in the $LOAD_PATH variable preferentially searches the Ruby paths first.
So, I needed to change the search order so that Ruby found the class in my common library before it searched the built-in libraries.
This code did it in the environment.rb file:
Rails::Initializer.run do |config|
* * * * *
path = []
path.concat($LOAD_PATH)
$LOAD_PATH.clear
$LOAD_PATH << 'C:\web\common\lib'
$LOAD_PATH << 'C:\web\common'
$LOAD_PATH.concat(path)
* * * * *
end
I don't think you can use any of the advanced coding constructs given before at this level, but it works just fine if you want to setup something at initialization time in your app. You must maintain the original order of the original $LOAD_PATH variable when it is added back to the new variable otherwise some of the main Ruby classes get lost.
In the application_controller.rb file, I simply use a
require 'profile'
require 'etc' #etc
and this loads the custom library files for the entire application, i.e., I don't have to use require commands in every controller.
For me, this was the solution I was looking for, and I thought I would add it to this answer to pass the information along.

Resources