object.class returns unexpected results in Ruby - ruby-on-rails

I have a descendants method in my Question model to return all objects that inherit from it.
class Question < ActiveRecord::Base
class << self
def descendants
ObjectSpace.each_object(Class).select do |klass|
klass < self
end
end
end
end
When I call Question.descendants I get back an array w/ a single object
[MultipleChoice(id: integer, text: text, scored: boolean, required: boolean, type: string, questionnaire_id: integer, created_at: datetime, updated_at: datetime)]
The problem is that when I call Question.descendants.first.class I get back Class instead of the expected MultipleChoice.
Why is this happening?

The thing is, that you already have a class in the array (the MultipleChoice class). When you ask Question.descendants.first you get that MultipleChoice class.
However, you are asking for Question.descendants.first**.class**. And the class of MultipleChoice is Class.
Getting Class as the class of MultipleChoice is perfectly OK. Have a look at the ruby metamodel as a reference:
image source: http://sermoa.wordpress.com/2011/06/19/ruby-classes-and-superclasses/

You have MultipleChoice class instead of instance in your array returned by descendants method. This is because you used ObjectSpace.each_object with Class parameter, which returns classes, since their class is Class.

[MultipleChoice(id: integer, text: text, scored: boolean, required: boolean, type: string, questionnaire_id: integer, created_at: datetime, updated_at: datetime)]
This is not an array of single object. This is an array in which you have something like [MultipleChoice]. And when you try MultipleChoice.class it will return Class.
There is some issue in your code that creates Question.descendants

Related

Model failing to receive call from method even though the method is being called in the controller

I'm trying to write a test to see if my Hospital model is receiving a custom method look_for_hospitals.
Here is the test:
Rspec.describe HospitalsController, type: :controller do
describe '#search' do
it 'should call the model method to do the search' do
# model should implement method look_for_hospitals
expect(Hospital).to receive(:look_for_hospitals).with('keyword')
# form in search page must be named 'keywords'
get :search, params: {:keywords => 'keyword'}
expect(assigns(:hospitals))
end
end
Here is my model:
class Hospital<ApplicationRecord
def self.look_for_hospitals term
end
end
And here is the method search in the HospitalsController:
def search
keyword = params[:keywords]
#hospitals = Hospital.look_for_hospitals(keyword)
end
When I run my test this is the error I'm having:
1) HospitalsController#search should call the model method to do the search
Failure/Error: expect(Hospital).to receive(:look_for_hospitals).with('keyword')
(Hospital(id: integer, cnes: string, number: integer, address: text, latitude: string, longitude: string, name: string, phones: text, nature: string, specialties: text, rpa: string, microregion: string, created_at: datetime, updated_at: datetime) (class)).look_for_hospitals("keyword")
expected: 1 time with arguments: ("keyword")
received: 0 times
I know there is pretty much nothing implemented yet, but i'm trying the tdd approach of writing the tests first and then the methods.
Sorry if my english is a bit strange, not native english speaker.
Thanks!
As Tom Lord pointed out, the problem was in the before_action line at the begining of the Controller. I just needed to stub those and the problem was gone.

How does ActiveRecord::Relation class act like Array?

How does ActiveRecord::Relation class act like array?
For example,
User.all
=> #<ActiveRecord::Relation [#<User id: 1, name: "Alex", nickname: "leha", created_at: "2017-05-05 12:36:31", updated_at: "2017-05-05 12:36:31">]>
It returns ActiveRecord::Relation which acts like Array.
If I create my own class MyClass
class MyClass
attr_accessor :relation
def initialize(options)
#relation = options
end
end
m = MyClass.new [1,2,3]
=> #<MyClass:0x007ffa3f9ab730 #relation=[1, 2, 3]>
The question is how to make class like ActiveRecord::Relation?
Because includes the module Enumerable, that enables a class to be sorted. Most of the methods that Arrays implements came from that specific module. So if you want to create a class that acts like an Array you should implement Enumerable.
As you see in the following code from Active Record, you can notice when it is included in the class:
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation.rb#L15
References:
https://ruby-doc.org/core-2.4.1/Enumerable.html
https://ruby-doc.org/core-2.4.1/Array.html#class-Array-label-Iterating+over+Arrays

Rails field enum as_json : get integer instead of string

In my projet, I have a User model with a gender enum :
class User < ActiveRecord::Base
enum gender: [:female, :male]
end
When I'm calling as_json on one of my object, I get the string "female" or "male". Is there any way to render the integer value instead of the string ?
I would try looking over the docs
http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html
for this, perhaps you could use a hash instead
class User < ActiveRecord::Base
enum gender: { female: 0, male: 1 }
end
hope its of any help
User.genders is an array of your enum and integer value. So, use User.genders[#user.gender].to_json.
If you're not happy with the default, to_json, investigate jBuilder to create the exact JSON you

Rails: Why don't the child classes get loaded when using class method to return attributes about child classes?

Hello I've been searching for a solution for this for a while. Using Rails 2.3.5
I have a parent class with several child classes and for the sake of not having a file that's 1500 lines long I have the child classes kept in a subdirectory of the app/models directory.
Up until recently when I viewed this post: here
I couldn't even get the child classes to load
Now I want access each child in a manner using the self.inherited class method
like this:
class Project < ActiveRecord::Base
CHILDREN = []
def self.inherited(child)
super
CHILDREN << child
puts "CHILDREN.inspect: #{CHILDREN.inspect}"
end
def self.valid_child_types
CHILDREN.collect{ |child| child.project_type}
end
end
Temporarily, I put some debug statements to get a better picture of how things are getting loaded. I fired up the console and noticed this behavior:
>> Project
require_or_load /Users/frankdrebin/Sites/cerp/app/models/project.rb
loading /Users/frankdrebin/Sites/cerp/app/models/project
require_or_load /Users/frankdrebin/Sites/cerp/app/models/status.rb
loading /Users/frankdrebin/Sites/cerp/app/models/status
=> Project(id: integer, url: string, deadline: date, state: string, type: string, priority: integer, status_id: integer)
>> Project::CHILDREN
=> []
>> ArticleProject
require_or_load /Users/frankdrebin/Sites/cerp/app/models/projects/article_project.rb
loading /Users/frankdrebin/Sites/cerp/app/models/projects/article_project
CHILDREN.inspect: [ArticleProject(id: integer, url: string, deadline: date, state: string, type: string, priority: integer, status_id: integer)]
require_or_load /Users/frankdrebin/Sites/cerp/vendor/gems/state_machine- 0.7.3/lib/state_machine.rb
loading /Users/frankdrebin/Sites/cerp/vendor/gems/state_machine-0.7.3/lib/state_machine
=> ArticleProject(id: integer, url: string, deadline: date, state: string, type: string, priority: integer, status_id: integer)
>> Project::CHILDREN
=> [ArticleProject(id: integer, url: string, deadline: date, state: string, type: string, priority: integer, status_id: integer)]
>>
I am sure there are less elegant solutions to this, such as putting the Child Classes all back into one gigantic file but I'd like to avoid this if at all possible.
Thanks
You have all sorts of problems:
CHILDREN is a constant in Ruby because it starts with a capital letter, you don't want that.
Next if you change it to children it would then be a local variable but you need an instance variable for the instance of the definition of the parent class so you need to use #children.
You need to make the #children availible at the class level (which is probably why you were trying the CHILDREN thing).
Here's how you would do it:
class Parent
#children = []
# Make the attr_reader for the class not an instance of the class
class << self
attr_reader :children
end
def self.inherited(child)
puts "Parent inherited by child: #{child.inspect}"
#children << child
super
end
end
class Child1 < Parent
end
puts "Child1 class created"
class Child2 < Parent
end
puts "Child2 class created"
c1 = Child1.new
c2 = Child2.new
puts "Parent.children: #{Parent.children}"
Output:
Parent inherited by child: Child1
Child1 class created
Parent inherited by child: Child2
Child2 class created
Parent.children: [Child1, Child2]
Perhaps you need to take a step back and ask yourself what you're really trying to accomplish, and is inheritance the best/only way? Modules/mixins?

ActiveRecord model subclasses don't show up

Here is the trivial inheritance (STI) setup:
class Parent < ActiveRecord::Base
end
class Daughter < Parent
end
class Son < Parent
end
Quick try in console. Expecting Parent.subclasses to return two subclasses, but got nothing!
ruby-1.9.2-p0 > Parent.subclasses
=> []
Also, calling
ruby-1.9.2-p0 > Daughter.subclasses
=> []
,which correctly returns no children, makes Parent start recognizing Daughter as subclass:
ruby-1.9.2-p0 > Parent.subclasses
=> [Daughter(id: integer, type: string, created_at: datetime, updated_at: datetime)]
The same works for another subclass:
ruby-1.9.2-p0 > Son.subclasses
=> []
ruby-1.9.2-p0 > Parent.subclasses
=> [Daughter(id: integer, type: string, created_at: datetime, updated_at: datetime), Son(id: integer, type: string, created_at: datetime, updated_at: datetime)]
This is rails 3, but the same behavior exhibits on 2.3.10
This is a known issue
One workaround is to register the subclasses at the bottom of the base class file.
%w(daughter son).each {|r| require_dependency r } if Rails.env.development?
I suppose it's a autoloading issue. The class are load only when you really need. You can try by example with the cache_classes = true configuration and see if this result is allways the same. I suppose is not.

Resources