⌃⇧H in TextMate to 'Tidy' HTML causes NoMethodError - ruby

I tried using 'Tidy' in an HTML document for the first time yesterday, and got...
/tmp/temp_textmate.Z2P0KX:30:in `<main>': undefined method `empty?' for nil:NilClass (NoMethodError)
I've not done anything to the code in the bundle...
#!/usr/bin/env ruby -wKU
require ENV['TM_SUPPORT_PATH'] + '/lib/ui.rb'
require ENV['TM_SUPPORT_PATH'] + '/lib/exit_codes.rb'
result = `"${TM_TIDY:-tidy}" -f /tmp/tm_tidy_errors -iq -utf8 \
-wrap 0 --tab-size $TM_TAB_SIZE --indent-spaces $TM_TAB_SIZE \
--indent yes \
${TM_XHTML:+-asxhtml --output-xhtml yes} \
${TM_SELECTED_TEXT:+--show-body-only yes} \
--enclose-text yes \
--doctype strict \
--wrap-php no \
--tidy-mark no`
status = $?.exitstatus
at_exit { File.unlink('/tmp/tm_tidy_errors') } # Clean up error log
if status == 2 # Errors
msg = "Errors: " + File.read('/tmp/tm_tidy_errors')
TextMate.exit_show_tool_tip msg
elsif status == 1 # Warnings - use output but also display notification with warnings
log = File.read('/tmp/tm_tidy_errors').to_a.select do |line|
! (ENV['TM_SELECTED_TEXT'] and (line.include?('Warning: missing <!DOCTYPE> declaration') or line.include?("Warning: inserting missing 'title' element")))
end.join rescue nil
unless log.empty?
options = {
:title => "Tidy Warnings",
:summary => "Warnings for tidying your document (press escape to close):",
:log => log
}
TextMate::UI.simple_notification(options)
end
end
if ENV['TM_SOFT_TABS'] == "YES"
print result
else
in_pre = false
result.each_line do |line|
unless in_pre
tab_size = ENV["TM_TAB_SIZE"].to_i
space, text = /( *)(.*)/m.match(line)[1..2]
line = "\t" * (space.length / tab_size).floor + " " * (space.length % tab_size) + text
end
print line
in_pre = true if line.include?("<pre>")
in_pre = false if line.include?("</pre>")
end
end
The problem line is unless log.empty?.
I'm running TextMate 1.5.10 (1631) on OS X 10.6.6. I recently installed rvm and upgraded default Ruby to 1.9.2, though forcing TextMate to use 1.8.7 did not fix the problem.

I had the same problem. I have setup my Textmate to use the RVM version of ruby so that I can quickly test scripts.
I solved the problem by unchecking the "TM_RUBY" for the environment variable I had created.
What appears to be happening is the Textmate scripts that wrapper the /usr/bin/tidy command are not executing properly when using a ruby version other than the one that ships with OSX.
I'm curious to see what happens when Lion comes out. Hopefully, Textmate will take another look at these build-in scripts and give them a little "dusting-off".

If you look at the assignment to log, you'll see this:
log = File.read('/tmp/tm_tidy_errors').to_a.select do |line| ... end.join rescue nil
The rescue nil at the end will put a nil into log if the /tmp/tm_tidy_errors file isn't there or it can't be read or what ever. Then the script will call the .empty? method on nil but the nil object has no such method and the script falls over and dies.
You can suppress the problem by changing rescue nil to rescue '' or by changing unless log.empty? to unless log.nil? || log.empty? but that might not be the real problem.
Do you have a TM_TIDY environment variable set? Is there a tidy command in your PATH? Looks like your Tidy install isn't right (or possible not there at all). My OSX has /usr/bin/tidy and apparently that's standard. Try running that big tidy command by hand in a terminal and see what happens.

I had the same problem too, on a machine running OS X 10.9.5 with Ruby upgraded to ruby 2.0.0. Fixed it by taking mu is too short's suggestion to change unless log.empty? to unless long.nil? || log.empty?. That allowed Tidy to run properly, but the top of my HTML selection was still showing me annoying errors:
ruby: warning: -K is specified; it is for 1.8 compatibility and may cause odd behavior
/Applications/TextMate.app/Contents/SharedSupport/Support/lib/ui.rb:129: warning: assigned but unused variable - pid
I shut that up by changing the first line of the script from #!/usr/bin/env ruby -wKU to #!/usr/bin/env ruby -wKU -W0. Obviously the problems are still there under the hood, but for something helpful but not essential, as this functionality is, I think it's plenty good enough.

Related

Ruby script does not work with weird errors about highline library

I have the following Ruby script:
begin
puts "What is the password? "
the_pass = ask("") { |q| q.echo = "*" }
end while the_pass == nil || the_pass == "\n" || the_pass == ""
And it fails when I hit Enter:
undefined method default_external' for REXML::Encoding:Module
/Library/Ruby/Gems/1.8/gems/highline-1.6.19/lib/highline.rb:621:in
say'
/Library/Ruby/Gems/1.8/gems/highline-1.6.19/lib/highline.rb:914:in
get_response'
/Library/Ruby/Gems/1.8/gems/highline-1.6.19/lib/highline.rb:259:in
ask'
Looks like it fails when validating the input for the_pass, but I cannot understand the error, how are they related?
Thanks
This is bad error handling in the HighLine gem for Ruby < 1.9.
The offending line (identified by your error message) is:
statement.force_encoding(Encoding.default_external) if defined?(Encoding) && Encoding.default_external
You can handle this by either:
Removing any include REXML commands in your script. This will keep REXML::Encoding from being associated with Encoding.
Adding the following line somewhere early in your script:
REXML::Encoding.instance_eval { def default_external; false; end }
This line will prevent the missing method error and will prevent HighLine from trying to force encoding where it shouldn't.

Pre-Filled Prompt in Ruby [duplicate]

This question already has answers here:
What will give me something like ruby readline with a default value?
(6 answers)
Closed 6 years ago.
I am using to Ruby to write a small command line utility to search Pubmed. Right now, I prompt the user for a query and display the results, and the user has the option of appending to the query or entering an entirely new query. I would like to add the ability to edit the current query; i.e. the prompt should come pre-filled with an editable version of the previous query, like so:
Enter query: <PREVIOUS QUERY HERE>
It's easy enough to print out the previous query next to the prompt, but how do I make this output editable, as if the user had typed it herself?
#casper:
Thank you for the response Casper. I tried the code that you supplied below, and it does indeed work on its own. Strangely enough, it doesn't seem to work when I try to use it in a gem. My gem is called db_hippo. I added rb-readline as a dependency in my gemspec, and I put the extension to RbReadline in lib/db_hippo/rb-readline.rb
module DbHippo
module RbReadline
<CASPER'S EXTENSION HERE>
end
end
I wish to use the functionality in another submodule of DbHippo, DbHippo::Source. In DbHippo::Source I added at the top:
require 'rb-readline'
require 'db_hippo/rb-readline'
Then in one of the methods of DbHippo::Source, I have:
RbReadline.prefill_prompt(query)
query = Readline.readline("Query: ", true)
The query variable is definitely not empty, but for some reason in this context the prompt doesn't get prefilled. I also notice that if I put the extension in the same file (lib/db_hippo/rb-readline) without making it a submodule of DbHippo, I get the error: uninitialized constant DbHippo::Source::Readline (NameError) on the line:
query = Readline.readline("Query: ", true)
This all seems to have something to do with proper naming of modules, require statements, and gems. This is the first gem I've tried to build. Any idea what's going wrong here?
Maybe googlers will find this useful.
With plain Readline on Ruby 2.1 you could use:
def ask(prompt, default=nil)
if default
Readline.pre_input_hook = -> {
Readline.insert_text(default)
Readline.redisplay
# prevent re-trigger on every `readline`
Readline.pre_input_hook = nil
}
end
data = Readline.readline("#{prompt}: ")
return data.chomp
end
ask("MOAR...?", "COMPUTARS!") # displays: MOAR...? COMPUTARS!
At the prompt the text COMPUTARS! will be editable
You can do it with RbReadline:
require 'rubygems'
require 'rb-readline'
module RbReadline
def self.prefill_prompt(str)
#rl_prefill = str
#rl_startup_hook = :rl_prefill_hook
end
def self.rl_prefill_hook
rl_insert_text #rl_prefill if #rl_prefill
#rl_startup_hook = nil
end
end
RbReadline.prefill_prompt("Previous query")
str = Readline.readline("Enter query: ", true)
puts "You entered: #{str}"

How do I pass an array in ARGV command line arguments?

I have the following:
=== My program test.rb
def test (app, download, launch )
for i in 0..(app.length - 1)do
#DO SOMETHING HERE WITH THIS
p app[i].to_s + download[i].to_s + launch[i].to_s
end
end
test (ARGV[0].split(//) , ARGV[2].split(//) ,ARGV[1].split(//))
=== other program "other.rb" that parses and executes test.rb
app = ["fda","asdf"]
down = ["ok","nok"]
la = ["ok","ok"]
system("ruby test.rb #{app} #{down} #{la}")
I would like to print it as something like this:
fda ok ok
asdf no nok
However it is printed like this:
foo
dkk
ano
aok
sk
d
f
How can I pass arrays to the app correctly?
I copied your source and pasted it into two files:
test.rb:
def test (app, download, launch )
for i in 0..(app.length - 1)do
#DO SOMETHING HERE WITH THIS
p app[i].to_s + download[i].to_s + launch[i].to_s
end
end
test (ARGV[0].split(//) , ARGV[2].split(//) ,ARGV[1].split(//))
and test2.rb:
app = ["fda","asdf"]
down = ["ok","nok"]
la = ["ok","ok"]
system("ruby test.rb #{app} #{down} #{la}")
Using Ruby 1.9.2p290, I get this result when I run your code:
ruby test2.rb
test.rb:7: syntax error, unexpected ',', expecting ')'
test (ARGV[0].split(//) , ARGV[2].split(//) ,ARGV[1].split(//))
^
test.rb:7: syntax error, unexpected ',', expecting $end
test (ARGV[0].split(//) , ARGV[2].split(//) ,ARGV[1].split(//))
That tells me you didn't accurately report the problem you're seeing.
Fixing the syntax error returns this:
ruby test2.rb
"[[a"
"fos"
"dkd"
"a,f"
",]"
The output I got is what I would expect from your code, however it doesn't resemble what you said you got, so I doubt your reported output is correct. Please be accurate when you ask for help, both in the code you've written and the output you've seen.
Looking at the data your code is passing shows the arguments are:
0 [fda,
1 asdf]
2 [ok,
3 nok]
4 [ok,
5 ok]
FYI, I got those using this bit of code in place of test.rb:
ARGV.each_with_index do |a, i|
puts "#{i} #{a}"
end
I'd expect that, because system("ruby test.rb #{app} #{down} #{la}") doesn't do what you think it does. Here is what the actual command sent to the sub-shell looks like when system processes it:
test.rb [fda, asdf] [ok, nok] [ok, ok]
Again, that matches what the arguments look like when the app sees them.
To understand what is happening, you need to read the documentation for system:
system([env,] command... [,options]) -> true, false or nil
[...]
command... is one of following forms.
[...]
cmdname, arg1, ... : command name and one or more arguments (no shell)
So, system allows us to send all the parameters separately, which is really what you want. Changing test2.rb to:
app = %w[ fda asdf ]
down = %w[ ok nok ]
la = %w[ ok ok ]
system(
"echo",
"test.rb",
%Q["#{ app.join(',') }"],
%Q["#{ down.join(',') }"],
%Q["#{ la.join(',') }"]
)
gives me this on the command-line:
ruby test2.rb
test.rb "fda,asdf" "ok,nok" "ok,ok"
Which seems a bit more usable. Because of how system works, I can clean it up and reduce that a bit to:
app = %w[ fda asdf ]
down = %w[ ok nok ]
la = %w[ ok ok ]
system(
"ruby", # execute this
"test.rb", # with this script name
app.join(','), down.join(','), la.join(',') # and these parameters
)
Looking at what test.rb sees shows:
ruby test2.rb
0 fda,asdf
1 ok,nok
2 ok,ok
Again, an improvement and closer to what your code expected.
I am not going to finish correcting your code because I suspect this is a homework assignment and not a practical use. Some hints to help you are:
Your use of split isn't correct, nor is it where I would put it.
Your use of for is not idiomatic Ruby. Look at Array.each AND learn why it is preferred instead of for.
Your use of to_s is unnecessary, since you are already dealing with strings and characters.
you could use Thor which is a dsl for creating command line apps

"which in ruby": Checking if program exists in $PATH from ruby

my scripts rely heavily on external programs and scripts.
I need to be sure that a program I need to call exists.
Manually, I'd check this using 'which' in the commandline.
Is there an equivalent to File.exists? for things in $PATH?
(yes I guess I could parse %x[which scriptINeedToRun] but that's not super elegant.
Thanks!
yannick
UPDATE: Here's the solution I retained:
def command?(command)
system("which #{ command} > /dev/null 2>&1")
end
UPDATE 2: A few new answers have come in - at least some of these offer better solutions.
Update 3: The ptools gem has adds a "which" method to the File class.
True cross-platform solution, works properly on Windows:
# Cross-platform way of finding an executable in the $PATH.
#
# which('ruby') #=> /usr/bin/ruby
def which(cmd)
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
exts.each do |ext|
exe = File.join(path, "#{cmd}#{ext}")
return exe if File.executable?(exe) && !File.directory?(exe)
end
end
nil
end
This doesn't use host OS sniffing, and respects $PATHEXT which lists valid file extensions for executables on Windows.
Shelling out to which works on many systems but not all.
Use find_executable method from mkmf which is included to stdlib.
require 'mkmf'
find_executable 'ruby'
#=> "/Users/narkoz/.rvm/rubies/ruby-2.0.0-p0/bin/ruby"
find_executable 'which-ruby'
#=> nil
def command?(name)
`which #{name}`
$?.success?
end
Initially taken from hub, which used type -t instead of which though (and which failed for both zsh and bash for me).
Use MakeMakefile#find_executable0 with Logging Disabled
There are a number of good answers already, but here's what I use:
require 'mkmf'
def set_mkmf_log(logfile=File::NULL)
MakeMakefile::Logging.instance_variable_set(:#logfile, logfile)
end
# Return path to cmd as a String, or nil if not found.
def which(cmd)
old_mkmf_log = MakeMakefile::Logging.instance_variable_get(:#logfile)
set_mkmf_log(nil)
path_to_cmd = find_executable0(cmd)
set_mkmf_log(old_mkmf_log)
path_to_cmd
end
This uses the undocumented #find_executable0 method invoked by MakeMakefile#find_executable to return the path without cluttering standard output. The #which method also temporarily redirects the mkmf logfile to /dev/null to prevent cluttering the current working directory with "mkmf.log" or similar.
You can access system environment variables with the ENV hash:
puts ENV['PATH']
It will return the PATH on your system. So if you want to know if program nmap exists, you can do this:
ENV['PATH'].split(':').each {|folder| puts File.exists?(folder+'/nmap')}
This will print true if file was found or false otherwise.
Here's what I'm using. This is platform neutral (File::PATH_SEPARATOR is ":" on Unix and ";" on Windows), only looks for program files that actually are executable by the effective user of the current process, and terminates as soon as the program is found:
##
# Returns +true+ if the +program+ executable is found in the user's path.
def has_program?(program)
ENV['PATH'].split(File::PATH_SEPARATOR).any? do |directory|
File.executable?(File.join(directory, program.to_s))
end
end
I have this:
def command?(name)
[name,
*ENV['PATH'].split(File::PATH_SEPARATOR).map {|p| File.join(p, name)}
].find {|f| File.executable?(f)}
end
works for full paths as well as commands:
irb(main):043:0> command?("/bin/bash")
=> "/bin/bash"
irb(main):044:0> command?("bash")
=> "/bin/bash"
irb(main):006:0> command?("bush")
=> nil
I'd like to add that which takes the flag -s for silent mode, which only sets the success flag, removing the need for redirecting the output.
This is an improved version based on #mislav's answer. This would allow any type of path input and strictly follows how cmd.exe chooses the file to execute in Windows.
# which(cmd) :: string or nil
#
# Multi-platform implementation of "which".
# It may be used with UNIX-based and DOS-based platforms.
#
# The argument can not only be a simple command name but also a command path
# may it be relative or complete.
#
def which(cmd)
raise ArgumentError.new("Argument not a string: #{cmd.inspect}") unless cmd.is_a?(String)
return nil if cmd.empty?
case RbConfig::CONFIG['host_os']
when /cygwin/
exts = nil
when /dos|mswin|^win|mingw|msys/
pathext = ENV['PATHEXT']
exts = pathext ? pathext.split(';').select{ |e| e[0] == '.' } : ['.com', '.exe', '.bat']
else
exts = nil
end
if cmd[File::SEPARATOR] or (File::ALT_SEPARATOR and cmd[File::ALT_SEPARATOR])
if exts
ext = File.extname(cmd)
if not ext.empty? and exts.any?{ |e| e.casecmp(ext).zero? } \
and File.file?(cmd) and File.executable?(cmd)
return File.absolute_path(cmd)
end
exts.each do |ext|
exe = "#{cmd}#{ext}"
return File.absolute_path(exe) if File.file?(exe) and File.executable?(exe)
end
else
return File.absolute_path(cmd) if File.file?(cmd) and File.executable?(cmd)
end
else
paths = ENV['PATH']
paths = paths ? paths.split(File::PATH_SEPARATOR).select{ |e| File.directory?(e) } : []
if exts
ext = File.extname(cmd)
has_valid_ext = (not ext.empty? and exts.any?{ |e| e.casecmp(ext).zero? })
paths.unshift('.').each do |path|
if has_valid_ext
exe = File.join(path, "#{cmd}")
return File.absolute_path(exe) if File.file?(exe) and File.executable?(exe)
end
exts.each do |ext|
exe = File.join(path, "#{cmd}#{ext}")
return File.absolute_path(exe) if File.file?(exe) and File.executable?(exe)
end
end
else
paths.each do |path|
exe = File.join(path, cmd)
return File.absolute_path(exe) if File.file?(exe) and File.executable?(exe)
end
end
end
nil
end
On linux I use:
exists = `which #{command}`.size.>(0)
Unfortunately, which is not a POSIX command and so behaves differently on Mac, BSD, etc (i.e., throws an error if the command is not found). Maybe the ideal solution would be to use
`command -v #{command}`.size.>(0) # fails!: ruby can't access built-in functions
But this fails because ruby seems to not be capable of accessing built-in functions. But command -v would be the POSIX way to do this.
Solution based on rogeriovl, but complete function with execution test rather than existence test.
def command_exists?(command)
ENV['PATH'].split(':').each {|folder| File.executable?(File.join(folder, command))}
end
Will work only for UNIX (Windows does not use colon as a separator)
This is a tweak of rogeriopvl's answer, making it cross platform:
require 'rbconfig'
def is_windows?
Config::CONFIG["host_os"] =~ /mswin|mingw/
end
def exists_in_path?(file)
entries = ENV['PATH'].split(is_windows? ? ";" : ":")
entries.any? {|f| File.exists?("#{f}/#{file}")}
end
for jruby, any of the solutions that depend on mkmf may not work, as it has a C extension.
for jruby, the following is an easy way to check if something is executable on the path:
main » unix_process = java.lang.Runtime.getRuntime().exec("git status")
=> #<Java::JavaLang::UNIXProcess:0x64fa1a79>
main » unix_process.exitValue()
=> 0
main »
if the executable isn't there, it will raise a runtime error, so you may want to do this in a try/catch block in your actual usage.
#####################################################
# add methods to see if there's an executable that's executable
#####################################################
class File
class << self
###########################################
# exists and executable
###########################################
def cmd_executable?(cmd)
!ENV['PATH'].split(':').select { |f| executable?(join(f, cmd[/^[^ \n\r]*/])) }.empty?
end
end
end
Not so much elegant but it works :).def cmdExists?(c)
system(c + " > /dev/null")
return false if $?.exitstatus == 127
true
end
Warning: This is NOT recommended, dangerous advice!

Change the ruby process name in top

I would like to change the name of the ruby process that gets displayed in the linux/unix top command. I have tried the
$0='miname'
approach but it only works with the ps command and in top the process keeps getting displayed as "ruby"
Dave Thomas had an interesting post on doing this in rails. There's nothing rails specific about the actual process name change code. He uses the $0='name' approach. When I followed his steps the name was changed in ps and top.
In the post he suggests using the c keyboard command if your version of top doesn't show the short version of the command by default.
Ruby 2.1 introduced a Process.setproctitle method for this purpose:
Process.setproctitle("My new title")
I don't think Ruby has the facility builtin (setproctitle(3)). You should probably try to look at ruby-ffi and create the interface to setproctitle(3).
EDIT: I know you have your answer but I want to show you some code to use ffi:
require "ffi"
#
module LibC
extend FFI::Library
attach_function :setproctitle, [:string, :varargs], :void
end
LibC.setproctitle("Ruby: executing %s", :string, $0)
Does not work on OS X because setproctitle(3) does not exist, works on FreeBSD.
The $0 = 'Foo' method works -- but many versions of top will require you to toggle command-line mode on with 'c'. We this very method here with rails and CentOS. Works a treat
I had a similar problem, updated the technique from the Dave Thomas post a little by putting it in a rack middleware, rather than the before/after pattern.
Put this in lib/rack/set_process_title.rb:
# Set the process title to the URI being processed
#- useful for debugging slow requests or those that get stuck
class Rack::SetProcessTitle
def initialize(app)
#app = app
end
def call(env)
$0 = env['REQUEST_URI'][0..80]
#status, #headers, #response = #app.call(env)
$0 = env['REQUEST_URI'][0..80] + '*'
[#status, #headers, #response]
end
end
... and this goes at the end of config/environment.rb:
Rails.configuration.middleware.insert_after Rack::Lock, Rack::SetProcessTitle
More words in the blog post: http://blog.actbluetech.com/2011/06/set-your-process-name-in-top-and-ps.html
I know Keltia already posted something very similar, but Linux doesn't have setproctitle(3).
Linux has had this functionality in prctl() since version 2.6.9.
I used Fiddle/DL since they are included by default with Ruby.
require("fiddle")
def set_process_name_linux(name)
Fiddle::Function.new(
Fiddle::Handle["prctl".freeze], [
Fiddle::TYPE_INT, Fiddle::TYPE_VOIDP,
Fiddle::TYPE_LONG, Fiddle::TYPE_LONG,
Fiddle::TYPE_LONG
], Fiddle::TYPE_INT
).call(15, name, 0, 0, 0)
end
def set_process_name_unknown(name)
warn("No implementation for this OS.".freeze)
end
def set_process_name(name)
case RUBY_PLATFORM.split("-".freeze)[1]
when "linux".freeze
set_process_name_linux(name)
else
set_process_name_unknown(name)
end
end
From #jessehz answer, following code work perfect on my linux X86_64.
Ruby 1.9.3, 2.0, 2.1, 2.2, 2.3 is tested.
It will change the output in ps top command.
It can be kill or signal with pkill, pgrep, killall.
Perfect!
def set_process_name_linux(name)
handle = defined?(DL::Handle) ? DL::Handle : Fiddle::Handle
Fiddle::Function.new(
handle['prctl'.freeze], [
Fiddle::TYPE_INT, Fiddle::TYPE_VOIDP,
Fiddle::TYPE_LONG, Fiddle::TYPE_LONG,
Fiddle::TYPE_LONG
], Fiddle::TYPE_INT
).call(15, name, 0, 0, 0)
$PROGRAM_NAME = name
end
set_process_name_linux('dummy')

Resources