Accepting Command-Line Arguments into a Ruby Script - ruby

I'm trying to use the following code to accept in a file as an argument in Terminal which will then be read and update the body variable with its contents. If the file is not passed in then I want to have the prompt where the user can enter their own body copy.
require 'posterous'
Posterous.config = {
'username' => 'name',
'password' => 'pass',
'api_token' => 'token'
}
include Posterous
#site = Site.primary
#GETS POST TITLE
puts "Post title: "
title = STDIN.gets.chomp()
if defined?(ARGV)
filename = ARGV.first
end
if (defined?(filename))
body = File.open(filename)
body = body.read()
else
puts "Post body: "
body = STDIN.gets.chomp()
end
puts body
When I run the program without passing in a file I get this returned:
Post title:
Hello
posterous.rb:21:in `initialize': can't convert nil into String (TypeError)
from posterous.rb:21:in `open'
from posterous.rb:21:in `'
I'm rather new to ruby and thus am not the best at it. I've tried swapping a lot of things around and changing things but to no avail. What am I doing wrong?

defined?(ARGV) won't return a boolean false, but rather "constant". Since that doesn't evaluate to false, filename gets defined as ARGV[0], which is nil.
>> ARGV
=> []
>> defined?(ARGV)
=> "constant"
?> ARGV.first
=> nil
Instead you might check the length of ARGV:
if ARGV.length > 0
filename = ARGV.first.chomp
end
From the docs:
defined? expression tests whether or not expression refers to anything recognizable (literal object, local variable that has been initialized, method name visible from the current scope, etc.). The return value is nil if the expression cannot be resolved. Otherwise, the return value provides information about the expression.

Michael gave you the basic answer to your question. A slightly more Rubyish way of doing that would be to use ARGF to do the reading; then the conditional is only needed to decide whether or not to print the prompt:
puts "Post title: "
title = gets.chomp
puts "Post body: " if ARGV.length == 0
body = ARGF.gets.chomp
puts body
..of course, if you don't need to anything else with body, you can skip storing the contents of the file(s) and just do
puts ARGF.gets.chomp

Related

Ruby smart variables [duplicate]

What is the best way to write a function (or something DSLish) that will allow me to write this code in Ruby. How would I construct the function write_pair?
username = "tyndall"
write_pair username
# where write_pair username outputs
username: tyndall
Is it possible to do? Looking for the most simple way to do this.
Sure it is possible!
My solution tests the var by Object#object_id identity: http://codepad.org/V7TXRxmL
It's crippled in the binding passing style ...
Although it works just for local vars yet, it can be easily be made "universal" adding use of the other scope-variable-listing methods like instance_variables etc.
# the function must be defined in such a place
# ... so as to "catch" the binding of the vars ... cheesy
# otherwise we're kinda stuck with the extra param on the caller
#_binding = binding
def write_pair(p, b = #_binding)
eval("
local_variables.each do |v|
if eval(v.to_s + \".object_id\") == " + p.object_id.to_s + "
puts v.to_s + ': ' + \"" + p.to_s + "\"
end
end
" , b)
end
# if the binding is an issue just do here:
# write_pair = lambda { |p| write_pair(p, binding) }
# just some test vars to make sure it works
username1 = "tyndall"
username = "tyndall"
username3 = "tyndall"
# the result:
write_pair(username)
# username: tyndall
If it's possible for you to use a symbol instead of the variable name, you could do something like this:
def wp (s, &b)
puts "#{s} = #{eval(s.to_s, b.binding)}"
end
In use:
irb(main):001:0> def wp (s, &b)
irb(main):002:1> puts "#{s} = #{eval(s.to_s, b.binding)}"
irb(main):003:1> end
=> nil
irb(main):004:0> var = 3
=> 3
irb(main):005:0> wp(:var) {}
var = 3
Note that you must pass the empty block {} to the method or it cannot get the binding to evaluate the symbol.
You can't actually get a variable's name in Ruby. But you could do something like this:
data = {"username" => "tyndall"}
Or even,
username = "tyndall"
data = {"username", "password", "favorite_color"}
data.each { |param|
value = eval(param)
puts "#{param}: #{value}"
}
I made a vim macro for this:
" Inspect the variable on the current line (in Ruby)
autocmd FileType ruby nmap ,i ^"oy$Iputs "<esc>A: #{(<esc>"opA).inspect}"<esc>
Put the variable you'd like to inspect on a line by itself, then type ,i (comma then i) in normal mode. It turns this:
foo
into this:
puts "foo: #{(foo).inspect}"
This is nice because it doesn't have any external dependencies (e.g. you don't have to have a library loaded up to use it).
Building on previous answers relating to symbols & bindings ... if passing in the variable name as a symbol works for you (who doesn't love cutting out extra keystrokes?!), try this:
def wp(var_name_as_sym)
# gets caller binding, which contains caller's execution environment
parent_binding = RubyVM::DebugInspector.open{|i| i.frame_binding(2) }
# now puts the symbol as string + the symbol executed as a variable in the caller's binding
puts %Q~#{var_name_as_sym.to_s} = #{eval("#{var_name_as_sym.to_s}.inspect", parent_binding)}~
end
aa=1
bb='some bb string'
os = OpenStruct.new(z:26, y:25)
Console output:
> wp :aa
aa = 1
=> nil
> wp :bb
bb = "some bb string"
=> nil
> wp :os
os = #<OpenStruct z=26, y=25>
=> nil
Using ruby 2.2.2p95
(Credit to banister for getting binding of calling context)
This is a simple solution:
def write_pair(variable)
puts variable + eval(variable)
end
This is more readable:
def write_pair(variable)
puts 'A' * 100
puts variable + ': ' + eval(variable).inspect
puts 'Z' * 100
end
Invocation:
write_pair "variable"
def write_pair var, binding
puts "#{ var } = #{ eval(var, binding)}"
end
username = "tyndall"
write_pair "username", binding
This seems weird because binding is never defined, but it works. From Ruby: getting variable name:
The binding() method gives a Binding object which remembers the
context at the point the method was called. You then pass a binding
into eval(), and it evaluates the variable in that context.
Be sure to pass a string, not the variable.
# make use of dynamic scoping via methods and instance vars
#_binding = binding
def eval_debug(expr, binding = #_binding)
"#{expr} => #{eval(expr, binding)}"
end
# sample invocation:
x = 10
puts eval_debug "x"
puts eval_debug "x**x"

Why am I not getting same output in Ruby

I'm not getting the consistent output in two cases:
Scenario 1:
humen = {"hand" => 1, "eye" => 2, "head" => 3, "hair"=>4}
puts "enter any body part name"
internal = gets.chomp.downcase.to_s
body = humen[internal]
puts body
#if input is "eye", it comes out 2
Scenario 2:
humen = {hand:1, eye:2, head:3, hair:4}
puts "enter any body part name"
internal = gets.chomp.downcase.to_s
body = humen[internal]
puts body
I see nothing in irb console. Can anyone please explain why that's the case?
keys are symbol in second case -
{:hand=>1, :eye=>2, :head=>3, :hair=>4}
whereas internal is a string.
humen[internal] is expecting the string assigned to internal to be present in hash humen which is not the case.
:hand != 'hand'
You should convert the string to symbol by:
humen[internal.to_sym]
String#to_sym converts a string into a symbol.

Recursion in Ruby isnt working like I think it should

I don't understand why this method isn't working. when I put in a value that should pass the if statement it doesn't work.
def getBase
puts "What is the base URL for the test?"
x = gets
if (x.include? 'http://') && ((x.split('.').at(x.split('.').length - 1).length) == 3)
return x
else
puts "That is in the incorrect format."
puts "Please format your url like this"
puts "http://example.com"
getBase
end
end
input 'http://test.com'
result: statement repeats and does not exit recursion
When you get input with gets it includes the newline \n at the end (from the user hitting return). So your x is actually "http://test.com\n".
To get rid of this use String#chomp:
x = gets.chomp
That should do it.
If the purpose is to enforce correct URL format and/or make sure it's an HTTP URL, why don't you use a tool designed to do that? Ruby's URI class is your friend:
require 'uri'
URI.parse('http://foo.bar').is_a?(URI::HTTP)
=> true
URI.parse('ftp://foo.bar').is_a?(URI::HTTP)
=> false
URI.parse('file://foo.bar').is_a?(URI::HTTP)
=> false
URI.parse('foo.bar').is_a?(URI::HTTP)
=> false
I'd write the code more like this:
require 'uri'
def get_base
loop do
puts "What is the base URL for the test?"
x = gets.chomp
begin
uri = URI.parse(x)
return uri.to_s if uri.is_a?(URI::HTTP)
rescue URI::InvalidURIError
end
puts "That is in the incorrect format."
puts "Please format your URL like this:"
puts
puts " http://example.com"
end
end
puts "Got: #{ get_base() }"

Having trouble saving to file in Ruby

Hi I have a simple form that allows a user to input a name, their gender and a password. I use Digest::MD5.hexdigest to encrypt the input. Once I have the encrypted input eg, d1c261ede46c1c66b7e873564291ebdc, I want to be able to append this to a file I have already created. However every thing I have tried just isn't working. Can anyone please help and thank you in advance. Here is what I have:
input = STDIN.read( ENV["CONTENT_LENGHT"] )
puts "Content-type: text/html \n\n"
require 'digest/md5'
digest = Digest::MD5.hexdigest(input)
f = File.open("register.txt", "a")
f.write(digest)
f.close
I have also tried this with no luck:
File.open("register.txt", "a") do |f|
f.puts(digest)
end
If the code is verbatim then I think you have a typo in the first line: did you mean CONTENT_LENGHT or is it a typo? ENV[] will return a string if the variable is set, which will upset STDIN#read. I get TypeError: can't convert String into Integer. Assuming the typo, then ENV[] returns nil, which tells STDIN#read to read until EOF, which from the console means, I think, Control-Z. That might be causing a problem.
I suggest you investigate by modifying your script thus:
read_length = ENV["CONTENT_LENGTH"].to_i # assumed typo fixed, convert to integer
puts "read length = #{read_length}"
input = STDIN.read( read_length )
puts "input = #{input}"
puts "Content-type: text/html \n\n" # this seems to serve no purpose
require 'digest/md5'
digest = Digest::MD5.hexdigest(input)
puts "digest = #{digest}"
# prefer this version: it's more idiomatically "Rubyish"
File.open("register.txt", "a") do |f|
puts "file opened"
f.puts(digest)
end
file_content = File.read("register.txt")
puts "done, file content = #{file_content}"
This works on my machine, with the following output (when CONTENT_LENGTH set to 12):
read length = 12
abcdefghijkl
input = abcdefghijkl
Content-type: text/html
digest = 9fc9d606912030dca86582ed62595cf7
file opened
done, file content = 6cfbc6ae37c91b4faf7310fbc2b7d5e8
e271dc47fa80ddc9e6590042ad9ed2b7
b0fb8772912c4ac0f13525409c2b224e
9fc9d606912030dca86582ed62595cf7

Need help in debugging a tiny ruby script

I get the following error, when i run my script.
fetch_scores.rb:20:in `<main>': undefined local variable or method `__EVENTTARGET' for main:Object (NameError)
HereÅ› the script:
#fetch_scores.rb
require 'net/http'
require 'open-uri'
#define a constant named URL so if the results URL changes we don't
#need to replace a hardcoded URL everywhere.
URL = "http://www.nitt.edu/prm/nitreg/ShowRes.aspx"
#checking the count of arguments passed to the script.
#it is only taking one, so let's show the user how to use
#the script
if ARGV.length != 1
puts "Usage: fetch_scores.rb student_name"
else
roll_no = ARGV[0] #could drop the ARGV length check and add a default using ||
# or name = ARGV[0] || nikhil
end
params = {
__EVENTTARGET => "Dt1",
__EVENTARGUMENT => "",
__VIEWSTATE => "dDwtMTM3NzI1MDM3O3Q8O2w8aTwxPjs+O2w8dDw7bDxpPDE+O2k8Mj47aTw0Pjs+O2w8dDxwPHA8bDxWaXNpYmxlOz47bDxvPHQ+Oz4+Oz47bDxpPDE+O2k8Mz47PjtsPHQ8O2w8aTwwPjtpPDE+O2k8Mj47aTwzPjtpPDQ+O2k8NT47aTw2Pjs+O2w8dDw7bDxpPDA+Oz47bDx0PDtsPGk8MT47PjtsPHQ8cDxwPGw8VGV4dDs+O2w8VEhJUkQgU0VNRVNURVIgTUFTVEVSIE9GIENPTVBVVEVSIEFQUExJQ0FUSU9OUyBOT1YtMjAxMChSRUdVTEFSKTs+Pjs+Ozs+Oz4+Oz4+O3Q8O2w8aTwxPjs+O2w8dDw7bDxpPDE+Oz47bDx0PHA8cDxsPFRleHQ7PjtsPFNVREVFUCBBR0FSV0FMOz4+Oz47Oz47Pj47Pj47dDw7bDxpPDE+Oz47bDx0PDtsPGk8MT47PjtsPHQ8cDxwPGw8VGV4dDs+O2...xwPGw8VGV4dDs+O2w8MTk7Pj47Pjs7Pjs+Pjs+Pjt0PDtsPGk8MT47PjtsPHQ8O2w8aTwxPjs+O2w8dDxwPHA8bDxUZXh0Oz47bDwwOz4+Oz47Oz47Pj47Pj47Pj47dDxwPHA8bDxWaXNpYmxlOz47bDxvPGY+Oz4+Oz47Oz47Pj47dDw7bDxpPDk+O2k8MTE+Oz47bDx0PHA8cDxsPFRleHQ7VmlzaWJsZTs+O2w8U2VsZWN0IFNlc3Npb24gICA7bzx0Pjs+Pjs+Ozs+O3Q8dDxwPHA8bDxWaXNpYmxlOz47bDxvPHQ+Oz4+Oz47dDxpPDM+O0A8LSBTZWxlY3QgLTtOT1YtMjAxMChSRUdVTEFSKTtNQVktMjAxMVtSRUdVTEFSXTs+O0A8MDs2Mjs2Njs+PjtsPGk8MT47Pj47Oz47Pj47dDxwPHA8bDxUZXh0Oz47bDxcZTs+Pjs+Ozs+Oz4+Oz4+Oz53I0hrrJ9pq04wIekH/y79mq+lYQ==",
TextBox1 => roll_no,
Dt1 => 66
}
results = Net::HTTP.post_form( URL, params )
p results
__EVENTTARGET in the params hash is parsed as a variable and that variable was never defined. Try "__EVENTTARGET" => "Dt1".

Resources