Use parentheses to enclose a block in Ruby? - ruby

I have accidentally discovered the Ruby idiom of ||=(),
as in:
def app_logger
#app_logger ||= (
logfile = File.open(::Rails.root.join(LOG_FILE), 'a')
logfile.sync = true
AppLogger.new(logfile)
)
end
I tried to use {} instead of (), but it didn't work. I thought {} is meant to enclose a block.
Is this a known idiom? Is it a good style?
I haven't found much documentation on this kind of use of parentheses. Any pointers would be helpful.
Please note this post is about the use of () this way, not the use of ||=. There are many posts about this latter idiom already.

Like a lot of things in Ruby that can be done, many of them shouldn't be done and this is one of them.
Using brackets to group code when there are already other facilities is potentially confusing and almost certainly contrary to many coding style guides. If I saw this in code I was managing, I'd immediately fix it.
What's best is to use the begin/end markers to make it completely clear what's going on:
def app_logger
#app_logger ||= begin
logfile = File.open(::Rails.root.join(LOG_FILE), 'a')
logfile.sync = true
AppLogger.new(logfile)
end
end
A lot of things in Ruby evaluate down to a single value, and the contents of (...) are apparently one of those things.
Your other example of foo ||= (a=10; a+10) being "nicer" is also rather controversial. This does two assignment and addition, but only conditionally. This one would almost always be better written in long-form with begin/end to make it clear that a+10 is the result.
From a style perspective, hiding the "important" part of that, the a+10, at the end of a line is bad, it can be overlooked. Having it as the last line makes it abundantly clear. This is also why having if statements tacked on the end of long lines is also bad, it hides that the line is only conditionally executed.
Concern for brevity is always trumped by concerns of readability. Saving a couple of bytes on disk is not going to help you one bit when, due to someone misreading your code, they introduce a crippling bug.
That someone could be you in the future when you get caught up in your own cleverness. It's happened to all of us at some point.

In addition to #tadman's answer, I'd counter that writing the method in a more Ruby-like way maintains the readability and keeps it nice and tight:
def app_logger
return #app_logger if #app_logger
logfile = File.open(::Rails.root.join(LOG_FILE), 'a')
logfile.sync = true
#app_logger = AppLogger.new(logfile)
end
For my intent and rationale in writing it this way, see my comment to this answer in response to #fotanus's comment.
Our brains become conditioned to see patterns as we learn to program and learn new languages. Whether the patterns are in hexcode emitted by a debugger or C, Perl, Python, Java or Ruby, we still see patterns. When we're used to seeing them we tend to want to change code to resemble those familiar constructs. That's why Perl and C programmers tend to write Java, Python and Ruby like C and Perl at first. The "beauty is in the eye of the beholder", but that same beauty is a weed when it's out of place, just as wild-flowers are out of place in formal gardens or the middle of a fairway. We should write in the vernacular of the programming language, because that is the way of speaking expected by those who live in that land.
Something to remember is, though we, personally, might be a code-studly monster who can reduce code from multiple lines down to a single character, what will keep me in a job is the ability to write code other people can understand, without necessarily having to be of the same caliber. Code crunching invariably reaches a point of diminishing returns, well before it has been reduced to its minimal size, especially when that code is running in a production environment and is being maintained by junior programmers and there is a need to extend or debug it, and the clock is ticking. Minutes = dollars where I work, and the dollars have really big multipliers, which causes the walls to leak managers like cockroaches when a bug-bomb goes off. (Oh... did I call managers cockroaches? Whatever.)
Being called the morning after an event and being thanked for writing code that made it easy for them to debug/fix or amend/extend is a whole lot better than being called at 2:45AM and asked to get online to help. I value my sleep and that of my coding-partners and push for maintainable code as our #1 priority.
That's my $0.02.

Parentheses are really ugly for in my opinion. Why you don't delegate logic behind creating logger to method?
def app_logger
#app_logger ||= instantiate_app_logger
end
def instantiate_app_logger
logfile = File.open(::Rails.root.join(LOG_FILE), 'a')
logfile.sync = true
AppLogger.new(logfile)
end
But I am afraid that it breaks some OOP, your code also. Why AppLoger class can't open file based on passed path and turning syncing on? It will be so much cleaner.

Related

Ruby nested assignment and bracketless method calls

I'm surprised by a particular bit of syntax-sensitivity in Ruby. These all work:
var = method arg
var2 = (var1 = method arg)
method2(method1 arg)
But this does not:
method2(var = method1 arg)
Instead, I have to do either this, with extra parentheses:
method2(var = method1(arg))
..or this, which I find much more ambiguous than the version that fails:
method2 var = method1(arg)
I assume that this is either a specific design decision or the side effect of another one, and would appreciate any insight into those decisions.
Please note that I'm not looking for any opinions about style; I'm not asking what looks better, or what you think should or should not work. I will even stipulate that this particular construct would be clearer if split into two separate statements entirely. I'm just curious about the actual reasons why Ruby works this way, from anyone who might have that background information.
I assume that this is either a specific design decision or the side effect of another one, and would appreciate any insight into those decisions.
Ruby's syntax is ridiculously complex. And since most Ruby implementations use a parser generator like Bison, which however isn't actually powerful enough to parse such a ridiculously complex language, the parsers tend to be even more ridiculously complex. It's much more likely that it's two weird parsing corner cases interacting in an even weirder way than any sort of conscious design decision.

How to make Ruby's N ends look better?

As I write some scripts, I usually reach a point where my code looks like this :
end
end
end
end
end
end
I don't know about you, but this look very ugly to me. Can something be done about this?
Don't nest your code so much? Refactor to use more methods? Use blocks passed to other routines instead?
Generally speaking, deep nesting is an indicator that a method is getting too complex and should be broken up. It can help for implicit structural documentation too, by naming the inner compound statements according to their refactored methods.
The advice to break up into smaller pieces is good. But if you need a lot of nested blocks like that, you can label the end keywords with comments.
end # End conditional statement
end # End method declaration
end # End class declaration
Still ugly, but at least clearer.
The other options previously mentioned are preferable.
If those inner blocks do something easy to name (and maybe reusable?), why not refactor them into small separate functions ? Then you'd end up with much shorter sequences of end's.
Otherwise another approach is using Python :-)
Try to use small, testable functions. Not only are your functions and more importantly logic easy to test, but your code becomes way more readable.
I have seen nested "{ }" blocks and 4-space soft tabs and:
end;end;end;end
I suppose this saves vertical space, but I don't recommend, The above comments on avoiding deep nesting and commenting your block-ending lines are the valid approaches. Maybe deep nesting is to avoid method call overhead for things that need speeding up, but readability almost always trumps that kind of "optimization"
If you're happy to compile your own Ruby, you could use ennnnnnnd style syntax (link is to RubyKaigi talk). Unfortunately for you, this has been suggested and rejected by Ruby core.

Are there any Ruby language features you avoid?

It seems to me like Ruby has a lot of syntactic flexibility, and many things may be written in several ways.
Are there any language features/syntactic sugar/coding conventions that you avoid as a Ruby programmer for clarity? I am asking about things that you choose not to use on purpose, not stuff you still have to learn.
If your answer is, "I use everything!," do you ever comment code that would be otherwise obvious if the reader knows the relevant Ruby syntax?
[I'm particularly interested in Ruby in a RoR context, but everything is welcome.]
The whole range of "$" globals (see Pickaxe2 pp333-336) mostly inherited from Perl, are pretty ghastly, although I have on occasion found myself using $: instead of $LOAD_PATH.
I generally refrain from going overboard with monkey patching because it could lead to some maintainability and readability issues. It's a great feature if used correctly, but it's easy to get carried away.
The for ... in ... loop. It directly compiles to obj.each (and throws a strange error message accordingly) and is totally unnecessary. I don't even see where it improves readability - if you've been around ruby for more than a week, #each should be natural.
This might be obvious but I generally avoid using eval if there's any alternative.
First off: I'll break many of these rules if it's for a short one-off script, or a one-liner on the command line, or in irb. But most of my time is spent in medium sized or larger scripts or applications. So:
Avoid:
using class << self code block for class methods. It's a cute trick, but no better than def self.foo, and less readable (especially after the first page).
for i in collection: Use collection.each instead.
proc {...}: usually lambda {...} is better.
class variables (e.g. ##foo). They are problematic and can usually be replaced with class-level instance variables without much effort.
Anything that causes a warning, and preferably anything that causes a warning when run via the more strict ruby -w. This is especially important if you are writing a gem to be used by others.
'else' on a 'begin ... rescue ... end' block. Personal preference: It's too much of an edge case and so few people even know it exists or how it works to be worth it.
ObjectSpace and GC. You probably don't need to go there. You definitely don't want to go there.
=begin and =end multi-line comments. Personal preference for line-wise comments. These just annoy the hell out of me.
Use it, but sparingly or as a last resort (and comment it appropriately):
eval (or class_eval, etc) when passing in a string. There are some metaprogramming tricks you just can't do without passing in a string. And occasionally, the string version performs dramatically better (and sometimes that matters). Otherwise, I prefer to send in blocks of actual ruby code for my metaprogramming. eval can be avoided entirely for many metaprogramming tasks.
Adding or redefining methods on classes which were not created by me and may be used by code outside my control; a.k.a. monkey-patching. This rule is mostly for larger codebases and libraries; I'll gladly and quickly make an exception for small one-off scripts. I'll also make an exception for fixing buggy third-party libraries (although you may shoot yourself in the foot when you upgrade!). Selector namespaces (or something similar) would go a long way to make ruby nice in this regard. That said, sometimes it is worth the trouble. ;-)
Global variables (except for classes). I'll even pass in $stdout as a parameter to my objects or methods, rather than use them directly. It makes re-use of the code much easier and safer. Sometimes you can't avoid it (e.g. $0, $:, $$ and other environmental variables, but even then you can limit your usage).
speaking of which, I prefer to limit my usage of the perlish symbol globals entirely, but if they need to be used more than a little bit, then require "English".
break, redo, next, try: Often they make a block, loop, or method much more elegant than it otherwise could be. Usually they just make you scratch your head for a few minutes when you haven't seen that code for a while.
__END__ for a data-block. Excellent for a small one-file script. Unhelpful for a multi-file app.
Don't use it, but don't really avoid it either:
try/catch
continuations
Things I use often, that others might not care for, or I don't see often:
'and' and 'or' keywords: they have different precedence from && and ||, so you need to be careful with them. I find their different precedence to be very useful.
regex black magic (provided I've got some examples in unit tests for it)
HEREDOC strings
One thing I really loathe is "improper" use of {} and do ... end for blocks. I can't seem to find exactly where I learnt the practice myself but it is generally accepted to do {} for single line blocks and do ... end for multiline blocks.
Proper use:
[1, 2, 3, 4].map {|n| n * n }.inject(1) { |n,product| n * product }
or
[1, 2, 3, 4].inject do |n,product|
n = n * n
product = n * product
end
Improper use:
[1,2,3,4].map do |n| n * n end.inject(1) do |n,product| n * product end
or
[1, 2, 3, 4].inject { |n,product|
n = n * n
product = n * product
}
All of which, of course, will execute giving 576
Avoid chaining too many method calls together. It is very common in Ruby to chain methods together.
user.friends.each {|friend| friend.invite_to_party}
This may seem ok, but breaks Law of Demeter:
More formally, the Law of Demeter for functions requires that a method M of an object O may only invoke the methods of the following kinds of objects:
O itself
M's parameters
any objects created/instantiated
within M
O's direct component objects
The example above isn't perfect and a better solution would be something like this:
user.invite_friends_to_party
The example's problem isn't Ruby's fault but it is very easy to produce code that breaks Law of Demeter and makes code unreadable.
In short, avoid features that decreases code readability. It is a very important that the code you produce is easy to read.
begin nil+234 rescue '' end
above syntax is valid, but you should never use it.

What is the most obfuscated code you've had to fix?

Most programmers will have had the experience of debugging/fixing someone else's code. Sometimes that "someone else's code" is so obfuscated it's bad enough trying to understand what it's doing.
What's the worst (most obfuscated) code you've had to debug/fix?
If you didn't throw it away and recode it from scratch, well why didn't you?
PHP OSCommerce is enough to say, it is obfuscated code...
a Java class
only static methods that manipulates DOM
8000 LOCs
long chain of methods that return null on "error": a.b().c().d().e()
very long methods (400/500 LOC each)
nested if, while, like:
if (...) {
for (...) {
if (...) {
if (...) {
while (...) {
if (...) {
cut-and-paste oriented programming
no exceptions, all exceptions are catched and "handled" using printStackTrace()
no unit tests
no documentation
I was tempted to throw away and recode... but, after 3 days of hard debugging,
I've added the magic if :-)
Spaghetti code PHP CMS system.
by default, programmers think someone else's code is obfuscated.
The worse I probably had to do was interpreting what variables i1, i2 j, k, t were in a simple method and they were not counters in 'for' loops.
In all other circumstances I guess the problem area was difficult which made the code look difficult.
I found this line in our codebase today and thought it was a nice example of sneaky obfuscation:
if (MULTICLICK_ENABLED.equals(propService.getProperty(PropertyNames.MULTICLICK_ENABLED))) {} else {
return false;
}
Just making sure I read the whole line. NO SKIMREADING.
When working on a GWT project, I would reach parts of GWT-compiled obfuscated JS code which wasn't mine.
Now good luck debugging real obfuscated code.
I can't remember the full code, but a single part of it remains burned into my memory as something I spend hours trying to understand:
do{
$tmp = shift unless shift;
$tmp;
}while($tmp);
I couldn't understand it at first, it looks so useless, then I printed out #_ for a list of arguments, a series of alternating boolean and function names, the code was used in conjunction with a library detection module that changed behaviour if a function was broken, but the code was so badly documented and made of things like that which made no sense without a complete understanding of the full code I gave up and rewrote the whole thing.
UPDATE from DVK:
And, lest someone claims this was because Perl is unreadable as opposed to coder being a golf master instead of good software developer, here's the same code in a slightly less obfuscated form (the really correct code wouldn't even HAVE alternating sub names and booleans in the first place :)
# This subroutine take a list of alternating true/false flags
# and subroutine names; and executes the named subroutines for which flag is true.
# I am also weird, otherwise I'd have simply have passed list of subroutines to execute :)
my #flags_and_sub_names_list = #_;
while ( #flags_and_sub_names_list ) {
my $flag = shift #flags_and_sub_names_list;
my $subName = shift #flags_and_sub_names_list;
next unless $flag && $subName;
&{ $subName }; # Call the named subroutine
}
I've had a case of a 300lines function performing input sanitization which missed a certain corner case. It was parsing certain situations manually using IndexOf and Substring plus a lot of inlined variables and constants (looks like the original coder didn't know anything about good practices), and no comment was provided. Throwing it away wasn't feasible due to time constraints and the fact that I didn't have the specification required so rewriting it would've meant understanding the original, but after understanding it fixing it was just quicker. I also added lots of comments, so whoever shall come after me won't feel the same pain taking a look at it...
The Perl statement:
select((select(s),$|=1)[0])
which, at the suggestion of the original author (Randal Schwartz himself, who said he disliked it but nothing else was available at the time), was replaced with something a little more understandable:
IO::Handle->autoflush
Beyond that one-liner, some of the Java JDBC libraries from IBM are obfuscated and all variables and functions are either combinations of the letter 'l' and '1' or single/double characters - very hard to track anything down until you get them all renamed. Needed to do this to track down why they worked fine in IBM's JRE but not Sun's.
If you're talking about HLL codes, once I was updating project written by a chinese and all comments were chinese (stored in ansii) and it was a horror to understand some code fragments, if you're talking about low level code there were MANY of them (obfuscated, mutated, vm-ed...).
I once had to reverse engineer a Java 1.1 framework that:
Extended event-driven SAX parser classes for every class, even those that didn't parse XML (the overridden methods were simply invoked ad hoc by other code)
Custom runtime exceptions were thrown in lieu of method invocations wherever possible. As a result, most of the business logic landed in a nested series of catch blocks.
If I had to guess, it was probably someone's "smart" idea that method invocations were expensive in Java 1.1, so throwing exceptions for non-exceptional flow control was somehow considered an optimization.
Went through about three bottles of eye drops.
I once found a time bomb that had been intentionally obfuscated.
When I had finally decoded what it was doing I mentioned it to the manager who said they knew about the time bomb but had left it in place because it was so ineffective and was interwoven with other code.
The time bomb was (presumably) supposed to go off after a certain date.
Instead, it had a bug in it so it only activated if someone was working after lunchtime on Dec 31st.
It had taken three years for that circumstance to occur since the guy who wrote the time bomb left the company.

Ruby Equivalent of C++ Const?

I'm learning Ruby in my spare time, and I have a question about language constructs for constants. Does Ruby have an equivalent of the C++ const keyword to keep variables from being modified? Here's some example code:
first_line = f.gets().chomp()
column_count = first_line.split( %r{\s+} ).size()
print column_count, "\n"
I'd like to declare column_count to be const, because I use it below in my program and I really don't want to modify it by mistake. Does Ruby provide a language construct for doing this, or should I just suck it up and realize that my variables are always mutable?
Response to comments:
'The most likely cause of "accidental" overwriting of variables is, I'd guess, long blocks of code.' I agree with the spirit of your point, but disagree with the letter. Your point about avoiding long blocks of code and unnecessary state is a good one, but for constants can also be useful in describing the design of code inside of the implementation. A large part of the value of const in my code comes from annotating which variables I SHOULD change and which I shouldn't, so that I'm not tempted to change them if I come back to my code next year. This is the same sentiment that suggests that code that uses short comments because of good variable names and clear indentation is better than awkwardly written code explained by detailed comments.
Another option appears to be Ruby's #freeze method, which I like the look of as well. Thanks for the responses everyone.
Ruby variables in general are, well, variable.
Beyond Jeremy's answer, while entirely accurate, doesn't lead you to a Ruby style that's very "mainstream" or idiomatically sound and I wouldn't recommend it for adoption. Ruby doesn't work like C++ and generally isn't very appropriate for things that C++ is best used for. Operating systems, word processors, that kind of thing.
The most likely cause of "accidental" overwriting of variables is, I'd guess, long blocks of code. After all, if you change the value of a variable in a five-line method, it's going to be fairly apparent! If you're habitually writing blocks of code longer than, say, 10 lines, then those chunks are probably doing too many things and I strongly advise that you make efforts to break them up (increase cohesion). Localise variables as much as possible to minimise the chance of unexpected side-effects (reduce coupling).
By convention, constants in ruby are generally written in all caps such as COLUMN_COUNT. But as it was pointed out, all variables that start with a capital letter are Constants.
Variables that start with a capital letter are constants in Ruby. So you could change your code to this:
first_line = f.gets().chomp()
Column_count = first_line.split( %r{\s+} ).size()
print Column_count, "\n"
Now you'll get a warning if you try to modify Column_count.

Resources