I have a Company and a User model, both with a slug via friendly_id. Slugs are ensured to be unique across both models.
I'd like to have URLs:
http://www.example.com/any_company_name
http://www.example.com/any_user_name
e.g. both /apple and /tim
I'm not sure how to achieve this in Rails.
I have tried various permutations of:
routes.rb:
resources :users, path: ''
resources :companies, path: ''
get '*search', to: 'my_controller#redirect'
and
my_controller#redirect:
#company = Company.friendly.find(params[:search])
redirect_to #company if #company
#user = User.friendly.find(params[:search])
redirect_to #user if #user
However I can't get it to work. I can get /apple to redirect to /companies/apple and /tim to redirect to /users/tim (by removing the path: '' option) but this is not what I want to achieve.
Instead redirect with urls, you can redirect with controller#action by using url_for.
For example:
my_controller#redirect:
#company = Company.friendly.find(params[:search])
redirect_to url_for(action: 'show', controller: :companies , status: :success, company_id:#company.id)
#user = User.friendly.find(params[:search])
redirect_to url_for(action: 'show', controller: :users, status: :success,user_id:#user.id)
I had a similar problem and was able to solve it by creating a PublicSlug model with a slug attribute and a polymorphic association to a "Sluggable" class. I also used a Sluggable concern that I included in models I would like to query.
The PublicSlug model
class PublicSlug < ActiveRecord::Base
extend FriendlyId
friendly_id :sluggable_name, use: :slugged
belongs_to :sluggable, polymorphic: true
private
# Generate the slug based on the title of the Sluggable class
def sluggable_name
sluggable.name
end
end
The Sluggable concern
module Sluggable
extend ActiveSupport::Concern
included do
has_one :public_slug, dependent: :destroy, as: :sluggable
delegate :slug, to: :public_slug
end
end
Company and User models.
class User < ActiveRecord::Base
include Sluggable
end
class Company < ActiveRecord::Base
include Sluggable
end
I can now query both models using
Sluggable.friendly.find(slug).sluggable
The redirect could be handled in your controller as follows:
def redirect
#sluggable = Sluggable.friendly.find(params[:search]).sluggable
redirect_to #sluggable
end
Building off of #Essn's answer...
Still use a PublicSlug model:
# app/models/public_slug.rb
class PublicSlug < ActiveRecord::Base
extend FriendlyId
friendly_id :sluggable_name, use: :slugged
belongs_to :sluggable, polymorphic: true
validates :slug, presence: true, uniqueness: { case_sensitive: false }
private
# Generate the slug based on the title of the Sluggable class
def sluggable_name
sluggable.name
end
end
And a Sluggable concern:
# app/models/concerns/sluggable.rb
module Sluggable
extend ActiveSupport::Concern
included do
before_validation :create_public_slug
has_one :public_slug, dependent: :destroy, as: :sluggable
delegate :slug, to: :public_slug
private
def create_public_slug
self.public_slug = PublicSlug.new unless public_slug.present?
end
end
end
Include that concern in all models you want to lookup:
# app/models/user.rb
class User < ActiveRecord::Base
include Sluggable
...
end
Create a migration:
# db/migrate/...create_public_slugs.rb
class CreatePublicSlugs < ActiveRecord::Migration[5.0]
def change
create_table :public_slugs do |t|
t.references :sluggable, polymorphic: true
t.string :slug
end
end
end
Then you can lookup the model via:
# app/controllers/home_controller.rb
class HomeController < ApplicationController
# /:slug
def show
#sluggable = PublicSlug.friendly.find(params[:id]).sluggable
render #sluggable
end
end
Related
So I'm having a problem with ams. I'm using Rails 5.2 and I read lots of tutorials and even when I did exactly what they showed I still have something that they don't and I didn't find answer on google.
I have model Course, Video, Quiz and Segment.
Course has many segment, segment can be video or quiz (I'm using sti).
This is how I wrote it:
app/models/course.rb
class Course < ApplicationRecord
validates :title ,presence: true
validates :author ,presence: true
has_many :videos
has_many :quizs
has_many :segments
end
app/models/segment.rb
class Segment < ApplicationRecord
belongs_to :course
end
app/models/quiz.rb
class Quiz < Segment
validates :course_id ,presence: true
validates :name ,presence: true
belongs_to :course
end
app/models/video.rb
class Video < Segment
validates :course_id ,presence: true
validates :name ,presence: true
belongs_to :course
end
app/controllers/courses_controller.rb
class CoursesController < InheritedResources::Base
def show
#course = Course.find(params[:id])
render json: #course.attributes
end
def index
#courses = Course.all
render json: #courses
end
end
app/serializers/course_serializer.rb
class CourseSerializer < ActiveModel::Serializer
attributes :title, :author
has_many :segments
end
and this is what is showed me
I have couple of problems:
I don't know where this data name come from and I don't know how to change it or hide it.
even when I request to see one course I get the created date and other stuff I didn't want, although I configured it so I only see title and author.
I want to know if I can custom the json response so I won't see the title of the relations or change it's name.
You haven't created a SegmentSerializer. By default, AMS will serialize all fields.
class SegmentSerializer < ActiveModel::Serializer
attributes :name
end
Remove the attributes method call. It returns a Hash and then your serializer is not used.
```
class CoursesController < InheritedResources::Base
def show
#course = Course.find(params[:id])
render json: #course
end
def index
#courses = Course.all
render json: #courses
end
end
```
Use the key option
has_many :segments, key: :your_key
I'm new to Ruby on Rails and currently trying to make a little test website for me.I've got an issue in my code that states an "undefined method `service_providers' for #"
The line of code which produces the error is the following:
def new
#service_provider = current_user.service_providers.build(serviceprovider_params)
end
My Database Model is Table "User" has_one "ServiceProvider" has_many "Services".
I use the rubygem "devise" for the user-model.
I've tried to transfer the idea of the micropost of the "Ruby on Rails Tutorial" (https://www.railstutorial.org/book/user_microposts) in my example app. In Listing 13.36 there's also this code because with this ruby knows the reference between the current_user and the micrrpost.
I don't have an idea why it isn't working with my code:
Model
class ServiceProvider < ApplicationRecord
belongs_to :user
has_many :service
validates :name, presence: true
validates :street, presence: true
validates :plz, presence: true
validates :location, presence: true
validates :user_id, presence: true
end
Controller
class ServiceProvidersController < ApplicationController
before_action :set_serviceprovider, only: [:show, :edit, :update]
before_action :authenticate_user!
def index
end
def show
end
def new
#service_provider = current_user.service_providers.build(serviceprovider_params)
end
def create
#service_provider = current_user.service_provider.build(serviceprovider_params)
if #service_provider.save
redirect_to #service_provider, notice: "Gespeichert"
else
render :new
end
end
def edit
end
def update
end
private
def set_serviceprovider
#service_provider = Service_Provider.find(params [:id])
end
def serviceprovider_params
params.require(:service_provider).permit(:name, :street, :plz, :location)
end
end
ServiceProvider-Helper
module ServiceProvidersHelper
def current_service_provider
#current_service_provider = service_provider.user_id.find(current_user.id)
end
end
If there's some coding missing which you need for your help, please ask. I'm a newbie in coding with ruby, but i think taht must be the relevant parts of the code which is involved.
Thanks for help.
If you have has_one association then you need to use build_ASSOCIATION_NAME so in this case, it will be build_service_provider
#service_provider = current_user.build_service_provider(serviceprovider_params)
Also, I think you need to take a look at this association
class ServiceProvider < ApplicationRecord
belongs_to :user
has_many :service
If you are building an association with has_many then the class name should be plural.
Change it to
has_many :services
currently I have the model commodity_group, there are created_by, and updated_by columns in it. It belongs to user, and one user will have commodity_groups. Now, I want to whenever a user created a commodity group, his id will be saved into the created_by, and whenever somebody update a commodity group, his id will be saved into the field update_by.
At the moment, i get this error:
unknown attribute 'user_id' for CommodityGroup.
basically, I don't want to add the column user_id to the commodity_group table since it is the same with the column created_by. Therefore, could somebody guide me here a little bit. Here are my files:
commodity_groups_controller.rb:
class CommodityGroupsController < ApplicationController
before_action :set_commodity_group, only: [:show, :edit, :update, :destroy]
# GET /commodity_groups
def index
#commodity_groups = CommodityGroup.all
end
# GET /commodity_groups/1
def show
end
# GET /commodity_groups/new
def new
#commodity_group = current_user.commodity_groups.build
end
# GET /commodity_groups/1/edit
def edit
end
# POST /commodity_groups
def create
#commodity_group = current_user.commodity_groups.build(commodity_group_params)
if #commodity_group.save
redirect_to commodity_groups_path, notice: init_message(:success, t('message.new_success', page_name: t('page_name.commodity_group')))
else
redirect_to new_commodity_group_path, notice: init_message(:error, t('message.new_error', page_name: t('page_name.commodity_group')))
end
end
# PATCH/PUT /commodity_groups/1
def update
if #commodity_group.update(commodity_group_params)
redirect_to #commodity_group, notice: 'Commodity group was successfully updated.'
else
render :edit
end
end
# DELETE /commodity_groups/1
def destroy
#commodity_group.destroy
redirect_to commodity_groups_url, notice: 'Commodity group was successfully destroyed.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_commodity_group
#commodity_group = CommodityGroup.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def commodity_group_params
params[:commodity_group]
end
end
commodity_group.rb:
class CommodityGroup < ActiveRecord::Base
extend FriendlyId
friendly_id :code, use: :history
belongs_to :user_created,
class_name: 'User',
primary_key: :id,
foreign_key: :created_by
belongs_to :user_updated,
class_name: 'User',
primary_key: :id,
foreign_key: :updated_by
validates_presence_of :code
validates_presence_of :name
validates_presence_of :user
end
user.rb:
class User < ActiveRecord::Base
extend FriendlyId
include Filterable
friendly_id :slug_candidates, use: :history
has_secure_password
acts_as_paranoid
has_many :activities
has_many :pricing_histories
has_many :commodity_groups
end
CommodityGroup is searching for user_id in commodity_groups table.
You need to specify that in the User model.
class User < ActiveRecord::Base
extend FriendlyId
include Filterable
friendly_id :slug_candidates, use: :history
has_secure_password
acts_as_paranoid
has_many :activities
has_many :pricing_histories
has_many :commodity_groups_created, class_name: 'CommodityGroup',
foreign_key: :created_by
has_many :commodity_groups_updated, class_name: 'CommodityGroup',
foreign_key: :updated_by
end
you've got the foreign_key on the CommodityGroup side, you just need to add it to the User side as well and you should be good to go eg:
has_many :commodity_groups, class_name: 'User', foreign_key: :created_by
(you may need to jigger with the details to figure out what else you need to add, but this will basically give you the idea on how to solve it)
I'm using FriendlyID to make my URLs look nice, so what I got now is:
/items/my-personal-item
But what I would like is:
/user1/my-personal-item
Where user1 is the username of a user owning my-personal-item.
#config/routes.rb
resources :users, path: "" do
resources :items, path: "", only: [:show, :index] #-> url.com/:user_id/
end
FriendlyID has to reside in any model with which you're using, so if you wanted to use it on User as well as Item, you'd have to use the following setup:
#app/models/user.rb
class User < ActiveRecord::Base
extend FriendlyId
friendly_id :username
end
#app/models/item.rb
class Item < ActiveRecord::Base
extend FriendlyId
friendly_id :title
end
This should give you the ability to call: url.com/user-name/my-personal-item
--
If you wanted to back it all up with the appropriate controller actions, you could use the following:
#app/controllers/items_controller.rb
class ItemsController < ApplicationController
def show
#user = User.find params[:user_id]
#item = #user.items.find params[:id]
end
end
As a pro-tip, you'll want to look at the friendly_id initializer to set some defaults for your app:
#config/initializers/friendly_id.rb
config.use :slugged #-> make sure this is valid
config.use :finders #-> make sure this is valid
By setting the above options, you'll just be able to call friendly_id [[attribute]] in your models.
FriendlyID also supports Unique Slugs by Scope.
Because you haven't provided some information about your app I can't give you a exact explanation.
But I've created an explanation based on your url.
If I'm right than you have a Item and a User model. The Item model belongs to the User model and the User model has many Items.
Here is how I would solve this problem:
Models:
# User model
class User < ActiveRecord::Base
extend FriendlyId
has_many :items
friendly_id :name, :use => :slugged
end
# Item model
class Item < ActiveRecord::base
extend FriendlyId
belongs_to :user
friendly_id :name, :use => :scoped, :scope => :user
end
Controller:
class ItemsController < ApplicationController
before_action :set_user, :set_item
def show
# Show your item ...
end
private
def set_user
User.friendly.find(params[:user_id])
end
def set_item
#user.items.friendly.find(params[:item_id])
end
end
Routes:
Rails.application.routes.draw do
get ':user_id/:item_id' => 'items#show'
end
This is a minimal implementation without validation. I hope this helps.
Happy coding :)
So I have a couple of models in my app and they are all registered with ActiveAdmin. They all work great except for one and I can't figure out why. I keep getting the same error:
NameError at /admin/reports
uninitialized constant Report::Users
The model that it is happening on is called Report
class Report < ActiveRecord::Base
belongs_to :users
belongs_to :cars
enum reason: [:accident,:totaled,:stolen]
validates :reason, presence:true
end
The controller looks like this:
Class ReportsController < ApplicationController
before_action :authenticate_user!
def create
#car=Car.find(params[:car_id])
#report=#car.reports.build(report_params)
#report.user_id=current_user.id
#report.car_id=#car.id
if #report.save
redirect_to car_path(car)
else
render 'new'
end
end
def destroy
#report=Report.find(params[:id])
#report.destroy
end
private
def report_params
params.require(:report).permit(:reason)
end
end
This is the migration used to create the model:
class CreateReports < ActiveRecord::Migration
def change
create_table :reports do |t|
t.references :user, index: true
t.references :car, index: true
t.integer :reason, default: 0
t.timestamps null: false
end
add_foreign_key :reports, :users
add_foreign_key :reports, :cars
end
end
Lastly here is the active_admin app/admin/report.rb:
ActiveAdmin.register Report do
# See permitted parameters documentation:
# https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters
#
# permit_params :list, :of, :attributes, :on, :model
#
# or
#
# permit_params do
# permitted = [:permitted, :attributes]
# permitted << :other if resource.something?
# permitted
# end
end
I have been trying to figure it out for a couple of hours. Solutions that I saw on SO that don't work. I ran rails generate active_admin:resource Report to create it so it is singular. Why is it misbehaving?
NameError at /admin/reports uninitialized constant Report::Users
Association name for a belongs_to should be singular as per naming conventions.
class Report < ActiveRecord::Base
belongs_to :user #here
belongs_to :car #and here too
enum reason: [:accident,:totaled,:stolen]
validates :reason, presence:true
end