【问题标题】:ruby: how to load .rb file in the local contextruby:如何在本地上下文中加载 .rb 文件
【发布时间】:2010-10-29 00:07:39
【问题描述】:

如何在 Ruby 中完成这个简单的任务?
我有一些简单的配置文件

=== config.rb
config = { 'var' => 'val' }

我想从main.rb 文件中定义的某个方法加载配置文件,以便config.rb 中的局部变量成为该方法的局部变量。
像这样的:

=== main.rb
Class App
    def loader
        load('config.rb') # or smth like that
        p config['var']   # => "val"
    end
end

我知道我可以在 config.rb 中使用全局变量,然后在完成后取消定义它们,但我希望有一个 ruby​​ 方式)

【问题讨论】:

  • 你可以用 eval 来做,但这不是一个好主意(在任何语言中)。
  • 有沙箱可以让你在控制器环境中执行代码,所以我会投票来弥补你的反对

标签: ruby variables scope load local


【解决方案1】:

配置文件。

{ 'var' => 'val' }

加载配置文件

class App
  def loader
    config = eval(File.open(File.expand_path('~/config.rb')).read)
    p config['var']
  end
end

【讨论】:

    【解决方案2】:

    正如其他人所说,对于配置,最好使用 YAML 或 JSON。评估文件

    binding.eval(File.open(File.expand_path('~/config.rb')).read, "config.rb") binding.eval(File.read(File.expand_path('~/config.rb')), "config.rb")

    这种语法可以让你在回溯中看到文件名,这很重要。请参阅 api 文档 [1]。

    更新了eval 命令以避免 FD(文件描述符)泄漏。我一定是在睡觉,或者应该在晚上的那个时候睡觉,而不是在 stackoverflow 上写东西..

    [1]http://www.ruby-doc.org/core-1.9.3/Binding.html

    【讨论】:

      【解决方案3】:

      您当然可以使用 eval 和 File.read 破解出一个解决方案,但是这很难的事实应该给您一个信号,即这不是一种类似于 ruby​​ 的方式来解决您遇到的问题。两种替代设计是使用 yaml 作为您的配置 api,或者定义一个简单的 dsl。

      YAML 案例是最简单的,您只需在 main.rb 中有类似的内容:

      Class App
        def loader
            config = YAML.load('config.yml')
            p config['var']   # => "val"
        end
      end
      

      你的配置文件看起来像:

      --- 
      var: val
      

      【讨论】:

      • Yaml 不起作用:config.rb 只是一个例子——应该有一些处理——不仅仅是序列化的数据。实际上,我需要一个简单的命令“exec_file_as_it_was_typed_here(file)”。顺便说一句,php可以做到这一点)
      • 另外,dsl 尤其是 eval 不是变体。至少现在。我只是想让事情变得简单。
      • 简短的版本是 ruby​​ 无法做到这一点,较长的版本是您应该定义和执行 DSL,而不是依赖于范围界定的巧妙 hack。
      • "php 可以做到这一点" ...尽量不要尖叫...互联网上有无数的网站证明 PHP 可以做到这一点,这导致我们其他人诅咒允许它的程序员。
      【解决方案4】:

      我不建议这样做,除非在受控环境中。

      将模块保存到具有预定名称的文件中,该名称定义了initializerun_it 方法。对于这个例子,我使用 test.rb 作为文件名:

      module Test
        @@classvar = 'Hello'
        def initialize
          @who = 'me'
        end
      
        def get_who
          @who
        end
      
        def run_it
          print "#{@@classvar} #{get_who()}"
        end
      end
      

      然后编写一个简单的应用程序来加载并执行它:

      require 'test'
      
      class Foo
        include Test
      end
      
      END {
        Foo.new.run_it
      }
      
      # >> Hello me
      

      仅仅因为您可以做某事并不意味着您应该做某事。我想不出我在生产中这样做的原因,只是在这里展示它作为一种好奇心和概念证明。将其提供给不知名的人将是让您的机器被黑客入侵的好方法,因为该代码可以执行拥有帐户可以执行的任何操作。

      【讨论】:

      • 感谢您的方法,但正如您自己所看到的 - 这甚至比“php 风格”更多余的解决方案......
      • 我对元编程并不是特别热衷,但 php 对我来说简直就是地狱。我认为像 ruby​​ 这样强大的动态工具可以完成如此​​原始的任务。坏消息。它不能。感谢大家的关注!
      • 您可能需要将行 require 'test' 更改为 require './test'
      【解决方案5】:

      我只需要做类似的事情,因为我希望能够加载一个“Ruby DLL”,它返回一个匿名类(事物实例的工厂)我创建了它来跟踪已加载的项目并允许加载的文件返回一个值,该值可以是任何东西——一个完全匿名的类、模块、数据等。它可以是一个模块,然后你可以在加载后将其“包含”在一个对象中,它可以提供大量“属性”或方法。您还可以添加一个“卸载”项以从加载的哈希中清除它并取消引用它加载的任何对象。

      module LoadableModule
      
      @@loadedByFile_ = {};
      
      def self.load(fileName)
          fileName = File.expand_path(fileName);
          mod = @@loadedByFile_[fileName];
          return mod if mod;
          begin           
              Thread.current[:loadReturn] = nil;
              Kernel.load(fileName);
              mod = Thread.current[:loadReturn];
              @@loadedByFile_[fileName] = mod if(mod);
          rescue => e
              puts(e);
              puts(e.backtrace);
              mod = nil;
          end
          Thread.current[:loadReturn] = nil;
          mod
      end
      def self.onLoaded(retVal)
          Thread.current[:loadReturn] = retVal;
      end
      end 
      

      在加载的文件中:

      LoadableModule.onLoaded("a value to return from the loaded file");
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-07-20
        • 1970-01-01
        • 2011-04-23
        • 2013-07-25
        • 2019-02-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多