Trying to apply a better refactoring in this rails code - ruby-on-rails

I have main 3 controllers, which has a function called activation that is taking the params from the form but before creating into the database, I have to merge some kind of hash into that params, Now am doing in this way:
class AccountsController < ApplicationController
def activation
activation_params = if valid_user?
# This is a service, there I am taking this params and adding up one hash and returning the same
# params back
Account::ActivationParamsModifier.new(params).call
else
params
end
#activations = Activation.new(activation_params[:activations])
if #activations.save!
# Code Here
end
end
private
def valid_user?
# Valid User Check
end
end
end
I am trying to figure out one thing here, this below same code in 3 controllers and also I guess this code is not with a good refactoring. Any kind of suggestions to improve this? I know this small code but shows in different controllers. I am not sure this before_action is a good fit for this.
activation_params = if valid_user?
Account::ActivationParamsModifier.new(params, user_id).call
else
params
end

Related

Rails 5 API: custom hidden responder that would process value returned by the action

I have rails 5 based api app, using fast_jsonapi
and after a while I observe that all most all my actions are having one common pattern
def action_name
#some_object.perform_action_name # this returns #some_object
render json: ControllerNameSerializer.new(#some_object).to_h
end
I do not wish to write the last render line here and it should work, for that I want that the returned value by the action should be processed by any hidden responder like thing, Serializer klass can be made out looking at controller name.
Perhaps this could be achieved by adding a small middleware. However at first, I find it not a good idea/practise to go for a middleware. In middleware, we do get rendered response, we need a hook prior to that.
I would imagine like
class SomeController ...
respond_with_returned_value
def action_name
#some_object.perform_action_name # this returns #some_object
end
Any suggestions?
Note, do not worry about error/failure cases, #some_object.errors could hold them and I have a mechanism to handle that separately.
Sketched out...
class ApplicationController < ...
def respond_with_returned_value
include MyWrapperModule
end
...
end
module MyWrapperModule
def self.included(base)
base.public_instance_methods.each do |method_name|
original_method_name = "original_#{method_name}".to_sym
rename method_name -> original_method_name
define_method(method_name) { render json: send(original_method_name) }
end
end
end
Seems like there really should be some blessed way to do this - or like someone must have already done it.

Rails controller design pattern idea for decreasing it's size

The question is what do you think about this pattern?
Problem:
You've got controller with index action, and this action is huge.
Action is full of ActiveRecord chaining and maybe some computations with records.
When you are adding new methods controller is getting bigger.
I've heard about "skinny controller fat model", and I'm just what? My models are already fat, are you crazy?
I've heard about service objects, they are not very usable as for me.
result = SomeService.new(params, request, flash, current_user).call
After such service object you could try:
result = SomeService.new(controller).call
# or
result = SomeService.new.(controller)
And what to do with returning error statutes from that service? As answered below, exceptions.
So, you need to create exceptions class, throw it, catch it and only then render something or make redirect.
Here is the pattern from subject:
# controllers/some_controller.rb
class SomeController < OtherController
before_actions
include Index
include Show
def create_update_and_destroy
# small methods have no reason to leave controller
end
private
def common_private_method
end
end
# controllers/some_controller/index.rb
module SomeController::Index
def index
# code here
end
private
def index_do_some_stuff
# this method is prefixed by "index" to avoid name collision
end
end
Yes, there is some_controller.rb and some_controller directory with actions as files.
Nobody in OOP likes prefixes and if your method has well explaining not short name - prefix is not necessary.
In my opinion, this is the most simple and obvious way. Just take fat code and split to modules!
What do you think?
Explainations about why I have many code:
In one project I have view, it requires records from several models, some related, some not related.
So I had started to write many scopes in models, time passed, I realized that this is wrong approach.
Different actions required very specific records selections, only action should know about such specifics. And I had scopes named "for_index", "for_show".
Now I'm creating module Index with metod index, and all record fetching and computing code is splitted into private methods right in place.
In other project I have API. Specific endpoint is returning specific deep nested json, several models are fetching. I already know that creating scopes in model for one specific endpoint is bad idea, so I'm splitting code amoung private methods. One action and five private methods for it. And next five public methods and 25 private? In single controller?
Combining form objects with service objects and other patterns can make your model and controller thin. You can also add an ActiveResource::Errors object in your service object to collect errors.
Here's an example using user's input. Customize it according to your specification
class ProductForm
...
def save
if service_object.call
self
else
append_errors(service_object)
false
end
end
def service_object
#service_object ||= ProductCreationService.new(params)
end
def append_errors object
errors.append object.errors # just for simplicity
end
end
In your controller
def create
#product = ProductForm.new params
if #product.save
...
else
...
end
end
Controller actions should be straight forward and as short as possible. Complexities inside these actions can be lessen with other design patterns.
Actions that collects data can be abstracted by using query/finder objects.
Here's a rough example
class DashboardQuery
attr_reader :options
def initalize(options = {})
#options = options
end
def branches
#branches ||= Branch.all
end
def branch_count
#branch_count ||= branches.count
end
end
# Usage
#dashboard = DashboardQuery.new(params)
#dashboard.branch_count
#dashabord.branches

How to DRY up 2 controllers/models/views that are basically the exact same

An Order has_many AItems and BItems. As you can tell, the items are basically identical but with an important business reason for categorizing them separately. Wondering what's the best strategy to DRY this up. I realize this is a little opinionated... but hoping to get some clear points of view and arguments.
View code
Currently I'm using a partial. Like this:
class AItemsController
def new
end
end
class BItemsController
def new
end
end
# view files layout
> views
> AItems
> new.html.erb
> BItems
> new.html.erb
# routing
get '/AItems/new'
get '/BItems/new'
# code for /views/AItems/new.html.erb
<%= render "layouts/items_new", object: "AItems" %>
# code for /views/BItems/new.html.erb
<%= render "layouts/items_new", object: "BItems" %>
I'm wondering if it'd be easier to get rid of the partial entirely and just do parameters like this:
class AItemsController
def new
end
end
class BItemsController
def new
end
end
# view files layout
> views
> Items
> new.html.erb
# routing
get '/items/new/:type'
# code for /views/Items/new.html.erb
# code from partial evaluating the param[:type] instead of a passed object
Controller code
Currently everything is duplicated... (I haven't made any attempt at DRYing yet) as in it looks like this (very illustrative, the point is to just show that short of the naming conventions literally everything is basically the same):
class AItemsController
def new
#items = AItems.joins(:order).where("orders.status_id IS NULL")
end
def do_something
a_items_params.each do |item_params|
key_var = item_params[:some_attribute]
...
end
end
end
class BItemsController
def new
#items = BItems.joins(:order).where("orders.status_id IS NULL")
end
def do_something
b_items_params.each do |item_params|
key_var = item_params[:some_attribute]
...
end
end
end
I haven't DRYed this yet because I'm a little conflicted as to how. Examples below are illustrative, forgive if the code isn't exact, but hopefully you get the gist.
Solution A: In one way, I could keep the action definitions in each controller, and then have the code within the action pull from a shared concern:
class AItemsController
include SharedCode
def new
shared_new
end
def do_something
shared_do_something
end
end
Solution B: abstract away the action definitions to the shared concern:
class AItemsController
included SharedAction
shared_action("AItems")
end
Solution C: route everything to a singular controller and again use params to differentiate (passed from view)
class ItemsController
def new
item_type = params[:item_type]
end
def do_something
item_type = params[:item_type]
end
end
Model code
This one is a little more cut and dry, and I don't need a ton of feedback here, I will just used shared concerns for key methods/ callback.
Obviously the answer for one will affect the other. For example if everything routes through a single controller, then I'll have a single view with parameters rather than a partial approach. But because the controller has multiple DRYing options, there's still room for debate.
If you've read this far, I will happily take angry comments about how this question is too loosely defined in exchange for at least some thoughts on what you would do. What's more understandable for you if you were taking over my code?
I am trying to learn and the best way to do that is to solicit multiple points of view and pros and cons to weigh out.
Check out the InheritedResources Gem: https://github.com/josevalim/inherited_resources
Inherited Resources speeds up development by making your controllers
inherit all restful actions so you just have to focus on what is
important. It makes your controllers more powerful and cleaner at the
same time.
Or the Responders Gem, a replacement to Inherited Resources: https://github.com/plataformatec/responders
A set of responders modules to dry up your Rails 4.2+ app.

Rails: How to POST internally to another controller action?

This is going to sound strange, but hear me out...I need to be able to make the equivalent of a POST request to one of my other controllers. The SimpleController is basically a simplified version of a more verbose controller. How can I do this appropriately?
class VerboseController < ApplicationController
def create
# lots of required params
end
end
class SimpleController < ApplicationController
def create
# prepare the params required for VerboseController.create
# now call the VerboseController.create with the new params
end
end
Maybe I am over-thinking this, but I don't know how to do this.
Inter-controller communication in a Rails app (or any web app following the same model-adapter-view pattern for that matter) is something you should actively avoid. When you are tempted to do so consider it a sign that you are fighting the patterns and framework your app is built on and that you are relying on logic has been implemented at the wrong layer of your application.
As #ismaelga suggested in a comment; both controllers should invoke some common component to handle this shared behavior and keep your controllers "skinny". In Rails that's often a method on a model object, especially for the sort of creation behavior you seem to be worried about in this case.
You shouldn't be doing this. Are you creating a model? Then having two class methods on the model would be much better. It also separates the code much better. Then you can use the methods not only in controllers but also background jobs (etc.) in the future.
For example if you're creating a Person:
class VerboseController < ApplicationController
def create
Person.verbose_create(params)
end
end
class SimpleController < ApplicationController
def create
Person.simple_create(params)
end
end
Then in the Person-model you could go like this:
class Person
def self.verbose_create(options)
# ... do the creating stuff here
end
def self.simple_create(options)
# Prepare the options as you were trying to do in the controller...
prepared_options = options.merge(some: "option")
# ... and pass them to the verbose_create method
verbose_create(prepared_options)
end
end
I hope this can help a little. :-)

How to always set a value for account-scope in Rails?

I'm working on a multi-user, multi-account App where 1 account can have n users. It is very important that every user can only access info from its account. My approach is to add an account_id to every model in the DB and than add a filter in every controller to only select objects with the current account_id. I will use the authorization plugin.
Is this approach a good idea?
What is the best way to always set the account_id for every object that is created without writing
object.account = #current_account
in every CREATE action? Maybe a filter?
Also I'm not sure about the best way to implement the filter for the select options. I need something like a general condition: No matter what else appears in the SQL statement, there is always a "WHERE account_id = XY".
Thanks for your help!
This is similar to a User.has_many :emails scenario. You don't want the user to see other peoples emails by changing the ID in the URL, so you do this:
#emails = current_user.emails
In your case, you can probably do something like this:
class ApplicationController < ActionController::Base
def current_account
#current_account ||= current_user && current_user.account
end
end
# In an imagined ProjectsController
#projects = current_account.projects
#project = current_account.projects.find(params[:id])
I know, I know, if you access Session-variables or Instance variables in your Model you didn't understand the MVC pattern and "should go back to PHP". But still, this could be very useful if you have - like us - a lot of controllers and actions where you don't always want to write #current_account.object.do_something (not very DRY).
The solution I found is very easy:
Step 1:
Add your current_account to Thread.current, so for example
class ApplicationController < ActionController::Base
before_filter :get_current_account
protected
def get_current_account
# somehow get the current account, depends on your approach
Thread.current[:account] = #account
end
end
Step 2:
Add a current_account method to all your models
#/lib/ar_current_account.rb
ActiveRecord::Base.class_eval do
def self.current_account
Thread.current[:account]
end
end
Step 3: Voilá, in your Models you can do something like this:
class MyModel < ActiveRecord::Base
belongs_to :account
# Set the default values
def initialize(params = nil)
super
self.account_id ||= current_account.id
end
end
You could also work with something like the before_validation callback in active_record and then make with a validation sure the account is always set.
The same approach could be used if you always want to add the current_user to every created object.
What do you think?
To answer your second question, check out the new default_scope feature in Rails 2.3.
I understand that you don't want to bother about scoping you account all time. Lets be honest, it's a pain in the a**.
To add a bit magic and have this scoping done seamlessly give a look at the following gem
http://gemcutter.org/gems/account_scopper
Hope this helps,
--
Sebastien Grosjean - ZenCocoon

Resources