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
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 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?
In my Rails 3.1 app (with Ruby 1.9), I have a Deployer1 class that is in a worker subdirectory below the model directory
I am trying to load/instantiate this class dynamically with this code:
clazz = item.deployer_class # deployer_class is the class name in a string
deployer_class = Object.const_get clazz
deployer = deployer_class.new
If I dont use namespaces, eg something global like this:
class Deployer1
end
Then it works fine (deployer_class="Deployer1") - it can load the class and create the object.
If I try and put it into a module to namespace it a bit, like this:
module Worker
class Deployer1
end
end
It doesnt work (deployer_class="Worker::Deployer1") - gives an error about missing constant, which I believe means it cannot find the class.
I can access the class generally in my Rails code in a static way (Worker::Deployer1.new) - so Rails is configured correctly to load this, perhaps I am loading it the wrong way...
EDIT:
So, as per Vlad's answer, the solution I went for is:
deployer_class.constantize.new
Thanks
Chris
try using constantize instead:
module Wtf
class Damm
end
end
#=> nil
'Wtf::Damm'.constantize
#=> Wtf::Damm
Object.const_get 'Wtf::Damm'
#=> Wtf::Damm
Object does not know a constant named Worker::Deployer1, which is why Object.const_get 'Worker::Deployer1' doesn't work. Object only knows a constant Worker. What does work is Worker.const_get 'Deployer1'.
Vlad Khomisch's answer works, because if you look at the implementation of constantize, this is exactly what it does: it splits the string on '::' and recursively const_get's.
I want to expand a method to the String class in Sinatra, in the erb file, do something like
<%= 'some string'.my_method %>
but I don't know how to put the definition code:
String.class_eval do
def my_mythod
some_code
end
end
By the way I'm using the sinatra modular coding style
I tend to stick code like this in its own file, under the lib/ext folder. Then, you can require this file from your Sinatra app.
Under lib/ext/string.rb:
class String
my_mythod
some_code
end
end
Then add the following to your Sinatra app, assuming your base class file is inside the lib folder:
require File.dirname(__FILE__) + '/ext/string'
I'd be interested to see what over people think on this as well.
seems to work wherever i put it (is this a good observation?)
anywhere that is evaluated on script start
drop it in the app class itself along with all the other fields and methods
should work in the helper class too
This may seem like a silly question but I have a model named Ad and I have a library name auto which contains a class Ad(lib/auto.rb).
#auto.rb lib file
module auto
class Ad
def initialize
...
end
def convert
# here I would like to access my model class Ad
# ::Ad does not work. Requiring it does not work either.
end
end
end
Does Rails 3 store models under some global namespace?
Am I missing something or you are defining auto::Ad?
If so, then ::Ad will never work. use auto::Ad or Ad (from within the auto module).
If you really don't want the auto namespace . Remvoe the module auto part in your code.
I think that you can't get it straight from your external class. But you can pass it from your exact model.
I think this can be useful
http://guides.rubyonrails.org/plugins.html#add-an-acts_as-method-to-active-record