How to dynamically define a class method which will refer to a local variable outside?
Class methods don't really exist in Ruby, they are just singleton methods of the class object. Singleton methods don't really exist, either, they are just ordinary instance methods of the object's singleton class.
Since you already know how to define instance methods (using Module#define_method
), you already know everything you need to know. You just need to call class_eval
on C
's singleton class instead of C
itself:
(class << C; self end).class_eval do
define_method(:a_class_method) do
puts var
end
end
Current versions of Ruby have a singleton_class
method to make that easier:
C.singleton_class.class_eval do
define_method(:a_class_method) do
puts var
end
end
But actually, current versions of Ruby also have Module#define_singleton_method
, so, in this particular case, that is unnecessary:
C.define_singleton_method(:a_class_method) do
puts var
end
How do I dynamically create class method by using a variable from outside a singleton class?
To accomplish what you want, you can do:
class MyClass
@@columns = ['col1', 'col2']
def self.create_methods
@@columns.each do |column_name|
define_method(column_name) { |column_name| puts column_name }
end
class << self
@@columns.each do |column_name|
define_method(column_name) { |col_name| puts "class_method defined" }
end
end
end
end
Note: Using def self."method_name"
to define a class method let you to use instances variables (ie. @columns
), that's why i redefined as class variable.
When you open the singleton class (class << self
), you lose the previous scope, so you cant get column_name
(Name Error). But have access to class variables.
In your case, use define_singleton_method
. As follow:
class MyClass
@columns = ['col1', 'col2']
def self.create_methods
@columns.each do |column_name|
define_method(column_name) { |column_name| puts column_name }
define_singleton_method(column_name) { |col_name| puts "class_method defined" }
end
end
end
How can I dynamically create class methods for a class in python
You can dynamically add a classmethod to a class by simple assignment to the class object or by setattr on the class object. Here I'm using the python convention that classes start with capital letters to reduce confusion:
# define a class object (your class may be more complicated than this...)
class A(object):
pass
# a class method takes the class object as its first variable
def func(cls):
print 'I am a class method'
# you can just add it to the class if you already know the name you want to use
A.func = classmethod(func)
# or you can auto-generate the name and set it this way
the_name = 'other_func'
setattr(A, the_name, classmethod(func))
In Ruby, is there no way to dynamically define a local variable in the current context?
It seems that Ruby's magic would provide a way, but according to Matz, this was only possible in 1.8 via eval
and only in certain contexts (i.e. irb
). As of 1.9, this behavior was taken out ("strictly forbidden"):
Matz himself weighs in here: https://www.ruby-forum.com/topic/155673#685906
I read from somewhere that now Ruby can't dynamically create local variable. Is it true or just a bug?
The local variables are created in compile time, so that local
variables that are defined in eval() cannot be accessed outside of
eval. In 1.8, irb and tryruby does line by line compilation so that
local variables are spilled from eval(), but in 1.9, it's strictly
prohibited even under line-by-line compilation.matz.
(Non-sequitur alternative here, for anyone who wants something like this but not the exact technical situation that the questioner has):
Use a hash:
local_hash = {}
my_vars.each_pair do |k,v|
local_hash[k] = v
end
puts local_hash['foo']
#=> 'baz'
Objective-C class method local variables of type self
I understand what you're looking for, but there is no instanceof(self)
pattern. The following achieves what you want, though admittedly doesn't have the elegance of typeof(self)
pattern:
@interface Foo: NSObject
@property (nonatomic, copy) NSString *string;
@end
@implementation Foo
+ (instancetype)fooWithString:(NSString *)string {
Foo *foo = [[self alloc] init];
foo.string = string;
return foo;
}
@end
@interface Foobar: Foo
// perhaps some more properties here
@end
@implementation Foobar
// and perhaps some more methods here
@end
This implementation demonstrates that the convenience method still allows subclassing. I.e., you can do:
Foobar *foobar = [Foobar fooWithString:@"baz"];
And the resulting object will be a Foobar
instance.
How to define methods dynamically inside a python class?
This is one of the classic Python stumbles. Your get the value of the variable, and the variable ends up with the final value.
You can do what you want by "capturing" the value of the variable as a default:
for method_name in method_names:
def _f(method=method_name):
print(method)
Getting local variable names defined inside a method from outside the method
You can (re)-parse the method and inspect the S-EXPR tree. See below for a proof of concept. You can get hold of the file where the method is defined using Method#source_location
and then read that file. There is surely room for improvement to my code but should get you started. It is a fully functional piece of code and only requires the ruby parser gem (https://github.com/whitequark/parser).
require 'parser/current'
node = Parser::CurrentRuby.parse(DATA.read) # DATA is everything that comes after __END__
def find_definition(node, name)
return node if definition_node?(node, name)
if node.respond_to?(:children)
node.children.find do |child|
find_definition(child, name)
end
end
end
def definition_node?(node, name)
return false if !node.respond_to?(:type)
node.type == :def && node.children.first == name
end
def collect_lvasgn(node)
result = []
return result if !node.respond_to?(:children)
node.children.each do |child|
if child.respond_to?(:type) && child.type == :lvasgn
result << child.children.first
else
result += collect_lvasgn(child)
end
end
result
end
definition = find_definition(node, :foo)
puts collect_lvasgn(definition)
__END__
def foo
var = 100
arr = [1,2]
if something
this = 3 * var
end
end
def bar
var = 200
arr = [3, 4]
end
Do you mind telling us WHY you want to find the variables?
How to dynamically open a class so to add to it a scope method that makes use of a local variable?
counter_cache_column
is a local variable. Local variable are local to the scope they are defined in (that's why they are called local variables).
In this case, the scope is the block passed to included
.
The class definition and the method definition create a new empty scope. Only blocks create nested scopes, so, you need to use a block to defined your method. Thankfully, there is a way to do so: by passing a block to define_method
:
module MyModule
extend ActiveSupport::Concern
included do
klass = get_class_name.constantize # => User
counter_cache_column = get_counter_cache # => "counter_count"
klass.define_singleton_method(:order_by_counter) {
order("#{counter_cache_column} DESC")
}
end
end
I made some other style improvements:
self
is the implicit receiver in Ruby, there is no need to specify itCLASS_NAME
is misleading: it doesn't contain the name of the class, it contains the class itself- also, I don't see why it would need to be a constant
How to dynamically create local and global variables?
You cannot define a local variable with a full stop (.
) character in ruby. That is not valid syntax.
(eval):2: unexpected fraction part after numeric literal
string_1.0 = "1.0"
Additionally, you cannot dynamically define local variables. There are various workarounds to sort-of achieve this, however, fundamentally I think you are asking an XY problem.
For example, have you considered using an OpenStruct
, or passing this hash as locales
when rendering a template, or instead dynamically setting instance variables?
Related Topics
Best Way to Cache a Response in Sinatra
What's the Best Way to Parse a Tab-Delimited File in Ruby
How to Set a Blank Value for an F.Select Form Field
How to Check If a Gem Is Installed
Listing the Names of Associated Models
Rails Activeadmin - Custom Association Select Box
Checking If a Method Is Defined on the Class
Anybody Tried the Crystal Programming Language (Machine-Code Compiled Ruby)
Sidekiq List All Jobs [Queued + Running]
Ruby Method with Maximum Number of Parameters
Ruby on Rails: Provide VS Content_For
No Implicit Conversion of String into Integer (Typeerror)
Conditional Tag Wrapping in Rails/Erb
Adding a Method to Built-In Class in Rails App