Rails, helper with "!" - ruby-on-rails-3.1

I often write stuff that looks like this:
def AModel < ActiveRecord::Base
belongs_to :user
def SomeCodeThatDoesSomeCalculations
# some code here
end
def SomeCodeThatDoesSomeCalculations!
self.SomeCodeThatDoesSomeCalculations
self.save
end
end
Is there a better way to generate the functions with the suffix "!" ?

If you're doing it really often you can do smth like that:
class Model < ActiveRecord::Base
def self.define_with_save(method_name)
define_method "#{method_name}!" do
send method_name
save
end
end
def save # stub method for test purpose
puts 'saving...'
end
def do_stuff
puts 'doing stuff...'
end
define_with_save :do_stuff
end
m = Model.new
m.do_stuff
# => 'doing stuff...'
m.do_stuff!
# => 'doing stuff...'
# => 'saving...'
If you want that in multiple models may be you'd like to create your own base class for them containing this define_with_save class method, or you can add it to ActiveRecord::Base itself if you are sure you need it.
Btw, I hope you're not really naming you your methods in SomeCodeThatDoesSomeCalculations notation as they are usually named like some_code_that_does_some_calculations.

Related

Insert code into the beginning of each method of a class

How can I dynamically and easily insert code into the beginning of each method of a class and subclasses without actually inserting it manually? I want something like a macros.
class C1
def m1
#i_am = __method__
end
def m2
#i_am = __method__
end
end
This is one of the examples where I want to avoid repetition.
I initially misinterpreted the question (but have left my original answer after the horizontal line below). I believe the following may be what you are looking for.
class C1
[:m1, :m2].each do |m|
define_method(m) do |name|
#i_am = __method__
puts "I'm #{name} from method #{#i_am}"
end
end
end
C1.instance_methods(false)
#=> [:m1, :m2]
c1 = C1.new
#=> #<C1:0x007f94a10c0b60>
c1.m1 "Bob"
# I'm Bob from method m1
c1.m2 "Lucy"
# I'm Lucy from method m2
My original solution follows.
class C1
def add_code_to_beginning(meth)
meth = meth.to_sym
self.class.send(:alias_method, "old_#{meth}".to_sym, meth)
self.class.send(:define_method, meth) do
yield
send("old_#{meth}".to_sym)
end
end
end
Module#alias_method
and Module#define_method are private; hence the need to use send.
c = C1.new
#=> #<C1:0x007ff5e3023650>
C1.instance_methods(false)
#=> [:m1, :m2, :add_code_to_beginning]
c.add_code_to_beginning(:m1) do
puts "hiya"
end
C1.instance_methods(false)
#=> [:m1, :m2, :add_code_to_beginning, :old_m1]
c.m1
# hiya
#=> :m1
You can use a rails like class decorators to that. The piece of code below is rendering a method called before_action defined in the Base class of the ActiveRecord module. The Test class is inherited from the ActiveRecord. The define_method is used if we want to call something explicitly from the Base class.
module ActiveRecord
class Base
def self.before_action(name)
puts "#{name}"
puts "inside before_action of class Base"
define_method(name) do
puts "Base: rendering code from Base class"
end
end
end
end
class Test < ActiveRecord::Base
before_action :hola
def render()
puts "inside render of class Test"
end
end
test = Test.new
test.render
test.hola
It has the output
hola
inside before_action of class Base
inside render of class Test
Base: rendering code from Base class
So, before running the render method it runs the before_action method in the Base class. It can be applied to all other methods in the Test class. This is a way of representing macros in ruby.
Assuming that the function is the same, you could create a module and include it in your classes.
Example:
module MyModule
def test_method
puts "abc"
end
end
class MyClass
include MyModule
def my_method
puts "my method"
end
end
inst = MyClass.new
inst.test_method # => should print "abc"
inst.my_method # => should print "my method"

ruby call method based on record type

I have the following methods on my vehicle model like so:
class Vehicle < ActiveRecord::Base
def get_history(start_time, end_time)
#...
end
def gsm_get_history(start_time, end_time)
#...
end
end
get_history is being called all over the codebase, I defined gsm_get_history method that's very similar, accepts the same arguments.
how can I keep calling get_history without changing every instance of this method call in my code base? and call gsm_get_history only if communication channel on the vehicle is :gsm
Change get_history to look like this:
def get_history(start_time, end_time)
if communication_channel == :gsm
return gsm_get_history(start_time, end_time)
end
# ...
end
You could shortcircuit the get_history method
class Vehicle < ActiveRecord::Base
def get_history(start_time, end_time)
return gsm_get_history(start_time, end_time) if communication_channel == :gsm
#...
end
def gsm_get_history(start_time, end_time)
#...
end
end

Execute method like before_filter in Rails

I try to write a metaprogramming for execute a method before 'master' method. Why ? Because, I have several class and it's ugly to repeat the call in the head of the method
Case :
class MyClass
include MySuperModule
before :method, call: before_method
def before_method
puts "Before.."
end
end
class SomeClass < MyClass
def method
puts "Method.."
end
end
module MySuperModule
# the awesome code
end
Output :
SomeClass.new.method => "Before.. Method.."
So, I try write a module with ClassMethodsor method_missingwithout success.
You don't need a gem for simple metaprogramming like this. What you can do is redefine the "after" method to call the "before" method and then the original "after" method.
This works even when using before multiple times on the same method or when creating a chain of before calls.
module MySuperModule
def before meth, opts
old_method = instance_method(meth)
define_method(meth) do
send opts[:call]
old_method.bind(self).call
end
end
end
class MyClass
extend MySuperModule
def foo
puts "foo"
end
def bar
puts "bar"
end
def baz
puts "baz"
end
before :foo, call: :bar
before :bar, call: :baz
end
MyClass.new.foo
# baz
# bar
# foo
If it is just for subclassing purposes you can take advantage of Module#prepend:
class Superclass
def self.inherited(subclass)
# subclass.send :prepend, Module.new { on Ruby < 2.1
subclass.prepend Module.new {
def method
before_method
super
end
}
end
def before_method
puts 'Before'
end
end
class Subclass < Superclass
def method
puts 'Method'
end
end
Subclass.new.method
#=> Before
#=> Method
What you are looking for is Aspect oriented programming support for ruby. There are several gems implementing this, like aquarium.
Another way to do this is to use the rcapture gem.
It is pretty awesome.
Eg:
require 'rcapture'
class A
# Makes the class intercept able
include RCapture::Interceptable
def first
puts 'first'
end
def second
puts 'second'
end
end
# injects methods to be called before each specified instance method.
A.capture_pre :methods => [:first, :second] do
puts "hello"
end
n = A.new
n.first
n.second
produces:
hello
first
hello
second
Maybe you can use a decorator. In ruby there is a nice gem called 'drapeer'. See Drapper Link
Every call in ruby runs through set_trace_func so you can hook into that and call exactly what you want. Not the prettiest solution and there are better ways but it does work. Another option is the Hooks gem, though I haven't tried it myself, it looks like it should give you the ability to do what you want.
module MySuperModule
# the awesome code
end
class MyClass
include MySuperModule
def before_method
puts "Before.."
end
end
class SomeClass < MyClass
def method
puts "Method.."
end
end
set_trace_func proc { |event, file, line, id, binding, class_name|
if event == "call" && class_name == SomeClass && id == :method
caller = binding.eval("self")
caller.send(:before_method)
end
}
SomeClass.new.method
#=> Before..
#=> Method..

implement a rails before_filter in ruby without rails

I am using g a logger in all of my classes.
I want each msg to begin with class name and method name like so:
Class_name::Method_name
this is what i'm doing now :
class FOO
def initialize
end
def bar
msg_prefix = "#{self.class}::#{__method__}"
... some code ...
#logeer = "#{msg_prefix} msg ..."
end
def bar2
msg_prefix = "#{self.class}::#{__method__}"
... some code 2 ...
#logeer = "#{msg_prefix} msg2 ..."
end
end
i want to use a before_filter like in rails to prevent duplicity,
I am using sinatra but the classes are plain old ruby 1.9.3 classes
ideas??
You can get a callback on any method being created with Module#method_added, alias the old method, then define a new method that calls the before_filter method first. Here's my (extremely) rough first concept:
module Filter
def before_filter name
##filter = name
end
def method_added name
return if #filtering # Don't add filters to original_ methods
return if ##filter == name # Don't filter filters
return if name == :initialize
#filtering = true
alias_method :"original_#{name}", name
define_method name do |*args|
self.send ##filter, name
self.send :"original_#{name}", *args
end
#filtering = false
end
end
class FilterTest
extend Filter
before_filter :prepare_logs
def baz
puts "#{#msg_prefix} message goes here"
end
def prepare_logs name
#msg_prefix = "#{self.class}::#{name}"
end
end
ft = FilterTest.new
ft.baz
By using __method__ like you were in create_prefix, you'll get the name of the filter method, not the original method, so you have to pass the method name in. There might be other solutions to make that a bit cleaner.
You can use ActiveModel::Callbacks to get before_filter-like behaviour in plain Ruby classes (though perhaps in your case it's overkill for just executing one line):
require 'active_model'
class FOO
extend ActiveModel::Callbacks
define_model_callbacks :baz, only: :before
before_baz :create_prefix
def initialize
end
def bar
run_callbacks :baz do
... some code ...
#logeer = "#{#msg_prefix} msg ..."
end
end
def bar2
run_callbacks :baz do
... some code 2 ...
#logeer = "#{#msg_prefix} msg2 ..."
end
end
private
def create_prefix
#msg_prefix = "#{self.class}::#{__method__}"
end
end

Can a DataMapper scope use an associated model's scope?

Suppose I have a DataMapper scope for carnivores, like this:
class Animal
#...
def self.carnivores
all(:diet => 'meat')
end
#...
end
Can I reuse that scope in an association's scope?
class Zoo
#...
def self.with_carnivores
# Use `Animal.carnivores` scope to get zoos that have some?
all(:animals => ???)
end
#...
end
You can do this by going "in reverse" from the association.
class Zoo
#...
def self.with_carnivores
# Assuming `Animal belongs_to :zoo`
Animal.carnivores.zoo
end
#...
end
class Habitat
#...
def self.with_carnivores
# Assuming `Animal has_many :habitats`
Animal.carnivores.habitats
end
#...
end

Resources