Ruby on Rails nested resource routing error - ruby-on-rails-3

I have a user, comment, and route model as shown:
class User < ActiveRecord::Base
has_many :routes, :dependent => :destroy
has_many :comments, :dependent => :destroy
end
class Route < ActiveRecord::Base
belongs_to :user
end
class Comment < ActiveRecord::Base
belongs_to :user
end
I have the routes.rb file nesting comments and routes within user as shown:
MyApp::Application.routes.draw do
resources :users do
resources :comments
resources :routes
end
When I run 'rake routes', the route to the Routes_controller index appears as so:
user_routes GET /users/:user_id/routes(.:format) {:action =>"index", :controller=>"routes"}
Yet for some reason when a user is signing in, I get a routing error saying that the routes controller cannot be found. This happens when the system is posting a new session in the session controller. I know that it attempts to sign in the user, but fails on the redirect. Any suggestions?
class SessionsController < ApplicationController
...
def create
user = User.authenticate(params[:session][:email],
params[:session][:password])
if user.nil?
flash.now[:error] = "Invalid email/password combination."
#title = "Sign in"
render 'new'
else
sign_in user
redirect_to user_routes_path
end
end
...
end
For some reason, the stack trace wasn't displayed when I redirect to user_routes_path, so I have it direct to root_path and the same thing happens. Here is the trace for that:
app/views/layouts/_header.html.erb:3:in
`_app_views_layouts__header_html_erb___917786942_46449696_315190'
app/views/layouts/application.html.erb:11:in
`_app_views_layouts_application_html_erb__423035099_46500948_0'

I will give it a try, after reading Fernandez: The rails 3 way about redirect_to.
When you look at the output from rake routes, you have the output:
user_routes GET /users/:user_id/routes(.:format) {:action =>"index", :controller=>"routes"}
The methods you may use to that route are:
user_routes_url: Full URL (with protocol and everything)
user_routes_path: Relative URL to the host
But your user_routes tells you another thing: the URL has to contain a user_id, and this user_id has to come from somewhere. So to call the different url and path methods, you have to look at the arguments:
users_path: no argument, shows all users
user_path(#user): one argument, because the information about the user is needed. Could be the user, or the user_id
`user_routes_path(#user): needs the user, so that all routes (index view) for one user could be shown.
So include in you source code in the controller:
...
else
sign_in user
redirect_to user_routes_path(user)
end
...
I don't understand the error message you have appended, but I think you should first correct the path call.

Related

Redirect to root_path after failed Devise user registration

I've read & attempted the solutions posted in numerous SO posts (i.e. here, here, and here) as well as Devise's answer on how to change the path after a failed registration, and Devise's RegistrationsController code, all to no avail.
Instead of doing the custom failure method in the /lib/ folder like most suggest, It seems like the easiest place to fix/override this would be in the RegistrationsController#create method at the bottom where it's:
else
clean_up_passwords resource
respond_with resource
end
It's (I assume) correctly responding with the user (i.e. redirecting them to root_path/users), but here's the tricky part: I create a nested model when my user registers, and that was quite difficult hacking into Devise for that. I'm afraid that if I go messing with the RegistrationsController#create method, that I'll break my perfectly-working nested models.
Someone also mentioned that the Devise solution didn't work for them, but then got it to work after changing a routing problem. I doubt that's the case with me, but here's my routes.rb file just in case:
devise_for :users, :controllers => { :registrations => "registrations" }
resources :users
resources :users do
resources :lockers
end
resources :lockers do
resources :products
end
resources :products
resources :lockups
match '/user/:id', :to => 'users#show', :as => :user
root :to => 'home#index'
I completely appreciate any help anyone can provide me. I'm still pretty new to Rails, but I'm always happy to learn.
EDIT: Thanks to Passionate, I've been trying to figure out how to change the logic in the final else block in the registrations controller. Here's what I've tried (along with my noob logic):
# Obviously cleared all the fields since it made no mention of resource
# redirect_to root_path
# Too many arguments since root_path takes 0
# redirect_to root_path resource
# Just bombed, something about a String somethingorother
# render root_path
# Heh, apparently call 'respond_with' and 'redirect_to' multiple times in one action
# respond_with resource
# redirect_to root_path
First you have to fix routes.rb file . Since you're using custom controller , you also have to customize devise_scope block . Change
get "signup", :to => "devise/registrations#new"
to
get "signup", :to => "registrations#new"
And, if you try to override the method and want to set the root_path after devise rails registration, you can do like
# app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
def create
build_resource
// The `build_resource` will build the users .
if resource.save
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_navigational_format?
sign_up(resource_name, resource)
respond_with resource, :location => after_sign_up_path_for(resource)
else
set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_navigational_format?
expire_session_data_after_sign_in!
respond_with resource, :location => after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
## replace your logic here
redirect_to root_path
end
end
end
Please note that the above code works with devise 2.2.4 . Currently, the code which is on master branch on github is changed somewhat due to rails 4 and strong_parameter compatibility .

rails 3 how can i give permissions to user for specific task like tickets, messages?

i am new to rails and i want to give permissions to users for particular task involved in my project on different modules.
i have two models "user" and "project"... in "user.rb" has_many : projects
and in "project.rb" belongs_to :user. and i have one more model which combine both models named "user_project.rb" in this model have proj_id an user_id stored.
i want to give permission and after that also check for the permission to current user for creating messages and tasks according to permissions assign to them.
where can i define permissions and how it works for me in view as well....
If you created user_project.rb only for storing these ids - remove it.
This look like you need to write before_filter in your controllers
class ProjectsController << ApplicationController
before_filter :current_user_required, only: [ :edit, :update ]
#there def of actions
private
def current_user_required
unless current_user == #project.user
flash[:error] = 'error 403'
redirect_to :back
end
end
And when you find #project, for edit and update actions, you can do it like:
#project = current_user.projects.find(params[:project_id]) #need to change :project_id
If you want to create model Message(belongs_to :project and Project has_name: messages) and give access for creation and edit it only for project.user you can do it using before_filter or validation in model
class Message << ActiveRecord::Base
validate :author_is_project_user, on: :create
private
def author_is_project_user
errors.add :base, 'author not is project user' unless self.author == self.project.user
end
end
According to this you can define permission for another things
And if you want to get permission for another user you have_to create model which belongs_ to :user and :project and check in before_filter present of it.
sort of this:
class Permission << ActiveRecord::Base
belongs_to :user
belongs_to :project
scope :about, -> project { where project_id: project }
scope :of_user, -> user { where project_id: project }
end
and in User model define method like
def access_to_project? project
Permission.about(project).of_user(self).first.present?
end
or you can add variable to this model and make more complicated logic of access

How can I default an ancestral relationship with cancan to an internal node of the tree?

I am using cancan to authorize my controller actions. One of classes where access is authorized by cancan is a tree, implemented with acts_as_ancestry. I'm having problems using load_and_authorize_resource when the user is not permitted to access the root level, but rather is allowed access starting at an interior node.
Here are some relavant class definitions:
class User < ActiveRecord::Base
belongs_to :organization, :inverse_of => :users
end
class Post < ActiveRecord::Base
belongs_to :organization, :inverse_of => :posts
end
class Organization < ActiveRecord::Base
has_ancestry :cache_depth => true
has_many :users, :inverse_of => :organization
has_many :posts, :inverse_of => :organization
end
The rules for managing posts are "You can manage posts in any organization below yours". My cancan abilities definition is this:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
# subtree_ids is added by acts_as_ancestry
can :manage, Post, {:organization_id => user.organization.subtree_ids}
end
end
In the controller, I have this (other actions omitted)
class PostsController < ApplicationController
load_and_authorize_resource :post
def index
end
def new
end
end
Everything works fine when the authorized user belongs to the root organization. However, when I login as a user authorized at an internal node, the index action works fine, but when the new action is invoked, I get a can-can authorization error.
Here is what I see in the log:
Access denied on new #<Post id: nil, organization_id: 1>
The organization_id 1 (the root) is coming from the schema:
create_table "posts", :force => true do |t|
t.integer "organization_id", :default => 1
end
With cancan, the new action will build a new Post and assign it to #post. When it does this, it will initialize all the attributes with values taken from the can definition in Abilities.rb. However, it will not do anything if those attributes are Arrays, Hashes or Ranges and the default value ends up coming from the schema.
How can I authorize users to manage posts in their subtree, but when they create a new post, default it to their organization?
In cancan, if the #post variable is already initialized by you, it will not call load_resource on it, only do the authorize part. See this part of the docs: https://github.com/ryanb/cancan/wiki/Authorizing-controller-actions, "Override loading".
So the simplest solution is to take control of the initialization yourself and make it what you need, like here:
class PostsController < ApplicationController
before_filter :initialize_post, :only => [:new, :create]
def initialize_post
#post = current_user.organization.posts.build(params[:post]||{:name=>'Smashing Kittens'})
end
load_and_authorize_resource :post
def index
end
def new
end
def create
end
end
You can see it working in this test project that I created from your post: https://github.com/robmathews/cancan_test.
I had a similar issue and ended up writing ancestry related permissions in blocks like so:
can :manage, Post do |post|
post.organization.subtree_ids.include?(user.organization_id)
end

Routes in Rails3 - Controller and routes for 2 post functions

I'm trying to write an app in rails 3 and I'm having some trouble figuring out the routes and controllers for a test that I want the user to take. The basic requirements for this app are:
Users, Tests and Questions are all in separate models.
A User has_many Tests. A Test has_many Questions
Provide a link on the user_profile page to /test/new to create the test record.
Provide a link on /test/new to /test/:id/part1 (where :id is the test_id) so that the user can complete the first part of the test. Questions will be retrieved from the db and presented on this page.
Provide a link on /test/:id/part1 to /test/:id/part2 so that the user can complete the second part of the test. Again, questions are retrieved from the db.
Provide a link on /test/:id/part2 to submit the test and return to the user's profile.
I've completed the models, which even pass their tests, so I think I have finished parts 1 and 2.
user.rb
Class User < ActiveRecord::Base
has_many :tests
end
test.rb
Class Test < ActiveRecord::Base
belongs_to :user
has_many :questions
end
question.rb
Class Question < ActiveRecrod::Base
belongs_to :test
end
My issues start when I try to put these models together using routes and controllers.
routes.rb
resources :users
resources :tests do
member do
post 'part1'
post 'part2'
end
end
users/show.html.erb
<%= link_to "Start The Test", new_test_path %>
tests/new.html.erb
<%= link_to "Part 1", part1_test_path(#test) %>
tests_controler.rb
class TestsController < ApplicationController
def new
#test = Test.new(current_user)
end
def part1
# still just a stub
end
end
I'm getting this error when I click on the link to take Part 1 of the test:
No route matches {:action=>"part1", :controller=>"tests", :id=>#<Test id: nil, taken_at: nil, user_id: nil, created_at: nil, updated_at: nil>}
Any help on this would be greatly appreciated.
By defining a member of the routes it's expecting an existent test, ie. one which is saved and has an id.
e.g.
part1_test_path = /test/123/part1
What you need is a collection route.
resources :tests do
collection do
post 'part1'
end
member do
post 'part2'
end
end
e.g.
part1_test_path = /test/part1
edit
Suggested solution:
resources :test, :path_names => { :new => 'part_1', :edit => 'part_2' } *1
def new
#test = Test.new
#new view
form_for #test do
...
def create
#test = Test.new params[:test]
if #test.save
redirect_to edit_test_path #test
def edit
#test = Test.find params[:id]
#edit view
form_for #test do
def update
#test = Test.find params[:id]
if #test.update_attributes params[:test]
redirect_to test_path #test
def show # test results
#test = Test.find params[:id]
if #test.incomplete *2
redirect_to edit_test_path #test
*1 See rails guide on routing. This will give you urls like this
test/part1
test/123/part2
You should put all of your validation in the model; your requirements of test data. Conditional validation will be required, depending on whether it's a new_record? or not ie if you're at part 1 or 2.
*2
add a method to your model which checks test completeness.
def incomplete
self.some_test_field.blank?
Let me know if you don't understand anything.

How to create a profile after user registration with Rails3 & Devise

I'm doing a simple user with profile application. User registers and
are automatically logged in. Works fine so far. Now, I'd like to
create a profile after a successful registration and redirect the user
to his/her profile.
I have a User model and controller. Devise also created the
registration controller. I installed the gem. I copied over the devise
files and I plan to override the create action.
First, whatever I edit in registrations_controller.rb nothing
changes.
class Devise::RegistrationsController < ApplicationController
prepend_before_filter :require_no_authentication, :only =>
[ :new, :create, :cancel ]
prepend_before_filter :authenticate_scope!, :only =>
[:edit, :update, :destroy]
include Devise::Controllers::InternalHelpers
Secondly, how to insert the profile creation step?
def create
build_resource
if resource.save
if resource.active?
set_flash_message :notice, :signed_up
sign_in_and_redirect(resource_name, resource)
else
set_flash_message :notice, :inactive_signed_up, :reason =>
resource.inactive_message.to_s
expire_session_data_after_sign_in!
redirect_to after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords(resource)
render_with_scope :new
end
end
I was thinking to add
current_user.create_profile under is resource.active?
How would you guys tackle that issue?
First, Please format your post and use <code> blocks for the snippets. That way it becomes very readable.
Coming to your problem:
Devise by default sign ins and redirects to application root_path, after registration.
If you wish to redirect to some other path you can specify it in a couple of ways.
One is to specify root_path for your devise reource. So in your case it will be
match '/user/profile/new' => 'profiles#new', :as => 'user_root'
This will redirect you to profile#new every time you login.
To prevent redirecting to profile#new each time you can add a before_filter on profile#new to check if profile exists and redirect to some other page, say dashboards, if profile exists.
Here is the link showing how to change redirect_path for devise:
https://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-on-successful-sign-in