Forked from softmonkeyjapan/categories_controller.rb
Created
October 24, 2024 09:50
-
-
Save gmhawash/7cdc10f7c3b15df6243f8523dd9e4d71 to your computer and use it in GitHub Desktop.
Medium : Rails : nested routes, polymorphic associations and controllers (https://medium.com/@loickartono/rails-nested-routes-polymorphic-associations-and-controllers-8ade7249fa49)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CategoriesController < ApplicationController | |
include Behaveable::ResourceFinder | |
include Behaveable::RouteExtractor | |
# Response type. | |
respond_to :json | |
# Get categories. | |
# | |
# GET (/:categorizable/:categorizable_id)/categories(.:format) | |
# | |
# ==== Returns | |
# * <tt>Response</tt> - JSON serialized categories. | |
def index | |
categories = categorizable.all | |
respond_with categories, status: :ok, location: extract(@behaveable) | |
end | |
# Get a category. | |
# | |
# GET (/:categorizable/:categorizable_id)/categories/:id(.:format) | |
# | |
# ==== Returns | |
# * <tt>Response</tt> - JSON serialized category. | |
def show | |
category = categorizable.find(params[:id]) | |
respond_with category, status: :ok, location: extract(@behaveable, category) | |
end | |
# Create a category. | |
# | |
# POST (/:categorizable/:categorizable_id)/categories(.:format) | |
# | |
# ==== Returns | |
# * <tt>Response</tt> - JSON serialized category or errors if any. | |
def create | |
category = categorizable.new(category_params) | |
respond_to do |format| | |
category.transaction do | |
if category.save | |
categorizable << category if @behaveable | |
format.json { render json: category, status: :created } | |
else | |
format.json { render errors_for(category) } | |
end | |
end | |
end | |
end | |
# Update a category. | |
# | |
# PATCH (/:categorizable/:categorizable_id)/categories/:id(.:format) | |
# | |
# ==== Returns | |
# * <tt>Response</tt> - JSON serialized category or errors if any. | |
def update | |
category = categorizable.find(params[:id]) | |
respond_to do |format| | |
if category.update(category_params) | |
format.json { render json: category, status: :ok } | |
else | |
format.json { render errors_for(category) } | |
end | |
end | |
end | |
# Delete a category. | |
# | |
# DELETE (/:categorizable/:categorizable_id)/categories/:id(.:format) | |
# | |
# ==== Returns | |
# * <tt>Response</tt> - 204 no content. | |
def destroy | |
category = categorizable.find(params[:id]) | |
category.destroy if category | |
respond_to do |format| | |
format.json { head :no_content } | |
end | |
end | |
private | |
# Get Category context object. | |
# | |
# ==== Returns | |
# * <tt>ActiveRecord</tt> - Categorizable's categories or Category. | |
def categorizable | |
@behaveable ||= behaveable | |
@behaveable ? @behaveable.categories : Category | |
end | |
# ActiveRecord object errors. | |
# TODO: Should be placed at ApplicationController level ??. | |
# | |
# ==== Parameters | |
# * <tt>object</tt> - ActiveRecord object. | |
# | |
# ==== Returns | |
# * <tt>Hash</tt> - Hash containing object errors if any. | |
def errors_for(object) | |
{ json: { errors: object.errors }, status: :unprocessable_entity } | |
end | |
# Sanitize request data. | |
# | |
# ==== Returns | |
# * <tt>Hash</tt> - Sanitized request params. | |
def category_params | |
params.require(:category).permit(:name) | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Behaveable | |
module ResourceFinder | |
# Get the behaveable object. | |
# | |
# ==== Returns | |
# * <tt>ActiveRecord::Model</tt> - Behaveable instance object. | |
def behaveable | |
klass, param = behaveable_class | |
klass.find(params[param.to_sym]) if klass | |
end | |
private | |
# Lookup behaveable class. | |
# | |
# ==== Returns | |
# * <tt>Response</tt> - Behaveable class object or nil if not found. | |
def behaveable_class | |
params.each do |name, _value| | |
if name =~ /(.+)_id$/ | |
model = name.match(%r{([^\/.]*)_id$}) | |
return model[1].classify.constantize, name | |
end | |
end | |
nil | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Behaveable | |
module RouteExtractor | |
# Generate url location. | |
# | |
# ==== Parameters | |
# * <tt>behaveable</tt> - Behaveable object. | |
# * <tt>resource</tt> - Resource object. (member routes). | |
# | |
# ==== Returns | |
# * <tt>Route</tt> - Url location. | |
def extract(behaveable = nil, resource = nil) | |
resource_name = resource_name_from(params) | |
behaveable_name = behaveable_name_from(behaveable) | |
location_url = "#{resource_name}_url" | |
return regular(location_url, resource) unless behaveable | |
location_url = "#{behaveable_name}_#{resource_name}_url" | |
nested(location_url, behaveable, resource) | |
end | |
private | |
# Handle non-nested url location. | |
# | |
# ==== Parameters | |
# * <tt>location_url</tt> - Url route as string. | |
# * <tt>resource</tt> - Resource object. | |
# | |
# ==== Returns | |
# * <tt>Route</tt> - Url location. | |
def regular(location_url, resource) | |
return send(location_url) unless resource | |
send(location_url, resource) | |
end | |
# Handle nested url location. | |
# | |
# ==== Parameters | |
# * <tt>location_url</tt> - Url route as string. | |
# * <tt>behaveable</tt> - Behaveable object. | |
# * <tt>resource</tt> - Resource object. | |
# | |
# ==== Returns | |
# * <tt>Route</tt> - Url location. | |
def nested(location_url, behaveable, resource) | |
return send(location_url, behaveable) unless resource | |
send(location_url, behaveable, resource) | |
end | |
# Get resource name from params. | |
# | |
# ==== Parameters | |
# * <tt>params</tt> - ApplicationController's params. | |
# | |
# ==== Returns | |
# * <tt>String</tt> - Resource name (singular or plural). | |
def resource_name_from(params) | |
inflection = params[:id].present? ? 'singular' : 'plural' | |
params[:controller].split('/').last.send("#{inflection}ize") | |
end | |
# Get behaveable class name. | |
# | |
# ==== Parameters | |
# * <tt>behaveable</tt> - Behaveable object. | |
# | |
# ==== Returns | |
# * <tt>String</tt> - Behaveable class snake case name or nil. | |
def behaveable_name_from(behaveable) | |
return unless behaveable | |
behaveable.class.name.underscore | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment