Sharing routing concerns between rails engines - ruby-on-rails-4

I have two different isolated mountable rails engines; one named Core, other as Finance;
The core engine has a comment resource and routing concern like;
Core::Engine.routes.draw do
concern :commentable do
resources :comments
end
end
And the Finance engine has a invoice model;
Finance::Engine.routes.draw do
resources :invoices, concerns: :commentable
end
Both these engines added main app's Gemfile, and routes.rb file like below;
Gemfile;
gem 'core', path: "../core"
gem 'finance', path: "../finance"
routes.rb;
mount Core::Engine, at: "/"
mount Finance::Engine, at: "/"
At the finance gem; invoice show.erb has comment form like below;
<%= form_for [#invoice, #comment] %>
but it seems rails 4 can't share routing concerns between engines. I have found so many questions on stackoverflow, but still can't find a good solution.
Maybe this not avaliable in rails engines; is there any way two handle this.
Thanks.

Struggling a little bit with Ryan's answer (how to correctly include it where?), I came up with the following solution without explicitly using concerns but still sharing routes:
# 1. Define a new method for your concern in a module,
# maybe in `lib/commentable_concern.rb`
module CommentableConcern
def commentable_concern
resources :comments
end
end
# 2. Include the module it into ActionDispatch::Routing::Mapper to make
# the methods available everywhere, maybe in an initializer
ActionDispatch::Routing::Mapper.include CommentableConcern
# 3. Call the new method where you want
# (this way `concerns: []` does not work) obviously
Finance::Engine.routes.draw do
resources :invoices do
commentable_concern
end
end
This monkey patches ActionDispatch::Routing::Mapper, but as long as you only define new methods (and do not touch existing ones) it should be safe.

I don't think that it's possible to do that because each engine is its own container and you can't reach across between engines to do what you're attempting to do.
Instead, define a module which you can include in both contexts which define the same concern:
module CommentableConcern
def self.included(base)
base.instance_eval do
concern :commentable do
resources :comments
end
end
end
end
I think this is the only way you can accomplish that.

Related

rails 4 and thoughtbot/clearance - adding fields to the user model

I am a relative newbie so would really appreciate any assistance.
I'm using Rails 4.2, with the Clearance gem for authentication. I'm hoping someone could describe the best practise for over-riding the controllers to include custom attributes on the sign_up form.
I've read a lot of suggestions with varied advice, many of which are from previous versions of rails that do not use strong_parameters.
If anyone could provide a brief breakdown of the methods I need to be over-riding (user_params/user_from_params/etc) I would be most grateful. I can get things functioning by defining a new 'new' method that just includes #user = User.new, and the a new 'user_params' method using .permit, but I'm concerned about the default code I'm bypassing in user_from_params.
Any advice on best practise here would be fantastic!
Thanks
First, extend Clearance::UsersController and override #user_params to permit the new attribute(s):
# app/controllers/users_controller.rb
class UsersController < Clearance::UsersController
private
def user_params
params[:user].permit(:email, :password, :your_custom_attribute)
end
end
Then, update your routes file to use your new controller:
# config/routes.rb
resources :users, controller: :users, only: :create
I'm working when I have time on updating the docs for Clearance, but right now the simplest thing to do is to inspect the Clearance users controller to see the various methods that can be overridden.
The default implementation of user_from_params is a bit odd now because Clearance 1.x still supports Rails 3.x, so it was written for a time when we used attr_accessible rather than strong parameters.
I'd probably do something like:
def user_params
# Use strong params to do what you need
end
def user_from_params
User.new(user_params)
end
You may need to massage user_params a bit so that whatever you do there is still valid for the new action, as user_from_params is still used there. I've opened an issue in Clearance to see if we can improve that aspect before 2.0.

Ruby on Rails choosing wrong controller action

Today I came across some strange (and very inconvenient) Ruby on Rails behavior that even persistent combing of the net did not yield a satisfying answer to.
Note: I translated the method and route names to be easier to read in English, and hope I did not introduce any inconsistencies.
Situation
Environment
Ruby on Rails 4.2.0 executing under Ruby 2.0 (also tested under Ruby 2.2.0)
Relevant Code
consider a controller with these actions, among others:
class AssignmentsController < ApplicationController
def update
...
end
def takeover_confirmation
...
end
end
routes.rb
Since I use a lot of manually defined routes, I did not use resources in routes.rb. The routes in question are defined as follows:
...
post 'assignments/:id' => 'assignments#update', as: 'assignment'
post 'assignments/takeover_confirmation' => 'assignments#takeover_confirmation'
...
The relevant output of rake routes:
assignment POST /assignments/:id(.:format) assignments#update
assignments_takeover_confirmation POST /assignments/takeover_confirmation(.:format) assignments#takeover_confirmation
Problem
When I do a POST to the assignments_takeover_confirmation_path, rails routes it to the update method instead. Server log:
Started POST "/assignments/takeover_confirmation" for ::1 at ...
Processing by AssignmentsController#update as HTML
Mitigation
If I put the update route definition after the takeover_confirmation one, it works as intended (didn't check a POST to update though).
Furthermore, after writing all this I found out I used the wrong request type for the update method in routes.rb (POST instead of PATCH). Doing this in routes.rb does indeed solve my problem:
patch 'assignments/:id' => 'assignments#update', as: 'assignment'
However, even when defining it as POST, Rails should not direct a POST request to the existing path "/assignments/takeover_confirmation" to a completely different action, should it?
I fear the next time I use two POST routes for the same controller it will do the same thing again.
It seems I have a severe misconception of Rails routing, but cannot lay my finger on it...
Edit: Solution
As katafrakt explained, the above request to /assignments/takeover_confirmation matched the route assignments/:id because Rails interpreted the "takeover_confirmation" part as string and used it for the :id parameter. Thus, this is perfectly expected behavior.
Working Example
For the sake of completeness, here is a working (if minimalistic) route-definition that does as it should, inspired by Chris's comment:
resources :assignments do
collection do
post 'takeover_confirmation'
end
end
In this example, only my manually created route is explicitly defined. The routes for update, show, etc. (that I defined manually at first) are now implicitly defined by resources: :assignments.
Corresponding excerpt from rake routes:
...
takeover_confirmation_assignments POST /assignments/takeover_confirmation(.:format) assignments#takeover_confirmation
...
assignment GET /assignments/:id(.:format) assignments#show
PATCH /assignments/:id(.:format) assignments#update
PUT /assignments/:id(.:format) assignments#update
DELETE /assignments/:id(.:format) assignments#destroy
....
Thanks for the help!
However, even when defining it as POST, Rails should not direct a POST request to the existing path "/assignments/takeover_confirmation" to a completely different action, should it?
It should. Rails routing is matched in exact same order as defined in routes.rb file (from top to bottom). So if it matches a certain rule (and /assignments/takeover_confirmation matches assignments/:id rule) it stops processing the routing.
This behaviour is simple and efficient. I imagine that any kind of "smart" matching the best route would result in cumbersome and unexpected results.
BTW that's why catch-all route used to be defined at the very bottom of routing file.

Rails4 - Maintain Context in Deeply Nested Hierarchy

I have a 5 to 10 level deep model hierarchy in my Rails Project.
Example (this is not the actual hierarchy)
(has_many)
-- Project
-- SubProject
-- Blog
-- Post
-- Comment
-- Rating
Each Project has many Sub Projects, which has many Blogs, which has many Posts, and so forth.
So, in practice I want to be able to (for example) see all Posts of a specific Blog, see all Ratings of a specific Comment, and so forth. But I never want to loose the context of a earlier hierarchy level (e.g. Project).
Basically, the solution seems to be
resource :projects do
resource :sub_projects do
resource :blogs do
resource posts do
resource :comments do
resource :ratings do
# might go even deeper...
end
end
end
end
end
end
So I can navingate to e.g. projects/2/sub_projects/3/blogs/1/posts/5/comments/2/ratings
Is there a better solution?
Besides the ugly route path I think it will be painful to work with paths like
new_project_sub_project_blog_post_comment_rating_path(#project, #sub_project, #blog, #post, #comment, #rating)

Helpers in Rails 4

I make an application writed in Rails, it's growing fast and I learning with it. But I'm don't understand about helpers.
application_helper.rb
module ApplicationHelper
# This file it's empty
end
users_helper.rb
module UsersHelper
def avatar
# Do something
end
end
customer_helper.rb
module CustomerHelper
# This file it's empty
end
Why in any customer's view can call avatar helper method on user helper module?
Then, why separate helpers in many files?
Thanks in advance.
P.S: Rails' version 4.
Because all helpers are included in all controllers, by default. The separate files are really just for logical separation in this scenario. You can change that behaviour though:
By default, each controller will include all helpers.
In previous versions of Rails the controller will include a helper
whose name matches that of the controller, e.g., MyController will
automatically include MyHelper. To return old behavior set
config.action_controller.include_all_helpers to false.
http://api.rubyonrails.org/classes/ActionController/Helpers.html
To add to Mike Campbell's answer:
Framework
As magic as it is, Rails is a set of files which are called
sequentially
These files hold classes, methods, etc; but they're still files.
And that means that when you run an action through Rails, it loads up
a series of other dependent files to help it run (that's what a
framework is)
The design of Rails is such that your helper methods are all
loaded each time you run an action. I don't know why, but it helps
administer the methods for different areas of your app
So to answer your question, there's no real reason why the helpers are split up, at least with Rails 4

How to auto-reload changes in a Rails Engine?

I have a Rails 4.1.0 mountable engine. In the engine's application_helper.rb:
module MyEngine
module ApplicationHelper
def test123
"test123"
end
end
end
The method is in the dummy app's view general/index.html.erb view:
%<= test123 %>
This works. However, when I change the string returned by def test123 and refresh the browser, the new string is not displayed.
Of course, restarting the web server in the dummy app shows the new string.
So the question is, how to reload the engine's files without having to restart the web server?
PS. I am preferably looking for a way to do this using Rails itself, or a specific gem that solves this problem (but not the generic gems like Guard, Spork etc. although if all else fails, I will consider those too.)
PPS. There are similar questions on SO, but I have tried them all (even though they are for Rails 2.x, 3.x), and they have not worked for me.
You should explicitly require dependent helpers:
# engines/my_engine/app/controllers/my_engine/application_controller.rb
require_dependency "my_engine/application_helper" # This is a key point!
module MyEngine
class ApplicationController < ::ApplicationController
helper ApplicationHelper
...
you can use some thing like zeus which is helping a lot in watching project files for changes except in some cases when you change the configuration files it doesn't work and needs to be manually restarted.
but overall in most cases this works more than awesome