Monkey patching a Gem method in Rails 3 - ruby-on-rails

I'm willing to use friendly_id gem with Firebird, but I need to override a method that implements a specific SQL function with code related to Firebird.
I've created the file config/initializers/friendly_id_monkey_patch.rb containing the following code:
module FriendlyId
class SlugGenerator
def conflicts
#my_code
end
end
end
However the application keeps executing the original gem's method, ignoring my patch. What could be wrong?

Related

How can I extend gem class in Rails 6/Zeitwerk without breaking code reloading?

How do I extend a class that is defined by a gem when I'm using rails 6 / zeitwerk?
I've tried doing it in an initializer using require to load up the class first.
I've tried doing it in an initializer and just referencing the class to let autoloading load it up first.
But both of those approaches break auto-reloading in development mode.
I've tried putting it in lib/ or app/, but that doesn't work because then the class never gets loaded from the gem, since my new file is higher up in the load order.
There is a similar question here, but that one specifically asks how to do this in an initializer. I don't care if it's done in an initializer or not, I just want to figure out how to do it some way.
What is the standard way of doing something like this?
I do have one nasty hack that seems to be working, but I don't like it (update: this doesn't work either. reloading is still broken):
the_gem_root = $LOAD_PATH.grep(/the_gem/).grep(/models/).first
require("#{the_gem_root}/the_gem/some_model")
class SomeModel
def my_extension
...
end
end
I know is late, but this was a real pain and someone could find it helpful, in this example I'll be using a modules folder located on app that will contain custom modules and monkey patches for various gems.
# config/application.rb
...
module MyApp
class Application < Rails::Application
config.load_defaults(6.0)
overrides = "#{Rails.root}/app/overrides"
Rails.autoloaders.main.ignore(overrides)
config.to_prepare do
Dir.glob("#{overrides}/**/*_override.rb").each do |override|
load override
end
end
end
end
Apparently this pattern is called the Override pattern, it will prevent the autoload of your overrides by zeitwerk and each file would be loaded manually at the end of the load.
This pattern is also documented in the Ruby on Rails guide: https://edgeguides.rubyonrails.org/engines.html#overriding-models-and-controllers

Why do methods defined in an initializer intermittently raise a 'not defined' error which is resolved by restarting the server?

I'm using PaperTrail 4.1 with Rails 4.2.
I have defined several custom methods in an initializer (see: How to add a method to the versions model of Paper_trail?)
#config/initializers/paper_trail.rb
PaperTrail::Rails::Engine.eager_load!
module PaperTrail
class Version < ActiveRecord::Base
scope :scoped, lambda { #selects some records }
def custom_method
#does some stuff
end
end
end
Every so often in development environment I get a method not defined error for methods/ scopes defined in this initializer.
Restarting the server fixes the problem.
Why are these methods being 'lost' to Rails?
Is this an issue that will also present itself in production or other environments?
What steps can I take to find the cause of this issue?
For anyone else arriving here, apparently this is a known issue with PaperTrail
From https://github.com/airblade/paper_trail/pull/492
Now the paper_trail source get's reloaded in the development
environment when saving a file which means the class gets discarded
from the cache and rebuild from the paper_trail sources. The
initializer is not interpreted again since they are one time only, no
module_eval, no abstract class -> exceptions.
And a fix has been included in the latest version of the gem: https://github.com/airblade/paper_trail/pull/557
In essence, it is no longer advised to use an initializer to add custom methods to PaperTrail, and instead to use a model that inherits from PaperTrail (which is a much better fit with AR).
# app/models/paper_trail/version.rb
module PaperTrail
class Version < ActiveRecord::Base
include PaperTrail::VersionConcern
# my custom methods
end
end

gem namespaces within rails class

I'm brand new to rails (and ruby) and having a a lot of trouble with accessing different namespaces. Specifically, I can't access the namespace of the flickraw gem from within a controller class:
class ImageSourcesController < ApplicationController
def show
list = flickr.photos.getRecent
...
end
end
Calling this method, I get the response:
undefined local variable or method `flickr' for #<ImageSourcesController:0x00000005006658>
I am using bundler, which I thought ensured that the methods of all gems in the gemfile are required by rails.
EDIT: I'm stupid, turns out I just needed to reset the server!
It is a good idea to create an initializer for flickraw:
# config/initializers/flickraw.rb
FlickRaw.api_key= ENV['FLICKR_API_KEY']
FlickRaw.shared_secret= ENV['FLICKR_API_SECRET']
If you are creating a open source app you may want to use ENV variables to store your API key and shared secret. The dotenv gem is a really nice tool for that.
You also seem to be confused about namespaces in Ruby. Ruby doesn't actually have namespaces in the same way as for example PHP which has a special keyword and namespace accessors.
Ruby has modules which act as both namespaces (grouping classes, constants etc.) and traits. Foo::Bar.create() is an example of accessing a class method on a "namespaced" class.
module Foo
class Bar
def create
end
end
end
Your flickraw example is simply accessing nested properties (which does'nt really have anything to do with namespaces):
flickr.photos.getRecent
Your taking the object flickr (which flickraw creates when we require flickraw) and sending it the message photos which returns a FlickRaw::Flickr::Photos instance.
We then send the message getRecent to flickr.photos

Auto-generate transliterated cyrillic slugs with friendly_id

I want to implement friendly_id into existing model. Application uses russian gem, which handles new or hand-saved records well, but it doesn't seem to work when I update records from the command line.
User.find_each(&:save) (as friendly_id docs syggested) generate slugs like --<id>.
I used custom normalize method to provide transliterated slug:
def normalize_friendly_id(input)
Russian.transliterate input.to_s.mb_chars.downcase
end
but it definitely may miss some edge cases, and handles string differently from "normal" workflow. What I'm looking for is the way to reuse regular create/update flow and native behavior.
The best way to resolve this problem:
1) Add gem 'babosa' n your Gemfile
gem 'friendly_id'
gem 'babosa'
2) Owerride friendly_id's method in your model
def normalize_friendly_id(text)
text.to_slug.transliterate(:russian).normalize.to_s
end

Monkey-patching database adapter in Rails app

I'm using PosgreSQL adapter to connect to Vertica database which is mostly compatible with PostgreSQL but doesn't support options like client_min_messages (which are still passed to PGconn.connect despite not being present in database.yml). I've made a quick and dirty monkey patch for ActiveRecord::ConnectionAdapters::PostgreSQLAdapter but the problem is that I guess everything in AR in lazy loaded and the original file is read after my patch.
If I add require 'active_record/connection_adapters/postgresql_adapter' at the top of the monkey patch then ActiveRecord tries to establish connection and fails. Is it possible to alter this behavior to make the monkey patch work or should I just write a full-fledged connection adapter?
You can hook your code to railties initialization. Including sample from my gem multi_config:
module <YourModule>
# Railtie subclass for the gem/plugin
class Railtie < Rails::Railtie
# Railtie initializer method
initializer '<your_plugin>.active_record' do
# When active_record is loaded, only then run this.
ActiveSupport.on_load :active_record do
# Hook your code here. For .e.g.
ActiveRecord::Base.send(:include, <YourPluginModule>)
end
end
end
end

Resources