Chain methods on String without core extensions - ruby

I am looking for a way (or perhaps a best practice) to chain various methods on a string object without actually opening up the String class. This is because the transformations I want to apply to the String object are fairly project specific, and I don't see a reason to pollute the global space with it.
Is there a way to achieve this? Maybe a wrapper of some sort? I experimented with gsub! on an instance variable but gsub!, unless the gsub, throws nil on failure to match, so it halts the chaining I want to achieve.
Basically, I need to be able to do:
"my_string".transformation1.transformation2.transformation3
and get to keep all those transformations namespaced to my application. Any ideas?

If you are using Ruby >= 2.0 you can use refinements:
module MyRefinements
refine String do
def transformation1
# ...
self
end
def transformation2
# ...
self
end
def transformation3
# ...
self
end
end
end
# Either in global scope (active until the end-of-file)
# or in lexical scope (active until the lexical scope end)
# More on this in the refinements docs scope section
# global scope
using MyRefinements
"test".transformation1.transformation2.transformation3 #=> "test"
# lexical scope
begin
using MyRefinements
"test".transformation1.transformation2.transformation3 #=> "test"
end

You can add a method to an instance, but you're going to need to jump through a hoop to chain the methods:
str = 'now is the time'
def str.foo(target, replacement)
self.gsub(target, replacement)
end
def str.bar
self.reverse
end
str.singleton_methods # => [:foo, :bar]
str.foo('ow', 'ever') # => "never is the time"
str.bar # => "emit eht si won"
This won't work:
str.foo('ow', 'ever').bar
# ~> NoMethodError
# ~> undefined method `bar' for "never is the time":String
You can use "bang" type methods, that mutate the original object:
str = 'now is the time'
def str.foo(target, replacement)
self.gsub!(target, replacement)
self
end
def str.bar
self.reverse!
self
end
str.singleton_methods # => [:foo, :bar]
str.foo('ow', 'ever').bar # => "emit eht si reven"
Be careful though. This won't work on every type of object, only ones that allow mutating.
And, the fact that the added methods are only available to that particular variable is really limiting; It's messier reusing that same functionality. You can clone the variable and assign that clone to a new variable, but replacing the value with something different becomes messy:
str2 = str.clone
str.object_id # => 70259449706680
str2.object_id # => 70259449706520
str.singleton_methods # => [:foo]
str2.singleton_methods # => [:foo]
str2 = 'all good men'
str2.foo('ll', '') # =>
# ~> NoMethodError
# ~> undefined method `foo' for "all good men":String
Personally, I'd do it through a subclass:
class MyString < String
def foo(s, t)
self.gsub(s, t)
end
end
str = MyString.new('now is the time')
str.foo('ow', 'ever') # => "never is the time"
str2 = 'all good men'
str2.foo('ll', '') # =>
# ~> NoMethodError
# ~> undefined method `foo' for "all good men":String
If you're on Ruby v2+, look at #mdesantis's answer using refinements. They were introduced to solve this sort of problem. If you're < v2.0, I'd go the sub-class route or accept having to modify String.

Related

Kernel#Array - How does Ruby interpret methods that have the same name as a constant?

When I'm coding in Ruby, to avoid type checks and respond_to?s and nil checks I often coerce objects into the type I expect. If I expect an Array, instead of foo.nil? ? handle_nil(foo) : handle_array(foo) I prefer to just handle_foo(foo.to_a) since nil.to_a # => [].
This doesn't work for everything, 1.to_a raises NoMethodError: undefined method 'to_a' for 1:Integer. 1.to_ary raises the same error. Array() seems to solve the problem, handling what seems like any object and either making a sensible conversion to an Array or wrapping it in [].
Array(nil) # => []
Array('') # => [""]
Array(Class) # => [Class]
Array(1) # => [1]
Array(Object.new) # => [#<Object:0x007fdbd40df4c8>]
Array({}) # => []
Array(foo: :bar) # => [[:foo, :bar]]
You can do the same thing with other classes too. String(), Float()... I'd used it a lot without thinking about the unusual syntax. I wanted to learn more about the implementation. Is it syntactic sugar for a special method, similar to #[]?
Both of these raise a SyntaxError
def self.(foo)
puts foo
end
def self.()(foo)
puts foo
end
You use Array() like you would use a method. Looking at the docs for Kernel it becomes clear that that's what's going on. The following works as you'd expect:
class Thing
attr_accessor :name
def initialize(name)
#name = name
end
end
module Kernel
def Thing(name)
Thing.new(name)
end
end
thing = Thing('name') # => #<Thing:0x007fefef89d8e8 #name="name">
thing.name # => "name"
You can also do this
HI = 'hi'.freeze
def HI(word)
word
end
HI # => "hi"
HI("ho") # => "ho"
My question is: How does Ruby interpret this? How does it distinguish between the method and the constant? Is this a silly question? Given "normal" behavior, one might expect Ruby to complain with a name warning when you define the method, or raise a SyntaxError. Does anyone know how Ruby handles this under the hood?
Thanks!
How does Ruby interpret this? How does it distinguish between the method and the constant?
The rules are pretty much the same as with local variables: Ruby will assume it is a constant unless it's obvious that it isn't: constants can't have arguments, and constants can't have receivers.
FOO # constant
self.FOO # message send
FOO() # message send
FOO :bar # message send
Given "normal" behavior, one might expect Ruby to complain with a name warning when you define the method, or raise a SyntaxError.
Why? Defining a method is perfectly legal syntactically (after all, that's what OO is all about), and Array is a perfectly legal Ruby identifier.

what does a ruby method ending with an "=" mean?

What does a ruby method ending with an "=" mean?
See the available methods in this print out:
2.2.0 :066 > obj.methods(false)
=> [:label=, :label, :description=, :description, :thumbnail=, :thumbnail, :attribution=, :attribution, :license=, :license, :logo=, :logo, :see_also=, :seeAlso=, :see_also, :seeAlso, :related=, :related, :within=, :within, :metadata=, :metadata, :sequences=, :sequences, :structures=, :structures, :viewing_hint=, :viewingHint=, :viewing_hint, :viewingHint, :viewing_direction=, :viewingDirection=, :viewing_direction, :viewingDirection, :service=, :service]
For example whats this difference between label= and label?
foo= is no different than any other method, except:
it requires precisely one argument and
Ruby permits you to add spaces before the = character.
class Foo
def foo=(other)
puts 'hi'
end
end
Foo.new.foo = 7
hi
class Goo
def goo=
puts 'hi'
end
end
Goo.new.goo=
Ruby says, "I'm waiting for an argument...". So we provide one:
4
and then she complains about what she asked you to do:
#=> ArgumentError: wrong number of arguments (1 for 0)
= methods are typically used to create a setter for an instance variable (if attr_acccessor or attr_writer is not used):
class Foo
def initialize(foo)
#foo=foo
end
# getter
def foo
#foo
end
# setter
def foo=(other)
#foo = other
end
end
f = Foo.new('dog')
f.foo
#=> "dog"
f.foo = 'cat'
#=> "cat"
f.foo
#=> "cat"
the methods ending with "=" are setting the instance variable
look at the answer here: why-use-rubys-attr-accessor-attr-reader-and-attr-writer
It is equivalent of setter methods in other languages, it is just convention so it looks more natural to say
obj.description="Fun Site"
vs
obj.setDescription("Fun Site")
There is nothing special about methods that end in =
You can see this by running the code below:
def bob=
puts "bob="
end
p send('bob='.to_sym)
What is special is the '=' infix operator. When you write self.bob = "bill". It is interpreted as self.send('bob='.to_sym, "bill").
Putting a ? at the end of a method is a hint that it returns a boolean (true/false). Methods that end in ! hint that the method affects the instance. See String#chomp vs String#chomp.
You can find out more about ruby operators at http://www.tutorialspoint.com/ruby/ruby_operators.htm and more about naming conventions at https://github.com/bbatsov/ruby-style-guide#naming

How are respond_to and respond_to_missing different?

I'm confused when to use each of this methods.
From respond_to? documentation:
Returns true if obj responds to the given method. Private methods
are included in the search only if the optional second parameter
evaluates to true.
If the method is not implemented, as Process.fork on Windows,
File.lchmod on GNU/Linux, etc., false is returned.
If the method is not defined, respond_to_missing? method is called and
the result is returned.
And respond_to_missing?:
Hook method to return whether the obj can respond to id method or
not.
See #respond_to?.
Both methods takes 2 arguments.
Both methods seems to the same thing(check if some object respond to given method) so why we should use(have) both?
Defining 'resond_to_missing?` gives you ability to take methods:
class A
def method_missing name, *args, &block
if name == :meth1
puts 'YES!'
else
raise NoMethodError
end
end
def respond_to_missing? name, flag = true
if name == :meth1
true
else
false
end
end
end
[65] pry(main)> A.new.method :meth1
# => #<Method: A#meth1>
Why respond_to? couldn't do this?
What I guess:
respond_to? checks if method is in:
Current object.
Parent object.
Included modules.
respond_to_missing? checks if method is:
Defined via method_missing:
Via array of possible methods:
def method_missing name, *args, &block
arr = [:a, :b, :c]
if arr.include? name
puts name
else
raise NoMethodError
end
end
Delegating it to different object:
class A
def initialize name
#str = String name
end
def method_missing name, *args, &block
#str.send name, *args, &block
end
end
2 . Other way that I'm not aware of.
Where should both be defined/used(my guessing too):
Starting from 1.9.3(as fair I remember) define only respond_to_missing? but use only respond_to?
Last questions:
Am I right? Did I missed something? Correct everything that is bad and/or answer questions asked in this question.
respond_to_missing? is supposed to be updated when you make available additional methods using the method missing technique. This will cause the Ruby interpreter to better understand the existence of the new method.
In fact, without using respond_to_missing?, you can't get the method using method.
Marc-André posted a great article about the respond_to_missing?.
In order for respond_to? to return true, one can specialize it, as follows:
class StereoPlayer
# def method_missing ...
# ...
# end
def respond_to?(method, *)
method.to_s =~ /play_(\w+)/ || super
end
end
p.respond_to? :play_some_Beethoven # => true
This is better, but it still doesn’t make play_some_Beethoven behave exactly like a method. Indeed:
p.method :play_some_Beethoven
# => NameError: undefined method `play_some_Beethoven'
# for class `StereoPlayer'
Ruby 1.9.2 introduces respond_to_missing? that provides for a clean solution to the problem. Instead of specializing respond_to? one specializes respond_to_missing?. Here’s a full example:
class StereoPlayer
# def method_missing ...
# ...
# end
def respond_to_missing?(method, *)
method =~ /play_(\w+)/ || super
end
end
p = StereoPlayer.new
p.play_some_Beethoven # => "Here's some_Beethoven"
p.respond_to? :play_some_Beethoven # => true
m = p.method(:play_some_Beethoven) # => #<Method: StereoPlayer#play_some_Beethoven>
# m acts like any other method:
m.call # => "Here's some_Beethoven"
m == p.method(:play_some_Beethoven) # => true
m.name # => :play_some_Beethoven
StereoPlayer.send :define_method, :ludwig, m
p.ludwig # => "Here's some_Beethoven"
See also Always Define respond_to_missing? When Overriding method_missing.

get instance variable name from itself in ruby

I have an instance variable #foo and I want to write some code so that I get string 'foo'
any hint?
If all you have is a reference to the object, you can't really do it cleanly.
def foo
bar #something
end
def bar(value)
value # no clean way to know this is #something
end
The only hack I can think of is to loop through ALL instance variables on self, looking for matches. But its a very messy approach that's likely to be slow.
def bar(value)
instance_variables.each do |ivar_name|
if instance_variable_get(ivar_name) == value
return ivar_name.to_s.sub(/^#/, '') # change '#something' to 'something'
end
end
# return nil if no match was found
nil
end
#something = 'abc123'
bar #something # returns 'something'
# But passing the same value, will return a value it's equal to as well
bar 'abc123' # returns 'something'
This works because instance_variables returns an array of symbols that are the names of instance variables.
instance_variables
#=> [:#something, :#whatever]
And instance_variable_get allows you to fetch the value by it's name.
instance_variable_get :#something # note the #
#=> 'abc123'
Combine the two methods and you can get close to what you want.
Just use it wisely. Before using a solution based on this, see if you can refactor things a way so that it's not necessary. Meta-programming is like a martial art. You should know how it works, but have the discipline to avoid using it whenever possible.
In Ruby, you can only manipulate objects. Variables (including instance variables) aren't objects.
Besides, what do you want your magic method to return in this case:
foo = Object.new
bar = foo
#baz = bar
#qux = bar
magic_method(foo) # what should the return value be and how would it know?
You can call the method instance_variables to get the name of all instance variables of an object. Caution though that instance variables are only included in that list after they have been initialized.
>> class A; attr_accessor :foo; end
=> nil
>> a = A.new
=> #<A:0x103b310b0>
>> a.instance_variables
=> []
>> a.foo = 42
=> 42
>> a.instance_variables
=> ["#foo"]
class Object
def get_name
line_number = caller[0].split(':')[1].to_i
line_exectued = File.readlines( __FILE__)[line_number-1]
line_exectued.match(/(\S+).get_name/)[1]
end
end
inconceivable = true
p inconceivable.get_name

Is there an elegant way to test if one instance method is an alias for another?

In a unit test I need to test whether alias methods defined by alias_method have been properly defined. I could simply use the same tests on the aliases used for their originals, but I'm wondering whether there's a more definitive or efficient solution. For instance, is there a way to 1) dereference a method alias and return its original's name, 2) get and compare some kind of underlying method identifier or address, or 3) get and compare method definitions? For example:
class MyClass
def foo
# do something
end
alias_method :bar, :foo
end
describe MyClass do
it "method bar should be an alias for method foo" do
m = MyClass.new
# ??? identity(m.bar).should == identity(m.foo) ???
end
end
Suggestions?
According to the documentation for Method,
Two method objects are equal if that
are bound to the same object and
contain the same body.
Calling Object#method and comparing the Method objects that it returns will verify that the methods are equivalent:
m.method(:bar) == m.method(:foo)
bk1e's method works most of the time, but I just happened to hit the case where it doesn't work:
class Stream
class << self
alias_method :open, :new
end
end
open = Stream.method(:open)
new = Stream.method(:new)
p open, new # => #<Method: Stream.new>, #<Method: Class#new>
p open.receiver, new.receiver # => Stream, Stream
p open == new # => false
The output is produced in Ruby 1.9, not sure if it's a bug or not since Ruby 1.8 produces true for the last line. So, if you are using 1.9, be careful if you are aliasing an inherited class method (like Class#new), These two methods are bound to the same object (the class object Stream), but they are considered not equivalent by Ruby 1.9.
My workaround is simple - alias the original method again and test the equality of the two aliases:
class << Stream; alias_method :alias_test_open, :new; end
open = Stream.method(:open)
alias_test_open = Stream.method(:alias_test_open)
p open, alias_test_open # => #<Method: Stream.new>, #<Method: Stream.new>
p open.receiver, alias_test_open.receiver # => Stream, Stream
p open == alias_test_open # => true
Hope this helps.
UPDATE:
See http://bugs.ruby-lang.org/issues/7613
So Method#== should return false in this case since a super call would invoke different methods; it is not a bug.
Calling MyClass.instance_method(:foo) will result UnboundMethod instance, which has eql? method.
So the answer is:
describe MyClass do
subject { described_class }
specify do
expect(subject.instance_method(:foo)).to be_eql(subject.instance_method(:bar))
end
end

Resources