I have a rails application where I need to extend the Hash module to add a method -
class Hash
def delete_blank
delete_if{|k, v| v.nil? or (v.instance_of?(Hash) and v.delete_blank.empty?)}
end
end
I have created a filed named, hash_extensions.rb and placed this in my lib folder and of course configured autoloading paths with the following line in config/application.rb
config.autoload_paths += %W(#{config.root}/lib)
When I call the delete blank method on a Hash however, I get the below error -
undefined method `delete_blank' for #<Hash:0x000000081ceed8>\nDid you mean? delete_if
In addition to this, I have also tried placing require "hash_extensions" at the top of the file I am calling the delete_blank method from.
What am I doing wrong here or can I avoid extending Hash to have the same functionality?
You could resolve this issue in a few different ways:
Assuming that hash_extensions.rb resides under your_app/lib/extensions. (It's a good idea to store all extensions in a separate folder), require all extensions in config/application.rb as below:
Dir[File.join(Rails.root, "lib", "extensions", "*.rb")].each {|l| require l }
Move hash_extensions.rb under config/initializers and it should be automoagically loaded.
Create a folder say lib or extensions under your_app/app and move hash_extensions.rb to it and Rails would take care of loading it.
Related
I have many service objects in my service folder. I'm using Rails 4:
->services
a_gen.rb
b_gen.rb
...
a_pro.rb
b_pro.rb
...
I would like my folder structure to be something like
->services
->gen
a_gen.rb
b_gen.rb
...
->pro
a_pro.rb
b_pro.rb
...
I tried just making the folders and putting the objects there, but Rails complains about not able to find it. Do I have to tell rails to look there?
Update:
I am getting this error:
NameError - uninitialized constant ExampleModelName::APro
Even with the path loaded. Once it is in the subfolder.. Rails is looking into the wrong place.
You can do that in two ways.
If you want rails to autoload them without you making any modifications to the config.autoload_paths, define your classes per the folder structure as follows:
module Gen
class ServiceA
end
end
If you don't want to make any changes to your class definition, then you could add those sub folders to the config.autoload_paths as follows in your config/application.rb
module YourApplication
class Application < Rails::Application
config.autoload_paths += [
"#{Rails.root}/app/services/gen",
"#{Rails.root}/app/services/prod"
]
end
end
for a recursive solution
# application.rb
config.autoload_paths += Dir["#{config.root}/app/services/**/"]
Yes, you do. In config/application.rb, you can add the subfolders to the config.autoload_paths collection.
I use the following line in an initializer to autoload code in my /lib directory during development:
config/initializers/custom.rb:
RELOAD_LIBS = Dir[Rails.root + 'lib/**/*.rb'] if Rails.env.development?
(from Rails 3 Quicktip: Auto reload lib folders in development mode)
It works great, but it's too inefficient to use in production- Instead of loading libs on each request, I just want to load them on start up. The same blog has another article describing how to do this:
config/application.rb:
# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
However, when I switch to that, even in development, I get NoMethodErrors when trying to use the lib functions.
Example of one of my lib files:
lib/extensions.rb:
Time.class_eval do
def self.milli_stamp
Time.now.strftime('%Y%m%d%H%M%S%L').to_i
end
end
Calling Time.milli_stamp will throw NoMethodError
I realize others have answered similar questions on SO but they all seem to deal with naming conventions and other issues that I didn't to have to worry about before- My lib classes already worked for per-request loading, I just want to change it to per-startup loading. What's the right way to do this?
I think this may solve your problem:
in config/application.rb:
config.autoload_paths << Rails.root.join('lib')
and keep the right naming convention in lib.
in lib/foo.rb:
class Foo
end
in lib/foo/bar.rb:
class Foo::Bar
end
if you really wanna do some monkey patches in file like lib/extensions.rb, you may manually require it:
in config/initializers/require.rb:
require "#{Rails.root}/lib/extensions"
P.S.
Rails 3 Autoload Modules/Classes by Bill Harding.
And to understand what does Rails exactly do about auto-loading?
read Rails autoloading — how it works, and when it doesn't by Simon Coffey.
Though this does not directly answer the question, but I think it is a good alternative to avoid the question altogether.
To avoid all the autoload_paths or eager_load_paths hassle, create a "lib" or a "misc" directory under "app" directory. Place codes as you would normally do in there, and Rails will load files just like how it will load (and reload) model files.
This might help someone like me that finds this answer when searching for solutions to how Rails handles the class loading ... I found that I had to define a module whose name matched my filename appropriately, rather than just defining a class:
In file lib/development_mail_interceptor.rb (Yes, I'm using code from a Railscast :))
module DevelopmentMailInterceptor
class DevelopmentMailInterceptor
def self.delivering_email(message)
message.subject = "intercepted for: #{message.to} #{message.subject}"
message.to = "myemail#mydomain.org"
end
end
end
works, but it doesn't load if I hadn't put the class inside a module.
Use config.to_prepare to load you monkey patches/extensions for every request in development mode.
config.to_prepare do |action_dispatcher|
# More importantly, will run upon every request in development, but only once (during boot-up) in production and test.
Rails.logger.info "\n--- Loading extensions for #{self.class} "
Dir.glob("#{Rails.root}/lib/extensions/**/*.rb").sort.each do |entry|
Rails.logger.info "Loading extension(s): #{entry}"
require_dependency "#{entry}"
end
Rails.logger.info "--- Loaded extensions for #{self.class}\n"
end
I have written a small module lib/encryption/encryption.rb
module Encryption
def self.encrypt(value)
...
end
def self.decrypt(value)
...
end
end
I want to use/access this module in these two files from Devise, namely:
token_authenticatable.rb
authenticatable.rb
I already have overwritten both of them by creating 2 new files and putting them into /config/initilaizers (copied the original source code within them and modified them)
/config/initializers/token_authenticable.rb
/config/initializers/authenticatable.rb
One file looks like this for instance:
require 'devise/strategies/token_authenticatable'
require './lib/encryption/encryption.rb' #TRIED THIS, BUT DOES NOT WORK
module Devise
module Models
# The TokenAuthenticatable module is responsible for generating an authentication token and
# validating the authenticity of the same while signing in.
...
my modifications work, but how can I access my lib/Encryption.rb module within these files?
Is this modification approach best practice?
If not, what is the right approach?
If you have this in your application.rb:
config.autoload_paths += %W(#{config.root}/lib)
Then '/lib' will be autoloaded. Meaning you can call
require 'encryption/encryption'
And it should work.
Wrap two methods inside a class such as MyEncryptionAlgo. Create an object of that class
obj = Encryption::MyEncryptionAlgo.new
Use this object to access those two method.
obj.encrypt(value)
obj.decrypt(value)
I want to extend core Array class with simple method:
class Array
def to_hash
result = Hash.new
self.each { |a| result[a] = '' }
result
end
end
I put array.rb into lib/core_ext and tried to require it in application.rb by
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
But still get undefined method 'to_hash' for ["var1", "var2", "var3"]:Array if tried to use it in model method. Of course I rebooted the server after code changes.
Once way you can do this is by adding the following to one of the files in config/initializers
require 'core_ext/array`
All your autoload_paths config value does is make the paths available for when the classes/files are requested. In my app I might have some file structure as follows
- lib/
|
|- deefour.rb
|- deefour/
|
|- core_ext.rb
In my deefour.rb I have
require 'deefour/core_ext'
and inside config/initializers I have a deefour.rb file containing simply
require 'deefour'
The only way the autoload config value you set will cause Rails to look auto load lib/deefour/core_ext.rb is if you had some call to a class Deefour::CoreExt that existed in that file. This is why my require 'deefour' line in the initializer knows to autoload the lib/deefour.rb file.
The explicit require 'deefour/core_ext' in lib/deefour.rb serves the same purpose, since it too does not follow the standard class-name-to-directory mapping Ruby/Rails will expect.
I have a bunch of custom classes in my Rails 3.2 app in lib folder: i.e. extending ActiveRecord, etc. It all works fine.
However I'm trying to add a couple of custom methods to FileUtils, i.e.
module FileUtils
def last_modified_file(path='.')
# blah ...
end
end
I put it in lib/file_utils.rb
In my application.rb I have
config.autoload_paths += %W(#{config.root}/lib)
My other custom classed are loaded but not the module.
I read (Best way to load module/class from lib folder in Rails 3? ) that I'm supposed to define a class inside module in order for Rails to pick it up and according to FileUtils.class - it should be Object < BasicObject.
So I tried
module FileUtils
class Object
def last_modified_file(path='.')
# blah ...
end
end
end
But that doesn't work either.
However when I fire up irb and just paste my code which effectivly puts my new code inside object and reinclude my module - it works fine.
Whaat amd I missing here?
Your patch is never going to be loaded because autoload is only invoked when Rails can't find a constant. Since the FileUtils constant already exists, the autoloader is never called, and your file is never loaded.
Simply require it from an initializer.
require File.join(Rails.root, "lib/file_utils.rb")