कुछ नाम रखने के लिए कैरियरवेव, पेपरक्लिप और ड्रैगनफ्लाई जैसे कई फ़ाइल अपलोड करने वाले रत्न हैं। उन सभी की अपनी विशिष्टताएं हैं, और शायद आप इनमें से कम से कम एक रत्न का उपयोग कर चुके हैं।
आज, हालांकि, मैं एक अपेक्षाकृत नया, लेकिन बहुत अच्छा समाधान पेश करना चाहता हूं, जिसे श्राइन कहा जाता है, जिसे जेनको मारोनीक द्वारा बनाया गया है। कुछ अन्य समान रत्नों के विपरीत, इसका एक मॉड्यूलर दृष्टिकोण है, जिसका अर्थ है कि प्रत्येक सुविधा एक मॉड्यूल (या प्लगइन) के रूप में पैक की जाती है। श्राइन की शब्दावली में)। सत्यापन का समर्थन करना चाहते हैं? एक प्लगइन जोड़ें। कुछ फाइल प्रोसेसिंग करना चाहते हैं? एक प्लगइन जोड़ें! मैं वास्तव में इस दृष्टिकोण से प्यार करता हूं क्योंकि यह आपको आसानी से नियंत्रित करने की अनुमति देता है कि किस मॉडल के लिए कौन सी सुविधाएं उपलब्ध होंगी।
इस लेख में मैं आपको यह दिखाने जा रहा हूं कि कैसे:
- एक रेल एप्लिकेशन में श्राइन को एकीकृत करें
- इसे कॉन्फ़िगर करें (विश्व स्तर पर और प्रति-अपलोडर)
- फ़ाइलें अपलोड करने की क्षमता जोड़ें
- फ़ाइलों को संसाधित करें
- सत्यापन नियम जोड़ें
- अतिरिक्त मेटाडेटा स्टोर करें और Amazon S3 के साथ फ़ाइल क्लाउड स्टोरेज का उपयोग करें
इस लेख का स्रोत कोड GitHub पर उपलब्ध है।
कार्यशील डेमो यहां पाया जा सकता है।
एकीकृत मंदिर
प्रारंभ करने के लिए, डिफ़ॉल्ट परीक्षण सूट के बिना एक नया रेल एप्लिकेशन बनाएं:
rails new FileGuru -T
मैं इस डेमो के लिए रेल 5 का उपयोग करूंगा, लेकिन अधिकांश अवधारणाएं संस्करण 3 और 4 पर भी लागू होती हैं।
श्राइन रत्न को अपने जेमफाइल में डालें:
gem "shrine"
फिर दौड़ें:
bundle install
अब हमें एक मॉडल की आवश्यकता होगी जिसे मैं Photo
call कॉल करने जा रहा हूं . श्राइन फ़ाइल से संबंधित सभी जानकारी को एक विशेष टेक्स्ट कॉलम में संग्रहीत करता है, जिसके अंत में _data
. होता है प्रत्यय संबंधित माइग्रेशन बनाएं और लागू करें:
rails g model Photo title:string image_data:text rails db:migrate
ध्यान दें कि रेल के पुराने संस्करणों के लिए, बाद वाला कमांड होना चाहिए:
rake db:migrate
श्राइन के लिए कॉन्फ़िगरेशन विकल्प विश्व स्तर पर और प्रति-मॉडल दोनों सेट किए जा सकते हैं। वैश्विक सेटिंग्स, निश्चित रूप से, प्रारंभकर्ता फ़ाइल के अंदर की जाती हैं। वहां मैं आवश्यक फाइलों और प्लगइन्स को हुक करने जा रहा हूं . श्राइन में प्लगइन्स का उपयोग कार्यक्षमता के टुकड़ों को अलग-अलग मॉड्यूल में निकालने के लिए किया जाता है, जिससे आपको सभी उपलब्ध सुविधाओं का पूर्ण नियंत्रण मिलता है। उदाहरण के लिए, सत्यापन, इमेज प्रोसेसिंग, कैशिंग अटैचमेंट आदि के लिए प्लग इन हैं।
अभी के लिए, आइए दो प्लगइन्स जोड़ें:एक ActiveRecord का समर्थन करने के लिए और दूसरा लॉगिंग सेट करने के लिए। उन्हें वैश्विक स्तर पर शामिल किया जाएगा। साथ ही, फ़ाइल सिस्टम संग्रहण सेट करें:
config/initializers/shrine.rb
require "shrine" require "shrine/storage/file_system" Shrine.plugin :activerecord Shrine.plugin :logging, logger: Rails.logger Shrine.storages = { cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"), store: Shrine::Storage::FileSystem.new("public", prefix: "uploads/store"), }
लॉगर केवल आपके लिए कंसोल के अंदर कुछ डिबगिंग जानकारी आउटपुट करेगा, जिसमें कहा गया है कि किसी फ़ाइल को संसाधित करने में कितना समय लगा। यह काम आ सकता है।
2015-10-09T20:06:06.676Z #25602: STORE[cache] ImageUploader[:avatar] User[29543] 1 file (0.1s) 2015-10-09T20:06:06.854Z #25602: PROCESS[store]: ImageUploader[:avatar] User[29543] 1-3 files (0.22s) 2015-10-09T20:06:07.133Z #25602: DELETE[destroyed]: ImageUploader[:avatar] User[29543] 3 files (0.07s)
सभी अपलोड की गई फ़ाइलें सार्वजनिक/अपलोड . के अंदर संग्रहीत की जाएंगी निर्देशिका। मैं इन फ़ाइलों को Git में ट्रैक नहीं करना चाहता, इसलिए इस फ़ोल्डर को बाहर कर दें:
.gitignore
public/uploads
अब एक विशेष "अपलोडर" वर्ग बनाएं जो मॉडल-विशिष्ट सेटिंग्स को होस्ट करने जा रहा है। अभी के लिए, यह कक्षा खाली रहने वाली है:
मॉडल/image_uploader.rb
class ImageUploader < Shrine end
अंत में, इस वर्ग को Photo
. के अंदर शामिल करें मॉडल:
मॉडल/photo.rb
include ImageUploader[:image]
[:image]
एक आभासी विशेषता जोड़ता है जिसका उपयोग प्रपत्र बनाते समय किया जाएगा। उपरोक्त पंक्ति को इस प्रकार लिखा जा सकता है:
include ImageUploader.attachment(:image) # or include ImageUploader::Attachment.new(:image)शामिल करें
अच्छा! अब मॉडल श्राइन की कार्यक्षमता से लैस है, और हम अगले चरण पर आगे बढ़ सकते हैं।
नियंत्रक, दृश्य और मार्ग
इस डेमो के प्रयोजनों के लिए, हमें फ़ोटो प्रबंधित करने के लिए केवल एक नियंत्रक की आवश्यकता होगी। index
पेज रूट के रूप में काम करेगा:
pages_controller.rb
class PhotosController < ApplicationController def index @photos = Photo.all end end
दृश्य:
views/photos/index.html.erb
<h1>Photos</h1> <%= link_to 'Add Photo', new_photo_path %> <%= render @photos %>
@photos
रेंडर करने के लिए सरणी, एक आंशिक आवश्यक है:
दृश्य/फ़ोटो/_photo.html.erb
<div> <% if photo.image_data? %> <%= image_tag photo.image_url %> <% end %> <p><%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %></p> </div>
image_data?
ActiveRecord द्वारा प्रस्तुत एक विधि है जो जांचती है कि रिकॉर्ड में कोई छवि है या नहीं।
image_url
एक श्राइन विधि है जो मूल छवि का पथ लौटाती है। बेशक, इसके बजाय एक छोटा थंबनेल प्रदर्शित करना बेहतर है, लेकिन हम बाद में इस पर ध्यान देंगे।
सभी आवश्यक मार्ग जोड़ें:
config/routes.rb
resources :photos, only: [:new, :create, :index, :edit, :update] root 'photos#index'
यह बात है—आधारभूत कार्य पूरा हो गया है, और हम दिलचस्प भाग पर आगे बढ़ सकते हैं!
फ़ाइलें अपलोड करना
इस खंड में मैं आपको दिखाऊंगा कि वास्तव में फ़ाइलों को अपलोड करने के लिए कार्यक्षमता कैसे जोड़ें। नियंत्रक क्रियाएं बहुत सरल हैं:
photos_controller.rb
def new @photo = Photo.new end def create @photo = Photo.new(photo_params) if @photo.save flash[:success] = 'Photo added!' redirect_to photos_path else render 'new' end end
एकमात्र गोचा यह है कि मजबूत मापदंडों के लिए आपको image
. को अनुमति देनी होगी वर्चुअल विशेषता, न कि image_data
।
photos_controller.rb
private def photo_params params.require(:photo).permit(:title, :image) end
new
बनाएं देखें:
views/photos/new.html.erb
<h1>Add photo</h1> <%= render 'form' %>
फ़ॉर्म का आंशिक भी तुच्छ है:
दृश्य/फ़ोटो/_form.html.erb
<%= form_for @photo do |f| %> <%= render "shared/errors", object: @photo %> <%= f.label :title %> <%= f.text_field :title %> <%= f.label :image %> <%= f.file_field :image %> <%= f.submit %> <% end %>
एक बार फिर, ध्यान दें कि हम image
. का उपयोग कर रहे हैं विशेषता, न कि image_data
।
अंत में, त्रुटियों को प्रदर्शित करने के लिए एक और आंशिक जोड़ें:
विचार/साझा/_errors.html.erb
<% if object.errors.any? %> <h3>The following errors were found:</h3> <ul> <% object.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> <% end %>
बस इतना ही—आप अभी से चित्र अपलोड करना शुरू कर सकते हैं।
सत्यापन
बेशक, डेमो ऐप को पूरा करने के लिए बहुत अधिक काम करना होगा। मुख्य समस्या यह है कि उपयोगकर्ता किसी भी प्रकार की फ़ाइल को किसी भी आकार के साथ अपलोड कर सकते हैं, जो विशेष रूप से महान नहीं है। इसलिए, सत्यापन का समर्थन करने के लिए एक और प्लगइन जोड़ें:
config/inititalizers/shrine.rb
Shrine.plugin :validation_helpers
ImageUploader
के लिए सत्यापन तर्क सेट करें :
मॉडल/image_uploader.rb
Attacher.validate do validate_max_size 1.megabyte, message: "is too large (max is 1 MB)" validate_mime_type_inclusion ['image/jpg', 'image/jpeg', 'image/png'] end
मैं केवल 1 एमबी से कम की जेपीजी और पीएनजी छवियों को अपलोड करने की अनुमति दे रहा हूं। जैसा आप उचित समझें इन नियमों में बदलाव करें।
MIME प्रकार
ध्यान देने योग्य एक और महत्वपूर्ण बात यह है कि, डिफ़ॉल्ट रूप से, श्राइन सामग्री-प्रकार HTTP शीर्षलेख का उपयोग करके फ़ाइल के MIME प्रकार का निर्धारण करेगा। यह हेडर ब्राउज़र द्वारा पास किया जाता है और केवल फ़ाइल के एक्सटेंशन के आधार पर सेट किया जाता है, जो हमेशा वांछनीय नहीं होता है।
यदि आप फ़ाइल की सामग्री के आधार पर MIME प्रकार निर्धारित करना चाहते हैं, तो एक प्लग इन का उपयोग करें, जिसका नाम determine_mime_type है। मैं इसे अपलोडर वर्ग के अंदर शामिल करूंगा, क्योंकि अन्य मॉडलों को इस कार्यक्षमता की आवश्यकता नहीं हो सकती है:
मॉडल/image_uploader.rb
plugin :determine_mime_type
यह प्लगइन डिफ़ॉल्ट रूप से Linux की फ़ाइल उपयोगिता का उपयोग करने वाला है।
संलग्न छवियों को कैशिंग करना
वर्तमान में, जब कोई उपयोगकर्ता गलत डेटा के साथ एक फॉर्म भेजता है, तो फॉर्म को ऊपर दी गई त्रुटियों के साथ फिर से प्रदर्शित किया जाएगा। हालाँकि, समस्या यह है कि संलग्न छवि खो जाएगी, और उपयोगकर्ता को इसे एक बार फिर से चुनना होगा। कैश्ड_अटैचमेंट_डेटा नामक एक अन्य प्लग इन का उपयोग करके इसे ठीक करना बहुत आसान है:
मॉडल/image_uploader.rb
plugin :cached_attachment_data
अब बस अपने फ़ॉर्म में एक छिपी हुई फ़ील्ड जोड़ें।
views/photos/_form.html.erb
<%= f.hidden_field :image, value: @photo.cached_image_data %> <%= f.label :image %> <%= f.file_field :image %>
फ़ोटो संपादित करना
अब छवियों को अपलोड किया जा सकता है, लेकिन उन्हें संपादित करने का कोई तरीका नहीं है, तो चलिए इसे तुरंत ठीक करते हैं। संबंधित नियंत्रक की कार्रवाइयां कुछ हद तक तुच्छ हैं:
photos_controller.rb
def edit @photo = Photo.find(params[:id]) end def update @photo = Photo.find(params[:id]) if @photo.update_attributes(photo_params) flash[:success] = 'Photo edited!' redirect_to photos_path else render 'edit' end end
वही _form
आंशिक उपयोग किया जाएगा:
views/photos/edit.html.erb
<h1>Edit Photo</h1> <%= render 'form' %>
अच्छा है, लेकिन पर्याप्त नहीं:उपयोगकर्ता अभी भी अपलोड की गई छवि को नहीं हटा सकते हैं। इसकी अनुमति देने के लिए, हमें चाहिए—अनुमान लगाएं—क्या—एक और प्लगइन:
मॉडल/image_uploader.rb
plugin :remove_attachment
यह :remove_image
. नामक वर्चुअल विशेषता का उपयोग करता है , इसलिए इसे नियंत्रक के अंदर अनुमति दें:
photos_controller.rb
def photo_params params.require(:photo).permit(:title, :image, :remove_image) end
यदि किसी रिकॉर्ड में अटैचमेंट है तो छवि को निकालने के लिए अब बस एक चेकबॉक्स प्रदर्शित करें:
views/photos/_form.html.erb
<% if @photo.image_data? %> Remove attachment: <%= f.check_box :remove_image %> <% end %>
थंबनेल इमेज बनाना
वर्तमान में हम मूल चित्र प्रदर्शित करते हैं, जो पूर्वावलोकन के लिए सबसे अच्छा तरीका नहीं है:फ़ोटो बड़े हो सकते हैं और बहुत अधिक स्थान घेर सकते हैं। बेशक, आप बस सीएसएस width
का इस्तेमाल कर सकते हैं और height
विशेषताएँ, लेकिन यह भी एक बुरा विचार है। आप देखते हैं, भले ही शैलियों का उपयोग करके छवि को छोटा करने के लिए सेट किया गया हो, फिर भी उपयोगकर्ता को मूल फ़ाइल डाउनलोड करने की आवश्यकता होगी, जो कि बहुत बड़ी हो सकती है।
इसलिए, प्रारंभिक अपलोड के दौरान सर्वर साइड पर एक छोटी पूर्वावलोकन छवि उत्पन्न करना बहुत बेहतर है। इसमें दो प्लगइन्स और दो अतिरिक्त रत्न शामिल हैं। सबसे पहले, रत्नों को गिराएं:
gem "image_processing" gem "mini_magick", ">= 4.3.5"
Image_processing श्राइन के लेखक द्वारा बनाया गया एक विशेष रत्न है। यह छवियों में हेरफेर करने के लिए कुछ उच्च-स्तरीय सहायक विधियों को प्रस्तुत करता है। यह रत्न, बदले में, ImageMagick के लिए एक रूबी रैपर मिनी_मैजिक पर निर्भर करता है। जैसा कि आपने अनुमान लगाया है, इस डेमो को चलाने के लिए आपको अपने सिस्टम पर ImageMagick की आवश्यकता होगी।
इन नए रत्नों को स्थापित करें:
bundle install
अब प्लगइन्स को उनकी निर्भरता के साथ शामिल करें:
मॉडल/image_uploader.rb
require "image_processing/mini_magick" class ImageUploader < Shrine include ImageProcessing::MiniMagick plugin :processing plugin :versions # other code... end
प्रसंस्करण वह प्लग इन है जो हमें एक छवि में हेरफेर करने की अनुमति देता है (उदाहरण के लिए, इसे सिकोड़ना, घुमाना, दूसरे प्रारूप में बदलना, आदि)। संस्करण, बदले में, हमें विभिन्न रूपों में एक छवि रखने की अनुमति देता है। इस डेमो के लिए, दो संस्करणों को संग्रहित किया जाएगा:"मूल" और "अंगूठे" (300x300
के आकार में) )।
यहाँ एक छवि को संसाधित करने और उसके दो संस्करणों को संग्रहीत करने के लिए कोड है:
मॉडल/image_uploader.rb
class ImageUploader < Shrine process(:store) do |io, context| { original: io, thumb: resize_to_limit!(io.download, 300, 300) } end end
resize_to_limit!
image_processing मणि द्वारा प्रदान की गई एक विधि है। यह केवल एक छवि को 300x300
. तक छोटा कर देता है अगर यह बड़ा है और अगर यह छोटा है तो कुछ भी नहीं करता है। इसके अलावा, यह मूल पक्षानुपात को बनाए रखता है।
अब छवि प्रदर्शित करते समय, आपको बस या तो :original
प्रदान करना होगा या :thumb
image_url
. का तर्क विधि:
views/photos/_photo.html.erb
<div> <% if photo.image_data? %> <%= image_tag photo.image_url(:thumb) %> <% end %> <p><%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %></p> </div>
फ़ॉर्म के अंदर भी ऐसा ही किया जा सकता है:
views/photos/_form.html.erb
<% if @photo.image_data? %> <%= image_tag @photo.image_url(:thumb) %> Remove attachment: <%= f.check_box :remove_image %> <% end %>
अपलोड पूर्ण होने के बाद संसाधित फ़ाइलों को स्वचालित रूप से हटाने के लिए, आप एक प्लग-इन जोड़ सकते हैं जिसका नाम है delete_raw:
मॉडल/image_uploader.rb
plugin :delete_raw
छवि का मेटाडेटा
वास्तव में एक छवि प्रस्तुत करने के अलावा, आप इसका मेटाडेटा भी प्राप्त कर सकते हैं। आइए, उदाहरण के लिए, मूल फ़ोटो का आकार और MIME प्रकार प्रदर्शित करें:
दृश्य/फ़ोटो/_photo.html.erb
<div> <% if photo.image_data? %> <%= image_tag photo.image_url(:thumb) %> <p> Size <%= photo.image[:original].size %> bytes<br> MIME type <%= photo.image[:original].mime_type %><br> </p> <% end %> <p><%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %></p> </div>
इसके आयामों के बारे में क्या? दुर्भाग्य से, वे डिफ़ॉल्ट रूप से संग्रहीत नहीं होते हैं, लेकिन यह store_dimensions नामक प्लगइन के साथ संभव है।
छवि के आयाम
store_dimensions प्लगइन fastimage रत्न पर निर्भर करता है, इसलिए इसे अभी कनेक्ट करें:
gem 'fastimage'
दौड़ना न भूलें:
bundle install
अब बस प्लगइन शामिल करें:
मॉडल/image_uploader.rb
plugin :store_dimensions
और width
. का उपयोग करके आयाम प्रदर्शित करें और height
तरीके:
views/photos/_photo.html.erb
<div> <% if photo.image_data? %> <%= image_tag photo.image_url(:thumb) %> <p> Size <%= photo.image[:original].size %> bytes<br> MIME type <%= photo.image[:original].mime_type %><br> Dimensions <%= "#{photo.image[:original].width}x#{photo.image[:original].height}" %> </p> <% end %> <p><%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %></p> </div>
इसके अलावा, एक dimensions
है उपलब्ध विधि जो चौड़ाई और ऊंचाई वाली एक सरणी लौटाती है (उदाहरण के लिए, [500, 750]
)।
क्लाउड पर जाना
डेवलपर्स अक्सर अपलोड की गई फ़ाइलों को होस्ट करने के लिए क्लाउड सेवाओं का चयन करते हैं, और श्राइन ऐसी संभावना प्रस्तुत करता है। इस खंड में, मैं आपको दिखाऊंगा कि कैसे Amazon S3 पर फ़ाइलें अपलोड करें।
पहले चरण के रूप में, Gemfile . में दो और रत्न शामिल करें :
gem "aws-sdk", "~> 2.1" group :development do gem 'dotenv-rails' end
aws-sdk को S3 के SDK के साथ काम करने की आवश्यकता है, जबकि dotenv-rails का उपयोग विकास में पर्यावरण चर को प्रबंधित करने के लिए किया जाएगा।
bundle install
आगे बढ़ने से पहले, आपको एपीआई के माध्यम से S3 तक पहुंचने के लिए एक कुंजी जोड़ी प्राप्त करनी चाहिए। इसे प्राप्त करने के लिए, Amazon Web Services Console में साइन इन करें (या साइन अप करें) और सुरक्षा क्रेडेंशियल> उपयोगकर्ता पर नेविगेट करें . S3 पर फ़ाइलों में हेरफेर करने की अनुमति के साथ एक उपयोगकर्ता बनाएँ। यहाँ सरल नीति है जो S3 तक पूर्ण पहुँच प्रस्तुत करती है:
{ "Version": "2016-11-14", "Statement": [ { "Effect": "Allow", "Action": "s3:*", "Resource": "*" } ] }
बनाए गए उपयोगकर्ता की कुंजी जोड़ी डाउनलोड करें। वैकल्पिक रूप से, आप रूट एक्सेस कुंजियों का उपयोग कर सकते हैं, लेकिन मैं दृढ़ता से हतोत्साहित आपको ऐसा करने से रोकना क्योंकि यह बहुत असुरक्षित है।
इसके बाद, अपनी फ़ाइलों को होस्ट करने के लिए एक S3 बकेट बनाएं और अपने कॉन्फ़िगरेशन को होस्ट करने के लिए प्रोजेक्ट के रूट में एक फ़ाइल जोड़ें:
.env
S3_KEY=YOUR_KEY S3_SECRET=YOUR_SECRET S3_BUCKET=YOUR_BUCKET S3_REGION=YOUR_REGION
कभी भी उजागर न करें इस फ़ाइल को जनता के लिए, और सुनिश्चित करें कि आपने इसे Git से बाहर रखा है:
.gitignore
.env
अब श्राइन के वैश्विक विन्यास को संशोधित करें और एक नया संग्रहण पेश करें:
config/initializers/shrine.rb
require "shrine" require "shrine/storage/s3" s3_options = { access_key_id: ENV['S3_KEY'], secret_access_key: ENV['S3_SECRET'], region: ENV['S3_REGION'], bucket: ENV['S3_BUCKET'], } Shrine.storages = { cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"), store: Shrine::Storage::S3.new(prefix: "store", **s3_options), }
इतना ही! ऐप के अन्य हिस्सों में कोई बदलाव नहीं करना है, और आप तुरंत इस नए स्टोरेज का परीक्षण कर सकते हैं। यदि आपको S3 से गलत कुंजियों से संबंधित त्रुटियाँ प्राप्त हो रही हैं, तो सुनिश्चित करें कि आपने बिना किसी अनुगामी रिक्त स्थान और अदृश्य विशेष प्रतीकों के, कुंजी और रहस्य की सटीक प्रतिलिपि बनाई है।
निष्कर्ष
हम इस लेख के अंत में आ गए हैं। उम्मीद है, अब तक आप श्राइन का उपयोग करने में काफी आत्मविश्वास महसूस कर रहे हैं और इसे अपनी किसी एक परियोजना में नियोजित करने के लिए उत्सुक हैं। हमने इस रत्न की कई विशेषताओं पर चर्चा की है, लेकिन और भी बहुत कुछ है, जैसे फाइलों के साथ अतिरिक्त संदर्भ को संग्रहीत करने की क्षमता और प्रत्यक्ष अपलोड तंत्र।
इसलिए, श्राइन के दस्तावेज़ और इसकी आधिकारिक वेबसाइट ब्राउज़ करें, जो सभी उपलब्ध प्लगइन्स का अच्छी तरह से वर्णन करती है। यदि आपके पास इस रत्न के बारे में अन्य प्रश्न शेष हैं, तो उन्हें पोस्ट करने में संकोच न करें। मेरे साथ रहने के लिए मैं आपको धन्यवाद देता हूं, और मैं जल्द ही आपसे मिलूंगा!