Ruby overriding method from Trie gem - undefined method - ruby-on-rails

I use Trie (https://github.com/tyler/trie) gem in project and love it. But it has one issue that is really anoying.
has_key? method returns nil when key is not found instead of false (as every method ending with ? should)
I've tried opening an issue on their GitHub (https://github.com/tyler/trie/issues/26) but no luck.
So, reasonable next step - try to override the method.
I added this to my project:
class Trie
alias :old_has_key? :has_key?
def has_key?(key)
puts "My new Trie has_key"
old_has_key?(key)
end
end
Just to see if I can get away with it.
Unfortunately, when I run rails console I get:
`<class:Trie>': undefined method `has_key?' for class `Trie' (NameError)
As I found elsewhere, this should work. Any idea why it doesn't?
What I'm missing here? Location of trie.rb? Something else?

From your error message, it seems you are calling the has_key? method on the Trie class, not an instance of it. If you create an instance, your code works just fine:
my_trie = Trie.new
my_trie.has_key?('foo')
# My new Trie has_key
# => nil

So I found the problem.
My new class was nested under /lib folder in my Rails app. As soon as I moved it to /lib/utils/ it worked!
This means that loader picked up my custom class before actual Trie class. Still not sure why.

You can patch a little more simply:
class Trie
def has_key?(key)
!!super
end
end

Related

Override method Ruby

I would like to override the method: authorize_endpoint_url from the Gem in a Rails application: https://github.com/AzureAD/omniauth-azure-activedirectory/blob/master/lib/omniauth/strategies/azure_activedirectory.rb
I tried to do it by adding a file to config/initializers/oauth.rb
With the code:
module OmniAuth
module Strategies
# A strategy for authentication against Azure Active Directory.
class AzureActiveDirectory
def request_phase
debugger
"www.hans.com"
end
end
end
end
But this approach doesn't seem to work, nothing get's actually overwriten. What do I wrong? Thank you
When writing "monkey patch" style alterations you'll want to ensure they're loaded correctly. One way to test this is, after all is said and done, to interrogate Ruby to find out which method is actually being used:
OmniAuth::Strategies::AzureActiveDirectory.instance_method(:‌​request_phase).sourc‌​e_location
The instance_method call returns an object with information about that method and the source_location property tells you where that was defined.
If if's your method, great, you got it loaded right. If not you may need to check that you're hooking in at the correct time.

How does `new` create an object?

In Rails initializers, I found the following line:
LineAdsClient = LineAds::AdsClient.new(api_key: LineAccessToken.order('created_at').last[:access_token])
I tried to look for AdsClient class in the workspace, but I could not find it.
How does this style of object creation work without declaring a class?
After creating an instance, we are using LineAdsClient like below:
def client
#client ||= ::LineAdsClient
end
If you couldn't find the class, it doesn't mean there's no class. You just didn't find it (looked in the wrong place or other reasons).
If you're using Pry, try this in your rails console
show-source LineAds::AdsClient
# or
show-source LineAds::AdsClient.new
It'll show you where it is defined.

What scope can delayed_job access

I'm trying to use delayed_job to handle some tasks, but it keeps telling me it cannot see the functions in my library. I've included the library in the model where I'm calling delay on the method call.
Job.delay.save_job_data(job_id)
def self.save_job_data(job_id)
job = Job.find_by(id:job_id)
file = Marshal.dump(job.image_data)
save_file_to_AWS(file,job.file_name)
...
end
MyLibray
def save_file_to_AWS(file,file_name)
...
end
end
Is there a way for a method call though delay to access other parts of my code?
I've had this issue before and the way I've "fixed" it is by doing the following:
Created an initializer file config/initializers/load_classes_for_dj.rb and put something like this in it:
MyLib::MyClass1
MyLib::MyClass2
# init all classes inside lib/communication for delayed job
Dir["#{Rails.configuration.root}/lib/communication/**/*.rb"].each do |file|
require file
# get the ruby files, remove extension and camelize in order to get the class name
class_name = File.basename(file, ".rb").camelize
# evaluate class name; this will notify environment about class's existence
eval(class_name)
end
If there's a better answer, I'b be happy to see it here!
It occurred to me that my issue might be related to the fact that I was calling the method as a procedure call since there was no real structure involved. I changed the call to a call on class by wrapping my utilities in a utility class and then calling them via Utility.method.
Delayed_job was happy with that. I didn't proceed with Abdo solution since I don't need it yet, but I'll keep it noted incase I run into the issue again, maybe then we'll know what change pushed delayed_job over the edg.

Class name is not defined in the module unless it's used in the code

I have created some classes in a module, f.i. Request (app/models/api/request.rb)
class Api::Request
end
And now I want to check in the console does the class exist?
>> Api.const_defined?('Request')
=> false
>> Api::Request
=> Api::Request
>> Api.const_defined?('Request')
=> true
Strange, but the script cannot see the class name unless I use it in the code.
How to fix this? And if you can explain the issue, it will be great.
The project uses Rails 2.3
The problem here is with the rails autoload mechanism. Basically what it does is to react to the method const_missing which you can define on every object. When this method is called, Rails looks for a file which is likely to contain a definition for the missing constant and requires it. The problem is that
Api::Request
triggers const_missing but
Api.const_defined?('Request')
does not.
You can write your own const_defined? like this (the code would go into a rails initializer):
def Module.autoload_const_defined?(name)
self.const_get name
ensure
return self.const_defined?(name)
end
which first tries to autoload the constant.
For anyone coming upon this in future - it's much better style to do something like the following:
def Module.autoload_const_defined?(name)
const_get(name)
rescue NameError => e
const_defined?(name)
end
return inside an ensure block means that any exceptions that occur are silently thrown away, and have the potential to make debugging really tricky.

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

Resources