我需要加载一个YAML文件(我正在尝试使用SettingsLogic),我希望该实例使用与它相同的名称加载YAML。简言之:
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...
原因我还不知道我必须加载哪些配置文件,并输入:
my_config = MySettings.new("my_config.yml")
或
my_config = MySettings.new(:MyConfig)
似乎在重复自己。
我浏览了Google和Stackoverflow,我最接近答案的是“Get Instance Name”或关于实例名称无意义的讨论! (但我可能会错误地查询错误。)
我尝试了instance#class
和instance#name
;我也试过了instance#_id2ref(self)
。
我缺少什么?!
提前致谢!
答案 0 :(得分:2)
O.K。,因此对于局部变量赋值,存在障碍,例如该赋值可能比局部变量符号添加到局部变量列表稍晚发生。但这是我的模块ConstMagicErsatz
,我曾经用它来实现类似于开箱即用的Ruby常量魔术:
a = Class.new
a.name #=> nil - anonymous
ABC = a # constant magic at work
a.name #=> "ABC"
这里的优点是您不必编写ABC = Class.new(名称:“ABC”),名称被“魔术”分配。这也适用于Struct类:
Koko = Struct.new
Koko.name #=> "Koko"
但没有其他课程。所以我的ConstMagicErsatz
允许你这样做
class MySettings < SettingsLogic
include ConstMagicErsatz
end
ABC = MySettings.new
ABC.name #=> "ABC"
以及
a = MySettings.new name: "ABC"
a.name #=> "ABC"
这就是:
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
上面的代码实现了对整个Ruby命名空间的自动搜索,目的是在调用#name方法时找到哪个常量引用给定的实例。
使用常量的唯一约束是,您必须将其大写。当然,您想要的是在对象已经生成并分配给常量后修改对象的元类。既然没有钩子,你必须找到这样做的机会,例如当新对象首次用于其目的时。所以,有
ABC = MySettings.new
然后,当您第一次使用MySettings实例时,在执行任何其他操作之前,修补其元类:
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
答案 1 :(得分:0)
你不应该做任何一件事
File.open(File.join(File.expand_path(File.dir_name(__FILE__)), foo.class), "r")
或
require foo.class
第一个不一定必须那么复杂。但如果我正确理解你,你可以直接在require或file load语句中使用foo.class。
根据需要调整YAML加载,但#class返回一个普通的旧字符串。
答案 2 :(得分:0)
不幸的是,Ruby没有用于变量赋值的钩子,但是这可以解决。战略大纲如下:首先,您需要将MySettings.new
方法转到eval code in the caller's binding。然后,您将通过在那里调用local_variables
方法找到调用者绑定中的局部变量符号列表。然后,您将迭代它们以查找哪个引用了super
方法中MySettings.new
调用返回的实例。并且您将其符号传递给source
方法调用。