{"id":21216033,"url":"https://github.com/krystal/attach","last_synced_at":"2025-10-06T08:32:23.212Z","repository":{"id":40008913,"uuid":"84554901","full_name":"krystal/attach","owner":"krystal","description":"Simple file uploads for Active Record models","archived":false,"fork":false,"pushed_at":"2024-11-21T13:58:08.000Z","size":88,"stargazers_count":14,"open_issues_count":3,"forks_count":0,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-01-14T20:11:32.708Z","etag":null,"topics":["activerecord","attachments","file-upload","files","images","rails","uploads"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/krystal.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"MIT-LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-10T11:51:08.000Z","updated_at":"2022-10-21T20:51:32.000Z","dependencies_parsed_at":"2022-07-27T05:02:15.505Z","dependency_job_id":null,"html_url":"https://github.com/krystal/attach","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krystal%2Fattach","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krystal%2Fattach/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krystal%2Fattach/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krystal%2Fattach/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/krystal","download_url":"https://codeload.github.com/krystal/attach/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":235515426,"owners_count":19002481,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["activerecord","attachments","file-upload","files","images","rails","uploads"],"created_at":"2024-11-20T21:49:19.571Z","updated_at":"2025-10-06T08:32:17.856Z","avatar_url":"https://github.com/krystal.png","language":"Ruby","readme":"# Attach\n\nAttach allow you to attach files/images/documents to Active Record models with ease. Just define which attachments you wish to add and you can easily upload them to your server (either file system or database).\n\n## Installation\n\nIn order to get started, add the gem to your Gemfile:\n\n```ruby\ngem 'attach', '~\u003e 2.0'\n```\n\nOnce included, add the database table which will store your attachments.\n\n```\n$ rake attach:install:migrations\n$ rake db:migrate\n```\n\n## Getting started\n\nYou can define the attachments which you wish to use on any of your models as such:\n\n```ruby\nclass Person \u003c ActiveRecord::Base\n  attachment :cover_photo\n  attachment :profile_picture\nend\n```\n\n## Uploading attachments\n\nYou'll have a reader and a writer for the attachment that you've created which allows you to set the file to be uploaded. For example:\n\n```ruby\nperson = Person.new\n\n# Set the photo from some data you have\nperson.cover_photo = some_binary_data\nperson.cover_photo.file_name = \"cover-photo.jpg\"\nperson.cover_photo.file_type = \"image/jpg\"\n\n# Or you can pass in an ActionDispatch::Http::UploadedFile\nperson.cover_photo = params[:person][:cover_photo]\n\n# You can also pass a pre-constructed `Attach::File` object\nfile = Attach::File.new(binary)\nfile.name = \"some-name.pdf\"\nfile.type = \"application/pdf\"\nperson.cover_photo = file\n```\n\nIt's worth noting that calling your reader will always return an `Attach::Attachment` object regardless of what you pass in. If you pass in an uploaded file it will be converted to the `Attach::Attachment` object at the point it is set.\n\n\n### Uploading from a form\n\n```erb\n\u003c% form_for @person, :html =\u003e {:multipart =\u003e true} do |f| %\u003e\n  \u003c%= f.file_field :profile_picture %\u003e\n  \u003c%= f.file_field :cover_photo %\u003e\n  \u003c%= f.submit \"Upload Attachments\" %\u003e\n\u003c% end %\u003e\n```\n\n## Accessing attachments\n\nYou can access any of your attachments easily through the methods as shown below.\n\n```ruby\n# Accessing attachments\nperson = Person.find(person.id)\nperson.cover_photo              #=\u003e Attach::Attachment\nperson.cover_photo.url          #=\u003e \"/attachment/145d17ed-d5e3-4b55-8c89-ecad9521ad73/snom-mm2.jpg\"\nperson.cover_photo.file_name    #=\u003e \"snom-mm2.jpg\"\nperson.cover_photo.digest       #=\u003e \"c4de7fd75a7e2ec37bde3a5ef9fa53a1ce9228c0\"\nperson.cover_photo.blob.read    #=\u003e \u003cBinary data\u003e\n```\n\nTo download the stored asset, you can use the value of the `url`. Attach has a middleware that will render these files for you automatically. By default, the middleware will serve all attachments as long as the user has the UUID of the attachment. If you wish to disable the serving of certain attachments (i.e. secure files that should be authenticated first), you should set the `serve` option to false.\n\n```ruby\nattachment :passport_scan, :serve =\u003e false\n```\n\n### Preloading attachments\n\nIf you're obtaining an array of objects and wish to have attachment information ready to go, you can include it as follows:\n\n```ruby\n# This will include the details about the attachment (not including the binary)\npeople = Person.includes_attachments(:cover_photo)\n```\n\n## Deleting images\n\nIf you wish to remove an image you can simply call `destroy`. If you want to do this from a form, you can add a checkbox with the name `{name}_delete`.\n\n## Backends\n\nYou can choose between storing your images in your database or on the file system. The method you choose will depend on your environment and usage requirements. By default, files are stored in the database.\n\nTo use the file system, just use the following:\n\n```ruby\n# Stores files in an 'attachments' directory in the root of your app\nAttach.use_filesystem!\n# Stores files in the directory you specify\nAttach.use_filesystem!(:root =\u003e 'path/to/root/dir')\n```\n\nYou can also write your own backends. Check out the abstract backend for instructions. Once you've written one, just set it as the backend.\n\n```ruby\nAttach.backend = MyApp::MyCleverAttachBackend.new\n```\n\n## Caching \u0026 Disposition\n\nWhen you serve assets out through the included middleware, by default they will be served with a `private, max-age=\u003c1 year\u003e` cache control header. This can be changed to suit the needs of each type of attachment.\n\n```ruby\nattachment :cover_photo, :cache_type =\u003e 'public', :cache_max_age =\u003e 5.days\n```\n\nThe disposition of a file served by the middleware will be `attachment` by default. You can change this:\n\n```ruby\nattachment :cover_photo, :disposition =\u003e 'inline'\n```\n\n## Validation\n\nTo validate an image before persisting it to your backend you can include a validation block.\n\n```ruby\nattachment :image do\n  validator do |attachment, errors|\n    unless Lizard::Image.is_image?(attachment.blob.read)\n      errors \u003c\u003c \"must be an image\"\n    end\n  end\nend\n```\n\n## Custom Data\n\nAttachments have a `custom` attribute which allows you to store data with an attachment. You might use this to store the width/height of an image in a processor.\n\n```ruby\nattachment :image do\n  processor do |attachment|\n    image = Lizard::Image.new(attachment.blob.read)\n    attachment.custom['width'] = image.width\n    attachment.custom['height'] = image.height\n  end\nend\n```\n\n## Children\n\nAttachments can have child attachments which are associated with the first one. This is useful if you're uploading images and wish to generate different thumbnails for it automatically. It works like this:\n\n### Creating children\n\nThe easiest place to create children is in the processing block for an attachment. You should call the `add_child` method with the role for the new item. This should be unique across all children in the parent image. If you upload a new child with the same name later, the original will be removed.\n\n```ruby\nattachment :cover_photo do\n  processor do |attachment|\n    image = Lizard::Image.new(attachment.blob.read)\n    attachment.add_child(:thumb500) do |c|\n      c.blob = Attach::BlobTypes::Raw.new(image.resize(500, 500).data)\n      c.file_name = \"thumb500x500.jpg\"\n    end\n  end\nend\n```\n\n### Accessing children\n\nIf you have a single object you wish to find a child for, the easiest way is like such...\n\n```ruby\npost = Post.find(31)\npost.cover_photo                    # =\u003e The original attachment\npost.cover_photo.child(:thumb500)   # =\u003e The child attachment\n```\n\nIf you're loading multiple objects though you may wish to preload the images that you desire in a single query rather than looking up each one in turn.\n\n```ruby\nPost.includes_attachments(:cover_photo =\u003e [:thumb500]).each do |post|\n  post.cover_photo.child(:thumb500) # =\u003e No additional database queries\nend\n```\n\n## CDNs\n\nIf you have an origin pull CDN and would like the `url` attribute for your attachments to include the appropriate CDN host, you can set it.\n\n```ruby\nAttach.asset_host = \"https://cdn.exampleapp.com\"\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrystal%2Fattach","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkrystal%2Fattach","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrystal%2Fattach/lists"}