Ruby: what is wrong with this change_keys functions? - ruby

class Klass
attr_accessor :keys
def change_keys(opt)
if opt == 1
keys = [keys[0], keys[keys.length - 1]]
else
tmp = keys[0]
keys[0] = keys[keys.length-1]
keys[keys.length-1] = tmp
end
keys
end
end
klass = Klass.new
klass.keys = [1,2,3,4,5]
# puts klass.change_keys(1)
# puts klass.change_keys(2)
This is not working at all, the error says: undefined method '[]' method for nil:NilClass

Ruby interprets keys[0] and the others in line 5 as local variables because it has seen a keys = ... . There is an ambiguity in Ruby grammar when it comes to differentiate local variables from method calls without arguments that gets disambiguated by that heuristic. that is, if the parser sees an assignment to that identifier then is a local variable if not is a method call.
You can solve this by referring to self.keys instead. to make clear that you want to use the accessor method.
class Klass
attr_accessor :keys
def change_keys(opt)
if opt == 1
self.keys = [keys[0], keys[keys.length - 1]]
else
tmp = keys[0]
keys[0] = keys[keys.length-1]
keys[keys.length-1] = tmp
end
keys
end
end
klass = Klass.new
klass.keys = [1,2,3,4,5]
puts klass.change_keys(1)
puts klass.change_keys(2)

It looks like you're expecting keys, within your change_keys method, to refer to an instance variable (the same one as you set explicitly by writing klass.keys = [1,2,3,4,5]), but it doesn't. You want #keys instead.
(There are programming languages, such as C++ and Smalltalk and Java, in which unadorned variable names are automatically taken to refer to instance variables. Ruby isn't one of them.)

You did not define an instance variable named keys to reference it. I would add the initialization and use the #keys instance variable:
class Klass
#attr_accessor :keys
def initialize(keys)
#keys = keys
end
def change_keys(opt)
if opt == 1
#keys = [#keys[0], #keys[#keys.length - 1]]
else
tmp = #keys[0]
#keys[0] = #keys[#keys.length-1]
#keys[#keys.length-1] = tmp
end
#keys
end
end
klass = Klass.new([1,2,3,4,5])
# puts keys.change_keys(1)
# puts keys.change_keys(2)
Try it out and let me know.

Attributes are instance variables, so you should be referring to keys as #keys everywhere within your method.

Related

How to use a hash in a class_eval statement in Ruby

I was working on a homework assignment when I ran into a frustrating issue. The assignment is an exercise in Ruby metaprogramming and the goal is to define an 'attr_accessor_with_history' that does all the same things as 'attr_accessor', but also provides a history of all values that an attribute has ever been. Here is the provided code from the assignment along with some code I added in an attempt to complete the assignment:
class Class
def attr_accessor_with_history(attr_name)
attr_name = attr_name.to_s
attr_hist_name = attr_name+'_history'
history_hash = {attr_name => []}
#getter
self.class_eval("def #{attr_name} ; ##{attr_name} ; end")
#setter
self.class_eval %Q{
def #{attr_name}=(val)
# add to history
##{attr_hist_name} = [nil] if ##{attr_hist_name}.nil?
##{attr_hist_name} << val
history_hash[##{attr_name}] = ##{attr_hist_name}
# set the value itself
##{attr_name} = val
end
def history(attr) ; #history_hash[attr.to_s] ; end
}
end
end
class Foo
attr_accessor_with_history :bar
attr_accessor_with_history :crud
end
f = Foo.new # => #<Foo:0x127e678>
f.bar = 3 # => 3
f.bar = :wowzo # => :wowzo
f.bar = 'boo!' # => 'boo!'
puts f.history(:bar) # => [3, :wowzo, 'boo!']
f.crud = 42
f.crud = "Hello World!"
puts f.history(:crud)
I wanted to use a hash to store different histories for different attributes but I cannot access that hash in the class_eval statement for the setter. No matter how I try to set it up I always either seem to get a NoMethodError for the []= method because 'history_hash' somehow becomes type NilClass, or a NameError occurs because it sees 'history_hash' as an undefined local variable or method. How do I use the hash in the class_eval statements?
or a NameError occurs because it sees 'history_hash' as an undefined local variable or method
I'd say you can't, because it is a local variable, one that is inaccessible in the context you want it. However, why do you even need it? I'm reasonably sure it's in the "some code I added in an attempt to complete the assignment", and not the original assignment code (which, I assume, expects you to store the history of #bar in #bar_history - or else what is attr_hist_name all about?)
I'm also uncomfortable about string evals; it's generally not necessary, and Ruby can do better, with its powerful metaprogramming facilities. Here's how I'd do it:
class Class
def attr_accessor_with_history(attr_name)
attr_setter_name = :"#{attr_name}="
attr_getter_name = :"#{attr_name}"
attr_hist_name = :"##{attr_name}_history"
attr_name = :"##{attr_name}"
self.class_eval do
define_method(attr_getter_name) do
instance_variable_get(attr_name)
end
define_method(attr_setter_name) do |val|
instance_variable_set(attr_name, val)
history = instance_variable_get(attr_hist_name)
instance_variable_set(attr_hist_name, history = []) unless history
history << val
end
end
end
end
class Object
def history(attr_name)
attr_hist_name = :"##{attr_name}_history"
instance_variable_get(attr_hist_name)
end
end
Finally, as it's monkey-patching base classes, I'd rather use refinements to add it where needed, but that's probably an overkill for an assignment.

How deny creating instance variables in Ruby

I would like to deny creating instance variables in Ruby,to prevent unattended variables being created 'by mistake'.
My class:
class Test
def initialize
#a = 'Var A'
end
def make_new
#b = 'Var B' <-- I would like to deny creation of any variables that were not defined during the init
end
end
I don't claim this is a good idea, but just b/c it's kind of interesting, here is a solution that will throw an exception when a new ivar is created, but will also let you modify defined instance variables (unlike freezing the class). Just threw this together, there are undoubtably some issues w/ it, including the fact that it duplicates every method :)
module IvarBlocker
def method_added(method)
alias_name = "__#{method}_orig"
return if method == :initialize || method_defined?(alias_name) || method.match(/__.*_orig/)
alias_method alias_name, method
define_method(method) do |*args|
ivars_before = instance_variables.dup
send(alias_name, *args).tap { raise "New Ivar assigned" if !(instance_variables - ivars_before).empty? }
end
end
end
Usage
class Test
extend IvarBlocker
def initialize
#a = 1
end
def set_b
#b = 2
end
def set_a
#a = 6
end
end
t = Test.new #=> #<Test:0x007f87f13c41e8 #a=1>
t.set_b #=> RuntimeError: New Ivar assigned
t.set_a #=> 6
You can freeze object instances at the end of initialize method:
class Test
def initialize
#a = 'Var A'
freeze
end
def make_new
#b = 'Var B' # I would like to deny creation of any variables that were not defined during the init
end
end
t=Test.new
p t.instance_variable_get :#a
# "Var A"
t.make_new
#a.rb:24:in `make_new': can't modify frozen Test (RuntimeError)
# from a.rb:30:in `<main>'
t.instance_variable_set :#c, 'Var C'
# a.rb:31:in `instance_variable_set': can't modify frozen Test (RuntimeError)
# from a.rb:31:in `<main>'
class << t
#d = 'Var D'
end
#a.rb:33:in `singletonclass': can't modify frozen Class (RuntimeError)
# from a.rb:32:in `<main>'
p t.instance_variable_get :#d
There is a way - a hacky (but fun) way which is not meant for production (and is relatively slow). My sample implementation works for a single object only, but can be extended to support many objects.
Let's assume the following setup:
class Foo
def initialize
#a = :foo
end
def set_b; #b = 3; end
def set_c; #c = 7; end
end
def freeze_variables_of(obj)
frozen_variables = obj.instance_variables
set_trace_func lambda {|event, file, line, id, binding, classname|
if classname == obj.class
this = binding.eval 'self'
if this == obj
(this.instance_variables - frozen_variables).each {|var| this.remove_instance_variable var}
end
end
}
end
With the use of set_trace_func we can set a Proc which is called very often (usually more than once per statement). In that Proc we can check instance variables and remove unwanted variables.
Let's look at an example:
a = Foo.new
# => #<Foo:0x007f6f9db75cc8 #a=:foo>
a.set_b; a
# => #<Foo:0x007f6f9db75cc8 #a=:foo, #b=3>
freeze_variables_of a
a.set_c; a
# => #<Foo:0x007f6f9db75cc8 #a=:foo, #b=3>
We see that after doing the "freeze", set_c cannot set the instance variable (in fact the variable is removed at the very moment the set_c method returns).
In contrast to freezing the object (with a.freeze) (which I'd recommend for any real world application), this way allows you to modify all allowed instance variables while forbidding new ones.
This even works, if you directly assign instance variables (while Alex' method is probably faster, but relies on accessor methods).
There is no way to prevent creation of accidental instance variables defined that way. Why do you want to do this? What would you want such code to achieve?

Name of the current object/class instance

I need to load a YAML file (I'm experimenting with SettingsLogic) and I'd like the instance to load the YAML with the same name as it. Briefly:
class MySettings < SettingsLogic
source "whatever_the_instance_is_called.yml"
# Do some other stuff here
end
basic_config = MySettings.new # loads & parses basic_config.yml
advanced_cfg = MySettings.new # loads & parses advanced_cfg.yml
...and so on...
The reason for this I don't yet know what configuration files I'll have to load, and typing:
my_config = MySettings.new("my_config.yml")
or
my_config = MySettings.new(:MyConfig)
just seems to be repeating myself.
I took a look around both Google and Stackoverflow, and the closest I came to an answer is either "Get Instance Name" or a discussion about how meaningless an instance name is! (I'm probably getting the query wrong, however.)
I have tried instance#class, and instance#name; I also tried instance#_id2ref(self).
What am I missing?!
Thanks in advance!
O.K., so with local variable assignment, there are snags, such as that assignment might occur slightly later than local variable symbol addition to the local variable list. But here is my module ConstMagicErsatz that I used to implement something similar to out-of-the box Ruby constant magic:
a = Class.new
a.name #=> nil - anonymous
ABC = a # constant magic at work
a.name #=> "ABC"
The advantage here is that you don't have to write ABC = Class.new( name: "ABC" ), name gets assigned 'magically'. This also works with Struct class:
Koko = Struct.new
Koko.name #=> "Koko"
but with no other classes. So here goes my ConstMagicErsatz that allows you to do
class MySettings < SettingsLogic
include ConstMagicErsatz
end
ABC = MySettings.new
ABC.name #=> "ABC"
As well as
a = MySettings.new name: "ABC"
a.name #=> "ABC"
Here it goes:
module ConstMagicErsatz
def self.included receiver
receiver.class_variable_set :##instances, Hash.new
receiver.class_variable_set :##nameless_instances, Array.new
receiver.extend ConstMagicClassMethods
end
# The receiver class will obtain #name pseudo getter method.
def name
self.class.const_magic
name_string = self.class.instances[ self ].to_s
name_string.nil? ? nil : name_string.demodulize
end
# The receiver class will obtain #name setter method
def name= ɴ
self.class.const_magic
self.class.instances[ self ] = ɴ.to_s
end
module ConstMagicClassMethods
# #new method will consume either:
# 1. any parameter named :name or :ɴ from among the named parameters,
# or,
# 2. the first parameter from among the ordered parameters,
# and invoke #new of the receiver class with the remaining arguments.
def new( *args, &block )
oo = args.extract_options!
# consume :name named argument if it was supplied
ɴς = if oo[:name] then oo.delete( :name ).to_s
elsif oo[:ɴ] then oo.delete( :ɴ ).to_s
else nil end
# but do not consume the first ordered argument
# and call #new method of the receiver class with the remaining args:
instance = super *args, oo, &block
# having obtained the instance, attach the name to it
instances.merge!( instance => ɴς )
return instance
end
# The method will search the namespace for constants to which the objects
# of the receiver class, that are so far nameless, are assigned, and name
# them by the first such constant found. The method returns the number of
# remaining nameless instances.
def const_magic
self.nameless_instances =
class_variable_get( :##instances ).select{ |key, val| val.null? }.keys
return 0 if nameless_instances.size == 0
catch :no_nameless_instances do search_namespace_and_subspaces Object end
return nameless_instances.size
end # def const_magic
# ##instances getter and setter for the target class
def instances; const_magic; class_variable_get :##instances end
def instances= val; class_variable_set :##instances, val end
# ##nameless_instances getter for the target class
def nameless_instances; class_variable_get :##nameless_instances end
def nameless_instances= val; class_variable_set :##nameless_instances, val end
private
# Checks all the constants in some module's namespace, recursivy
def search_namespace_and_subspaces( ɱodule, occupied = [] )
occupied << ɱodule.object_id # mark the module "occupied"
# Get all the constants of ɱodule namespace (in reverse - more effic.)
const_symbols = ɱodule.constants( false ).reverse
# check contents of these constant for wanted objects
const_symbols.each do |sym|
# puts "#{ɱodule}::#{sym}" # DEBUG
# get the constant contents
obj = ɱodule.const_get( sym ) rescue nil
# is it a wanted object?
if nameless_instances.map( &:object_id ).include? obj.object_id then
class_variable_get( :##instances )[ obj ] = ɱodule.name + "::#{sym}"
nameless_instances.delete obj
# and stop working in case there are no more unnamed instances
throw :no_nameless_instances if nameless_instances.empty?
end
end
# and recursively descend into the subspaces
const_symbols.each do |sym|
obj = ɱodule.const_get sym rescue nil # get the const value
search_namespace_and_subspaces( obj, occupied ) unless
occupied.include? obj.object_id if obj.kind_of? Module
end
end
end # module ConstMagicClassMethods
end # module ConstMagicErsatz
The above code implements automatic searching of whole Ruby namespace with the aim of finding which constant refers to the given instance, whenever #name method is called.
The only constraint using constants gives you, is that you have to capitalize it. Of course, what you want would be modifying the metaclass of the object after it is already born and assigned to a constant. Since, again, there is no hook, you have to finde the occasion to do this, such as when the new object is first used for its purpose. So, having
ABC = MySettings.new
and then, when the first use of your MySettings instance occurs, before doing anything else, to patch its metaclass:
class MySettings
def do_something_useful
# before doing it
instance_name = self.name
singleton_class.class_exec { source "#{instance_name}.yml" }
end
# do other useful things
end
Shouldn't you be able to do either
File.open(File.join(File.expand_path(File.dir_name(__FILE__)), foo.class), "r")
or
require foo.class
The first one need not be that complicated necessarily. But if I'm understanding you correctly, you can just use foo.class directly in a require or file load statement.
Adjust as necessary for YAML loading, but #class returns a plain old string.
Well if you have tons of variables to instantiate, I'd personally just create a Hash to hold them, it's cleaner this way. Now to instantiate all of this, you could do a loop other all your yaml files :
my_settings = {}
[:basic_config, :advanced_cfg, :some_yaml, :some_yaml2].each do |yaml_to_parse|
my_settings[yaml_to_parse] = MySettings.new(yaml_to_parse)
end
Make sure your initialize method in MySettings deals with the symbol you give it!
Then get your variables like this :
my_settings[:advanced_cfg]
Unfortunately, Ruby has no hooks for variable assignment, but this can be worked around. The strategy outline is as follows: First, you will need to get your MySettings.new method to eval code in the caller's binding. Then, you will find the list of local variable symbols in the caller's binding by calling local_variables method there. Afterwards, you will iterate over them to find which one refers to the instance returned by super call in your custom MySettings.new method. And you will pass its symbol to source method call.

Change the binding of a Proc in Ruby

I have this code:
l = lambda { a }
def some_function
a = 1
end
I just want to access a by the lambda and a special scope which has defined a already somewhere like inside some_function in the example, or just soon later in the same scope as:
l = lambda { a }
a = 1
l.call
Then I found when calling l, it is still using its own binding but not the new one where it was called.
And then I tried to use it as:
l.instance_eval do
a = 1
call
end
But this also failed, it is strange that I can't explain why.
I know the one of the solution is using eval, in which I could special a binding and executing some code in text, but I really do not want to use as so.
And, I know it is able to use a global variable or instance variable. However, actually my code is in a deeper embedded environment, so I don't want to break the completed parts if not quite necessary.
I have referred the Proc class in the documentation, and I found a function names binding that referred to the Proc's context. While the function only provided a way to access its binding but cannot change it, except using Binding#eval. It evaluate text also, which is exactly what I don't like to do.
Now the question is, do I have a better (or more elegant) way to implement this? Or using eval is already the regular manner?
Edit to reply to #Andrew:
Okay, this is a problem which I met when I'm writing a lexical parser, in which I defined a array with fixed-number of items, there including at least a Proc and a regular expression. My purpose is to matching the regular expressions and execute the Procs under my special scope, where the Proce will involved some local variables that should be defined later. And then I met the problem above.
Actually I suppose it is not same completely to that question, as mine is how to pass in binding to a Proc rather than how to pass it out.
#Niklas:
Got your answer, I think that is what exactly I want. It has solved my problem perfectly.
You can try the following hack:
class Proc
def call_with_vars(vars, *args)
Struct.new(*vars.keys).new(*vars.values).instance_exec(*args, &self)
end
end
To be used like this:
irb(main):001:0* lambda { foo }.call_with_vars(:foo => 3)
=> 3
irb(main):002:0> lambda { |a| foo + a }.call_with_vars({:foo => 3}, 1)
=> 4
This is not a very general solution, though. It would be better if we could give it Binding instance instead of a Hash and do the following:
l = lambda { |a| foo + a }
foo = 3
l.call_with_binding(binding, 1) # => 4
Using the following, more complex hack, this exact behaviour can be achieved:
class LookupStack
def initialize(bindings = [])
#bindings = bindings
end
def method_missing(m, *args)
#bindings.reverse_each do |bind|
begin
method = eval("method(%s)" % m.inspect, bind)
rescue NameError
else
return method.call(*args)
end
begin
value = eval(m.to_s, bind)
return value
rescue NameError
end
end
raise NoMethodError
end
def push_binding(bind)
#bindings.push bind
end
def push_instance(obj)
#bindings.push obj.instance_eval { binding }
end
def push_hash(vars)
push_instance Struct.new(*vars.keys).new(*vars.values)
end
def run_proc(p, *args)
instance_exec(*args, &p)
end
end
class Proc
def call_with_binding(bind, *args)
LookupStack.new([bind]).run_proc(self, *args)
end
end
Basically we define ourselves a manual name lookup stack and instance_exec our proc against it. This is a very flexible mechanism. It not only enables the implementation of call_with_binding, it can also be used to build up much more complex lookup chains:
l = lambda { |a| local + func(2) + some_method(1) + var + a }
local = 1
def func(x) x end
class Foo < Struct.new(:add)
def some_method(x) x + add end
end
stack = LookupStack.new
stack.push_binding(binding)
stack.push_instance(Foo.new(2))
stack.push_hash(:var => 4)
p stack.run_proc(l, 5)
This prints 15, as expected :)
UPDATE: Code is now also available at Github. I use this for one my projects too now.
class Proc
def call_with_obj(obj, *args)
m = nil
p = self
Object.class_eval do
define_method :a_temp_method_name, &p
m = instance_method :a_temp_method_name; remove_method :a_temp_method_name
end
m.bind(obj).call(*args)
end
end
And then use it as:
class Foo
def bar
"bar"
end
end
p = Proc.new { bar }
bar = "baz"
p.call_with_obj(self) # => baz
p.call_with_obj(Foo.new) # => bar
Perhaps you don't actually need to define a later, but instead only need to set it later.
Or (as below), perhaps you don't actually need a to be a local variable (which itself references an array). Instead, perhaps you can usefully employ a class variable, such as ##a. This works for me, by printing "1":
class SomeClass
def l
#l ||= lambda { puts ##a }
end
def some_function
##a = 1
l.call
end
end
SomeClass.new.some_function
a similar way:
class Context
attr_reader :_previous, :_arguments
def initialize(_previous, _arguments)
#_previous = _previous
#_arguments = _arguments
end
end
def _code_def(_previous, _arguments = [], &_block)
define_method("_code_#{_previous}") do |_method_previous, _method_arguments = []|
Context.new(_method_previous, _method_arguments).instance_eval(&_block)
end
end
_code_def('something') do
puts _previous
puts _arguments
end

Ruby create methods from a hash

I have the following code I am using to turn a hash collection into methods on my classes (somewhat like active record). The problem I am having is that my setter is not working. I am still quite new to Ruby and believe I've gotten myself turned around a bit.
class TheClass
def initialize
#properties = {"my hash"}
self.extend #properties.to_methods
end
end
class Hash
def to_methods
hash = self
Module.new do
hash.each_pair do |key, value|
define_method key do
value
end
define_method("#{key}=") do |val|
instance_variable_set("##{key}", val)
end
end
end
end
end
The methods are created and I can read them on my class but setting them does not work.
myClass = TheClass.new
item = myClass.property # will work.
myClass.property = item # this is what is currently not working.
If your goal is to set dynamic properties then you could use OpenStruct.
require 'ostruct'
person = OpenStruct.new
person.name = "Jennifer Tilly"
person.age = 52
puts person.name
# => "Jennifer Tilly"
puts person.phone_number
# => nil
It even has built-in support to create them from a hash
hash = { :name => "Earth", :population => 6_902_312_042 }
planet = OpenStruct.new(hash)
Your getter method always returns the value in the original hash. Setting the instance variable won't change that; you need to make the getter refer to the instance variable. Something like:
hash.each_pair do |key, value|
define_method key do
instance_variable_get("##{key}")
end
# ... define the setter as before
end
And you also need to set the instance variables at the start, say by putting
#properties.each_pair do |key,val|
instance_variable_set("##{key}",val)
end
in the initialize method.
Note: I do not guarantee that this is the best way to do it; I am not a Ruby expert. But it does work.
It works just fine for me (after fixing the obvious syntax errors in your code, of course):
myClass.instance_variable_get(:#property) # => nil
myClass.property = 42
myClass.instance_variable_get(:#property) # => 42
Note that in Ruby instance variables are always private and you never define a getter for them, so you cannot actually look at them from the outside (other than via reflection), but that doesn't mean that your code doesn't work, it only means that you cannot see that it works.
This is essentially what I was suggesting with method_missing. I'm not familiar enough with either route to say why or why not to use it which is why I asked above. Essentially this will auto-generate properties for you:
def method_missing sym, *args
name = sym.to_s
aname = name.sub("=","")
self.class.module_eval do
attr_accessor aname
end
send name, args.first unless aname == name
end

Resources