Create a 'Random User' button on my Rails 3 web app - ruby-on-rails-3

I want to have a button which goes to a random user on my site. I am using the friendly_id gem so the URLs are, for example, /users/dean and I've also set it up so its /dean.
I'm guessing I would add something similar to this in my routes.rb file:
match '/users/random' => 'users#index'
And then some extra code in the user controller?
How would I go about doing this?
Many thanks.

I'd do this:
Define a class method random on User model (or in a module that's included into your model if you'd want to reuse it for other models later).
class User
def self.random
offset = rand(count)
first(:offset => offset)
end
end
Other ways of getting a random record, if performance becomes an issue.
Add a random action in your UsersController like this
def random
redirect_to User.random
end
And finally create a route
match '/users/random' => 'users#random'

I would have a specific action random in the user controller and localize the logic for choosing a user there. Return a redirect to the route to that user from that action. I would prefer this over complicating the index action with extra logic to handle a different action.

Related

Why doesn't CanCan respect my rule?

I need to restrict users to only access their shipping list items by 'supplier_id '. I know I can use 'accessible_by' to add a 'where' clause. But if I forget to add the accessible_by scope to my query, in the following controller, in action index, the user can read any list.
class SupplierShippingListsController < ApplicationController
load_and_authorize_resource
def index
#shipping_lists = SupplierShippingList.where(:supplier_id => params[:supplier_id])
authorize! :read, SupplierShippingList
end
end
I defined this rule first, hopping the instance would be checked against my hash but it doesn't, as if :supplier_id was pointless.
can :read, SupplierShippingList, :supplier_id => user.company_id
So I tried a block with a random value to make it fail the authorization, but the user can still access the action.
can :read, SupplierShippingList do |list|
list.supplier_id == 17
end
Is there some hidden thing to set?
EDIT
I also tried
authorize! :read, #shipping_lists
with no avail.
Index actions will ignore the block, so your second example will not work. Your best bet for relatively simple abilities like this is to use a hash of conditions like your first example. But load_and_authorize_resource will automatically load #supplier_shipping_lists for you. Your index action as shown will overwrite the variable and make the 'load' portion of 'load and authorize resource' pointless. You should remove your index action and let CanCan do the loading for you. If you don't override the instance variable, you should see what you expect as long as you use the hash of conditions.
Also, you don't need to manually call authorize! either. That is the 'authorize' part of load_and_authorize_resource. You need to just let CanCan do the work. When you enter your index action, the instance variable will either already be initialized, but will be empty if you don't have the authority to view any lists.
Since you're using load_and_authorize_resource you don't have to do anything. In your index action it should find all of the supplier_shipping_lists that correspond to the user company if you're using the can :read, SupplierShippingList, :supplier_id => user.company_id. If that isn't working, do you have any other rules being applied to the SupplierShippingList?

Rail3 | How to create standard route/action for ALL controllers?

Well, DRY! So i thought it should be easy to add a new action (like the existing new, edit) to all my controllers (in my case copy). But how do you setup a new route for ALL controllers?
Without going in to 'loops' (i.e. %w().each ...) inside the routes.rb ?
I mean, we want DRY right? So you don't want copy your action inside the routes file for each resource. I guess you should be able to extend the default actions/routes (index, new, edit,etc.) easy?
Thanks!
AFIK no way to do this by default. You could monkey-patch resources to include this functionality:
https://github.com/rails/rails/blob/b229bc70e50ec0887c5bb3aaaa9c6ee8af054026/actionpack/lib/action_dispatch/routing/mapper.rb#L982
...but my hunch is you would be better off re-considering whether this functionality can be created another way, since what you want to do is "off the Rails".
One option is create a CloneController#new that accepts a model and id and creates a clone. This seems like it would be drier, and wouldn't require you to pepper a gazillion "clone_article" "clone_blog" "clone_user" paths all over the place.
Obviously you would want to carefully white-list the models/ids that can be passed in.
Looking through the source there isn't a way to add to the default actions for a resource.
But, as #juwiley says, the methods resources :item is just a shortcut for creating a load of member and collection methods.
All you need to do is something like this
class ActionDispatch::Routing::Mapper
def resources_with_copy(*resources, &block)
block_with_copy = lambda do
block.call
member do
post :copy
end
end
resources(*resources, &block_with_copy)
end
end
Then in your routes.rb just say
resources_with_copy :items
resources_with_copy :posts do
member do
post :share
end
end
...

How to user defined friendly URLs in Rails 3?

Now i have something like this
http://myapp.com/pages/1
http://myapp.com/pages/2
http://myapp.com/pages/3
http://myapp.com/pages/4
And each page belong to one user
What i need is to each user to set it's own custom name for the page.
I was thinking of using the friendly_id gem http://norman.github.com/friendly_id/
but I don't find any method to directly edit the slug to set a custom friendly url
how should i proceed?
FriendlyID is a great gem.
It shouldn't be hard to implement user defined page URL.
Create table pages with user_id and link
class User < ActiveRecord::Base
has_many :pages
class Page < ActiveRecord::Base
belongs_to :user
has_friendly_id :link # link is name of the column whose value will be replaced by slugged value
On the page#new you add an input for the link attribute.
Alternatively, you could set friendly_id on title or something else with :use_slug => true option. This way FriendlyID will take the title and modify it so it doesn't have and restricted characters. It will use it's own table to store slugs. Use cached_slug to increase performanse.
Updated
To give users a choice whether they wan't to set a custom link, you could do this:
Set friendly_id on the link field without slugs..
Make a virtual attribute permalink so you could show it in your forms.
In the before_filter, check whether the permalink is set.
If it is, write it to the link field.
If it's not, write title to the link field.
FriendlyID uses babosa gem to generate slugs. If you decide to use it as well, this is how your filter could look like:
protected
def generate_link
#you might need to use .nil? instead
self.link = self.permalink.empty? ? make_slug(self.title) : make_slug(self.permalink)
end
def make_slug(value)
value.to_slug.normalize.to_s #you could as well use ph6py's way
end
Adding to_param method to one of the models should help:
def to_param
"#{id}-#{call_to_method_that_returns_custom_name.parameterize}"
end
Hope this is what you are looking for :)
I am not using the friendly_url gem and am not sure whether my way is efficient. But it works fine for me.
I have a model called Node with id and friendly url field called url_title.
My routes.rb file:
resource 'nodes/:url_title', :to => 'Nodes#view'
nodes_controller.rb
class NodesController <ActiveController
def view
#node = Node.find_by_url_title(:params(url_title))
end
end
And use the #node variable to populate your view.
Now, whenever I type www.example.com/nodes/awesome-title , it takes me to the proper page. One argument against this can be need to create an index on a non-primary field. But I think that might be required for better performance even in the friendly_url gem. Also, the non-primary field url_title needs to be unique. Again, this might be required even for correct working for friendly_url .
Feel free to correct me if I am wrong in these assumptions.
There are a variety of ways, you can achieve this-
1) using Stringex
2) sluggable-finder
3) friendly_id
A complete step by step methodology with reasons for each to be used can be found out here. Happy reading!

Mapping domain.com/user/1 to domain.com/loginName

Has anyone tried before using subdomain-fu to change domain.com/user/1 to domain.com/UserName ?
Are there any other plugins to make this happen on rails 3?
You can do this by "overwriting" the to_param method in your User model.
Assuming you are using a User model, as you stated put some code like this in it:
#cleans up the url so it's purdy.
def to_param
loginName # you probably want login_name or just name, but whatever it's your code
end
So notice that you now have to change your User#show controller call though, since you really are doing a
User.find_by_name(params[:id]
Lastly, we should be able to change the routes so that the last route in your routes.rb file says:
match ':id' => 'users#show'
What you want is called pretty URLs. Take a look here: Pretty (dated) RESTful URLs in Rails

Rails 3 routing based on context

I am trying to implement a "context" system similar to the one used by GitHub. For example, a Post may be created belonging either to the User or one of the Companies the User belongs to depending on whether to User is in the "User" context or a context that refers to one of the Companies.
As a part of this, I'd like to be able to do routing based on the user's current context. For example, if the User is in their own context, /dashboard should route to users/show, but if they are in the context for Company with ID 35, then /dashboard should route to companies/35/dashboard.
I could route /dashboard to a special controller responsible for making such decisions, such as context#dashboard which could then do a redirect_to, but this doesn't feel quite right (perhaps because we're taking logic that the Rails routing module should be responsible for and moving it to a controller?)
What would be the proper way to solve this problem in Rails 3?
I finally found a solution to my problem that I like. This will use the URLs from my original question.
First, assume a session-stored Context object that stores whether the user is in a "user" context or a "company" context. If the user is in a "company" context, then the ID of the company they're working as is in the object as well. We can get the context via a helper named get_context and we can get the currently logged-in user via current_user.
Now, we set up our routes as so:
config/routes.rb:
MyApplication::Application.routes.draw do
get "dashboard" => "redirect", :user => "/users/show", :company => "/companies/:id/dashboard"
end
Now, app/controllers/redirect_controller.rb:
class RedirectController < ApplicationController
def method_missing(method, *args)
user_url = params[:user]
company_url = params[:company]
context = get_context
case context.type
when :user
redirect_to user_url.gsub(":id", current_user.id.to_s)
when :company
redirect_to company_url.gsub(":id", context.id.to_s)
end
end
end
It's easy enough to keep the actual URLs for the redirect where they belong (in the routes.rb file!) and that data is passed in to a DRY controller. I can even pass in the ID of the current context object in the route.
Your approach seems like the best way to me. Anything else would be more cluttered and not very standard.