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?
Related
I have a Ruby module in a file called my_module.rb:
module My_module
def my_module_method
puts 'inside my method'
end
end
In a file my_class.rb in the same folder, I have a class contained within the module.
module My_module
class My_class
def my_object_method
My_module.my_module_method
end
end
end
My_module::My_class.new.my_object_method => 'undefined method 'my_module_method''
I was not expecting this error. I assumed that Ruby would run into the line 'My_module.my_module_method' and search for a module called 'My_module' and a method within it called 'my_module_method.' This is what Java does, for example. However, Ruby does not do this. In order to get my_object_method to work, I have to write in my_class.rb:
require 'my_module.rb'
Why doesn't Ruby search for My_module when I call my_object_method? It seems obvious what it should search for and therefore redundant to require the programmer to explicitly write 'yes, Ruby, please allow me to make calls to module-wide methods.' What am I missing?
Ruby doesn't automatically load files. If you need a code from some file, you have to load it (by calling require) explicitly.
Thus, when you run "ruby my_class.rb" it loads only this file and you have to define dependencies between files by yourself.
You seem to have a misunderstanding of how to define a class method. In order to make your method call work, you could define it as def self.my_method_name.
In both classes and modules, methods work the same when you define them as class methods using self. or alternatively the class << self syntax. However instance methods (methods without the self.) work differently in these 2 cases. In classes, as you seem to understand, they're accessible once you instantiate the class using .new. In modules, they're only accessible if you include or extend.
See also:
difference between class method , instance method , instance variable , class variable?
http://www.rortuts.com/ruby/ruby-include-vs-extend/
Oh any by the way. Ruby doesn't enforce any convention where you have 1 file per class (named identically). You need to manually require files wherever you need them. Although there are some frameworks such as Rails which auto-require files, and enforce naming conventions.
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 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.
I want to build an index for different objects in my Rails project and would like to add a 'count_occurences' method that I can call on String objects.
I saw I could do something like
class String
def self.count_occurences
do_something_here
end
end
What's the exact way to define this method, and where to put the code in my Rails project?
Thanks
You can define a new class in your application at lib/ext/string.rb and put this content in it:
class String
def to_magic
"magic"
end
end
To load this class, you will need to require it in your config/application.rb file or in an initializer. If you had many of these extensions, an initializer is better! The way to load it is simple:
require 'ext/string'
The to_magic method will then be available on instances of the String class inside your application / console, i.e.:
>> "not magic".to_magic
=> "magic"
No plugins necessary.
I know this is an old thread, but it doesn't seem as if the accepted solution works in Rails 4+ (at least not for me). Putting the extension rb file in to config/initializers worked.
Alternatively, you can add /lib to the Rails autoloader (in config/application.rb, in the Application class:
config.autoload_paths += %W(#{config.root}/lib)
require 'ext/string'
See this:
http://brettu.com/rails-ruby-tips-203-load-lib-files-in-rails-4/
When you want to extend some core class then you usually want to create a plugin (it is handy when need this code in another application). Here you can find a guide how to create a plugin http://guides.rubyonrails.org/plugins.html and point #3 show you how to extend String class: http://guides.rubyonrails.org/plugins.html#extending-core-classes
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