My project name is clog, so I named my models and controllers like this: Clog::User Clog::Userscontroller.
Is this naming convention mandatory?
No, in a conventional Rails project, that's not necessary. Just name your models and controllers the usual way, like eg User or UsersController.
The other thing is that, when your project grows in size, you may need to organize your models into submodules. One approach to do so is extending your models with app concerns, as show eg here or here.
As for organizing controllers, one approach is to create a module in the lib directory, which you then include in your ApplicationController, like so:
In lib/authentication.rb:
module Authentication
def self.included(base)
base.send :before_filter, :login_required
base.send :helper_method, :current_user, :logged_in?
end
def current_user
#current_user ||= User.find_by_remember_token(cookies[:remember_token]) if cookies[:remember_token].present?
end
#...
end
In app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
include Authentication
#...
end
For this to work, you need to add
config.autoload_paths << "#{config.root}/lib"
to your config/application.rb file
However, if you plan to build your Rails project as a Rails Engine, you may want to follow some naming convention. A good example of a Rails Engine is forem.
Yes, following the naming convention helps a great deal because not only does rails use it to generate other names, but other gems as well.
Specific to your question, you may be asking if you need to name the controller as UserController given that your model is called User. That is not necessary at all, and you may call it anything else if it better fits your purpose.
In this case, you will probably want to create a few controllers like so:
My::AccountController # for e.g.. /my/account
Admin::UsersController # for e.g. /admin/users/1
For a user, you refer to your own user record, as 'your account' so this makes more sense. However, the administrator's perspective would be to manage user records. You may also name a controller one thing and serve it under a different route. In your routes file, you may do this:
namespace :admin do
resources :users, :path => "user-accounts"
end
To reiterate, your model name need not match up to the controller name. They are only named similarly by association: UserController is understood to handle User records.
Related
Is there a proper place for helper methods for models in Rails? There are helper methods for controllers and views, but I'm not sure where the best place to put model helper methods. Aside from adding a method to ActiveRecord::Base, which I'd prefer not to.
UPDATE: It seems Concerns make a lot of sense. Here's an example of what I want. Certain models can never be deleted, so I add a callback that always throws an exception:
before_destroy :nope
def nope
raise 'Deleting not allowed'
end
With concerns, I could do something like this?
class MyModel < ActiveRecord::Base
include Undeletable
end
module Undeletable
extend ActiveSupport::Concern
included do
before_destroy :nope
end
def nope
raise 'Deleting not allowed'
end
end
Is this the Rails way of doing this?
If you want to use a helper_method my_helper_method inside a model, you can write
ApplicationController.helpers.my_helper_method
If you need a bit more flexibility, for example if you also need to override some methods, you can do this:
class HelperProxy < ActionView::Base
include ApplicationController.master_helper_module
def current_user
#let helpers act like we're a guest
nil
end
def self.instance
#instance ||= new
end
end
and then use with
HelperProxy.instance.my_helper_method
If you have strong nerves, you can also try to include the ApplicationController.master_helper_module directly into your model.
via : makandracards's post.
For your reference: http://railscasts.com/episodes/132-helpers-outside-views
If what you are asking is where to put code that is shared across multiple models in rails 4.2, then the standard answer has to be to use Concerns: How to use concerns in Rails 4
However, there are some good arguments (e.g. this) to just using standard rails module includes, and extends as marek-lipka suggests.
I would strongly recommend NOT using ApplicationController helper methods in a model, as you'll be importing a lot unnecessary baggage along with it. Doing so is usually a bad smell in my opinion, as it means you are not separating the MVC elements, and there is too much interdependency in your app.
If you need to modify a model object by adding a method that is just used within a view, then have a look at decorators. For example https://github.com/drapergem/draper
In my case I get an instance of a resource and I have to find out, whether it's model class represents a singular or plural resource.
I digged through Rails.application.routes and it's instance variables but did not find any possibility to solve this problem.
Update:
I am using polymorphic_path helpers with anonymous model instances in a gem that includes some modules into models and controllers. The final goal is to provide some automatisms for REST controllers and models and provide pathes / routes of the current model/instance as well as it's ancestors.
My latest attempts looked something like that:
Rails.application.routes.instance_variable_get(:#router).instance_variable_get(:#routes).instance_variable_get(:#named_routes).delete_if{|k,v| !k.match(/my_model_name_as_downcased_string_here/)}.values.map{|v| v.instance_variable_get(:#defaults)}.map{|h| h[:action]}
In the hope it would bring up action :index for plural models and :show for singular. There MUST be a way to make use of introspection to find out whether a model is a singular one, I simply do not find the catch.
Since I did not find any way to find out, whether a resource is singular or plural, I build something on my own. The most secure way IMHO is to hook into route creation direcly. Here's what I did in case anyone encounters the same problem:
# alias :resource method of Rails routing mapper and remember which resourses
# are setup as singular
module ActionDispatch::Routing::Mapper::Resources
def resource_with_singular_recognizer *res, &block
Rails.application.instance_variable_set(:#sr, ((Rails.application.instance_variable_get(:#sr) || []).push res.first).uniq)
resource_without_singular_recognizer *res, &block
end
alias_method_chain :resource, :singular_recognizer
end
# define a model wide method :singular_resource? that checks the previously
# setup singular resource flag
module ClassMethods
def singular_resource?
Rails.application.instance_variable_get(:#sr).include? self.to_s.downcase.to_sym
end
end
# include :singular_resource? method into all model classes
module Mongoid::Document
def self.included base
base.class_eval do
extend ClassMethods
end
end
end
I know :alias_method_chain is not the best choice here, feel free to find a better solution.
I have a kind of social network website.
I have a logic to create the path for the user, and select an avatar for each user described in UsersHelper methods user_path(user) and user_avatar(user).
Instead I want to have methods like user.path and user.avatar, but I don't want to have that code inside the model file.
I tried extending the User class inside the helper:
module UsersHelper
class User
def avatar
...
end
end
end
That doesn't work - the method I added aren't recognized (I'm guessing Rails dynamically generates the ActiveRecord class on demand so my methods don't apply?)
I'd really appreciate ideas
First, there's a reason helpers are separated from models. According to the MVC pattern your model shouldn't know anything about your helpers, and not vice versa either (the latter is usually ignored in Rails, though).
But, if you want to do this, you need to do class ::User in model UsersHelper, or the following:
module UsersHelper
end
class User
end
The reason for this is that Ruby namespaces classes. So you defined UsersHelper::User, while your model is called User. Calling the class ::User or defining it outside the module forces it into the root namespace.
However, as I said, this is not the "correct" way to do it. A better way would be how you're already doing it, or maybe using a decorator pattern.
Draper is an awesome little gem that does an excellent job of achieving the functionality you're looking for (adding view / presentation specific code while still making it "feel" like the model you're working with). We've removed almost all of our model-specific helpers after starting to use draper.
Basically, it works by defining decorators, which work like a wrapper around your model. A decorator looks and feels like the model it's decorating, but can have additional functionality defined on top of it. In addition, you can restrict access to certain fields and lock stuff down if you like.
For your example, adding a decorator would be as simple as:
(in app/decorators/user_decorator.rb)
class UserDecorator < ApplicationDecorator
decorates :user
def avatar
# your implementation
end
(in your controller)
def index
respond_with UserDecorator.decorate(User.all)
end
(in your views)
<div class='avatar'><%= user.avatar %></div>
<div class='name'><%= user.name %></div>
Helpers are intended to use with the views, not with the models.
If you wish to have something like user.avatar, you have to add it to your model.
If you want to stick it in the helpers, then in the UsersHelper add a user_avatar method.
railstutorial.org has a suggestion which strikes me as a little odd.
It suggests this code:
class ApplicationController < ActionController::Base
protect_from_forgery
include SessionsHelper
end
The include SessionsHelper makes the methods available from ApplicationController, yes, but it makes them available in any view, as well. I understand that authentication/authorization is cross-cutting, but is this really the best place?
That seems to me to be potentially too broad of a scope. Putting code which implements, say, a before_filter which conditionally redirects (as the railstutorial.org example does) in a module which more commonly contains view helpers seems surprising.
Would functionality not strictly needed in views be better placed in ApplicationController or elsewhere?
Or am I just thinking too much about this?
Indeed, your feeling is correct.
I would implement this the other way around: add the functions sign_in and current_user to ApplicationController (or if you really want to: in a separate module defined in lib and include it), and then make sure that the current_user method is available in the view.
In short:
class ApplicationController
helper_method :current_user
def sign_in
end
def current_user
#current_user ||= user_from_remember_token
end
end
Of course, if you have a lot of code to place into your ApplicationController it can get messy. In that case I would create a file lib\session_management.rb:
module SessionManagement
def self.included(base)
base.helper_method :current_user
end
def sign_in
..
end
def current_user
..
end
end
and inside your controller you can then just write:
class ApplicationController
include SessionManagement
end
They seem to be taking (sneaky) advantage of the fact that, in Rails, Helpers are just ruby Modules.
Placing behavior that is shared across Controllers in a Module is, in my opinion, good practice. Putting it in a Helper, on the other hand, is potentially misleading and I would avoid it. Place it in a “standard” Module.
This is a philosophical question on the same level as the argument that questions the REST method provided in scaffolding and if A scaffold is worth having at all. You have to consider the fact that the tutorial book in RailsTutorial.org is a get-up-and-go Rails instructive guide. So for the purpose which it serves, I think it does the job.
However, is there a better place to put code needed across controllers and views? Yes, there is.
Some may follow Michael Hartl form Railstutorial and include the entire SessionHelper into the ApplicationController
Others may decide to expose only the essential helpers needed for the view i.e. sign_in, sign_out, current_user and the like.
I see a suggestion to put such code in the /lib directory and include it where needed.
All are viable options. Whichever you take may not matter that much in performance because Ruby would have to parse the file which you want to call (or include) a class, module or method from. What happens is, before any code is executed in a class, Ruby goes through the whole class once to know what's in it. It all depends on what one needs and the design of their app
FWIW, I store the current user in the User class:
class User < ActiveRecord::Base
cattr_accessor :current
...
end
This can be referenced in all 3 MVC tiers; it is set in the controller like so (and likewise on login, of course):
def set_current_user
User.current = (session[:user_id]) ? User.find_by_id(session[:user_id]) : nil
end
Among other things, this allows me to have audit logs at the ActiveRecord level that capture the current user (when applicable).
I am in the middle of migrating my application from using subdirectories for userspace to subdomains (ie. domain.com/~user to user.domain.com). I've got a method in my user class currently to get the "home" URL for each user:
class User
def home_url
"~#{self.username}"
# How I'd like to do it for subdomains:
#"http://#{self.username}.#{SubdomainFu.host_without_subdomain(request.host)}"
end
end
I'd like to update this for subdomains, but without hardcoding the domain into the method. As you can see, I am using the subdomain-fu plugin, which provides some methods that I could use to do this, except that they need access to request, which is not available to the model.
I know it's considered bad form to make request available in a model, so I'd like to avoid doing that, but I'm not sure if there's a good way to do this. I could pass the domain along every time the model is initialized, I guess, but I don't think this is a good solution, because I'd have to remember to do so every time a class is initialized, which happens often.
The model shouldn't know about the request, you're right. I would do something like this:
# app/models/user.rb
class User
def home_url(domain)
"http://#{username}.#{domain}"
end
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# ...
def domain
SubdomainFu.host_without_subdomain(request.host)
end
# Make domain available to all views too
helper_method :domain
end
# where you need it (controller or view)
user.home_url(domain)
If there is such a thing as a canonical user home URL, I would make a configurable default domain (e.g. YourApp.domain) that you can use if you call User#home_url without arguments. This allows you to construct a home URL in places where, conceptually, the "current domain" does not exist.
While molf's answer is good, it did not solve my specific problem as there were some instances where other models needed to call User#home_url, and so there would be a lot of methods I'd have to update in order to pass along the domain.
Instead, I took inspiration from his last paragraph and added a base_domain variable to my app's config class, which is the set in a before_filter in ApplicationController:
module App
class << self
attr_accessor :base_domain
end
end
class ApplicationController < ActionController::Base
before_filter :set_base_domain
def set_base_domain
App.base_domain = SubdomainFu.host_without_subdomain(request.host)
end
end
And thus, when I need to get the domain in a model, I can just use App.base_domain.