I am using Ruby on Rails v3.2.2 and I would like to call methods "directly" on instance attributes and not on their receiver objects. That is, if I have an User instance #user for which
#user.name
# => "Leonardo da Vinci"
I would be able to implement methods that act "directly" on the name attribute so that I can write something like
# Note: 'is_correct?' and 'has_char?' are just sample methods.
#user.name.is_correct?
#user.surname.has_char?('V')
Is it possible? If so, how can I make that?
Note: I am trying to implement a plugin.
In order to do this, you would have to use a special type of class for each attribute, which IMHO, would be hugely overkill, assuming your reasons are purely with concern for visual style.
For example, since #user.name returns a String, you can only call methods on it that belong to the String class by default. If you want to call additional methods on it, you either want to use a subclass of String, or add some singleton methods to that particular instance of String. I think it would be confusing and inconsistent and would likely get in the way of real progress.
A better solution is just to ask something like:
#user.valid?(:name)
As for has_char?('V'), you can already do that with instances of String:
#user.surname.include?('V')
I just played a lil and got it working using singleton methods in a normal class and I'm not sure if it's possible to do the same on your case. I doubt it! But still here is what I got :)
class Bar
def initialize foo
#foo = foo
def #foo.bar
p "baaaaaaar"
end
end
def foo
#foo
end
def foo=(foo)
#foo = foo
def #foo.bar
p "baaaaaaar"
end
end
end
a = Bar.new "foo"
p a.foo
p a.foo.bar
a.foo = "bar"
p a.foo.bar
# >> "foo"
# >> "baaaaaaar"
# >> "baaaaaaar"
# >> "baaaaaaar"
# >> "baaaaaaar"
Related
In a Rails controller you can pass a symbol to the layout method that corresponds to a method in you controller that will return the layout name like this:
layout :my_method
def my_method
'layout_1'
end
I want to have a similar functionality to likewise pass a symbol to my classes method and that class should call the corresponding function and use its return value, like this
myClass.foo :my_method
def my_method
'layout_1'
end
I've read posts[1] that tell me I need to pass
myClass.foo(method(:my_method))
which I find ugly and inconvenient. How is rails here different allowing to pass just the symbol without any wrapper? Can this be achieved like Rails does it?
[1] How to implement a "callback" in Ruby?
If you want to only pass a :symbol into the method, then you have to make assumptions about which method named :symbol is the one you want called for you. Probably it's either defined in the class of the caller, or some outer scope. Using the binding_of_caller gem, we can snag that information easily and evaluate the code in that context.
This surely has security implications, but those issues are up to you! :)
require 'binding_of_caller'
class Test
def foo(sym)
binding.of_caller(1).eval("method(:#{sym})").call
end
end
class Other
def blork
t = Test.new
p t.foo(:bar)
p t.foo(:quxx)
end
def bar
'baz'
end
end
def quxx
'quxx'
end
o = Other.new
o.blork
> "baz"
> "quxx"
I still don't understand, what is author asking about. He's saying about "callbacks", but only wrote how he wants to pass parameter to some method. What that method(foo) should do - i have no idea.
So I tried to predict it's implementation. On class initialising it gets the name of method and create private method, that should be called somewhere under the hood. It possible not to create new method, but store method name in class variable and then call it somewhere.
module Foo
extend ActiveSupport::Concern
module ClassMethods
def foo(method_name)
define_method :_foo do
send method_name
end
end
end
end
class BaseClass
include Foo
end
class MyClass < BaseClass
foo :my_method
private
def my_method
"Hello world"
end
end
MyClass.new.send(:_foo)
#=> "Hello world"
And really, everything is much clearer when you're not just wondering how it works in rails, but viewing the source code: layout.rb
In Python, you can write a decorator for memoizing a function's response.
Is there something similar for Ruby on Rails? I have a model's method that makes a query, which I would like to cache.
I know I can do something inside the method, like:
def foo(param)
if self.cache[param].nil?
self.cache[param] = self.get_query_result(param)
else
self.cache[param]
end
end
However, given that I would do this often, I'd prefer a decorator syntax. It is clearer and better IMO.
Is there something like this for Ruby on Rails?
I usually do this using custom accessors, instance variables, and the ||= operator:
def foo
#foo ||= something_or_other
end
something_or_other could be a private method on the same class that returns the object that foo should be.
EDIT
Here's a slightly more complicated solution that lets you cache any method based on the arguments used to call them.
class MyClass
attr_reader :cache
def initialize
#cache = {}
end
class << self
def cacheable(symbol)
alias_method :"_#{symbol}_uncached", symbol
define_method(symbol) do |*args|
cache[[symbol, *args]] ||= (send :"_#{symbol}_uncached", *args)
end
end
end
end
How this works:
class MyClass
def foo(a, b)
a + b
end
cacheable :foo
end
First, the method is defined normally. Then the class method cacheable is called, which aliases the original method to a new name, then redefines it under the original name to be executed only if it's not already cached. It first checks the cache for anything using the same method and arguments, returns the value if present, and executes the original method if not.
http://martinfowler.com/bliki/TwoHardThings.html:
There are only two hard things in Computer Science: cache invalidation and naming things.
-- Phil Karlton
Rails has a lot of built in caching(including query caching). You might not need to do anything:
http://guides.rubyonrails.org/caching_with_rails.html
Here is a recent blog post about problems with roll your own caching:
http://cmme.org/tdumitrescu/blog/2014/01/careful-what-you-memoize/
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
I've been studing Rails for a not such a long time up to now .... so if there are feel free to correct me
I see that there are two ways of defining methods in rails
def method_name(param)
def self.method_name(param)
The difference (as i understand) is that 1 is mainly used in controllers while 2 is used in models... but occasionaly i bump into methods in models that're defined like 1.
Could you explain to me the main difference of thease two methods?
Number 1. This defines a instance method, that can be used in instances of the model.
Number 2. This defines a class method, and can only be used by the class itself.
Example:
class Lol
def instance_method
end
def self.class_method
end
end
l = Lol.new
l.instance_method #=> This will work
l.class_method #=> This will give you an error
Lol.class_method #=> This will work
The method self.method_name defines the method on the class. Basically within the class definition think of self as referring to the class that is being defined. So when you say def self.method_name you are defining the method on the class itself.
class Foo
def method_name(param)
puts "Instance: #{param}"
end
def self.method_name(param)
puts "Class: #{param}"
end
end
> Foo.new.method_name("bar")
Instance: bar
> Foo.method_name("bar")
Class: bar
I have a module, whose purpose is to act on any given ActiveRecord instance.
For argument's sake, let's say that this method puts the string "match" if it matches certain properties with another instance of the same type.
module Foo
def check_against_other_instances
self.all.each do |instance|
if self.respond_to? :color && self.color == instance.color
puts "match"
end
end
end
end
However, I can't just simply call self.all here, because self is an instance. How do I call the class method all from here?
Ah.. found the solution almost right after I asked...
self.class.all.each do |instance|
...
if you want to extend the behavior of rails classes, then you are best of using ActiveSupport::Concern!
http://apidock.com/rails/ActiveSupport/Concern
You can pull the name of a class from an instance and then constantize it.
For example, given a class Thing:
t = Thing.new
t.class.name
# => "Thing"
t.class.name.constantize
# => Thing