Ruby convert string to method name of another class - ruby

The purpose is to store preferences of TestApp model into MobileConfiguration model with same name using migration.
Here is a migration:
TestApp.where.not(business_id: nil).each do |test_app|
configuration = MobileConfiguration.find_or_initialize_by(business_id: test_app.business_id)
MobileConfiguration::DISCLAIMERS.each { |disclaimer| test_app.public_send(disclaimer) }
configuration.max_store_distance_radius = test_app.max_store_distance_radius
configuration.save
end
MobileConfiguration::DISCLAIMERS.each { |disclaimer| test_app.public_send(disclaimer) } should store the test_app data into mobile_configuration.
DISCLAIMERS is an array of Model MobileConfiguration.
MobileConfiguration::DISCLAIMERS = [:app_disclaimer, :upgrade_disclaimer, :game_disclaimer]
:app_disclaimer, :upgrade_disclaimer, :game_disclaimer are preferences in MobileConfiguration Model
preferences(:configurations) do
preference_group "Disclaimer" do
string :app_disclaimer
string :game_disclaimer
string :upgrade_disclaimer
end
end

Using public_send with one attribute just calls that method. You need to use another attribute to pass some data into the method. Also you need to call the setter to pass data in. So:
class Foo
attr_accessor :bar
end
foo = Foo.new
foo.public_send 'bar' # Just returns the current value of #bar - so nil.
foo.public_send 'bar=', 'something' # calls the setter and passes in 'something'
foo.public_send 'bar' # Now returns 'something'

[MobileConfiguration::DISCLAIMERS].each do |property|
configuration.public_send("#{property}=",test_app.public_send(property))
end
can be used to copy values from one model to another for preferences.

Related

How to create "#property" and "#property=" that are actually methods

Is there were a way to make an #property into a method with set and get, so, #property would call a method instead of returning an actual property, and #property = someval would also call a method instead of assigning to an actual property?
In my project, objects store values in a database. Consider this simple database module that stores records in memory. In my real life project it's a DBM like PostgreSQL:
module MyDB
RECORDS = {}
def self.create(pk)
RECORDS[pk] ||= {}
end
def self.set(pk, key, val)
return RECORDS[pk][key] = val
end
def self.get(pk, key)
return RECORDS[pk][key]
end
end
Objects have fields that are stored in that database. So, in this class, the species field is stored in and retrieved from the database:
class Pet
def initialize(pk)
#pk = pk
MyDB.create(#pk)
end
def species=(val)
MyDB.set #pk, 'breed', val
end
def species()
return MyDB.get(#pk, 'breed')
end
end
A simple use of the Pet class could look like this:
motley = Pet.new('motley')
motley.species = 'cat'
It works currently, but here's where I ran into an annoyance. I did something like this within the class:
def some_method(newval)
#species = newval
end
Then, when I ran the code I got this result:
motley.some_method 'whatever'
puts motley.species #=> cat
Then I realize that wasn't corrent and what I should have done is:
def some_method(newval)
self.species = newval
end
I think #species = newval makes sense. It feels like I'm setting a property of the object.
Is were a way to assign a method to the property, something like:
def :#species=(val)
return MyDB.set(#pk, 'breed', 'val')
end
def :#species
return MyDB.get(#pk, 'breed')
end
Is there a way to do such a thing? Should there be?
Is there a way to do such a thing?
No. In Ruby setter and getter methods are the way to get/set the internal state of an object. Instance variables are just lexical variables that are scoped to an instance.
Ruby is a language based on message passing and #foo = bar sends the message =, bar to the recipient that is the lexical variable #foo. If it called self##foo= instead that would break the entire model of the language.
Should there be?
Hell no.
Do we really need a completely new language feature just because you find it hard to remember to call self.foo= instead of #foo =? No.
Would this feature add anything to the language that cannot already be done? No.
Would it break existing code? Yes.

In Ruby, what is are the specific methods to retrieve the return value of Class.new (an instance)?

Given the following code below:
class Animal
end
dog = Animal.new # --> return value of Animal.new
What are the specific methods to retrieve the return value for Animal.new? I understand that a new instance is created from the class Animal and assigned to the variable dog. But what are the methods to retrieve the return value of Animal.new?
According to docs here:
new(args, ...) → obj
Calls allocate to create a new object of class’s class, then invokes
that object’s initialize method, passing it args. This is the method
that ends up getting called whenever an object is constructed using
.new.
You can test this yourself in irb. It returns an instance of that object.
# Instantiating a new instance of a class
irb(main):001:0> Class.new
=> #<Class:0x007fe24193c918>
# Assigning a new instance of a class to a variable
irb(main):005:0> some_var = Class.new
=> #<Class:0x007fe2410e68b8>
irb(main):006:0> some_var
=> #<Class:0x007fe2410e68b8>
class A
end
Calling a = A.new return the new object of class A, the string appear as eg #<A:0x00000002d8eb98> is the string containing human readable representation of the object from the default implementation of inspect method which is called each time new object is created.
The default inspect shows the object’s class name, an encoding of the object id and a list of the instance variables and their values User custom classes eg A should/adviced to override this method to provide better representation of their objects eg
class A
def inspect
"I am what i am"
end
end
b = A.new
prints -> I am what i amnot the hexadecimal value

Reflection in ruby?

I am curious how this works. For example if I create a factory pattern based class where you can "register" classes for later use and then do something like
FactoryClass.register('YourClassName', [param, param, ...]);
FactoryClass.create('your_class_name').call_method_from_this_object
where 'class_name' is a key in a hash that maps to value: ClassName
is there anything like php reflection, where I can create an instance of a class based on a string name and pass in the arguments in? (in php the arguments would be an array of them that php then knows how what to do with)
So if we take a real world example:
class Foo
attr_reader :something
def initialize(input)
#something = input
end
def get_something
return #something
end
end
# In the factory class, foo is then placed in a hash: {'foo' => 'Foo'}
# This step might not be required??
FactoryClass.create('Foo', ['hello'])
# Some where in your code:
FactoryClass.create('foo').get_something # => hello
Is this possible to do in ruby? I know everything is essentially an object, but I haven't seen any API or docs on creating class instances from string names like this and also passing in objects.
As for the hash above, thinking about it now I would probably have to do something like:
{'foo' => {'class' => 'Foo', 'params' => [param, param, ...]}}
This way when you call .create on the FactoryClass it would know, ok I can instantiate Foo with the associated params.
If I am way off base, please feel free to educate me.
Check out Module#const_get (retrieving a constant from a String) and Object#send (calling a method from a String).
Here is an answer that doesn't use eval.
PHP's Reflection is called Metaprogramming in Ruby, but they are quite different. Everything in Ruby is open and could be accessed.
Consider the following code:
class Foo
attr_reader :something
def initialize(input)
#something = input
end
def get_something
return #something
end
end
#registered = { }
def register(reference_name, class_name, params=[])
#registered[reference_name] = { class_name: class_name, params: [params].flatten }
end
def create(reference_name)
h = #registered[reference_name]
Object.const_get(h[:class_name]).new(*(h[:params]))
end
register('foo', 'Foo', ['something'])
puts create('foo').get_something
You can use Object#const_get to get objects from strings. Object.const_get('Foo') will give you the object Foo.
However, you don't need to send class name as string. You can also pass around the class name as object and use that directly.
class Foo
attr_reader :something
def initialize(input)
#something = input
end
def get_something
return #something
end
end
#registered = { }
def register(reference_name, class_name, params=[])
#registered[reference_name] = { class_name: class_name, params: [params].flatten }
end
def create(reference_name)
h = #registered[reference_name]
h[:class_name].new(*(h[:params]))
end
register('foo', Foo, ['something else'])
puts create('foo').get_something
Actually one of the strong points in ruby is meta-programming. So this is really easy to do in ruby.
I am going to skip the registering part, and jump straight to the creation
A simple implementation would be this
class FactoryClass
def self.create(class_name, params)
klass = Object.const_get(class_name)
klass.new(*params)
end
end
and then you can just do:
FactoryClass.create('YourClassName', [param, param, ...]);
and this would be equivalent to calling
YourClassName.new(param, param, ...)

ruby .extend help needed

given
module Foo
def bar
puts "foobar"
end
end
I can do
String.extend(Foo)
and as a consequence do
String.bar # => "foobar"
Why doesnt this work?:
a = String.new
a.bar # => NoMethodError: undefined method `bar' for "":String
Is it because 'a' is now and instance and .extend only works against class methods? Why does it lose the 'new' functionality I have given String via .extend?
Ruby allows you to add methods from a Module to a Class in two ways: extend and include.
Using the module you gave:
module Foo
def bar
puts "foobar"
end
end
With extend the methods are added as class methods, and can be called directly on the class object itself:
class Bar
extend Foo
end
Bar.bar # => "foobar"
Alternatively, you can call it outside of the class scope:
class Bar
end
Bar.extend Foo
Bar.bar # => "foobar"
include is slightly different. It will add the methods as instance methods. They are only callable on an instance of the class:
class Bar
include Foo
end
Bar.bar # NoMethodError
a = Bar.new
a.bar # => "foobar"
The key difference was that we first had to make an instance a, before we could call the instance method.
As sepp2k noted, extend is can be called on any Object, not just Class objects. For example, you can go:
class Bar
end
a = Bar.new
a.extend Foo
a.bar # => "foobar"
However, bar will only be added to the single instance a. If we create a new instance, it you will not be able to call it.
b = Bar.new
b.bar # => MethodNotFoundError
In Ruby, class String is an object, and by doing String.extend(Foo), you create a singleton class for the String class object and include the module Foo in it (that means that you are adding class methods to the String class object, and so can call String.bar). Full stop.
a.bar doesn't work because there's no instance method bar.
No, you haven't given new to String via extend.
The Object#extend method adds to the receiver the methods defined by the argument module.
You have to remember that the String class is itself an object (of type Class) and so when you call "extend" with it as the receiver, that class object (String) itself gets the new method.
So in your first example, String.extend(Foo), adds to the String class instance the function "bar". That is, "bar" is now an instance method of the shadow class (aka Singleton class), so the object String (which is a class) now has the method "bar". This has roughly the same effect as adding a class method to the String class (e.g. def String.bar; puts 'foobar'; end), so it has no effect on instances of type String.

Class Method to 'Know' Class Name in Ruby?

I want an inherited ruby class to 'know' its class name via a class method. This is best illustrated by a contrived example:
class Parent
def self.whoami
??
end
end
class Child < Parent
#No code should be needed.
end
So I should be able to call:
Parent.whomai
and expect a return of "Parent" I should then be able to call:
Child.whoami
and expect a return of "Child" I have a feeling that in conventional languages this might not be possible. But Ruby's metaprogramming model has amazed me before. Any thoughts? Thanks in advance.
A Class Method is a method where the CLASS is the receiver, so to find the object upon which the method is invoked (what you appear to be trying to do here) simply inspect the value of self.
class Parent
def self.whoami
self
end
end
class Child < Parent
end
puts Parent.whoami #=> Parent
puts Child.whoami #=> Child
The method to get the name of a class (module, actually) is just Module#name. There's no need to write your own:
Parent.name # => 'Parent'
Child.name # => 'Child'
However, in Ruby, there really is no such thing as a "class name" as there is in some other languages. In Ruby, a class is simply an object like any other object which gets assigned to a variable like any other variable.
All the Module#name method does is loop through all the constants in the system and check whether the module has been assigned to any one of them, and return that constant's name or nil if it cannot find any.
So, just like any other object, the "name" of a class is really nothing but whatever variable you use to refer to it.
Example:
foo = Class.new
foo.name # => nil
Now, the "name" of the class is foo. However, Module#name returns nil, because foo is not a constant.
bar = foo
bar.name # => nil
Now, the "name" of the class is both foo and bar, but Module#name obviously still returns nil.
BAZ = foo
foo.name # => 'BAZ'
Now, since the class has been assigned to a constant, that constant's name will be considered that class's name …
BAZ = nil
foo.name # => 'BAZ'
… even after the constant has been assigned to something different and …
QUX = foo
QUX.name # => 'BAZ'
… even after the class has been assigned to a different constant.
Module#to_s uses Module#name if it is not nil, so, to print the name of a class, you simply do
puts Parent
There's really absolutely no need for all the complex fluff in the other answers.
Isn't that what Parent.class will tell you?
class Parent
def self.whoami
self.to_s
end
end
class Child < Parent
end
> Parent.whoami
=> "Parent"
> Child.whoami
=> "Child"
Suppose you class:
class ABC
def self.some_method
self.name #it will return 'ABC'
self.name.constantize #it will return ABC
end
end

Resources