[ANN] HighLine 0.4.0

  • Thread starter James Edward Gray II
  • Start date
J

James Edward Gray II

HighLine 0.4.0 Released
=======================

Not a lot of features in this release, but two that sure are nice to
have. This version brings word wrap and paged printing capabilities
to all output HighLine's output. Limits for both operations are
adjustable with new accessors `wrap_at =` and `page_at =` on all
HighLine objects.

Let me just say a big thanks to Greg Brown, who did a lot of the
initial work on getting word wrap into HighLine. Thanks Greg!

If anyone uses this, feedback is welcome
([email protected]). I do have a TODO list of features I
would like to add, but I'm also open to suggestions of how to grow
the project and make in useful to all.

What is HighLine?
-----------------

(from the README)

HighLine was designed to ease the tedious tasks of doing console
input and output with low-level methods like gets() and puts().
HighLine provides a robust system for requesting data from a user,
without needing to code all the error checking and validation rules
and without needing to convert the typed Strings into what your
program really needs. Just tell HighLine what you're after, and let
it do all the work.

What's new in this release?
---------------------------

(highlights from the CHANGELOG)

* Implemented line wrapping with adjustable limit.
* Implemented paged printing with adjustable limit.

Plus documentation and examples for the new features.

Where can I learn more?
-----------------------

HighLine is hosted on RubyForge.

Project page: http://rubyforge.org/projects/highline/
Documentation: http://highline.rubyforge.org/
Downloads: http://rubyforge.org/frs/?group_id=683

How do I get HighLine?
----------------------

HighLine is a gem, so as long as you have RubyGems installed it's as
simple as:

$ sudo gem install highline

If you need to install RubyGems, you can download it from:

http://rubyforge.org/frs/?group_id=126&release_id=1885

HighLine can also be installed manually. Just download the latest
release and follow the instructions in INSTALL:

http://rubyforge.org/frs/?group_id=683&release_id=2143

James Edward Gray II
 
V

Vincent Foley

Hello James,

good job on the new release, I plan to use HighLine in an application I
am building right now, however I would really like it if you could add
an ask_password method. Right now, I use this small hackish method:

# File lib/helpers.rb, line 12
12: def self.ask_password(prompt)
13: print prompt
14: system("stty -echo echonl")
15: pass = gets.chomp
16: system("stty echo -echonl")
17: return pass
18: end

Maybe something to add to your TODO :)

Vincent.
 
J

James Edward Gray II

Maybe something to add to your TODO :)

Did you check the TODO? :D

Seriously, I thank you for the request. It is already on the list
and I will add it.

My biggest problem here is that I'm trying to keep HighLine pretty
cross-platform (Unix and Windows is enough for me). I understand the
method you just posted, but if anyone can tell me the Window's
equivalent it would sure help me along...

Thanks!

James Edward Gray II
 
A

Andre Nathan

James Edward Gray II said:
I understand the method you just posted, but if anyone can tell me
the Window's equivalent it would sure help me along...

Dunno about Windows but I think that using ruby-termios would be
better then calling an external program.

I'm using something like this in ruby-managesieve's sievectl utility
(IIRC I just copied it from one of the usage examples in the termios
lib :)

oldt = Termios.tcgetattr(STDIN)
newt = oldt.dup
newt.lflag &= ~Termios::ECHO
Termios.tcsetattr(STDIN, Termios::TCSANOW, newt)
print 'Password: '
info['password'] = STDIN.gets
Termios.tcsetattr(STDIN, Termios::TCSANOW, oldt)

Regards,
Andre
 
R

Ryan Leavengood

Vincent said:
Hello James,

good job on the new release, I plan to use HighLine in an application I
am building right now, however I would really like it if you could add
an ask_password method. Right now, I use this small hackish method:

# File lib/helpers.rb, line 12
12: def self.ask_password(prompt)
13: print prompt
14: system("stty -echo echonl")
15: pass = gets.chomp
16: system("stty echo -echonl")
17: return pass
18: end

Maybe something to add to your TODO :)

Especially if you can do it multi-platform (the above does not work on
Windows.)

Here is a Windows version that I just wrote:

require 'Win32API'

@getch = Win32API.new("crtdll", "_getch", [], 'L')

def ask_password(prompt)
print prompt
pass = ''
while ((c = @getch.call) != 13)
pass << c.chr
end
pass
end

pass = ask_password("Please give me your password: ")
puts
puts "Got '#{pass}'"
__END__

There may be a better way, but this works. James, feel free to put this
in HighLine, just give me the credit :)

Obviously this will need to be partitioned for the different platforms.

Another cool thing is you could take a block that checks the password
for strength :)

Ryan
 
R

Ryan Leavengood

James said:
My biggest problem here is that I'm trying to keep HighLine pretty
cross-platform (Unix and Windows is enough for me). I understand the
method you just posted, but if anyone can tell me the Window's
equivalent it would sure help me along...

You can see that in my other response.

Something else I was just thinking is that if you could get the Unix
version of getch as a proc object, it could be used instead of the
Win32API version I used. I believe that can be gotten from the Curses
library.

That would minimize the difference between each platform, and wouldn't
require external programs like Vincent's does.

My only concern would be whether the enter key was always decimal 13 on
each machine...I do know that newlines in text files on each platform
are different (CRLF on Windows, CR in Unix, LF in Mac...though maybe OS
X is like Unix.)

Ryan
 
L

Logan Capaldo

On 5/7/05 said:
My only concern would be whether the enter key was always decimal 13 on
each machine...I do know that newlines in text files on each platform
are different (CRLF on Windows, CR in Unix, LF in Mac...though maybe OS
X is like Unix.)

Ryan

I think you have that a little backwards. unix is LF, and Mac is CR
although OS X is LF also.

Darwin Logan-Capaldos-Computer.local 7.9.0 Darwin Kernel Version
7.9.0: Wed Mar 30 20:11:17 PST 2005;
root:xnu/xnu-517.12.7.obj~1/RELEASE_PPC Power Macintosh powerpc
logan:/Users/logan% irb
irb(main):001:0> ?\n
=> 10
 
R

Ryan Leavengood

Logan said:
I think you have that a little backwards. unix is LF, and Mac is CR
although OS X is LF also.

Darwin Logan-Capaldos-Computer.local 7.9.0 Darwin Kernel Version
7.9.0: Wed Mar 30 20:11:17 PST 2005;
root:xnu/xnu-517.12.7.obj~1/RELEASE_PPC Power Macintosh powerpc
logan:/Users/logan% irb
irb(main):001:0> ?\n
=> 10

Yes, you are right. Hmmm, this is kind of confusing though. Does ?\n
return 13 on a non-OS-X Mac? I would think that \n and \r would always
mean NL (i.e. decimal 10) and CR (i.e. decimal 13), no matter what the
operating system.

But for the matter at hand, I just want to make sure that pressing the
enter key on a Unix and Mac machine always produces decimal 13.

Ryan
 
J

James Edward Gray II

Here is a Windows version that I just wrote:

require 'Win32API'

@getch = Win32API.new("crtdll", "_getch", [], 'L')

def ask_password(prompt)
print prompt
pass = ''
while ((c = @getch.call) != 13)
pass << c.chr
end
pass
end

pass = ask_password("Please give me your password: ")
puts
puts "Got '#{pass}'"
__END__

There may be a better way, but this works. James, feel free to put
this in HighLine, just give me the credit :)

I have a question about this solution. You're stopping when you see
a carriage-return above, but what does Windows really tact onto the
end of the line? Is it a carriage return line feed pair, as I
suspect? Put another way, if you changed the above from 13 to 10,
would it still work?

Thanks.

James Edward Gray II

P.S. I'm not a Window's user. That should be painfully obvious by
now, but I just wanted to explain why I'm asking so many dumb
questions. :)
 
J

James Edward Gray II

require 'Win32API'

@getch = Win32API.new("crtdll", "_getch", [], 'L')

def ask_password(prompt)
print prompt
pass = ''
while ((c = @getch.call) != 13)
pass << c.chr
end
pass
end

pass = ask_password("Please give me your password: ")
puts
puts "Got '#{pass}'"
__END__

Last question on this, I promise. Does Windows echo the carriage
return (and possibly line feed), when character reading like this?

James Edward Gray II
 
R

Ryan Leavengood

James said:
I have a question about this solution. You're stopping when you see a
carriage-return above, but what does Windows really tact onto the end
of the line?

Nothing, by using getch I get every character and DOS/Windows doesn't do
anything. That is why I put that extra puts there. Otherwise the result
looks like this:

Please give me your password: Got 'ryan'
Is it a carriage return line feed pair, as I suspect?
Nope.

Put another way, if you changed the above from 13 to 10, would it still
work?

Nope. In that case you have to type Ctrl-J to end, whereas with 13 you
can hit the enter key or type Ctrl-M. I'm starting to think you will see
the same behavior on Unix and Mac.
Last question on this, I promise. Does Windows echo the carriage
return (and possibly line feed), when character reading like this?

As I said above, it echoes nothing.

I'm definitely thinking my solution would make a good general
multi-platform password prompter, with only the implementation of getch
requiring multi-platform code.

Ryan
 
J

James Edward Gray II

Nothing, by using getch I get every character and DOS/Windows
doesn't do anything. That is why I put that extra puts there.
Otherwise the result looks like this:

Please give me your password: Got 'ryan'
Thanks.

I'm starting to think you will see the same behavior on Unix and Mac.

Na, Unix uses line feeds:

$ ruby -e 'p gets[-1]'
This will be a line feed =>
10

James Edward Gray II
 
R

Ryan Leavengood

James said:
Na, Unix uses line feeds:

$ ruby -e 'p gets[-1]'
This will be a line feed =>
10

I get the same result on Windows, so I suspect Ruby is doing some stuff
behind the scenes. You may want to try my password prompter with the
getch of the curses libraries on a Unix box just to be sure.

Ryan
 
J

James Edward Gray II

I get the same result on Windows, so I suspect Ruby is doing some
stuff behind the scenes. You may want to try my password prompter
with the getch of the curses libraries on a Unix box just to be sure.

Duh. You're right:

$ ruby -e 'system "stty raw"; p $stdin.getc; syst "stty -raw"'
13

Thanks for the lesson. ;)

James Edward Gray II
 
J

James Edward Gray II

Dunno about Windows but I think that using ruby-termios would be
better then calling an external program.

I'm using something like this in ruby-managesieve's sievectl utility
(IIRC I just copied it from one of the usage examples in the termios
lib :)

oldt = Termios.tcgetattr(STDIN)
newt = oldt.dup
newt.lflag &= ~Termios::ECHO
Termios.tcsetattr(STDIN, Termios::TCSANOW, newt)
print 'Password: '
info['password'] = STDIN.gets
Termios.tcsetattr(STDIN, Termios::TCSANOW, oldt)

I'm playing around with termios over here, to see if it's a viable
replacement to the stty hack. I'm having some trouble getting what I
need out of it though.

The above code reads a line from the terminal. HighLine requires
code to read a single character. Can someone please show me how to
get that out of termios?

Making this change will make HighLine depend on a C extension.
Everyone agrees that's better than the external stty program?

Thanks.

James Edward Gray II
 
V

Vincent Foley

Indeed, I read it just after I posted :) Sorry about that. However, I
think I have an idea that could be fun which isn't yet on your TODO:
readline support. It would be nice if people could have a history of
things they wrote, if they could use the arrows to go fix a simple
mistake instead of deleting until the mistake and typing the rest
again. How about that?

Vincent.
 
J

James Edward Gray II

Indeed, I read it just after I posted :) Sorry about that.
However, I
think I have an idea that could be fun which isn't yet on your TODO:
readline support. It would be nice if people could have a history of
things they wrote, if they could use the arrows to go fix a simple
mistake instead of deleting until the mistake and typing the rest
again. How about that?

I put in on the list. I'll definitely look into it.

Thanks for the suggestion.

James Edward Gray II

P.S. The release with the no echo option, and a few other goodies,
should be out later today.
 
V

Vincent Foley

How did you handle the noecho thing on different platforms? I looked
at the getpass.py Python module, they used a module called msvcrt which
had a getch() function which reads a character without echoing it. The
whole Windows function seemed rather hackish in my opinion.
 
J

James Edward Gray II

How did you handle the noecho thing on different platforms? I looked
at the getpass.py Python module, they used a module called msvcrt
which
had a getch() function which reads a character without echoing it.
The
whole Windows function seemed rather hackish in my opinion.

I used pretty much what Ryan showed.

I'm not too troubled by using the Win32API to character read. That
seems pretty solid. I've seen that solution in many places over the
years and HighLine was using it even before we started talking
passwords and Ryan showed his version. (If you look back, you'll see
that I wrote about it in the summary to Ruby Quiz #5.) I'm almost
never on Windows, but the couple of times I've tried it there, it
worked perfectly.

Hunting for a carriage return feels a little more shaky to me, but I
would still be doing that if I was using a different getch() routine,
right? It too seems to work.

Personally, I find the Unix hack more troubling. HighLine has been
using the external stty for character reading and still is. I agree
with Andre that there's probably a better way. I played with termios
quite a bit for this release, but I'm not convinced it's a better
solution. Just to give one example, the stty trick works on Mac OS X
out of the box, but I believe you can only install termios if you've
installed Apple's optional Developer Tools. (I haven't tested
that.) Here again, I've never seen the stty "hack" not work. For
now at least, I'm calling it good enough.

The moral: File a bug report if you have problems. Oh, and be sure
it includes your patch it fix it. :) Nothing like releasing code
into the wild to see just how much it doesn't work, eh?

I really do want HighLine to work (well!) on at least Windows and
Unix. I am trying to make the decisions to make that happen with my
imperfect information. Feel free to enhance my knowledge at any time!

James Edward Gray II
 
M

Mark Hubbart

Hi --

Duh. You're right:

$ ruby -e 'system "stty raw"; p $stdin.getc; syst "stty -raw"'
13

You might not want to use stty in that exact way. From the stty
manpage (on OSX):

Note that since the terminal driver no longer has a single RAW bit, it
is not possible to intuit what flags were set prior to setting raw.
This means that unsetting raw may not put back all the setting that
were previously in effect. To set the terminal into a raw state and
then accurately restore it, the following shell code is recommended:

save_state=$(stty -g)
stty raw
...
stty "$save_state"

So the equivalent ruby code:

state = `stty -g`
system "stty raw"
...
system "stty #{state}"

cheers,
Mark
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top