Overriding instance variable array’s operators in Ruby and scoping - ruby

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.

Related

Ruby add initialized object to class array

I am unable to figure out or find any information on how to push the initialized object pointer to an array accessed from a class level variable. Here is an example.
Class Color
##colors = Array.new
def initialize
##colors << red
end
def self.list
##colors.each do |color|
puts color.to_hex
end
end
end
red = Color.new
Thanks guys for your help.
I would do it this way:
class Color
#colors = []
def self.new(*args, &blk)
#colors << super
end
def self.list
puts #colors.map(&:to_hex)
end
end
red = Color.new
Color.list
Personally, I feel uncomfortable doing class-level stuff in the instance initializer, it just doesn't feel right. The class is a completely independent object, having the instance know too much about the class smells of bad OO.
You can use self to reference the current instance of the class:
class Color
##colors = Array.new
def initialize
##colors << self
end
def self.list
##colors.each do |color|
puts color.to_hex
end
end
end
You should prefer class instance variables over class variables. Class variables are like globals - if you change it in a subclass it will also change the variable in the superclass. This is rarely the wanted effect. Here's #JKillian's code rewritten with class instance variables:
class Color
class << self
attr_accessor :colors
end
#colors = Array.new
def initialize
Color.colors << self
end
def self.list
#colors.each do |color|
puts color.to_hex
end
end
end

Dynamic properties in ruby class

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

Creating class methods from a module

Given the simple example here:
class Base
#tag = nil
def self.tag(v = nil)
return #tag unless v
#tag = v
end
end
class A < Base
tag :A
end
class B < Base
tag :B
end
class C < Base; end
puts "A: #{A.tag}"
puts "B: #{B.tag}"
puts "A: #{A.tag}"
puts "C: #{C.tag}"
which works as expected
A: A
B: B
A: A
C:
I want to create a module that base will extend to give the same functionality but with all the tag information specified by the class. Eg.
module Tester
def add_ident(v); ....; end
end
class Base
extend Tester
add_ident :tag
end
I've found i can do it with a straight eval, so:
def add_ident(v)
v = v.to_s
eval "def self.#{v}(t = nil); return ##{v} unless t; ##{v} = t; end"
end
but i really dislike using eval string in any language.
Is there a way that i can get this functionality without using eval? I've gone through every combination of define_method and instance_variable_get/set i can think of and i can't get it to work.
Ruby 1.9 without Rails.
You want to define a dynamic method on the singleton class of the class you're extending. The singleton class of a class can be accessed with expression like this: class << self; self end. To open the scope of a class's class, you can use class_eval. Putting all this together, you can write:
module Identification
def add_identifier(identifier)
(class << self; self end).class_eval do
define_method(identifier) do |*args|
value = args.first
if value
instance_variable_set("##{identifier}", value)
else
instance_variable_get("##{identifier}")
end
end
end
end
end
class A
extend Identification
add_identifier :tag
end
If you're using recent versions of Ruby, this approach can be replaced with Module#define_singleton_method:
module Identification
def add_identifier(identifier)
define_singleton_method(identifier) do |value = nil|
if value
instance_variable_set("##{identifier}", value)
else
instance_variable_get("##{identifier}")
end
end
end
end
I don't believe you want to use self.class.send(:define_method), as shown in another answer here; this has the unintended side effect of adding the dynamic method to all child classes of self.class, which in the case of A in my example is Class.
module Tester
def add_ident(var)
self.class.send(:define_method, var) do |val=nil|
return instance_variable_get("##{var}") unless val
instance_variable_set "##{var}", val
end
end
end
My favourite ruby book Metaprogramming Ruby solved these questions like the following way:
module AddIdent
def self.included(base)
base.extend ClassMethods # hook method
end
module ClassMethods
def add_ident(tag)
define_method "#{tag}=" do |value=nil|
instance_variable_set("##{tag}", value)
end
define_method tag do
instance_variable_get "##{tag}"
end
end
end
end
# And use it like this
class Base
include AddIdent
add_ident :tag
end
Bah isn't it always the way that once you get frustrated enough to post you then find the answer :)
The trick seems to be in (class << self; self; end) to give you the class instance without destroying the local scope. Referencing: How do I use define_method to create class methods?
def add_ident(v)
var_name = ('#' + v.to_s).to_sym
(class << self; self; end).send(:define_method, v) do |t = nil|
return instance_variable_get(var_name) unless t
instance_variable_set(var_name, t)
end
end
I'll accept better answers if them come along though.

Create attr_reader methods while setting the values for the according instance variables in the initialize method?

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'

Overriding instance variable array's operators in Ruby

Sorry for the poor title, I don't really know what to call this.
I have something like this in Ruby:
class Test
def initialize
#my_array = []
end
attr_accessor :my_array
end
test = Test.new
test.my_array << "Hello, World!"
For the #my_array instance variable, I want to override the << operator so that I can first process whatever is being inserted to it. I've tried #my_array.<<(value) as a method in the class, but it didn't work.
I think you're looking for this:
class Test
def initialize
#myarray = []
class << #myarray
def <<(val)
puts "adding #{val}" # or whatever it is you want to do first
super(val)
end
end
end
attr_accessor :myarray
end
There's a good article about this and related topics at Understanding Ruby Singleton Classes.
I'm not sure that's actually something you can do directly.
You can try creating a derived class from Array, implementing your functionality, like:
class MyCustomArray < Array
def initialize &process_append
#process_append = &process_append
end
def << value
raise MyCustomArrayError unless #process_append.call value
super.<< value
end
end
class Test
def initialize
#my_array = MyCustomArray.new
end
attr_accessor :my_array
end
Here you go...
$ cat ra1.rb
class Aa < Array
def << a
puts 'I HAVE THE CONTROL!!'
super a
end
end
class Test
def initialize
#my_array = Aa.new
end
attr_accessor :my_array
end
test = Test.new
test.my_array << "Hello, World!"
puts test.my_array.inspect
$ ruby ra1.rb
I HAVE THE CONTROL!!
["Hello, World!"]
$
a = []
a.instance_eval("alias old_add <<; def << value; puts value; old_add(value); end")
Very hackish, and off the top of my head ...
Just change 'puts value' with whatever preprocessing you want to do.
You can extend the metaclass of any individual object, without having to create a whole new class:
>> i = []
=> []
>> class << i
>> def <<(obj)
>> puts "Adding "+obj.to_s
>> super
>> end
>> end
=> nil
>> i << "foo"
Adding foo
=> ["foo"]
i extend the class, creating a method which provides access to the instance variable.
class KeywordBid
def override_ignore_price(ignore_price)
#ignorePrice = ignore_price
end
end

Resources