-
Star
(285)
You must be signed in to star a gist -
Fork
(36)
You must be signed in to fork a gist
-
-
Save dhh/10022098 to your computer and use it in GitHub Desktop.
| # config/routes.rb | |
| resources :documents do | |
| scope module: 'documents' do | |
| resources :versions do | |
| post :restore, on: :member | |
| end | |
| resource :lock | |
| end | |
| end | |
| # app/controllers/documents_controller.rb | |
| class DocumentsController < ApplicationController | |
| include ProjectScoped | |
| def index | |
| @documents = @project.documents | |
| end | |
| def show | |
| @document = @project.documents.find(params[:id]) | |
| end | |
| def new | |
| @document = Document.new | |
| end | |
| def create | |
| @document = @project.documents.create! document_params.merge(creator: current_person) | |
| end | |
| end | |
| # app/controllers/documents/locks_controller.rb | |
| module Documents | |
| class LocksController < ApplicationController | |
| include DocumentScoped, ProjectScoped | |
| def update | |
| @document.lock!(current_person) | |
| end | |
| def destroy | |
| @document.unlock!(current_person) | |
| end | |
| end | |
| end | |
| # app/controllers/documents/versions_controller.rb | |
| module Documents | |
| class VersionsController < ApplicationController | |
| include DocumentScoped, ProjectScoped | |
| before_action :set_version | |
| def show | |
| end | |
| def restore | |
| @document.restore!(@version) | |
| end | |
| private | |
| def set_version | |
| @version = @document.versions.find(params[:id]) | |
| end | |
| end | |
| end | |
| # app/controllers/concerns/document_scoped.rb | |
| module DocumentScoped | |
| extend ActiveSupport::Concern | |
| included do | |
| before_action :set_document | |
| end | |
| private | |
| def set_document | |
| @document = @project.documents.find(params[:document_id]) | |
| end | |
| end |
@nambrot, yeah, same concept. It matters because ProjectScoped must be loaded first, since DocumentScoped depends on it. It's a Ruby quirk that #include loads things in reverse order, so "include DocumentScoped, ProjectScoped" will load ProjectScoped first, then DocumentScoped.
@mcmorgan, our code in Basecamp started this way, so don't have the before. Wouldn't be hard to recreate though.
@atrauzzi, when they diverge, you can't reuse the logic anyway. Per definition.
@dhh you can simplify the routes by wrapping the nested resources with a scope, e.g:
resources :documents do
scope module: 'documents' do
resources :versions do
post :restore, on: :member
end
resource :lock
end
end@dhh Re: DocumentScoped / ProjectScoped order, is it a Ruby quirk or ActiveSupport::Concern's? I thought ActiveSupport::Concern saves dependencies in an Array and we could reverse it if that's what we want. Was there any reason that we don't do it that way?
@pixeltrix, yes of course. I've updated the gist to reflect that. Definitely nicer!
@kenn, that's a Ruby quirk. Concern is a module itself, it doesn't change how #include operates.
@dhh Wouldn't including ProjectScoped in DocumentScoped make the module dependency explicit, and thus allow you to remove ProjectScoped's inclusion from Documents::VersionsController and Documents::LocksController, avoiding the module ordering quirk entirely?
Also, any particular reason why you call Document.new here, instead of @project.documents.build?
@dhh would you then apply this namespacing also for models?
So document's versions would go to document folder as version.rb with class Document::Version? Same thing with locks to lock.rb to document folder with class Document::Lock.
Final tree structure would be then
models/document/version.rb
models/document/lock
class Document::Version
# code here
endor wrap those classes in plural module?
models/documents/version.rb
models/documents/lock
module Documents
class Version
# code here
end
end
I assume ProjectScoped works similarly to DocumentScoped. In that case, does it matter in which order you include DocumentScoped/ProjectScoped?