I'm currently developing a couple of modules to reuse code from my controllers on a Rails 3 using InheritedResources (last version) app.
My idea is to have some behavior that should be run after a successful create or update of the resource but, except from redeclaring the "create" or "update" actions, I'm not to sure on how to tackle this.
I'm currently using something like
module SessionStorable
extend ActiveSupport::Concern
include Base
included do
before_filter :setup_resource, :only => :new
after_filter :reset_session_resource_id, :only => [:create, :update]
end
# ....
end
I have a particular resource setup I use which, among other things, adds the id of the resource to the session. After the resource has being successfully saved to the BD, I'd like to remove it's id from the session and that is what the after_filter does.
I've deal with it so far by also saving the updated_at info onto the session and comparing to see if the model was updated (if so, it should have been successfully) and run the method.
However, I'm not to happy with it (kinda hacky) and also I'm planning on having other modules that will work with resources also after they've been updated and wouldn't want to use the same approach twice.
Is there a hook on IR that I should be using? Or any other ideas on how to proceed?
I've solved it by using the "object.errors.empty?" condition. If there are no errors on the object after the create or update action, it should be safe to assume that the model was saved properly, and thus running the code would be fine.
Maybe you could use an inheritance based approach instead:
class BaseController < InheritedResources::Base
before_filter :setup_resource, :only => :new
after_filter :reset_session_resource_id, :only => [:create, :update]
# ...
end
class YourController < BaseController
# ...
end
I'm sorry to use the answer feature for a comment, but since I can not do it under your answer I see no other option.
"object.errors.empty?" condition. If there are no errors on the object after the create or update action, it should be safe to assume that the model was saved properly
I think this is not always true, let me put you and example:
class Project < ActiveRecord:Base
has_many :members
# ...
end
Imagine you got a form for the project where you could also create members for it (nested forms). Errors in the creation of the associated members will make de project object invalid, but the project instance will return true for the method errors.empty?
Related
I am looking to create a filtering system where I can apply a set of sanity checks to a subset of routes on a REST API. What is the most railsy way to create the mapping of routes to functions which need to be called on that route? I am looking to create a one to many mapping where a function can be mapped to more than one route. Currently I am creating a module where I am mapping the routes and checking the request after it comes in. Is there a native rails way to do this?
Don't know about best way but one way would be to create a controller with this sanity check and have your controllers inherit from it.
class SanityController < ApplicationController
before_action :sanity_check
def sanity_check
# some world class sanity checks
end
end
class OtherController < SanityController
end
You can also skip certain actions if needed
class OtherController < SanityController
skip_before_action :sanity_check, only: :index
end
Currently I have a route called requests that may have GET/POST endpoints. But another requirement is to achieve the following format: /api/requests/sync.
I tried the following in routes.rb:
Rails.application.routes.draw do
resources :requests do
get "sync"
end
end
But this gives me the following format:
/requests/:request_id/sync
How can I create a sub-route as requests/sync without having it as a sub-route of /:request_id/sync?
Check out the guide. Specifically, collection routes. You'll do something like:
Rails.application.routes.draw do
resources :requests do
collection do
get "sync"
end
end
end
Which will give you requests/sync
To pick up on the sync_controller question...
Personally, not knowing much about what you're actually up to, I would keep sync as an action on the requests_controller. Something like:
class RequestsController < ApplicationController
...
def sync
...
end
...
end
While sync is not one of the standard RESTful actions, it seems more natural to me than creating a new controller. In general, but not always, I think of controllers as being noun-oriented (e.g., 'request', in your case) and actions being verb-oriented. "Sync" seems way more verb-y to me than noun-y.
You could do something along the lines of what Cyzanfar suggests. But, I would suggest you ask yourself:
Do you need all the standard actions for your would-be sync_controller?
Is there some meaningful reason to inherit from Requests::RequestsController?
Do you even need Requests::RequestsControler or could you just do RequestsController and then have Requests::SyncController inherit from RequestsController (which seems less tortured to me)?
And probably other important questions I'm not thinking about on-the-fly.
Here is another way to achieve this with namespacing your controllers so that you can have a distinct controller for sync and requests where the request controller will act as the parent (base) controller.
routes.rb
namespace :requests do
resources :sync
end
requests/requests_controller.rb
class Requests::RequestsController < ApplicationController
end
requests/sync_controller.rb
class Requests::SyncController < Requests::RequestsController
end
Now you'll have the nested CRUD paths under requests
/requests/sync/new
/requests/sync/index
/requests/sync/create
...
I would like to ask if i should keep the empty methods in my controller (Its a question about code style):
before_action :set_project, only: [:show,:new]
def show
end
def new
end
Should i keep it like this or simpy remove show and new action
class ProjectController < ApplicationController
before_action :set_project
def index
#indexaction
end
def create
#createaction
end
is it more Railish way? Rails Styleguide doesnt indicate any sollution to it, only that:
def show
end
is better than
def show; end
If you're not defining any data in those methods, you can remove them.
Rendering By Default:
Rails automatically infers the view from action names in routes.
If you're not defining data (only to be done in the controller), you'll can rely on the view being loaded without the action being present:
You've heard that Rails promotes "convention over configuration". Default rendering is an excellent example of this. By default, controllers in Rails automatically render views with names that correspond to valid routes. For example, if you have this code in your BooksController class:
class BooksController < ApplicationController
end
And the following in your routes file:
resources :books
And you have a view file app/views/books/index.html.erb:
<h1>Books are coming soon!</h1>
Rails will automatically render app/views/books/index.html.erb when you navigate to /books and you will see "Books are coming soon!" on your screen.
Good ref: Rails doesn't need index method in controller defined?
If you want to use those methods in future, keep those methods, else remove them. There will not be any problem even if they are kept.
Also remove the routes to those methods if they are created and you dont want to use them. Code should be as simple as it can.
If it's truly about style first keep your comments above, so that if you ever run rdoc or generate docs, they take whatever comments come before the def show and use that to build out the paragraph describing what this method "does".
Example
class ProjectController < ApplicationController
before_action :set_project
#indexaction is awesome and will be used to do stuff like look at all the projects
def index
end
# will hopefully create your project
def create
end
Yeah but don't keep it if it isn't used...
If routes aren't used, don't have them lying around. By default you get things like blah.com/projects/1.json and if you haven't locked this down (or even if you have and a user is inside the app) they could easily get the json result, assuming you left everything there for later. Let alone if you have a before_filter that does 'stuff' to projects on load`.
commit it once into git and then delete it and commit again. If you ever need to reinstate it, at least github/bitbucket history or git history if you are sadistic, will have it to copy and paste.
Does around_action in ApplicationController::Base include before_action and after_action? I understand that around_action wraps around the specified action, but would like to know if it also wraps around the before, and after callbacks associated with that action.
For instance, let's look at a code modified from Rails documentation:
class ChangesController < ApplicationController
before_action :some_callback, only: show
around_action :wrap_in_transaction, only: :show
...
private
def wrap_in_transaction
yield unless true
end
end
Will some_callback ever be executed?
Yes, some_callback will be executed. These methods are completely unaware of one another, and will be performed in the order they are written.
It may interest you to see the code that rails uses to do this. You can find it at https://github.com/rails/rails/blob/cdaab2c479c819b04cc72a97c52b804832365cef/actionpack/lib/abstract_controller/callbacks.rb#L180. You will notice that they both call the _insert_callbacks method (https://github.com/rails/rails/blob/cdaab2c479c819b04cc72a97c52b804832365cef/actionpack/lib/abstract_controller/callbacks.rb#L87).
Also, why not just try it using console output or something? This type of thing should be very easy to verify with a quick trial (which is, I suspect, the reason behind this question's downvote).
In my previous learning projects I always used a single controller, but now I wonder if that is good practice or even always possible.
In all RESTful Rails tutorials the controllers have a show, an edit and an index view. If an authorized user is logged on, the edit view becomes available and the index view shows additional data manipulation controls, like a delete button or a link to the edit view.
Now I have a Rails application which falls exactly into this pattern, but the index view is not reusable:
The normal user sees a flashy index page with lots of pictures, complex layout, no Javascript requirement, ...
The Admin user index has a completly different minimalistic design, jQuery table and lots of additional data, ...
Now I'm not sure how to handle this case. I can think of the following:
Single controller, single view: The view is split into two large blocks/partials using an if statement.
Single controller, two views: index and index_admin.
Two different controllers: BookController and BookAdminController
None of these solutions seems perfect, but for now I'm inclined to use the 3rd option.
What's the preferred way to do this?
I asked myself this question almost every time when I get a new project. I usually choose one of the two solutions:
1). Single Controller, Single View
I almost never choose this solution now a days unless the project is really simple, and only one or two types of users. If you get multiple user types it is better to use solution # 2. Although this solution may be appealing because you think you save yourself some time by writing less code, but in the end, your controller and view will grow in complexity. Not to mention all the edge cases you have to consider. This usually means bugs.
My company once had to rescue a failed project, it had 3 user types. (admin, business, and member). They used solution #1. The code was in a horrible condition, ( and that's why we were asked to rescue this project) We were jokingly saying it is not MVC, it was MMM. (Model-Model-Model) This is because business logic was not properly extracted and put into models, but spread in controllers and views as well.
2). Multiple Controller, Multiple Views
I use this solution more and more these days. I usually namespace the controllers with user types. For example:
In "app/controllers"
class BookController < ApplicationController
end
and in "app/controllers/admin"
class Admin::BookController < Admin::BaseController
end
I only need to consider regular users when I fill in BookController, and only need to consider admin users when I fill in Admin::BookController
I'm not sure if there are better ways, but this is what I learned from a dozen projects I've done so far...
What I do in such a situation changed quite a bit lately. The current approach is as follows:
I separate controllers based on access requirements. This gives me a clear
mental model and a very easy way to check for access control (and test it).
I even go as far as to separate 'your own access' to models into separate
controller. I also usually keep the controller's name just put it into a
separate namespace.
This approach is also makes it very easy to use standard restuful controller
implementations like InheritedResources.
Note that you can reuse many of the views if same functionality is required for
different kinds of access.
So I'd have something like this:
### lets start with routes
# this is basically guest access level. you can only list it and see details
map.resources :books, :only => [:index, :show]
namespace :my do |my|
# this will require at least login.
# index and show will be basically same as at the guest level. we can reuse the views
my.resources :books
end
namespace :admin do |admin|
# this will require admin login
admin.resources :books
end
# now the controllers
# this might be just enough of a controller code :). the rest goes into model.
class BooksController < InheritedResources::Base
actions :index, :show
end
module My
class BooksController < InheritedResources::Base
before_filter :require_user
protected
def begin_of_association_chain
# this will force resources to be found on current_user.books.
# so if you go to /my/books/123 and book 123 is not your book you will get 404
current_user
end
end
end
module Admin
class BooksController < InheritedResources::Base
before_filter :require_admin
# this controller is essentially unrestricted. you can get/edit/update any book
# and you can have separate view template for index too
end
end
## Views
# well, this is the part that I like the least in this approach, but
# I think the good outweight the bad.
# I see 2 ways to reuse the views w/o writing lots of ugly customization code for the InheritedResources
# 1) render 'parent' views inside templates. i.e. like:
# my/books/index.html.haml:
!= render :file => "/books/index"
# or just link the templates on the filesystem level with symlinks.
# (or if all of them are the same you can link the directory)
Use two current if there are two modules 1] Admin 2] User
Say
class BookUserController < ApplicationController
#Here layout is used will be of layouts/application.html.erb
#Here all the methods which user can will be present
end
class BookAdminController < ApplicationController
layout 'admin' #here we set the layout is layouts/admin.html.erb
end
IF Only one page you want to show admin you can use single controller and two methods
class BookUserController < ApplicationController
layout 'admin', :only=>['index_admin']
def index_admin
end
def index
end
end
OR
class BookUserController < ApplicationController
def index_admin
render :action=>'index_admin', :layout => false
end
def index
end
end
When I need a clearly separated administration area, I tend to go for a solution with two controllers for one resource. An Admin::BooksController in the admin/ sub directory for the admin interface with all actions and a public BooksController that only has index and show methods depending on needs.