How can I create a dynamic property in ruby? This functionality exists in python.
class Example(object):
value = "This is property!"
class Test(object):
#property
def check(self):
return Example
test = Test()
print(test.check.value) # This is property!
How can I do the same in ruby?
class Example
def value
"This is property!"
end
end
class Test
def check
Example.new
end
end
test = Test.new
puts test.check.value # This is property!
class Test
def check
"This is property!"
end
end
test = Test.new
puts(test.check) # This is property!
Not sure what you want from your example. Properties (from what I've seen) are usually used to create setters and getters. You can have that in Ruby with attr_accessor:
class Test
attr_accessor :check
end
You can call attr_accessor anytime you want an attribute:
class Test
%w{this are possible attribute names}.each do |att|
attr_accessor att
end
end
Or
Class Test
end
test = Test.new
Test.send(:attr_accessor, :whatever)
test.whatever = "something"
test.whatever # => "something"
If you only want a getter you have attr_reader, and there's attr_writer for a writer. They all, for an attribute called attribute_name, use an instance variable called #attribute_name. They all may be built with instance_variable_set and instance_variable_get, which allow dynamically setting and getting instance variables.
You can use ruby's method_missing to achieve something similar:
class TestCheck
def method_missing(methodId)
if(methodId.id2name == "check")
puts "check called"
else
puts "method not found"
end
end
end
t = TestCheck.new
t.check #=> "check called"
t.something_else #=> "method not found"
Reference: Ruby docs
Related
I am looking for way to create the according attr_reader methods while setting the values for the according instance variables in the initialize method? For example, the following code:
class SomeClass
attr_reader :hello
def initialize( arg)
#hello = arg
end
end
I am looking for way to write as follows:
class SomeClass
def initialize( arg)
some_method_as_described_in_question( #hello, arg)
end
end
Does a method doing what I have described exist in the Ruby built-in Classes and Modules?
You can open the eigenclass from within the method and set the attribute there:
class SomeClass
def initialize(arg)
(class << self; self; end).send(:attr_reader, :hello)
#hello = arg
end
end
That way each instance's eigenclass will have that attribute reader. But really it only makes sense to do things that way if the attribute name is dynamic, and can vary from instance to instance. If it's always hello, I don't see any drawback to just defining it in the class like your original code block.
For example, if you are dynamically passing in the attribute name, you could do it like this:
class SomeClass
def initialize(attr, arg)
(class << self; self; end).send(:attr_reader, attr.to_sym)
instance_variable_set("##{attr}", arg)
end
end
This is compatible with Ruby 1.8. Taking a tip from #HenrikN in the comment to your question, you can use define_singleton_method in Ruby 1.9:
class SomeClass
def initialize(attr, arg)
define_singleton_method(attr) { instance_variable_get("##{attr}") }
instance_variable_set("##{attr}", arg)
end
end
Not sure if I understand the question, but you can use Struct to get an initializer and accessor methods:
class SomeClass < Struct.new(:hello)
end
x = SomeClass.new("yo")
puts x.hello # "yo"
x.hello = "what up"
puts x.hello # "what up"
require 'ostruct'
p = OpenStruct.new
p.hello = 'world'
p.could_be_anything = 'nothing'
puts p.hello #=> 'world'
puts p.could_be_anything #=> 'nothing'
How can a class method (inside a module) update an instance variable? Consider the code bellow:
module Test
def self.included(klass)
klass.extend ClassMethods
end
module ClassMethods
def update_instance_variable
#temp = "It won't work, bc we are calling this on the class, not on the instance."
puts "How can I update the instance variable from here??"
end
end
end
class MyClass
include Test
attr_accessor :temp
update_instance_variable
end
m = MyClass.new # => How can I update the instance variable from here??
puts m.temp # => nil
You'd have to pass your object instance to the class method as a parameter, and then return the updated object from the method.
That does nto quite make sense.
You use the initialize method to set default values.
class MyClass
attr_accessor :temp
def initialize
#temp = "initial value"
end
end
The initialize method is automatically run for you when you create a new object.
When your class declaration is run, there are no, and cannot be any, instances of the class yet.
If you want to be able to change the default values later you can do something like this:
class MyClass
attr_accessor :temp
##default_temp = "initial value"
def initialize
#temp = ##default_temp
end
def self.update_temp_default value
##default_temp = value
end
end
a = MyClass.new
puts a.temp
MyClass.update_temp_default "hej"
b = MyClass.new
puts b.temp
prints
initial value
hej
If you also want that to change already created instances' variables you need additional magic. Please explain exactly what you wish to accomplish. You are probably doing it wrong :)
I have a test class and a box class, in the test class i have a var called boxHolder, which is an array, i want to override the << method for this array. Inside the singleton how can i access moski_call ?
class Test
attr_accessor :boxHolder
def initialize()
super
self.boxHolder = Array.new
class << #boxHolder
def <<(box)
box.setPositionWithinColumn(moski_call)
super(box)
end
end
end
def moski_call
"YAAAAAAAAAAAAAAAAAAAA"
end
end
class Box
def initialize
end
def setPositionWithinColumn(str)
puts "got a string #{str}"
end
end
# test
box = Box.new
test = Test.new
test.boxHolder
like this:
# need this or else `moski_call` method is looked up in context of #boxholder
moski_call_output = moski_call
class << #boxholder; self; end.send(:define_method, :<<) { |box|
box.setPositionWithinColumn(moski_call_output)
super(box)
}
What about:
def self.boxHolder.<< (box)
box.setPositionWithinColumn(moski_call)
super(box)
end
This would declare a method for your instance boxHolder. But boxHolder does not have access to the method moski_call
You need to maintain access to the "parent" Test object. This can be done using the fact that blocks are closures:
parent = self # to be accessible in the closure
#boxHolder.define_singleton_method(:<<) do |box|
box.setPositionWithinColumn(parent.moski_call)
super(box)
end
Note: define_singleton_method is new in Ruby 1.9, so either upgrade, require 'backports/1.9.1/kernel/define_singleton_method' or do class << #boxHolder; define_method(:<<){ "..." } end if using an older Ruby.
Given the following class:
class Test
attr_accessor :name
end
When I create the object, I want to do the following:
t = Test.new {name = 'Some Test Object'}
At the moment, it results in the name attribute still being nil.
Is that possible without adding an initializer?
ok,
I came up with a solution. It uses the initialize method but on the other hand do exactly what you want.
class Test
attr_accessor :name
def initialize(init)
init.each_pair do |key, val|
instance_variable_set('#' + key.to_s, val)
end
end
def display
puts #name
end
end
t = Test.new :name => 'hello'
t.display
happy ? :)
Alternative solution using inheritance. Note, with this solution, you don't need to explicitly declare the attr_accessor!
class CSharpStyle
def initialize(init)
init.each_pair do |key, val|
instance_variable_set('#' + key.to_s, val)
instance_eval "class << self; attr_accessor :#{key.to_s}; end"
end
end
end
class Test < CSharpStyle
def initialize(arg1, arg2, *init)
super(init.last)
end
end
t = Test.new 'a val 1', 'a val 2', {:left => 'gauche', :right => 'droite'}
puts "#{t.left} <=> #{t.right}"
As mentioned by others, the easiest way to do this would be to define an initialize method. If you don't want to do that, you could make your class inherit from Struct.
class Test < Struct.new(:name)
end
So now:
>> t = Test.new("Some Test Object")
=> #<struct Test name="Some Test Object">
>> t.name
=> "Some Test Object"
There is a general way of doing complex object initialization by
passing a block with necessary actions. This block is evaluated in the
context of the object to be initialized, so you have an easy access to
all instance variables and methods.
Continuing your example, we can define this generic initializer:
class Test
attr_accessor :name
def initialize(&block)
instance_eval(&block)
end
end
and then pass it the appropriate code block:
t = Test.new { #name = 'name' }
or
t = Test.new do
self.name = 'name'
# Any other initialization code, if needed.
end
Note that this approach does not require adding much complexity
to the initialize method, per se.
As previously mentioned, the sensible way to do this is either with a Struct or by defining an Test#initialize method. This is exactly what structs and constructors are for. Using an options hash corresponding to attributes is the closest equivalent of your C# example, and it's a normal-looking Ruby convention:
t = Test.new({:name => "something"})
t = Test.new(name: "something") # json-style or kwargs
But in your example you are doing something that looks more like variable assignment using = so let's try using a block instead of a hash. (You're also using Name which would be a constant in Ruby, we'll change that.)
t = Test.new { #name = "something" }
Cool, now let's make that actually work:
class BlockInit
def self.new(&block)
super.tap { |obj| obj.instance_eval &block }
end
end
class Test < BlockInit
attr_accessor :name
end
t = Test.new { #name = "something" }
# => #<Test:0x007f90d38bacc0 #name="something">
t.name
# => "something"
We've created a class with a constructor that accepts a block argument, which is executed within the newly-instantiated object.
Because you said you wanted to avoid using initialize, I'm instead overriding new and calling super to get the default behavior from Object#new. Normally we would define initialize instead, this approach isn't recommended except in meeting the specific request in your question.
When we pass a block into a subclass of BlockInit we can do more than just set variable... we're essentially just injecting code into the initialize method (which we're avoiding writing). If you also wanted an initialize method that does other stuff (as you mentioned in comments) you could add it to Test and not even have to call super (since our changes aren't in BlockInit#initialize, rather BlockInit.new)
Hope that's a creative solution to a very specific and intriguing request.
The code you're indicating is passing parameters into the initialize function. You will most definitely have to either use initialize, or use a more boring syntax:
test = Test.new
test.name = 'Some test object'
Would need to subclass Test (here shown with own method and initializer) e.g.:
class Test
attr_accessor :name, :some_var
def initialize some_var
#some_var = some_var
end
def some_function
"#{some_var} calculation by #{name}"
end
end
class SubClassedTest < Test
def initialize some_var, attrbs
attrbs.each_pair do |k,v|
instance_variable_set('#' + k.to_s, v)
end
super(some_var)
end
end
tester = SubClassedTest.new "some", name: "james"
puts tester.some_function
outputs: some calculation by james
You could do this.
class Test
def not_called_initialize(but_act_like_one)
but_act_like_one.each_pair do |variable,value|
instance_variable_set('#' + variable.to_s, value)
class << self
self
end.class_eval do
attr_accessor variable
end
end
end
end
(t = Test.new).not_called_initialize :name => "Ashish", :age => 33
puts t.name #=> Ashish
puts t.age #=> 33
One advantage is that you don't even have to define your instance variables upfront using attr_accessor. You could pass all the instance variables you need through not_called_initialize method and let it create them besides defining the getters and setters.
If you don't want to override initialize then you'll have to move up the chain and override new. Here's an example:
class Foo
attr_accessor :bar, :baz
def self.new(*args, &block)
allocate.tap do |instance|
if args.last.is_a?(Hash)
args.last.each_pair do |k,v|
instance.send "#{k}=", v
end
else
instance.send :initialize, *args
end
end
end
def initialize(*args)
puts "initialize called with #{args}"
end
end
If the last thing you pass in is a Hash it will bypass initialize and call the setters immediately. If you pass anything else in it will call initialize with those arguments.
obj = SomeObject.new
def obj.new_method
"do some things"
end
puts obj.new_method
> "do some things"
This works ok. However, I need to do same thing inside an existing method:
def some_random_method
def obj.new_method
"do some things"
end
end
Works ok as well, but having a method inside a method looks pretty horrible. The question is, is there any alternate way of adding such a method?
In ruby 1.9+, there's a better way of doing this using define_singleton_method, as follows:
obj = SomeObject.new
obj.define_singleton_method(:new_method) do
"do some things"
end
Use a Mixin.
module AdditionalMethods
def new_method
"do some things"
end
end
obj = SomeObject.new
obj.extend(AdditionalMethods)
puts obj.new_method
> "do some things"
There are several ways to achieve this, and they are all related to the singleton class:
You can use class << idiom to open the singleton class definition:
obj = Object.new
class << obj
def my_new_method
...
end
end
Or you can use define_singleton_method on the obj:
obj = Object.new
obj.define_singleton_method(:my_new_method) do
...
end
You can also use define_method from the singleton class:
obj = Object.new
obj.singleton_class.define_method(:my_new_method) do
...
end
Or you can use def directly:
obj = Object.new
def obj.my_new_method
...
end
Pay attention to example 3, I think the concept of a singleton class becomes clearer on that one. There is a difference between these two examples:
a = Object.new
b = Object.new
# -- defining a new method in the object's "class" --
a.class.define_method(:abc) do
puts "hello abc"
end
a.abc # prints "hello abc"
b.abc # also prints "hello abc"
# -- defining a new method in the object's "singleton class" --
a.singleton_class.define_method(:bcd) do
puts "hello bcd"
end
a.bcd # prints "hello bcd"
b.bcd # error undefined method
This is because every object has its own singleton class:
a = Object.new
b = Object.new
p a.class # prints "Object"
p a.singleton_class # prints "#<Class:#<Object:0x000055ebc0b84438>>"
p b.class # also prints "Object"
p b.singleton_class # prints "#<Class:#<Object:0x000055ebc0b84410>>" (a different reference address)
Just an interesting point to note:
if you had instead gone:
def my_method
def my_other_method; end
end
Then my_other_method would actually be defined on the CLASS of the object not withstanding that the receiver ofmy_method is an instance.
However if you go (as you did):
def my_method
def self.my_other_method; end
end
Then my_other_method is defined on the eigenclass of the instance.
Not directly relevant to your question but kind of interesting nonetheless ;)
You can use modules.
module ObjSingletonMethods
def new_method
"do some things"
end
end
obj.extend ObjSingletonMethods
puts obj.new_method # => do some things
Now if you need to add more methods to that object, you just need to implement the methods in the module and you are done.
Use instance_eval:
obj = SomeObject.new
obj.instance_eval do
def new_method
puts 'do something new'
end
end
obj.new_method
> "do something new"
class Some
end
obj = Some.new
class << obj
def hello
puts 'hello'
end
end
obj.hello
obj2 = Some.new
obj2.hello # error
Syntax class << obj means that we are opening definition of the class for an object. As you probably know we can define Ruby class methods using syntax like this:
class Math
class << self
def cos(x)
...
end
def sin(x)
...
end
end
end
Then we can use those methods like this:
Math.cos(1)
In Ruby, everything is an object - even classes. self here is an object of Math class itself (you can access that object with Math.class). So syntax class << self means we are opening class for Math class object. Yes, it means that Math class has class too (Math.class.class).
Another way to use a Mixin
obj = SomeObject.new
class << obj
include AnotherModule
end
This includes all of the methods from AnotherModule into the current object.