Proper technique when passing data between methods - ruby-on-rails

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

Related

RSpec: How can I not use `allow_any_instance_of` for objects that get instantiated in the functions I call?

I have a class A with a method M for which I want to write a test T. The problem is that method M creates a new object O. I want to mock a method F of that new object O.
class A
def M(p1, p2)
#o = O.new(p1, p2)
end
end
class O
def F(q)
...
end
end
I can very easily do so with the allow_any_instance_of feature of RSpec, but I really don't see a way of doing so with just allow or expect. I understand that I can mock a method of an existing instance and of a class but from my tests I couldn't make it work against methods of objects that get created in a method I'm testing.
T :process do
it "works" do
# This works
allow_any_instance_of(O).to receive(:F).and_return(123)
...
end
it "does not works" do
# This fails
allow(O).to receive(:F).and_return(123)
...
end
end
How do I know that it fails?
I changed my F method with a puts() and I can see that output on the screen when I use the allow(O). It does not appear at all when I use the allow_any_instance_of(). So I know that it's working as expected only in the latter.
def F(q)
puts("If I see this, then F() was not mocked properly.")
...
end
I would think that allow(O)... should connect to the class so whenever a new instance is created the mocked functions follow, but apparently not.
Do you have RSpec tests handling such mocking cases in a different way that would not involve the use of the allow_any_instance_of() function?
The reason I ask is because it is marked as obsolete (#allow-old-syntax) since RSpec 3.3 so it sounds like we should not be using this feature anymore, especially once RSpec 4.x comes out, it probably will be gone.
The reason this
allow(O).to receive(:F).and_return(123)
Doesn't work is that :F is not a method of O, so the O never receives this message (method invocation).
The best solution for you would be to refactor your code to use dependency injection. (Please note that your example is abstract to the extreme, if you provided a real life example - closer to the ground - some better refactoring might be possible)
class A
attr_accessor :o_implementation
def initialize(o_implementation)
#o_implementation = o_implementation
end
def M(p1, p2)
#o = o_implementation.new(p1, p2)
end
end
RSpec.describe A do
subject { described_class.new(klass) }
let(:klass) { O }
let(:a_double) { instance_double(klass) }
it do
allow(klass).to receive(:new).and_return(a_mock)
allow(a_double).to receive(:F).and_return(123)
end
end
With the Dependency injection you move outside the decision which class to instantiate. This decouples your code (A stops being coupled to O, now it depends only on the O interface that it's using), and makes it easier* to test.
(*) One could argue that allow_any_instance is easier (less involved, less typing), but it has some issues, and should be avoided if possible.
(as a small aside: I can understand the probable need for very thorough anonymization of your code, but you could still follow ruby style guide: methods start with lower-case, only classes start with upper-case)
So first off: allow(O) works, but will only capture class methods. If you need to capture instance methods, you need to call allow for a specific instance.
Since your example is pretty sparse, I see no reason why we could not split up the creation of the object from the test? If that is possible, a very simple approach would be to write something like:
describe :process do
before do
#o = A.o_maker(p1,p2)
allow(#o).to receive(:some_function) { 123 }
end
it "works" do
# do something with `#o` that should call the function
end
end
I personally prefer this approach over creating the mock class, as suggested before.
This is probably well known, but for clarity: the problem with a mock class imho is that you are no longer testing class A but the mock. This could in some cases be useful, but from the original question it is unclear if it applies in this case and if this is not needlessly complicated. And secondly: if your code is this complicated (e.g. some method that creates a new object and then calls F), I would rather 1) refactor my code to make it test-able, and/or 2) test side effects (e.g. F adds an audit-log-line, sets a state, ...). I do not need to "test" my implementation (is the correct method called), but is it performed (and of course, as always, there are exceptions e.g. when calling external services or something --but again all that is impossible to deduce from the original question).

ABCService.new.do_foo - why not a class method?

I just inherited an RoR codebase, and in many of the controllers I see the following style of code:
ABCService.new.do_foo
I have been working on RoR codebases for quite a long time, but I fail to understand why the .new. style is used. The service classes in question do not hold any state (even with class-level variables) and the same can be achieved with self (ie class-level) methods - so, any explanation of why this style is better? To me, it looks like some java developers coded this app and "ported over" some coding paradigms from that language.
Making service objects stateful as a convention has its benefits:
Minimal refactoring when one of them requires state
Easy to mock in tests without messing around with constants
Save brain juice on this decision when implementing a new service object
That being said, whether this is beneficial for your codebase is something you / your team need to assess as part of defining your own architectural / code style conventions.
It can be quite irritating to always have to call Klass.new.do_something. You can wrap it in a class method, eg:
class Service
class << self
def do_something
new.do_something
end
end
def do_something
end
end
"Right tool for the job"
Having only class method, will explicitly tell other developers/readers of your code, that this service doesn't have state.
Even better, use module instead of class, then your intentions would be clear for others and for later you.
When you need state, use instance method.
For example you can have service which accepts two arguments, first one is argument which should be used for all calls of this service, but second can be change for every call.
class AddTax
def initialize(tax_rate)
#tax_rate = tax_rate
end
def to(amount)
amount * (1.0 + #tax_rate)
end
end
# Usage
prices = [20, 100, 50, 49, 50]
add_tax = AddTax.new(0.24);
with_taxes = prices.map { |price| add_tax.to(price) }

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

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

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.

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.

Resources