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
, 和 second 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
解决方案
当您在另一个类的方法中定义一个类时,就会发生该错误。即你可能正在做类似下面的事情:
class SomeClass
def some_method
class Hash
def sort_by_array(a)
end
end
end
end
假设你想Hash
通过添加一个方法来扩展对象的功能sort_by_array
,那么你可以像下面这样进行猴子修补:
解决方案(简单):
- 您只能定义“实例”方法。如果您还想定义“类”方法,请参阅下面的“高级”解决方案。
库/扩展/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!'
解决方案(高级):
- 现在允许定义“类”和“实例”方法:
库/扩展/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,如果方法已经定义,可以说你不需要引发错误,但这只是我个人防止“错误”的口味(例如,如果你使用的一些“宝石”也定义了相同的确切方法名称但有不同的功能,您无法控制)。随意删除它们。