Reputation: 52967
I wish to use a method in the controller:
class Hash
def sort_by_array a; Hash[sort_by{|k, _| a.index(k) || length}] end
end
But after placing the code in the controller, I receive an error: class definition in method body
I tried removing the class Hash
, and second end
, and have also tried
class Hash
def self.class.sort_by_array a; Hash[sort_by{|k, _| a.index(k) || length}] end
end
But I still can't get it to stop erroring
For reference, here is the controller:
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
Upvotes: 0
Views: 85
Reputation: 6603
That error occurs when you define a class inside the method of another class. i.e. you were probably doing something like below:
class SomeClass
def some_method
class Hash
def sort_by_array(a)
end
end
end
end
Assuming you want to extend the functionality of Hash
objects by adding a method sort_by_array
, then you can do monkey-patching like below:
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
i.e. let's say another class you want to extend functionality:
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
i.e. let's say another class you want to extend functionality:
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!'
P.S, arguably you won't need to raise an error if a method is already defined, but this is just my personal taste to prevent "bugs" (i.e. if for example some "gems" you used also defined same exact methods name but having different functionality, of which you have no control of). Feel free to remove them though.
Upvotes: 2
Reputation: 311
Place it in a separate file. This would extend the base class Hash
and would allow you to use in the whole application.
The easiest would be putting the code into config/initializers/hash.rb
and restarting the server.
Or, better, similarly to how Rails does it (e.g. https://github.com/rails/rails/tree/master/activesupport/lib/active_support/core_ext
):
Put your code here: lib/core_ext/hash/sort_by_array.rb
And then either add this path to autoload, or require it manually from where you'd like to use it, like this:
require "core_ext/hash/sort_by_array"
.
Upvotes: 1