Rails routes for non-standard assets in a mountable engine - ruby-on-rails

I have a few non-standard assets (i.e. files that are not images/javascript files/stylesheets such as json and binary files) that live within a mountable engine (without isolate_namespace) in app/assets/data. I want these to be part of the asset pipeline (in the same way as e.g. images).
I can add them to the asset paths collection, e.g.
class Engine < ::Rails::Engine
config.after_initialize do
Rails.application.config.assets.paths << root.join("app", "assets", "data")
end
end
and I can see in the Rails console that the assets are visible to the asset pipeline (e.g. via Rails.application.assets[] and ActionController::Base.helpers.asset_path). For instance, for a file app/assets/data/foo.json, the asset_path helper in the rails console for the hosting app gives me a path assets/foo.json, however that path does not work, I get a
ActionController::RoutingError (No route matches [GET] "/assets/foo.json")
error.
How can I get the hosting Rails app to serve these files?

Turns out, this is some odd behavior with json files with specific names. The files in question are named something like schema-[UUID].json. Rails seems to think these are calls to some controller (even though there is no such route, nor a schema controller) that want json-formatted data back. When I rename the files to [UUID]-schema.json, they all of a sudden work.

Related

carrierwave file storage saved in public directory but rails does not recognize path

A carrierwave uploader in a Rails 6.1 application defines
storage :file
def store_dir
"archive/#{model.id}"
end
development.rb does define
config.active_storage.service = :local
The file and versions are properly process and saved to the archive directory.
However, when calling
attachment.image_url(:preview).to_s
the link is created, but clicking on it returns a rails routing error:
No route matches [GET] "/archive/3/PHOTO-2021-10-02-17-29-15.jpg"
Calling the path of the image in the browser window returns the same error.
the same occurs with /public/archive[...] and image tags return broken links.
While it is understandably that rails might be oriented towards an image tag expecting the file in the assets/images directory - the application still writes the html tag as
<img src="/archive/3/preview_PHOTO-2021-10-02-17-29-15.jpg" />
and the full URL does not fish out something in the public folder, returning the no route error.
of note the application serves as expected the images when running under localhost. The difference in behaviour is concerning.
two avenues of solution, thus questions:
a) how can Carrierwave be directed to saving in the assets/images directory
b) How can this link be properly generated and served with the full URL from the application's public directory
This question merits an answer as it may be useful for other users.
Carrierwave and ActiveStorage do seem to be able to co-exist together, though I am not certain how as I did not document every step taken (& I wanted to test this hypothesis to a positive conclusion).
A sort of garden path was created via the No route matches error message.
The public directory used by carrierwave was being generated on every release of the application. Thus deployments would get new public directories, but not point to the proper one for items loaded before that deploy. Symlink required.
Solution is to edit deploy.rb, in this case:
append :linked_dirs, 'public/archive'
note: this should be only for the carrierwave defined store_dir

Rails Sprockets file uses local manifest for digests instead of asset host version

In our web application built in Rails we have several clients using the same application who will have different assets that are used dependant on which subdomain is used.
To achieve this we swap out what folder is being used on the CDN like so:
config.action_controller.asset_host = Proc.new { |source, request|
if request.subdomain.present?
"http#{request.ssl? ? 's' : ''}://cdn.domain.com/#{request.subdomain}/"
else
"http#{request.ssl? ? 's' : ''}://#{request.host_with_port}/"
end
}
Each time we create a new client we compile the assets manually using a custom build tool that uses Sprockets to build the assets the same way Rails would and then upload them to our CDN under a folder that matches the subdomain. This then allows us to have different sets of assets based purely on the subdomain.
Now this works fine except that when we update an asset the digest will change for that file but Rails will still try and load the old asset digests because the sprockets-manifest file (which is in /public/assets) e.g. .sprockets-manifest-12345.json is being loaded instead of the one that's on the CDN. Even though the asset host is different it still loads the local one.
Rails it seems doesn't care about other manifest files as the file itself only stores the filename to the fingerprinted version so even when things like the host changes it would normally be able to find the correct asset. It would seem as though Rails has been designed this way deliberately.
However we really need to get Rails to use the manifest file that is on the CDN itself rather than use the one in the public folder local to the application.
After reading the docs, it seems you can change the manifest location. We tried doing it by using the same logic as above for the manifest like so:
config.assets.manifest = Proc.new { |source, request|
if request.subdomain.present?
"http#{request.ssl? ? 's' : ''}://cdn.domain.com/#{request.subdomain}/"
else
"http#{request.ssl? ? 's' : ''}://#{request.host_with_port}/"
end
}
But Rails/Sprockets is still using the local sprockets file... Any ideas why?

How to get the filesystem path to a JavaScript file in Rails

I use Rails 4. I can use URL helpers in my views to reference assets without thinking where the resources are located in the filesystem.
Is it easy to find location in the filesystem by providing only the asset name? I suppose that it should be possible as Rails do it somehow when serving assets.
For instance, if I use some gem and it has assets I'd like to have such a function:
f("chartkick.js") # => "/Users/username/.rvm/gems/ruby-2.0.0-p451/gems/chartkick-1.3.2/app/assets/javascripts/chartkick.js"
It was simple. Just went thru asset paths and checked the first existing file:
Rails.application.config.assets.paths.map {|x| x.to_s + "/chartkick.js" }.find { |x| File.exists?(x) }

How to override public folder path in Rails 4?

I would like to use a different public folder from a parent directory called client which contains the entire AngularJS app. Essentially I want to tell Rails to load AngularJS app and the only job that Rails has to do is serve JSON.
Is that possible in Ruby on Rails?
As others have mentioned, it may or may not be a great idea to override the existing paths['public'] folder. But you can do the following safely in somewhere like application.rb:
Rails.application.config.middleware.insert_after(
ActionDispatch::Static,
ActionDispatch::Static,
Rails.root.join("client").to_s,
Rails.application.config.static_cache_control
)
The public folder is exposed to the web server through the Rack middleware ActionDispatch::Static. There's nothing else special about it, and the above code simply adds another instance of the middleware that points to the directory client. So in the above case, the browser would be able to access everything in public as well as client.
Just had to do it myself for an Angular app.
Put this in your application.rb:
config.serve_static_files = true
paths['public'] = File.join 'client', 'app'
Or if you still use asset pipeline (config.assets.enabled = true):
paths['public/javascripts'] = File.join 'client', 'app', 'scripts'
paths['public/stylesheets'] = File.join 'client', 'app', 'styles'
Would be interesting to know if there are any consequences with the second bit as my front-end is served completely separately thus I keep asset pipeline switched off and use grunt instead.
You can define another path like
# config/application.rb
paths['my_website'] = 'website'
Then you can use this path in your routes like
# routes.rb
get '/my_website', to: redirect('my_website/index.html')

Rails Engine isn't being loaded in asset precompile

I have a Rails Mountable Engine called Blog.
Inside the module, I have a method called root_path. The module loads the root path of the engine.
module Blog
def self.root_path
Engine.routes.url_helpers.root_path
end
end
Inside one of the javascript assets of the Rails engine, I load the root url of the engine using erb syntax. Like the following line:
url = <%= Blog.root_path %>
When I run, rake assets:precompile, inside my app, I get an error saying the module does not contain such method. Like it isn't loading the engine library before precompiling the assets.
The error is:
undefined method `root_path' for #< Module:0xc185e14>
Rails engines provide their routing helpers through a routing proxy. You don't need to define root_path methods like this.
Instead, call the method which is your engine's name and then the routing helper on it like this:
blog.root_path
For more information, read the Engines Guide.
Even though Ryan's answer was helpfull, it wasn't the reason I was getting the error.
The reason was that I had set sep up initialize_on_precompile to false in my config/application.rb, so my app wasn't being started.
The Rails guides clearly states:
*For faster asset precompiles, you can partially load your application by setting config.assets.initialize_on_precompile to false in config/application.rb, though in that case templates cannot see application objects or methods*

Resources