I caricamenti di file sono in genere un'area difficile nello sviluppo web. In questo tutorial, impareremo come utilizzare Dragonfly, una potente gemma Ruby che rende semplice ed efficiente l'aggiunta di qualsiasi tipo di funzionalità di caricamento a un progetto Rails.
La nostra applicazione di esempio mostrerà un elenco di utenti, e per ognuno di essi, potremo caricare un avatar e memorizzarlo. Inoltre, Dragonfly ci permetterà di:
In questa lezione, seguiremo un approccio BDD [Comportamento guidato dallo sviluppo], utilizzando Cucumber e RSpec.
Avrai bisogno di avere Imagemagick installato: puoi fare riferimento a questa pagina per i binari da installare. Poiché sono basato su una piattaforma Mac, uso Homebrew, posso semplicemente digitare brew install imagemagick
.
Dovrai anche clonare un'applicazione di Rails di base che utilizzeremo come punto di partenza.
Inizieremo clonando il repository di partenza e impostando le nostre dipendenze:
git clone http: //[email protected]/cloud8421/tutorial_dragonfly_template.git cd tutorial_dragonfly_template
Questa applicazione richiede almeno Ruby 1.9.2 per essere eseguita, tuttavia, ti incoraggio ad usare 1.9.3. La versione di Rails è 3.2.1. Il progetto non include a .rvmrc
o a .rbenv
file.
Successivamente, eseguiamo:
bundle installa bundle exec rake db: setup db: test: prepara db: seed
Questo si occuperà delle dipendenze della gemma e della configurazione del database (useremo sqlite, quindi non c'è bisogno di preoccuparsi della configurazione del database).
Per verificare che tutto funzioni come previsto, possiamo eseguire:
bundle exec rspec bundle exec cetriolo
Dovresti scoprire che tutti i test sono passati. Esaminiamo l'output di Cucumber:
Caratteristica: gestione del profilo utente Come utente Per gestire i miei dati desidero accedere alla mia pagina del profilo utente Background: Dato che un utente esiste con l'email "[email protected]" Scenario: visualizzazione del mio profilo Dato che sono nella home page Quando Seguo "Profilo" per "[email protected]" Quindi dovrei essere sulla pagina del profilo per "[email protected]" Scenario: modificare il mio profilo Dato che sono nella pagina del profilo per "[email protected]" Quando Seguo "Modifica" e cambio la mia email con "[email protected]" e faccio clic su "Salva" Quindi dovrei essere sulla pagina del profilo per "[email protected]" e dovrei vedere "Utente aggiornato" 2 scenari (2 passati) 11 passaggi (11 passati) 0m0.710s
Come puoi vedere, queste funzionalità descrivono un tipico flusso di lavoro dell'utente: apriamo una pagina utente da un elenco, premiamo "Modifica" per modificare i dati dell'utente, cambiare l'email e salvare.
Ora prova a eseguire l'app:
rotaie s
Se apri http :: // localhost: 3000
nel browser, troverai un elenco di utenti (abbiamo pre-compilato il database con 40 record casuali grazie alla gemma Faker).
Per ora, ognuno degli utenti avrà un piccolo avatar 16x16px e un grande avatar segnaposto nella sua pagina del profilo. Se modifichi l'utente, sarai in grado di cambiare i suoi dettagli (nome, cognome e password), ma se provi a caricare un avatar, questo non verrà salvato.
Sentiti libero di sfogliare il codebase: l'applicazione utilizza Simple Form per generare visualizzazioni di moduli e Twitter Bootstrap per CSS e layout, poiché si integrano perfettamente e aiutano molto ad accelerare il processo di prototipazione.
Inizieremo aggiungendo un nuovo scenario a Caratteristiche / managing_profile.feature
:
... Scenario: aggiunta di un avatar Dato che sono nella pagina del profilo per "[email protected]" Quando seguo "Modifica" E carico l'avatar dei baffi E faccio clic su "Salva" Quindi dovrei essere sulla pagina del profilo per "email @ example.com "E il profilo dovrebbe mostrare" l'avatar dei baffi "
Questa funzione è abbastanza auto-esplicativa, ma richiede alcuni passaggi aggiuntivi da aggiungere Caratteristiche / step_definitions / user_steps.rb
:
... Quando / ^ carico l'avatar dei baffi $ / do attach_file 'utente [avatar_image]', Rails.root + 'spec / fixtures / mustache_avatar.jpg' fine Allora / ^ il profilo dovrebbe mostrare "([^"] *) " $ / do | image | pattern = immagine del caso quando "avatar del baffo" / mustache_avatar / end n = Nokogiri :: HTML (pagina.body) n.xpath (".// img [@ class = 'thumbnail']") .first ['src']. dovrebbe = ~ fine modello
Questo passaggio presuppone che tu abbia un'immagine, chiamata mustache_avatar.jpg
dentro spec / fixtures
. Come puoi immaginare, questo è solo un esempio; può essere tutto ciò che vuoi.
Il primo passo utilizza Capibara per trovare il utente [avatar_image]
file e carica il file. Nota che stiamo assumendo che avremo un an avatar_image
attributo sul Utente
modello.
Il secondo passo utilizza Nokogiri (una potente libreria di analisi HTML / XML) e XPath per analizzare il contenuto della pagina del profilo risultante e cercare il primo img
etichetta con a miniature
classe e prova che il src
attributo contiene mustache_avatar
.
Se corri cetriolo
ora, questo scenario attiverà un errore, in quanto non esiste un campo file con il nome specificato. Adesso è tempo di concentrarsi sul Utente
modello.
Prima di integrare Dragonfly con il Utente
modello, aggiungiamo un paio di specifiche a user_spec.rb
.
Possiamo aggiungere un nuovo blocco subito dopo attributi
contesto:
contesto "attributi avatar" fallo should respond_to (: avatar_image) it should allow_mass_assignment_of (: avatar_image) fine
Testiamo che l'utente abbia un avatar_image
attributo e, poiché aggiorneremo questo attributo tramite un modulo, deve essere accessibile (seconda specifica).
Ora possiamo installare Dragonfly: facendo ciò, otterremo queste specifiche per diventare verdi.
Aggiungiamo le seguenti linee al Gemfile:
gem 'rack-cache', richiede: 'rack / cache' gemma 'libellula', '~> 0.9.10'
Successivamente, possiamo correre installazione bundle
. Il rack-cache è necessario in fase di sviluppo, in quanto è l'opzione più semplice per avere il caching HTTP. Può essere utilizzato anche in produzione, anche se soluzioni più solide (come Varnish o Squid) sarebbero migliori.
Abbiamo anche bisogno di aggiungere l'inizializzatore Dragonfly. Creiamo il config / inizializzatori / dragonfly.rb
file e aggiungere il seguente:
richiede app 'dragonfly' = Dragonfly [: images] app.configure_with (: imagemagick) app.configure_with (: rails) app.define_macro (ActiveRecord :: Base,: image_accessor)
Questa è la configurazione di vanilla Dragonfly: imposta un'applicazione Dragonfly e la configura con il modulo necessario. Aggiunge anche una nuova macro a ActiveRecord
che potremo usare per estendere il nostro Utente
modello.
Dobbiamo aggiornare config / application.rb
, e aggiungi una nuova direttiva alla configurazione (subito prima di config.generators
bloccare):
config.middleware.insert 0, 'Rack :: Cache', verbose: true, metastore: URI.encode ("file: # Rails.root / tmp / dragonfly / cache / meta"), entitystore: URI.encode ("file: # Rails.root / tmp / dragonfly / cache / body") a meno che Rails.env.production? config.middleware.insert_after 'Rack :: Cache', 'Dragonfly :: Middleware',: images
Senza entrare nei dettagli, stiamo installando Rack :: Cache
(eccetto per la produzione, dove è abilitato di default), e impostando Dragonfly per usarlo.
Conserveremo le immagini su disco, tuttavia, abbiamo bisogno di un modo per tenere traccia dell'associazione con un utente. L'opzione più semplice è aggiungere due colonne alla tabella utente con una migrazione:
rails g migration add_avatar_to_users avatar_image_uid: string avatar_image_name: string bundle exec rake db: migrate db: test: prepara
Ancora una volta, questo è direttamente dalla documentazione di Dragonfly: abbiamo bisogno di avere un avatar_image_uid
colonna per identificare in modo univoco il file dell'avatar e a avatar_image_name
per memorizzare il nome file originale (quest'ultima colonna non è strettamente necessaria, ma abilita la generazione di URL immagine che terminano con il nome file originale).
Finalmente, possiamo aggiornare il Utente
modello:
utente di classe < ActiveRecord::Base image_accessor :avatar_image attr_accessible :email, :first_name, :last_name, :avatar_image…
Il image_accessor
il metodo è reso disponibile dall'inizializzatore Dragonfly e richiede solo un nome di attributo. Rendiamo anche lo stesso attributo accessibile nella riga sottostante.
In esecuzione RSpec
ora dovrebbe mostrare tutte le specifiche in verde.
Per testare la funzione di upload, possiamo aggiungere un contesto a users_controller_spec.rb
nel Aggiornamento PUT
bloccare:
context "immagine avatar" do let! (: image_file) fixture_file_upload ('/ mustache_avatar.jpg', 'image / jpg') contesto "caricamento di un avatar" fai prima di mettere: update, id: user.id, user: avatar_image: image_file termina "dovrebbe salvare efficacemente il record di immagini sull'utente" fai user.reload user.avatar_image_name.should = ~ / mustache_avatar / end end end
Riutilizzeremo lo stesso dispositivo e creeremo una simulazione per il caricamento con fixture_file_upload
.
Poiché questa funzionalità si basa su Dragonfly, non è necessario scrivere codice per farlo passare.
Ora dobbiamo aggiornare le nostre visualizzazioni per mostrare l'avatar. Iniziamo dalla pagina di visualizzazione degli utenti e apriamo app / views / utenti / show.html.erb
e aggiornalo con il seguente contenuto:
<% if @user.avatar_image.present? %> <%= image_tag @user.avatar_image.url, class: 'thumbnail' %> <% else %> <% end %>
<%= @user.name %>
<%= @user.email %>
<%= link_to 'Edit', edit_user_path(@user), class: "btn" %>
Successivamente, possiamo aggiornare app / views / utenti / edit.html.erb
:
<%= simple_form_for @user, multipart: true do |f| %><% end %><% if @user.avatar_image.present? %> <%= image_tag @user.avatar_image.url, class: 'thumbnail' %> <% else %> <% end %><%= f.input :avatar_image, as: :file %><%= f.input :first_name %> <%= f.input :last_name %> <%= f.input :email %><%= f.submit 'Save', class: "btn btn-primary" %>
Possiamo mostrare all'utente avatar con una semplice chiamata a @ user.avatar_image.url
. Ciò restituirà un URL a una versione non modificata dell'avatar caricato dall'utente.
Se corri cetriolo
ora vedrai la funzione verde. Sentiti libero di provarlo anche nel browser!
Ci stiamo implicitamente affidando ai CSS per ridimensionare l'immagine se è troppo grande per il suo contenitore. È un approccio traballante: il nostro utente può caricare avatar non quadrati o un'immagine molto piccola. Inoltre, stiamo sempre servendo la stessa immagine, senza troppa preoccupazione per le dimensioni della pagina o la larghezza di banda.
Dobbiamo lavorare su due aree diverse: aggiungere alcune regole di convalida al caricamento dell'avatar e specificare le dimensioni e il rapporto dell'immagine con Dragonfly.
Inizieremo aprendo il user_spec.rb
file e aggiungendo un nuovo blocco spec:
contesto "attributi avatar" do% w (avatar_image retained_avatar_image remove_avatar_image) .each do | attr | dovrebbe rispondere_to (attr.to_sym) end% w (avatar_image retained_avatar_image remove_avatar_image) .each do | attr | it should allow_mass_assignment_of (attr.to_sym) termina "dovrebbe convalidare la dimensione del file dell'avatar" do user.avatar_image = Rails.root + 'spec / fixtures / huge_size_avatar.jpg' user.should_not be_valid # la dimensione è> 100 KB fine "dovrebbe convalidare il formato dell'avatar" do user.avatar_image = Rails.root + 'spec / fixtures / dummy.txt' user.should_not be_valid end end
Stiamo testando la presenza e stiamo consentendo "l'assegnazione di massa" per gli attributi aggiuntivi che utilizzeremo per migliorare la forma utente (: retained_avatar_image
e : remove_avatar_image
).
Inoltre, stiamo anche testando che il nostro modello utente non accetterà grandi caricamenti (più di 200 KB) e file che non sono immagini. In entrambi i casi, è necessario aggiungere due file fixture (un'immagine con il nome specificato e la cui dimensione è superiore a 200 KB e un file di testo con qualsiasi contenuto).
Come al solito, l'esecuzione di queste specifiche non ci porterà al verde. Aggiorniamo il modello utente per aggiungere le regole di convalida:
... attr_accessible: email,: first_name,: last_name,: avatar_image,: retained_avatar_image,: remove_avatar_image ... validates_size_of: avatar_image, maximum: 100.kilobytes validates_property: format, of:: avatar_image, in: [: jpeg,: png,: gif, : jpg] validates_property: mime_type, di:: avatar_image, in: ['image / jpg', 'image / jpeg', 'image / png', 'image / gif'], case_sensitive: false
Queste regole sono abbastanza efficaci: si noti che, oltre a controllare il formato, controlliamo anche il tipo MIME per una maggiore sicurezza. Essendo un'immagine, permettiamo i file jpg, png e gif.
Le nostre specifiche dovrebbero passare ora, quindi è ora di aggiornare le visualizzazioni per ottimizzare il carico dell'immagine.
Per impostazione predefinita, Dragonfly utilizza ImageMagick per elaborare dinamicamente le immagini quando richiesto. Supponendo di avere un utente
esempio in una delle nostre opinioni, potremmo quindi:
user.avatar_image.thumb ('100x100'). url user.avatar_image.process (: greyscale) .url
Questi metodi creeranno una versione elaborata di questa immagine con un hash univoco e grazie al nostro livello di memorizzazione nella cache, ImageMagick verrà chiamato solo una volta per immagine. Successivamente, l'immagine verrà pubblicata direttamente dalla cache.
Puoi usare molti metodi integrati o semplicemente crearne uno tuo, la documentazione di Dragonfly ha molti esempi.
Rivisitiamo il nostro utente modificare
pagina e aggiorna il codice della vista:
... <% if @user.avatar_image.present? %> <%= image_tag @user.avatar_image.thumb('400x400#').url, class: 'thumbnail' %> <% else %>...
Faremo lo stesso per l'utente mostrare
pagina:
... <% if @user.avatar_image.present? %> <%= image_tag @user.avatar_image.thumb('400x400#').url, class: 'thumbnail' %> <% else %>...
Stiamo forzando la dimensione dell'immagine a 400 x 400 pixel. Il #
parametro indica a ImageMagick di ritagliare l'immagine mantenendo una gravità centrale. Puoi vedere che abbiamo lo stesso codice in due punti, quindi riadattiamolo in un partial chiamato viste / utenti / _avatar_image.html.erb
<% if @user.avatar_image.present? %> <%= image_tag @user.avatar_image.thumb('400x400#').url, class: 'thumbnail' %> <% else %> <% end %>
Quindi possiamo sostituire il contenuto del .miniature
contenitore con una semplice chiamata a:
<%= render 'avatar_image' %>
Possiamo fare ancora meglio spostando l'argomento di pollice
fuori dal parziale. Facciamo l'aggiornamento _avatar_image.html.erb
:
<% if user.avatar_image.present? %> <%= image_tag user.avatar_image.thumb(args).url %> <% else %> & text = Super + cool + avatar "alt =" Super cool avatar "> <% end %>
Possiamo ora chiamare il nostro parziale con due argomenti: uno per l'aspetto desiderato e uno per l'utente:
<%= render 'avatar_image', args: '400x400#', user: @user %>
Possiamo usare lo snippet qui sopra modificare
e mostrare
viste, mentre possiamo chiamarlo nel modo seguente all'interno viste / utenti / _user_table.html.erb
, dove stiamo mostrando le piccole miniature.
...<%= link_to 'Profile', user_path(user) %> <%= render 'avatar_image', args: '16x16#', user: user %> <%= user.first_name %> ...
In entrambi i casi, eseguiamo anche un Regex sull'aspetto per estrarre una stringa compatibile con il servizio placehold.it (cioè rimuovere caratteri non alfanumerici).
Dragonfly crea due attributi aggiuntivi che possiamo utilizzare in un modulo:
retained_avatar_image
: memorizza l'immagine caricata tra ricariche. Se la convalida di un altro campo modulo (ad esempio e-mail) non riesce e la pagina viene ricaricata, l'immagine caricata è ancora disponibile senza necessità di ricaricarla. Lo useremo direttamente nel modulo.remove_avatar_image
: quando è vero, l'immagine attuale dell'avatar verrà cancellata sia dal record dell'utente che dal disco.Possiamo testare la rimozione dell'avatar aggiungendo una specifica aggiuntiva a users_controller_spec.rb
, nel immagine avatar
bloccare:
... contesto "rimozione di un avatar" prima di fare user.avatar_image = Rails.root + 'spec / fixtures / mustache_avatar.jpg' user.save end it "dovrebbe rimuovere l'avatar dall'utente" do put: update, id: user. id, user: remove_avatar_image: "1" user.reload user.avatar_image_name.should be_nil end end ...
Ancora una volta, Dragonfly otterrà questa specifica per passare automaticamente come già abbiamo remove_avatar_image
attributo disponibile per l'istanza utente.
Aggiungiamo quindi un'altra funzionalità a managing_profile.feature
:
Scenario: rimozione di un avatar Dato l'utente con e-mail "[email protected]" ha l'avatar di baffi e io sono nella pagina del profilo per "[email protected]" Quando seguo "Modifica" e controllo "Rimuovi immagine avatar" E faccio clic su "Salva" Quindi dovrei essere sulla pagina del profilo per "[email protected]" E il profilo dovrebbe mostrare "l'avatar segnaposto"
Come al solito, dobbiamo aggiungere alcuni passaggi a user_steps.rb
e aggiorna uno per aggiungere un Regex per l'avatar segnaposto:
Dato / ^ l'utente con email "([^"] *) "ha l'avatar di baffi $ / do | email | u = User.find_by_email (email) u.avatar_image = Rails.root + 'spec / fixtures / mustache_avatar.jpg 'u.save end Quando / ^ I check "([^"] *) "$ / do | checkbox | check box end Quindi / ^ il profilo dovrebbe mostrare "([^"] *) "$ / do | image | pattern = immagine del caso quando" il segnaposto avatar "/placehold.it/ quando" il baffo avatar "/ mustache_avatar / fine n = Nokogiri :: HTML (page.body) n.xpath (".// img [@ class = 'thumbnail']"). first ['src']. dovrebbe = ~ fine modello
Abbiamo anche bisogno di aggiungere due campi aggiuntivi al modificare
modulo:
...<%= f.input :retained_avatar_image, as: :hidden %> <%= f.input :avatar_image, as: :file, label: false %> <%= f.input :remove_avatar_image, as: :boolean %>...
Questo farà passare la nostra funzionalità.
Per evitare di avere una funzione ampia e troppo dettagliata, possiamo testare la stessa funzionalità in una specifica di richiesta.
Creiamo un nuovo file chiamato spec / richieste / user_flow_spec.rb
e aggiungere questo contenuto ad esso:
richiedere 'spec_helper' descrivere "Flusso utente" do let! (: utente) Factory (: utente, email: "[email protected]") descrive "visualizzazione del profilo" fallo "dovrebbe mostrare il profilo per l'utente" visitare '/' page.find ('tr', text: user.email) .click_link ("Profile") current_path = URI.parse (current_url) .path current_path.should == user_path (utente) end end descrive "aggiornamento i dati del profilo "esegui" dovrebbero salvare le modifiche "do visit '/' page.find ('tr', text: user.email) .click_link (" Profilo ") click_link 'Modifica' fill_in: email, con:" new_email @ example.com "click_button" Salva "current_path.should == user_path (utente) page.should have_content" Utente aggiornato "end end descrive" gestione dell'avatar "fallo" dovrebbe salvare l'avatar caricato "do user.avatar_image = Rails.root + 'spec / fixtures / mustache_avatar.jpg' user.save visita user_path (utente) click_link 'Modifica' attach_file 'utente [avatar_image]', Rails.root + 'spec / fixtures / mustache_avatar.jpg' click_button 'Salva' current_path.should == user_path (utente) page.should have_con tent "Utente aggiornato" n = Nokogiri :: HTML (page.body) n.xpath (".// img [@ class = 'thumbnail']"). first ['src']. should = ~ / mustache_avatar / end esso "dovrebbe rimuovere l'avatar se richiesto" do user.avatar_image = Rails.root + 'spec / fixtures / mustache_avatar.jpg' utente.salva visita user_path (utente) click_link 'Modifica' seleziona "Rimuovi avatar immagine" click_button 'Salva' percorso corrente .should == user_path (utente) page.should have_content "Utente aggiornato" n = Nokogiri :: HTML (page.body) n.xpath (".// img [@ class = 'thumbnail']"). first [' src ']. dovrebbe = ~ /placehold.it/ end end end
La specifica incapsula tutti i passaggi che abbiamo usato per definire la nostra caratteristica principale. Esamina a fondo il markup e il flusso, così possiamo assicurarci che tutto funzioni correttamente a livello granulare.
Ora possiamo accorciare il managing_profile.feature
:
Caratteristica: gestione del profilo utente Come utente Per gestire i miei dati, desidero accedere alla mia pagina del profilo utente Background: Dato un utente esiste con l'email "[email protected]" Scenario: modifica il mio profilo Dato che cambio l'email con "new_mail @ example.com "per" [email protected] "Quindi dovrei vedere" Utente aggiornato "Scenario: aggiunta di un avatar Dato che ho caricato l'avatar dei baffi per" [email protected] "Quindi il profilo dovrebbe mostrare" l'avatar dei baffi " Scenario: rimozione di un avatar Dato l'utente "[email protected]" ha l'avatar di baffo e lo rimuovo Quindi l'utente "[email protected]" dovrebbe avere "l'avatar segnaposto"
aggiornato user_steps.rb
:
Dato / ^ un utente esiste con l'email "([^"] *) "$ / do | email | Factory (: utente, email: email) termina Dato / ^ l'utente con email" ([^ "] *)" ha l'avatar dei baffi $ / do | email | u = User.find_by_email (email) u.avatar_image = Rails.root + 'spec / fixtures / mustache_avatar.jpg' u.save end Then / ^ Dovrei vedere "([^"] *) "$ / do | content | page.should have_content (content) end Then / ^ il profilo dovrebbe mostrare "([^"] *) "$ / do | image | n = Nokogiri :: HTML (page.body) n.xpath (".// img [@ class = 'thumbnail']"). first ['src']. dovrebbe = ~ pattern_for (image) end Dato / ^ I cambia l'e-mail con "([^"] *) "per" ([^ "] *)" $ / do | new_email, old_email | u = User.find_by_email (old_email) visita edit_user_path (u) fill_in: email, con: new_email click_button 'Save' end Given Given / ^ Carico il mustache avatar per "([^"] *) "$ / do | email | u = User.find_by_email (email) visita edit_user_path (u) attach_file 'utente [avatar_image]', Rails.root + 'spec / fixtures / mustache_avatar.jpg' click_button 'Salva' termina Dato / ^ l'utente "([^"] * ) "ha l'avatar dei baffi e lo rimuovo $ / do | email | u = User.find_by_email (email) u.avatar_image = Rails.root + 'spec / fixtures / mustache_avatar.jpg' u.salva visita edit_user_path (u) seleziona "Rimuovi avatar image" click_button 'Salva' end Then / ^ the user " ([^ "] *)" dovrebbe avere "([^"] *) "$ / do | email, immagine | u = User.find_by_email (email) visita user_path (u) n = Nokogiri :: HTML (page.body) n.xpath (".// img [@ class = 'thumbnail']"). first ['src'] .should = ~ pattern_for (image) end def pattern_for (image_name) case image_name quando 'the placeholder avatar' /placehold.it/ quando 'the mustache avatar' / mustache_avatar / end end
Come ultimo passaggio, possiamo facilmente aggiungere il supporto S3 per archiviare i file dell'avatar. Riapri config / inizializzatori / dragonfly.rb
e aggiorna il blocco di configurazione:
Dragonfly :: App [: images] .configure do | c | c.datastore = Dragonfly :: DataStorage :: S3DataStore.new c.datastore.configure do | d | d.bucket_name = 'dragonfly_tutorial' d.access_key_id = 'some_access_key_id' d.secret_access_key = 'some_secret_access_key' end end a meno che% (sviluppo test cetriolo) .include? Rails.env
Questo funzionerà immediatamente e interesserà solo la produzione (o qualsiasi altro ambiente che non è specificato nel file). Dragonfly imposterà automaticamente l'archiviazione del file system per tutti gli altri casi.
Spero che tu abbia trovato interessante questo tutorial e che sia riuscito a raccogliere alcune informazioni interessanti.
Vi incoraggio a fare riferimento alla pagina GitHub di Dragonfly per un'ampia documentazione e altri esempi di casi d'uso - anche al di fuori di un'applicazione Rails.