This question already has answers here:
Do I need to indent my code in Ruby?
(3 answers)
Closed 7 years ago.
I got some errors in Ruby on Rails' code, but I don't know if that's because it's not indented. Does Ruby code work without indentation?
With a project that's strictly Ruby, your indentation will not matter. Here's a SO answer to the same question that has a good example of functioning Ruby code with poor indentation. If you're curious about proper indentation style in general, Github's Ruby style guide will be a good reference for you.
Since you mentioned Rails specifically, I imagine you might be running into errors with your template code. If you're using a template like slim or jade (your views will have .slim or .jade or .haml), indentation can be important, and the lack of indentation can cause errors. For example,
h1 Example code
- #foos.each do |foo|
p = foo
Is valid slim code, but
h1 Example code
- #foos.each do |foo|
p = foo
will generate a syntax error.
As #sawa says, indentation is not obligatory but it is good practice as it allows your code to be much easier to read (for humans), which means it's far easier to catch any errors. According to the book Eloquent Ruby a sensible practice is to use two spaces per line for indentation since this is clearly an indent, but without using up too much room on the line. The book also advises not to use tabs since there is no universal length of a tab, so they can vary in length greatly. To see all this in action...
Code without indentation:
class Human
def laugh
puts "laugh"
end
def cry
puts "cry"
end
The code isn't quite right, but it's not immediately obvious where the error is.
Code with indentation and sensible spacing:
class Human
def laugh
puts "laugh"
end
def cry
puts "cry"
end
Here it's much easier to see that the end that should be aligned with the class opening is missing. In short, yes indent your code and also use sensible spacing (such as the space between the methods). It'll help you and any humans working with or reading your code.
Indentation isn't as strict as it is in python. Your code should work fine if indentation is off, but may not be maintainable or readable to another developer.
Related
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.
I am a fairly new to programming (3 months in), and am attempting to learn via TDD.
Obviously the point to TDD is write the test cases first, this particular piece I wasn't sure how to.
The code snippet is:
class PhraseFactory
def initialize
#sentence = ''
end
def make_sentences_from
for i in 0 ... self.length
#sentence += self[i] + ' '
end
end
How I was thinking to test it was using:
describe "When sent a message(<< is that proper terminology?) from an array of strings"
it "Builds a sentence"
my_word_array.should_have (here is where I am unclear)sent_a_message_to(make_sentences_from)
Thanks for any help.
i like TDD but i would not recommend anyone to use TDD to learn something new! it's always a good idea to use the REPL (irb) to experiment with code.
your example is full of WTFs for any ruby developer:
you are missing all the ENDs (looks kinda like python?!)
you are naming something Factory (are you a java guy?)
you use for instead of each
you are doing stuff in a class that should be a oneliner
you reinvent the wheel by rebuilding core functionality
besides that, i don't really understand your question and code...
what should the result of your code be? what is the input to your "factory"
$ irb
> %(you can just use join to build a sentence from an array of words).join
"you can just use join to build a sentence from an array of words"
Learning both a new language (Ruby) and technique (TDD) at once may be a bit too much. On the other hand, I find unit tests a great way to clarify code behavior, and as such a good learning tool. One suggestion here would be to look into something like the Ruby Koans: http://rubykoans.com/
I am not a Rubyist, so I can't comment on their quality, but I used the F# Koans, which were adapted from the Ruby ones, and were pretty good. This should both give you a good entry point into the language, as well as a familiarity with unit testing, which should serve you well once you start working on your own project and get into TDD.
I don't understand why some people use the percentage syntax a lot in ruby.
For instance, I'm reading through the ruby plugin guide and it uses code such as:
%w{ models controllers }.each do |dir|
path = File.join(File.dirname(__FILE__), 'app', dir)
$LOAD_PATH << path
ActiveSupport::Dependencies.load_paths << path
ActiveSupport::Dependencies.load_once_paths.delete(path)
end
Every time I see something like this, I have to go and look up the percentage syntax reference because I don't remember what %w means.
Is that syntax really preferable to ["models", "controllers"].each ...?
I think in this latter case it's more clear that I've defined an array of strings, but in the former - especially to someone learning ruby - it doesn't seem as clear, at least for me.
If someone can tell me that I'm missing some key point here then please do, as I'm having a hard time understanding why the percent syntax appears to be preferred by the vast majority of ruby programmers.
One good use for general delimited input (as %w, %r, etc. are called) to avoid having to escape delimiters. This makes it especially good for literals with embedded delimiters. Contrast the regular expression
/^\/home\/[^\/]+\/.myprogram\/config$/
with
%r|^/home/[^/]+/.myprogram/config$|
or the string
"I thought John's dog was called \"Spot,\" not \"Fido.\""
with
%Q{I thought John's dog was called "Spot," not "Fido."}
As you read more Ruby, the meaning of general delimited input (%w, %r, &c.), as well as Ruby's other peculiarities and idioms, will become plain.
I believe that is no accident that Ruby often has several ways to do the same thing. Ruby, like Perl, appears to be a postmodern language: Minimalism is not a core values, but merely one of many competing design forces.
The %w syntax shaves 3 characters off each item in the list... can't beat that!
It's easy to remember: %w{} is for "words", %r{} for regexps, %q{} for "quotes", and so on... It's pretty easy once you build such memory aids.
As the size of the array grows, the %w syntax saves more and more keystrokes by not making you type in all the quotes and commas. At least that's the reason given in Learning Ruby.
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.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
I was having a discussion with some programmer friends who said that they see Ruby programmers (in particular) producing a lot of code that's "too clever". So I'm wondering what would that look like? I'm referring to the unnecessary use of an obscure language feature in a context in which something straightforward would have worked just as well or better. Know any good Ruby examples of this?
After giving a straight answer to your question, I'd like to also dispute the premise; whenever a group of programmers characterizes the users of another language in this way, the odds are that they are telling you more about themselves than about the community they are describing.
You could, for example, accuse c programmers of being too obsessed with low level details, or haskell programmers with being blinded by their desire for functional purity; perl mongers for brevity, etc. But you would, IMHO, by getting the causality backwards when you do so.
When I want to write a program that is best expressed in a certain style, I try to choose a language that supports that style. Sometimes you want a tool that lets you do unusual things, and for such a task having a language such as ruby is as valuable as having mathematica for math or javascript for browser manipulation in your toolkit. If I want to play with typography I hop into postscript because that's what it's best at.
It's like saying "Have you ever noticed that people who use power drills are always poking holes in things?" It's true, but it kind of misses the point.
class Tree
def initialize*d;#d,=d;end
def to_s;#l||#r?"<#{#d},<#{#l}>,<#{#r}>>":#d;end
def total;(#d.is_a?(Numeric)?#d:0)+(#l?#l.total: 0)+(#r?#r.total: 0);end
def insert d
alias g instance_variable_get
p=lambda{|s,o|d.to_s.send(o,#d.to_s)&&
(g(s).nil??instance_variable_set(s,Tree.new(d)):g(s).insert(d))}
#d?p[:#l,:<]||p[:#r,:>]:#d=d
end
end
The double-bang: !!something
I'm not gonna write what it does. Forget that you ever saw this syntax.
Any use of metaprogramming without having thought damn hard about whether there's a better way to acheive this using the normal, non-'meta' idioms of the language, I tend to find annoying.
An obsession with "DRY" (don't repeat yourself) where some fiendish piece of metaprogramming spaghetti is invoked to avoid repeating yourself, say, twice in a simple and actually-more-straightforward-and-readable-than-the-alternative fashion.
Any use of 'eval' in particular. As metaprogramming goes, this one should be your absolute last resort after trying everything else. eg a lot of rubyists appear not to have heard of Class#define_method.
The output phase of yaml.rb; that's why I co-authored zaml.rb. The standard yaml version does all sorts of metaprogramming (it was originally written by why-the-lucky-stiff, who I generally admire) but by replacing it with a straight forward hierarchical version that directly maps to the class tree we were able to eliminate several O(n^3) cases, resulting in a factor of ten speedup for cases of interest, fix several bugs, and do so in a fraction of the code.
Plus, even people who aren't ruby gurus can see what it does.
Many of the examples in this article would seem to qualify:
21 Ruby Tricks You Should Be Using In Your Own Code.
The title of the article was a bit of a giveaway, given that it reads "Should" instead of "Should Not". Code "should" be transparent. Code "should not" be tricky.
I'm not sure if this qualifies as "too clever," but I have seen code that made me wonder if the author was either a genius or an idiot. One developer seemed to have a rule that no method should have more than two lines of code. That pushed the call stack very deep and made debugging rather difficult. The upside is that his overall design was very abstract and even elegant from a distance.
Cucumber (or RSpec Stories)
Quoted from the above RSpec Stories link:
Based around plain text descriptions
of application behaviour, it lets you
write integration tests with good
reuse and good diagnostic reporting.
For example, here's a story I wrote to
check the login process.
Story: login as an existing user
As an unauthenticated user
I want to log in to Expectnation
So I can see my account details
Scenario: login details are correct
Given an event provider
And my test#example.org account
When I log in with email test#example.org and password foofoo
Then I will be logged in
And I will be shown the account page
The words such as "Given", "When" and
"Then" are cues to the story runner to
execute some code. Behind the story
sits a collection of steps. Here's a
couple of steps from this test:
Given "my $email account" do |email|
#user = find_or_create_user_by_email({:email => email,
:password => 'foofoo',
:password_confirmation => 'foofoo'})
end
When "I log in with email $email and password $password" do |email, password|
post '/user/account/authenticate',
:user => {:email => email, :password => password}
end
Notice how a clever bit of string
matching allows you to pass parameters
from the story prose.
With a small bit of bolting together, the prose stories are then run as code and the tests executed.
It depends. (I love "it depends" questions)
It depends on the knowledge of the writer and reader. I used to think the use of Symbol#to_proc in Rails was unnecessarily arcane, for example, preferring
a.map { |e| e.downcase }
to
a.map(&:downcase)
Now I'm happy when I read it, although I still don't think to write it.
There are areas of libraries (Rails and others) where I have felt excessive and self-indulgent metaprogramming may have occurred but again the division between "too clever" and "really very clever indeed" is often paper-thin. DSLs are a good area: the ways in which "macros" are made available within classes (think of all that declarative goodness in things like ActiveRecord::Base or ActionController::Base) is very hard for a relative novice to understand and would probably seem like over-cleverness. It did to me. Now I find myself referencing the same code for guidance as I implement similar capabilities.
method_missing can be abused and it's one of those things that may cause you to pull your hair out when you have to fix a bug 3 months after you've written code.
Take a look at the source of Markaby. Insanity.
You shouldn't have to go from method to method to method to try to figure out what in the hell something is doing, for the sole purpose of not repeating a few lines of code. Being too focused on the LOC count and ultra-skinny methods might feel cool at the time but is time-consuming for someone else trying to debug or follow the code (and that someone may be you months later).
compare:
if MODELS.keys.inject(true) {|b, klass| b and klass.constantize.columns.map(&:name).include? association.options [:foreign_key]} then
# ...
end
1 line (if), 132 chars, 132 avg len, 22.9 flog
vs
fk = association.options[:foreign_key]
columns = MODELS.keys.map { |key| key.constantize.columns.map { |c| c.name } }
if columns.all? {|column| column.include? fk} then
# ...
end
4 lines, 172 chars, 43 avg chars, 15.9 flog
much faster too.
Original author actually argued maintainability for the first version.
Recently uncovered this monster:
def id
unless defined?(#id)
#id = if id = local_body.to_s[/(?:#\s*|#[[:punct:]]?)#{URL_REGEX}/,1]
id.to_i
end
end
#id
end
Not that I disagree with caching a calculation, it could just be far more clear.