Adding a Method to Built-In Class in Rails App

adding a method to built-in class in rails app

One way to do this is to create a file at lib/rails_extensions.rb. Then, add your extensions like so:

class Array
def bring_me_food
# ...
end

def make_tea
# ...
end
end

class Hash
def rub_my_shoulders
# ...
end
end

Then in config/environment.rb, add this:

require 'rails_extensions'

Your mileage with subservient objects may vary.

How can i add a method to an existing class in Rails 5?

Putting monkey patched files under initializers is fine (in spite of how "good" monkey patching is itself :)).

You want to change the method definition to following:

class Date
def self.date_of_next(day)
date = parse(day)
delta = date > today ? 0 : 7
date + delta
end
end

Your problem was that you called a singleton method on the object Date, whereas it did not have such method defined.

What is the proper way to add a method to a built-in class in ruby?

tenebrousedge there was probably hinting at refinements.

Or, rather, not patching String at all. More often than not, monkeypatching creates more problems than it solves. What if String already knew alpha? and it did something different?

For example, future versions of ruby might add String#alpha? that will handle unicode properly

'新幹線'.alpha? # => true

and your code, as it is, would overwrite this built-in functionality with an inferior version. Now your app is breaking in all kinds of places, because stdlib/rails assumes the new behaviour. Chaos!

This is to say: avoid monkeypatching whenever possible. And when you can't avoid, use refinements.

Where is the best place to extend the functionality of built in classes in Rails?

config/environment.rb isn't really the best place, since you can run into serious load ordering-problems if try to extend classes that haven't been resolved at the time environment.rb is executed.

Better to put a file into config/initializers. Any script placed there will be executed after the rails runtime is loaded.

What you could do is to create a file lib/my_extensions.rb

module MyExtensions
end

then in lib/my_extensions/array.rb :

module MyExtensions::Array 
def join_with_commas
join(", ")
end
end

and in config/initializers/load_my_extensions.rb

class Array
include MyExtensions::Array
end

This will cause MyExtensions::Array to be auto-reloaded each time you invoke a request in development mode. This is nicer than restarting your application everytime you make a change to your code.

Inherit a class from a gem and add local methods

You can use concept of mixin, wherein you include a Module in another class to enhance it with additional functions.

Here is how to do it. To create a complete working example, I have created modules that resemble what you may have in your code base.

# Assumed to be present in 3rd party gem, dummy implementation used for demonstration
module Gmail
class Message
def initialize
@some_var = "there"
end
def subject
"Hi"
end
end
end

# Your code
module GmailMessage
# You can code this method assuming as if it is an instance method
# of Gmail::Message. Once we include this module in that class, it
# will be able to call instance methods and access instance variables.
def something_clever
puts "Subject is #{subject} and @some_var = #{@some_var}"
end
end

# Enhance 3rd party class with your code by including your module
Gmail::Message.include(GmailMessage)

# Below gmail object will actually be obtained by reading the user inbox
# Lets create it explicitly for demonstration purposes.
gmail = Gmail::Message.new

# Method can access methods and instance variables of gmail object
p gmail.something_clever
#=> Subject is Hi and @some_var = there

# You can call the methods of original class as well on same object
p gmail.subject
#=> "Hi"

In Rails, how to add a new method to String class?

You can define a new class in your application at lib/ext/string.rb and put this content in it:

class String
def to_magic
"magic"
end
end

To load this class, you will need to require it in your config/application.rb file or in an initializer. If you had many of these extensions, an initializer is better! The way to load it is simple:

require 'ext/string'

The to_magic method will then be available on instances of the String class inside your application / console, i.e.:

>> "not magic".to_magic
=> "magic"

No plugins necessary.

How to add a default class to text_field in rails 3

In order to get this behavior you will either have to overwrite the existing text field method or add a new method that does what you want. I would recommend the latter since you won't be changing the existing behavior of a built-in Rails method.

Similar to another answer:

class ActionView::Helpers::FormBuilder
def inputbox_field(method, options = {})
text_field(method, options.merge(class: 'inputbox'))
end
end

Then you would just change you view to use this instead:

<%= f.inputbox_field :title %>

Variable available to class methods (within Concerns)

For a quick solution, to make Product.all and Product.visible work with the least amount of modification to your existing code, you can define a parameters method inside module ClassMethods. For example:

def parameters
@parameters ||= [:visible, :desc, :value]
end

This method solution can also serve as a long-term solution if you plan to use the parameters outside of the concern, or if a subclass might want to define its own parameters.

However, if the parameters are only meant to be used inside this concern, and this data will never change, at least not through any application logic, then a constant would be the best solution because it conveys the proper meaning to the reader. I would also freeze it to prevent modification:

PARAMETERS = [:visible, :desc, :value].freeze

Another option, as Rich mentioned, is to define a class variable. Note that the constant will work whether you define it inside the List module, or inside the ClassMethods module. However, a class variable will only work inside the ClassMethods module if you want Product to be able to call it as parameters.

Also, note that self is implied in any method within ClassMethods, so you don't need to specify it. If you defined a parameters method, it would be considered a Product class method, and if you used parameters within the all method, it would refer to the class method, not an instance method as suggested by Rich.

Class variables are generally discouraged in Ruby because their side effects are often misunderstood. The Ruby Style Guide recommends avoiding them: https://github.com/bbatsov/ruby-style-guide#no-class-vars

As for speed, I compared the method and constant solutions, and it looks like the constant is faster:

require "benchmark/ips"

PARAMETERS = [:visible, :desc, :value].freeze

def parameters
@parameters ||= [:visible, :desc, :value]
end

def uses_constant
puts PARAMETERS
end

def uses_method
puts parameters
end

Benchmark.ips do |x|
x.report("constant") { uses_constant }
x.report("method") { uses_method }
x.compare!
end

The result:

Comparison:
constant: 45256.8 i/s
method: 44799.6 i/s - 1.01x slower


Related Topics



Leave a reply



Submit