What is the best way to implement comments in Rails? - ruby-on-rails

To the point: I want to put Post has_many Comments but I do not want to create a separate comment controller and subsequent views. Mainly because the comments will never show up anywhere else but inside the SHOW action of a Post. Or am I breaking the MVC paradigm?

You are breaking the MVC paradigm, as you said. The point of MVC is to split everything up into bite-size chunks so it is more manageable. That's how I see it at least.
How would comments be created without a specific controller for them. The showing part on a Post is the easy part:
#comments = #post.comments

There is a fundamental distinction to be made between the internal domain model of your system and the public interface your system exposes.
If you are using a relational database, it is good practice to have
Comment.belongs_to :post
Post.has_many :comments
The internal domain model of your system can help you design your public interface - but you can also tailor your public interface how you want it, without being forced to make it a strict reflection of your internal domain model!
In your case, I would suggest having a CommentsController. But in this controller class, you do not need all of the normal REST actions. You only need a few of them.
# app/controllers/comments_controller.rb
class CommentsController < ApplicationController
respond_to :js
def create
#post = Post.find(params[:post_id])
#comment = post.comments.create(params[:comment])
respond_with [#post, #comment]
end
end
In this controller, you only have a create action which would be the target of the "new comment" form at the bottom of the page displaying a post. You do not need any of the other REST actions because people never view, edit, or delete a comment in isolation - they only create new ones, and not from a dedicated new-comment page either. The routing for this is as follows:
# config/routes.rb
MyApp::Application.routes.draw do
resources :posts do
resources :comments, :only => [:create]
end
end

The more you deviate from the MVC paradigm, the more problems you'll have later on. For example, if you wanted to add admin views for your Comments, it would be easier to expand on it through the Comments Controller. Else, you'll end up having multiple actions for your comment in the Posts controller (eg. approve_comment, delete_comment, voteup_comment, etc).
That being said, you can always wire things up so that actions on your comments direct the user back to the Post that originated it. So, comment related actions will reside in the Comments Controller, but the user is generally working with Posts (and its associated Comments).

Related

Ruby on Rails 7 Multistep form with multiple models logic

I am currently struggling with building up a multi step form where every step creates a model instance.
In this case I have 3 models:
UserPlan
Connection
GameDashboard
Since the association is like that:
An user has an user_plan
A connection belongs to an user_plan
A game_dashboard belongs to a connection
I would like to create a wizard to allow the current_user to create a game_dashboard going through a multi-step form where he is also creating connection and user_plan instance.
For this purpose I looked at Wicked gem and I started creating the logic from game_dashboard (which is the last). As soon as I had to face with form generating I felt like maybe starting from the bottom was not the better solution.
That’s why I am here to ask for help:
What would be the better way to implement this wizard? Starting from the bottom (game_dashboard) or starting
from the top (use_plan)?
Since I’m not asking help for code at the moment I didn’t write any controller’s or model’s logic, in case it would be helpful to someone I will put it!
Thanks a lot
EDIT
Since i need to allow only one process at a time but allowing multiple processes, to avoid the params values i decided to create a new model called like "onboarding" where i handle steps states there, checking each time the step
The simplest way would be to rely on the standard MVC pattern of Rails.
Just use the create and update controller methods to link to the next model's form (instead of to a show or index view)
E.g.
class UserPlansController < ApplicationController
...
def create
if #user_plan = UserPlan.create(user_plan_params)
# the next step in the form wizard process:
redirect_to new_connection_path(user_id: current_user, user_plan_id: #user_plan.reload.id)
else
#user_plan = UserPlan.new(user: current_user)
render :new
end
end
...
# something similar for #update action
end
For routes, you have two options:
You could nest everything:
# routes.rb
resources :user do
resources :user_plan do
resources :connection do
resources : game_dashboard
end
end
end
Pro:
This would make setting your associations in your controllers easier because all your routes would have what you need. E.g.:
/users/:user_id/user_plans/:user_plan_id/connections/:connection_id/game_dashboards/:game_dashboard_id
Con:
Your routes and link helpers would be very long and intense towards the "bottom". E.g.
game_dashboard_connection_user_plan_user_path(:user_id, :user_plan_id, :connection_id, :game_dashboard)
You could just manually link your wizard "steps" together
Pro:
The URLs and helpers aren't so crazy. E.g.
new_connection_path(user_plan_id: #user_plan.id)
With one meaningful URL variable: user_plan_id=1, you can look up everything upstream. e.g.:
#user_plan = UserPlan.find(params['user_plan_id'])
#user = #user_plan.user
Con:
(not much of a "con" because you probably wind up doing this anyway)
If you need to display information about "parent" records, you have to perform model lookups in your controllers first:
class GameDashboardController < ApplicationController
# e.g. URL: /game_dashboards/new?connection_id=1
def new
#connection = Connection.find(params['connection_id'])
#user_plan = #connection.user_plan
#user = #user_plan.user
#game_dashboard = GameDashboard.new(connection: #connection)
end
end

Dynamic Routes Rails 4, taken from db

Frustrating, I can't find an eligible solution for my problem.
In my Rails 4 app, I want to give my users the possibility to add their own custom post types to their sites. Like:
www.example.com/houses/address-1
www.example2.com/sports/baseball
Both would work, but only for the linked sites. Sports and houses would be the (RESTful) post types, taken from the db, added by users.
I have been struggling to find a elegant solution to accomplish this. I found http://codeconnoisseur.org/ramblings/creating-dynamic-routes-at-runtime-in-rails-4 but that feels kinda hacky and I'm not sure if reloading the routes works in production, I'm getting signals that it won't.
I'd say I have to use routes constraints http://guides.rubyonrails.org/routing.html#advanced-constraints but I don't have a clue how to approach this.
To be clear, I have no problem with the site setting stuff, the multi tenancy part of my app is fully functional (set in Middleware, so the current site is callable in the routes.rb file). My issue is with the (relative) routes, and how they could be dynamically set with db records.
Any pointers much appreciated.
I think route constraints don't work for you because your domain is a variable here. Instead, you should be examining the request object.
In your ApplicationController, you could define a method that would be called before any action, like so:
class ApplicationController < ActionController::Base
before_action :identify_site
def identify_site
#site = Site.where(:domain => request.host).first
end
end
As you scale, you could use Redis for your domains so you're not making an expensive SQL call on each request.
Then you can just add the #site as a parameter to whatever call you're making. I'm assuming you're doing some sort of "Post" thing, so I'll write some boilerplate code:
class PostController < ApplicationController
def show
#post = Post.where(:site => #site, :type => params[:type], :id => params[:id])
end
end
Just write your routes like any other regular resource.

Rails way to create several "create" pages

I have an "admin control panel" page which is handled by an AdminController. You can do two things in the control panel: create_product and create_order. There will be forms for each object, and when you submit the form, it will insert new records into the database.
What is the Rails way for implementing this? Do I generate a CreateProductController and CreateOrderController along with the appropriate view, which are accessible by clicking on the create_product and create_order links from the control panel?
Does the Rails way describe a way to handle both workflows using a single controller? At some point I will need to define a post method for each form, so it seems like creating separate controllers is the easiest way to set up required behavior and also the routing details.
Product and Order are all resources when having controller. The better way is to use RESTful resources. You can also add namespace for easier identification.
class Admin::ProductsController < AdminController
def create
end
def new
end
# And #show, #index, #destroy etc.
end
class Admin::OrdersController < AdminController
def create
end
def new
end
end

Displaying different views for the same controller, based on the route

I am building an admin interface for a website. I have certain controllers that have admin functions/views that also have user facing views. For example, when a user goes to /blog, it should show the title, date, first paragraph, etc. of each blog post with links to read the whole post. If an admin goes to admin/posts they would see a list of just the blog post titles, how many comments, edit/delete links, link to create a post, etc.
How would I accomplish this? My (simplified) routes files is this:
namespace :admin do
resources :posts
end
Do I need to have separate controllers?
Usually when using namespaces you want your code to be namespaced as well. I would go for 2 different controllers serving 2 different views.
app/controllers/posts_controller.rb
app/controllers/backend/posts_controller.rb
or
app/controllers/posts_controller.rb
app/controllers/admin_area/posts_controller.rb
You get the idea. I would do the same thing with the views.
You controllers would look like this:
class PostsController < ApplicationController
end
class Backend::PostsController < BackendController
end
class BackendController < ApplicationController
end
There are quite a few ways you could approach this, I can't really think of one being "right" over the other. For simplicity's sake, I'll offer one quick solution, though, admittedly, it's a shortcut.
Presumably, you have a logged in user going to this /admin route, and the current_user would be authorized as an admin, so you can use that to your advantage in your show method.
if current_user.admin?
render 'admin_show'
else
render 'show'
Your views, of course, would render the different implementations.
If you are going to have a number of differences in what the methods do, though, it may be worth creating a new admin_posts_controller. But if there are only a couple differences, then that could be good enough.
Meh, hope that helps.

Is it ok to use both nested and shallow resources in rails? How to write the controller/views?

I have resources for which it makes perfect sense to address them both as nested withing other resources and separately. I.e. i expect to use all urls like these:
/account/4/transfers # all transfers which belong to an account
/user/2/transfers # all transfers input by specific user
/project/1/transfers # all transfers relevant to a project
/transfers # all transfers
my concern is how do I write TransfersController actions (for example index) as it would double the logic found in parent models - is there a better way than doing something like
TransfersController
...
def index
if !params[account_id].nil?
#account = Account.find(params[account_id])
#transfers = #account.transfers
elsif !params[user_id].nil?
#user = User.find(params[user_id])
if #user.accesible_by?(current_user)
#transfers = #user.transfers
end
elsif !params[projects_id].nil?
.....
and the same holds for views - although they all will list transfers they will have very different headers, navigation etc for user, account, project, ...
I hope that you see the pattern from this example. I think there should be some non-ugly solution to this. Basically I would love to separate the logic which selects the transfers to be displayed and other things like context specific parts of view.
I've got an open question on this. In my question I outline the 2 methods I came up with. I'm using the second currently, and it's working pretty well.
Routing nested resources in Rails 3
The route I'm using is a bit different because I'm using usernames in place of the IDs, and I want them first. You would stick with something like:
namespace :projects, :path => 'projects/:project_id' do
resources :transfers #=> controllers/projects/transfers_controller.rb
end
# app/controllers/projects/transfers_controller.rb
module Projects
class TransfersController < ApplicationController
# actions that expect a :project_id param
end
end
# app/controllers/transfers_controller.rb
class TransfersController < ApplicationController
# your typical actions without any project handling
end
The reason I use the namespace instead of a call to resources is to have Rails let me use a separate controller with separate views to handle the same model, rather than pushing all the nasty conditional logic into my controller actions.

Resources