class ReportMailer < ActionMailer::Base
def venues
#venues ||= Venue.find(VENUE_MAP.values)
end
end
How can I test venues, if:
ReportMailer.new => nil
How is this behavior called? is ReportMailer an abstract class?
All the methods you define in ReportMailer can be called as class methods
ReportMailer.venues
To deliver the email
ReportMailer.venues.deliver
Read
I extracted methods to module and then, included and extended it:
class ReportMailer
include ReportMailer::PrivateMethods
extend ReportMailer::PrivateMethods
...
In app/models/report_mailer/private_methods.rb:
module ReportMailer::PrivateMethods
...
Related
I need to call a helper method within a model, from both a class and an instance method, e.g. Model.method(data) and model_instance.method. However, the class method always returns "NoMethodError: undefined method 'helper_method' for #<Class ...>"
model.rb:
class Model < ActiveRecord::Base
include ModelHelper
def method
helper_method(self.data)
end
def self.method(data)
self.helper_method(data)
end
end
model_helper.rb:
module ModelHelper
def helper_method(data)
# logic here
end
end
I even tried adding def self.helper_method(data) in the helper to no avail.
After quite a bit of seraching, I wasn't able to find anything on how to achieve this, or at least anything that worked.
The answer turned out to be pretty simple, and doesn't require any Rails magic: you just re-include the helper and define the class method within a class block:
class Model < ActiveRecord::Base
include ModelHelper
def method
helper_method(self.data)
end
# Expose Model.method()
class << self
include ModelHelper
def method(data)
helper_method(data)
end
end
end
No changes to the helper needed at all.
Now you can call method on both the class and an instance!
If there's no additional logic in method, then you can simply do:
class Model < ActiveRecord::Base
include ModelHelper
extend ModelHelper
end
And get both the instance (#model.helper_method) and the class (Model.helper_method) methods.
If, for legacy (or other) reasons, you still want to use method as an instance and class method, but method doesn't do anything different than helper_method, then you could do:
class Model < ActiveRecord::Base
include ModelHelper
extend ModelHelper
alias method helper_method
singleton_class.send(:alias_method, :method, :helper_method)
end
And now you can do #model.method and Model.method.
BTW, using modules to include methods in classes is seductive, but can get away from you quickly if you're not careful, leaving you doing a lot of #model.method(:foo).source_location, trying to figure out where something came from. Ask me how I know...
you need to define model_helper.rb as:
module ModelHelper
def self.helper_method(data)
# logic here
end
end
and call this method in model.rb as:
class Model < ActiveRecord::Base
include ModelHelper
def method
ModelHelper.helper_method(self.data)
end
def self.method(data)
ModelHelper.helper_method(data)
end
end
How does one override a class method defined in a model concern?
This is a bit tricky since you’re not really overriding a class method right? Because it’s using the concern api of definining class methods in the class_methods block.
so say I have a concern that looks like this:
module MyConcern
extend ActiveSupport::Concern
class_methods do
def do_something
#some code
end
end
end
In model.. how would I override that method so that I could call it like we do with super when using inheritance? So in my model I’d like to go:
def self.do_something
#call module do_something
end
?
If you've included MyConcern in the model that defines self.do_something, you should just be able to use super:
module MyConcern
extend ActiveSupport::Concern
class_methods do
def do_something
puts "I'm do_something in the concern"
end
end
end
class UsesMyConcern < ActiveRecord::Base
include MyConcern
def self.do_something
super
end
end
UsesMyConcern.do_something
# => "I'm do_something in the concern"
If you haven't or don't want to include MyConcern in the model and you want to invoke do_something on the module without creating any intermediary objects, you can change your model to:
class UsesMyConcern < ActiveRecord::Base
def self.do_something
MyConcern::ClassMethods.instance_method(:do_something).bind(self).call
end
end
UsesMyConcern.do_something
# => "I'm do_something in the concern"
ActiveSupport::Concern.class_methods defines a ClassMethods module in the concern if there isn't one already, and that's where we can find the do_something method.
Why not simply call the module's method: MyConcern.do_something?
I'm not sure if there's an easy of doing super for modules (though I can see why that may be useful).
The next best solution could be doing something like calling #included_modules and manually iterating with #responds_to?:
def self.do_something
self.super_module(__method__)
end
def self.super_module(method)
self.included_modules.find { |m| m.responds_to? method }.public_send(method)
end
The old way using alias_method_chain: https://ernie.io/2011/02/03/when-to-use-alias_method_chain/
The new way (requires > ruby 2.0.0) you really should use this, as there will be a DEPRECATION WARNING when using it in rails 5.0:
http://paweljaniak.co.za/2014/09/30/understanding-ruby-module-prepend-and-include/
class ApplicationController < ActionController::Base
protect_from_forgery #What is this syntax? When is this executed and how to create one?
end
class Comment < ActiveRecord::Base
belongs_to :post
attr_accessible :body, :commenter, :post
end
In the first case, I understand ApplicationController is a new Derived class of class called Base in the module ActionController. What happens in the next line? Is protect_from_forgery a method in base class or in module ActionController? What is it called? I couldn't find in ruby classes documentation. I tried creating a method in base class but got errors like below. How do I create such special commands which can be used in a class?
class Base
def foo
#name = "foo"
end
end
class Der < Base
foo
def bar
#dummy = "bar"
end
end
Error:
expr1.rb:62:in `<class:Der>': undefined local variable or method `foo' for Der:Class (NameError)
from expr1.rb:61:in `<main>'
protect_from_forgery is a class-method defined in one of the modules included in ActionController::Base and made available to the child class when you inherit from ActionController::Base.
This kind of methods in Rails are sometimes called "macro" as they are class methods that enables some specific feature (sometimes also using metaprogramming to define extra methods or helpers). In reality, the term "macro" is incorrect as Ruby has no macro. They are nothing else than class methods.
The most important detail to keep in mind is that, when they are used in the class-definition. these methods are run at code-evaluation and not at runtime.
class Base
def foo_instance
p "foo instance"
end
def self.foo_class
p "foo class"
end
end
class Der < Base
foo_class
def bar
p "bar"
end
end
Der.new.bar
will produce
"foo class"
"bar"
To create class methods you can do either of these.
class Base
def self.foo
end
end
class Base
class << self
def foo
end
end
end
Because they are class methods you call them on the class
Base.foo
So what you're talking about are "class methods" - class methods are methods that are defined on the class itself, not on the instance of the class. Note the following:
class Greeter
def initialize(name)
#name = name
end
def greet
"hello, #{#name}"
end
end
Greeter.new("bob").greet # => "hello, bob"
#greet is an instance method on the Greeter classes. However, .new is a "class method" - which is a method called on the class itself. Attempting to call .greet on the Greeter class would result in a NameError:
Greeter.greet # ! NameError
So if you want to define such a "class method", you have to use one of the following syntaxes:
class Greeter
def self.greet(name)
"hello, #{name}"
end
class << self
def greet(name)
"hello, #{name}"
end
end
class << Greeter
# same as above
end
end
def Greeter.greet(name)
"hello, #{name}"
end
Going back to the original question, if you reopen the greeter class, you can now use the .greet method:
class Greeter
greet "bob" # => "hello, bob"
end
This also applies for subclassing, as well:
class Host < Greeter
greet "bob" # => "hello, bob"
end
This is how Rails provides these methods - they define class methods on the base class, most often ActiveRecord::Base, which you can then use - explaining methods such as protect_from_forgery.
protect_from_forgery is a class method:Could look like this
def protect_from_forgery(options = {})
self.forgery_protection_strategy = protection_method_class(options[:with] || :null_session)
self.request_forgery_protection_token ||= :authenticity_token
prepend_before_action :verify_authenticity_token, options
append_after_action :verify_same_origin_request
end
So its inherited by application controller:
class ApplicationController < ActionController::Base
Your example could be written like this:
class Base
class << self
def foo
#name = "foo"
end
end
end
class Der < Base
foo #class method from Base
def bar
#dummy = "bar"
end
end
To see foo's values
Der.foo.inspect
Its inheritance. In your child class you can use all method inhertited from parrent class.
Firstly you can call methods on class instance.
In your example you can do sth like this:
base_object = Base.new
base_object.foo
der_object = Der.new
der_object.bar
but also thanks to inheritance you can do sth like this:
der_object.foo
Here is simple tutorial for ruby inheritance with examples:
http://rubylearning.com/satishtalim/ruby_inheritance.html
happy coding!
My controller calls the method bar:
class CompsController < ApplicationController
include ApplicationHelper
def quick_create
#var = Matview.bar #projects
end
end
bar is defined in a model that represents a materialized view in my database (it is not in my schema):
class MatView < ActiveRecord::Base
include ApplicationHelper
table_name = 'mat_views'
def self.bar(arg)
foo arg
end
end
'bar' calls method foo, which is defined in my ApplicationHelper:
module ApplicationHelper
def foo(arg1)
#do stuff
end
end
I've included the ApplicationHelper in both my controller and model, and yet I get this error:
NoMethodError in CompsController#quick_create
undefined method `foo' for Matview(Table doesn't exist):Class
why?
Matview.bar #projects
Is calling a class level method on the MatView class.
But your foo and bar are both instance method definitions. To make them class methods, you need def self.bar(arg) or def self.foo(arg1)
And to get class methods into your ActiveRecord model, you need to extend, not include the module:
class MatView < ActiveRecord::Base
extend ApplicationHelper
end
Or, if that does not sound like what you meant to do, then maybe you meant to do:
Matview.new.bar #projects
in which case the instance methods like you wrote them should work.
I try to do this:
app/models/my_model.rb:
class MyModel < ActiveRecord::Base
include MyModule
...
end
lib/my_module.rb:
module MyModule
before_destroy :my_func #!
def my_func
...
end
end
but I get an error:
undefined method `before_destroy' for MyModule:Module
How can I correct it.
Also I'm new to ruby. What type has these "attributes": before_destroy, validates, has_many?
Are they variables or methods or what?
Thanks
before_destroy, validates, etc. are not attributes or anything like that. These are method calls.
In ruby, the body of a class is all executable code, meaning that each line of the class body is executed by the interpeter just like a method body would.
before_destroy :my_func is a usual ruby method call. The method that gets called is before_destroy, and it receives a symbol :my_func as an argument. This method is looked up in the class (or module) in the scope of which it is called.
So moving on to your question, I think now you should understand that when the interpreter loads your module
module MyModule
before_destroy :my_func #!
def my_func
...
end
end
it starts executing its body and searches for the method before_destroy in this module and cannot find one. What you want to do is call this method not on the module, but rather on the class where the module is included. For that we have a common idiom using the Module#included method:
module MyModule
module InstanceMethods
def my_func
...
end
end
def self.included(base)
base.send :include, InstanceMethods
base.before_destroy :my_func
end
end
In lib/my_module.rb, do this:
class MyInheritedClass < ActiveRecord::Base
before_destroy :my_func
def my_func
...
end
end
In app/models/my_model.rb, do this:
class MyModel < MyInheritedClass
...
end
There is no before_destroy filter in the module you are trying to create above. What my code does is creating a class that will inherit from ActiveRecord::Base and that will be your template class which all your other classes can inherit from. The template class also contains all properties of ActiveRecord::Base.
u can correct this by removing before_destroy from MyModule and place it in MyModel instead
before_destroy and other callbacks are only available to classes which extends ActiveRecord::Base, more info here
hope this helps =)