To my Rails Gemfile I added a new gem. It is used like this:
Email::Client
The issue I'm having right now is that I also have a class in my rails app that's called Email. Now sometimes when I try to initialize it I get the following error:
Email.new # >> undefined method 'new' for Email:Module
Probably because Email is a Module in the gem. How can I fix this issue? Is there a way to namespace the gem module? I don't want to rename my Email class.
I don't want to rename the Email-Class.
Yet this is what you'll have to do. Your code is the only thing you control. Either rename/alias it (MyEmail) or namespace it (MyApp::Email).
Btw, you got off easy this time. Imagine what'd happen if the other Email was also a class. Suddenly, all your methods are gone. You add or change a method and your email doesn't see it. This could make for a frustrating debugging session.
Related
I have a Rails project that I have been working on for a while. Like many Rails projects I have a User class. In one of my controllers I need to access some methods from a gem I am using. The example code from the gem demonstrates using an include to a particular gem module. I'm not going to use the actual gem here because it is not important to the question. I have no control over this gem and I need functionality from the module.
include GemName::Module
The problem is that the Gem includes its own User class directly under the module I have included in my controller. This results in calls to my own User class to methods not defined in the gem's user class failing:
User.working?(:test_user)
NoMethodError (undefined method `working?' for GemName::Module::User:Class):
What I would like to do is be able to include the module in my controller and be able to use my application's User class in that same controller.
I have tested the following workarounds, both of which seem to work, neither of which I am particularly happy with:
Create a new constant to refer to my own user class before including the gem module.
LocalUser = User
include GemName::Module
Do not include the module and explicitly call any classes I may need with the fully namespaced call.
GemName::Module::Class.method
I realize I could namespace my User class but that would involve a lot a refactoring around my codebase and I don't really love the idea anyway. This controller calls classes from the gem about 20 times and my own User class exactly once. The controller never calls the gem's User class. If possible, I'd love to force the call to my own User class to refer to the my non namespaced class explicitly and keep the include to the gem module.
Hopefully there is an elegant solution which will increase my understanding of namespaces in Ruby
Just after posting the question, I thought "What if I simply add :: in front of User when calling my own non-namespaced class?"
Sure enough, it works.
::User.working?(:test_user)
Calls my own User class.
I'm leaving the question and this answer up in case it helps others.
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).source_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.
I'm new to Ruby on Rails and I'm looking at an application that has a variable called current_teacher. I cannot seem to find where this is set. Everywhere I look the code seems to read from it but where is it set. Is this one of those things that Rails does for you. There is a mode and a table called teachers, so I'm sure this has something to do with it.
I'm very confused by statements like the following, can someone tell me how Rails does this?
if current_teacher.can_request_fieldtrip
Suppose you have a controller like :
class ClientsController < ApplicationController
def new
if current_teacher.can_request_fieldtrip
# code
end
end
end
Here is debugging tips :
(a) put this in your Gemfile and do bundle install :
`gem 'pry-rails', :group => :development`
(b) Put the line binding.pry just before the if statement.
(c) Start rails server using rails s.
(d) Hit the browser like http://localhost:3000/new
(e) Now you will be in the Pry console. Just do in the console,
method(:current_teacher).source_location
And the above line tell you where the method has been defined.
Documentation of Method#source_location
Returns the Ruby source filename and line number containing this method or nil if this method was not defined in Ruby (i.e. native)
Rails does not support authentication by itself, however there are a lot of 'add-ons' that rails can use. These 'add-ons' are called gems. This can be a little confusing because you can't actually see their code inside your project folder.
If you open a file called "Gemfile" (it should be in your project folder) you can see a list of gems that you use. Try searching their names on google, you will probably find official web page that contains it's documentation. That way can learn what they do and how to use them.
current_teacher method smells like "Devise" gem
https://github.com/plataformatec/devise
I'm not sure about can_request_fieldtrip, this could be a custom method defined in Teacher model.
Ok, I have a rails gem that I am working on and I want it to override a specific method in sprockets.
The method I want to override is: Sprockets::Base.digest so that I can base the fingerprint off my gem version when compiling the app's assets.
How would I go about doing that?
In my gem I create a file lib/sprockets/base.rb and place the following code:
class Sprockets::Base
def digest
#digest = digest_class.new.update(MyGem::VERSION)
#digest.dup
end
end
When I run bundle exec rake assets:precompile I get:
undefined method 'logger=' for #<Sprockets::Environment:0x1315b040>
So it almost seems to me like the entire class is getting overridden somehow (this losing that, and the other methods), instead of just overriding the one method.
If I include that snippet of code directly into the app's rakefile that's using both gems, things work perfectly.
It's impossible to override an entire Ruby class in that manner, but I think it is possible to prevent the original class from loading...if it's using autoload. I was curious, so I checked out https://github.com/sstephenson/sprockets/blob/master/lib/sprockets.rb, and yes, Sprockets is using autoload.
autoload :Base, "sprockets/base"
Importantly, that doesn't load the code. It simply tells Ruby that if/when an undefined constant called "Sprockets::Base" is ever encountered, to load it from the specified file. Your patch defines Sprockets::Base before it is ever called anywhere, thus preventing the original file from loading.
When you moved your patch to the Rakefile, something in Rails had already referenced Sprockets::Base, loading the original code. Your patch then applied cleanly on top.
I've never actually used autoload, so I'm not sure how cases like this are supposed to be handled. I'm betting though, that this would work:
Sprockets::Base
class Sprockets::Base
def digest
...
By referencing the class first, you should force Ruby to load the original class. Then you can safely go about the business of overriding one of its methods.
Ok, I marked your answer correct, but it really only led me to figure out the problem.
Anyway, the rails app was requiring my base file instead of the one in the gem itself. Which is what you said. However, the reason it was happening seems to have been caused by the path itself. The path to the file was basically the same as the gem's (lib/sprockets/base.rb).
Moving that file into my gem's "namespace" (lib/my_gem instead of lib/sprockets) and renaming it to sprockets_base.rb fixed the problem! Weird, huh?
In other words, me trying to keep the directory structure nice actually seems to have confused Rails into thinking it was the gem itself or something.
I am using the acts_as_taggable_on gem and would like to add a method to one of the gem source files (tag.rb), but I do not want to change the gem source in any way.
I have tried creating my own tag.rb file to in the /app/models directory or in the /lib directory, and then adding the desired method to that file expecting that ruby will merge the two tag.rb files
But when I do I get a NoMethodError: undefined method ...
What am I missing?
I think you're right that reopening the Tag class is the way to go. I wouldn't introduce another level of inheritance unless it really made sense for your code.
I'm not sure, off the top of my head, why reopening the Tag class didn't work. A few thoughts:
1 - When you wrote your own Tag class, did it descend from ActiveRecord::Base? The Tag class in acts as taggable on does, and I could see how neglecting that might mess things up.
2 - If I needed a place to put code that reopened a plugin class for a single method, I'd probably put it in an initializer file (such as config/initializers/tag_patch.rb). Just to keep things clean.
3 - If all else fails and you still can't get the Tag class reopened properly (for whatever reason) there are other metaprogramming techniques you might try to add the method. For example:
Tag.send(:define_method, “method_name”) do
#code for your method
end
Wait, you shouldn't be adding method to the file, but to the class instead. Are you familiar with the concept of reopening the class? You can't add a method just by naming your file same as the one which original class is defined in. Fortunately. :)