image_path always uses assets/ when I want just public/images - ruby-on-rails

Why does image_path insist on appending assets/ to the front of image paths that are stored in public/images?
I'm building a photographer's website, so naturally we want photos to be in it, including a random sub-selection of some photos to be shown as part of the layout. Further, I've built a super-simple gallery that just displays all photos. Those image files are stored in:
public/images/gallery
I've gotten an ImageMagick gem to shrink them to the desired size. Those small files are stored in:
public/images/gallery/sm/
The shrinking is done on page load as part of rendering the layout ERB. I know it might initially sound awful, but it only shrinks an image once, and the lazy-shrinking means we don't have to restart the server to add new photos (which he'd want to do).
My reading suggests that the asset pipeline is for static layout stuff, but this is more dynamic than that. I'm led to believe that public/images is where this stuff should go, especially since production-mode Rails complains that the generated thumbnails are not compiled.
Enter the problem: I place those images in the paths shown above, and image_path gives back what looks like an asset pipeline path. It doesn't even work when I flatten the subdirectories and have everything live in public/images.
My workaround is to build the tag manually. So is image_path (and by extension, image_tag) only for asset pipeline stuff? Am I supposed to construct tags from strings for public images? I've also found mentions that image_path is supposed to look in public for a matching file first, but I've also seen documentation (http://api.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html) claiming that it won't verify the existence of something it returns a path for.

Edit: error in original answer
Rails asset pipeline is for serving assets that are present when the application starts. For serving dynamic assets as you suggest there's this answer to a similar question: Rails 3.1 assets not recognizing new images uploaded by rmagick until server restart which answers far better than my answer just did.
Having searched around to improve on my first answer, I think the best solution to the image_tag creation then is to create your own image_tag-like helper. Perhaps:
def public_image_path(filename)
[your path to that file]
end
def public_image_tag(filename, options={})
image_tag(public_image_path(filename), options)
end

Related

Specific Image Not Loading After Rails 7 ESBuilt Update

I have a very frustrating issue with a Rails 7 app after migrating to Ruby 3.2 with Esbuild.
Basically there is a few specific images that simply will not load, however, there are many others that load just fine that live in the same location, and are accessed the exact same way. It's driving me nuts.
I have cleared cached, restarted servers, cleared all the local build files, everything I can think of. This is also happening in both dev and production.
My Esbuild is running just fine, it is finding the files and compiling them with a finger print. The files all exist and are in the right location. (all sitting under app/assets/builds)
Accessing the file direcly in the browser, ie
http://localhost:4000/assets/logo_white_trans-QEBURZJB.png
Fails with a 404, cannot find the image. This file however exists with the correct name in the app/assets/builds folder.
Accessing another image from the page ie
http://localhost:4000/assets/leadstory-symbol-B5T7OIJB.png
Loads just fine.
It's almost like there is a static list of rails routes that match the images and it is not generating the route for some of these specific images, hence the 404, even though the file exists.
Some screenshots that highlight the odd behaviour
and the files listed in the directory, showing the file clearly exists
My package.json build step is
esbuild app/javascript/bundles/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=/assets --minify --log-limit=0 --loader:.js=jsx --loader:.png=file --loader:.svg=file
And a snip from the app of how its being loaded.
import LogoWhiteTrans from "../../assets/images/logo_white_trans.png";
<img src={LogoWhiteTrans} className="logo" alt="logo" />
Which looks to be working fine, the HTML outputs
<img src="/assets/logo_white_trans-QEBURZJB.png" class="logo" alt="logo">
The image can be loaded fine, from elsewhere in the app in a regular rails view using asset helpers (not from within the React app)
ie <%= asset_path('logo_white_trans.png') %>
Something I have noticed is in the logs, I see
ActionController::RoutingError (No route matches [GET] "/logo_white_trans-QEBURZJB.png"):
Notice there it does not say "/assets/logo_white_trans..."? I thought that was weird, as the URL in the image tag clearly has a /assets at the start. Trying either path does not work, with or without /assets directly in the browser. Just seems odd rails would see it that way
Im going nuts here, what am I missing. Its not a png specific issue, as other pngs are loading fine in the same way, nor is it an image issue the file exists and the naming is fine.
Is there some sort of manifest thats not being updated? An internal asset route list or something along those lines?
Im running Rails 7
Ruby 3.2
ESBuild
This isn't really an answer, but what I have ended up doing is moving all image assets out of the asset pipeline and into the public folder. I noticed that my assets were being duplicated by esbuilt and the rails asset precompile process, and basically the javascript build and rails eco system just do not work well together.
For anyone else having issues like this, we've just moved all our static assets in the public/images folder and we refernce the path /images/blah.png the same way in both React and Ruby now.
All image tags in either React or standard .erb views are just <img src="/images/blah.png/>. Its a lot cleaner.
Yes, we have given up asset finger printing, but its a small loss, considering most images never change and It's dramatically simplified things and sped up our build process considerably as it does not have to touch each file during precompilation.
Our views now also just have standard tags, instead of <asset_path> tags, which im sure is just quicker in general instead of ruby generating these asset strings all the time.
So, not really and answer to the initial question but it is a solution, and one i think anyone who is fusing modern javascript, react, typescript etc into a Rails app.

Serving dynamically generated images in Rails 6.1

My application dynamically generates images using the gruff gem and then serves these images. The images are stored in app/assets/images/gruff. The images are altered during runtime (keeping the same filename but altering their contents), since these images contain a bar graph containing information for the user that changes over time. The images are generated if they are missing, and there is no requirement for them to exist for long periods of time. The images are served using image_tag.
The issue is that there are 2 types of intermittent problems that occur. One is that the image link generated will sometimes have the wrong fingerprint, even though the images will have just been updated before the image_tag is rendered. (Thus 404'ing.)
The second issue is that even though I can verify the image exists on the server, I still occasionally get a ActionView::Template::Error (The asset "gruff/image-1.png" is not present in the asset pipeline.):
If this should be working, how can I further dig into understanding what the issue is?
If the asset pipeline is simply not a good mechanism for what I'm trying to do, what would the community suggest instead?
A better idea for your problem is save this generated image with activestorage, so you should create a model like that:
class DynamicImages < ApplicationRecord
has_one_attached :image
end
And create a record when use gruff to generated this image.

Rails - how do you edit files included by a gem in the asset pipeline?

I am new to rails so this is probably a simple question about using the asset pipeline.
In my app, I want to use this jquery plugin: http://www.fyneworks.com/jquery/star-rating/
So to do it, I included the following gem in my gemfile: https://github.com/RichGuk/jquery-star-rating-rails
However, I find that the image used for the star ratings is too low resolution and I'd also like to change the style. However, all 3 versions of the stars that are displayed are held in one image so I'd have to play around with the scripts as well to make sure they are configured properly if I make the image for the stars larger.
Back to my question: How do I edit this image file in my application?
I've tried downloading all the files and putting them in my vender directory and editing the file but it did not seem to work.
I know the files are included by the gem but how do make the files visible to edit?
Appreciate the help!
So the asset pipeline consists of potentially many directories (assuming you are using gems that inject their own assets into the pipeline). When an asset is being grabbed in Rails, Rails goes through these directories (in the same order, every time) to find the asset. When the name of the file is first found, that's it, Rails grabs it and uses that file.
Vendor asset directories are specified after app assets, I believe. So, if you place the image that you want to change in the app/assets/images folder, you'll essentially be overriding that vendor image in your application with your own image since Rails will search it's own app/assets first. Obviously, the files need to be named the same.
Try adding your star image in your assets path. It seem to
reference star.gif using the asset_path
I would also try
overriding the star plugin by creating your own css file.

How should I handle the rails asset pipeline for uploaded images that change the scope of their public view?

After reading about how rails is handling image assets, I am confused and am having trouble deciding where to keep uploaded images in my app's directory tree. If a user has uploaded an image to my rails app, should I make the image_save_path /assets/images/ or /public/images ?
My concern/question: If I want to allow users to make their images public when they choose to do so, should an uploaded image that is 'un-published' (image is served only if current_user == image.owner) be considered more protected if it resides in assets/images instead of public/images? Equally protected? Less protected somehow?
Am I right to assume that the benefit of using the asset pipeline for images is simply that the file names get hashed in production mode and that's it? Is there some implied additional or diminished security here in terms of how the resource is served? Is this more a matter of subtle opinion or does the rails convention have something to say here?
Thanks!
EDIT1:
How would you handle uploaded content that you want to make available to the uploading user, but not discoverable to guests or other users? Should (can?) one store uploaded content outside the tree under web_root ?
Application assets are those assets that are part of your application, such as backgrounds and icons (in the case of images). The asset pipeline has nothing to do with user-uploaded images. I'd suggest to keep them separated, serve them from separate CDN if needed.
The hashing of filenames in the asset pipeline is done only during asset compilation, so a link to a specific image is made unique for the image contents. This helps to avoid setting complex caching headers to images and other assets, as when the image changes, the filename changes too, and the cached version won't be used at all.
All this does not apply to user-uploaded images, you are not going to compile on-the-fly the assets for their images.

Rails 3.1 and Image Assets

I have put all my images for my admin theme in the assets folder within a folder called admin. Then I link to it like normal ie.
# Ruby
image_tag "admin/file.jpg" .....
#CSS
.logo{ background:url('/assets/images/admin/logo.png');
FYI. Just for testing I am not using the asset_path tag just yet as I have not compiled my assets.
Ok all good so far until I decided to update an image. I replaced some colors but on reload the new styled image is not showing. If I view the image directly in the browser its still showing the old image. Going one step further I destroyed the admin images folder. But it has broken nothing all the images are still being displayed. And yes I have cleared my cache and have tried on multiple browsers.
Is there some sort of image caching going on? This is just local development using pow to serve the pages.
Even destroying the whole images folder the images are still being served.
Am I missing something?
In 3.1 you just get rid of the 'images' part of the path. So an image that lives in /assets/images/example.png will actually be accessible in a get request at this url - /assets/example.png
Because the assets/images folder gets generated along with a new 3.1 app, this is the convention that they probably want you to follow. I think that's where image_tag will look for it, but I haven't tested that yet.
Also, during the RailsConf keynote, I remember D2h saying the the public folder should not have much in it anymore, mostly just error pages and a favicon.
You'll want to change the extension of your css file from .css.scss to .css.scss.erb and do:
background-image:url(<%=asset_path "admin/logo.png"%>);
You may need to do a "hard refresh" to see changes. CMD+SHIFT+R on OSX browsers.
In production, make sure
rm -rf public/assets
bundle exec rake assets:precompile RAILS_ENV=production
happens upon deployment.
For what it's worth, when I did this I found that no folder should be include in the path in the css file. For instance if I have app/assets/images/example.png, and I put this in my css file...
div.example { background: url('example.png'); }
... then somehow it magically works. I figured this out by running the rake assets:precompile task, which just sucks everything out of all your load paths and dumps it in a junk drawer folder: public/assets. That's ironic, IMO...
In any case this means you don't need to put any folder paths, everything in your assets folders will all end up living in one huge directory. How this system resolves file name conflicts is unclear, you may need to be careful about that.
Kind of frustrating there aren't better docs out there for this big of a change.
In rails 4 you can now use a css and sass helper image-url:
div.logo {background-image: image-url("logo.png");}
If your background images aren't showing up consider looking at how you're referencing them in your stylesheets.
when referencing images in CSS or in an IMG tag, use image-name.jpg
while the image is really located under ./assets/images/image-name.jpg
http://railscasts.com/episodes/279-understanding-the-asset-pipeline
This railscast (Rails Tutorial video on asset pipeline) helps a lot to explain the paths in assets pipeline as well. I found it pretty useful, and actually watched it a few times.
The solution I chose is #Lee McAlilly's above, but this railscast helped me to understand why it works. Hope it helps!
The asset pipeline in rails offers a method for this exact thing.
You simply add image_path('image filename') to your css or scss file and rails takes care of everything. For example:
.logo{ background:url(image_path('admin/logo.png'));
(note that it works just like in a .erb view, and you don't use "/assets" or "/assets/images" in the path)
Rails also offers other helper methods, and there's another answer here: How do I use reference images in Sass when using Rails 3.1?

Resources