如何以及在哪里定义在控制器中使用的方法?

时间:2019-02-22 10:17:12

标签: ruby-on-rails

我希望在控制器中使用一种方法:

class Hash
  def sort_by_array a; Hash[sort_by{|k, _| a.index(k) || length}] end
end

但是将代码放入控制器后,我收到错误消息:class definition in method body

我尝试删除了class Hash和第二个end,也尝试了

class Hash
  def self.class.sort_by_array a; Hash[sort_by{|k, _| a.index(k) || length}] end
end

但是我仍然无法停止错误

作为参考,以下是控制器:

class StaticPagesController < ApplicationController

  def main

  class Hash
    def self.class.sort_by_array a; Hash[sort_by{|k, _| a.index(k) || length}] end
  end

   @languages = Listing.group_by(&:language)
   @languages.sort_by_array(@languages)

  end

end

2 个答案:

答案 0 :(得分:2)

当您在另一个类的方法内定义一个类时,就会发生该错误。即您可能正在执行以下操作:

class SomeClass
  def some_method
    class Hash
      def sort_by_array(a)
      end
    end
  end
end

假设您想通过添加方法Hash来扩展sort_by_array对象的功能,则可以像下面这样进行猴子修补:

解决方案(简单):

  • 您只能定义“实例”方法。如果您还想定义“类”方法,请参见下面的“高级”解决方案。

lib / extensions / hash.rb

module Extensions
  module Hash
    def sort_by_array(a)
      sort_by do |k, _|
        a.index(k) || length
      end
    end
  end
end

即假设您要扩展功能的另一个类:

lib / extensions / active_record / base.rb

module Extensions    
  module ActiveRecord
    module Base
      def say_hello_world
        puts 'Hello World!'
      end
    end
  end
end

config / initializers / extensions.rb

Hash.include Extensions::Hash
ActiveRecord::Base.include Extensions::ActiveRecord::Base

用法:

# rails console
some_array = [:a, :c, :b]
some_hash = { a: 1, b: 2, c: 3 }

some_hash.sort_by_array(some_array)
# => [[:a, 1], [:c, 3], [:b, 2]]


user = User.find(1)
user.say_hello_world
# => 'Hello World!'

解决方案(高级):

  • 现在允许同时定义“类”和“实例”方法:

lib / extensions / hash.rb

module Extensions
  module Hash
    def self.included(base)
      base.extend ClassMethods
      base.include InstanceMethods
    end

    # define your Hash "class methods" here inside ClassMethods
    module ClassMethods
      # commented out because not yet fully working (check update later)
      # # feel free to remove this part (see P.S. for details)
      # def self.extended(base)
      #   instance_methods.each do |method_name|
      #     raise NameError, "#{method_name} method already defined!" if (base.singleton_methods - instance_methods).include? method_name
      #   end
      # end
    end

    # define your Hash "instance methods" here inside InstanceMethods
    module InstanceMethods
      # commented out because not yet fully working (check update later)
      # # feel free to remove this part (see P.S. for details)
      # def self.included(base)
      #   instance_methods.each do |method_name|
      #     raise NameError, "#{method_name} method already defined!" if (base.instance_methods - instance_methods).include? method_name
      #   end
      # end

      def sort_by_array(a)
        sort_by do |k, _|
          a.index(k) || length
        end
      end
    end
  end
end

即假设您要扩展功能的另一个类:

lib / extensions / active_record / base.rb

module Extensions    
  module ActiveRecord
    module Base
      def self.included(base)
        base.extend ClassMethods
        base.include InstanceMethods
      end

      module ClassMethods
        # commented out because not yet fully working (check update later)
        # # feel free to remove this part (see P.S. for details)
        # def self.extended(base)
        #   instance_methods.each do |method_name|
        #     raise NameError, "#{method_name} method already defined!" if (base.singleton_methods - instance_methods).include? method_name
        #   end
        # end

        def say_hello_mars
          puts 'Hello Mars!'
        end
      end

      module InstanceMethods
        # commented out because not yet fully working (check update later)
        # # feel free to remove this part (see P.S. for details)
        # def self.included(base)
        #   instance_methods.each do |method_name|
        #     raise NameError, "#{method_name} method already defined!" if (base.instance_methods - instance_methods).include? method_name
        #   end
        # end

        def say_hello_world
          puts 'Hello World!'
        end
      end
    end
  end
end

config / initializers / extensions.rb

Hash.include Extensions::Hash
ActiveRecord::Base.include Extensions::ActiveRecord::Base

用法:

# rails console
some_array = [:a, :c, :b]
some_hash = { a: 1, b: 2, c: 3 }

some_hash.sort_by_array(some_array)
# => [[:a, 1], [:c, 3], [:b, 2]]


user = User.find(1)
user.say_hello_world
# => 'Hello World!'
ActiveRecord::Base.say_hello_mars
# => 'Hello Mars!'

PS,可以说,如果已经定义了一种方法,则无需引发错误,但这只是我个人的喜好,可以防止出现“ bug”(例如,如果您还定义了一些“ gems”完全相同的方法名称,但功能不同,您无法控制)。随时删除它们。

答案 1 :(得分:1)

将其放在单独的文件中。这将扩展基类config/initializers/hash.rb,并允许您在整个应用程序中使用。

最简单的方法是将代码放入https://github.com/rails/rails/tree/master/activesupport/lib/active_support/core_ext中,然后重新启动服务器。

或者更好,类似于Rails的操作方式(例如lib/core_ext/hash/sort_by_array.rb):

将您的代码放在这里:require "core_ext/hash/sort_by_array"

然后添加此路径以自动加载,或从您要使用它的位置手动要求它,如下所示:

{{1}}。