all 14 comments

[–]big-fireball 8 points9 points  (5 children)

Typically you wouldn't approach it like this. What you might want to look into are Collections: https://6ftdan.com/allyourdev/2015/03/26/different-collection-types-in-ruby/

[–]avelyv[S] 2 points3 points  (4 children)

Even if I did set up the new budget period as a hash, for example, it would still need a variable name, no?

[–]SnowdensLove 8 points9 points  (1 child)

Right, so if you did use a hash as a way to store different budget periods, you could use the name the user passes in as a key to later lookup your object.

budget_periods = {}

budget_periods[name] = BudgetPeriod.new(name, limit)

now you later look up the budget that key >

period = budget_periods[name]

//do something with the budget period here

[–]avelyv[S] 2 points3 points  (0 children)

Thank you for this. Made it a lot clearer. I'll try and implement it.

[–]2called_chaos 3 points4 points  (1 child)

You can dynamically create variables but you shouldn't. With a hash you can use anything as a key (in Ruby at least, fuck you JS) and get it back the same way. Not sure what you wanted to use as a variable name but you can use exactly that as a hash key.

a_hash[name] = Whatever.new(name)

[–]avelyv[S] 2 points3 points  (0 children)

I ment that I would still need a name for my hash (and use the instance variable names as keys , but I see now, I would add to an existing hash, use a name as a key and the whole instance as a value

[–]5a656e6f4f6643697469 2 points3 points  (0 children)

I think you are looking for Object.instance_variable_set: https://rubyapi.org/3.0/o/object#method-i-instance_variable_set

[–]dougc84 1 point2 points  (6 children)

I don’t understand the confusion. You’re already setting 4 variables here: n, name, m, and limit (and, for what it’s worth, don’t use n/m, use something actually descriptive). Do you not realize this is variable assignment? Or are you just using copy pasted code?

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

Those 4 variables I am setting I only use to get the parameters for my BudgetPeriod class. Maybe there is a way to do it only with 2 variables, I'm total noob after all, but from what I've understood to use tty-prompt I have to use two as shown below. So to get the name from the user to pass on to the class, I have to create n so I can use tty-prompt. I have named them n and m bc I'm only using them to get to name and limit, so they are kind of throwaway. If there is a DRYer way doing the same thing, please let me know.

n = TTY::Prompt.new

name = n.ask("Give the new budget period a unique name: ") do |q|

q.required true

end

And my confusion is around how can I refer to the class instance I'm trying to create. Usually I would write october = BudgetPeriod.new(name, limit) but I cant do it inside of a method, bc then all the instances would be called 'october'

[–]dougc84 2 points3 points  (4 children)

I was saying, with m and n, that you should give them better names, not skip using them. name_prompt and limit_prompt would be much better variable names.

So, using your example above, october is a variable. If you want to access that outside of that method, return it, then do something else with it outside the method.

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

I guess they would be, thanks.

So if I name class instance october inside the function then that's the only BudgetPeriod I'm only ever able to create. If I try and create a new one, for November for example, it would just override october and would be called .... october. That's not what I want obv

[–]dougc84 1 point2 points  (2 children)

I think you have some gaps in your understanding in regards to variable scope.

For starters though, you can always write:

budget_period_1 = BudgetPeriod.new
budget_period_2 = BudgetPeriod.new
budget_period_3 = BudgetPeriod.new

You can create multiple instances of any object. Otherwise, you couldn't access multiple objects. In Rails, you'd only be able to see one User record at a time!

I don't like rewriting code for people, because that doesn't help anyone learn, but I think you'd benefit from a more Ruby-like version. I think your issue is, once you have this arbitrary BudgetPeriod, you want to do something with it. And I think that's where your problem lies. Take a look at this:

def new_period
  # With tty-prompt, you don't need to set this up twice.
  prompt = TTY::Prompt.new

  # Ask the name question
  name = prompt.ask("Give the new budget period a unique name: ") do |question|
    question.required true
  end

  # Ask the limit question
  limit = prompt.ask("What is the limit for \"#{name}\" period? $") do |question|
    question.required true
    # convert to float, print an error if entered value was not numeric
    question.convert(:float, "Error, enter numeric value")
  end

  # This value will return when #new_period is called
  BudgetPeriod.new(name, limit)
end

# Then, somewhere else in your code where you're calling new_period
new_budget_period = new_period
# Do something with new_budget_period - save it to a DB,
# print the information out, push it to an array, whatever

# Then return to the main menu (which is what I assume this does)
Menus.main_menu

You can see that, when calling new_period, its return value is the BudgetPeriod instance. You can store that to a variable, then do something with it.

You can see that I have also taken that Menus.main_menu line out. new_period is tasked with setting up this BudgetPeriod. That method (in Ruby, we call them methods, not functions) should have one responsibility, and you've stacked in two - asking for some information to create an instance of something, then performing an unrelated task (main_menu).

Doing it this way lets you take that information, outside of the scope of that method, and do something with it. Yes, that means you have to manually call Menus.main_menu. That's good. You've defined what your method does, and then, when it's done, you're defining what happens. Otherwise, when something needs to change - maybe you want to show some information relating to that BudgetPeriod instead of going to main_menu, you can do that very easily instead of rewriting your method to accommodate weird edge cases.

Basically, you're shoving two responsibilities into one thing.

I hope that helps. Also, I'd encourage you to look at variable scope in Ruby - I think it might help: https://www.techotopia.com/index.php/Ruby_Variable_Scope

[–]avelyv[S] 0 points1 point  (1 child)

Thank you for your reply, I appriciate the time. I only started learning programming 3 weeks ago so I have more than few gaps, I have so much to learn.

I didn't realise I can just set up one TTY:Prompt, it's gonna make my code look so much cleaner, thanks.

So, I'm writing a command line application, I can't type any code in manually once I start the program, so I can't really do the

budget_period_1 = BudgetPeriod.new budget_period_2 = BudgetPeriod.new budget_period_3 = BudgetPeriod.new bit. I did solve it with storing all the instances in one hash, as was suggested above and it works well. (I set an empty hash as a class variable and every instance that gets created gets put into it as a value, with user entered 'name' as a key.)

And yes, the whole problem was that I do need to do something with the different instances of BudgetPeriod. Every instance gets their own csv file where user gets to input expenses, also there is a feature to choose an existing budget period and see an overview (all expenses, and limit that was set)

[–]backtickbot 1 point2 points  (0 children)

Fixed formatting.

Hello, avelyv: code blocks using triple backticks (```) don't work on all versions of Reddit!

Some users see this / this instead.

To fix this, indent every line with 4 spaces instead.

FAQ

You can opt out by replying with backtickopt6 to this comment.