Access a subclass' instance method from a superclass object - ruby-on-rails

I have a class that inherits from another class. The setup of the superclass is as follows:
class Creator::BaseResource < Creator::Base
def request_attributes(action = :create)
"super"
end
end
And the subclass:
class Creator::Resource::HypervisorGroup < Creator::BaseResource
def request_attributes(action = :create)
"sub"
end
end
Now, if I have an instance of "Creator::BaseResource", and there is a DB column named "resource_class" which contains "Resource::HypervisorGroup" that makes it known that its a subclass, I would like to be able to say object.request_attributes and get "super" returned, in this example.
The code is far more complicated than this, obviously, but that's the gist of what I want to accomplish. Is it possible? Thank you.

What about that :
class Creator::Resource::HypervisorGroup < Creator::BaseResource
def request_attributes(action = :create, use_super=false)
super and return if use_super
"sub"
end
end
And then, you can call object.request_attributes(:create, true) to get the superclass method called

Related

Override part of the inherited controller method in Rails

i have a method in the first class
class1
def method(param_test)
if !organization.empty?
organization = test(param_test)
end
end
end
Class 2 inherits from Class 1
class2 < class1
end
I would like to modify the line organization = test(param_test)
without having to copy the whole method in the class2
it's possible?
I would separate the logic into two methods like this:
# in the parent class
def method(param_test)
if !organization.empty?
organization = fallback(param_test)
end
end
def fallback(param)
test(param)
end
And then just override the one method in the child class like this:
# in the child class
def fallback(param)
# logic to return the expected response
end
An alternative might be to change the test method in the subclass. But how that might look like depends on the specific use case and the internals of the test method.

No method error for class variable defined with attr_accessor

I want to define methods dynamically using an array of strings.
Here is a simple piece of code that should achieve that.
class SomeClass
attr_accessor :my_array
def initialize(user, record)
#my_array=[]
end
my_array.each do |element|
alias_method "#{element}?".to_sym, :awesome_method
end
def awesome_method
puts 'awesome'
end
end
When I instantiate this class in the console, I get the following error
NoMethodError (undefined method `each' for nil:NilClass)
What is wrong with this code and how to make it work. any help highly appreciated :)
Edit 1:
What I ultimately want to achieve is to inherit from SomeClass and override my_array in the child class to dynamically define methods with its attributes like so
class OtherClass < SomeClass
my_array = %w[method1 method2 method3]
# Some mechanism to over write my_array.
end
And then use self.inherited to dynamically define methods in child class.
Is there a good way to achieve this?
In your code, you use an instance variable (#my_array) and an attr_accessor over it, and then try to access my_array from class level (that is, from the body of the class definition, outside of any methods). But instance variables only exist at instance level, so it is not available in the class scope.
One solution (the natural one, and the one which you would probably use in other languages) is to use a class variable: ##my_array. But class variables in ruby are a little problematic, so the best solution would be to make use of class instance variables, like that:
class SomeClass
class << self
attr_accessor :my_array
end
#my_array=[]
def initialize(user, record)
end
#my_array.each do |element|
alias_method "#{element}?".to_sym, :awesome_method
end
def awesome_method
puts 'awesome'
end
end
The syntax is a little tricky, so, if you look that up and it still doesn't makes sense, try just reading about scopes and using a regular class variable with ##.
Edit:
Ok, so, after your edit, it became more clear what you are trying to accomplish. A full working example is like follows:
class SomeClass
class << self
attr_accessor :my_array
end
#my_array=[]
def awesome_method
puts 'awesome'
end
def self.build!
#my_array.each do |element|
self.define_method("#{element}?".to_sym){ awesome_method }
end
end
end
class ChildClass < SomeClass
#my_array = %w[test little_test]
self.build!
end
child_instance = ChildClass.new
child_instance.test?
>> awesome
child_instance.little_test?
>> awesome
So, I've made some tweaks on SomeClass:
It does not need an initialize method
I tried to use the inherited hook for this problem. It won't ever work, because this hook is called as soon as "ChildClass < SomeClass" is written, and this must be before you can define something like #my_array = %w[test little_test]. So, I have added a self.build! method that must be called in the child instances so that they build their methods from my_array. This is inevitable, but I think it is also good, because it makes more explicit in the subclasses that you are doing something interesting there.
I think you want "define_method", not "alias_method".
awesome_method in passed in a block, which is ruby's way of doing functional programming.
With that done, ChildClass inherits from SomeClass, and it's instances have the dynamically created methods 'test?' and 'little_test?'.
You need to change my_array to class level accessible, in my case class constant.
class SomeClass
DYNAMIC_METHOD_NAMES = %w(method_a method_b method_C).freeze
def initialize(user, record)
end
DYNAMIC_METHOD_NAMES.each do |element|
alias_method "#{element}?".to_sym, :awesome_method
end
def awesome_method
puts 'awesome'
end
end

Create a method that can initialize a new object from another class

What is the correct structure to be able to call a class from another class?
I can call MyObject.new by using MyModule::MyClass::MyObject.new()
However, I would prefer to call it using:
MyModule::MyClass.myobject.new()
How do I structure my code to by able to do this?
module MyModule
class MyClass
class MyObject
def initialize(value)
#value = value
end
def method1
"This is a #{value}"
end
end
end
end
You need to define a method myobject on MyClass which returns MyObject...
module MyModule
class MyClass
class MyObject
end
def self.myobject; return MyObject; end
end
end
If I understand you correctly, you have a variable of some class and want to create a new object of the same class. Assuming that your classes all have an empty constructor, you could do a
myobject.class.new()
If you want to also have the new object the same internal state as the other one, write a method
class MyMethod
def clone
...
end
end
which performs this task.
The Answers here are right, but don't do this it's not standard and will be confusing for anyone who reads your code.
MyModule::MyClass.my_object_instance
class MyClass
def self.my_object_instance
MyObject.new
end
end
this could be better to create a factory method that returns a new instance of the class if want a shorter way to create an instance of the class.

Rails scope: Find records using class method rather than db column

In my rails 4 app I have a model called Property that has the following class method:
def under_contract?
self.contracts.last && self.contracts.last.accepted? ? true : false
end
What this does is it checks if the property has a contract associated with it and then whether that contract has been accepted.
I want to create a scope for properties that are rented (if you use the class method under_contract? on them it will return true). Here's what I tried doing to accomplish that:
scope :rented, -> {where(under_contract?: true)}
The problem is that under_contract is not a column in the db, it's just a class method, so I get an error saying "No Such Column".
Am I approaching this completely wrong or am i just missing something small?
class method
It is an instance method - if it were class it would be def self.under_contract?
--
Am I approaching this completely wrong
Yes.
Context
Firstly, a scope is the same as a class method; it initializes a new instance of the class to return your data. Instance methods perform actions on already-invoked classes:
#app/models/property.rb
class Property < ActiveRecord::Base
def under_contract? #-> instance method
...
end
def self.under_contract #-> class method
where under_contract: true
end
end
The above can be used as follows:
#property = Property.find x
#property.under_contract? #-> instance method
#properties = Property.under_contract #-> class method
#properties.each do |property|
property.under_contract?
end
The difference is subtle but important. It lies at the root of your issues.
-
Secondly, you cannot mix class and instance methods. They have totally different scopes; you cannot call an instance method on a class method.
You cannot use a scope with an already invoked object. You have to either invoke the object & use an instance method, or use a scope to invoke the required objects initially.
Fix
Do this:
#app/models/property.rb
class Property < ActiveRecord::Base
has_many :contracts
scope :rented, -> { joins(:contracts).where(accepted: true) }
def under_contract?
self.contracts.any? && self.contracts.exists(accepted: true) #-> returns true / false
end
end
This gives you a scope for pulling the rented properties from the db:
#properties = Property.rented
... as well as an instance method to determine if a specific property has been rented:
#property = Property.find x
#property.under_contract?
Thanks for all the help. I solved my specific problem with the code below.
def under_contract
self.contracts.any? && self.contracts.last.accepted?
end
scope :rented, -> {select { |p| p.under_contract == true}}
I don't know what you're accepted? method on contracts looks like but assuming you have a accepted:boolean column in contracts then you can build your scope using includes like this ...
scope :rented, -> { includes(:contracts).where.not(contracts: { property_id: nil }).where(contracts: {accepted: true}) }

How to discover the overrided methods in Ruby/Rails?

Hey guys.
How do I know the methods that a child class overrided in my super class?
I have this:
class Test
def self.inherited(child)
# child.overrided_methods???
end
def self.foo
end
def self.bar
end
end
def Child < Test
def self.bar
puts "bar"
end
end
The method self.inherited is called when a subclass of Test is loaded. So I get the reference to this subclass in child, but I don't know how to get the methods that were overrided by this subclass.
Any ideas?
--
Arsen suggested the use of self.method_added(name) instead of self.inherited(child), but this method catches only instance methods and I want to catch class methods. Does anyone know another methods that does the same thing but with class methods?
In the last case I'll consider using a singleton and convert all this class methods to instance methods then the problem is solved.
For instance methods there is an Object::method_added(name) method you can override, similar to 'inherited' you have used:
class test
def self.method_added(name)
puts "method_added(#{name.inspect})"
super
end
end
irb(main):002:0> class Child < Test; def foo; end; end
method_added(:foo)
=> nil
You can then compare a received name to a list of your methods:
Test.instance_methods.include?(name.to_s)
With class methods this approach does not work (even if you do things like class << self magic), but a helpful fellow knew the answer: http://www.ruby-forum.com/topic/120416 :
class Test
def self.singleton_method_added(name)
puts "Class method added #{name.inspect}"
end
end
This is only the first part of the problem, because you need to know which class defined the method (it will be self) and whether the method is a new one, or overridden one. Experiment with this code:
class Test
def self.singleton_method_added(name)
if self == Test
puts "My own class method added: #{self.name}.#{name.inspect}"
elsif Test.methods(false).include?(name.to_s)
puts "Class method overriden: #{self.name}.#{name.inspect}"
elsif Test.methods(true).include?(name.to_s)
puts "My parent's class method overriden: #{self.name}.#{name.inspect}"
else
puts "New class method added: #{self.name}.#{name.inspect}"
end
end
end
Maybe a first step to the solution:
By calling child.instance_method(:bar) (if child refers to the class) or child.method(:bar) (if it refers to an instance of Child) you can get an UnboundMethod or Method object representing your method:
a = Test.instance_method(:foo)
b = Child.instance_method(:foo)
Unfortunately, a == b evaluates to false, although both refer to the same method.
def overridden_methods
klass = self.class
klass.instance_methods.select {|m| klass.instance_method(m).owner == klass}
end
Change according to your needs.

Resources