Why isn't a module being read by my View-backed Model? - ruby-on-rails

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.

Related

Can't access to attr_accessor in Concern Ruby on Rails

I want to register the class method in concern and access to attr_accessor, but it doesn't work. This is my sample code. Please help me how can I do this. Thank you so much!
app/controllers/concerns/foobar_concern.rb
module FoobarConcern
extend ActiveSupport::Concern
included do
class << self
attr_accessor :foo
end
end
class_methods do
def test_method(bar)
self.foo = bar
end
end
end
app/controllers/foobar_controller.rb
class FoobarController < ApplicationController
include FoobarConcern
test_method 'Just test'
def index
self.foo => NoMethodError: undefined method "foo"
foo => NameError: undefined local variable or method "foo"
end
end
Just delegate required methods to the class like this
module FoobarConcern
extend ActiveSupport::Concern
included do
delegate :foo, :foo=, to: :class
class << self
attr_accessor :foo
end
end
end
The issue is that you're defining a method at the class level (FoobarController.foo) but calling it on an instance of the class (FoobarController.new.foo).
One option is to call the foo method on the class instead:
def index
self.class.foo
end
You can also define an accessor method for instances of the class like:
module FoobarConcern
extend ActiveSupport::Concern
included do
class << self
attr_accessor :foo
end
end
class_methods do
def test_method(bar)
self.foo = bar
end
end
# -- NEW ---
# This `foo` method is defined for instances of the class and calls the class method.
def foo
self.class.foo
end
end

Calling a helper from a class and instance method in a Rails model

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

Run a helper method on a model class method

I created a helper method that I want to run on a model class method and getting a method not found error.
lib/model_helper
module ModelHelper
def method_i_want_to_use
puts "I want to use this method"
end
end
model/foo
class Foo < ActiveRecord::Base
include ModelHelper
def self.bar
method_i_want_to_use
end
end
This setup gives me a no method error.
You have to extend the module instead of include.
extend ModelHelper
include makes the methods available as instance methods of Foo. That means, you can call the method method_i_want_to_use on instances of Foo, not on Foo itself. If you want to call on Foo itself, then use extend.
module ModelHelper
def method_i_want_to_use
puts "I want to use this method"
end
end
class Foo
extend ModelHelper
def self.bar
method_i_want_to_use
end
end
Foo.bar
# >> I want to use this method

Common method in model and helper

I have a common method that exists in my model because it is called by my model. Retrospectively, my view also requires this model method. In order to accomplish this, I have:
moved the model method to the application_helper.rb file
my model calls the application_helper method by adding include ApplicationHelper at the top of my ActiveRecord model
Functionality wise, it works. But is this good practice?
My Model looks like this:
class Client < ActiveRecord::Base
include ApplicationHelper
end
Writing include ApplicationHelper in to your model is bad practice because ApplicationHelper is a nice place to put tons of helper functions you need in your views. These functions will end up being imported as instance methods of your model. These functions are mostly unrelated to your model and will not work if they depend on things like params or request. Here are two other options:
Option 1:
You can just define the method inside the Client class, and then call it from the view, like this:
class Client < ActiveRecord::Base
def self.my_class_method
end
def my_instance_method
end
end
And then in your view:
<%= Client.my_class_method %>
<%= #client.my_instance_method %>
Option 2:
Make a separate module in lib and include it in the places you need it. The file name should match the module name for auto-loading to work.
In lib/my_module.rb:
module MyModule
def my_method
end
end
In your model:
class Client < ActiveRecord::Base
include MyModule
def other_method
my_method
end
end
Include the module in ApplicationHelper so it is available to all your views:
module ApplicationHelper
include MyModule
end
Then in your view you can call it easily:
<%= my_method %>
If you do want to move it to a helper, you should move it in to the client_helper, as it is something just for your Client model and not for the whole application.
The method you speak of though, is it a static class method or an instance method? If it's an instance method, then your models (even if they're in views) can call that method. If it's a static class method, then your views can use it too by calling it like any other static class method (i.e, Client.do_method or something).
I don't see any reason why it needs to be in a helper, unless your method has absoloutely nothing to do with your model, in which case that would be a different question.
5 Common method in model and helper:
Option 1:
You can just define the method inside the Client class, and then call it from the view,
like this:
class Client < ActiveRecord::Base
def self.my_class_method
end
def my_instance_method
end
end
And then in your view:
<%= Client.my_class_method %>
<%= #client.my_instance_method %>
Option 2:
module MyModule
def my_method
end
end
Include the module in ApplicationHelper so it is available to all your views:
module ApplicationHelper
include MyModule
end
Then in your view you can call it easily:
<%= my_method %>
option 3:
module MyModule
def my_method(amount)
end
end
In your model:
class Client < ActiveRecord::Base
include MyModule
def self.other_method(amount)
my_method(amount)
end
end
Then in your view you can call it easily:
<%= Client.other_method(amount) %>
Option 4:
Or you can declare the helper method as a class function, and use it like so:
module Myhelper
def self.my_village
"bikharniyan kallan,Rajasthan,India"
end
end
Then in your view you can call it easily:
<%= Myhelper.my_village %>
option 5:
use many helper in controller
helper=>
module Myhelper
def my_info
"my information is"
end
end
module Myhelper1
def my_village
"bikharniyan kallan,Rajasthan,India"
end
end
ApplicationHelper=>
module ApplicationHelper
include Myhelper
include Myhelper1
end
ApplicationController
class ApplicationController < ActionController::Base
include ApplicationHelper
end
YourController
class YourController < ActionController::Base
def action
my_info
my_village
end
end
At this moment, with rails 5 you can simply push your common method into application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
def self.common_class_method
# some awesome implement
end
def common_method
# implement this
end
end
Then in each model class you can call common_class_method by : YourClassName.common_class_method
or call common_method by: YourClassInstance.common_method

How to use a custom helper via the "helper" method in Rails 3?

I'm trying to create a custom helper like this:
# app/controllers/my_controller.rb
class MyController < ApplicationController
helper :my
def index
puts foo
end
end
# app/helpers/my_helper.rb
module MyHelper
def foo
"Hello"
end
end
But, I got the following error:
undefined local variable or method `foo' for #<MyController:0x20e01d0>
What am I missing ?
Generally, I do the opposite: I use controller methods as helpers.
class MyController < ApplicationController
helper_method :my_helper
private
def my_helper
"text"
end
end
Helpers are accessed from the views, not the controllers. so if you try to put the following inside your index template it should work:
#my/index.html.erb
<%= foo %>
If you do want to access something from the controller, then you should use the include syntax instead of helper, but do not name it like a helper module in that case.
How about just including the helper as a mixin in the controller...
class MyController < ApplicationController
include MyHelper
def index
puts foo
end
end

Resources