I'm new to Ruby, and expect to use methods inside of method or associate construct_const method with interpret method. Interpret method should simply return the value for this constant
def construct_const(value)
def interpret()
return #value
end
end
e = construct_const(0)
assert_equal(0, e.interpret)
In your posting, you are using value and #value. I guess this is a typo, since you are not refering value anywhere in your example code, so I assume that you wanted to use value in your interpret method, not #value.
You can define a method on the fly for a certain object using define_singleton_method, i.e.
def construct_const(value)
define_singleton_method(:interpret) { value }
end
With this, if you do then a
e.construct_const
the variable e will then have a new method 'interpret` attached to it.
When you define interpret method inside construct_const you get a :interpret symbol as the result of calling construct_const. Because every statement returns a value defining a method returns the method name.
> def interpret()
> return #value
> end
=> :interpret
So each time you call construct_const you define a method and return its name.
Also, you mismatch a local variable value and an instance variable #value.
You can use lambda or proc to return a function, the function will have call method:
def construct_const(value)
-> { value }
end
> e = construct_const(0)
=> #<Proc:0x000055c060a47f60#(irb):12 (lambda)>
> e.call
=> 0
You can define an anonymous class with a method interpret:
def construct_const(value)
my_class = Class.new do
def initialize(value)
#value = value
end
def interpret
#value # Here you can use an instance variable because it's inside of the anonymous class
end
end
my_class.new(value) # Pass a value to initialize a new instance
end
> e = construct_const(0)
=> #<#<Class:0x0000563663366c68>:0x0000563663366b50 #value=0>
> e.interpret
=> 0
A similar solution but with a predefined class:
class Interpreter
def initialize(value)
#value = value
end
def interpret
#value
end
end
def construct_const(value)
Interpreter.new(value)
end
> e = construct_const(0)
=> #<Interpreter:0x0000563663367b68 #value=0>
> e.interpret
=> 0
A solutions with Struct object:
def construct_const(value)
Struct.new(:interpret).new(value)
end
> e = construct_const(0)
=> #<struct interpret=0>
> e.interpret
=> 0
You've probably had better answers already, but as far as I can see, these seem to work (though I don't really know what I'm doing):
def construct_const(value)
mod = Module.new
mod.module_eval do
define_method :interpret do
value
end
end
obj = Object.new
obj.extend(mod)
obj
end
e = construct_const(0)
=> #<Object:0x00000000084a0e90>
e.interpret
=> 0
def construct_const(value)
obj = Object.new
obj.instance_eval do
#value = value
def interpret
#value
end
end
obj
end
e = construct_const(0)
=> #<Object:0x000000000a369098 #value=0>
e.interpret
=> 0
Related
I have a class called Test. I've created an instance:
example = Test.new
I want to return a value when I type this:
example
I know how to return a value when typing:
example.give_me_some_stuff
But I can't figure out how to override just the instance name and return some value, preferably the result of a method inside of the class.
EDIT:
This works for a method that returns a string, but I can't return an instance variable from the instance of the class. For example here is the test I'm trying to make work:
def test_push
stack = Stack.new
stack.push(10)
binding.pry
refute stack.empty?
end
And here is my class:
class Stack
attr_accessor :contents
def inspect
#contents
end
def initialize
#contents = []
end
def push(n)
#contents << n
end
end
In the test my object comes back as:
#<Stack:0x007f8cd9051938 #contents=[10]>
When I want it to return just the value of #contents.
example always returns your object. If it would return another object, example.give_me_some_stuff would not work anymore.
Maybe you're looking for inspect. IRB calls inspect when printing your object:
class Test
def inspect
"I am Test"
end
end
IRB session:
irb(main):001:0> example = Test.new
=> I am Test
irb(main):002:0> example
=> I am Test
Did you mean this?:
Test = Struct.new('Test', :a, :b)
example = Test.new(1, 2)
example.instance_eval do
def give_me_some_stuff
a + b
end
end
example.give_me_some_stuff #=> 3
another_example = Test.new(1, 2)
# => #<struct Struct::Test a=1, b=2>
another_example.give_me_some_stuff
#=> NoMethodError: undefined method `give_me_some_stuff' for #<struct Struct::Test a=1, b=2>
I can't inherit the Struct. I must implement class which act like Struct.
Is there a way improve my code for use "ClassName" and functional like Struct ? And write k=Dave.new("Rachel" , "Greene") ???
class MyStruct
def self.new(*attributes)
puts "ppp"
dynamic_name = "ClassName"
Kernel.const_set(dynamic_name,Class.new() do
attributes.each do |action|
self.send(:define_method, action) {
puts "call #{action}"
}
end
end
)
end
end
# class ClassName
# def new *args
# puts "iii"
# end
# end
Dave = MyStruct.new(:name, :surname)
k=Dave.new() # k=Dave.new("Rachel" , "Greene")
k.surname
k.name
Here is a version of your code which works:
class MyStruct
def self.new(*attributes)
Class.new do
self.send(:attr_accessor, *attributes)
self.send(:define_method, :initialize) do |*values|
values.each_with_index { |val, i| self.send("#{attributes[i]}=", val) }
end
end
end
end
Dave = MyStruct.new(:name, :surname)
k = Dave.new('Rachel', 'Green')
# => #<Dave:0x00000001af2b10 #name="Rachel", #surname="Green">
k.name
# => "Rachel"
k.surname
# => "Green"
You don't need to const_set inside the method - Dave = is enough
I'm creating here an attr_accessor for each of the attributes, so you are getting a getter and a setter for each
In the initialize method I'm sending each value to its corresponding setter, to set all values. If there are less values than anticipated, the last attributes will not be set, if there are more - an exception will be thrown (undefined method '=')
Have you looked at the Struct class in Ruby?
http://www.ruby-doc.org/core-2.1.2/Struct.html
class MyStruct < Struct.new(:first_name, :last_name)
end
MyClassObj = MyStruct.new("Gavin", "Morrice")
Also, you shouldn't ever overwrite self.new, define initialize instead
How can I call a nested hash of methods names on an object?
For example, given the following hash:
hash = {:a => {:b => {:c => :d}}}
I would like to create a method that, given the above hash, does the equivalent of the following:
object.send(:a).send(:b).send(:c).send(:d)
The idea is that I need to get a specific attribute from an unknown association (unknown to this method, but known to the programmer).
I would like to be able to specify a method chain to retrieve that attribute in the form of a nested hash. For example:
hash = {:manufacturer => {:addresses => {:first => :postal_code}}}
car.execute_method_hash(hash)
=> 90210
I'd use an array instead of a hash, because a hash allows inconsistencies (what if there is more than one key in a (sub)hash?).
object = Thing.new
object.call_methods [:a, :b, :c, :d]
Using an array, the following works:
# This is just a dummy class to allow introspection into what's happening
# Every method call returns self and puts the methods name.
class Thing
def method_missing(m, *args, &block)
puts m
self
end
end
# extend Object to introduce the call_methods method
class Object
def call_methods(methods)
methods.inject(self) do |obj, method|
obj.send method
end
end
end
Within call_methods we use inject in the array of symbols, so that we send every symbol to the result of the method execution that was returned by the previous method send. The result of the last send is automatically returned by inject.
There's a much simpler way.
class Object
def your_method
attributes = %w(thingy another.sub_thingy such.attribute.many.method.wow)
object = Object.find(...)
all_the_things << attributes.map{ |attr| object.send_chain(attr.split('.')) }
end
def send_chain(methods)
methods.inject(self, :try)
end
end
There is no predefined method, but you can define your own method for that:
class Object
def send_chain(chain)
k = chain.keys.first
v = chain.fetch(k)
r = send(k)
if v.kind_of?(Hash)
r.send_chain(v)
else
r.send(v)
end
end
end
class A
def a
B.new
end
end
class B
def b
C.new
end
end
class C
def c
D.new
end
end
class D
def d
12345
end
end
chain = { a: { b: { c: :d } } }
a = A.new
puts a.send_chain(chain) # 12345
Tested with http://ideone.com/mQpQmp
For academic reasons, I'd like to make an instance of Ruby class act like a hash.
GOALS
Initialize MyClass instance with a hash # success
Request values from instance of myClass, like a hash # success
Then set properties as a hash # fail
Although some discussion exists, I tried what's out there (1, 2) with no success. Let me know what I'm doing wrong. Thanks!
class MyClass
attr_accessor :my_hash
def initialize(hash={})
#my_hash = hash
end
def [](key)
my_hash[key]
end
def set_prop(key, value)
myhash[key] = value
end
end
test = myClass.new({:a => 3}) #=> #<MyClass:0x007f96ca943898 #my_hash={:a=>3}>
test[:a] #=> 3
test[:b] = 4 #=> NameError: undefined local variable or method `myhash' for #<MyClass:0x007f96ca9d0ef0 #my_hash={:a=>3}>
You declared set_prop, but you're using []= in tests. Did you mean to get this?
class MyClass
attr_accessor :my_hash
def initialize(hash={})
#my_hash = hash
end
def [](key)
my_hash[key]
end
def []=(key, value)
my_hash[key] = value
end
end
test = MyClass.new({:a => 3}) # success
test[:a] # success
test[:b] = 4 # success
test.my_hash # => {:a=>3, :b=>4}
module HashizeModel
def [](key)
sym_key = to_sym_key(key)
self.instance_variable_get(sym_key)
end
def []=(key, value)
sym_key = to_sym_key(key)
self.instance_variable_set(sym_key, value)
end
private
def to_sym_key(key)
if key.is_a? Symbol
return ('#'+key.to_s).to_sym
else
return ('#'+key.to_s.delete('#')).to_sym
end
end
end
You should write it as test = MyClass.new({:a => 3}) and the below code should work.
class MyClass
attr_accessor :my_hash
def initialize(hash={})
#my_hash = hash
end
def [](key)
#my_hash[key]
end
def []=(key,val)
#my_hash[key]=val
end
def set_prop(key, value)
#myhash[key] = value
end
end
test = MyClass.new({:a => 3})
test[:a]
test[:b]= 4
test.my_hash # => {:a=>3, :b=>4}
How can I limit a variable that belongs to new Class < Fixnum, between 0 and 255?
Or if I can't create a limit in subclass of Fixnim how to write my own class with limit?
Don't make the number a class, make access to that number limited as part of your class via a setter method.
Within your class never set the instance variable except via the setter method.
If you need to do this often, make a helper method for it:
class Module
def limited_value( name, range=0..100 )
attr_reader name
define_method(:"#{name}=") do |new_value|
if range.include?( new_value )
instance_variable_set :"##{name}", new_value
else
raise "Out of Bounds"
end
end
end
end
class Foo
limited_value :bar, 0..255
end
f = Foo.new
p f.bar #=> nil
f.bar = 10
p f.bar #=> 10
f.bar = 300
#=> tmp.rb:8:in `block in limited_value': Out of Bounds (RuntimeError)
You could alternatively choose to set the value to the nearest limit instead of raising a runtime error.
Write a non inherited class and use method_missing to call all functions from a instance variable, them, limit the return value.
class MyNum
instance_methods.each {|m| eval("undef " << m) }
def initialize(fixnum)
#num = fixnum
end
def method_missing(name, *args, &blk)
ret = #num.__send__(name, *args, &blk)
Numeric === ret ? MyNum.new([[ret, 0].max, 255].min) : ret
rescue NoMethodError
super
end
def inspect
"MyNum(#{#num.inspect})"
end
def class
MyNum
end
end
int = MyNum.new(50) # => MyNum(50)
int += 52 # => MyNum(102)
int.succ # => MyNum(103)
int + 300 # => MyNum(255)
int = -int # => MyNum(0)
int.zero? # => true
int == 0 # => true