Cannot push into ruby array - ruby

I am writing a new accessor and it has its own array variable to keep information but when I try to call class_eval in its method, the push method on that variable doesn't work.
Its a method written in Class and the class_eval line reads as follows:
class_eval "def #{attr_name}=(value); #{attr_name} = value; #{information}.push value; end; def #{attr_name}_history; #{information}; end"
so the push in this line doesn't work.

You have to make sure that whatever string is stored by the local variable information when you call the class_eval is actually a method on the class that you're adding this function to.
The following works, because I make sure the class has a history method.
class A
def history
#history ||= []
end
end
attr_name = "foo"
information = "history"
A.class_eval "def #{attr_name}=(value); #{attr_name} = value; #{information}.push value; end; def #{attr_name}_history; #{information}; end"
a = A.new
a.foo = "bar"
a.foo = "baz"
a.foo_history

It won't work because "#{information}" will return the string value of information, not the actual Array object itself. This is because it is substituted for a string when it is built.
You have to change it to a variable that is part of both the scope of the object you're working in, and the class you're doing things to. For example, try changing it to something like:
Foo::Information = [...]
class_eval "
class_eval "
def #{attr_name}=(value)
#{attr_name} = value
Foo::Information.push value
end
def #{attr_name}_history
Foo::Information
end"
"

This is because information variable should be initialized as an array, then you can call push method on it. Something like this:
def #{attr_name}=(value);
#{attr_name} = value;
(#{information}||=[]).push value;
end;
def #{attr_name}_history;
#{information};
end
BTW, your formating is ugly, so you have to think twice next time.

Related

How would one return a value from a private method to a public method

I'm creating a program in ruby which I'm new to the language and was wondering how I would return the data from a private method to a public method so when the method is called upon the data is shown
I've looked around online but with being new to the language I'm finding it hard to transfer what I'm seeing to what I need in my code
def display
# return calcutalateFrequencies data here
end
private
def calculateFrequencies
#text = text.downcase.chars.each do |c|
next if c =~ /\s/
#letters[c] += 1
end
end
You can just call the private method in the public method.
def display
calculateFrequencies
end
A private method is just a method that can ONLY be called by other methods in the object, so the above is the only way calculateFrequencies would work (excepting if you were using special methods like send)
It could be done in many different ways. Since you are setting instance variables, you might use them or return the result as is.
[I have slightly modified the code to look more rubyish.]
def display
puts calculateFrequencies("foo bar")
# or puts #letters
end
private
def calculateFrequencies(text)
#text = text
#letters =
text.downcase.each_char.with_object(Hash.new(0)) do |c, letters|
next if c == " "
letters[c] += 1
end
end
You've changed the question slightly, but things you need to consider:
methods return the last executed statement. If you do an each as
the last executed statement (with or without a block) the each
enumerable is returned.
If the method modifies instance variables
(as yours do) you may not need to look at what is returned by the
method, just display the instance variables.
An example of making this work...
class FrequencyCalculator
def initialize(text)
#text = text
calculateFrequencies
end
def display
#letters
end
private
def calculateFrequencies
#letters = Hash.new(0)
#text.downcase.chars.each do |c|
next unless c =~ /\s/ # change from "if" to "unless" otherwise you're skipping letters
#letters[c] += 1
end
end
end
And then you could do
frequency_calculator = FrequencyCalculator.new('Hello World')
frequency_calculator.display
Or more compactly
FrequencyCalculator.new('Hello World').display

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, ...)

How to dynamically declare subclass in Ruby?

This question pretty much sums up the simple case for dynamically extending the class hierarchy in Ruby.
The problem I'm having is that I want to define this subclass with a DSL, and I think I'm a victim of my own complicated scope.
I have working code which uses a base class:
module Command
class Command
...
end
end
And then each command is implemented as a subclass:
module Command
class Command_quit < Command
def initialize
name = "quit"
exec do
#user.client.should_terminate = true
end
end
end
end
There is a lot of rote and repetition here, and I have envisioned a DSL which cleans this up significantly:
module Command
define :quit do
exec do # this is global.rb:7 from the error below
#user.client.should_terminate = true
end
end
end
As you can see, I want to DRY out the boilerplate as I am only concerned with the contents of #initialize, which sets some metadata (such as name) and defines the exec block (which is the important part).
I have gotten stuck with the following module method:
module Command
def self.define(cmd_name, &init_block)
class_name = "Command_#{cmd_name.to_s}"
class_definition = Class.new(Command)
class_initializer = Proc.new do
name = cmd_name
init_block.call
end
::Command.const_set class_name, class_definition
::Command.const_get(class_name).send(:define_method, :initialize, class_initializer)
end
end
This code yields lib/commands/global.rb:7:in 'exec': wrong number of arguments (0 for 1+) (ArgumentError)
And suppose I have some metadata (foo) which I want to set in my DSL:
module Command
define :quit do
foo "bar" # this becomes global.rb:7
exec do
#user.client.should_terminate = true
end
end
end
I see lib/commands/global.rb:7:in block in <module:Command>': undefined method 'foo' for Command:Module (NoMethodError)
I think I've got my Proc/block/lambda-fu wrong here, but I'm struggling to get to the bottom of the confusion. How should I write Command::define to get the desired result? It seems like although Ruby creates Command::Command_help as a subclass of Command::Command, it's not actually inheriting any of the properties.
When you refer to something in Ruby, it first look up something in local bindings, if it fails, it then look up self.something. self represents a context of the evaluation, and this context changes on class definition class C; self; end, method definition class C; def m; self; end; end, however, it won't change on block definition. The block captures the current self at the point of block definition.
module Command
define :quit do
foo "bar" # self is Command, calls Command.foo by default
end
end
If you want to modify the self context inside a block, you can use BasicObject.instance_eval (or instance_exec, class_eval, class_exec).
For your example, the block passed to define should be evaluated under the self context of an instance of the concrete command.
Here is an example. I added some mock method definition in class Command::Command:
module Command
class Command
# remove this accessor if you want to make `name` readonly
attr_accessor :name
def exec(&block)
#exec = block
end
def foo(msg)
puts "FOO => #{msg}"
end
def run
#exec.call if #exec
end
end
def self.define(name, &block)
klass = Class.new(Command) do
define_method(:initialize) do
method(:name=).call(name) # it would be better to make it readonly
instance_eval(&block)
end
# readonly
# define_method(:name) { name }
end
::Command.const_set("Command_#{name}", klass)
end
define :quit do
foo "bar"
exec do
puts "EXEC => #{name}"
end
end
end
quit = Command::Command_quit.new #=> FOO => bar
quit.run #=> EXEC => quit
puts quit.class #=> Command::Command_quit
Your problem is that blocks preserve the value of self (among other things) - when you call init_block.call and execution jumps to the block passed to define, self is the module Command and not the instance of Command_quit
You should be ok if you change your initialize method to
class_initializer = Proc.new do
self.name = cmd_name # I assume you didn't just want to set a local variable
instance_eval(&init_block)
end
instance_eval executes the block, but with the receiver (in this case your instance of Command_quit as the subclass.
An exception to the "blocks preserve self" behaviour is define_method: in that case self will always be object on which the method is called, much like with a normal method.

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.

How does this ruby custom accessor work

So the method below in class_eval dynamically creates accessors for attributes defined at runtime. It can be used, for example, to create configuration objects with attributes read from a config file (and unknown until runtime). I understanding all of it except for the else branch. If I am correct the else branch returns the attribute value (val[0]) if there is one value passed in *val. However the way its written I would expect it to return an array (val) if there is more then one value passed in *var. In particular, if I have something like the following:
value = 5
then from reading the code I would expect #value to be [=,5]. However #value returns 5 and not the array [=,5]. How is this possible?
class Module
def dsl_accessor(*symbols)
symbols.each do |sym|
class_eval %{
def #{sym}(*val)
if val.empty?
##{sym}
else
##{sym} = val.size == 1 ? val[0] : val
end
end
}
end
end
end
An equals sign is not an argument for the method, it's a part of the method name. Actually you can call an assignment like this:
value=(5)
So only the integer 5 is an argument for the function.
class Module
def dsl_accessor(*symbols)
symbols.each do |sym|
class_eval %{
def #{sym}
##{sym}
end
def #{sym}=(val)
##{sym} = val
end
}
end
end
end

Resources