overriding as_json for as_json(:includes => :association) - ruby-on-rails

I have two classes, Foo and Bar. Foo has_many Bars. Bar is actually the superclass of several classes that are sharing a STI table.
I want to make a dump of my Foo records including their associated Bars. To do this I call
Foo.all.to_json(:incude => :bars)
The initial problem is that I want to be able to distinguish between the different kinds of Bar classes. Rails makes this distinction via the type column in the Bar table, but that column isn't included in the json serialization of the Ber records.
So, I overrode to_json in the Bar class to include the type attribute. when I call to_json on an instance of Bar, I get the new results, but when I call to_json on Foo and include its Bars, I get the old to_json (i.e. without the type attribute included).
I've since given up on this and am going with a different approach, but I'm still curious about what's going on here. Maybe I should be using as_json instead of to_json? I still don't understand the different between those two methods.

I can't replicate this. It behaves properly in my test class.
Let's call class #1 Foo and Foo is included as an argument to Bar. In the Bar.to_json(foo), add this:
foo.class.ancestors.each do |c|
has_json = c.instance_methods.include?(:to_json)
p "#{c} has to_json: #{has_json}"
if has_json
p "Owner: #{c.instance_method(:to_json).owner}"
end
end
It might shed some light on the call hierarchy and also whether your instance variable is getting to_json from the right class.

Related

Ruby/Rails - force subclasses to override specific methods?

I'm wondering if there is a way to force a subclass to override a method from its parent method in either Ruby or Rails (in Java you would do this with an abstract class/method).
Let's say I have the following classes:
class Pet
end
class Dog < Pet
def collar_color
"red"
end
end
class Cat < Pet
end
dog = Dog.new
dog.collar_color
==> red
cat = Cat.new
cat.collar_color
==> NoMethodError
In this example I would never instantiate a Pet object, but it exists to serve as a way to collect common methods to common classes. But let's say I want to ensure that all subclasses of Pet override the collar_color method. Is there a way to do that? Could I achieve it through testing in some way? Assume I don't want a default defined in the parent class.
My real-life use case is a collection of classes that all have polymorphic ownership of another class. If I have a display page of the owned class, then one of the owner classes not having a method could leave me with a NoMethodError problem.
No, there is no way to enforce this.
I can guarantee you, whatever idea you can come up with, it will break in some way.
First off: doing this statically is out of the question. Determining whether a method is overridden or not is known to be equivalent to solving the Halting Problem.
So, you have to do it dynamically. But even that is going to be problematic.
For example: you could implement the inherited hook and check whether every class that inherits from Pet implements the method. But, that will prevent someone from inheriting their own abstract class. (Also, there is no guarantee when the inherited hook will run – it could run when the class is opened, i.e. before the methods are defined.)
Also, even if you can check that the method exists at the point where a class inherits Pet, the method can still be removed again later, so you don't get any guarantees. And, of course, they can just provide a dummy method, in order to get around your protection.
You could create default implementations of the methods that just raise an Exception, but there is no need to do that: if you don't create a default implementation, that will already raise a NoMethodError exception anyway. (If you do go down this route, do not use NotImplementedError. Instead, use a custom exception that inherits from RuntimeError.)
There are examples of this in the core library: for example, the Enumerable mixin depends on an abstract method each that must be implemented by subclasses. And the way this is handled is by simply documenting that fact:
Usage
To use module Enumerable in a collection class:
Include it:
include Enumerable
Implement method #each which must yield successive elements of the collection. The method will be called by almost any Enumerable method.
That is actually the way any type-related issues have been dealt with in Ruby since the beginning. Since Ruby does not have types, typing only happens in the programmer's head and type information is only written down in documentation.
There always were informal third-party type annotation languages that were used by various IDEs. More recently, two type annotation languages have been introduced: RBI, a third-party type annotation language used by the Sorbet type checker, and RBS, a type annotation language that is part of Ruby proper.
As far as I know, RBS has no way of expressing abstract methods, but RBI does:
class Pet
extend T::Sig
extend T::Helpers
interface!
sig {abstract.returns(String)}
def collar_color; end
end
This will give you a type error if there is object instantiated from a subclass that does not at some point in the inheritance chain define the method. But, of course, only if the user of the code actually type-checks the code using a type-checker like Sorbet. If the user of the code does not type-check it, they will not get a type error.
Ruby has relatively few keywords but it provides the basic building blocks to implement something that vaguely resembles abstract classes or methods.
In its simplest form you just raise an error in the parent "abstract" method:
class AbstractMethodError < StandardError
def initialize(klass, m)
super("Expected #{klass} to implement #{m}")
end
end
class Pet
def collar_color
raise AbstractMethodError.new(self.class, __method__)
end
end
class Cat < Pet
end
Cat.new.collar_color # Expected Cat to implement collar_color (AbstractMethodError)
__method__ is a magic variable that contains the name of the current method.
You can make this a bit more elegant by creating a class method that defines the "abstract method":
module Abstractions
def abstract_method(name)
define_method(name) do
raise AbstractMethodError.new(self.class, __method__)
end
end
end
class Pet
extend Abstractions
abstract_method :collar_color
end
However Ruby is a dynamic langauge and you don't have a compile time check so this will only give a slightly more obvious error message when the method is called. It doesn't actually give any guarentees that subclasses implement the method.
That is down to testing or using type checkers like Sorbet or RBS. In general it might be helpful when learning to forget everything you think you know about Object Oriented Programming and learn the Ruby way. It has a very different design philophy compared to Java - instead of abstract methods and interfaces you use duck typing to see if the object responds to that method.
Just define the default method implementation in the abstract class by raising Not implemented error or something. By doing that you also clarifies in your class design that when others / you want to inherit the Pet class they need to override collar_color method. Clarity is a good think and there is no benefit in not defining a default method in the abstract class.
Or if you want to achieve that by testing you can create a test case for Pet class that check if its descendants is defining their own collar_color method or not. I think Rails / Ruby 3.1 have .descendants methods defined or you can just google them.
# Pet_spec.rb
describe "descendants must implement collar_color" do
it "should not throw error" do
descendants = Pet.descendants
descendants.each do |descendant|
expect { descendant.new.collar_color }.to.not raise_error
end
end
end

Change attribute output from model

Is there any way to change the output of an attribute from inside the model?
For example, if I have an attribute called bar in a Foo class, I would like it to be displayed titleized.
I tried by defining it inside the model:
def bar
self.bar.titleize
end
But got an stack level too deep error, because it was redefining itself recursively.
Perhaps the most logical way would be to handle it when I save the record, which I will finally maybe do. But I was wondering if it would be possible something like this.
You can refer to overriden bar method with super:
def bar
super.titleize
end

Rails - How do Controllers pass instance variables to Views...can it be stopped?

I understand and appreciate that by putting # in front of a variable name in a Controller that it becomes available in whatever View is loaded. This is wonderfully useful, but I would like to understand the magic. How does it happen, and can it be stopped?
I am trying to DRY my CRUDdy resource controllers using inheritance, placing most of the logic in ApplicationController. The superclass should refer to the abstract variables #resource (for a single resource), #resources (for a collection of resources), and #parent_resource (for the parent resource when #resource is nested), but ideally the view would get more concrete names, for example;#customer, #customers, and #sales_territory respectively. Can this be done without sending duplicates of all objects (once in the abstract name, and once in the concrete name) to the view?
As I write this, the possibilities that come to mind are;
protected instance variables...does Ruby have such a thing, and
if so does the Controller magic hand them to the View?
setting the generic named variables to nil before render/redirect
using a protected empty method defined in the subclass to instead of
abstract named instance variables
What is the right choice in how to implement this?
What I'm assuming is happening here, is that there's a bunch of controllers in your app that literally just do the same thing and so you are wanting to make use of inheritance to DRY it up.
That being said, I'm not entirely sure the ApplicationController is the right place to dump all of this functionality as in the future if you had new controllers, they would also inherit all of this functionality without necessarily needing it.
I would do something like this:
Assuming you have controllers like this:
lions_controller.rb
tigers_controller.rb
hippos_controller.rb
and they pretty much have similar functionality... I would create a "Base" controller and then setup inheritance on child controllers. I would then also make an action that sets the "logical" defaults of child controllers, something like this.
AnimalsController.rb
class AnimalsController < ApplicationController
class_attribute :resource_class, :parent_resource_class
protected
def self.set_resource_attributes(options={})
self.resource_class = options[:resource_class]
self.parent_resource_class = options[:parent_resource_class]
end
end
LionsController.rb
class LionsController < AnimalsController
#call methods in AnimalsController here, start with setting the resource name
set_resource_attributes :resource_class => Lion, :parent_resource_class => Animal
end
and so on and so forth... the other thing that may be useful is to make use of the methods "instance_variable_set" so that you can set instance variable names in the view that actually make sense... You can make use of the class variables you just set to do this... so for example, lets re-open the AnimalsController.rb class:
class AnimalsController < ApplicationController
def show
instance_variable_set("##{self.resource_class.name.underscore}".to_sym, self.resource_class.find(params[:id]))
#... all the regular show stuff
end
end
This way, when you go to the lions#show path, what you will get in your view is access to a variable named #lion which will be set and contain an instance of the Lion class found through ActiveRecord.
Of course this pseudo code I threw in here can be cleaned up and DRY'd a bit more, but hopefully you get where I'm going with it. Hopefully this helps.

What's the difference between sending :include to class and directly defining method in second class definition?

Recently I had to add a method to Redmine's core class. I was unable to use inheritance, so I've done something like this:
require_dependency 'time_entry_query'
class TimeEntryQuery < Query
def my_new_method(foo, bar)
end
end
and it works perfectly - my method is added to all new objects. However, I've seen someone declaring the new method in their own module instead and then sending :include to class, so it become a mixin. Here's an example:
module Patches
module SomeClassPatch
def my_new_method
end
end
and somewhere in app's initialization:
SomeClass.send(:include, Patches::SomeClassPatch) unless SomeClass.include? (Patches::SomeClassPatch)
What's difference between these two methods and which one should I use?
There are two differences:
When you use a mixin, there is a clear place where your "patch" methods can live. If I wonder "Hmm, where's this my_new_method" coming from, and I look at, say, TimeEntryQuery.ancestors or TimeEntryQuery.instance_method(:my_new_method).owner, that will return Patches::SomeClassPatch. So I know I have to look for a file named lib/patches/some_class_patch.rb somewhere to find where it is probably defined. (I could try source_location as well, but that is not always reliable.)
Mixing in a module into a class makes the module the superclass of the class it is being mixed into. So, if there already is a my_new_method defined in TimeEntryQuery, your first option will overwrite it, whereas in your second option, your method will become the super method of that method. IOW: with your second option, your new method won't be called unless the already existing method calls super.

What type of language construct is Rails' "validates"?

I'm just starting to grok Ruby. My background is in .NET and PHP. In Rails, and I'm sure in other frameworks as well, I see stuff like this on classes:
class Person < ActiveRecord::Base
validates :terms_of_service, :acceptance => true
end
What exactly is "validates"? Is it a function? If it's a function, how does the validation actually work since you don't tell the validates function which model you are validating?
Where can I read more about how this actually works behind the scenes?
It's... a little complicated - but the short answer is that validates is a class method of Person, inherited from ActiveRecord::Base. That line could equally have been written validates(:terms_of_service, :acceptance => true).
Ruby, like a lot of interpreted languages, effectively "executes" class definitions, so when it encounters the validates line, it sees it as a method call where the current self object is the instance of the Class class representing the class Person, inheriting from ActiveRecord::Base. It calls the method, which has the effect of hooking a validator into the Person class.
You can read about the method here - but do note that that adds more confusion, since it lists the method as an instance method of ActiveModel::Validations::ClassMethods. Huh? Well, Ruby has two ways of taking functionality from another Module and putting it into your class - you can either include the module (in which case its instance methods become instance methods of your class), or extend the module (in which case its instance methods become class methods of your class).
So, to summarise: validates is declared as an instance method of ActiveModel::Validations::ClassMethods, which is extended into ActiveRecord::Base. Therefore, validates is a class method of ActiveRecord::Base and, by inheritance, Person. The line in your code snippet is just a method call.
However, having said all that, most Rubyists and Railsists will largely ignore those facts; validates is what's called a "decorator", and most people will simply read it as a statement about Person.
It's a special callback method that gets called to validate data before it's saved to the database.
A callback, more generally, is a method that "hooks into" an object or code module/library in such a way that the method gets called automatically when certain events occur. In this case, the event that calls the set of validation methods is the act of trying to save something to the database. This is useful because, before you write new or updated information to your database, you want to make sure it's valid, in order to prevent bad data from leaking into your application.
The following methods trigger validations, and will save the object to the database only if all validations pass (in the literal sense, this means all the validation methods must return a value that is not false, or nil, etc., otherwise validation is considered to have failed):
create
create!
save
save!
update
update_attributes
update_attributes!
"Methods" in Ruby are very similar to the concept of functions in other languages. In many cases you can think of them interchangeably - they declare some functionality, they take parameters as input, and they return a result as output.
More detail on validations: http://guides.rubyonrails.org/active_record_validations_callbacks.html
Ruby (and Rails) use "callbacks" in many different situations - pretty much anytime you want some method to be called as a result of some event occuring.
More reading on callbacks: http://www.khelll.com/blog/ruby/ruby-callbacks/

Resources