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.
Related
I've developed a set of Ruby scripts. Each of them should be 'self-contained', so a user can run it on its own. But also I would like to use them to build other scripts, I mean for example to use its methods but also to run it as a whole, without doing ` script.rb`.
So far what I have is just a couple of scripts (separate files) where I have no classes, just a couple of methods. The processing of taking user input and running those methods is outside of any functions. I see that this model may be not right.
My question is, what should I do now to keep every script self contained but also to allow other scripts to use it? Should every script just contain a class with a main method that I would run object.main?
Or maybe my approach of writing a simple scripts, no classes is also good?
If I start a new script, should I always go the objective way?
When I write a one off script, I often wrap it in a class. You've pointed out some advantages of doing this including reuse and cleaner documentation.
I find that there are several levels of polish for scripts depending on how they are going to be used. If the script is run once and never used again, I may not wrap it in a class. If it's important (taking backups of production systems), it's probably worth putting it in full gem form and writing tests. Somewhere in the middle is the single purpose class. Generally this means you're taking the code that's not in a method and putting it in the class constructor.
This:
#!ruby
def amethod(i)
i+1
end
ARGF.each do |l|
if l.chomp.to_i > 0
puts amethod(l.chomp.to_i)
end
end
Becomes:
#!ruby
class OneAdder
def amethod(i)
i+1
end
def initialize
ARGF.each do |l|
if l.chomp.to_i > 0
puts amethod(l.chomp.to_i)
end
end
end
end
OneAdder.new
A quirk of Ruby's require is that, while in general, it will only load a file once, if that file is accessible via multiple paths (e.g. symlinks), it can be required multiple times. This causes problems when there are things like class-level metaprogramming, or in general a code that should only be executed once on file loading, getting executed multiple times.
Is there any way, from inside a Ruby class definition, to tell whether the class has been defined before? I thought defined? or Object.const_get might tell me, but from those it looks like the class is defined as soon as it's opened.
This is not an answer to your question in the second paragraph, but a solution to the issue in your first paragraph. Actually, you cannot avoid multiple file loads by checking whether a class was defined already.
Instead of doing:
require some_file_name
do:
require File.realpath(some_file_name)
By doing so, different symbolic links pointing to the same real file would be normalized to the same real file name, and hence multiple loading of them would be correctly filtered by require.
Cf. this question and the answer given there.
The real solution is that requiring that a piece of code is only executed once is bad design and you should fix that.
However, what you could do is simply set some flag that the code has already been executed and check that flag. E.g.:
class Foo
unless #__executed__
def bla; end
puts 'Test'
end
#__executed__ = true
end
I've discovered that I can split a class into multiple files by calling class <sameclassname>; <code> ;end from within each file. I've decided to divide up a very large class this way. The advantages I see:
I can have separate spec files called by guard to reduce spec time.
Forces me to organize and compartmentalize my code
Are there any pitfalls to this method? I can't find any information about people doing it.
I often do this in my gem files to manage documentation and avoid long files.
The only issues I found (which are solvable) are:
Initialization of class or module data should be thoughtfully managed. As each 'compartment' (file) is updated, the initialization data might need updating, but that data is often in a different file and we are (after all) fallible.
In my GReactor project (edit: deprecated), I wrote a different initialization method for each section and called all of them in the main initialization method.
Since each 'compartment' of the class or module is in a different file, it is easy to forget that they all share the same namespace, so more care should be taken when naming variables and methods.
The code in each file is executed in the order of the files being loaded (much like it would be if you were writing one long file)... but since you 'close' the class/module between each file, than your method declaration order might be important. Care should be taken when requiring the files, so that the desired order of the code execution is preserved.
The GReactor is a good example for managing a Mega-Module with a large API by compartmentalizing the different aspects of the module in different files.
There are no other pitfalls or issues that I have experienced.
Defining / reopening the same class in many different files makes it harder to locate the source of any given method, since there's no one clear place for it.
This also opens up the possibility of nasty loading sequence bugs, eg. file A is trying to call a method in file B, but file B has not loaded yet.
Having a very large class is a sign that the class is trying to do too much, and should be split up into smaller modules/subclasses. Sandi Metz's POODR recommends limiting classes to under 100 lines, among other guidelines.
In Ruby classes are never closed. What you call "splitting" is actually just reopening the class. You can reopen classes and add methods to them at any time. If you define a class in file A and include it in file B, even if you reopen the class in file B it'll still contain all the code from file A. I personally prefer only to reopen a class when I have to. It sounds like in your case, I would define my class in one file. I think this method is better organized and has a lower risk of interfering with previously defined methods. More on the subject at rubylearning.
Here's a good collection of Ruby design patters, or actually design pattern examples in Ruby: https://github.com/nslocum/design-patterns-in-ruby
Take a look at decorator as a good way to achieve modularity without a rigid parent<->child tree of classes.
The only pitfall is that your class is split in multiple files, that you need to menage. User of your class would only need to require the second file, so if your class is part of gem or some package, they probably wouldn't even be aware that it was ever reopened.
I'm beginning with the Ruby programming language and I'm interested in understanding it in depth before I start studding the Rails framework.
I'm currently a little disappointed because everybody seams to care only about the Rails framework, and other aspects of the language are just not discussed in depth, such as its class loading mechanism.
Considering that I'm starting by doing some desktop/console experiments, I would like to better understand the following matters:
Is it a good practice to place each Ruby class in a separate Ruby file? (*.rb)
If I have, let's say .. 10 classes .. and all of them reference each other, by instantiating one another and calling each other's methods, should I add a 'require' statement in each file to state which classes are required by the class in that file? (just like we do with 'import' statements in each Java class file?)
Is there a difference in placing a 'require' statement before or after (inside) a class declaration?
What could be considered a proper Ruby program's 'entry point'? It seams to me that any .rb script will suffice, since the language doesn't have a convention like C or Java where we always need a 'main' function of method.
Is class loading considered a 'phase' in the execution of a Ruby program? Are we supposed to load all the classes that are needed by the application right at the start?
Shouldn't the interpreter itself be responsible for finding and loading classes as we run the code that needs them? By searching the paths in the $LOAD_PATH variable, like Java does with its $CLASSPATH?
Thank you.
In general terms, it's a good practice to create a separate .rb file for each Ruby class unless the classes are of a utility nature and are too trivial to warrant separation. An instance of this would be a custom Exception derived class where putting it in a separate file would be more trouble than its worth.
Tradition holds that the name of the class and the filename are related. Where the class is called ExampleClass, the file is called example_class, the "underscored" version of same. There are occasions when you'll buck this convention, but so long as you're consistent about it there shouldn't be problems. The Rails ActiveSupport auto-loader will help you out a lot if you follow convention, so a lot of people follow this practice.
Likewise, you'll want to organize your application into folders like lib and bin to separate command-line scripts from back-end libraries. The command-line scripts do not usually have a .rb extension, whereas the libraries should.
When it comes to require, this should be used sparingly. If you structure your library files correctly they can all load automatically once you've called require on the top-level one. This is done with the autoload feature.
For example, lib/example_class.rb might look like:
class ExampleClass
class SpecialException < Exception
end
autoload(:Foo, 'example_class/foo')
# ...
end
You would organize other things under separate directories or files, like lib/example_class/foo.rb which could contain:
class ExampleClass::Foo
# ...
end
You can keep chaining autoloads all the way down. This has the advantage of only loading modules that are actually referenced.
Sometimes you'll want to defer a require to somewhere inside the class implementation. This is useful if you want to avoid loading in a heavy library unless a particular feature is used, where this feature is unlikely to be used under ordinary circumstances.
For example, you might not want to load the YAML library unless you're doing some debugging:
def debug_export_to_yaml
require 'yaml'
YAML.dump(some_stuff)
end
If you look at the structure of common Ruby gems, the "entry point" is often the top-level of your library or a utility script that includes this library. So for an example ExampleLibrary, your entry point would be lib/example_library.rb which would be structured to include the rest on demand. You might also have a script bin/library_tool that would do this for you.
As for when to load things, if there's a very high chance of something getting used, load it up front to pay the price early, so called "eager loading". If there's a low chance of it getting used, load it on demand, or leave it "lazy loaded" as it's called.
Have a look at the source of some simple but popular gems to get a sense of how most people structure their applications.
I'll try to help you with the first one:
Is it a good practice to place each Ruby class in a separate Ruby file? (*.rb)
It comes down to how closely related those classes are. Let's see a few examples. Look this class: https://github.com/resque/resque/blob/master/lib/resque.rb
, it "imports" the functionality of several classes that, although they work together, they are not closely related to be bundled together.
On the other hand, take a look at this module: https://github.com/resque/resque/blob/master/lib/resque/errors.rb. It bundles 5 different classes, but these do belong together since they are all essentially representing the same.
Additionally, from a design standpoint a good rule of thump could be asking yourself, who else is using this class/ functionality (meaning which other parts of the code base needs it)?
Let's say that you want to represent a Click and WheelScroll performed by a Mouse. It would make more sense in this trivial example, that those classes be bundled together:
module ComputerPart
class Mouse; end
class WheelScroll; end
class Click; end
end
Finally, I would recommend that you peruse the code of some of these popular projects to kind of get the feeling how the community usually make these decisions.
1.) I follow this practice, but it is not necessary, you can put a bunch of classes in one file if you want.
2.) If the classes are in the same file, no, they will all be accessible when you run the script. If they are in separate files then you should require them, you can also require the entire directory that the file(self) is in.
3.)Yes, it should be at the top of the file.
4.) In ruby everything descends from the Main object, the Interpreter just handles creating it for you. If you are writing OO ruby and not just scripts, then the entry point will be the init method of the first class you call.
5.) Yes, before the program runs it loads up all the dependencies.
6.) I think it does this, all you have to do is require the proper files at the top of the files, after that you can use them as you wish without having to implicitly load them again.
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.