I want to add new method to class String, for example. But I don't want to make this change global (keeping classes clean is good, yes?).
So, instead of this code
class String
def is_palindrome?
self == self.reverse
end
end
module MyModule
class MyClass
def filter_palindrome(str_arr)
str_arr.select { |s| s.is_palindrome? }
end
end
end
I want to have something like this:
module MyModule
class String
def is_palindrome?
self == self.reverse
end
end
class MyClass
def self.filter_palindrome(str_arr)
str_arr.select { |s| s.is_palindrome? }
end
end
end
But, of course, it's not working (undefined method 'is_palindrome?' for :String). So, is there any point in what I want? And if there is, what is the best way to achieve it?
If you are using Ruby 2.0, you can try refinements.
module MyModule
module StringAlter
refine String do
def is_palindrome?
self == self.reverse
end
end
end
end
using MyModule::StringAlter
module MyModule
class MyClass
def self.filter_palindrome(str_arr)
str_arr.select { |s| s.is_palindrome? }
end
end
end
If prior to Ruby 2.0, you cannot achieve this directly. Changes made to String will be global. However, instead of s.is_palindrome?, why not write a helper method and call it like is_palindrome?(s). Then you don't have to reopen String and you can restrict is_palindrome? to be available only in some given scope.
This is the way Python does (self), and so as to C# extension method.
Related
Hi I am trying to create a helper for mass defining ruby methods as private class methods. In general one can define a method as a private class method by using private_class_method key work. But I would like to create a helper in the following style:
class Person
define_private_class_methods do
def method_one
end
def method_two
end
end
end
The way I planned to dynamically define this is in the following way, which is not at all working:
class Object
def self.define_private_class_methods &block
instance_eval do
private
&block
end
end
end
any ideas where I might be going wrong?
$ cat /tmp/a.rb
class Object
def self.define_private_class_methods &cb
existing = methods(false)
instance_eval &cb
(methods(false) - existing).each { |m| singleton_class.send :private, m }
end
end
class Person
define_private_class_methods do
def method_one
puts "¡Yay!"
end
end
end
Person.send(:method_one)
Person.public_send(:method_one)
$ ruby /tmp/a.rb
¡Yay!
/tmp/a.rb:18:in `public_send': private method `method_one'
called for Person:Class (NoMethodError)
Did you mean? method
from /tmp/a.rb:18:in `<main>'
Please note, that it’s hard to understand, what you are trying to achieve and possibly there is better, cleaner and more robust way to achieve this functionality.
Similar, yet different (and semantically more correct IMHO) to #mudasobwa's answer:
class Class
def define_private_class_methods(&definition)
class_methods_prior = methods
singleton_class.class_eval(&definition)
(methods - class_methods_prior).each do |method_name|
private_class_method method_name
end
end
end
class Person
define_private_class_methods do
def method_one
1
end
end
end
Person.method_one # !> NoMethodError: private method `method_one' called for Person:Class
Person.send :method_one # => 1
Note: It will not change the accessibility of a class method that you are currently overwriting.
You could define the methods in an anonymous module by passing the block to Module.new, make each instance method in the module private and extend your class with the module:
class Class
def define_private_class_methods(&block)
mod = Module.new(&block)
mod.instance_methods.each { |m| mod.send(:private, m) }
extend(mod)
end
end
This has the desired result:
class Person
define_private_class_methods do
def method_one
123
end
end
end
Person.send(:method_one)
#=> 123
Person.method_one
#=> private method `method_one' called for Person:Class (NoMethodError)
... and as a bonus, it also gives you a super method: (probably of little use)
class Person
def self.method_one
super * 2
end
end
Person.method_one
#=> 456
Of course, you don't have to use extend, you could just as well define the methods manually:
class Class
def define_private_class_methods(&block)
mod = Module.new(&block)
mod.instance_methods.each do |m|
define_singleton_method(m, mod.instance_method(m))
private_class_method(m)
end
end
end
The essential component is the anonymous module, so you have a (temporary) container to define the methods in.
I have a bunch of classes with similiar logic like this
class ApiWrapper
class << self
attr_accessor :app_id, :app_key
def configure
yield self
end
end
end
I want to extract this logic to a module similar to Ruby Struct class to be able to do something like this
class ApiWrapper
include Configurable.instance :app_id, :app_key
end
How can I do this?
From documentation
fred = Module.new do
def meth1
"hello"
end
def meth2
"bye"
end
end
I know I can execute the following to add methods to the String class
class String
def do_something
puts self.size
end
end
var = "test"
var.do_something
and this will return 4
I want to be able to have a module with a function that takes in a String, but be able to call the do_something method on this string (see below for example) - is it possible?
EDIT: Added sample code that is not working
module TestModule
class String
def do_something
puts self.size
end
end
def self.test(str)
str.do_something
end
end
This gives the error: undefined method 'do_something' for "hello":String (NoMethodError)
The way your code is written, you're defining a new class called TestModule::String. If you want to modify the built-in Ruby String class, you need to use the fully-qualified name of String (with the ""::") if you want to keep the declaration inside the module.
module TestModule
class ::String
def do_something
puts self.size
end
end
def self.test(str)
str.do_something
end
end
Adding the "::" tells Ruby that the String class that you want is not part of the TestModule.
It's probably cleaner to just declare String outside of TestModule in the same file.
If you don't want to pollute the global String class, you could just modify the specific String instance that you want to add the method to.
module TestModule
def self.test(str)
do_somethingify!(str)
str.do_something
end
def self.do_somethingify!(str)
unless str.respond_to? :do_something
str.instance_eval do
def do_something
puts size
end
end
end
end
end
Maybe this?
module TestModule
module_function
def test(str)
str.instance_eval{doSomething}
end
end
Test.test(str)
Edit Changed due to the change in the question
Just put the definition of doSomething outside out the TestModule class.
class String
def doSomething
puts size
end
end
module TestModule
module_function
def test(str)
str.doSomething
end
end
Have a look at the code below
initshared.rb
module InitShared
def init_shared
#shared_obj = "foobar"
end
end
myclass.rb
class MyClass
def initialize()
end
def init
file_name = Dir.pwd+"/initshared.rb"
if File.file?(file_name)
require file_name
include InitShared
if self.respond_to?'init_shared'
init_shared
puts #shared_obj
end
end
end
end
The include InitShared dosn't work since its inside the method .
I want to check for the file and then include the module and then access the variables in that module.
Instead of using Samnang's
singleton_class.send(:include, InitShared)
you can also use
extend InitShared
It does the same, but is version independent. It will include the module only into the objects own singleton class.
module InitShared
def init_shared
#shared_obj = "foobar"
end
end
class MyClass
def init
if true
self.class.send(:include, InitShared)
if self.respond_to?'init_shared'
init_shared
puts #shared_obj
end
end
end
end
MyClass.new.init
:include is a private class method, so you can't call it in instance level method. Another solution if you want to include that module only for specific instance you can replace the line with :include with this line:
# Ruby 1.9.2
self.singleton_class.send(:include, InitShared)
# Ruby 1.8.x
singleton_class = class << self; self; end
singleton_class.send(:include, InitShared)
I'm trying to figure out how to implement an event in a ruby class. Specifically, I am trying to make my class implement an interface (INotifyPropertyChanged) that includes an event (PropertyChanged). I can create my add_PropertyChanged and remove_PropertyChanged methods... but then what?
This is what my class looks like so far:
class TestClass
include System::ComponentModel::INotifyPropertyChanged
def add_PropertyChanged(handler)
end
def remove_PropertyChanged(handler)
end
end
OK, I figured it out. Here is how you do it:
class TestClass
include System::ComponentModel::INotifyPropertyChanged
def initialize
#change_handlers = []
end
def add_PropertyChanged(handler)
#change_handlers << handler
end
def remove_PropertyChanged(handler)
#change_handlers.delete(handler)
end
def NotifyPropertyChanged(name)
#change_handlers.each { |h| h.invoke(self, System::ComponentModel::PropertyChangedEventArgs.new(name)) }
end
end