I added irwi ( https://github.com/alno/irwi ) to my app.
I'd like it accessible when user access to http://example.com/shop/:shop_name/wiki
(I need to know how routes.rb should be)
Anyone can show me how to make it?
:shop_name is slug so that it could be various pattern.
Of course each shop record should have a wikipedia ( one-to-one here. When shop is being created, one wikipedia page for it should be created automatically at the same time. )
I'm using Cancan, and devise for authentication so if possible, I want it only the registered user can edit and update wikipedia page.
Thanks.
see - http://guides.rubyonrails.org/routing.html for how to do your routing if you want more detailed help you will have to provide more code. Will be something along the lines of-
resource :wiki, :controller => :shop, :only => [] do
get :wiki
post :wiki
end
depending on what you require wiki to be able to do, your controllers etc. you should just be able to use the
before_filter :authenticate_user!
for your authentication
Related
I am writing a rails application for an organization. Every user may have 1 or more roles and can only access certain controller actions depending on those roles.
For example, only admins can create, destroy and update certain fields of Users. Also, there are Teams which each have a team leader, and only the team leader can update certain information about the Team (like the member list, for example). However, Admins are the one who assign the team leader in the first place.
The specific details of my scenario are not important, I merely hope I described the situation where there are many different roles and permissions.
My question is: what gem to use? My first thought was CanCan, but the last commit was almost a year ago and there is no mention of Rails 4 compatibility. Is there a currently maintained alternative?
Your first guess was right, use cancancan and you'll be good with it.
EDIT Jul 24, 2015
I've been using cancancan for a long time now and it was always working great. I've recently started working on a project where Pundit is used for authorization.
It is awesome. It prompts you to define the policy for each resource and it feels more natural than one bloated Ability class.
For bigger projects, I would definitely recommend Pundit.
To control access to actions I'd recommend Action Access, it boils down to this:
class UsersController < ApplicationController
let :admin, :all
let :user, [:index, :show]
# ...
end
This will automatically lock the controller, allowing admins to access every action, users only to show or index users and anyone else will be rejected and redirected with an alert.
If you need more control, you can use not_authorized! inside actions to check and reject access.
It's completely independent of the authentication system and it can work without User models or predefined roles. All you need is to set the clearance level for the current request:
class ApplicationController < ActionController::Base
def current_clearance_level
session[:role] || :guest
end
end
You can return whatever you app needs here, like current_user.role for example.
Although it isn't required, it bundles a set of handy model additions that allow to do things like:
<% if current_user.can? :edit, :team %>
<%= link_to 'Edit team', edit_team_path(#team) %>
<% end %>
Here :team refers to TeamsController, so the link will only be displayed if the current user is authorized to access the edit action in TeamsController. It also supports namespaces.
You can lock controllers by default, customize the redirection path and the alert message, etc.
It's very straightforward and easy, I hope you find it useful.
Something that was suggested to me that we are now using is the petergate gem. Easy to use and very clean looking with a great rails feel.
Works well with devise.
Here is some examples from the readme.
If you're using devise you're in luck, otherwise you'll have to add following methods to your project:
user_signed_in?
current_user
after_sign_in_path_for(current_user)
authenticate_user!
This comes in your User.rb. Adding more roles is as easy as adding them to the array.
petergate(roles: [:admin, :editor], multiple: false)
Instance Methods
user.role => :editor
user.roles => [:editor, :user]
user.roles=(v) #sets roles
user.available_roles => [:admin, :editor]
user.has_roles?(:admin, :editors) # returns true if user is any of roles passed in as params.
Controller access syntax.
access all: [:show, :index], user: {except: [:destroy]}, company_admin: :all
I am little bit confused in using rails route. I need some suggestion about customizing my url.
This is my current url
http://localhost:3000/posts/product/41?product_id=2
and
http://localhost:3000/posts/product/41?model_id=24&product_id=2
This is my link
<%= link_to product_model.name, controller: :posts,action: :product,product_id: params[:product_id],model_id: product_model.id
Logically product should come first in url. But why model prefers first here.
And i need my url something like this
http://localhost:3000/posts/product/41/mobile
and
http://localhost:3000/posts/product/41/mobile/nokia
Since i am not familiar with rails route i didn't write any special coding in my route
Here is the simple route exist
resources :posts
Ok your question here actually contains two different problems, so i will give suggestions to both.
1. Nested resources
Your first problem is to use "nested routes". Rails guide has a long and good article about routes and how to write and use them, including nested routes. You can check it out here: http://guides.rubyonrails.org/routing.html#nested-resources.
However in your situation would the solution look something like this:
resources :category do
resources :sub_category do
resources :products do
resources :models
end
end
end
You can now greate links like this
<%= link_to product_model.name, category_sub_category_product_model_path(#category, #sub_category, #product, product_model) %>
You can see that i have removed posts, see 3. Refactor design to see why. If you really want this as a action on posts, should you however do something like this (but would recommend this!):
get "posts/product/:category_id/:subcategory_id/:product_id/:model_id", to "posts#product", as: :posts_product
This would be used like this in your views:
<%= link_to product_model.name, posts_product_path(#category, #sub_category, #product, product_model) %>
2. Pretty URL's
Your second problem is to use model names instead of id's in your urls. The simpels solution for this is having a unique attribute on your model that you can use instead of id, and then just add a to_param method. Fx for product could we do something like this:
class Product < ActiveRecord::Base
def to_param
name
end
end
Ryan Bates have made a good screencast about this: http://railscasts.com/episodes/63-model-name-in-url-revised. If you want something more flexible should you use the gem Friendly Id. And again does Ryan comes to the rescue with another great RailsCast: http://railscasts.com/episodes/314-pretty-urls-with-friendlyid.
3. Change design
Ok well so this is just my opinion, feel free to ignore it. But their is some bad practices and signs in your examples, so let me just quickly go through what i think you should improve on.
Restful actions
You should, when possible always avoid creating controller actions that is not restful (simply put is the base actions index, show, new, create, edit, update and destroy the only restful actions). In your example does this mean that the product action of the posts controller should be changed to something restful. Why not move it to the product model controller and call it "show"?
Deeply nested resources
You should avoid nesting your routes to deeply. Is it really important to show both the category, the sub category, the product AND the model in your url? Maybe that's how your models are associated internally in your application but why should the user know this? If you don't have a list of subcategories at "/posts/product" and a list of products at "posts/product/41" is there no reason to have so long a route. A rule of thumb is "nest no deeper then two levels", ie. ":category/:sub_category". Further more does short routes mean better SEO.
As i said, feel free to ignore these suggestions, your application would work without these changes. However changing these things would greatly help you structure you code, and keep your codebase clean and maintainable. These rules and principles is not something i have just conjured out of nothing, but very accepted principles in the Rails community. You can google each of these principles or patterns and see a lot of articles and posts on why it's a good idea to follow them, especially when you work with Rails.
Resources
Rails Routing from the Outside In — Ruby on Rails Guides
norman/friendly_id - Github
#314 Pretty URLs with FriendlyId - RailsCasts
#63 Model Name in URL (revised) - RailsCasts
Take a look at the friendly-id gem
There's a great RailsCast about it
Add this to your model inmodel.rb
def to_param
name
end
and then add
#model = Model.find_by_name(params[:id]) to your show method, then you can get the url as you mentioned above.
PS: You Should have name field for Model table in your schema.
I think you are looking for nested routes. Please refer this link http://guides.rubyonrails.org/routing.html#nested-resources
and use to_param method in the model if you want to display model_name instead of id as explained by #Ajay Kumar
def to_param
name
end
where name is the model attribute for that specific model.
Why not a namespace?
namespace :posts do
resources :products
end
This should do I think..
Namespace does not include restful ids into the scope..
My model holds expenses, including user and project references..
class Expense < ActiveRecord::Base
attr_accessible :amount, :expense_date, :description, :project_id, :user_id
belongs_to :project
belongs_to :user
end
The ExpensesController handles basic CRUD operations for the expenses.
I now am needing to build an administrators version of this same page, a new view preferably, which can include the different views of the data, by user, by project, etc, and can also edit data that the user cannot.
My question is: Do a build a second controller to handle the administrative perspective of the data, -- or do I setup conditions inside of every method, to detect the originating view and form, and then conditions to redirect them back to where they belong?
If I do build a second controller, how do I properly setup the form_for so that it knows what controller to go to?
Thanks!
PS - If anyone has any books about how to properly put together a rails app, I feel like I know the pieces ant parts, but I'm getting stuck on the big picture implementation. I learned rails with Michael Hartl's guide, prior to that I was a PHP developer.
IMHO, if security is a big concern for your app then using an admin namespace and separate controllers is the best way to make sure you don't leave any gaps. It's also just simpler and lower stress.
I would have a directory structure like so:
/app/controllers/application_controller.rb
/app/controllers/admin_controller.rb - inherits from application_controller
/app/controllers/expenses_controller.rb - non-admin, inherits from application_controller
/app/controllers/admin/expenses_controller.rb - inherits from admin_controller
Your views would be similarly separated/duplicated:
/app/views/expenses/* - non-admin expenses views
/app/views/admin/expenses/* - admin expenses views
In application_controller you'd put the Devise methods to authenticate_user and CanCan method to check_authorization (which throws an exception if authorization is not checked at some point in the controller action). In admin_controller you have more strict filters to make sure the user is an admin. Then you can get even more fine-grained in the specific controllers and their actions.
Of course each controller only has to define the actions it really needs and you don't have to duplicate views. Maybe the non-admin expenses_controller has index, show, new, create, while the admin one has only edit, update, and destroy. Then in the 'show' view you'd still have code that add links to the 'edit' action if the user is an admin.
Edit - Routes
With the above example, your routes.rb would look something like:
resources :expenses, :only => [:index, :show, :new, :create]
namespace :admin do
resources :expenses, :only => [:edit, :update, :destroy]
end
So you still use expenses_path() for the index and expense_path(foo) for show. A form on the admin page, however, would post to admin_expense_path(#expense).
If you wish to add another Controller I would suggest having common code in a module and importing them in each Controller. Each controller would also have an adequate before filter to check for adequate rights.
But I would prefer having one controller and one set of views since I think it avoids code duplication and/or confusion. Pass the user as a local variable to the view and check for administrator rights when you need to decide the URL for the form_for or whether to hide or show some part.
If the views differ significantly, check for admin rights in the controller and render either the user view or the admin view.
You can even create a special partial for the admin part of the view and decide whether to render it in the view or not, sending the appropriate data in the params or not.
You can use Devise gem for authentication.
You can create somekind of "namespace" for administrative part of your application.
I my opinion, creating another controller depends on how many views and actions your admin will access.
About the doubt about form_for action, it will be managed by your routes and paths that you configure in your form_for params.
I'm working on a rails app and using a singular resource. However the controller name for the singular resource is plural.
Eg map.resource activity_report expectes the activity_reports_controller.
The explanation given in the rails 3 guide is: "... you might want to use the same controller for a singular route and a plural route..." That is a reasonable explanation, but what is the use case for using the same controller to handle a singular route and a plural route?
In a RESTful Rails application there is usually a mapping of one controller per RESTful resource. For example, let's say we wanted a controller to process user logins (/session) but also to provide a list of users who are currently logged in (/sessions). Logically we could put both of those responsibilities within a SessionsController:
class SessionsController < ApplicationController
# GET /sessions
# Display a list of logged in users
def index
...
end
# GET /session/new
# Display the login form
def new
...
end
# POST /session
# Authenticate a user
def create
...
end
end
An alternative would be to split the functionality for listing logged in users out into a separate administration controller.
You can use it.
class UsersController < Application
end
map.resource :user
map.resources :users
Another situation in which I can imagine using it would be, let's say (and this isn't necessarily the business model you'd want, but stay with me for a moment) you are going to make a site of film reviews, and film information. So, on the one hand you'd have the link to your list of the latest reviews be a plural resource route, something like this:
http://yoursite.com/reviews?count=5
So, in this case, you have a controller for the collection, right? But you're only going to review each movie once. So what if you wanted to provide an easy access to a movie's review?
http://yoursite.com/movies/pirates_of_the_carribean_2/review
Well, there's a nested single resource route, because a movie has_one review, right?
I'm using Ryan Bates' nifty authentication in my application for user signup and login. Each user has_many :widgets, but I'd like to allow users to browse other users' widgets. I'm thinking that a url scheme like /username/widgets/widget_id would make a lot of sense--it would keep all widget-related code in the same place (the widgets controller). However, I'm not sure how to use this style of URL in my app.
Right now my codebase is such that it permits logged-in users to browse only their own widgets, which live at /widgets/widget_id. What changes would I need to make to routes.rb, my models classes, and any place where links to a given widget are needed?
I've done Rails work before but am a newb when it comes to more complicated routing, etc, so I'd appreciate any feedback. Thanks for your consideration!
Look into nested routes. You could nest widgets inside users, like this:
map.resources :users do |users|
users.resources :widgets
end
Which would give you URLs like these:
/users/1/widgets # all of user 1's widgets
/users/1/widgets/1 # one of user 1's widgets
Check out the routing guide for more details.
The easiest would be to go with InheritedResources plugin which handles most of the legwork for you.
# routes:
map.resources :users do |user|
user.resources :widgets
end
class WidgetsController < InheritedResources::Base
# this will require :user_id to be passed on all requests
# #user will be set accordingly
# and widget will be searched in #user.widgets
belongs_to :user
end
# no changes required to the models