How to submit the rails3.1 engine form rendered inside the main app to be submitted to the engine's controller? - ruby-on-rails-3

I'm building a new rails3.1 engine for commentable functionality.
I created the engine, generated the resource named Comment.
The engine's config/routes.rb has:
Kurakani::Engine.routes.draw do
resources :comments
end
The spec/dummy rails app has a resource named Post and its routes has:
Rails.application.routes.draw do
resources :posts do
resources :comments
end
mount Kurakani::Engine => "/kurakani"
end
I've setup the associations between the engine's Comment model and dummy rails app's Post model.
Then inside the spec/dummy rails app, I've rendered out the comment form in the show template of the Post.
The form also gets generated with its action path to post/1/comments.
When I run the spec, I think it tries to search for the controller inside the spec/dummy app itself instead of submitting to the engine's app/controllers/kurakani/comments_controller.rb, so I get the following error when I run the spec.
$ bundle exec rspec spec/integration/comments_spec.rb ruby-1.9.2-p180
No examples matched {:focus=>true}. Running all.
Run filtered excluding {:exclude=>true}
/Users/millisami/gitcodes/kurakani/app/views/kurakani/comments/_form.html.erb:3:in `___sers_millisami_gitcodes_kurakani_app_views_kurakani_comments__form_html_erb___1787449207373257052_2189921740'
F
Failures:
1) Kuraki::Comment authenticated user creating a new comment
Failure/Error: click_button "Create"
ActionController::RoutingError:
uninitialized constant CommentsController
# ./spec/integration/comments_spec.rb:29:in `block (3 levels) in <top (required)>'
How can I specify the comment to be submitted to the engine's comments_controller.rb instead of the spec/dummy app??
If I couldn't make the problem clear, I've pushed the repo at https://github.com/millisami/kurakani

The problem is that your route for the form that is generated goes to /posts/:post_id/comments, which is the route defined in your application, but not your engine. Your engine only defines this route:
resources :comments
This is (almost) working because the engine sees that the application has the route which matches to CommentsController, it's just that there's no CommentsController for it to go to.
I downloaded the application from GitHub and had a play around and changed the form_for in app/views/kurakani/comments/_form.html.erb to be this:
form_for(Kurakani::Comment.new, :url => kurakani.comments_path)
This makes the test pass, but I am not sure if it actually gives you what you want. You're probably going to want to play around with that URL part yourself.
What's happening here is that this view is rendered by the main application using the kurakani_list helper in spec/dummy/app/posts/show.html.erb, meaning that any URL helper that you reference (directly or indirectly) will point to the application and not to the engine, like I think you want it to.
So in order to tell Rails what the true route is that we want our form to go to we must specify the :url option and tell it we want to go to kurakani.comments_path, rather than the comments_path which may be defined by the application.
If we wanted to do the reverse (reference an application route from within an engine), we would use main_app rather than kurakani.

Related

Why Rails Controller Action Method Requires Parameter

It's a brand new project. Here's the exact commands I've run:
rails new MyProject
bundle install
rails generate controller Image
I've added this one route:
root :to => "image#process"
I've added this function to the ImageController (image_controller.rb)
def process
render :nothing => true
end
And finally I've removed the default index.html. When I run the project, it has an error saying process expects 0 parameters, not 1. So I modify the method to tell me what parameter is trying to be sent to process.
def process(arg)
p arg
render :nothing => true
end
The string "process" is printed to the screen. I've done several Rails projects before and never encountered this. Did I miss a step somewhere? Is this something new in Rails 3.0.10? Or maybe caused by Ruby 1.9.2? I think I usually use 1.8.7.
You can not name an action as process, this is an internal method for rails controllers, name it something else.
There's a bunch of other names you can't use for controller actions like render, params, request. Unfortunately there isn't a list of these things.
For future reference, in case you aren't using it, you can view all internal methods and classes here: ruby doc with nav on top right
Helps me when picking names.

Engine routes in Application Controller

I have a before_filter hook in my main app's application controller that does something like: (It doesn't just put a link in the flash, there is a message, but it isn't relevant to the question, it just accesses the route in the method)
class ApplicationController < ActionController::Base
before_filter :set_link
def set_link
flash[:notice] = items_path
end
end
This works fine for the app, however when I go into the controllers for an engine I made I get the exception
No route matches {:controller=>"items", :action=>"index"}
I understand that when in the engine, the routes helpers are for the engine unless prefixed with main_app
So changing the method in the application controller to
def set_link
flash[:notice] = main_app.items_path
end
Gets rid of the exception but I really don't want to have to do that. Is there another solution to getting the engine to recognize the main_app routes?
EDIT:
This also happens if the application layout calls path helpers. So if the engine is designed to integrated into the main_app's layout then this issue will crop there up too.
Mountable engines are designed to work like this, that is isolate the main app routes and the engine routes.
If you want the two sets of routes to be merged, you can use a non-isolated engine. The first step is removing the isolated_namespace method call in your engine definition:
module MyEngine
class Engine < Rails::Engine
isolate_namespace MyEngine # remove this line
end
end
The second step is to convert your routes in my_engine/config/routes.rb, you should go from this:
MyEngine::Engine.routes.draw do
# stuff that routes things
end
to this:
Rails.application.routes.draw do
# stuff that routes things
end
and remove the mount method call in your application's routes:
App::Application.routes.draw do
mount MyEngine::Engine => "/engine" # remove this line
end
The main advantages of doing it this way would be:
No need to monkey-patch rails. I know devise does this, but this could be a leftover from the days when engines didn't exist in rails.
No need to mount the engine in the application routes. On the other hand, this could backfire if you'd like to control more precisely the insertion point as all you engine routes would be called after (or before, I don't have the answer to this question) your main routes.
If you're looking for documentation on engines, the rails docs for the Engine class are a pretty good starting point. I'd strongly recommend that you read them (in case you haven't yet) if you're interested in the subject.
I figured out how to do this. The problems lies within the isolated namespace. In order to integrate the engine with the app and share the same layout (which may have path helpers from the main app) I did this:
Firstly I removed config/routes.rb from the engine
Then I removed the isolate_namespace from the engine class
module MyEngine
class Engine < Rails::Engine
- isolate_namespace MyEngine
end
end
end
I added a file that was loaded in the engine:
module ActionDispatch::Routing
class Mapper
def mount_my_engine_at(mount_location)
scope mount_location do
#Declare all your routes here
end
end
end
end
Finally, in the main app's config/routes.rb instead of 'mount'ing the engine, you can call your method
mount_my_engine_at "mount_location"
This will basically 'mount' your engine as part of the main app instead of being isolated from it. It is similar to how Devise does it too.
You can keep the isolate_namespace.
In your engine routes.rb
MyEngine::Engine.routes.draw do
...
root to: "something#index"
end
Rails.application.routes.draw do
get "something", to: "my_engine/something#index"
end
And then in the main app routes.rb
Rails.application.routes.draw do
mount MyEngine::Engine => "/anything_you_want"
root to: "main#index"
end
This way you can choose what routes you want to expose (and which you do not)
You can keep the isolate_namespace, as strongly recommended by Rails Engine guide, and do this:
# inside your main_app's config/routes.rb
Rails.application.routes.draw do
root to: 'home#index'
mount MyEngine::Engine, at: "/"
end
# inside your engine's controller
module MyEngine
class SomeController << ::ApplicationController
# include main_app's route helpers
helper Rails.application.routes.url_helpers
end
end
And inside your gem, make sure all the url helpers are prefixed with the correct routing proxy method (e.g. my_engine.pages_path).
Your main_app's layout and engine's controller will route and link to those url helpers correctly to the main app. You don't have to add "main_app" prefix anywhere to the main app. The only downside is you're mounting your engine's routes at main_app's root path, which could collide with any routes by the same name. This is expected anyway if you were to do non-isolate_namespace.
The easiest way is to draw the routes in both the main app, and the engine, so that they are accessible to both:
[MyEngine::Engine, App::Application].each do |app|
app.routes.draw do
# Declare all your routes here
end
end

Rails 3 - index action not loading by default on controller

Now i have a fresh rails 3 install running over rvm 1.9.2
I generated a controller using the follow instruction:
rails generate controller blog index
The output is
create app/controllers/blog_controller.rb
route get "blog/index"
invoke erb
create app/views/blog
create app/views/blog/index.html.erb
invoke test_unit
create test/functional/blog_controller_test.rb
invoke helper
create app/helpers/blog_helper.rb
invoke test_unit
create test/unit/helpers/blog_helper_test.rb
but in browser when i try to get to http://localhost:3000/blog i get:
No route matches "/blog"
but if i type http://localhost:3000/blog/index
it renders the index view.
doesn't it works like Rails 2? where i get to the index view by default with just putting the controller name on the url ?
thanks.
If you look into routes.rb you'll see
get "/blog/index" => "blog#index"
So just remove it with
get "/blog" => "blog#index"
or you can use resources here.
But only question: why do you use singular form? It is nonsensical to call index to singular noun. You should use or "blog#show" as a resource or "blogs#index" as a resources.
Conventions in Rails is a kind of basement. Don't break them if you can follow them
For rails 3:
match '/blog', :controller => 'blog', :action => 'index'
Rails generate does not generate resources for your controller by default. You specified one action for your controller, 'index', so in your case you end up with this in config/routes.rb:
Blog::Application.routes.draw do
get "blog/index"
The simplest thing to do would be to change this to:
get "blog", :to => 'blog#index'
ian.
This is a guess, based on my experience with Rails 2, but here's what I think is happening:
If you'd generated your controller with the scaffold option (that's still in Rails 3, right?), it would have created a model in addition to your controller, and added the corresponding routes via a call to map.resources (or Rails 3 equivalent) - this last bit is what gives you the /models routes you're expecting.
But since you just generated the controller, no model was created, and thus Rails doesn't put in a map.resources statement in routes.rb - map.resources really only makes sense when there's a model underlying your controller. In fact, I don't think it adds any special routes when you generate a controller; you're getting to your index by one of the default routes: /:controller/:action.
So if you want to get to your index from /blog, you'll have to add the route yourself. Luckily, it should be a one-liner.
Hope this helps!
PS: And if you're paranoid, you'll want to disable those default routes before you go to production - they allow GET requests to trigger actions that change your database (e.g. GET:/blog/destroy), opening you up to Cross-Site Request Forgery attacks.
Add this to your routes.rb file match ':controller(/:action(/:id))(.:format)'
It's better if you add it at the bottom of the routes.rb file.
The problem with this approach is that it will make all your actions available through get request. So be careful with that.
Instead of manual routing you can go to /app/controllers/application_controller.rb and add a blank index method
def index
end
make sure your generated controller extends the application controller, and boom all your generated controllers do what you want
Tested on Rails 3.2.*

Rails app doesn't see my views

I've on a while on rails now and here's the problem I've been having on and on:
When I create a controller through:
"rails generate controller ControllerName ViewName"
I get everything working as I want but if for some reason I create the controller through:
"rails generate controller ControllerName"
and then just add ViewName.html.erb to the folder inside views that has the same name as my controller things would go wrong.
So the concrete case is me writing:
rails generate controller Subjects list show.
Which creates for me:
1.controllers>subjects_controller.rb
2.views>subjects>list.html.erb
3.views>subjects>show.html.erb
So this whole thing works fine.But as I already said if I need another view; let's say "new" I just add "new.html.erb" next to the other *.html.erb files and an action:
def new
end
to my subjects_controller.rb then it won't work.
The two previous views would keep working but any other "*html.erb" created outside the command line wouldn't.
Is there anywhere else where info about views is being stored?.
I'm a Windows 7 user (32 bit).Rails version=3.0.3. WebServer=WEBrick.
Text editor = E-TextEditor
This is most likely caused by your routes not being correctly configured. So it would be helpful to see the content of your routes.rb
In your case I think the best way to configure the routes is to use the resources mapping:
resources :subjects
This will by default create routing for the standard RESTful actions :index, :show, :edit, :update, :new, :create and :destroy.
For more detailed information about the routing, I would recommend Rails Routing from the Outside In

Rspec2 and Rails 3 - View Spec Routing for Nested Resources

I have a nested resource, setup like this:
resources :chickens do
resources :eggs
end
The views for the EggsController are under views/eggs, but:
describe "eggs/index.html.erb" do
gives a "No route matches {:action => "create", :controller => "eggs"} error on calling render. How do I get RSpec to find the correct (nested) route for view specs?
As a side note, is there any way to specify a nested resource to the Rails 3 controller scaffold generator?
The test looks ok to me...
By any chance do you have a form on your eggs/index.html.erb for creating new eggs that might not yet be wired up correctly? It seems it may be trying to render the index view but failing because the view is trying build a route that doesn't exist? You'd want to make sure that the form is using the correct nested resource route. Does the view render when you load it up in the browser?