Resolving Namespace conflicts between a class and a gem - ruby-on-rails

In my Rails 2.3 app, I found that the problem emanates from namespace collisions between an existing ActiveRecord model and a gem I recently added.
Specifically, I was trying to add jammit support to the app. One of jammit's dependant gems require's 'platform', which introduces a Platform module into the app's namespace. However, I already have an AR class with that name, and renaming it will introduce a lot of additional work and possible bugs.
I was wondering if there's a relatively simple way (using ruby's metamagic or whatnot) to prevent this namespace collision without having to rename my class or alter the actual gem. Thanks.
EDIT:
Instead of renaming/wrapping the class Platform throughout my app, and since the module Platform is only required by one other gem, I resulted to forking both gems, renaming Platform to XPlatform (both for the original gem and the one that imports it), and rebuilding them both. That seemed to work [sigh].

if the conditions are unavoidable when you cannot change the name
try wrapping your activerecord class in a module
module YourModule
class Platform < Activerecord::Base
named_scope :your_scope, lambda {#your code here}
end
end
So the only change that you have to do is something like
YourModule::Platform.your_scope

At the top of the class before any actions, you could just make an alias.
NewPlatform = ActiveRecord::
And just use the alias in the action for that class to avoid namespace conflicts.

Related

Rails Zeitwerk not respecting class in a module

Running rails zeitwerk:check returns expected file app/api/mariana_tek_client.rb to define constant MarianaTekClient
The odd thing is that I have the following class defined in this file, which seems to follow the convention that I've seen documented: project/app/api/mariana_tek_client.rb
module Api
class MarianaTekClient
include HTTParty
end
end
If I remove the module from the file and leave the class definition only, Zeitwerk stops failing, but this is contrary to what I've seen in all its docs. Plus, I want my namespace!
This works:
class MarianaTekClient
include HTTParty
end
Would love if someone can clue me into why its failing with the namespace.
I'm guessing you've added app/api as an extra autoload path. If so, Zeitwerk will look in that folder for classes in the root namespace, and subfolders for classes in modules - so it expects app/api/mariana_tek_client.rb to contain MarianaTekClient; if you want Api::MarianaTekClient then that would need to go in app/api/api/mariana_tek_client.rb.
You could point Zeitwerk at app, and it would then look for Api::MarianaTekClient in app/api/mariana_tek_client.rb; but that is discouraged and would probably cause you more problems in the long term.
I'd recommend using the default Zeitwerk configuration, and putting your model classes under app/models; so it would then look for Api::MarianaTekClient in app/models/api/mariana_tek_client.rb - as would anyone else working on your code.

Ruby on Rails extending a gem's class and changing its inheriting class

I have a web app built on Ruby on Rails.
In this app obviously use different Gems.
Iv come to a point where i want to extend some specific gem's classes, and add methods to it.
Now i have a use case where i want to extend a gem's class, but instead of adding methods, i want to change its inherting class
lets take the Impressionist Gem for example:
Iv created a new class in my app - app/models/impression.rb
class Impression < ActiveRecord::Base
establish_connection(ENV[App_LOGS_DB'])
end
i want to change the Inheritance to use a my custom class called LogsBase
class Impression < LogsBase
end
the LogsBase class is definend as follows:
class LogsBase < ActiveRecord::Base
establish_connection ENV['APP_LOGS_DB']
self.abstract_class = true
end
When in try to run the server, the following exception is raised:
/gems/impressionist-1.6.1/lib/impressionist/models/active_record/impression.rb:5:in `<main>': superclass mismatch for class Impression (TypeError)
which from my understanding basically means there is a conflict between the gem's impression class definition and my own extention of that class.
Can anyone please help in finding out a way that i can change the Impression class inherting class, while still perserving the class's behaviour and making my server run properly?
PS: the goal of all this is to write the impressions data into a different database (logs db) rather than the main app db. in order to do that i need to establish a connection to the logs db, but if ill do it within the Impression class directly , it will blow up my pool of DB connections as indicated in the following link:
https://www.thegreatcodeadventure.com/managing-multiple-databases-in-a-single-rails-application/
thats why i need the abstract LogsBase class.
Any help will be appreciated.
Disclaimer: do not do that!
The only way I can think of is a nasty hack, neither reliable, nor robust, that is not compatible with newer versions of your external gem. Since Ruby does not allow to redefine the classes ancestors, you might (but please don’t):
grab the content of original /gems/impressionist-1.6.1/lib/impressionist/models/active_record/impression.rb file.
copy-paste it into your /blah/foo/impression.
make your class be loading impressionist/models/active_record/impression explicitly.
in the very second line of the file unset original class.
Something like this:
require 'impressionist/models/active_record/impression'
Object.send :remove_const, 'Impression'
class Impression < LogsBase
# ORIGINAL CONTENT OF THIS FILE
end
There is no (sensible) way to redefine a base class in ruby. It's possible, but only via weird hacks.
I would suggest taking one of two routes here:
Fork the library, and make the base class configurable (with backwards compatibility).
Don't do this via inheritance. Instead, put establish_connection ENV['SPLACER_LOGS_DB'] into a module, and include it in the class.
I would be inclined to use option 2 for now, as it's a quick/simple workaround that should fit well with the rest of the application.

Default namespace of models in Rails

After upgrading my Rails app, I have run into a problem where the name of one of my models are conflicting with another class in Rails, namely Configuration.
Is there a way for me to (from a controller) explicit use my model class Configuration instead of ActiveSupport::Configurable::Configuration? What is the default namespace for my models?
You can use ::Configuration to call your class. It means that you're referring to the constant Configuration from the toplevel namespace. But I think it's annoying. You can rename your class to avoid this.

Convention For Gem with Common Name

I recently authored a gem called 'setting' (found here). The extends ActiveRecord with a module named 'Setting'. I understand that gems are supposed to use the namespace they are named, however when testing this caused collisions with ActiveRecord models with the same name (a Setting model). Does a standard exist for creating a private module namespace? I don't need users of the gem to ever access the module outside the extension in ActiveRecord. Do I have any options outside of picking a less common name?
Since you're writing an Active Record extension, you could place your module inside the ActiveRecord namespace:
module ActiveRecord
module Setting
end
end
Other than that, no, there's no practical namespace solution for gems with very common names.

Rails 3 Plugin - Active Record table_name_prefix

Im having problem to use table_name_prefix on my projects. I have a main apllication in my project that have others applications as plugins, these plugins works like a subsystem from the main application.
To organize the tables on database of the subsystems I would like to use the table_name_prefix of the ActiveRecord Plugin.
If I put on init.rb of plugin the command config.active_record.table_name_prefix = "per_" the main application will not work because the ActiveRecord will try to find for "per_users" but the only thing I want is that only the Plugin on my main application use the prefix "per_".
I tried to create a rails folder at my plugin with the command above but the same problem occurs, all the application try to find for prefixed table name.
An alternative is use the set_table_name in the model of plugin, but its not good for me because Im developing subsystems as rails plugin and I dont want to change the models when put the subsystem at the main application.
Someone can help me?
To have each plugin with own prefix, for Rails 3, try organize Your models inside plugin in namespace:
class Foo::Bar < ActiveRecord::Base
...
end
module Foo
def self.table_name_prefix
'foo_'
end
end
This will works just inside the plugin without changing anything inside the main application.
Other approach is to use some main model and inherit it from others like that:
class Foo < ActiveRecord::Base
def self.table_name_prefix
'foo_'
end
end
class Bar < Foo
...
end
sometimes this approach is used to extend all models with extra features.
More information in Rails documentation here
Take a look at this question. I ran into the same problem, forgot my application's name (main module) had the same name as a namespace for my models.

Resources