Given the following module,
module Foo
def bar
:baz
end
end
def send_to_foo(method)
# ...?
end
send_to_foo(:bar) # => :baz
What code should go in send_to_foo to make the last line work as expected? (send_to_foo is obviously not how I would implement this; it just makes clearer what I'm looking for.)
I expected Foo.send(:bar) to work at first, but it makes sense that it doesn't. It would if the method were defined as def self.bar, but that's no fun.
well, the easy way would be
Foo.extend Foo # let Foo use the methods it contains as instance methods
def send_to_foo(method)
Foo.send(method)
end
So now
irb> send_to_foo(:bar)
#=> :baz
Related
Is there a simple way to extend a functionality of a standard library class like String inside a module without affecting anything outside of it? Example (won't work):
module Foo
class String
def hello
"hello #{self}!"
end
end
class Bar
def greet name
name.hello
end
end
end
Result I'm looking for:
Foo::Bar.new.greet 'Tom' #=> hello Tom!
'Tom'.hello #=> NoMethodError
I'm aware of solutions like creating MyString < String with desired functionality, but I would rather not call MyString.new('foo') every time I want to use a string inside the module.
I realise this may not be considered good practice, I'm just looking to expand my understanding of the language.
What you're looking for is Refinement:
Refinements are designed to reduce the impact of monkey patching on
other users of the monkey-patched class. Refinements provide a way to
extend a class locally.
module Foo
refine String do
def hello
"hello #{self}!"
end
end
end
puts 'Tom'.hello
#=> undefined method `hello' for "Tom":String
using Foo
puts 'Tom'.hello
#=> hello Tom!
Using refinement inside Bar class:
# Without refinement
class Bar
def greet(name)
name.hello
end
end
puts Bar.new.greet('Tom')
#=> undefined method `hello' for "Tom":String
# With refinement
class Bar
using Foo
def greet(name)
name.hello
end
end
puts Bar.new.greet('Tom')
#=> hello Tom!
For the complete info about scoping, method lookup and stuff look into docs link I've provided :)
P.S. Be aware, that Rubinius developers are philosophically opposed to Refinements and thus will never implement them (c) Jörg W Mittag.
Write a class of the same name then use require_relative (or similar method) to include it.
class Date
def itis
puts "It is " + self.to_s
end
Example:
[1] pry(main)> require_relative 'date.rb'
=> true
[2] pry(main)> require 'date'
=> true
[3] pry(main)> Date.new(2018, 10, 9).itis
It is 2018-10-09
=> nil
I'm doing some metaprogramming in Ruby, and I need to dynamically generate a sibling class inside of a module. In doing so, I want to call const_set on the module, but I don't know which Module constant to call that on until runtime. An example:
Given classes
Foo::Bar::Baz
Foo::Quox::Quack
I want to be able to call a function like this (oversimplified here):
def generate_from klass
mod = klass.enclosing_module # <- THIS LINE is the one I need to figure out
mod.const_set("GeneratedClassName", Class.new)
end
and what I want to end up with, when calling with Baz, is a new class defined as
Foo::Bar::GeneratedClassName
and with a Quack, I want
Foo::Quox::GeneratedClassName
The only way I know of is to split up klass.name, then repeatedly call const_get on those strings, constantized. Does anyone know of a more elegant way?
This should get you on track:
module Foo
module Bar
class Baz
def initialize
#nesting = Module.nesting
end
def enclosing_module
#nesting.last
end
end
end
end
puts Foo::Bar::Baz.new.enclosing_module #=> Foo
Relevant documentation:
http://ruby-doc.org/core/classes/Module.html#M000441
Got it.
ActiveSupport has this Ruby extension, Module#parent. It's good enough for my use.
In Rails you can use a combination of deconstantize and constantize.
'Foo::Bar::Baz'.deconstantize.constantize # => Foo::Bar
so in a method of the class it can be used like this:
self.class.name.deconstantize.constantize
In case anyone is looking for a pure ruby version:
def get_parent_type
#Note: This will break for base types (lacking '::' in the name)
parent_type=self.class.name.split('::')[0...-1]
begin
Object.const_get(parent_type.join('::'))
rescue NameError => e
nil
end
end
I have a bunch of methods like this in view helper
def background
"#e9eaec"
end
def footer_link_color
"#836448"
end
I'd like these methods exposed to the view, but I'd prefer the helper to be a bit more concise. What's the best way to turn a hash, say, into methods (or something else)?
module MyHelper
{:background => "#e9eaec", :footer_link_color => "#836448"}.each do |k,v|
define_method(k) {v}
end
end
Though I don't think trading this bit of conciseness for the readability of your first approach is necessarily a good idea.
If you want to generalize this, you can add the following method to the Module class:
class Module
def methods_from_hash(hash)
hash.each do |k,v|
define_method(k) {v}
end
end
end
And then in your helper call methods_from_hash(:background => ...).
If you create constants in a namespace, then you can easily whip up accessors for those constants:
class Foo
module Values
FOO = 1
BAR = 'bar'
BAZ = :baz
end
include Values
Values.constants.each do |name|
define_method(name.downcase) do
Values.const_get(name)
end
end
end
foo = Foo.new
p foo.foo # => 1
p foo.bar # => "bar"
p foo.baz # => :baz
The include Values mixes the constants into Foo for the convenience of Foo's own methods. It is not needed for this pattern to work.
Actually, ruby has something called OpenStruct, which is quite awesome and really useful for when you want hash but do not want to use it like one.
require 'ostruct'
colors = OpenStruct.new({:background => "0x00FF00", :color => "0xFF00FF"})
p colors.background #=> "0x00FF00"
Here is my remix of sepp2k's answer. It's a bit more OO and works even in irb. Not sure whether to patch Object or Hash.
class Hash
def keys_to_methods()
each do |k,v|
self.class.send(:define_method, k, Proc.new {v});
end
length
end
end
Test code
hash = {:color_one=>"black", :color_two=>"green"}
hash.keys_to_methods
has.color_one # returns black
OpenStruct: thanks to sepp2k again! I didn't know this existed.
Here is yet another version using method_missing
class Hash
def method_missing(method_id)
key = method_id.id2name
if has_key?(key)
return self[key]
elsif has_key?(key.to_sym)
return self[key.to_sym]
else
super.method_missing(method_id)
end
end
end
hash = {:color_one=>"black", :color_two=>"green"}
hash.color_one
I'm sure I could get the code tighter (if I knew how).
Is there a simple way to list the accessors/readers that have been set in a Ruby Class?
class Test
attr_reader :one, :two
def initialize
# Do something
end
def three
end
end
Test.new
=> [one,two]
What I'm really trying to do is to allow initialize to accept a Hash with any number of attributes in, but only commit the ones that have readers already defined. Something like:
def initialize(opts)
opts.delete_if{|opt,val| not the_list_of_readers.include?(opt)}.each do |opt,val|
eval("##{opt} = \"#{val}\"")
end
end
Any other suggestions?
This is what I use (I call this idiom hash-init).
def initialize(object_attribute_hash = {})
object_attribute_hash.map { |(k, v)| send("#{k}=", v) }
end
If you are on Ruby 1.9 you can do it even cleaner (send allows private methods):
def initialize(object_attribute_hash = {})
object_attribute_hash.map { |(k, v)| public_send("#{k}=", v) }
end
This will raise a NoMethodError if you try to assign to foo and method "foo=" does not exist. If you want to do it clean (assign attrs for which writers exist) you should do a check
def initialize(object_attribute_hash = {})
object_attribute_hash.map do |(k, v)|
writer_m = "#{k}="
send(writer_m, v) if respond_to?(writer_m) }
end
end
however this might lead to situations where you feed your object wrong keys (say from a form) and instead of failing loudly it will just swallow them - painful debugging ahead. So in my book a NoMethodError is a better option (it signifies a contract violation).
If you just want a list of all writers (there is no way to do that for readers) you do
some_object.methods.grep(/\w=$/)
which is "get an array of method names and grep it for entries which end with a single equals sign after a word character".
If you do
eval("##{opt} = \"#{val}\"")
and val comes from a web form - congratulations, you just equipped your app with a wide-open exploit.
You could override attr_reader, attr_writer and attr_accessor to provide some kind of tracking mechanism for your class so you can have better reflection capability such as this.
For example:
class Class
alias_method :attr_reader_without_tracking, :attr_reader
def attr_reader(*names)
attr_readers.concat(names)
attr_reader_without_tracking(*names)
end
def attr_readers
#attr_readers ||= [ ]
end
alias_method :attr_writer_without_tracking, :attr_writer
def attr_writer(*names)
attr_writers.concat(names)
attr_writer_without_tracking(*names)
end
def attr_writers
#attr_writers ||= [ ]
end
alias_method :attr_accessor_without_tracking, :attr_accessor
def attr_accessor(*names)
attr_readers.concat(names)
attr_writers.concat(names)
attr_accessor_without_tracking(*names)
end
end
These can be demonstrated fairly simply:
class Foo
attr_reader :foo, :bar
attr_writer :baz
attr_accessor :foobar
end
puts "Readers: " + Foo.attr_readers.join(', ')
# => Readers: foo, bar, foobar
puts "Writers: " + Foo.attr_writers.join(', ')
# => Writers: baz, foobar
Try something like this:
class Test
attr_accessor :foo, :bar
def initialize(opts = {})
opts.each do |opt, val|
send("#{opt}=", val) if respond_to? "#{opt}="
end
end
end
test = Test.new(:foo => "a", :bar => "b", :baz => "c")
p test.foo # => nil
p test.bar # => nil
p test.baz # => undefined method `baz' for #<Test:0x1001729f0 #bar="b", #foo="a"> (NoMethodError)
This is basically what Rails does when you pass in a params hash to new. It will ignore all parameters it doesn't know about, and it will allow you to set things that aren't necessarily defined by attr_accessor, but still have an appropriate setter.
The only downside is that this really requires that you have a setter defined (versus just the accessor) which may not be what you're looking for.
Accessors are just ordinary methods that happen to access some piece of data. Here's code that will do roughly what you want. It checks if there's a method named for the hash key and sets an accompanying instance variable if so:
def initialize(opts)
opts.each do |opt,val|
instance_variable_set("##{opt}", val.to_s) if respond_to? opt
end
end
Note that this will get tripped up if a key has the same name as a method but that method isn't a simple instance variable access (e.g., {:object_id => 42}). But not all accessors will necessarily be defined by attr_accessor either, so there's not really a better way to tell. I also changed it to use instance_variable_set, which is so much more efficient and secure it's ridiculous.
There's no built-in way to get such a list. The attr_* functions essentially just add methods, create an instance variable, and nothing else. You could write wrappers for them to do what you want, but that might be overkill. Depending on your particular circumstances, you might be able to make use of Object#instance_variable_defined? and Module#public_method_defined?.
Also, avoid using eval when possible:
def initialize(opts)
opts.delete_if{|opt,val| not the_list_of_readers.include?(opt)}.each do |opt,val|
instance_variable_set "##{opt}", val
end
end
You can look to see what methods are defined (with Object#methods), and from those identify the setters (the last character of those is =), but there's no 100% sure way to know that those methods weren't implemented in a non-obvious way that involves different instance variables.
Nevertheless Foo.new.methods.grep(/=$/) will give you a printable list of property setters. Or, since you have a hash already, you can try:
def initialize(opts)
opts.each do |opt,val|
instance_variable_set("##{opt}", val.to_s) if respond_to? "#{opt}="
end
end
At the top level, method definition should result in private methods on Object, and tests seem to bear this out:
def hello; "hello world"; end
Object.private_instance_methods.include?(:hello) #=> true
Object.new.send(:hello) #=> "hello world"
However, the following also works at top level (self.meta is the eigenclass of main):
self.meta.private_instance_methods(false).include?(:hello) #=> true
It appears that the hello method is simultaneously defined on the eigenclass of main as well as on Object. What's going on? Note that the false parameter to private_instance_methods excludes super class methods from the method list.
First of all, this behavior and the underlying reasoning have always existed; it's nothing new to 1.9. The technical reason it happens is because main is special and treated differently than any other object. There's no fancy explanation available: it behaves that way because it was designed that way.
Okay, but why? What's the reasoning for main to be magical? Because Ruby's designer Yukihiro Matsumoto thinks it makes the language better to have this behavior:
Is so, why are top-level methods not made singleton-methods on this object,
instead of being pulled in as instance methods on the Object class
itself
(and hence into all other classes i.e. more namespace pollution than is
usually intended). This would still allow top-level methods to call other
top-level methods. And if the top-level object were referred to by
some
constant like Main, then those methods could be called from anywhere
with
Main.method(...).
Do you really wish to type
"Main.print" everywhere?
Further on in the discussion, he explains that it behaves this way because he feels the "assumption is natural."
EDIT:
In response to your comment, your question is aimed at why main's eigenclass seems to report hello as a private instance method. The catch is that none of the top-level functions are actually added to main, but directly to Object. When working with eigenclasses, the instance_methods family of functions always behave as if the eigenclass is still the original class. That is, methods defined in the class are treated as being defined directly in the eigenclass. For example:
class Object
private
def foo
"foo"
end
end
self.send :foo # => "foo"
Object.private_instance_methods(false).include? :foo # => true
self.meta.private_instance_methods(false).include? :foo # => true
class Bar
private
def bar
"bar"
end
end
bar = Bar.new
bar.send :bar # => "bar"
Bar.private_instance_methods(false).include? :bar # => true
bar.meta.private_instance_methods(false).include? :bar # => true
We can add a method directly to main's eigenclass, though. Compare your original example with this:
def self.hello; "hello world"; end
Object.instance_methods.include? :hello # => false
self.meta.instance_methods.include? :hello # => true
Okay, but what if we really want to know that a given function is defined on the eigenclass, not the original class?
def foo; "foo"; end #Remember, this defines it in Object, not on main
def self.bar; "bar"; end #This is defined on main, not Object
foo # => "foo"
bar # => "bar"
self.singleton_methods.include? :foo # => false
self.singleton_methods.include? :bar # => true