Enum with duplicated values under different namespaces - ruby-on-rails

Is there a way to define an enum with duplicated keys but different namespaces ?
Something like this:
class MyModel
module NamespaceA
KEY = 'key'
end
module NamespaceB
KEY = 'key'
end
enum something: [
NamespaceA::KEY,
NamespaceB::KEY,
]
end
This example will raise the error:
You tried to define an enum named "something" on the model "MyModel", but this will generate a instance method "key?", which is already defined by another enum.

Related

How to convert an Object to array in ruby

I have a class with (as example) 3 attributes ,I want to convert the class's attribute to an array ,so I can store them into my csv file.
class Human
attr_accessor :name,:Lname,:id
....
end
when I create :
human1=Human.new("nice","human",1)
I need a function that return ["nice","human",1].
Is there a predefined one that I didn't find or I have to redefine to_a so it does the job.
note: the class has more than 3 attribute
is there a function to go through the object attribute or not.
I need a function that return ["nice","human",1]
Creating such method is trivial. If it is specifically for CSV, I would name it accordingly, e.g.:
class Human
attr_accessor :name, :lname, :id
# ...
def to_csv
[name, lname, id]
end
end
To generate a CSV:
require 'csv'
human1 = Human.new("nice", "human", 1)
csv_string = CSV.generate do |csv|
csv << ['name', 'lname', 'id']
csv << human1.to_csv
end
puts csv_string
# name,lname,id
# nice,human,1
Note that I've renamed Lname to lname in the above example. Uppercase is reserved for constants.
is there a function to go through the object attribute or not?
No, there is no built in way to actually do what you want and you might be falling for a common beginner trap.
attr_accessor does not "define attributes" since Ruby doesn't actually have properties/attributes/members like other langauges do. It defines a setter and getter method for an instance variable. Ruby doesn't keep track of which properties an object is presumed to have - only the actual instance variables which have been set.
But Ruby does provide the basic building blocks to make any kind of attributes system you want. This is very simplefied (and quite rubbish) example:
class Human
# this is a class instance variable
#attributes = []
# a class method that we use for "defining attributes"
def self.attribute(name)
attr_accessor name
#attributes << name
end
attribute(:name)
attribute(:l_name)
attribute(:id)
def initialize(**kwargs)
kwargs.each {|k,v| send("#{k}=", v) }
end
# the attributes that are defined for this class
def self.attributes
#attributes
end
# cast a human to an array
def to_a
self.class.attributes.map{ |attr| send(attr) }
end
# cast a human to an hash
def to_h
self.class.attributes.each_with_object({}) do |attr, hash|
hash[attr] = send(attr)
end
end
end
jd = Human.new(
name: 'John',
l_name: 'Doe',
id: 1
)
jd.to_a # ['John', Doe, 1]
jd.to_h # {:name=>"John", :l_name=>"Doe", :id=>1}
Here we are creating a class method attribute that adds the names of the "attributes" to a class instance variable as they are declared. Thus the class "knows" what attributes it has. It then uses attr_accessor to create the setter and getter as usual.
When we are "extracting" the attributes (to_a and to_h) we use the list we have defined in the class to call each corresponding setter.
Usually this kind functionality would go into a module or a base class and not the actual classes that represent your buisness logic. For example Rails provides this kind of functionality through ActiveModel::Attributes and ActiveRecord::Attributes.

How to use rails Helpers in graphql?

How can I use Helpers from /app/helpers directory in my /app/graphql directory? For example there is a data type which is a nested JSON Object and I got a JSON Schema file which describes the structure of it. There also is a JsonSchemaHelper which I would like to use to validate a Scalar type against the JSON schema. like that:
class Types::Scalar::Node < Types::Base::BaseScalar
def self.coerce_input(value, _context)
if Validators::GraphqlValidator.is_parsable_json?(value)
value = JSON.parse(value)
end
Validators::Node.validate!(value)
value
end
#Validators could be used to check if it fit the client-side declared type
def self.coerce_result(value, _context)
Validators::Node.validate!(value)
value
end
end
and the validator looks like:
module Validators
class Node
include JsonSchemaHelper
def self.validate!(ast)
json_schema_validate('Node', ast)
end
end
end
the include JsonSchemaHelper doesn't work.
include adds methods of JsonSchemaHelper as instance methods of Validators::Node class. self.validate!(ast) is a class method and you try to call json_schema_validate as a class method. Change include JsonSchemaHelper to extend JsonSchemaHelper.

How to resolve "no implicit conversion of Class into String" in rails?

I have a method in patch as below:
def applicable_resource_type(resource_type)
if resource_type.include?('Student')
student_setting
else
teacher_setting
end
end
This method is called in another patch which checks whether the resource type is 'teacher' or 'student' and stores the boolean value in 'active'.
def exams
School.new(resource: self).request if can_examine_students?
end
private
def can_examine_students?
active = applicable_resource_type(self.class.name).is_active?
if active && (self.is_a?(Teacher))
active = belongs_to_school?
end
active
end
However the resource_type is passed as a String whereas in can_examine_students? it is passed as a class/module. Is there any way to make them both consistent?
I tried the following:
def applicable_resource_type(resource_type)
if resource_type.include?(Student)
student_setting
else
teacher_setting
end
end
But it gave error as:
TypeError:
no implicit conversion of Class into String
I also tried
resource_type.include?('Student'.constantize)
But it gave error same typerror.
Is there a way to resolve the above error and keep both consistent resource_type consistent?
Thanks
Actually, in the second code snippet, when you call applicable_resource_type(self.class.name) you also hand over a String, because class.name returns a string.
If you want to write the first method more elegantly, you can use is_a? which accepts a class name as an argument. It would look like this:
def applicable_resource_type(resource_type)
if resource_type.is_a?(Student)
...
Note, that you pass Student as a class name.
You then have to adapt the second code snippet too and just pass the class and not class.name. Hence,
def can_examine_students?
active = applicable_resource_type(self.class).is_active?
...

Method, which return list of instance methods

I'm stuck with two things. This is structure of my file.
class Person
#...
def self.class_name (object)
object.class.name
end
end
class Worker < Person
#...
end
class Client < Person
#...
end
c = Client.new("1", "2", "3", "4")
Person.class_name(c)
I would like to create method, where as argument i can put some object and it will detect, what class is it and return me list of all instance methods, which don't require any argument. Later i need to somehow use all these methods.
I have found this:
testObject.class.name
# returns name of class as a string
Class.instance_methods(false)
# returns list of instance method, which were defined in Class
First problem, is that i don't understand why i can't make something like
className = testObject.class.name
className.instance_methods(false)
I suppose, that's because i got just class name, as a sting, not a real reference to the class. I have even created simple class_name method, which returns correct name of class, but i'm wondering how i can use instance_methods(false), once i have this name. Also is there some option to choose only methods, which don't require any argument?
I would like to create method, where as argument i can put some object
and it will detect, what class is it and return me list of all
instance methods
class Person
def self.instance_methods(object)
object.class.instance_methods(false)
end
end
Usage:
c = Client.new("1", "2", "3", "4")
Person.instance_methods(c)
#=> returns an array of all instance methods defined in Client class
Also is there some option to choose only methods, which don't require
any argument?
Yes, you have to check method's arity and select those, which has zero:
class Person
def self.instance_methods_with_arity_zero(object)
object.class.instance_methods(false).map do |method|
object.method(method).arity.zero?
end
end
end
Usage:
c = Client.new("1", "2", "3", "4")
Person.instance_methods_with_arity_zero(c)
#=> returns an array of instance methods which take no arguments defined in Client class
The latter method can be shortened to use the first defined method:
def self.instance_methods_with_arity_zero(object)
# we are using the previously defined instance_methods method
instance_methods(object).map { |method| object.method(method).arity.zero? }
end
You don't have to turn it into a string:
klass = testObject.class
methods = klass.instance_methods(false)
If you have to deal with a string representation of the class name, then turn it back into a class first:
klass = "String".constantize
string_methods = klass.instance_methods(false)

Make Rails recognise my class inside a string

I have a variable that stores the name of a class
my_class = "Homework"
This class has certain attributes which I would like to access
Homework.find_by
How can I make ruby see this string as an object?
e.g.
my_class.find_by
You can use classify and constantize
my_class.classify.constantize.find_by # something
classify
Create a class name from a plural table
name like Rails does for table names to models. Note that this returns
a string and not a Class (To convert to an actual class follow
classify with constantize).
constantize
Tries to find a constant with the name specified in the argument
string.
'Module'.constantize # => Module
'Test::Unit'.constantize # => Test::Unit
If you are sure about your input, you need only constantize
my_class.constantize.find_by # something

Resources