equivalent/pull_tempfile

View on GitHub
README.md

Summary

Maintainability
Test Coverage
# PullTempfile

[![Build Status](https://travis-ci.org/equivalent/pull_tempfile.svg?branch=master)](https://travis-ci.org/equivalent/pull_tempfile)
[![Code Climate](https://codeclimate.com/github/equivalent/pull_tempfile/badges/gpa.svg)](https://codeclimate.com/github/equivalent/pull_tempfile)

Simple way how to download file from url to temporary file. (Please read more on "why"  would you need to do this down bellow)

Gem has no runtime gem dependancies (just standard Ruby lib) and it's really lightweight.

## Installation

Add this line to your application's Gemfile:

```ruby
gem 'pull_tempfile'
```

And then execute:

    $ bundle

Or install it yourself as:

    $ gem install pull_tempfile

## Usage


#### Simplest example

```ruby
require 'pull_tempfile'

original_filename = 'no idea.png'
url = 'https://meme.eq8.eu/noidea.jpg'

file = PullTempfile.pull_tempfile(url: url, original_filename: original_filename)
file.unlink # delete file after you done

# ...or

PullTempfile.transaction(url: url, original_filename: original_filename) do |tmp_file|
  puts tmp_file.path
  # ...
end
```

`transaction` will automatically delete (`unlink`) the temporary file after block
finish.

#### Paperclip & Rails

```ruby
require 'pull_tempfile'

class Medium < ActiveRecord::Base
  has_attached_file :file

  # ...
end

# when used as "AWS S3 browser upload" you can fetch original filename as "${filename}" metadata
# or use some different way to determine the file name.
original_filename = 'no idea.png'
url = 'https://meme.eq8.eu/noidea.jpg'

medium = Media.new

PullTempfile.transaction(url: url, original_filename: original_filename) do |tmp_file|
  medium.file = tmp_file
  medium.save!
end
```

## Use HTTParty

```
PullTempfile.config.puller = ->(url){ HTTParty.get(url) }
```

## License

The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).


## Reason why this Gem exist:

This gem is trying to be usecase agnostic. For example you can use
it to download CSV report and parse it and the CSV will automatically be
deleted:

```ruby
PullTempfile.transaction(url: 'https://mycompany.org/stupid-csv-report.csv', original_filename: 'dont-care.csv') do |tmp_file|
  CSV.foreach(tmp_file.path) do |row|
    # ....
  end
end
```

But main reason for it's  existence is that when you using gem [paperclip](https://github.com/thoughtbot/paperclip) `> 3.1.4` you can do something like this to pull file from url:

```ruby
class User < ActiveRecord::Base
  attr_reader :avatar_remote_url
  has_attached_file :avatar

  def avatar_remote_url=(url_value)
    self.avatar = URI.parse(url_value)
    # Assuming url_value is http://example.com/photos/face.png
    # avatar_file_name == "face.png"
    # avatar_content_type == "image/png"
    @avatar_remote_url = url_value
  end
end

user = User.new
user.avatar_remote_url = 'http://example.com/photos/face.png'
user.save!
```

And this is how you upload file from url.

> source
> https://github.com/thoughtbot/paperclip/wiki/Attachment-downloaded-from-a-URL

The problem is that if you do this on AWS S3 signd url is sending binary mime type
`binary/octet-stream` and therefore
if you are implementing validations on your uploads:

```
class User < ActiveRecord::Base
  # ...
  validates_attachment_content_type :avatar, :content_type => ["image/jpg", "image/jpeg", "image/png", "image/gif"]
  # ...
end
```

...you'll get error:

```
Validation failed: File has contents that are not what they are
reported to be, File is invalid, File content type is invalid
```

One solution is to skip validations in this context:

```ruby
#...
medium.save!(validate: false)  # will skip validations
```

...but that's not secure.

> Yes you can use context validations, or implement validations differently
> http://www.eq8.eu/blogs/22-different-ways-how-to-do-rails-validations
> but that's not the point,

So way how to get around this is to  download the file to your `/tmp/` folder as a temp
file, and upload it via Paperclip

Install gem `httparty`

```
# Gemfile
# ...
gem 'httparty'
# ...
```

Run `bundle install`


Next create new file `./lib/s3_helper.rb`

```ruby
require 'httparty'      # gem
module S3Helper
  def pull_asset(url:, destination:)
    File.open(destination, "wb") do |f|
      f.binmode
      f.write HTTParty.get(url).parsed_response
      f.close
    end
  end
end
```

And now you can do something like:

```ruby
user = User.new
require Rails.root.join('lib', 's3_helper')

S3Helper.pull_asset(url: "http://s3....../bbuesubeueueue", destination: "/tmp/my-file.jpg")

user.avatar = File.open("/tmp/my-file.jpg")
user.save!
```

But the problem is that you need to delete this file manually. So why
not to take advantage of Ruby `Tempfile`. This is exactly what this gem
is doing. Have a look on the Source code  https://github.com/equivalent/pull_tempfile/blob/master/lib/pull_tempfile.rb