On my application I've several things with attachments on them, using paperclip.
Clients have one logo.
Stores can have one or more pictures. These pictures, in addition, can have other
information such as the date in which they were taken.
Products can have one or more pictures of them, categorized (from the font, from the
back, etc).
For now, each one of my Models has its own "paperclip-fields" (Client has_attached_file) or has_many models that have attached files (Store has_many StorePictures, Product has_many ProductPictures)
My client has also told me that in the future we might be adding more attachments to the system (i.e. pdf documents for the clients to download).
My application has a rather complex authorization system implemented with declarative_authorization. One can not, for example, download pictures from a product he's not allowed to 'see'.
I'm considering re-factoring my code so I can have a generic "Attachment" model. So any model can has_many :attachments.
With this context, does it sound like a good idea? Or should I continue making Foos and FooPictures?
I've found there are often cases where a generic Attachment class is a whole lot easier to manage than independent attachments on various other types of records. The only down-side to the simple Attachment approach is that the thumbnails that need to be produced are defined for all possible attachments simultaneously instead of on a case-by-case basis.
A hybrid approach that allows for more flexibility is to create a STI-based Attachment table by including a 'type' column and making use-specific subclasses such as ProductAttachment that defines specific styles.
Related
I'm building an app similar to Dropbox's concept where file storage is the key feature. It struck me when I started planning the app as to whether I should:
Have a User Model with has_many_attached to handle all files/images related to a user
Have a UserFile Model with has_one_attached and belongs_to :user
Still a rookie here, and I guess my concern is that I'm not sure if option 1 will have more limitations as the database grow in the future and accessing, storing, viewing, updating, and deleting any files belonging to the user may not be as flexible.
Also, additional tracking on the file is required, i.e. download counter, document verified etc.
Looking at option 2, it is definitely working but it makes the flow more complex and definitely will be difficult to maintain down the road.
Thanks in advance for your input.
Have search for stackoverflow and even rails guides but there is no information that I can see that helps me on this decision. At least, perhaps i cannot understand them.
I'm wondering what the best design for the following would look like in Rails:
A website with three different image galleries, let's say one showing pictures of the company's staff under mywebsite.com/gallery, another one shows pictures of product A at mywebsite.com/productA and the third shows product B under mywebsite.com/productB.
I assume it would be bad design to create a gallery resource, along with a productA and a productB resource since there will be only one of each and it's really the new photos and not new image collections which will have to be created.
So I'm thinking if it's a good idea to create an images resource and then use single table inheritance, instantiate a gallery_images, productA_images and productB_images and change the routes to the respective ones which I want to show up as URLs. I should probably mention that I don't want to mix up the images for the different sections.
I feel there must be a "proper" way of doing this, I guess tons of websites use a similar scheme.
Not to say that this is a "Rails way", but I've seen it done as follows in most of the Rails projects I had access to. Basically, when it comes to galleries, you would want to let customer upload and manage the images. So, you would use gems like paperclip and carrierwave. Once you choose one of these gems, your class design is predefined. You'd have a model that has an attachment.
Then, you would simply initialize a specific instance of your model (or a model with has-many attachment model association) and simply render a list of image URLs in your HTML template. The actual gallery is then styled and brought to live on frontend using CSS and JS.
As for the difference between other contexts. Let's say OOP. If you are looking for the "correct" way of doing it in OOP context, you would first have to consider your business domain. And design your classes so that they reflect your business requirements (taking into account all OOP principles). A best way to start this journey would possibly be learning and understanding OOP principles and forgetting that "Circle extends Shape, Rectangle extends Shape and this is what OOP is!" kind of mentality. Design patterns are actually a good example of what you end up with if you follow OOP principles. Head First DP is a good introductory book.
User should be able to send some pictures to his chat partner (whatsapp/line style).
There might be a possibility to also add other attachments and videos. We having an argue about the model structure. We are using carrierwave to manage the files. right now, the user can only send Pictures, in future there might be some other datatypes to be added.
Solution 1
pretty standard imo, having 3 classes (each has own DB table) and each class is getting his own carrierwave-uploader.
Chat::Picture
Chat::Video
Chat::PDF
Solution 2
this might be a little bit more trickier. Instead of 3 Models we are only having a Chat::Attachment and by that we use STI to define the type.
Chat::Attachment::Picture < Chat::Attachment
Chat::Attachment::Video < Chat::Attachment
Chat::Attachment::Pdf < Chat::Attachment
here also every class is getting his own uploader.
So the Question:: is this the right spot to use STI as a design pattern or should we stick to regular rails-models?
The answer depends on how much change you expect in future. If you expect more changes in future then you should go for rails usual way. I personally dont like STI much. I think you will be better off not using STI here, rails will provide you better separation of concern here. STI should be considered when dealing with model classes that share much of the same functionality and data fields, but you as the developer may want more granular control over extending or adding to each class individually. Rather than duplicate the code over and over for multiple tables (and not being DRY) or forego the flexibility of adding idiosyncratic functionality or methods, STI permits you to use keep your data in a single table while writing specialized functionality.
I'm building a Rails app that is probably going to have a large amount of models. For now let's say that i need to have Users, and each User will have a personal Blog
I've been developing the User-related part on a separate Engine (let's call it AuthEngine) that encapsulates the User model, authentication, etc. At the time this seemed like the best approach
In parallel, my colleague was developing another engine, to deal with the blog features (let's call it BlogEngine).
Now he needs to access user data which is stored in the other engine. He achieved this by defining:
BlogEngine.user_class = "AuthEngine::User"
This way, he can easily ask for user data, even though it isn't stored in the same engine. The problem comes when we want to define relashionships between models
He can easily say that each blog post belongs to a user
has_one :user, :class_name => BlogEngine.user_class
But as far as i know he can't specify that each User has multiple posts since the User model is within the other engine
The consequence of this is that he can't do things like #user.posts, but instead has to do Post.find_all_by_user(#user)
Is there a more elegant way to deal with this?
I also considered the possibility that each engine could simply generate the models inside the app, removing the encapsulation, but since the amount of models will grow quickly, i think this will make the app more of a mess, and not as much maintanable
I think you should reopen user class inside blog_engine in order to define has_many :posts relation and it should be appropriate way.
What about having a common models gem/engine with only the relationships as a dependency for your engines? This way you would have access to all relevant relationships in every engine.
I have a rails app where users share specific kinds of photos. Currently the app requires photos to be categorized in several ways before they are valid, hence users must upload photos one at a time and categorize them in order to save them to the database.
Categorization takes some time, so I'd like to allow users to upload batches of photos and then come back and categorize them when they have time, but when photos are stored without being fully categorized I don't want them mixed in with "complete" photos.
I'd ideally like this to be a sort of "Wizard" system where users can upload a bunch of photos at once and then proceed through their personal queue and categorize each photo (to finish creating it) when they have time.
My question is: how would you approach a problem like this?
I've been thinking about using Single Table Inheritance to create two subclasses of Photo: IncompletePhoto and CompletePhoto. The IncompletePhoto would only require the image file itself, but CompletePhoto would require categorization. Users could view their own IncompletePhotos, but search results within the app would only return CompletePhotos.
Does that sound like the right approach for the problem I'm trying to solve, or is there a better way? I've never used STI before and I'm not sure whether or not it's a good idea.
I'd say that STI was created to be useful when you have different objects with some, but not all common properties, for the cases where you'll benefit from DRY in both database and models. I'm not sure if there is a way to correctly change the type of instance of such a model. Well, you can just modify the type column itself, but the Ruby class of the object will be the same, and so will be validations, unless you will re-fetch the model after saving and then run validations manually. The latter sounds like a dirty hack for me.
As a correct way, I'd suggest you to add complete column, and use validators in form of validates ..., :if => :complete.