Tag: Rails 4

Rails + Paperclip + Open-Uri – Downloading files from the internet and saving them with Paperclip

I don't like hotlinking. Being dependent on someone’s else infrastructure makes me a bit worried. Especially when I don't know who this "someone" is. That's why I've decided to download all the images that users embed in comments. After download, I attach them with Paperclip to their comments. Thanks to that, I can be sure, that they will always be served from my own servers (also I can use them later for some fancy stuff).

Theoretically, to obtain this, we could do something like that:

# We will use open-uri to download embedded images
require "open-uri"

file = open(image_url)
current_comment.pictures.create!(file: file)

Unfortunately this will "almost" work. If we try to get the file url:

current_picture.file.url #=> '/images/pictures/12313/file.'

we'll get it without an file extension. This happens, because open-uri creates a Tempfile without a file extension. Unluckily Paperclip uses file name to determine extension. To get around this issue, we need to create a new Tempfile with a valid extension and copy open-uri Tempfile content to newly created one:

# We will use open-uri to download embedded images
require "open-uri"

file = Tempfile.new ['', ".#{image_url.split('.').last}"]
file.binmode # note that our tempfile must be in binary mode
file.write open(image_url).read
file.rewind
file
current_comment.pictures.create!(file: file)

Update - Paperclip can do this on its own!

Posted by Aditya Sanghi (thanks a lot!):

current_comments.pictures.create!(file: URI.parse(image_url))

Although keep in mind, that you still need to handle 500, 404, etc errors (Paperclip can raise them).

Rails + CoffeeScript + Jquery – Marking multiply objects for destruction at a time (frontend)

When we build complicated nested forms (for example with 2 or more nested resources in it), quite often we would like to add a multiply remove feature. For example to remove whole groups of resources. We can't do this by simply removing HTML form parts, because objects related to this form parts won't be deleted. Instead, Rails use a
_destroy input that should be set to true, when we want to remove an object (but the form part still needs to be sent).

<input 
  type="hidden" 
  value="false" 
  name="product[groups_attributes][0][_destroy]" 
  id="product_groups_attributes_0__destroy" 
  class="hidden"
>

It is quite easy to use when we remove one resource at a time, we can just do this:

$('#product').find( 'input:hidden' ).val(true)

Of course it is far from being perfect, but for one resource it should do the job. We don't search using _destroy, we just assume, that only the hidden field that is there is the one that we want. Bad idea

What should we do, when we have multiply hidden fields and we only want to set value for those responsible for destruction? Well, we can use a nice Jquery feature: filter with a regexp.

destroy: (node) ->
  node.hide()
  node.find('input').filter ->
    return this.id.match(/__destroy/)
  .val('true')

We get a node (this is the tree part, that we want to remove), we iterate through all the inputs and we take only those, that have an __destroy postfix in their ID. Then we set all of them to true and we hide the whole node (so user will think, that it was removed). After we send such a form, Rails will know that it should remove all objects that were marked for destruction.

Copyright © 2024 Closer to Code

Theme by Anders NorenUp ↑