Finding item by friendly_id instead of id - ruby-on-rails

I have the friendly_id gem installed and it works great. My issue is I have comments that have polymorphic relations to several other things, like blogs. I set up the comments to be found by the id of both, for example blog id, then comment id. Like so in my comments controller:
def load_commentable
resource, id = request.path.split('/')[1,2]
#commentable = resource.singularize.classify.constantize.find(id)
end
My problem is now that I have friendly ids for everything, this is no longer working. It's pulling the friendly id, of course.
Couldn't find Blog with 'id'=cobra-kai-here-we-go-again
How can I fix this so it finds the resource the comment belongs to by the friendly id?

I would keep a separation between the id and the friendly_id so the id is not overwritten by default and can still be used in model relationship while the friendly_id from the controllers only by using the Model.friendly.find('id') syntax.
Check this change in friendly_id gem in version 5.0

Related

Rails 5 issue friendly_id search in db by name does not allow to delete or update?

this is my model User
class User < ApplicationRecord
extend FriendlyId
friendly_id :name
end
the user name is not uniq.
i want my url like this:
http://localhost:3000/users/myname
this is my controller:
class UsersController < ApplicationController
before_action :set_user
def set_user
#user = User.friendly.find(params[:id])
end
end
the URL is shown fine, just the way i want, but the problem is when i try to delete a user, the console server shows a "rollback" i notice that this is because there are two or more users with the same name so It can delete the user.
So my problem now that I have implemented gem friendly_id is that it searchs and set_user by name but i want to set_admin by ID so i can delete by id (this way it does not matter if there are many users with the same name). This problem is repeated in my other models too.
How can avoid this behavoir, I need to set model by id but to show the name or encrypt the id in the url. Do you recomend me other gem or how should i implement correctly. (is not an option to set name as uniq). thanks
As per my understanding,
friendly id uses uniqueness validation at db level
https://github.com/norman/friendly_id/blob/aff0564584e84ffa505e6e278b65ca3c4ee5d698/lib/friendly_id/migration.rb#L12
If you already have users with same name, you should delete those entries.
(This is much similar to condition where we try to put unique index to a column in db where same entry values already exist)
Then install friendly_id gem, and make entries. I am sure slugs created by gem wont repeat so every record will be unique and you will be able to delete record using id Too

Generate slug when using ancestry gem

I'd like to create a slug based on the ancestors of the record. If I already have a slug created. The best solution I have come up with is:
def pretty_url
path.select(:slug).map(&:slug).join("-")
end
Is there a more precise way to do this using the ancestry gem?
Also, I am using friendly id to generate the slug, so maybe there is a better way using friendly id?
This is what I figured out, using friendly id and ancestry gem together.
friendly_id :slug_candidates, use: :slugged
def slug_candidates
[
[parent.try(:slug), :title]
]
end

friendly_id and acts_as_paranoid creating duplicate slugs

I'm current using acts_as_paranoid and friendly_id (5.0.1) on a model and when I destroy a model and try to create a new one that will generate the same slug I get:
ERROR: duplicate key value violates unique constraint "index_papers_on_slug"
I need to somehow get the code that checks if a slug already exists check within the scope of all of the objects not just the non-deleted ones.
How can I get friendly_id to use with_deleted when checking if a slug already exists. I should note that I am also using slug history which may be complicating things further.
Upon digging deeper I realized that since I am using history the slug is being fully deleted while the object is just being soft deleted:
DELETE FROM "friendly_id_slugs" WHERE "friendly_id_slugs"."id" = $1 [["id", 9423]]
So, I just need to figure out how to prevent this and I should be okay since it looks like the friendly_id code itself is already using unscoped when trying to find a valid slug.
Adding the following to the model allowed me to overrride the dependent destroy on the slugs
def has_many_dependent_for_slugs; end
The solution comes from a comment on this github issue.
Friendly_id has a module called scoped which allows you to generate unique slugs within a scope. So, probably
class Paper < ActiveRecord::Base
extend FriendlyId
friendly_id :title, :use => :scoped, :scope => :unscoped
end
will resolve the problem.
I just came across this issue too and I figured two different ways to address it.
Solution 1:
Use dependent: false.
friendly_id :title, dependent: false
Solution: 2
Overcoming this problem without overriding the dependent destroy for anyone that wants to avoid that.
The friendly_id gem uses a method called scope_for_slug_generator to set the model scope. That means we could override this method by adding the following to app/config/initializers/friendly_id.rb.
module FriendlyId
def scope_for_slug_generator
scope = if self.class.base_class.include?(Paranoia)
self.class.base_class.unscoped.with_deleted
else
self.class.base_class.unscoped
end
scope = self.class.base_class.unscoped
scope = scope.friendly unless scope.respond_to?(:exists_by_friendly_id?)
primary_key_name = self.class.primary_key
scope.where(self.class.base_class.arel_table[primary_key_name].not_eq(send(primary_key_name)))
end
end

`Simple_form` + nested model + CanCan: creating new child updates parent

Environment: Rails 3.2.13 + simple_form 2.1.0 + CanCan 1.6.10 + etc.
Model thumbnail: Articles have authors (Users) and Comments. Comments are a nested resource within Articles. The Comment model includes content, the commenter (currently logged-in user ID) and article ID.
Issue: Creating a new Comment on an Article causes the Article to be updated, understandably. At present, CanCan's Ability class is hardwired to allow that Article to be updated by that user. I want to limit that to allowing the update if the Article's Comments — and only that field — are updated. I've been poking around in pry for a couple of hours trying to figure out how to tell what's being updated, and am drawing a blank so far.
Models are posted in this Gist in response to Michael Szyndel's question.
Help?
In-lieu of identifying the culprit, which I would guess is related to the reliance on accepts_nested_attributes_for, I would rather offer a solution-- implement a before_update callback on the Article model.
before_update :verify_update_authorization
# virtual attribute to supply CanCan a user candidate
def initiator
#initiating_user if #initiating_user
end
def initiator=(user)
#initiating_user = user
end
private
def verify_update_authorization
return false if Ability.new(initiator).cannot?(:update, self)
end
The controllers would then need to set the Article's virtual attribute when an update is desired. In this particular case, it would be proper to override the InheretedResources update action.

Model and controller designs in rails for friendly URL

Another question from rails newbie. I am using friendly_id gem with mysql in rails 3.x
This is a design problem (may be easy in rails). I am expecting advises from rails experts. I am building a library listing app. Where user can view library by "metro-area" or by "city" in it. For example:
I wish to have URLs like:
www.list.com/library/san-francisco-bay-area
or
www.list.com/library/san-francisco-bay-area/palo-alto/
In database I have tables:
library
-------
id, name, city_id, slug
name is slugged here and city_id is FK
city
----
city_id, name, metro_area_id, slug
name is slugged here and metro_area_id is FK
metro_area
----------
metro_area_id, name, state, slug
name is slugged here
So when a user points browser to www.list.com/library/san-francisco-bay-area/palo-alto
I wish to get list of libraries in san-francisco-bay-area/palo-alto. But my library table model is containing slug only for library's name. So how this URL can be parsed to find the city_id that can be used in library model and controller to get the list.
Please remember, I cannot rely only on the name of the city. I would have to find city_id of 'palo-alto' which is in metro 'san-francisco-bay-area'. Since slug for metro_area and city is in other tables, so what is the best way to design model and controller.
Show method of controller is:
def show
#user = Library.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #library }
end
end
and model is
class Library < ActiveRecord::Base
attr_accessible :name, :city_id, :slug
extend FriendlyId
friendly_id :name, use: :slugged
end
This will not work per my friendly URL requirement. So I would appreciate advice from experts :)
Maybe you have found your solution, I'm new to rails as well, so I'm just guessing this would work out.
Since you wanna display slugs from two different models. I'm assuming the way to display ids would be something like
www.list.com/libraries/:id/cities/:id/metro_areas/:id
Which can be done through editing the route file by adding Nested Resources
As for just displaying two ids like
www.list.com/library/:city_id/:metro_area_id
Rails guide refers it as Dynamic Segments
After that, it's just a matter of converting the ids to slugs.
I also found this in FriendlyId's documentation which is addressing to your case.
Hope it helps

Resources