Problem with mime-types

Hallo,
I’m a Ruby newbie and have a ruby script (syncftp from Github) that I’m
trying to run. I had to install mime-types gem but unfortunately Ruby
reports an error:

C:/Ruby21-x64/lib/ruby/gems/2.1.0/gems/mime-types-2.1/lib/mime/type.rb:30:in
`<t op (required)>’: uninitialized constant MIME (NameError)

The mime-types are included as follows:
require ‘mime/types’

Can anyone help me to fix this? I’m using Ruby 2.1 on Windows 7.

Best regards - Ulrich

The source code on the line it reports is:

class MIME::Type

It produces an error because the module MIME has not been defined yet.
As a hotfix, you might be able to fix it by defining the module
yourself:

module MIME;end
require 'mime/types'

When I’m doing a fresh install of ruby (2.1.6) from
http://rubyinstaller.org/ on Windows 7 and install the gem with

$ gem install mime-types

It loads just fine with require’mime/types’. Checking the version in
MIME::Types::VERSION (defined in lib/mime/type.rb) gives 2.6.1. You seem
to be using an older version 2.1.

So I tried installing version 2.1, but this one works fine as well.

Try installing the latest version of mime-types, which can be found
here: GitHub - mime-types/ruby-mime-types: Ruby MIME type registry library

If you’re using gem, you can check where it gets the gems from with

$ gem sources list
https://rubygems.org/

You can force a version with

$ gem install mime-types -v 2.6.1

Hallo Dansei,
thanks for this detailed information. Installing the latest version of
mime-types fixed this problem. Now the sync seems to run but at the
end there is a new error:

D:\Gemeinsame
Dateien\Software\Internet\FTP\syncftp-master\lib>“C:\Ruby21-x64\bi
n\ruby.exe” -I. sync.rb
sync.bat: 100% |ooooooooooooooooooooooooooooooooooooooooo| Time:
0:00:00
sync.rb: 100% |ooooooooooooooooooooooooooooooooooooooooo| Time:
0:00:00
syncftp.rb: 100% |ooooooooooooooooooooooooooooooooooooooooo| Time:
0:00:00
D:/Gemeinsame
Dateien/Software/Internet/FTP/syncftp-master/lib/syncftp.rb:167:in
block in sync': undefined methodkeys’ for #String:0x00000004671c50
(NoMeth
odError)
from D:/Gemeinsame
Dateien/Software/Internet/FTP/syncftp-master/lib/sync
ftp.rb:230:in connect' from D:/Gemeinsame Dateien/Software/Internet/FTP/syncftp-master/lib/sync ftp.rb:146:insync’
from sync.rb:4:in `’

Hallo Ulrich,

tl;dr It works for me. Check your remote directory for a file called
“.syncftp”, delete it and try again. I can reproduce the error when I
change the contents of that file manually.

If it doesn’t work, check/post the contents of the file .syncftp. Also,
check your version of the gem and update if necessary.

If that fails as well, try running the following code and post the
output:

(change the arguments accordingly)

require ‘syncftp’
ftp = SyncFTP.new( ‘localhost’, :username => “MyUser”, :password =>
“********” )
begin
ftp.sync( :local => “rootdir”, :remote => “temp/syncfs/test” )
rescue StandardError
p ftp.instance_variable_get(:@remote_md5s)
p ftp.instance_variable_get(:@local_md5s)
end

And this code, to make sure YAML is working correctly.

require ‘yaml’
p YAML.load(“— {}”)

Also, it might help if you could post the contents of sync.rb


I assume you are using this gem: GitHub - glejeune/syncftp: Sync via FTP, only modified files

The code on the line 167 where the error occurs reads:

@remote_md5s.keys.clone.delete_if{ |f| @local_md5s.keys.include?(f) }.each

do |f|

A quick look tells us that @remote_md5s and @local_md5s are initialized
to an empty Hash {}. Upon doing #sync, it tries to update it:

@remote_md5s = YAML.load( File.open( tmpname ).read )

The file tmpname itself is read from

ftp.gettextfile( remote+“/”+“.syncftp”, tmpname )

+remote+ is what you pass when calling SyncFTP#sync, or “.” by default.

The file .syncftp itself is created by the gem upon doing a sync. When
it does not find the file (the first time you sync), it catches the
error and moves on, the value of @remote_md5s remains unchanged.

So my best guess is the .syncftp file contains invalid data.

Hallo Dansei,
many thanks for going so deep into this issue.
You are right, .syncftp contained some garbadge Ruby code.
I understand things in that way that syncftp will create this file, when
it doesn’t exist. Tried to delete it but then I also get an error:

D:\Gemeinsame
Dateien\Software\Internet\FTP\syncftp-master\lib>“C:\Ruby21-x64\bi
n\ruby.exe” sync.rb
C:/Ruby21-x64/lib/ruby/2.1.0/net/ftp.rb:325:in getresp': 450 t/.syncftp: No suc h file or directory (Net::FTPTempError) from C:/Ruby21-x64/lib/ruby/2.1.0/net/ftp.rb:339:in voidresp’
from C:/Ruby21-x64/lib/ruby/2.1.0/net/ftp.rb:530:in block (2 levels) in retrlines' from C:/Ruby21-x64/lib/ruby/2.1.0/net/ftp.rb:199:in with_binary’
from C:/Ruby21-x64/lib/ruby/2.1.0/net/ftp.rb:516:in `block in
retrlines’

    from C:/Ruby21-x64/lib/ruby/2.1.0/monitor.rb:211:in

mon_synchronize' from C:/Ruby21-x64/lib/ruby/2.1.0/net/ftp.rb:515:in retrlines’
from C:/Ruby21-x64/lib/ruby/2.1.0/net/ftp.rb:767:in list' from D:/Gemeinsame Dateien/Software/Internet/FTP/syncftp-master/lib/sync ftp.rb:38:in remote_file_exist?’
from D:/Gemeinsame
Dateien/Software/Internet/FTP/syncftp-master/lib/sync
ftp.rb:153:in rescue in block in sync' from D:/Gemeinsame Dateien/Software/Internet/FTP/syncftp-master/lib/sync ftp.rb:149:in block in sync’
from D:/Gemeinsame
Dateien/Software/Internet/FTP/syncftp-master/lib/sync
ftp.rb:230:in connect' from D:/Gemeinsame Dateien/Software/Internet/FTP/syncftp-master/lib/sync ftp.rb:146:in sync’
from sync.rb:5:in `’

Does syncftp has problems to create/access this file on the server? Any
other program like ftp clients doesn’t.
I also tried to create an empty .syncftp file on the server but this
results in an error too:

D:\Gemeinsame
Dateien\Software\Internet\FTP\syncftp-master\lib>“C:\Ruby21-x64\bi
n\ruby.exe” sync.rb
D:/Gemeinsame
Dateien/Software/Internet/FTP/syncftp-master/lib/syncftp.rb:254:in
block in send_dir': undefined method []’ for false:FalseClass
(NoMethodError)

    from D:/Gemeinsame

Dateien/Software/Internet/FTP/syncftp-master/lib/sync
ftp.rb:240:in foreach' from D:/Gemeinsame Dateien/Software/Internet/FTP/syncftp-master/lib/sync ftp.rb:240:in send_dir’
from D:/Gemeinsame
Dateien/Software/Internet/FTP/syncftp-master/lib/sync
ftp.rb:157:in block in sync' from D:/Gemeinsame Dateien/Software/Internet/FTP/syncftp-master/lib/sync ftp.rb:230:in connect’
from D:/Gemeinsame
Dateien/Software/Internet/FTP/syncftp-master/lib/sync
ftp.rb:146:in sync' from sync.rb:5:in

my sync.rb:

$LOAD_PATH.unshift(File.dirname(FILE))
require ‘syncftp’

ftp = SyncFTP.new( ‘ulrichbangert.de’, :username => “user”, :password =>
“*******” )
ftp.sync( :local => “test”, :remote => “t” )

Best regards - Ulrich

PS: Filezilla indicates adfrw (0644)for .syncftp

PPS: Output of your debug code:

D:\Gemeinsame
Dateien\Software\Internet\FTP\syncftp-master\lib>“C:\Ruby21-x64\bi
n\ruby.exe” syncftp_debug.rb
false
{“t/sync.bat”=>“8d7e798aa2f029711142d76275a02e33”}
{}

Great! Now the script works as expected: Loads up the files and when
executed a second time doesn’t. The .syncftp file now looks as follows:


t/sync.bat: 8d7e798aa2f029711142d76275a02e33
t/sync.rb: fffe9164ee40fec1fb07bc9e99946b55
t/syncftp.rb: ef92e1f402f9e06cad5afc3c33e25f8c
t/syncftp_debug.rb: 004b7ebbe777127891523a2e3d6c9768

“I suppose the author did not test it on Windows?” That’s possible
indeed. AFAIK the origin of Ruby ist Unix/Linux

One small issue is left: At the end of the run still the following error
is output:

D:\Gemeinsame
Dateien\Software\Internet\FTP\syncftp-master\lib>“C:\Ruby21-x64\bi
n\ruby.exe” sync.rb
sync.bat: 100% |ooooooooooooooooooooooooooooooooooooooooo| Time:
0:00:00
sync.rb: 100% |ooooooooooooooooooooooooooooooooooooooooo| Time:
0:00:00
syncftp.rb: 100% |ooooooooooooooooooooooooooooooooooooooooo| Time:
0:00:00
syncftp_debug: 100% |ooooooooooooooooooooooooooooooooooooooooo| Time:
0:00:00
D:/Gemeinsame
Dateien/Software/Internet/FTP/syncftp-master/lib/syncftp.rb:193:in
delete': Permission denied @ unlink_internal - C:/Users/Ulrich/AppData/Local/T emp/sync.rb20150607-5520-doysbp-0 (Errno::EACCES) from D:/Gemeinsame Dateien/Software/Internet/FTP/syncftp-master/lib/sync ftp.rb:193:insync’
from sync.rb:5:in `’

Hallo Ulrich,

I believe I understand what causes this problem now.

syncftp.rb uses Net for retrieving the file. If it does not find it, it
raises an exception. The script catches (rescues) the exception, and
proceeds normally if the file .syncftp does not exist on the remote
server.

Net has got many exceptions and it may raise different exceptions
depending upon your platform, operating system etc, a problem I
encountered once too.

On LinuxMint/Debian, it raises the following exception when a file is
not found:

#<Net::FTPPermError: 550 Requested action not taken.
storage/extSdCard/tmp/.syncftp : file does not exist

For you on Windows it raised an Net::FTPTempError “No such file or
directory”. I suppose the author did not test it on Windows?


Thus in order to fix this issue:

(a) edit the file syncftp.rb (D:/Gemeinsame
Dateien/Software/Internet/FTP/syncftp-master/lib/syncftp.rb) directly,
changing

 rescue Net::FTPPermError => e
   raise Net::FTPPermError, e.message, caller if
   ftp.remote_file_exist?(remote+"/"+".syncftp")
 end

to

 rescue Net::FTPPermError, Net::FTPTempError => e
   raise Net::FTPPermError, e.message, caller if
   ftp.remote_file_exist?(remote+"/"+".syncftp")
 end

(b) if editing the script file is not an option, monkey patch:

Net::FTPTempError = Net::FTPPermError
ftp.sync(:local => “test”, :remote => “tmp”)

This might introduce other issues if you start to use other
libraries/scripts relying on Net.Alternatively, redefine the entire
method after loading the library (this is the same as in the file
syncftp.rb, I only changed the one line with the rescue:

require ‘syncftp’

class SyncFTP
def sync( options = {} )
options = { :local => “.”, :remote => “.” , :passive =>
false}.merge( options )
local, remote , passive = options[:local], options[:remote],
options[:passive]

tmpname = tmpfilename
connect do |ftp|
  ftp.passive = passive
  # Read remote .syncftp
  begin
    ftp.gettextfile( remote+"/"+".syncftp", tmpname )
    @remote_md5s = YAML.load( File.open( tmpname ).read )
  rescue Net::FTPPermError, Net::FTPTempError => e
    raise Net::FTPPermError, e.message, caller if

ftp.remote_file_exist?( remote+"/"+".syncftp" )
end

  # Do the job Bob !
  send_dir( ftp, local, remote )

  # Write new .syncftp
  File.open( tmpname, 'w' ) do |out|
    YAML.dump( @local_md5s, out )
  end

  # Delete files
  @delete_dirs = []
  @delete_files = []
  @remote_md5s.keys.clone.delete_if{ |f|

@local_md5s.keys.include?(f) }.each do |f|
if @remote_md5s[f] == “*”
@delete_dirs << f
else
@delete_files << f
end
end
@delete_files.each do |f|
@log.info “Delete ftp://#{@host}:#{@port}/#{f}”
begin
ftp.delete( f )
rescue => e
@log.infoftp://#{@host}:#{@port}/#{f} does not exist”
end
end
@delete_dirs.each do |f|
@log.info “Delete ftp://#{@host}:#{@port}/#{f}”
begin
ftp.delete( f )
rescue => e
@log.infoftp://#{@host}:#{@port}/#{f} does not exist”
end
end

  ftp.puttextfile( tmpname, remote+"/"+".syncftp" )
end
File.delete( tmpname )

end
end


It interprets the file .syncftp as YAML, expecting an array. So in order
to create an “empty” file for testing, you need to create a file with an
empty array, eg. the following content:

— {}

Best regards, Dansei

I’m glad it worked. The script copies the remote .syncftp to your local
machine with the filename +tmpname+. At the end of the sync method, it
deletes the file. But as there had been no such file on the remote host,
there is no auch file on your local host either, and File.delete throws
an error.

According to the docs, File.delete is supposed to throw an error, so
this is probably a bug. But could be related to Windows locking a file
when it’s opened. Check that you’ve got read/write permissions for
C:/Users/Ulrich/AppData/Local/Temp/

At any rate, since deleting the file is the very last thing sync does,
it’s probably safe if you just ignore it, ie. rescue the error.

begin
ftp.sync …
rescue StandardError
end

Or just apply the change to the sync method (line 191 in syncftp.rb):

begin
File.delete( tmpname )
rescue StandardError
end

(I rescue StandardError because it might raise any number of other
Errno::xxxx errors depending on the circumstances…)

Just make sure your Temp directory is not getting clogged up with temp
files.

Ziemlich spät hier, gute Nacht erstmal :slight_smile:

Thanks for this advice but unfortunately it was not successful. I
changed to

  ftp.puttextfile( tmpname, remote+"/"+".syncftp" )
end
begin
    File.delete( tmpname )
    rescue StandardError
end

end
The script reports:

D:\Gemeinsame
Dateien\Software\Internet\FTP\syncftp-master\lib>“C:\Ruby21-x64\bi
n\ruby.exe” sync.rb
D:/Gemeinsame
Dateien/Software/Internet/FTP/syncftp-master/lib/syncftp.rb:193:in
delete': Permission denied @ unlink_internal - C:/Users/Ulrich/AppData/Local/T emp/sync.rb20150608-16000-v62j3-0 (Errno::EACCES) from D:/Gemeinsame Dateien/Software/Internet/FTP/syncftp-master/lib/sync ftp.rb:193:insync’
from sync.rb:5:in `’

Oh, Du sprichst deutsch? Bei mir wird es auch meistens spät. Manchmal
verbeiße ich mich in eine Aufgabe oder ein Problem und kann nicht
aufhören, bis ich fertig bin.

Some unfriendly translation experience

http://www.dear-fashion.com/2015-Trendy-Golden-Paillette-Deep-V-Backless-Evening-Dress-p12767.html

Please check your script. The error is raised within the method called
on line 5. In order to catch (=rescue) it, you need to put it inside the
block; after “begin” and before “rescue”. (ruby error handling tutorial
Learn How to Blog and Build Websites for Profit!)

begin

rescue StandardError

end

If that fails, try replacing “rescue StandardError” with “rescue
Exception” for testing purposes, this catches any and all errors.

PS: Dansei Yuuki ist die Übersetzung der Bedeutung meines deutschen
Namens xD

Hm, I assumed that
File.delete( tmpname )
on line 193 causes the error and put rescue StandardError immediately
below:
begin
File.delete( tmpname )
rescue StandardError
end

Oh, so you’re modifying “syncftp.rb” directly.

  • It’s pretty hard to tell with just the code snippet. If I remove the
    file before it calls File.delete in order to provoke the error and catch
    it like you do, it works for me. You can use attach files to your post
    here (“Choose File”, “attach more files”), so it would help if you could
    attach both files.

  • Try catching Exception or Errno::EACCES instead of StandardError; this
    has got to work or otherwise the error must be occurring on another
    line.

  • The script always creates the file +tmpname+ (line 162, “YAML.dump(
    @local_md5s, out )”) so the file should exist. The error traceback tells
    you what the file is named (eg. “C:/Users/Ulrich/AppData/Local/T
    emp/sync.rb20150608-16000-v62j3-0”) – have you checked whether it
    exists and whether you can access/delete it? Perhaps Windows has got it
    locked upon run-time?

That’s great, works perfectly now. Fine that you found a clean solution
where the tmp file is deleted correctly.

Many thanks for your assistance!

Fine script and pitty that obviously the author does no longer develop
it further. Exclude lists for files and folders would be a fine
enhancement.

Ich find’s Klasse, dass Du dir so viel Mühe gemacht hast, in dieses
Skript einzusteigen und die Fehler zu beheben.

Alright, I tried it on Windows and could reproduce the error. I also
found the cause of the error.

Windows can’t delete the file because it is locked. The problem is with
line 151:

@remote_md5s = YAML.load( File.open( tmpname ).read )

This opens the file and reads it. And leaves it opened. To fix it,
change it to

@remote_md5s = YAML.load( File.read( tmpname ) )

This closes the file properly, and makes the error go away, and actually
deletes the temporary file.