Code re-factoring - should I move code to separate methods? - ruby-on-rails

I have a controller comparing parsed documents with databases of key words. Typical snippets are encapsulated loops looping through the parsed document, the database of key words counting the number a match and building an array of matching words. Here is an example :
#key_words = KeyWords.all
#count = 0
#array_of_matching_words = []
#parsed_document.each do |a|
#key_words.each do |word|
if a =~ /#{key_word.word}/
#count = #count+1
#array_of_matching_words = #array_of_matching_words.push "#{key_word.word}"
end
end
Both instantiated variable #count and #array_of_matching_words are passed to a view. I have accumulated numerous of those snippets for the other databases of words and I am trying to re-factor the code by moving some of this code into separate methods.
One of the option I have considered is to move the code to a private method in the controller
def get_the_number_and_the_list_of_matching_words
#key_words = KeyWords.all
#count = 0
#array_of_matching_words = []
#parsed_document.each do |a|
#key_words.each do |word|
if a =~ /#{key_word.word}/
#count = #count+1
#array_of_matching_words = #array_of_matching_words.push "#{key_word.word}"
end
end
end
I then just leave
get_the_number_and_the_list_of_matching_words
in my action controller and the view will get both #count and #array_of_matching_words.
I find this coding "style" terrible. The method "get_the_number_and_the_list_of_matching_words" appears out of the blue, it does not instantiate anything, no parameter is passed, both instantiated variable (#count, #array_of_matching_words) are not visible (they are burried at the bottom of my .rb file in the private methods section). Is this really a good way to re-factor the code or is there other option? Does it make sense to move code to a method when the method is neither an instance method nor a method requiring to pass a parameter and returning new variables?

A method name like get_the_number_and_the_list_of_matching_words is preposterously long, and if it needs to be that long to convey what it's doing then your method is overly ambitious by design. Break it up into smaller chunks.
Your example method sets three different instance variables, depends on one which isn't passed in as an argument, it's just presumed to magically be there, and has no clear objective other than to define #array_of_matching_words.
Generally including things like the type of something in the variable name is extraneous information. #matching_words is vastly preferable here.
What you should do is break out the logic that does the comparison between #key_words and #parsed_document where both of those are passed in as parameters. Rails encourages putting such methods into "concerns" or, where they're applicable to a particular model, in there if that's a good fit.
Whatever KeyWords is could be extended to produce the desired output, for example, making your code look a lot more like this:
def prepare_key_words_matching
#key_words = KeyWords.all
#matching_words = KeyWords.matching(#parsed_document)
end
Where you could then use #matching_words.length instead of having a separate #count variable.

This could be a personal preference question, but here goes.
I would recommend splitting methods to be as small as possible. The benefits are two fold:
Consider a newcomer reading the method. If it is only a few lines long (regardless of the several other methods), it will be easier to follow
Testing becomes a lot easier if you delegate logic to other methods. You now only need to test that a method is called within your method, rather than test all of that logic
I agree that using different methods within a method is obfuscating things, especially when instance variables are concerned, but one only needs to look at the method and follow which others are called by it to learn what is happening.

Related

Ruby Methods and Params Validation

I have a general questions about how to treat params on a method. Let's say, we have a method that receives a number and multiplies it by 2.
def multiplier(num)
num*2
end
What happens when num is nil? Who is responsible to handle the error? The method itself or the one who calls the method? What is considered the best oop practice?
This is not related to OOP in any way, because it applies to other paradigms as well. That said, there are different schools of thought about the problem. Which one is the "best" depends on who you're talking to.
One is "defensive programming everywhere". This means each method is responsible for rigorous checks of its input.
Another is "caller is responsible for providing correct data".
Another is what I call a "security perimeter": rigorous checks only when we deal with external input, but once data is "inside of the system", it can be trusted.
And anything in between.
I agree with the other answers (#sergio-tulentsev, #Зелёный). And there is another thing to consider.
In a lot of cases it is a good practice not to expect an object of particular type, but an object which acts like a particular type. I mean, in your case, the method could expect not only a number to be passed as a parameter, but any object that could be treated like a number. That would not only make your method more powerful, but also solve the nil problem. Of course it depends on your needs.
The method version I am talking about might look like this:
def multiplier(num)
# trying to convert whatever is passed to a number
num.to_f * 2
end
In the case of nil it will return 0.0

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

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.

Ruby - Why is it possible to use Instance variables like everywhere

I'm into Ruby on Rails programming for almost 5 weeks now.
I was wondering why people always use instance variables instead of local ones.
I always thought that you would use instance variables only for classes (so these instance variables are the attributes of the class).
But people also use them not only for being attributes of a class. And this is the part where I am getting confused.
For instance, take a look at these lines of codes:
class Foo
def print_a_hello
puts "Hello World"
end
end
#instance_variable = Foo.new
#instance_variable.print_a_hello
# => "Hello World"
locale_variable = Foo.new
locale_variable.print_a_hello
# => "Hello World"
So, who of you got a great explanation for me?
I was wondering why people always use instance variables instead of locale ones.
I'm not sure how you get that impression. I certainly don't "always" use instance variables. I use instance variables when I need an instance variable, and I use local variables, when I need a local variable, and most code I see does it the same way.
Usually, it doesn't even make sense to interchange them. They have completely different purpose: local variables have static lexical scope, instance variables have dynamic object scope. There's pretty much no way to interchange them, except for the very narrow case of a simple single-file procedural script, where the dynamic scope of the top-level main object and the lexical scope of the script body are identical.
I always thought that you would use instance variables only for classes (so these instance variables are the attributes of the class).
No. Instance variables are attributes of the instance (i.e. object), not the class, that's why they are called "instance variables", after all. Class variables are attributes of the class, but class variables are a different beast and only used in very specific circumstances. (Classes are objects (i.e. instances), too, so they can have instance variables as well; there's generally no need to use class variables, which have some weird and un-intuitive properties, unless you specifically need those weird and un-intuitive properties).
For instance, take a look on this short codelines:
class Foo
def print_a_hello
puts "Hello World"
end
end
#instance_variable = Foo.new
#instance_variable.print_a_hello
# => "Hello World"
locale_variable = Foo.new
locale_variable.print_a_hello
# => "Hello World"
This is the case I mentioned above: in this specific case (and only in this case), the dynamic scope of the top-level main object and the static lexical scope of the script body are identical, so it doesn't matter whether you use a local variable of the script body or an instance variable of the main object.
However, if we make just a tiny change to that, by adding a second script and requireing it from the first, that condition will no longer hold, because we now have two separate script bodies and thus two separate script scopes, but still only one top-level object.
The idiomatic way in your example would definitely be to use a local variable, and nobody I know would do otherwise.
Best use case for instance variables is in Controller's when you want to pass parameter to the view.
Then you use something like
class TestController < ActionController::Base
def show
#usable_in_view = Test.first
not_usable_in_view = Test.first
end
end
In your view you can now use #usable_in_view, but cant use variable not_usable_in_view. Most people always use instance variable in controllers even if they do not need them in view, because they do not understand why they need instance variable
Instance variables are used so that they can be accessed in the view page.
Local variables are not accessible in the view. It has become the habit even I sometimes write instance variables though it is not required in the view.:-)
People probably get in the [bad] habit of using instance variables everywhere since it's common in Rails to use them to get information from the controller to the view.
In my own Ruby code I use instance variables only when I need to, local variables otherwise. That's the proper way to use Ruby.

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.

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