RoR Carrierwave: Add remote files with multiple upload - ruby-on-rails

I'm using the possibility to upload multiple files at once with carrierwave as described here:
https://github.com/carrierwaveuploader/carrierwave#multiple-file-uploads
So I have
class Item < ActiveRecord::Base
mount_uploaders :images, ImagesUploader
serialize :images, JSON
...
end
Now I want to upload a remote file (not from local drive). Usually I would use something like this in my controller
class ItemsController < ApplicationController
...
item.remote_images_url = params[:image_url]
...
end
But the helper remote_images_url (mind the plural version remote_images_url) only give me
undefined method remote_images_url
I also tried remote_image_url which would be the default helper in case of single file upload. Also undefined method.
How can I upload remote files when using "multiple files upload" with carrierwave?

Eventually I had a look into the github repository of carrierwave and I found this:
https://github.com/carrierwaveuploader/carrierwave/blob/master/lib/carrierwave/mount.rb
Luckily it's all described in the comments in this file. There was only one little thing I had to change. In the comments it says that for a column called images I had to use the helper method remote_image_urls. But actually I have to use remote_images_urls (always plural).
So that's the solution in my case:
class Item < ActiveRecord::Base
mount_uploaders :images, ImagesUploader
serialize :images, JSON
...
end
class ItemsController < ApplicationController
...
item.remote_images_urls = [params[:image_url]]
...
end
Mind the right plural types in the helper method as well as the surrounding array for the params (carrierwave excpects an array).

You need to use item.remote_images_urls not item.remote_images_url and assign the array of remote url. This works for me although CarrierWave rubydoc suggests me to use item.remote_image_urls . So, maybe you should try one of these .
For reference CarrierWave rubydoc

Related

Trouble Routing to active_storage_attachments

implementing drag and drop for active storage attachments. Some code from goRails and some from my brain/the web.
Since we are ordering attachments, I went ahead and created a model and controller to represent them since installing activestorage does not.
I am using "acts_as_list" so needed a model to add the acts_as_list hook.
So I have:
models/active_storage_attachment.rb
class ActiveStorageAttachment < ApplicationRecord
acts_as_list
end
controllers/active_storage_attachments_controller.rb
class ActiveStorageAttachmentsController < ApplicationController
before_action :set_active_storage_attachment, only: [:move]
def move
#active_storage_attachment.insert_at(params[:position].to_i)
head :ok
end
private
def set_active_storage_attachment
#active_storage_attachment = ActiveStorageAttachment.find(params[:id])
end
end
I also added a route:
resources :active_storage_atachments do
member do
patch :move
end
end
The route is simple for sortablejs to call via js for an ajax request.
The call ends up being something like this:
https://myamazonurl.com/active_storage_attachments/96/move
Rails is telling me no route exists but if I run rails routes I SEE the freaking route.
move_active_storage_attachment_path PATCH /active_storage_attachments/:id/move(.:format)
active_storage_attachments#move
I would assume this is only the beginning of my trouble but I can't sort out why the simplest part of this, the route, is not working.
Since we are ordering attachments, I went ahead and created a model and controller to represent them since installing activestorage does not.
Wrong. ActiveStorage has a model named ActiveStorage::Attachment. The model exists in the Rails gem - not in your application code. I wouldn't advise adding your own ActiveStorageAttachment model as this seems like an open invitation for bugs and confusion - especially as all the macros setup assocations to ActiveStorage::Attachment.
In some cases if you need to add a lot of buisness logic to your attachments you might actually be missing an intermediary model:
class Sortable < ApplicationRecord
has_one_attached :file
belongs_to :listable, polymorphic: true
accepts_nested_attributes_for :file
acts_as_list
delegate :service_url, # ...
to: :file
def move
#...
end
end
I would generally advise this over adding columns to active_storage_attachments and monkeypatching ActiveStorage::Attachment.
I would assume this is only the beginning of my trouble but I can't sort out why the simplest part of this, the route, is not working.
My guess is that you're not using the correct HTTP method. You want to use a POST request with the _method=PATCH parameter or a native PATCH request. Check your logs.
I would advise an integration test (request spec in RSpec) before you do the JS integration as you want to ensure that the server side works as intended before you start approaching the problem from both ends.

Rails admin gem showing s3 attachments which is IMAGES/PDF/DOCS/XLSX

I am using rails_admin gem for the rails api application for the backend admin side.
I am using rails 6 with active_storage and storing attachments on the S3.
On the admin side, I need to display the list of attachments which might be images or files anything.
My question is How to show those in index method, do I need to show images then what to show in pdf/docs, do I need to show the only the link of s3?
currently, it looks like these broken images are the images and other one were files
My model
class Attachment < AttachmentBlock::ApplicationRecord
self.table_name = :attachments
include Wisper::Publisher
has_one_attached :attachment
belongs_to :account, class_name: 'AccountBlock::Account'
has_and_belongs_to_many :orders , class_name: 'BxBlockOrdermanagement::Order'
scope :not_expired, -> {where('is_expired = ?',false)}
end
What should I use here to list the attachment that the user upload?
how to check the attachment type and then if its image then shows the image and if it files then show file url from s3?
thanks.
Yours is a two part question:
To add links to the side menu on rails admin you need to define models so if you wanted an index for all the attachments of type 'pdf' you could use rails STI (single table inheritance) or define a custom default_scope like this:
class ImageAttachment < Attachment
def self.default_scope
where('attachments.attachment LIKE ?', '%png') }
end
end
To customize the row of each individual attachment record you need to defined that behavior for the field on the model rails admin config.
For example you can put this block of code inside your ImageAttachment class.
class ImageAttachment < Attachment
def self.default_scope
where('attachments.attachment LIKE ?', '%png') }
end
rails_admin do
configure :attachment do
view = bindings[:view]
attachment = bindings[:object]
if view
view.content_tag(:img, attachment.attachment.url)
else
''
end
end
list do
field :attachment
end
end
end
As you can see inside the attachment field configuration block you have access to the attachment object and of course all its methods, you can determine the type of attachment and do some special html rendering.

How to dynamically determining carrierwave uploader column name?

I have some meta-programming that condenses multiple direct S3 upload processes into a single controller. Then, those actions are shared across many models. To accomplish this, among many other details, I need to know the column name of a given models uploader. I facilitate this by having an 'uploader_name' method in each of my models that use the shared actions. For example, I have an Expense model that has a receipts uploader...
def uploader_name
'receipt'
end
mount_uploader :receipt, ReceiptUploader
So, now I can call Expense.new.uploader_name to return a string, or in a shared action I would use #obj.send(#obj.uploader_name) to get the uploader object (where #obj is any one of the affiliated models using the shared actions). This works fine. However, I think I can clean it up. One way that would help me refactor is not needing the uploader_name method. Or, being able to use a single uploader_name method in a shared module that is able to dynamically determine the column name of the uploader.
Is there a method within Carrierwave where I can access the column name on a models uploader without know what the uploader is called? Since the module is shared across multiple classes, i have to figure it out dynamically---I have read through the carrierwave, but not finding a solution.
Something like this...for example:
obj = self.class.name
obj.new.uploader # would return the mounted :receipts uploader
obj.new.uploader_column # would return the uploader column, in this case :receipt
There are a few methods provided by the uploader class. So, to solve this I just delegate the :mounted_as method from the uploader to the class that calls it. Then, without knowing the name of the actual uploader I can just call self.mounted_as.to_s to return the name of the uploader.
So, for a User model with an avatar uploader I do this.
class User
mount_uploader :avatar, AvatarUploader
delegate :mounted_as, to: :avatar
So, if I call #user.mounted_as I get :avatar. Or, more importantly...if I call self.mounted as I get :avatar when self is a User. I have a module that is shared between all models that use an uploader. In that module I include
def uploader_name
self.mounted_as.to_s
end
Now, I can do some meta-programming since I do not know who self is, and I do not know what self's uploader is called. But, now I can get access to both dynamically. This is helpful because I can share one uploader controller and one uploader form across multiple classes who use an uploader.
I can also do things like pass accepted extensions to the form input (see html5 accepts option) by simply grabbing the extensions in the uploader's extension_whitelist method.
self.send(uploader_name).extension_whitelist
# this is dynamically getting #user.avatar.extension_whitelist
# or #company.logo.extension_whitelist
I can also set uploader attributes using, for example...
obj.send(obj.uploader_name).content_type = 'image/jpeg'
# this is dynamically setting #user.avatar.content_type = 'image/jpeg'
# or #company.logo.content_type = 'image/jpeg'
obj.class.uploaders.keys.first
=> :receipt

Mailboxer attachments via CarrierWave

I'm trying to send messages with attachments, using CarrierWave.
In Mailboxer gem I find that the uploader mounted here.
It uses AttachmentUploader for the field :attachment
In base functionality all works good. Attached files stored in public/uploads folder.
But I need add custom functionality to AttachmentUploader (Eg. change the path to store attachments etc.).
I trying create my own attachment_uploader.rb in app/uploaders but it doesn't work.
I just added to initializers/mailboxer.rb
...
class AttachmentUploader < AllAttachmentUploader
end
It allow me inherit from AllAttachmentUploader to AttachmentUploader which was defined in Mailboxer.

Ruby (Rails) Delegate attributes to another model's method?

-EDIT-
After reading about the Delegate method from the first answer, my question is this, is it possible to delegate two different methods to another single method.
IE: I currently have: #photo.attachment.file.url, and #photo.attachment.height, and #photo.attachment.width
I'd like to be able to access all of these via #photo.file.url, #photo.file.height, #photo.file.width.
The reason for the syntax is Attachment is a model that uses Paperclip to manage files, Paperclip is generating the .file method (the model is called Attachment, the model uses Paperclip's has_attached_file :file).
-ORIGINAL QUESTION-
I was wondering about aliasing methods and attributes in Ruby (I think this is a general ruby question, although my application is in Rails 3):
I have two models: Photo has_one Attachment.
Attachment has "height" and "width" attributes, and a "file" method (from Paperclip).
So by default I can access bits of the Attachment model like so:
photo.attachment.width # returns width in px
photo.attachment.height # returns height in px
photo.attachment.file # returns file path
photo.attachment.file.url #returns url for the default style variant of the image
photo.attachment.file.url(:style) #returns the url for a given style variant of the image
Now, in my photo class I have created this method:
def file(*args)
attachment.file(*args)
end
So, now I can simply use:
photo.file # returns file path
photo.file.url # returns file url (or variant url if you pass a style symbol)
My question is, I was able to direct photo.attachment.file to just photo.file, but can I also map height and width to photo.file, so that, for the sake of consistency, I could access the height and width attributes through photo.file.height and photo.file.width?
Is such a thing possible, and if so what does it look like?
So what you are asking is that
photo.file --> photo.attachment.file
photo.file.url --> photo.attachment.file.url
photo.file.width --> photo.attachment.width
You can't solve this with delegates, because you want that file to mean different things based on what follows next. To achieve this you would need to reopen paperclip, which i would not recommend (because i believe the api is good the way it is).
The only way i can think of to solve this, is to add eliminate the file level too. Like so:
photo.width --> photo.attachment.width
photo.file --> photo.attachment.file
photo.url --> photo.attachment.file.url
This you could then solve by using a delegate for each of the wanted methods.
So you write
class Photo
delegate :width, :height, :file, :to => :attachment
delegate :url, :to => :'attachment.file'
end
Hope this helps.
You can use Rails 'delegate' method. Have a look at my answer for this question:
What is a more Ruby-like way of doing this command?
The simplest way that comes to mind is to delegate url method in attachment to file:
class Attachment < ActiveRecord::Base
delegate :url, :to => :file
end
This way you can call photo.attachment.url, photo.attachment.width, photo.attachment.height, which for me seems pretty consistent. You could optionally alias attachment to file - this way you'd get the exact method names you asked for (photo.file.width, photo.file.url), but I would not recommend that, because it seems confusing (calling an attachment "file").
class Photo < ActiveRecord::Base
def file
attachment
end
end
With plain Ruby you can use Forwardable:
require 'forwardable'
class RecordCollection
attr_accessor :records
extend Forwardable
def_delegator :#records, :[], :record_number
end

Resources