I am implementing gem, I want to add some custom methods to String class. So, I created in 'lib' directory sub-directory 'core_ext', and placed there file 'string.rb'.
string.rb contains:
class String
def custom_method
some_action
end
end
In my main file I made:
require 'core_ext/string.rb'
but it doesn't have any influence on String class,
If I change file's name to some other name, like 'my_string.rb' and change line with require, everything will work properly. Why I can't use 'string.rb' as name of file?
If there's a file called core_ext/string that's already loaded anywhere else, this one is considered loaded as well. You may need to come up with a different name.
The require_relative feature of Ruby 1.9 that p11y points out is a much better solution.
Related
I have the name of a test class (as a string), eg."Reports::SalesReportTest". Without loading all the test files into memory, how can I find out which file Ruby would have loaded for this test-class to work?
To rephrase, is there a function which takes a class/module name (as a string), and returns the file-path that needs to be loaded for the class/module to work?
You can't. First, a class definition can span several files. Secondly, a class can be created dynamically, for instance using eval.
If you know that the application is always written in a style that each class is fully defined in one and only one file, and that each definition follows certain style rules (for instance, each class CLASSNAME starts on a line of its own) you could read all the files in your application directory and scan for the class definition. Don't forget that there might be lines commented out with =begin and =end, which you will have to ignore.
Given that you are using rails you could determine where it anticipates the file being (or at least where rails will look) but that does not actually mean it exists. For Example:
class_name = "Reports::SalesReportTest".underscore
potential_paths = ActiveSupport::Dependencies.autoload_paths.map do |path|
File.join(path.to_s,"#{class_name}.rb")
end
That will list all the locations for which rails will try and load the file from.
If you want to know that the file exists then you can ask ruby that too:
class_name = "Reports::SalesReportTest".underscore
loadable_paths = ActiveSupport::Dependencies.autoload_paths.map do |path|
File.exist?(File.join(path.to_s,"#{class_name}.rb"))
end
I currently creating a gem, so, I have a folder with different files, which contains different classes, this folder will be updated with more files and also current ones will be updated as well, in another file I have a module that should contain these classes.
Currently, I add manually to the module all the classes:
File1.rb:
module MyModule
class ClassA
# code here
end
end
File2.rb:
module MyModule
class ClassB
# code here
end
end
But, since I will add more classes and current classes will be updated this is not optimal and very dangerous to maintain clean, so is there any other way to add classes in different files in one module set in another file?
Thanks in advance
No there is no another way and I don't see any danger in doing it the way you did (correct way).
I want to extend the functionality of Array, add a method that checks if a key exists in array and that the array is not empty, where to write the class and how to make sure it's loaded?
You can either put it into lib/ and make sure that it is autoloaded as outlined in the answer by shioyama; or you could just put it into an initializer. I like the initializer approach a bit better, since it is easier (you get autoloading for free).
I usually create a core_ext subdirectory of the initializers directory and put my core class extensions in there. I always try to put the name of the class that is being extended and a description of what I add into the filename, so in you case I would create a file RAILS_ROOT/config/initializers/core_ext/array_my_function containing:
module MyFunctionForArray
def my_function(arg1, arg2)
# ...
end
end
Array.send :include, MyFunctionForArray
I always try to not reopen the class and extend it directly but to put my extensions into a module and then including this module into the class to extend.
Standard way to do it is to put the code in lib/ and make sure it's autoloaded by rails by adding a line to config/application.rb:
config.autoload_paths += Dir["#{config.root}/lib/**/"]
Then in your code, just make sure you require it wherever you use it. If you want to apply it everywhere, create an initializer in config/initializers with a line:
require 'my_array'
Where my_array.rb is the name of the file in lib where you have the file. That will make it available in your models, controllers, views, etc.
See also: Best way to load module/class from lib folder in Rails 3?
Also, beware of one pitfall of autoloading a directory structure in ruby (not just rails), explained in this answer: Best way to load module/class from lib folder in Rails 3?
I have two classes called Person and Animal that have a lot of methods in them. I have a need to export some data from these classes to CSV. I also want to refactor the CSV specific code out of the class files themselves (and move it to modules). So, I created directories called person/ and animal/, and put a csv_export.rb file in each (with a CSVExport Module).
person.rb
person/csv_export.rb
animal.rb
animal/csv_export.rb
CSV exported data is different enough for the two classes that I can't just have one common csv_export.rb file (for now).
Example of person.rb:
class Person
include CSVExport
# numerous methods...
end
Example of csv_export.rb:
module CSVExport
def to_csv
# some logic
end
end
My question is what's the correct way to namespace these two modules so they load properly? And what is the proper way to require them within Person/Animal (assuming the files aren't loaded by something like Rails)?
I would call the modules Person::CSVExport and Animal::CSVExport. You can require them (from each individual file) like this:
require File.join(File.dirname(__FILE__), 'animal', 'csv_export.rb')
require File.join(File.dirname(__FILE__), 'person', 'csv_export.rb')
This doesn't work from inside irb (at least in Ruby 1.9.2p290, which I am running), but it does work if a Ruby program is run from a file.
You made a good point; if you load (for example) animal/csv_export.rb at the top of animal.rb, at that point Animal is an undefined constant. You can get around that like this:
class Animal
module CSVExport
# contents
end
end
Note that if you can, a better approach might be to build some low-level utility methods for yourself, which could make it possible to code the CSV export logic at a higher level of abstraction, and make that code short enough to go in animal.rb and person.rb without overwhelming them. I don't know what those utilities would be exactly; I'd have to see the code to offer specific suggestions.
I have a Date class which I would like to use to overwrite Ruby's Date class. However, whenever I do a require 'Date' in my other files, it includes Ruby's Date class and not my own.
I thought that putting it in a module would work well, so I did so within the Date.rb file:
module myModule
class Date
#...
end
end
However I still can't figure out how to make my other classes include THIS Date class and not the built-in class. How can I achieve this?
All help is appreciated and thanks in advance!
Adam,
Your best bet is to simply follow some conventions:
Always name your filenames lower case (date.rb not Date.rb)
Put your files in a specific directory inside your library (lib is a good candidate)
Don't name your files the same thing as built in Ruby classes (call it my_date.rb or something) or if your class/module is name-spaced inside a module, put it in a folder of the module name (lib/my_module/date.rb).
This removes any ambiguity in which file you are trying to load. If you absolutely must keep it named date.rb, then load it with the full path by doing something like: File.join(File.dirname(__FILE__), "date.rb").
For debugging purposes you can look at the following special variables to see what's being loaded instead of your file:
$: will show the load path (i.e. every directory it looks in to find files to require. You will note that the current directory (.) is last. This is why your file isn't loaded -- it looks in the system path first. You can always move your current directory to the front of the load path as a solution by doing $:.unshift(File.dirname(__FILE__)), but I'd try one of the above approaches before resorting to this
$" shows every file that has been required into your current environment so far.
require 'path/to/Date.rb'
class MyClass
include MyModule::Date
end
First, you need to require the correct file. Often the right thing happens when you do require 'date' and it's resolved to a file based on your $LOAD_PATH. You can be more specific by putting your date.rb in a directory so you can require 'my_module/date' or just use a relative path like ./date
You can then specify the module hierarchy when referring to this class:
::MyModule::Date
Or you can include it wherever you prefer to call this Date over the standard one without specifying:
class Event
include MyModule
def initialize
#date = Date.today # refers to MyModule::Date
end
end