How to Make a Method Available to Both My Controller and Model in Rails

How do I make a method available to both my controller and model in Rails?

Modules are used for 3 different things in ruby. First is namespacing. Having class or constant definitions inside a module won't collide with classes or constants outside that module. Something like this

class Product
def foo
puts 'first'
end
end

module Affiliate
class Product
puts 'second'
end
end

p = Product.new
p.foo # => 'first'

p = Affiliate::Product.new
p.foo # => 'second'

The second use for modules is as a place to stick methods that don't really have a place anywhere else. You can do this inside a class too, but using a module sort of tells people reading the code that it is not meant to be instanciated. Something like this

module Foo
def self.bar
puts 'hi'
end
end

Foo.bar #=> 'hi'

Finally (and the most confusing) is that modules can be included into other classes. Using them this way is also referred to as a mixin, because you are "mixing in" all the methods into whatever you are including.

module Foo
def bar
puts 'hi'
end
end

class Baz
include Foo
end

b = Baz.new
b.bar #=> 'hi'

Mixins are actually a way more complected topic then I am covering here, but going deeper would probably be confusing.

Now, to me, S3 seems to be something that really belongs in the controller, since controllers are usually the things dealing with incoming and outgoing connections. If that is the case, I would just have a protected method on application controller, since that will be accessible to all other controllers, but still be private.

If you do have a good reason for it being in the model too, I would go for a mixin. Something like

module AwsUtils
private
def S3
AWS::S3::Base.establish_connection!\
:access_key_id => 'Not telling',
:secret_access_key => 'Really not telling'

data = yield
AWS::S3::Base.disconnect
data
end
end

If you put that in lib/aws_utils.rb, you should be able to use it by adding include AwsUtils in both your controller and your model. Rails knows to look for classes and modules in lib, but only if the name matches (in wide case). I called it AwsUtils because I know what rails will look for when it sees that (aws_utils.rb), and to be honest, I have no idea what it will need for S3Utils ;-)

Feel free to ask for more info if I wasn't clear on something. Modules tend to be one of those things in ruby that while amazing, are downright baffling to newcomers.

In Rails, where to put useful functions for both controllers and models

In answer to the specific question "where do you put common code that you'd want to use in both models and controllers?":

Put it in the lib folder. Files in the lib folder will be loaded and modules therein will be available.

In more detail, using the specific example in the question:

# lib/my_utilities.rb

module MyUtilities
def trim_string(string)
do_something
end
end

Then in controller or model where you want this:

# models/foo.rb

require 'my_utilities'

class Foo < ActiveRecord::Base
include MyUtilities

def foo(a_string)
trim_string(a_string)
do_more_stuff
end
end

# controllers/foos_controller.rb

require 'my_utilities'

class FoosController < ApplicationController

include MyUtilities

def show
@foo = Foo.find(params[:id])
@foo_name = trim_string(@foo.name)
end
end

Rails create a method accessible via both the model and controller

Instead of using a constant (VALID_MODULES), try making it an attribute of your job.

class Job < ActiveRecord::Base

attr_accessor :valid_modules
after_initialize :init

validates :name, presence: true
validates :desc, presence: true
validates :api, presence: true, :inclusion => { :in => VALID_MODULES}
validates :filters, presence: true
validates :toe, presence: true

def init
@valid_modules ||= []
Dir.foreach('lib/resources') do |item|
next if ['.', '..', 'resource.rb'].include?(item)
#Wont be called very often so O(n) complexity is fine (small #elements)
@valid_modules << item[0..-4] unless @valid_modules.include?(item[0..-4])
end
end

end

Now in your controller you can just call valid_modules on your Job object to return the array.
Example:

job = Job.new
job.valid_modules

I want to create a method that will be available in all controllers, and views

You can define your method in application_controller.rb and since every other controller inherits from this one, that method will be available to all the controllers. Also, to make it a helper method for it to be available in the views, you can say helper_method :my_method in the application_controller.rb.

Now, for it to be automatically evaluated before any other action in a controller, you can use a before_filter. Add before_filter :my_method
in the controller you want this method to be evaluated before any action or in the application_controller.rb

Rails: Create method available in all views and all models

You could add some modules, and then have in the models that you want the method to work (on a specific attribute) something like this:

The modules (add this to the lib folder)

module hasFoo
def self.included(base)
base.extend ClassMethods
end
end

module ClassMethods
def has_foo(method)
define_method foo do
field = self.send(method.to_sym)
field
end
end
end

Then in your model, just add

has_foo :name

Then you can just call model.foo

And I think that would do it...

Access method in controller from model

The concept which you are talking of is introducing a class method. The method needs to be defined on the Work class itself with the help of the self keyword.

It can be done as follows:

class Work < ActiveRecord::Base
def self.testMethod
return "string from test method"
end
end

To answer your second question, Rails being an opinionated framework, forces the pattern of pluralizing your Controller and Table names. This is really for being more grammatically correct.

In case you wish to tweak the pluralization you can do so by using the Inflections.

How to share code between model and controller in Rails?

I think the controller would defer the actual execution of writing a file to the file system to the model. While the controller is allowed to decide when to execute that code, it should not be responsible for it's implementation, So that code should really only be in the Model.

Rails App: Method in model or controller

The Rails way is to keep controllers skinny (very simple) and add logic to the model layer whenever possible. This method should be in the model.

To your other question:

Can I not put this method in the cart controller for the cart object to be able to access it?

Specifically, this is a bad idea. You do not want the model (cart object) accessing or calling anything in the controller. The controller should make calls to (depend on) the model layer, but not vice-versa.

Hope this helps! :)

Calling model custom method in controller

Change the class method:

def self.add_manager(params)

To an instance method:

def add_manager(params)

as @rally is an instance of your Rally class

(answered in comments above - removing from unanswered list)

How to use the same method in 2 controllers?

Might be some typos lurking in here but:

class GenericController < ApplicationController
def index
@objects = params[:controller].singularize.camelcase.constantize.all()
end

def show
@object = params[:controller].singularize.camelcase.constantize.find(params[:id])
end

def new
@object = params[:controller].singularize.camelcase.constantize.new
end

def edit
@object = params[:controller].singularize.camelcase.constantize.find(params[:id])
end

def create
model = params[:controller].singularize.downcase
@object = params[:controller].singularize.camelcase.constantize.new(params[model])
if @object.save
redirect_to '/'+params[:controller]
else
render :action => 'new'
end
end

def update
model = params[:controller].singularize.downcase
@object = params[:controller].singularize.camelcase.constantize.find(params[:id])
if @object.update_attributes(params[model])
redirect_to :controller => params[:controller], :action => 'index'
else
render :action => 'edit'
end
end

def destroy
if @object = params[:controller].singularize.camelcase.constantize.find(params[:id])
@object.destroy
end
redirect_to :controller => params[:controller], :action => 'index'
end

end

Specific controllers can override those implementations as needed, but:

class ProjectsController < GenericController
# done!
end

class ScenariosController < GenericController
# done!
end


Related Topics



Leave a reply



Submit