Activerecord association extensions on belongs_to - activerecord

Is it possible to do an association extension on a belongs_to relation?
I have tried to do it using a module but keep being told that the method I'm calling is private:
module TestExtension
def test
puts 'test successful'
end
end
class Question < ActiveRecord::Base
belongs_to :user, extend: TestExtension
end
Every time I run it though it complains that the method is private
q = Question.first
q.test
# => NoMethodError: Attempt to call private method `test'
I'm not 100% clear whether it's possible to do AR Extensions on belongs_to. It was working fine on Rails 3.0.7 but is now failing in 3.1.0

This is a known issue in 3.1.0 that hasn't been resolved yet. Basically, the new association design in 3.1.0 doesn't support extending belongs_to associations since it was never a supported feature in the first place. However, the issue is still open so it may be resolved in the future; you should probably comment on the issue to voice support if you want it.
Also, the private method error you're getting, you would be getting even if you didn't have the extend: TestExtension part; I believe #test is a private method on all ActiveRecord objects.

Related

Rails Association belongs_to marking as optional dynamically in class and mixin

I have came across a scenario in an existing code base.
Im upgrading Rails to 5.1 from 5.0 and we now need to define relationships as optional where its not required.
I have came across a situation where we have a class and a mixin that are causing conflict.
Is there a way to define the relationship with the :user so that it will meet requirements in both scenarios.
In the Model, a user is not required, but within the mixin, a users presence is validated, causing the conflict.
As the Mixin is included, writing a proc to evaluate whether to mark the relationship as optional by using a function/proc results in the class method being used at runtime. If you prepend the mixin instead, it will default to the method in the mixin, meaning either way they conflict.
class Foo < ActiveRecord::Base
belongs_to :user, optional: true
end
module Mixins
module Foo
extend ActiveSupport::Concern
validates :user, presence: true, if: :user_is_required?
private
def user_is_required?
<!-- LOGIC HERE -->
end
end
end
At an engine level.. the mixin is included as so:
::Foo.include(Mixins::Foo)

AssociationTypeMismatch but `expected` and `got` are the same class with the same object id

This is a rails application. Rails 5.1, Rspec 3.6. I have a failing test in my Rspec test suite and I cannot understand the error message. As far as I can see, the association expects and gets exactly the same object, yet it is still throwing an error. I would appreciate any insight into understanding this error and working towards a fix.
The relevant (minimal) model definitions:
class Member < ApplicationRecord
belongs_to :membership, inverse_of: :members
belongs_to :patient
end
class Patient < Client
has_many :members, dependent: :destroy
end
The error:
Failure/Error: return if member.update(membership: membership, patient: person)
ActiveRecord::AssociationTypeMismatch:
Patient(#47184337056660) expected, got #<Patient id: 163, ...> which is an instance of Patient(#47184337056660)
It looks like in your spec you're expecting a class, but an object of this class is being returned.
Patient(#47184337056660) expected, got # which is an instance of Patient(#47184337056660)
This is a key information here:
which is an instance of
It's hard to tell if you need to fix the specs or the code, since you didn't share the specs at all.
For future thread readers, I found the problem:
The test had stubbed the is_a? method to return false for this particular Patient instance.
Lord knows why 3-months-ago-me did this!
Under the hood rails uses this method to confirm the correct associated object type. In this case, rails checked whether the instance is_a?(Patient) and it returned false. So the patient instance was acting like it was not an instance of the Patient class

Adding ActiveRecord validations to PaperTrail's Version model?

I'm trying to add a validation to PaperTrail::Version which will prevent sensitive data from being stored in the versions table. The idea being you'll get lots of obvious errors if you forget to sanitize your has_paper_trail call within your model.
If I add a custom validator in config/initializers/paper_trail it works ... for a while. Then PaperTrail starts acting with its default behavior and my methods are undefined.
Example Code:
PaperTrail::Rails::Engine.eager_load!
module PaperTrail
class Version
# Ensure no sensitive values end up in the versions table
validate :prohibited_attributes
...
Try a custom version class. See documentation section 6.a. Custom Version Classes.
6.a. Custom Version Classes
You can specify custom version subclasses with the :class_name
option:
class PostVersion < PaperTrail::Version
# custom behaviour, e.g:
self.table_name = :post_versions
end
class Post < ActiveRecord::Base
has_paper_trail :class_name => 'PostVersion'
end
Using PaperTrail::Rails::Engine.eager_load! was a good idea. Not sure why that didn't work for you. Hopefully this is a workaround.

NoMethodError - undefined method `timestamp_sort_order' for Paper trail issue after upgrading Rails 4.2

I used paper_trail to tracking transnational changes when I migrated rails 3.2 to rails 4.2, have got below issue:
NoMethodError - undefined method `timestamp_sort_order' for
got this on rails 6.0.0beta3 because I patched papertrail and forgot to add PaperTrail::VersionConcern, e.g.:
module PaperTrail
class Version < ::ActiveRecord::Base
include PaperTrail::VersionConcern
end
end
PaperTrail::Rails::Engine.eager_load! did not fix it for me
looking at the source code it's calling it directly on an ActiveRecord::Base model, it's defined on PaperTrail::VersionConcern.
I fixed this by adding below line in intializer paper_trail.rb file
PaperTrail::Rails::Engine.eager_load!
See my final intializers/paper_trail.rb file
PaperTrail.config.track_associations = false
PaperTrail::Rails::Engine.eager_load!
module PaperTrail
class Version < ActiveRecord::Base
.....
end
end
Problem resolved....
Have updated my answer added below line extra using it with rails 5.2
PaperTrail.config.track_associations = false
In Rails 6, the preferred method is now to create a model, do not put model code in the initializer. This is what I'm using...
module PaperTrail
class Version < ::ActiveRecord::Base
include PaperTrail::VersionConcern
belongs_to :user, foreign_key: :whodunnit
end
end

Injecting a scope through an association extension in Rails 2.3.17

I'm upgrading from Rails 2.3.5 to 2.3.17 and have come across a pretty obscure problem. I use the following extensions to inject a scope within an association accessor, and provider a custom builder:
module XYZFilterExtension
def in_context(context)
if proxy_owner.context == :abc && context != :admin
scoped(:conditions => {:startup => false})
else
scoped({})
end
end
end
module OtherExtension
def build_with_component_instance(attributes = nil)
attributes ||= {}
attributes.reverse_merge!(:parent_id => proxy_owner.component_instance.id)
instance = build
instance.build_component_instance
instance.attributes = attributes
instance
end
end
has_many :pages, :extend => [ABCExtension, OtherExtension]
In Rails 2.3.5 I could call:
Something.pages.in_context(:abc).build_with_component_instance
and I'd get a Page object (And it's associated component_instance, the build is complicated because it's a polymorphic association being built from the other direction).
Now I get:
undefined method `build_with_component_instance' for #<Class:0x1151dcae8>
Inspecting the scope, the only difference that I could find is that calling proxy_scope on the scope created by in_context() returns the Page model in 2.3.17 and the scope in 2.3.5.
I'm not sure where to go from here. I can't extract the scope out into a module to include in each model because I need to make a decision based on the proxy_owner in the association.
Update: It appears the problem is around extension methods not being available within the context of a scope. Pretty strange but I guess it kind of makes sense. Unfortunately both my scope definition and the build extension require knowledge of their association context. Any ideas welcome :)
I ended up not finding a way to work around this. In the end I had to avoid the bug in the specific situation in which it occurred. Fortunately it was only in a couple of places in the app.

Resources