I am having a tough time grasping the idea of completely avoiding globals in Ruby.
To my understanding, if I define a method, the method would be considered global because I can call the method later on in the script. Same goes for classes. Can you completely avoid globalsl?
Research has pointed my inconclusively towards closures and singleton methods but I am still having trouble understanding how I would 'completely avoid globals.'
EDIT: I have also programmed a bit in JavaScript and used closure as follows to avoid the use of any globals: (function(){...})(); Can something similar be done in Ruby?
It is important to understand the reasoning behind avoiding globals. The main reason is avoiding global state. By storing variable information in a global variable, you are allowing components of the program to behave differently when used in the same way at different times. This usually results in unintended side-effects, causing testing and maintenance issues. Global classes or methods are unable to change (not considering reflection) and are not an issue because of that.
Another thing you may have associated with globals is namespace pollution, which can be partially resolved by nesting namespaces in a way that groups components semantically. Those are still global, though and thus not really avoidable.
Modules and Classes as globals are not a problem.
You could use a module to conceal a class, but ultimately you're going to have some globals.
Avoiding global variables and methods would be advised, though.
Related
I'm wondering what benefit discriminating between local and global variables provides. It seems to me that if everything were made a global variable, there would be a lot less confusion.
Wouldn't declaring everything a global variable result in fewer errors because one wouldn't mistakenly call a local variable in a global instance, thereby encountering fewer errors?
Where is my logic wrong on this?
Some of this boils down to good coding practices. Keeping variables local also means it becomes simpler to share code from one application to another without having to worry about code conflicts. While its simpler to make everything global, getting into the habit of only using global variables when you actually have to will force you to code more efficiently and will make your code more structured.
I think your key oversight is thinking that an error telling you a local variable doesn't exist is a bad thing - it isn't. You've made a mistake and ruby is telling you so. This type of mistake is usually easy to fix: you've misspelled something or you're using something that you forgot to create.
Global variables everywhere might remove those errors but they would replace them with a far harder set of errors to reason about: accidentally using a variable that another bit of code is using. Imagine if every time you called a function (one of your own or a standard library one or one from a gem) you had to check which global variables it might change (and which functions it called, since it might also change global variables) If you make a mistake then you might get an error message (if the class of the object in the variable changes enough) but often you would just silently get incorrect results (if the value of a variable you were using changes unexpectedly).
In general global variables are much harder to work with and people avoid them when possible.
If all variables are global, every line of code in every program (including those which haven't been written yet) written by every programmer on the planet (including those who haven't been born yet or are already dead) must universally, uniquely agree on the names of variables. If you use a variable name that someone else on a different continent two years from now will also use, both of your programs will break, when used together.
Does Ruby has static global variables?
With this I mean global variables only accessible from the file where they were defined.
Short answer: No.
The long answer is more complicated.
There's only one global namespace in Ruby and any alterations to it from any code will have the effect of changing it for all code. To keep things local you need to scope them to a particular context, typically module or class. For example:
module PrivateStuff
#private_variable = "Private (mostly)"
def self.expose_private_variable
#private_variable
end
end
Note this doesn't prevent others from accessing your private variables using instance_variable_get or techniques like that.
This usually isn't a big deal since global variables are usually a sign of bad design and should be avoided unless there's no alternative, a case that's exceedingly rare.
Unlike compiled languages which enforce very strict rules when it comes to data access, Ruby leaves it up to the programmer to be disciplined and simply not do it in the first place.
For example, is it theoretically safe to modify Object#object_id since there's always Object#__id__ if you really need to know what an object's id is?
Background: Curiosity piqued by What's another name for object_id?
In an ideal system where everything is perfectly documented and all people working with the code are aware of what's been re-defined and patched - then yes, maybe.
But as we all know, such situations rarely exist. Personally, I feel that patching anything already defined in Kernel, Class, Module or Object is a no-no (unless you're doing it at a framework level.)
Ultimately, I believe that Principle of Least Surprise should permeate coding decisions at all levels.
is it theoretically safe to modify Object#object_id
Well, I think we are probably more concerned with reality than theory here. The fact is, people aren't going to use the __X__ version until they realize that you have overriden and completely jacked up the default behavior. With power comes responsibility; use monkey-patching carefully and never introduce unexpected behavior. Better just to add a new method to the class instead.
I've never seen global variables used in any Ruby code. I understand that their use is frowned upon across languages but they seem actually useless in Ruby. Can anyone point to properly designed code that uses them?
If I'm right and they're redundant/historical, why do they persist in 1.9?
To be clear, I don't mean variables that Ruby sets up for you like $" and $stdin. I mean uses in one's own code.
The only time I see it in decent code is for a log.
$log = Logger.new('foo.log', 'daily')
A constant would probably do fine, but it somehow feels strange calling methods on a constant.
Environment variables are usually Global variables in Ruby.
So are CLASSPATH in jruby and so on...
Also, you can implement cheap singletons using global variables (although it's not advisable).
So, global variables definitely have a place in Ruby.
I have inherited an existing code base where the "features" are as follows:
huge monolithic classes with
(literally) 100's of member variables
and methods that go one for pages
(er. screens)
public and private methods with a large number of arguments.
I am trying to clean up and refactor the code, to leave it a little better
than how I found it. So my questions
is worth it (or do you) refactor methods with 10 or so arguments so that they are more readable ?
are there best practices on how long methods should be ? How long do you usually keep them?
are monolithic classes bad ?
is worth it (or do you) refactor methods with 10 or so arguments so that they are more readable ?
Yes, it is worth it. It is typically more important to refactor methods that are not "reasonable" than ones that already are nice, short, and have a small argument list.
Typically, if you have many arguments, it's because a method does too much - most likely, it should be a class of it's own, not a method.
That being said, in those cases when many parameters are required, it's best to encapsulate the parameters into a single class (ie: SpecificAlgorithmOptions), and pass one instance of that class. This way, you can provide clean defaults, and its very obvious which methods are essential vs. optional (based on what is required to construct the options class).
are there best practices on how long methods should be ? How long do you usually keep them?
A method should be as short as possible. It should have one purpose, and be used for one task, whenver possible. If it's possible to split it into separate methods, where each as a real, qualitative "task", then do so when refactoring.
are monolithic classes bad ?
Yes.
if the code is working and there is no need to touch it, i wouldn't refactor. i only refactor very problematic cases if i anyway have to touch them (either for extending them for functionality or bug-fixing). I favor the pragmatic way: Only (in 95%) touch, what you change.
Some first thoughts on your specific problem (though in detail it is difficult without knowing the code):
start to group instance variables, these groups will then be target to do 'extract class'
when having grouped these variables you hopefully can group some methods, which also be moved when doing 'extract class'
often there are many methods which aren't using any fields. make them static (they most likely are helper methods, which can be extracted to helper-classes.
in case non-related instance fields are mixed in many methods, do loads of 'extract method'
use automatic refactoring tools as much as possible, because you most likely have no tests in place and automation is more safe.
Regarding your other concrete questions.
is worth it (or do you) refactor methods with 10 or so arguments so that they are more readable?
definetely. 10 parameters are too many to grasp for us humans. most likely the method is doing too much.
are there best practices on how long methods should be ? How long do you usually keep them?
it depends... on preferences. i stated some things on this thread (though the question was PHP). still i would apply these numbers/metrics to any language.
are monolithic classes bad ?
it depends, what you mean with monolithic. if you mean many instance variables, endless methods, a lot of if/else complexity, yes.
also have a look at a real gem (to me a must have for every developer): working effectively with legacy code
Assuming the code is functioning I would suggest you think about these questions first:
is the code well documented?
do you understand the code?
how often are new features being added?
how often are bugs reported and fixed?
how difficult is it to modify and fix the code?
what is the expected life of the code?
how many versions of the compiler are you behind (if at all)?
is the OS it runs on expected to change during its lifetime?
If the system will be replaced in five years, is documented well, will undergo few changes, and bugs are easy to fix - leave it alone regardless of the size of the classes and the number of parameters. If you are determined to refactor make a list of your refactoring proposals in the order of maximum benefit with minimum changes and attack it incrementally.