0

I want to ask for user input, but I only want to do it once (possibly save the information within the program), meaning, something like this:

print "Enter your name (you will only need to do this once): "
name = gets.chomp

str = "Hello there #{name}" #<= As long as the user has put their name in the very first 
# time the program was run, I want them to never have to put thier name in again

How can I got about doing this within a Ruby program?

This program will be run by multiple users throughout the day on multiple systems. I've attempted to store it into memory, but obviously that failed because from my understand that memory is wiped everytime a Ruby program stops executing.

My attempts:

def capture_user
  print 'Enter your name: '
  name = gets.chomp
end
#<= works but user has to put in name multiple times

def capture_name      
  if File.read('name.txt') == ''
    print "\e[36mEnter name to appear on email (you will only have to do this once):\e[0m "
    @esd_user = gets.chomp
    File.open('name.txt', 'w') { |s| s.puts(@esd_user) }
  else
    @esd_user = File.read('name.txt')
  end
end
#<= works but there has to be a better way to do this?

require 'tempfile'

def capture_name
  file = Tempfile.new('user')
  if File.read(file) == ''
    print "\e[36mEnter name to appear on email (you will only have to do this once):\e[0m "
    @esd_user = gets.chomp
    File.open(file, 'w') { |s| s.puts(@esd_user) }
  else
    @esd_user = File.read(file)
  end
end
#<= Also used a tempfile, this is a little bit over kill I think,
# and doesn't really help because the users can't access their Appdata
13aal
  • 1,634
  • 1
  • 21
  • 47
  • 4
    Your question is premature. What have you tried? Why didn't it work? If you haven't tried, why not? Your example shows no attempt, and that's what we need to see. Please read "[ask]" including the links in that page, and "[mcve]". As is, you're asking for a tutorial, but we have no idea what your expertise is, so we'd have to start from the very bottom and work up, which is off-topic. Multiple users/times? From different systems? From one system? Using browsers? From the command-line? There's a lot of missing information. – the Tin Man May 06 '16 at 20:17
  • @theTinMan How's that? – 13aal May 06 '16 at 20:37
  • It's an improvement. Is the information being saved only useful to the individual on that machine or is it useful for everyone, across all the machines? The reason is, saving a file is only good to an individual on a specific machine. If you need to share the information you need to use a shared data repository, typically a database. – the Tin Man May 06 '16 at 20:41
  • @theTinMan I understand that, it's only good to the user on that machine, the end result is that this will basically become the users signature for the e-mail footers. – 13aal May 06 '16 at 20:42
  • 1
    You can't use Tempfile for your purposes. It's designed to create a temporary file that exists only during the execution of the script. As soon as the script exits the file is removed. SO isn't the place to ask this. You need to go through some file tutorials, get an idea of the various ways to save data (flat text file, vs. a local simple database, vs. a serialized structure using YAML or JSON), try things, then when you have a problem with the code ask a question. – the Tin Man May 06 '16 at 20:48
  • I didn't know that. Is using a text file really the easiest way to do this? – 13aal May 06 '16 at 20:49
  • 1
    http://meta.stackoverflow.com/questions/254572/what-is-the-proper-way-to-approach-stack-overflow-as-someone-totally-new-to-prog would probably be a useful page for you at this point. – the Tin Man May 06 '16 at 20:57

2 Answers2

1

If you need the name to persist across the user running the script several times, you're going to need to use some sort of data store. As much as I hate flat files, if all you're storing is the user's name, I think this is a valid option.

if File.exist?('username.txt')
    name = File.open( 'username.txt', 'r' ) do |file|
        name = file.gets
    end
else
    print "Enter your name (you will only need to do this once): "
    name = gets.chomp

    File.open( 'username.txt', 'w' ) do |file|
        file.puts name
    end
end
str = "Hello there #{name}"
erahm
  • 344
  • 1
  • 5
  • How would this work? This program will be run multiple times throughout the day, I want them to have to input their name _once_ and never have to do it again. This solution would require the user to input their name everytime the program is run – 13aal May 06 '16 at 20:02
  • 1
    You should specify these things in your question. You said nothing about running the script multiple times, just that you didn't want to ask for their name multiple times -- I assumed this was running in some sort of loop. To do what you're wanting to do, you're going to need some sort of data store. I'll edit my answer to show you what I'm talking about. – erahm May 06 '16 at 20:06
  • I see, that's what I figured, but I wasn't sure if there was a way to do it within Ruby memory. – 13aal May 06 '16 at 20:16
  • 1
    Every time that your script ends, the memory will be wiped. If the data is only going to be stored for each user running the script and it'll always be run from the same machine, there's no need to setup an actual database. It'll just be un-needed overhead. – erahm May 06 '16 at 20:48
1

You will want to store the username in a file on the local file system. Ruby provides many ways to do this, and we'll explore one in this answer: YAML files.

YAML files are a structured storage file that can store all kinds of different data, and is a good place to store config data. In fact, YAML configuration files are key parts of the largest Ruby projects in existence. YAML gives you a good starting point for supporting future configuration needs, beyond the current one, which is a great way to plan feature development.

So, how does it work? Let's take a look at your requirement using a YAML config:

require 'yaml'

config_filename = "config.yml"
config = {}
name = nil

if file_exists(config_filename)
  begin
    config = YAML.load_file(config_filename)
    name = config["name"]
  rescue ArgumentError => e
    puts "Unable to parse the YAML config file."
    puts "Would you like to proceed?"
    proceed = gets.chomp

    # Allow the user to type things like "N", "n", "No", "nay", "nyet", etc to abort
    if proceed.length > 0 && proceed[0].upcase == "N"
      abort "User chose not to proceed.  Aborting!"
    end
  end
end

if name.nil? || (name.strip.length == 0)
  print "Enter your name (you will only need to do this once): "
  name = gets.chomp

  # Store the name in the config (in memory)
  config["name"] = name

  # Convert config hash to a YAML config string
  yaml_string = config.to_yaml

  # Save the YAML config string to the config file
  File.open(config_filename, "w") do |out|
   YAML.dump(config, out)
  end
end

Rather than show you the bare minimum to meet your needs, this code includes a little error handling and some simple safety checks on the config file. It may well be robust enough for you to use immediately.

The very first bit simply requires the YAML standard library. This makes the YAML functions work in your program. If you have a loader file or some other common mechanism like that, simply place the require 'yaml' there.

After that, we initialize some variables that get used in this process. You should note that the config_filename has no path information in it, so it will be read from the current directory. You will likely want to store the config file in a common place, such as in ~/.my-program-name/config.yml or C:\Documents and Settings\MyUserName\Application Data\MyProgramName\. This can be done pretty easily, and there's plenty to help, such as this Location to Put User Config Files in Windows and Location of ini/config files in linux/unix.

Next, we check to see if the file actually exists, and if so, we attempt to read the YAML contents from it. The YAML.load_file() method handles all the heavy lifting here, so you just have to ask the config hash that's returned for the key that you're interested in, in this case, the "name" key.

If an error occurs while reading the YAML file, it indicates that the file might possibly be corrupted, so we try to deal with that. YAML files are easy to edit by hand, but when you do that, you can also easily introduce an error that will make loading the YAML file fail. The error handling code here will allow the user to abort the program and go back to fix the YAML file, so that it doesn't simply get overwritten.

After that, we try to see if we've been had a valid name from the YAML config, and if not, we go ahead and accept it from the user. Once they've entered a name, we add it to the config hash, convert the hash to a YAML-formatted string, and then write that string to the config file.

And that's all it takes. Just about anything that you can store in a Ruby hash, you can store in a YAML file. That's a lot of power for storing config information, and if you later need to add more config options, you have a versatile container that you can use exactly for that purpose.

If you want to do any further reading on YAML, you can find some good information here:

While some of these articles are a bit older, they're still very relevant and will give you a jumping off point for further reading. Enjoy!

Community
  • 1
  • 1
Michael Gaskill
  • 7,913
  • 10
  • 38
  • 43