When my project is growing up, I need to write some methods, but application_controller's private methods and helpers aren't provide enough space to store all extensions.
So I have looked at custom classes and methods, which are stored in the /lib folder.
But i still have some questions, which i can't solve:
-When should I use "class << self"? I have a class, to calculate difference between two arrays of numbers, and then return new array with a middle values of that numbers. I used to such code:
x = MyClass.new
x.calculate(array1, array2)
And then I have placed my class' methods into "class << self; end" to use class without initialization. Is it right solution?
-When should i use custom modules? Is it always need to 'include' or 'require' them? Please tell me about your modules in your projects, when do you use them?
-How can I call helper's method in the controller? I want to use in in ajax responce. For example I use helper method 'users_for_output', and if there was ajax call, my app should render only users as text, to process it with javascript after.
1) You don't have to instantiate the class to invoke a static method, i.e.
MyUtil.do_something
Vs.
MyUtil.new.do_something
In my project I keep such methods static.
2) You can use modules when want to share a set of functionality across classes. Read this mixin vs inheritance discussion. You will get a good idea about when to use modules.
2.1) The included method is intended for initializing the module variables. You don't need to use it if you don't have anything initialize.
3) If you want to expose a controller method as a helper method use the helper_method call in your ApplicationController class.
class ApplicationController < ActionController::Base
helper_method :user_for_output
end
Related
In my rails 4 app, I have a subfolder logic in my App folder, where I put classes/methods that don't belong to controllers or models.
However, when I try to access these methods from a controller, I get an unknown method error.
Here is a class in the logic folder:
class Analyze
def intent_determination(msg, context)
keywords = [["categories", "category"], ["brands", "brand"], ["stock", "stocks"], ["info", "information"], ["no"], ["yes"]]
tokenized_array = msg.split
keywords.each {|array| context["intent"] = array.first if (tokenized_array & array).any? }
context
end
def update_context(msg, session)
session.update(context: intent_determination(msg, session.context))
session.update(context: brand_determination(msg, session.context))
session.update(context: style_determination(msg, session.context))
session
end
end
How can I access these methods in my controllers ?
When I just execute update_context(my_message, #session), as I said, I get an unknown method error.
Here is my App folder structure:
App
Assets
Controllers
Logic
analyze.rb
Helpers
Mailers
Models
Views
EDIT:
I did add:
config.autoload_paths << Rails.root.join('app/logic/**/') to my application.rb file.
So this is not a duplicate.
Your update_context method is an instance method inside the class Analyze. To invoke it, you have to do Analyze.new.update_context.
The line config.autoload_paths << Rails.root.join('app/logic/**/') you added tells Rails where to start looking for classes and modules. This means that Rails would be able to find your Analyze class just like that without you needing to do require 'some/path'.
Now, it seems like you probably want to have update_context be a class method rather than an instance method, so that you do not need to instantiate an Analyze object with Analyze.new. This is simple enough, just add self. before the method name:
class Analyze
def self.update_context(msg, session)
end
end
Analyze could also be a module in that case instead of a class, since an "instance" of it doesn't really seem to represent anything concrete.
Either as class or module, when defining the method as def self.update_context you'll be able to do Analyze.update_context(...) from anywhere in your app.
If you're looking for these helper methods to be used inside your views, consider defining update_context as a method inside app/helpers/application.helper.rb. Simply define it as you did (without the self.) and you'll be able to call it from any view and without any "prefix" (i.e. you'll be able to simply do update_context instead of Analyze.update_context). Rails also allows you to call such helpers from controllers (see here) and models (see here), although it would be wise to keep separation of concerns in mind (view logic stays in view helpers, persistency logic and access to data in models, and action-specific business-logic in controllers) rather than "including everything into everything" because it seems convenient.
Recently I had to add a method to Redmine's core class. I was unable to use inheritance, so I've done something like this:
require_dependency 'time_entry_query'
class TimeEntryQuery < Query
def my_new_method(foo, bar)
end
end
and it works perfectly - my method is added to all new objects. However, I've seen someone declaring the new method in their own module instead and then sending :include to class, so it become a mixin. Here's an example:
module Patches
module SomeClassPatch
def my_new_method
end
end
and somewhere in app's initialization:
SomeClass.send(:include, Patches::SomeClassPatch) unless SomeClass.include? (Patches::SomeClassPatch)
What's difference between these two methods and which one should I use?
There are two differences:
When you use a mixin, there is a clear place where your "patch" methods can live. If I wonder "Hmm, where's this my_new_method" coming from, and I look at, say, TimeEntryQuery.ancestors or TimeEntryQuery.instance_method(:my_new_method).owner, that will return Patches::SomeClassPatch. So I know I have to look for a file named lib/patches/some_class_patch.rb somewhere to find where it is probably defined. (I could try source_location as well, but that is not always reliable.)
Mixing in a module into a class makes the module the superclass of the class it is being mixed into. So, if there already is a my_new_method defined in TimeEntryQuery, your first option will overwrite it, whereas in your second option, your method will become the super method of that method. IOW: with your second option, your new method won't be called unless the already existing method calls super.
I've been told that the helpers are just for functions that are needed by the views.
Where should I put in functions that are used commonly by models? What about controllers?
What's the convention to place commonly used functions that will be used in:
1) models
2) views
3) controllers
Problem: Creating a module in lib to hold the functions and including the module in a class would create a boat-load of instance methods for the class.
Problem: What about functions that are common and needed in all three?
Problem: Creating a module in lib to hold the functions and including the module in a class would create a boat-load of instance methods for the class.
First organize, then optimize
Problem: What about functions that are common and needed in all three?
Do you really have methods that are needed in all the three and not exist yet ?
If yes, may be you can give an exemple
I think the question should be where to put logic in general.
You should first think what your method does before to think about where to put it.
But whatever you create, when it's getting big and or valuable, you should really think about exporting it as a gem/plugin.
Inner navigation logic (what to display and where to go after an action) : Controllers
App navigation logic; application_controller
Sub set of app logic; create a namespace with master controller, class API_controller < application_controller
Data logic (How to manipulate, process data) : Models
Data; Model class method (search, sorting, counting, macro process ...)
Datum; Model instance method (modification, micro process ...)
Data presentation logic (How to display data) : Helper, Partial and Decorators
Helper are not designed for that in my opinion.
Partial handle layouting of specific data.
application decorator; handle generic data presentation help
scope_decoration; you can use inheritance
Layout language logic (layout language help) : Helper
Specific to your app; application_helper
Specific to a model ...; model_helper, but you should consider decorator
Generic; export it in a gem (super form helper, templating system ...)
Layout logic (should i display this menu ?) : ?
Helper/decorator/model can should answer the question : #user.can_edit?(#article)
Layout handle the display <%= render :partial => allowed ? "something" : "somthing else" %>
I think if you are not in this configuration you are creating kind of backend system.
So it should go in lib, then in a gem later.
This organization is just an exemple. The most important thing is to organize your code and split different logic layers and don't hesitate to refactor/export code to make it generic after adding new features...
for Controllers - put common methods in application_controller.rb
for Views - put common methods in application_helper.rb
for Models - monkeypatch ActiveRecord::Base to include common methods OR write a module with common model methods and include it in the models that need it OR do it in OOP way by subclassing ActiveRecord::Base with your abstract class, then inheriting all your models from this class.
To use common methods in both Model and Controller, do one of the following:
Write a plain ruby class, put it in /lib or elsewhere, just make sure it's loaded, then require it when you need to use its methods.
Extract common functionality to a gem, install it, require it when you need it. Publish it to rubygems if it's something valuable.
... Usually, I put those kind of functions into common superclasses: For models, that could be (for example) Animal for subclasses Dog, Cat, etc. Within the Animal model, you would have to
self.abstract_class = true
so it doesn't expect a table for that class. For controllers, you could either use ApplicationController or you could make your controllers be derived by another common subclass.
In the Model you should store all the methods that have a relation to the model itself like manipulating attributes, scopes, associating,...
In the View you dont store any logic! The logic belongs to the model. In the view you only put code that helps you to display stuff.
The Controller is the "bridge" between both. You select data in the controller, call methods that are stored in the model,... A common failure is to store the logic in the controller which should be stored in the model.
When you store a method in your Modelyou can access it from the model, the view and the controller! If you have a method that doesn't have a relation to a specific model or its needed in several models you can use the Helper. An example for such a case might be a method that rewrites your url using a pattern. This might be needed in 20 models to prepare a string for to_param. That method would be stored in an Helper that could be included in the Models its needed.
Watching this video by Yehuda, and he gave this snippet about how Ruby helps you build better abstractions.
class FOWA
def self.is_fun
def fun?
true
end
end
is_fun
end
He was talking about, in ruby, how if you are repeating code in your class over and over again, you can abstract it out without having to think about things in terms of methods etc. And he said this was using a metaprogramming technique.
Can someone explain what this is?
It is a class method on FOWA (so its like a static method, you don't need an instance to call it), and this class method is really just wrapping another method that returns true.
And this is_fun class method is now being executed or what? not sure what the last line "is_fun" is doing?
http://vimeo.com/11679138
The is_fun call at the end of the class calls the static method. The static method then defines the fun? method inside of the FOWA class. Then, you can do this:
f = FOWA.new
f.fun?
If you take out the is_fun call at the end of the class, the fun? method doesn't get defined.
He mentioned that you wouldn't use it in this way, but the point is how easy it is to dynamically add a method to a class. You might use it like this if you wanted the method to be available in subclasses and you wouldn't call is_fun in FOWA, but you might in a subclass. It gets a little more interesting if you have a parameter for is_fun and the definition of fun? changes depending on that parameter.
This also leads right into modules because you can define a module with the same is_fun method and then just have your class extend the module and the methods in the module are available in the class. You would use this technique if you want your method to be available to more than just subclasses of FOWA.
I know that if I want to call a helper of another controller, I can do something like:
helper :other_controllers
But I was wondering why I can't do something like OtherControllersHelper.method inside the view?
Due to the way that Rails loads your modules, you cannot do this without modification.
Rails includes the associated helper models into the ActionView::Base instance used to render a template. ActionController::Helpers#helper (used in the example above) adds more helper modules to the list of those to be included. The helper methods that are used in views are written as instance methods. Modules in Ruby do not provide any good ways of getting at instance methods without using a constructor. Which is one of the big things that separates modules from classes.
To access your helpers from another controller with just OtherControllersHelper.method, you will need to redefine method as a class method. However, redefining those methods as class methods would make them inaccessible from your views.
You could duplicate all instance methods in your helpers as class methods, but that's definitely not a better solution that adding helper :other_controllers. There are ways to define wrappers pragmatically, but again, it's not the best way to handle the situation.
If you've got a lot of helpers that are likely to be used in multiple controllers/views maybe you're better off putting them somewhere else. Somewhere like app/helpers/application_helper.rb. Or another helper module that could be loaded only in the controllers that need it.