Does this method that changes an instance variable belong in my Rails controller or model? - ruby-on-rails

I have a basic "best practice" question about controllers and instance variables.
Say you have an instance variable in anew or update action in a controller, is it ok to modify that instance variable via a private method in the controller? Or should the method exist in the model?
e.g. in this example below, I need to loop through the attributes of an instance variable, and add or remove something. For example, if I am using nested attributes 3 layers deep and have to remove certain attributes, change them and then add them back in. I know this may seem strange, but assume it is necessary.
def new
#some_thing = SomeThing.new(:some_params)
do_something_to_inst_var # method call
#some_thing.save
end
private
def do_something_to_inst_var
#some_thing.addresses.each do |address|
# modify it in some way
end
end
Or is this bad practice? Should this be a method in the model and should be called like:
#some_thing.do_something_to_inst_var
OR
should we explicitly pass the instance variable to the method like:
def new
#some_thing = SomeThing.new(:some_params)
do_something_to_inst_var(#some_thing) # method call
#some_thing.save
end
private
def do_something_to_inst_var(some_thing)
some_thing.addresses.each do |addresses|
# modify it in some way
end
end
I'm looking for some clarity here, with an example if possible. I'm still learning and trying to improve and I didn't find an answer by searching.

Rails applications should have "thin controllers" and "fat models" for a couple of reasons:
Each object should handle only its own responsibilities. A controller should just be about connecting the web, the the model and the view, which thanks to Rails doesn't take much code. If a controller method refers repeatedly to methods of the same model, it's incorrectly taking on model responsibilities; we say that it's not cohesive or that it has "Feature Envy". It is more likely that if the model changes the controller will have to change in parallel.
It's easier to test models than to test controllers.
Fix it by writing a method in the model that does the model-specific work and call it in the controller (your second option). (Eventually your model will get too fat and you'll have to break it up too, but that's another story.) For example:
class SomeThingsController
def new
#some_thing = SomeThing.new(:some_params)
#some_thing.do_something # method call
#some_thing.save
end
end
class SomeThing
def do_something
addresses.each do |address|
# modify it in some way
end
end
end
Regarding instance variables.
Define them only if necessary. Presumably the one in your example is needed for the view.
Assuming an instance variable is justified at all, there's no reason not to refer to it in private methods of the class that contains it. That's what they're for. So your first option (referring directly to the instance variable) is a bit better than your third option (passing it in). But, as discussed above, extracting a model method is better than both of the other two options.

In my opinion Modifying #instance_vars from private method is okay if your controller is just 100 lines long.
Imagine a scenario where there are 500 LOC in your controller and after a struggle of a couple of hours you found out that the #intance_var is being modified by some private method.
Helpful tips:
create small private methods with single responsibility
put ! at the end of method_name! indicating that it modifies something. Specially this is helpful when you see my_private_method!, ! makes you realize that its modifying something.
lets not put code in controller that do not belong here.

There is one more option:
In Controller:
def new
#some_thing = SomeThing.new(:some_params)
#some_thing_modified = #some_thing.modify_somehow(params)
#some_thing_modified.save
end
In SomeThing Model:
def modify_somehow(params)
result = self.clone
# ... modify result ...
return result
end
Because modify_somehow is now pure function (assuming you don't do anything in ... modify result ... part, that makes it impure), what you gain here is Referential transparency. Main benefit of referential transparency is that you can determine what function/method invocation will do, only by looking at its arguments, and get result of its work only via return value, and not via side effects. This makes your code more predictable, which in turn makes it easier to understand and debug.
There are of course disadvantages: Because you create new object this option can be less performant, it's also more verbose than its alternatives.
Functional programming concepts, like referential transparency, are not very popular in Rails community (probably because of how OO-centric Ruby is). But referential transparency is there if you want it, with its pros and cons.

Related

Rails best practice for calling a model method from a different controller

I have Model_a, model_a_controller and Model_b. I have a method in model_b that I would like to call in model_a_controller. But I am not sure what the best practice for doing so would be. This is my method right now:
def method
id = Model_b.method
.... some other stuff
end
Im calling the method directly from the model but is it better rails practice to perhaps make an instance of the model and then call the method from that instance? Something like
def method
#model_b = Model_b
id = #model_b.method
.... some other stuff
end
Im not quite sure what the best practice would be or if my alternative would make sense; Any pointers are welcome.
It depends what you'd like to achieve and what's the context. I can only answer with some quick guiding questions that might be a start point for your own decision making.
I see two different threads in you question:
Class method vs instance method
For this it doesn't matter where you call the method_b. It's about its logic and responsibility. What does this method suppose to do? Do you need a Model B instance (e.g. #instance_B.valid?)? Or is it a behaviour related to class only or all Model B instances (e.g. Model_B.count)? Think about the outcome of this method and what you need to achieve it.
Where to call the method_b
As a rule of thumb, a good practice in rails is to keep skinny controller.
Is there any relationship between model A and model B? Then probably it's better to call the method_b in the model A. Maybe you should consider adding the service layer for that?
https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial
Consider relationships between models and the controller actions.
There are no strict rules that always work. It depends on the business context, the app design and direction of the development.
If you provide more specific example, then we can give more specific advice and discuss pros & cons of different approaches.
Maybe you can create a service object like this:
In app, create a folder services(app/services)
Inside services folder you can organize all the services for your project.
services/model_b_service/model_b_method.rb
module ModelBService
class ModelBMethod
def initialize
end
def call
.. logic goes here
end
end
So, in your controller:
def method
id = ModelBService::ModelBMethod.new.call
.... some other stuff
end
If you want to pass arguments just ModelBService::ModelBMethod.new(args).call and fetch in initialize method

Proper organization of server side code?

I'm relatively new to rails, but I've made a few basic CRUD apps. However, this time, I'm making something different, and I'm not sure how to organize it.
It's essentially a one page app. The user fills out a form, and the code does some calculations based on those values. I have all the code written, but it's all just sitting in the controller. I'm assuming that this isn't correct.
There are two parts:
Using an external API, 2 constant arrays are generated. I need these variables to be global, or at least accessible for the calculator function.
I have a function that takes some inputs from the form that also calls other functions. A simplified version is below. I could put all the code into one function if that's necessary. I have them separate just so that the code is more readable.
def calc(input)
func1(input)
func2(input)
# do more stuff
return answer #I need to show this in the view
end
def func1(a)
end
def func2(b)
end
So, where should I put each part of this code?
To make your controllers thin, you can keep business logic at Service Objects.
Just create "services" directory at "app", add there some class like "user_searcher.rb".
And call it in the controller, passing all necessary data.
Such technique will help you to isolate business logic and incapsulate it in separate class.
BTW read this article http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
I think, from what I understand of you question, is this code should be placed in the helper classes. If you have dedicated class for this calculation, you can use class attributes to access array to access anywhere in the class or declare them constant, in case they are constant.
I don't think making global is a good practice, just because this is needed in some other function, instead return that variable and pass them as parameter where they are needed.

Rails - How do Controllers pass instance variables to Views...can it be stopped?

I understand and appreciate that by putting # in front of a variable name in a Controller that it becomes available in whatever View is loaded. This is wonderfully useful, but I would like to understand the magic. How does it happen, and can it be stopped?
I am trying to DRY my CRUDdy resource controllers using inheritance, placing most of the logic in ApplicationController. The superclass should refer to the abstract variables #resource (for a single resource), #resources (for a collection of resources), and #parent_resource (for the parent resource when #resource is nested), but ideally the view would get more concrete names, for example;#customer, #customers, and #sales_territory respectively. Can this be done without sending duplicates of all objects (once in the abstract name, and once in the concrete name) to the view?
As I write this, the possibilities that come to mind are;
protected instance variables...does Ruby have such a thing, and
if so does the Controller magic hand them to the View?
setting the generic named variables to nil before render/redirect
using a protected empty method defined in the subclass to instead of
abstract named instance variables
What is the right choice in how to implement this?
What I'm assuming is happening here, is that there's a bunch of controllers in your app that literally just do the same thing and so you are wanting to make use of inheritance to DRY it up.
That being said, I'm not entirely sure the ApplicationController is the right place to dump all of this functionality as in the future if you had new controllers, they would also inherit all of this functionality without necessarily needing it.
I would do something like this:
Assuming you have controllers like this:
lions_controller.rb
tigers_controller.rb
hippos_controller.rb
and they pretty much have similar functionality... I would create a "Base" controller and then setup inheritance on child controllers. I would then also make an action that sets the "logical" defaults of child controllers, something like this.
AnimalsController.rb
class AnimalsController < ApplicationController
class_attribute :resource_class, :parent_resource_class
protected
def self.set_resource_attributes(options={})
self.resource_class = options[:resource_class]
self.parent_resource_class = options[:parent_resource_class]
end
end
LionsController.rb
class LionsController < AnimalsController
#call methods in AnimalsController here, start with setting the resource name
set_resource_attributes :resource_class => Lion, :parent_resource_class => Animal
end
and so on and so forth... the other thing that may be useful is to make use of the methods "instance_variable_set" so that you can set instance variable names in the view that actually make sense... You can make use of the class variables you just set to do this... so for example, lets re-open the AnimalsController.rb class:
class AnimalsController < ApplicationController
def show
instance_variable_set("##{self.resource_class.name.underscore}".to_sym, self.resource_class.find(params[:id]))
#... all the regular show stuff
end
end
This way, when you go to the lions#show path, what you will get in your view is access to a variable named #lion which will be set and contain an instance of the Lion class found through ActiveRecord.
Of course this pseudo code I threw in here can be cleaned up and DRY'd a bit more, but hopefully you get where I'm going with it. Hopefully this helps.

How can I keep an instance variable DRY in a controller helper with Rails?

I am writing this widget that is to be included with a single page of the site, so I don't see a reason to declare what I am doing in the ApplicationController. However, this new widget does have a fair amount of complexity to it, so I want to keep it separate from the controller that normally handles this page since that controller already has quite the bit of complexity in it. I originally figured in my design I could just make a helper module for the controller and keep most of the widget logic there.
I am trying to write this helper module, but then I realized that there is a particular array I need to be able to initialize from a single location, but multiple methods need access to it and it needs to be insured that it is initialized before any other method makes calls to this array. Is there a way to do something similar to a before_filter in such a module where I can ensure this variable is initialized before use or is there a better overall way of approaching this that is the "Rails way"?
I think you can create a new class and instantiate it in the before_filter or your controller.
Not sure what you want to achieve but an example could be :
class MyArrayBuilder
def initialize(some, param)
#something
end
def get_array
return my_array
end
end
class SomethingsController < ApplicationController
before_filter :build_array
def build_array
#array = MyArrayBuilder.new(some, param).get_array
end
end
Then you pass the #array variable to your helper.
I'm not sure there is a "Rails way" of doing what you want : Rails as a lot of convention but when you start to care a lot about design and DRY, I believe it's better to think in term of good practice for Oriented Object Programming than Rails convention.
You may in need of a good presenter. Look for it in ruby toolbox.
The presenter is a middle ground between the controller and the view. It can access data, and handle complex logic, which is related to presentation, and not to data manipulation. It can do lot of things clean and simple. Check it out, maybe it is good for your problem.
Tutorial for draper.

Proper technique when passing data between methods

If you have two methods in a model or controller and you want to pass a variable between methods e.g.
def foo
#param = 2
#test = 1
callee
#do something with #test
end
def callee
#test += #param
end
Is it better to use instance variables to do this or regular variables like so
def foo
param = 2
test = 1
test = callee(param, test)
#do something with test
end
def callee(param, test)
test += param
test
end
Thanks in advance!
There isn't a definite answer to this question, it depends a lot on the context - the thing you need to ask is "which approach best demonstrates the intent of the code". You should definitely have tests for the model/controller class you are talking about.
As a very rough guideline:
The first approach is commonly seen when the method is part of the class's public API and it alters the internal state of instances of the class (although it may be the sign of a code smell if you have public methods chained as in your example.) This is probably going to be seen more often in a model object.
The second approach is usually seen when the method you are calling is a private convenience method that factors out some code duplication, or a method which does very specialised operations on the parameters and returns some result (in which case it should probably be factored out into a utility class.) This may be seen in model or controller objects.
If you are aiming for maintainable OO code, then the principles of SOLID design are very good guidelines - have a look at Uncle Bob's article about them here:
http://blog.objectmentor.com/articles/2009/02/12/getting-a-solid-start
It depends on your needs. Also, prototype of the function that you are passing variables to is also important. If you want the method not to change any of the parameters without your permission, you should use your second implementation. But, if you trust the function, you can use first method. This is a big topic called as "call by reference" and "call by value". You can examine following link;
http://www.exforsys.com/tutorials/c-language/call-by-value-and-call-by-reference.html

Resources