 Okay, so today's talk actually is intro to Shrine. So Shrine is this Ruby Toolkit for basically dealing with file uploads. So I'm not sure like how many other toolkits are out there but this one I had to do a side project to basically build a file upload feature and then before knowing about Shrine, all I know was active storage and then mostly people are just talking about using active storage to deal with file uploads. So then with the conversation there are people who brought up Shrine, Shrine RB also and some people already said by it so I thought that give you a go. So today's talk basically is a very basic intro I would say. It's a summary of everything that I've learned through the Shrine Docs and trying to build my basic feature. So the official Docs is actually quite comprehensive but the problem is I found it very confusing when I was trying to read it to build my feature because I guess the Docs are not every part of the Docs is updated to the latest one. So sometimes they have to filter which are the newer way and which are the older way of doing things but although the basics are all still around this, feel the same. So the basics is what I'm going to go through today. If you know the basics, I think it's very easy to reason about Shrine ready then from there now you can build however kind of complicated UX1 with regards to file upload. So the examples in this talk are mostly for general usage and also a bit of Ruby on Rails example. But because I will go through some basics and then it's okay for general usage. So I think that whatever framework you're using, whatever Ruby application that you're using, it should be okay. It should be relatable to a large extent. So on top of foundation of Shrine today, I will not cover how to configure your Amazon bucket, S3 bucket or Google Cloud Storage bucket. And of course there are many-many file hosting, storage hosting services out there. So this is also one of the things that I have to figure out. I spend quite a lot of time figuring out how to configure like 8VL or this stuff. But then I will leave it out for the talk today. And then I will also not cover complicated technical decisions required to make very seamless file upload UX. So file upload can become very complicated and if you talk to the most senior engineers or people who have deal with file upload, they will tell you like, oh, you should do this, you should do that. A lot of things you need to consider. So those are the things I'm not going to go through today. As I've heard some, but because I also have not really dive into it so I don't think I'm in a good place to talk about them. So let's start with Shrine. Just now I say it's the file attachment toolkit for Ruby application. And then when it comes to what is file attachment, you might think that it's just like file upload, maybe a file input, form input. Then you just upload the file from the browser and then where does it go after that? So this is where Shrine comes in. So Shrine makes it very easy to integrate with Amazon S3 or Google Cloud Storage or even just with your file system. It just gives the, the toolkit makes standardize everything how you interact and then you also make sure that your file go to the storage service that you need it to go to. Very simply, if you set it up well. So let's just stick into the technical stuff or how to use it. And now I've explained things, there's different concepts along the way. So setting up Shrine, actually quite straightforward. So of course, you install the gem. If you use gem file, use bundler, you can just include gem Shrine. If you're not using like bundler, maybe you might just write own ruby script. Then you can just gem install Shrine on your machine. Then your script can just require Shrine. So in this example, because it's Rails, so naturally start from initializer. But again, it can be any ruby script if you can require Shrine from wherever it is. So the most basic thing that needs to be configured for Shrine after you require is actually configuring the storage. So in this, in here, we can see that it's actually Shrine.storage in a hash. So there's two keys, hash and store. And both of them are assigned a file system. So basically, here what we're saying what is configured here is that when you upload a file, Shrine will save it really write it out into a file on the machine. Wherever it's hosting. So this is just an example. Here we have cash and store. So for, so then, what are the different storage systems that Shrine provide? Just now, I showed you file system, right? So file system is also one of the the the storage systems that came out of the box for Shrine. So there are three main ones come out of the box from Shrine, which is S3. So of course, S3 is used for Amazon S3, but it also can be used for compatible services like mean IO, digital ocean spaces. So basically, it's compatible because the API supports the API, the shape of the API is the same. So then, that's why it's compatible. Then the other, the other two is file system and memory. So then people who are not familiar with hosting in general, of course, if you use third party file hosting services, storage hosting services, then it's mostly for production. Like you only use production, like you start pay money and get a production file hosting system for your production app. For then, if you use the file system, then mostly it should be using for development and test environment. Technically, yes, you can use it for production, but you must know what you're doing, right? Because if you use, like if you deploy your app using Kubernetes, right? You use Docker, maybe, then then you might have many different pods and then the request routes to many different pods and the file that is written is only, it will write to a single pod only, right? Terus, then then suddenly your file cannot be found. The user cannot get their file. So, or even you use Heroku, if you deploy a Heroku app, you use file system and then the file gets saved in the Heroku instance, then when you deploy again, like Heroku wipes everything and redeploy your app, right? Then the file gone again, right? So it's technically possible to use file system for production, but you must know what you're doing. If you have your own server like really like hardware server then maybe it's okay. So, yeah, this file system and for memory, of course, their memory is for development and test environment. So, these are the three main one. So, they are also third party like storage system that people built for, built for shrine because later we'll use texture and stuff. So, then there are Google Cloud Storage actually, there are many, many people built for many different file hosting services. So, this is like one good thing because shrine is quite mature already and then what's our time if you want to use some storage system, right? There should be a bug in it already. Even if there isn't building one, we'll just be going to the E6-4 just now. So, here you can see that if you want to use the S3 one, of course you need to require so the AWS SDK. So, here you can see that after you install the AWS SDK machine or project, you just need require build-in. It comes with shrine already. And then here you can see that cache and store uses shrine storage S3 and then some configuration. So, so more info on the configuration can be found on the website but basically basic usage is like that. And then for file system, file system because it's file system, right? You don't need to require anything special. It mostly uses Ruby's basic out of the box like file IO kind of libraries to deal with your file upload and all the stuff. So, here you just use file system and then the last one that is built out of the box is memory. So, here you just see memory.new, right? So, behind the scenes what it does is it creates a hash and then everything is done into the hash. Of course, when the memory deleted you get garbage collector all this, right? Then it just all be gone also. So, this is memory and the last file system storage system that I have here is Google Cloud Storage. I think I'm doing showing Google Cloud Storage because I use Google Cloud Storage for my site project. So, then might as well just put in the code which I've already figured out how to do it. So, here because Google Cloud Storage is the third party one so you can see the jam name is called Shrine-Google Cloud Storage. So, you also need the Google Cloud Storage SDK and then here you need to include the Shrine Storage Google Cloud Storage files from the body plug-in and also to require the Google Cloud Storage SDK. So, this is how Google Cloud Storage is configured so basically you need to configure first and then after that you use the Google Cloud Storage Shrine Storage, right? It basically picks up the Google Cloud Storage configuration. So, this is how roughly how you configure your project if you want to use Google Cloud Storage. So, so just now was just talking about storage system and now if after you have configured our storage system already so, you don't need to care about where where files are going anymore. Basically you configure that my backend for cache will be maybe file system and then for for permanent storage is S3 and then all you need to do now is you just work with Shrine. You don't need to deal with all the other all the SDKs anymore. So, in Shrine the call object is this Shrine Attachor. So, this Shrine Attachor object is actually the class that you use to upload, download, promote your file from bucket to another bucket or you fetch metadata get the URL of the file because if your URL is like because if you upload your file to S3 then of course the file can be publicly accessible then you might want to get the URL if your user and then they can download the file so it's also possible to get URL so so this Shrine Attachor is really the the class for you to to to do your to do the wonders to to upload, download everything you need is this class okay so in Shrine it always assumes to default bucket do default storage which is which is cache and store of course you can give it a different name so in this example in the last line you can in the last two lines if you use cache and store you just do Shrine.attachor.new it automatically reads cache and store but if you choose to use another key to represent your cache and store storage Shrine storage then you must pass in tell the attachor class that use which storage for cache and use which storage for store so so but but back to the basics it will always assume to do storage system one is for cache one is for store then then for in terms of file upload mostly temporary storage will be will be where like the file might be like uploaded but then user might not have decided that this is the file they want right so for example maybe you have you have a your user experience you have a a form and then maybe you the user needs to go through like three forms then they submit and then finalize the whole whole submission completion of their like maybe they sign up for something right they might upload the file in step one and then they go to step two and then go to step three that finalize right but then the user have not like really submit at a point if the user drop off then the file will just throw away right you don't need because the user haven't finished like signing up so then even if you save the file the file is useless you you might just like want to throw away so so you file in cash and then when during re-renders of the in step two and step three the user while while in that session while doing step two and step three he still knows like what what is the file that he or she have uploaded and then at a point when they finally first step they finally submit the form then you promote the file into permanent storage and then you can assign it to that user maybe for example in inside the database so I guess like most of the time like having cash install is having a cash storage and a permanent storage is useful in terms of like to save cost and delete and clear unwanted files whenever you can okay so shine attached is the object sorry I totally went through this already so so how to upload file to to cash so after you created create an attached right a new attached class actually it's very easy to upload to cash basically you just call assign dot assign and then you pass in the file so this file can be any class any object that matches the ruby IO class shape the behavior of the ruby IO class so like like maybe just a normal ruby basic ruby file class or a temp file or shrink IO or for rails you'll be action dispatch HTTP upload the file and then there's a shine rec file etc etc so so here you can see you call attached dot assign then you pass in the file and then you can read the file like that was attached from from the attached so attached dot file and you can see that here the storage is actually cash so it you call assign you will upload it to the cash to the to the cash storage so then like what if you don't want to upload into the cash storage you don't have like a form re-display kind of like flow you immediately want it to go into permanent storage then you can just call instead of calling assign you call attached so you call attached and you pass in the file and then it it upload it to the permanent storage which is the the the key store the store like shine storage okay so then if you have like a flow like kind if you have a flow where you want it to be being cash and then automatically doing to store right maybe you can maybe like a very simple most basic way it's like okay first you upload it to cash then you attach the file and then you grab the file upload into attached then you call attach and then upload into permanent storage but actually that's the easier way that shine that you do is is this method called finalize so in this example you can see that first attach dot assign to upload it to the cash storage then we assign like previous file as a variable that points to the cash file then you call attach dot finalize from the cash storage to the permanent storage so so like be if if your cash storage file system your permanent storage is AWS or your cash storage AWS your permanent storage Google Cloud you don't really need to care shine will do it offer you so you just call finalize you move from one bucket into another bucket wherever the bucket is so one here is that when you call when you check previous file it actually deletes the file from the cash storage so this if you have a lot of file uploads every other day then this can release save you a lot of money because it automatically cleans up the old file that actually if it's in permanent storage you've got no use for it in the cash storage anymore so this is like one very easy way to promote and save some money basically so if you have the file in permanent storage then how do you delete it so like maybe the user the one that file anymore maybe it's like a photo album upload then upload the photo and then they want to delete the photo already so here you can do you can call destroy attached so basically you upload the file already sorry I missed like one line to show that okay upload the file and then you get the file then you call destroy attached so so here this destroy attached is like like a safer method so basically you only delete it if the file is in permanent storage so I thought I have to use this yet but from the official guide it just says that maybe maybe you still need the temporary one for background job now you might have a background job that is running and then you don't want to delete it if it's still in cache so this is like a safer one but you if you died I want to just destroy the file just delete it from your S3 or your Google cart storage right then you can immediately just call destroy it will it doesn't have like this check if it's permanent storage yep so so this is like how you can easily delete a file so the next thing is then now just now we talk about okay we have this attached class that that simplifies your interaction with with whatever storage system in the back end whether you attach you you upload download read meta data etc right so so what we are dealing with right is this this class got uploaded file so so you call attached file you will get this this object the class is trying uploaded file and then you can on this file you can check if it's attached or not if if if it's in the cache storage if it's in a permanent storage it lets you pull the URL so just now it's an example maybe in cache storage but then you still want to show the user in the first step what they uploaded and then before they confirm submit submit the form so then you then you can get a URL here and so but then there's a cat share is that if you have a URL then then if you render to HTML then you must make sure that your URL is reachable by from the user's browser so so maybe like if you use file system you will probably get the file inside the public folder or if you have a third party storage system service then you must make sure that the the the the files inside the bucket is reachable by the general internet right so so that's like getting the URL then you can also read metadata of the file for example the file name file size what kind of mind type the file has maybe it's a PDF file or or a JPEG file PNG file et cetera so this is so this is the artifact that we using Shrine you always deal with okay so so the next thing is plugin so just now I've covered a lot I've covered like configuring your storage how to upload download files and how to how to how to like what is the object that you always deal with right so so those are probably like the very very basic if you run write a rule ruby script you probably can do your file upload very easily already okay so so so actually Shrine has also a plugin system and the the what the plugin system actually it tries to split things into into modules right and here we call it Shrine call it which you can enable certain feature if you only want want to use it so you might not want to like require load the entire Shrine library into memory like ruby will always we have to load it into memory before you can use it so you might not want to load everything you need want to load part of it in so this like one one reason like one good good example of plugins so I'm going to go through two basic plugins that most people I think most people will use and then and then there is a whole bunch of other plugins that come out of the box with Shrine also in the Shrine official but there are also many third party once that you can check out so basically there are a lot of plugins probably whatever you need to do there is a good chance that there's already a plugin to do it so the the first one is actually the active record so just now just now whatever we seen it just feels like the just like ruby law plain ruby right but then in active record there is a there is this plugin that hooks into the active record like callbacks and stuff and make things like more like real see the way you do things just very the very real way of doing things so so for the for active record in the initialiser after you set up your storage basically what you need to do is you need to write this line called Shrine.plugin and active record because it is out of the box with Shrine so you don't need to really require anything basically you you call this method it will set up a bunch of like stuff and then hook into the active record lifecycle okay so so there are there are some things to know to hook into active record is that basically you need a new column a new DB column and here it's called I call it image data in the photos table and then I have a photo class with photo model application record and then here you must call include Shrine attachment and then you pass in a key so you realise that here the key image and then therefore it will infer the data in the column called image data so this is a convention that you just follow and Shrine attachment is just a module that provides a lot of helper method that to use but so but the call is still attached attached class the storage class and the Shrine uploaded file object okay so so after you include all this all the setup is done ready you realise that everything is still like just the rails way of doing things so here maybe there is a photo form and then an image form view file input view and then you upload the file form you submit the form and then it goes to the controller right and then if you require the file itself and then you pass it into your photos.new and then you just stop save and then the file will be save into permanent storage okay so if you do photos.new and then you pass in the file itself it goes to cache storage and then you stop save then you will be promoted to permanent storage and then if you destroy the photo here when we call photo.destroy it actually destroys the photo that entire DB record in the photos table but you also destroy the photo that is uploaded to the S3 so here it's like pretty straightforward it's just like the rails way of doing create a simple crack okay so so the next one is actually this plugin called Derivatives and if you do if far upload a lot of time you will need like most likely you will create thumbnails so you you have like one huge file and then you want to generate a small medium big and then small you will then there will be places where you use the small photo there will be places you use the medium and the big photo optimising like the performance of your website so there is also a plugin called Derivatives and actually you use this jam called image processing which based on image magic which is what people mostly use to do your to do magic on your images like shrink and all the stuff so again you do trying to plug in Derivatives which there will like will load all the the code for the plugin and then here is slightly a little bit more like complicated I would say just need to do something more so basically here this example is you create a new class image uploader that that inherit like shrink and then then you do the configuration of that Derivatives inside that class so so what the pros of this of doing this thing is that maybe you don't want to create a Derivatives from every single like file upload in in the entire Ruby application because Derivatives like a very photo specific like it might not work it definitely will not work on like a PDF file or something so then then you so here we create a new class image uploader and you put shrink and then we configure the Derivatives inside that only for the image uploader so so the so the the usage is still the same so just now we do shrink attachment.new now we just do image uploader attachment.new because image uploader is also a shrink shrink object shrink yeah it also inherit from shrink so then it in itself also has an attachment that you can reference from inside and then you just do attached again and then everything the rest is still the same for as with the active record the examples in active record plug-in and then where you call dot safe like basically you save the file it will automatically generate the all the three sizes for you basically however many sites you declare just now lah so so in this example if you do photo.image you return to the original file but if you do photo.image then you put pass in the key which you declare just now here is large and then you get like the large file basically the derivative file and then you again you can get URL size at one time so just now I declare small, medium, large so in doter there will be four files that gets uploaded to your storage the original one the small, the large and the small, medium and large so you then here you can use whichever you you find more suitable for yourself for your use case yup so there are actually a lot of extensions, a lot of default plug-ins that you can use already in shrine that people have built for shrine so definitely if you need to do file load go and check it out if you have some problem go and check out the plug-in maybe that's already a solution for you so so what's next like okay today all we covered is like the call pieces of shrine how to use shrine and all but after knowing all these basics basically you can think about then what is your what is your UX for your file upload and then really just build it out build out your the the seamless file load experience you have for your application so like for example these days if you you see file upload UX online it's mostly like maybe you will use the drag and drop and then you will have some javascript that uploads it to the file storage first uploads it to to your your storage system first it doesn't even have a form upload to your Ruby on Rails app the client-side already generated a signed URL and then upload it javascript inside so all these fancy stuff that's like the the next step to file upload so so which is really up to whatever UX that you want so yep there is the end of the talk today end of the intro to to this shrine RB so I hope that now if you go to read the docs everything will make a lot of sense and then you can get started very yep that's it any questions okay that's only okay and that's it just thinking right let's say you want to temporary store 2 different files so do you have to create you have to initialise 2 separate shrine object is it you mean 2 separate shrine attacher right yes you need to initiate 2 shrine attacher if you only talking about let's put aside all the active record stuff then you you need to do this 2 different attacher and each attacher attach to 1 file if you do attach file and then you do attach again right I've not tried it but I believe you will overwrite the previous one so if you need 2 files you need 2 attacher put in the way the machine at any time kind of so let's say if I attach a file and then I don't do anything about it it will stay in the it will stay in the like memory forever or how does that work no so so you do you create an attacher already and then you call attach right depending on what what you configure your storage to be if you configure your storage to be S3 the file immediately goes to S3 then then because this reference is only in memory if you don't save this reference in your DB or whatever and then you lose it then you don't have a reference to the object in the S3 already so so then like the most basic use case for example you have a profile like you have a table called like user right and then user have like a profile picture then you probably want to save that reference basically the shrine upload the file like reference into the database and then you it will be a hash then after that if you load it out you can load it back into the shrine upload the file you will be able to fetch the file from the same storage back end again so you you have to save the reference before you throw it away if not then your your storage like your S3 will have a lot of file and then from your code you can never delete it you have to manually go to S3 interface to delete it but if I use a sign as I go to S3 no a sign goes to the a sign goes to the cache storage right but a cache storage can be in memory can can be S3 also can be Google Cloud Storage it can be anywhere yeah so so what I did was that I have what I did was that in my production add I have to storage both Google Cloud Storage but then for my Google Cloud that bucket for the cache right I set a rule to wipe it to delete the file every 24 hours so I don't need to worry like the cache bucket becomes like bloated up but then it's also but it's still it's still important to recognize that your cache storage is also production storage somewhere just use it use for a different purpose okay and good okay and I think we can stop here okay