Re-opening a class, where to put this code? - ruby-on-rails

I have a class that I want to re-open that is in a gem.
I put it in:
/lib/ClassName.rb
class ClassName
class << self
def some_method(a)
end
end
end
Now if I call this class method, it says its an undefined method.
I do have autoload set to the /lib folder.
Is this the wrong way to do this?

I find that sometimes I need to manually require certain files (especially one's that monkey patch existing classes/modules), even though the lib folder is being configured to autoload. I have yet to figure out why exactly.
To get around this, in config/initializers/application.rb (create it if necessary) I would require the file manually:
require 'ClassName'

Related

Rails Module/Folder Naming Convention

I'm having a problem with a module name and the folder structure.
I have a model defined as
module API
module RESTv2
class User
end
end
end
The folder structure looks like
models/api/restv2/user.rb
When trying to access the class, I get an uninitialized constant error. However, if I change the module name to REST and the folder to /rest, I don't get the error.
I assume the problem has to do with the naming of the folder, and I've tried all different combos of /rest_v_2, /rest_v2, /restv_2, etc.
Any suggestions?
Rails uses the 'underscore' method on a module or class name to try and figure out what file to load when it comes across a constant it doesn't know yet. When you run your module through this method, it doesn't seem to give the most intuitive result:
"RESTv2".underscore
# => "res_tv2"
I'm not sure why underscore makes this choice, but I bet renaming your module dir to the above would fix your issue (though I think I'd prefer just renaming it to "RestV2 or RESTV2 so the directory name is sane).
You'll need to configure Rails to autoload in the subdirectories of the app/model directory. Put this in your config/application.rb:
config.autoload_paths += Dir["#{config.root}/app/models/**/"]
Then you should be able to autoload those files.
Also, your likely filename will have to be app/model/api/res_tv2/user.rb, as Rails uses String.underscore to determine the filename. I'd just call it API::V2::User to avoid headaches, unless you have more than one type of API.

Uninitialized constant trying to reopen a Class

I am using a plugin in Rails, and I call its methods without problems:
plugin_module::class_inside_module.method_a(...)
I want to re-open the class_inside_module and add a new method, I tried in many different ways. I can't figure out why in this way doesn't work:
class plugin_module::class_inside_module
def new_method
puts 'new method'
end
end
I get the error: uninitialized constant plugin_module, but how is possible if I can call without problem plugin_module::class_inside_module.any_methods ?
Do you know why I get that error ? why "uninitialized constant" ? (it is a class declaration :-O )
Do you have any ideas how I can add a new methods in a class inside a module (that is part of a plugin) ?
Thank you,
Alessandro
If you have written your class and module-names like you did, so plugin_module instead of PluginModule this is against ruby/rails standards, and rails will not be able to automatically find the class and module.
If you write something like
module MyModule
class MyClass
end
end
Rails will expect this file to be located in lib\my_module\my_class.
But this can always easily be overwritten by explicitly doing a require.
So in your case, when you write
module plugin_module::class_inside_module
Rails will not know where to find the module plugin_module.
This way of writing only works if module plugin_module is previously defined (and loaded).
So either add the correct require, or rename your modules to standard rails naming, or write it as follows:
module plugin_module
class class_inside_module
This way will also work, because now the order no longer matters.
If the module is not known yet, this will define the module as well.
Either you are re-opening the class, or you define it first (and the actual definition will actually reopen it).
Hope this helps.
Have you tried reopening the module that's wrapping the class, rather than relying on ::?
module plugin_module
class class_inside_module
def new_method
puts 'new_method'
end
end
end
By the way, you know that the proper name for modules and classes is use CamelCase with a capital first letter?
module PluginModule
class ClassInsideModule
def new_method
puts 'new_method'
end
end
end

override lib module method for specific rails environment

I've got a library module I'd like to override based on the rails environment I'm running in
Module is located in lib/package/my_module.rb:
module Package
module MyModule
puts "Defining original module"
def foo
puts "This is the original foo"
end
end
end
I have been able to partially solve with the info at Overriding a module method from a gem in Rails - specifically, in my environments/dev_stub.rb:
Package::MyModule.module_eval do
puts "Defining override"
def foo
puts "This is foo override"
end
end
(The other solution at that link seems to cause errors when rails tries to lookup other classes related to package)
Now, this seems to get me most of the way there, and works if I set
config.cache_classes = true
...but I want to use this as a stub development environment, and the comment recommendation on this value for a dev environment is to use false... in which case the override only works the first time the module is included, and any subsequent times, it uses the original.
My question: Am I going about this the right way? I could hack up the lib module itself to conditionally override based on RAILS_ENV, but I'd like to keep it cleaner than that...
Edit
My use case for this is to reference it from a controller function. If I have
class SomethingController < ApplicationController
def show
Package::MyModule.foo
end
end
and config.cache_classes=false (which I ideally want since it is a development environment), and access the action through my web browser (http://localhost/something/show) then the first time I hit it, my override is loaded and it works, but the second and any subsequent times, the original library class is reloaded (outputs "Defining original module" on my console without "Defining override"), and the override is lost.
Another alternative I tried was add something like config.load_paths += %W( #{RAILS_ROOT}/lib_patch/#{RAILS_ENV}) to environment.rb - but defining the same module/class didn't quite work without putting in an explicit hook in the original library to basically load the patch if it existed
Edit 2 (in response to #apneadiving answer)
I've tried doing this without module_eval, and just using the following in development_stub.rb:
require 'package/my_module'
module Package
module MyModule
puts "Defining override"
def foo
puts "This is foo override"
end
end
end
The problem I initially had with doing this is that Rails no longer automatically finds all content in my lib directory, and I need to sprinkle 'require' statements throughout all other lib files (and my controllers that reference the libs) to cover all of their dependencies. Although this is all done, it does work, but it also has a similar effect as config.cache_classes=true does, in that all the lib classes are not reloaded on change, even in my regular development environment that does not have a monkey-patch (since all the 'require' statements are added).
Setting config.cache_classes=true in dev_stub.rb and using module_eval to define the patch as described in the question seems the way to go for what the goal is here - to create an environment specific patch for a module that doesn't impact the other environments in both code path and Rails class loading behavior.
you could simply override the module and it's instance without module_eval.
I guess your module is included as a Mixin and it's methods aren't impacted by your monkey patch.
That's where alias_method_chain comes in action.
Look at this great article to get how you should use it to fit your needs.

Using class function from the lib directory in rails

I am creating a rails3 application and I want to create a class that handles string formatting, so I made a class called FormatUtilites.rb in the lib directory but whenever I try calling it from somewhere else in my app I get this error:
ActionView::Template::Error (uninitialized constant ActionView::CompiledTemplates::FormatUtilities)
So it thinks its a constant and not a class method, which is how it is defined. Any ideas?
class FormatUtilities
def self.slugify(name)
name.downcase.gsub(/\s|\W|\D/, "")
end
end
Thanks!
Turns out rails3 stop autoloading the lib directory. I have no idea why they did it, but they did. Just needed to add it to the autoload in the application.rb
thanks anyways!
Classes are constants in Ruby, besides also being classes. Probably you just need to do "require format_utilities"
You need to add:
# in config/application.rb
config.autoload_paths = %W(#{config.root}/lib
The name of your file should be format_utilities.rb for autoload to work.
In your particular case i would use a different aproach. Instead of creating a class with static functions i would create a module named FormattingHelper in app/helpers/formatting_helper.rb like this.
class FormattingHelper
def slugify(name)
name.downcase.gsub(/\s|\W|\D/, "")
end
end
Then in ApplcationController or in a specific controller i would add:
class ApplicationController < ActionController::Base
helper :formatting
end
If you want rails to automatically load this file when it boots, you will need to name your file format_utilities.rb. The next time you restart your server or console, you should be able to do FormatUtilities.slugify("name")

How do I invoke a method from a custom module?

I've created a module in the /lib folder:
Module CookieHelper
$str = cookies["shoppingcart"]
def get_all_cookie_info()
end
end
But this isn't working. If I move the code into a controller it works fine.
Also, I'm trying to invoke the method within this module. I've tried:
require CookieHelper
CookieHelper::get_all_cookie_info()
to use methods from a module inside of an other class (such as a controller), you do something like this:
include CookieHelper
Once you do that inside of your controller's class, you can call get_all_cookie_info() with just the method name.
It sounds like you might be trying to something odd, so if you want to spell out what get_all_cookie_info is supposed to do, then maybe I can offer more advice.
CookieHelper::get_all_cookie_info is the correct way to call this method.
include CookieHelper
get_all_cookie_info
is another valid way, if you want include all of the methods in cookie helper available without having to namespace them (once the file lib/cookie_helper has been loaded).
What the issue probably is, is that the lib file isn't even required yet, this is because rails3 doesn't automatically load files in lib anymore. You can tell it to do so by editing your application.rb file, and setting inside class Application < Rails::Application
config.autoload_paths += %W( #{config.root}/lib )

Resources