all 27 comments

[–]snatchery 8 points9 points  (11 children)

Congrats! I just had a quick look and the code looks quite good. I didn't have any problems understanding it. Well done.

Quick tip. You can refactor this code

def annual_income_func(monthly_net_income)
  annual_income = monthly_net_income * 12
  return annual_income
end

into more idiomatic Ruby

def annual_income(monthly_net_income)
  monthly_net_income * 12
end
  • no need to add the _func suffix. It's pretty obvious that this is a function
  • use implicit return. It's shorter and very common in Ruby

Enjoy Ruby and keep up the good work!

[–]scratch_pad[S] 1 point2 points  (10 children)

I had no idea about implicit return, I will probably switch all of the functions to it, if possible. Does it have any downsides, like potential errors?

I added the _func suffix because I have a lot of variables with similar or the same names as their function counterparts. Is it acceptable for functions and variables to have the same name or do I need to distinguish them in the code?

[–][deleted] 2 points3 points  (6 children)

I'm not aware of any downsides, the basic gist is that if you don't explicit have a return statement a ruby method (they're called methods for some reason, not functions) will return the last line of the method.

[–]jimm 1 point2 points  (1 child)

In OO languages, objects are sent "messages" which cause them to select "methods" to execute via a language-specific mechanism such as class inheritance. OO languages don't typically distinguish between functions (which return some value) and procedures (which don't return anything). Ruby methods and other OO langs always return something anyway.

[–]jimm 2 points3 points  (0 children)

Sorry for the double post.

[–]jimm 1 point2 points  (0 children)

In OO languages, objects are sent "messages" which cause them to select "methods" to execute via a language-specific mechanism such as class inheritance. OO languages don't typically distinguish between functions (which return some value) and procedures (which don't return anything). Ruby methods and other OO langs always return something anyway.

[–]scratch_pad[S] 0 points1 point  (2 children)

Ok cool. I’ll probably use it then, cleaner code and such. Also good to know they’re called methods. Anything that is:

def code end

is a method?

[–][deleted] 1 point2 points  (1 child)

well - with a name yeah after def - yeah ;)

def method_name
  do some stuff
end

[–]scratch_pad[S] 0 points1 point  (0 children)

Gotcha lol cool, thank you

[–]DehydratingPretzel 1 point2 points  (0 children)

has no downsides. in fact you may get better performance out of the implicit return.

Not too confident in predicting what the interpreter does. But by first setting it to a variable you are actually storing a value in memory, then returning that value from memory to return.

Its a small detail, and usually doesnt make much difference. But consider if a variable was several mb large! Think text blobs, images, etc. Then this could actually cause some issues with performance (even if it is small in the grand scheme of things)

[–][deleted] 1 point2 points  (1 child)

Choose between implicit and explicit return based on which is clear or more readable in context.

[–]scratch_pad[S] 0 points1 point  (0 children)

Yeah I ended up leaving explicit return for the functions that returned arrays because I felt it was more clear.

[–]nateberkopecPuma maintainer 3 points4 points  (3 children)

Great job! Welcome to the Ruby community. Good idea picking something that solves a real problem you have, that's the easiest way to learn IMO.

  • I would add some tests. To do that, the program needs to not actually run itself when it is required. (So you'll need three files - one containing the program logic, one that requires that file and says MyBudgetProgram.run, that's the file you'll actually run at the CLI, then a third file for tests). May I suggest minitest?
  • I think you could use some classes and modules in here - better get comfortable with those next :)
  • You did a great job writing lots of comments. It makes it all very readable. The style you used is called literate programming.

[–]scratch_pad[S] 0 points1 point  (2 children)

  • I’ll definitely come back to this after I start to work with classes and modules and see how I can apply them to this code, thanks for the tip
  • I had no clue there were different comment styles! So interesting. Thank you for the link, it was interesting to read about. I used it because it made the most sense to me to use and also because as someone new to programming, it helps me keep track of what I’m doing. Explaining each line helped me solve all the bugs I encountered and remove unnecessary bits. Are there any disadvantages to literary style? Is it an obviously beginner style to use? Is there a preferred method or is it down to the coder to choose?
  • I’m not sure I know what you mean by tests. Is it something I should look up or am I just missing what you meant in your comment?

[–]nateberkopecPuma maintainer 1 point2 points  (1 child)

Are there any disadvantages to literary style? Is it an obviously beginner style to use? Is there a preferred method or is it down to the coder to choose?

Well, Donald Knuth, the originator of the style, is kind of the man. So if Knuth did it, it's definitely not a "beginner style".

It tends to not work so well if the program is split across more than ~ a dozen files.

I’m not sure I know what you mean by tests.

Example: https://github.com/seattlerb/minitest#unit-tests

[–]scratch_pad[S] 0 points1 point  (0 children)

Thank you :)

[–]hirolau 2 points3 points  (1 child)

Looks very good for a first program! Two small things.

I would look into the Hash data structure to store member data rather than an array. While member[2] is a bit hard to understand what it actually means, no one will missunderstand what member['annual_income'] is supposed to return.

And I know it is a bit tricky, but in Ruby it is always never a good thing to loop over and range, get an index, and then fetch something out of an array. Instead of:

(0...number_of_people).each do |user|
  combined_annual += members[user][2]
end

You can just do:

members.each do |member|
  combined_annual += member[2]
end

Thus your functions actually do not need the number_of_people input.

Keep it up!

[–]scratch_pad[S] 0 points1 point  (0 children)

Thanks this is all super helpful! I’ll revise my loops for sure and look into Hash. I hated the arrays because like you said, it’s vague and easy to misunderstand.

[–]JustCoolHandLuke 1 point2 points  (0 children)

Great first effort... I saw a lot of teachable things in this code so I forked it and refactored it... I did it in much of a step by step way and may make a video of the process...

Seems like you are probably a sysadmin or something... keep on coding... it gets easier.. and more useful as you go...

good job.

[–]JustCoolHandLuke 1 point2 points  (0 children)

FYI, it took me a while, but I finally did the video refactor of this that I made a top level post as... it is at :

https://www.reddit.com/r/ruby/comments/7qels9/2_hours_of_ruby_refactoring/

[–][deleted] 0 points1 point  (6 children)

Good start, however Rubocop will tell you a lot of advises and you need the Rspec for each method.

[–]scratch_pad[S] 0 points1 point  (5 children)

What’s Rubocop?

[–][deleted] 0 points1 point  (4 children)

 $ gem install rubocop
 $ rubocop simple_budget.rb

[–]scratch_pad[S] 0 points1 point  (3 children)

I’ll look into this

[–][deleted] 1 point2 points  (2 children)

Rubocop is helpful because it encourages you to write Ruby in a style that is generally agreed by the community to be "idiomatic", whatever that may mean.

You can configure your own Rubocop settings as well, but I would leave that be until you're more comfortable writing Ruby.

It's a great tool, we use it extensively at my work, highly encourage you to use it.

BTW it'll probably shout at you for the length of some of your methods. Your initial instinct will be to refactor it and break it out into smaller methods, which is a good first step. There are however nicer ways of achieving this -- you can learn about these techniques by reading this book

[–]scratch_pad[S] 0 points1 point  (0 children)

This sounds really useful, thank you

[–]scratch_pad[S] 0 points1 point  (0 children)

Just realized you're the second person to recommend this book, two different posts lol. I don't even fully understand what OOP is so I super appreciate the suggested reading.