sinatra and http PUT - ruby

suppose i want to use curl to put a file to a webservice this way
curl -v --location --upload-file file.txt http://localhost:4567/upload/filename
in sinatra i can do:
#!/usr/bin/env ruby
require 'rubygems'
require 'sinatra'
put '/upload/:id' do
#
# tbd
#
end
how can i read the streaming file?
more or less i want something like this:
http://www.php.net/manual/en/features.file-upload.put-method.php#56985

The most basic example is writing it to the currect directory you are running sinatra in with no checking for existing files ... just clobbering them.
#!/usr/bin/env ruby
require 'rubygems'
require 'sinatra'
put '/upload/:id' do
File.open(params[:id], 'w+') do |file|
file.write(request.body.read)
end
end
Also, you can leave off the filename portion in the curl command and it will fill it in for you with the filename. Foe example:
curl -v --location --upload-file file.txt http://localhost:4567/upload/
will result in writing the file to http://localhost:4567/upload/file.txt

require 'rubygems'
require 'sinatra'
require 'ftools'
put '/upload' do
tempfile = params['file'][:tempfile]
filename = params['file'][:filename]
File.mv(tempfile.path,File.join(File.expand_path(File.dirname(File.dirname(__FILE__))),"public","#{filename}"))
redirect '/'
end
In this way, you does not have to worry about the size of the file, since he's not opened (readed) in memory but just moved from the temp directory to the right location skipping a crucial blocker. In fact, the php code do the same stuff, read the file in 1k chunks and store in a new file, but since the file its the same, its pointless.
To try you can follow the answer of Ben.

Related

How can I avoid redirecting when try to download a file using wget or curl?

I'm trying to get/download some files from an url. I'm make a tiny script in ruby to get this files. Follow the script:
require 'nokogiri'
require 'open-uri'
(1..2).each do |season|
(1..3).each do |ep|
season = season.to_s.rjust(2, '0')
ep = ep.to_s.rjust(2, '0')
page = Nokogiri::HTML(open("https://some-url/s#{season}e{ep}/releases"))
page.css('table.table tbody tr td a').each do |el|
link = el['href']
`curl "https://some-url#{link}"` if link.match('sujaidr.srt$')
end
end
end
puts "done"
But the response from curl is:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL:
/some-url/s0Xe0Y/releases. If not click the link.
When I use wget the redirected page is downloaded. I tried to set the user agent but not works. The server always redirect the link only when I try download the files through curl or others cli's like wget, aria2c, httpie, etc. And I can't find any solution for now.
How can I do this?
Solved
I decide use Watir webdriver to do this. Works great for now.
If you want to download the file, rather then the page doing the redirection try using the option -L within your code for example:
curl -L "https://some-url#{link}"
From the curl man:
-L, --location
(HTTP) If the server reports that the requested page has moved to a different
location (indicated with a Location: header and a 3XX
response code), this option will make curl redo the request on
the new place.
If you are using ruby, instead of calling curl or other 3rd party tools, you may cat to use something like this:
require 'net/http'
# Must be somedomain.net instead of somedomain.net/, otherwise, it will throw exception.
Net::HTTP.start("somedomain.net") do |http|
resp = http.get("/flv/sample/sample.flv")
open("sample.flv", "wb") do |file|
file.write(resp.body)
end
end
puts "Done."
Check this answer from where the example came out: https://stackoverflow.com/a/2263547/1135424

passing arguaments to a system call in Rails, but not plain ruby program

Ubuntu 12.04
Sinatra 1.3.3
Why does passing an argument to a ruby system call (%x[] or ``) give me a 'not found' error in my sinatra app? The same code works fine in a normal ruby script running from the same directory.
I have a file test.rb like this
output = %x["ls"]
p output
When I run it with "ruby test.rb" I get the contents of the current directory in the console, as expected.
If I modify the program to give an argument to the system call like so:
output = %x["ls sub_dir/"]
p output
I get the contents of sub_dir, which sits in the current directory, as expected.
So far so good.
Now if I make a Sintra app with a post method:
require 'rubygems'
require 'bundler/setup'
require 'sinatra'
post "/" do
output = x["ls"]
return output
end
The response to a Post call to "/" returns the contents of the current directory, which includes 'sub_dir', as expected.
If I try to add the argument to the system call to the sinatra app like so:
require 'rubygems'
require 'bundler/setup'
require 'sinatra'
post "/" do
output = x["ls sub_dir/"]
return output
end
the response is nil and there is an error in the console:
sh: 1: ls sub_dir/: not found
Why does adding a parameter to a system call in my sinatra app cause it to crash, when the same code called from a plain ruby script, run from the same location works perfectly.
By the way, the 'ls' example shown here is not the command I really need to run, so please don't explain a different way to get this information. I have an executable file that takes a file name as a parameter that I need to run, which behaves exactly the same way.
Thanks in advance!
If you want to specify a path in relation to the application, you could use something like this:
post "/" do
path = File.join(File.dirname(__FILE__), "sub_dir")
%x[ls #{path}]
end
However, if you want to list the contents of a directory, why not do it in Ruby?
I rewrote the sinatra app in another file in the same directory.
Everything works as expected.
I did not find the reason and I deleted the original file so that I won't lose anymore time trying to figure it out.

Require a ruby file in a ruby file

I want to require a file called config.rb in a different ruby file called basics.rb. I'm using Sinatra as my web framework. I'm sure there's a way to do this, I just can't find anything in the docs.
Hopefully it would look something like
post '/' do
require 'config.rb'
// logic
end
If config.rb is in your load path, you can require it at the top of your basics.rb file with require 'config'. If it is not in your load path, you'll need something like require '/path/to/your/config'.
The code you've posted will require the file. But only when someone POSTs to '/'.
Also, it's normal to omit the .rb extension when requiring ruby files. But you can include it if you like.
You can view your load path by inspecting the global variable $LOAD_PATH. From the command line ruby -e 'puts $LOAD_PATH' will print it for your version of ruby. You can also add directories to your load path.

How to open a file name that looks like a URL after require open-uri?

How to open a file name that seems like a URL after require 'open-uri'
require 'open-uri'
open("http://google.com")
in folder
- http://google.com
According to ruby-doc.org, open-uri creates an alias to the original open called open_uri_original_open. You can use that method for enforcing opening local files.
But then, I'm not sure if you can really have filenames like http://google.com in your OS/filesystem.

Is there a shorter way to require a file in the same directory in ruby?

Is there a shorter way to require a file located in the same directory (as the script being executed)?
require File.expand_path(File.dirname(__FILE__) + '/some_other_script')
I read that require "my_script" and require "./my_script" will actually load the script twice (ruby will not recognize that it is actually the same script), and this is the reason why File.expand_path is recommended: if it is used every time the script is required, then it will only be loaded once.
It seems weird to me that a concise language like Ruby does not seem to have a shorter solution. For example, python simply has this:
import .some_other_module_in_the_same_directory
I guess I could monkey-patch require... but that's just evil! ;-)
Since ruby 1.9 you can use require_relative.
Check the latest doc for require_relative or another version of the Core API.
Just require filename.
Yes, it will import it twice if you specify it as filename and ./filename, so don't do that. You're not specifying the .rb, so don't specify the path. I usually put the bulk of my application logic into a file in lib, and then have a script in bin that looks something like this:
#!/usr/bin/env ruby
$: << File.join(File.dirname(__FILE__), "/../lib")
require 'app.rb'
App.new.run(ARGV)
Another advantage is that I find it easier to do unit testing if the loading the application logic doesn't automatically start executing it.
The above will work even when you're running the script from some other directory.
However, inside the same directory the shorter forms you refer to work as expected and at least for ruby 1.9 won't result in a double-require.
testa.rb
puts "start test A"
require 'testb'
require './testb'
puts "finish test A"
testb.rb
puts "start test B"
puts "finish test B"
running 'ruby testa.rb' will result in:
start test A
start test B
finish test B
finish test A
However, the longer form will work even from another directory (eg. ruby somedir/script.rb)
Put this in a standard library directory (somewhere that's already in your default loadpath $:):
# push-loadpath.rb
if caller.first
$: << File.expand_path(File.dirname(caller.first))
end
Then, this should work
% ls /path/to/
bin.rb lib1.rb lib2.rb #...
% cat /path/to/bin.rb
load 'push-loadpath.rb'
require 'lib1'
require 'lib2'
#...
caller gives you access to the current callstack, and tells you what file and where, so push-loadpath.rb uses that to add the file that load'd it to the loadpath.
Note that you should load the file, rather than require it, so the body can be invoked multiple times (once for each time you want to alter the loadpath).
Alternately, you could wrap the body in a method,
# push-loadpath.rb
def push_loadpath
$: << File.expand_path(File.dirname(caller.first))
end
This would allow you to require it, and use it this way:
% ls /path/to/
bin.rb lib1.rb lib2.rb #...
% cat /path/to/bin.rb
require 'push-loadpath'
push_loadpath
require 'lib1'
require 'lib2'
#...

Resources