Prevent methods lookup in ancestor chain(method_missing) - ruby-on-rails

i have a code as below
def address
p "address"
end
class Person
def method_missing(sym, *args)
"#{sym} not defined on #{self}"
end
def name
"My name is Person"
end
end
p = Person.new
p.name # => My name is Person
p.address # => expected output is 'address not defined on # <Person:0x007fb2bb022fe0>' but actual output is 'address'
I want to leverage method_missing. But lexical scoping comes to play here. So method_missing becomes obsolete. any workaround?

As #AlekseiMatiushkin says the code works fine in a file. If you really need it to work the way you want in irb, you could define address as a class method
def self.address
p "address"
end
then missing_method will not find it as an instance method of Object, but you can still call address as if it were a global function.

Related

Is is possible to return virtual attributes automatically in Rails models?

Given:
class Foo
has_one :bar
def bar_name
bar.name
end
end
class Bar
belongs_to :foo
end
In the console or in a view, I can #foo.bar_name to get 'baz'.
I'm aware that I can #foo.as_json(methods: :bar_name) to get {"id"=>"abc123", "bar_name"=>"baz"}.
I could also denormalize the attribute and make it non-virtual, but I would rather not do that in this case.
Is it possible to automatically return the model with the virtual attribute included?
#<Foo id: "abc123", bar_name: "baz">
I want to do this because I am constructing a large object with nested collections of models, and the as_json call is abstracted away from me.
Not 100% sure I understand if your concern is related to as_json but if so this will work
class Foo
has_one :bar
def bar_name
bar.name
end
def as_json(options={})
super(options.merge!(methods: :bar_name))
end
end
Now a call to #foo.as_json will by default include the bar_name like your explicit example does.
Ugly would not recommend but you could change the inspection of foo e.g. #<Foo id: "abc123", bar_name: "baz"> as follows
class Foo
def inspect
base_string = "#<#{self.class.name}:#{self.object_id} "
fields = self.attributes.map {|k,v| "#{k}: #{v.inspect}"}
fields << "bar_name: #{self.bar_name.inspect}"
base_string << fields.join(", ") << ">"
end
end
Then the "inspection notation" would show that information although I am still unclear if this is your intention and if so why you would want this.
You could use attr_accessor - per the Rails docs:
Defines a named attribute for this module, where the name is symbol.id2name, creating an instance variable (#name) and a corresponding access method to read it. Also creates a method called name= to set the attribute.

Dynamically defining instance method within an instance method

I have a several classes, each of which define various statistics.
class MonthlyStat
attr_accessor :cost, :size_in_meters
end
class DailyStat
attr_accessor :cost, :weight
end
I want to create a decorator/presenter for a collection of these objects, that lets me easily access aggregate information about each collection, for example:
class YearDecorator
attr_accessor :objs
def self.[]= *objs
new objs
end
def initialize objs
self.objs = objs
define_helpers
end
def define_helpers
if o=objs.first # assume all objects are the same
o.instance_methods.each do |method_name|
# sums :cost, :size_in_meters, :weight etc
define_method "yearly_#{method_name}_sum" do
objs.inject(0){|o,sum| sum += o.send(method_name)}
end
end
end
end
end
YearDecorator[mstat1, mstat2].yearly_cost_sum
Unfortunately define method isn't available from within an instance method.
Replacing this with:
class << self
define_method "yearly_#{method_name}_sum" do
objs.inject(0){|o,sum| sum += o.send(method_name)}
end
end
...also fails because the variables method_name and objs which are defined in the instance are no longer available. Is there an idomatic was to accomplish this in ruby?
(EDITED: I get what you're trying to do now.)
Well, I tried the same approaches that you probably did, but ended up having to use eval
class Foo
METHOD_NAMES = [:foo]
def def_foo
METHOD_NAMES.each { |method_name|
eval <<-EOF
def self.#{method_name}
\"#{method_name}\".capitalize
end
EOF
}
end
end
foo=Foo.new
foo.def_foo
p foo.foo # => "Foo"
f2 = Foo.new
p f2.foo # => "undefined method 'foo'..."
I myself will admit it's not the most elegant solution (may not even be the most idiomatic) but I've run into similar situations in the past where the most blunt approach that worked was eval.
I'm curious what you're getting for o.instance_methods. This is a class-level method and isn't generally available on instances of objects, which from what I can tell, is what you're dealing with here.
Anyway, you probably are looking for method_missing, which will define the method dynamically the first time you call it, and will let you send :define_method to the object's class. You don't need to redefine the same instance methods every time you instantiate a new object, so method_missing will allow you to alter the class at runtime only if the called method hasn't already been defined.
Since you're expecting the name of a method from your other classes surrounded by some pattern (i.e., yearly_base_sum would correspond to a base method), I'd recommend writing a method that returns a matching pattern if it finds one. Note: this would NOT involve making a list of methods on the other class - you should still rely on the built-in NoMethodError for cases when one of your objects doesn't know how to respond to message you send it. This keeps your API a bit more flexible, and would be useful in cases where your stats classes might also be modified at runtime.
def method_missing(name, *args, &block)
method_name = matching_method_name(name)
if method_name
self.class.send :define_method, name do |*args|
objs.inject(0) {|obj, sum| sum + obj.send(method_name)}
end
send name, *args, &block
else
super(name, *args, &block)
end
end
def matching_method_name(name)
# ... this part's up to you
end

Simulating abstract classes in Ruby (Rails)

I want to simulate an abstract class in Ruby on Rails. I.e. I want to raise an exception if someone tries to call Abstract.new, but he should be able to call Child.new (while Child < Abstract).
How to do this? Overwriting both new and initialize does not work.
In another comment, the OP mentions that the purpose of the abstract class is to share behavior (methods) needed by its children. In Ruby, that's often best done with a module used to "mix in" methods where needed. For example, instead of:
class Abstract
def foo
puts "foo!"
end
end
class Concrete
end
Concrete.new.foo # => "foo!"
this:
module Foo
def foo
puts "foo!"
end
end
class Concrete
include Foo
end
Concrete.new.foo # => "foo!"
But here's how the original request might be satisfied:
#!/usr/bin/ruby1.8
class Abstract
def initialize(*args)
raise if self.class == Abstract
super
end
end
class Concrete < Abstract
end
Concrete.new # OK
Abstract.new # Raises an exception
Why would you want to do this? The point of abstract/interfaced classes are to hack Strongly typed languages into a dynamic paradigm. If you need your class to fit in the signature, name your methods according to the original class or make a facade and plug it in, no need to trick a compiler into allowing it, it just works.
def my_printer obj
p obj.name
end
So I defined the interface as any object with a name property
class person
attr_accessor :name
def initialize
#name = "Person"
end
end
class Employee
attr_accessor :name
def initialize
#name = "Employee"
#wage = 23
end
end
so nothing stops us from calling our printer method with either of these
my_printer Person.new
my_printer Employee.new
both print there names without a hitch :D
You almost always need to do this to enforce an API, when some third party is going to implement some stub, and you're sure they're going to mess it up. You can use specific prefix-templates in your parent class and a module that introspects on creation to achieve this:
module Abstract
def check
local = self.methods - Object.methods
templates = []
methods = []
local.each do |l|
if l =~ /abstract_(.*)/ # <--- Notice we look for abstract_* methods to bind to.
templates.push $1
end
methods.push l.to_s
end
if !((templates & methods) == templates)
raise "Class #{self.class.name} does not implement the required interface #{templates}"
end
end
end
class AbstractParent
include Abstract
def initialize
check
end
def abstract_call # <--- One abstract method here
end
def normal_call
end
end
class Child < AbstractParent # <-- Bad child, no implementation
end
class GoodChild < AbstractParent
def call # <-- Good child, has an implementation
end
end
Test:
begin
AbstractParent.new
puts "Created AbstractParent"
rescue Exception => e
puts "Unable to create AbstractParent"
puts e.message
end
puts
begin
Child.new
puts "Created Child"
rescue Exception => e
puts "Unable to create Child"
puts e.message
end
puts
begin
GoodChild.new
puts "Created GoodChild"
rescue Exception => e
puts "Unable to create GoodChild"
puts e.message
end
Result:
[~] ruby junk.rb
Unable to create AbstractParent
Class AbstractParent does not implement the required interface ["call"]
Unable to create Child
Class Child does not implement the required interface ["call"]
Created GoodChild
If you want this for doing STI, you could follow the suggestions in this thread:
class Periodical < ActiveRecord::Base
private_class_method :new, :allocate
validates_presence_of :type
end
class Book < Periodical
public_class_method :new, :allocate
end
class Magazine < Periodical
public_class_method :new, :allocate
end
Caveat: I'm not sure if this is a working solution. This hides new and allocate in the base class and re-enables them in child classes -- but that alone does not seem to prevent objects being created with create!. Adding the validation on type prevents the base class from being created. I guess you could also hide create!, but I'm not sure if that covers all the ways Rails can instantiate a model object.

Creating new Ruby string helper without argument params

I really had a hard time figuring out how to word this question, but in essence, I want to do this:
model = MyModel.new
model.title = "foo bar"
model.title.to_id #=> "foo_bar"
I have an ActiveRecord class for MyModel
class MyModel < ActiveRecord::Base
def to_id(str)
str.downcase.gsub(" ", "_")
end
end
but, of course, it's looking for the to_id method on String, and I don't want to override string, because I don't require this behaviour on every string. Just strings associated with MyModel. I could keep it simple and do something like:
model.to_id(model.title)
But that's not very Ruby.
I know I've seen examples of this sort of method implemented before, I just can't track them down.
Halp anyone?
you can extend a specific object instance with a method, using modules.
module ToId
def to_id
self.downcase.gsub " ", "_"
end
end
class MyClass
def title=(value)
value.extend ToId
#title = value
end
def title
#title
end
end
m = MyClass.new
m.title = "foo bar"
puts m.title #=> foo bar
puts m.title.to_id #=> foo_bar
since the value passed into the .title= method is a string, when we extend the string string with the ToId module, "self" in the module's methods is a string. therefore, we have direct access to the string that was passed into the .title= method, and we can manipulate it directly.
this is all done without having to modify the String class directly. we are only extending the specific instance that represents the title.
I believe that true Ruby solution is based on meta-programming. I'd strongly recommend you this book http://pragprog.com/titles/ppmetr/metaprogramming-ruby ($20) if you are interested.
By the way - the solution proposed above probably will not work as overriding column accessors is not that simple.
So I would recommend to create a class method that you use in your model definition like this:
class MyModel < ActiveRecord::Base
# adds to_id to the following attributes
ideize :name, :title
end
Well, that was an easy part, now comes the tougher one - the module itself:
#
# extends the ActiveRecord with the class method ideize that
# adds to_id method to selected attributes
#
module Ideizer
module ClassMethods
def ideize(*args)
# generates accessors
args.each do |name|
define_method("#{name}") do
# read the original value
value = read_attribute(name)
# if value does not contain to_id method then add it
unless value.respond_to?(:to_id)
# use eigen class for the value
class << value
def to_id
self.downcase.gsub " ", "_"
end
end
end
# return the original value
value
end
end
end
end
def self.included(base)
base.extend(ClassMethods)
end
end
# extend the active record to include ideize method
ActiveRecord::Base.send(:include, Ideizer)
I have to admit that I did not write the solution above just from my memory so I've prepared some tests that I'm sharing here:
require 'spec_helper'
describe MyModel do
before :each do
#mod = MyModel.new(:name => "Foo Bar",
:title => "Bar Bar",
:untouched => "Dont touch me")
end
it "should have to_id on name" do
#mod.name.respond_to?(:to_id).should be_true
#mod.name.to_id.should eql "foo_bar"
end
it "should have to_id on title" do
#mod.title.respond_to?(:to_id).should be_true
#mod.title.to_id.should eql "bar_bar"
end
it "should NOT have to_id on untouched" do
#mod.untouched.respond_to?(:to_id).should be_false
end
it "should work with real model" do
#mod.save!
#mod.name.to_id.should eql "foo_bar"
# reload from the database
#mod.reload
#mod.name.to_id.should eql "foo_bar"
end
end
Ruby rules!
You should take a look at the functions within ActiveSupport::CoreExtensions::String::Inflections, specifically in your case I would use the underscore method that exist in the (expanded) String class.
Then to override the accessor you could do something like:
def title_id
read_attribute(:title).underscore
end
I think that's what you want.

Why do migrations need the table block param?

Why does the ruby on rails migration syntax look like this:
create_table :my_table do |t|
t.integer :col
t.integer :col2
t.integer :col3
end
And not:
create_table :my_table do
integer :col
integer :col2
integer :col3
end
Personally I find the second snippet much more readable, are there any reasons why the implementation uses the first?
The fundamental implementation of the two approaches is different.
In the first (and actual) case, create_table calls yield with a TableDefinition object. So t in your example block points to that TableDefinition. The alternative method is to use instance_eval. This would look something like:
def create_table(name, &block)
table_definition = TableDefinition.new
# Other setup
table_definition.instance_eval(&block)
# More work
end
Which way you do it is partially a matter of preference. However, some people are not fans of eval so they like to avoid this. Also, using the yield method makes it clearer what object you're working with.
My understanding is that ruby is lexically scoped, meaning that "integer" has to refer to something defined at the point it occurs in the code. You'd need dynamic scoping to accomplish what you're asking for.
It may be that I'm wrong and at least one of procs, blocks and lambdas is dynamically scoped, but then you still have your answer -- obscure details of how scope behaves is not a good thing to expect a programmer to know.
Basically the interface designer should have chosen to do so due to this little trick regarding scopes and how eval and instance_eval work, check this example:
Having 2 classes Foo and Boo with the following definitions:
class Foo
def speak(phrase)
puts phrase
end
def self.generate(&block)
f = Foo.new
f.instance_eval(&block)
end
end
class Boo
attr_reader :name
def initialize(name) ; #name = name ; end
def express
Foo.generate { speak name}
end
end
Generally this should work fine for most cases, but some situations like the following statement will issue an error:
Boo.new("someone").express #`express': undefined local variable or method `name' for #<Foo:0xb7f582fc> (NameError)
We don't have access here to the instance methods of Boo inside Foo instances that's because we are using instance_eval, so the method name which is defined for Boo instances is not in the scope for Foo instances.
To overcome such problems it’s better to redefine generate as follows:
class Foo
def speak(phrase)
puts phrase
end
def self.generate(&block)
f = Foo.new
block.arity < 1 ? f.instance_eval(&block) : block.call(f)
end
end
This is a flexible interface where you evaluate the code block depending on the passed block params. Now we have to pass the current foo object as a param when we need to call instance methods on it, let's redefine Boo, check express and talk:
class Boo
attr_reader :name
def initialize(name) ; #name = name ; end
def express
Foo.generate { |f| f.speak name}
end
def talk(anything)
Foo.generate { speak anything}
end
end
Boo.new("someone").express #=> someone
Boo.new("someone").talk("whatever") #=> whatever

Resources