How to load an svg from an npm package in rails? - ruby-on-rails

In our company, we have a shared npm package that contains images (SVG's mostly) that we share between applications to be able to update them easier when needed and keep them consistent.
I'm trying to use such an image in our rails app (more specifically in an ERB template), it sounds so simple to me but I can't get it to work. I researched this a lot online and could only find articles about images in SCSS/JS... but not how to load it as a normal image.
Can somebody point me to docs or give a simple example of how to do this? We use both sprockets and webpacker and still use Rails 5.1 (I know we need to update...)

npm packages are installed to <rails root>/node_modules, so nothing prevents you from doing:
# config/initializers/assets.rb:
Rails.application.configure do
# ...
config.assets.paths << Rails.root.join('node_modules')
# ...
end
(or just path to your single package)
and then working with these like with regular sprockets your_package/some_file.svg (adding to manifest/precompiled files may also be required), file paths will depend on your package structure.

Related

Using Parcel 2 with Ruby on Rails

How can I output the javascript file from Parcel, include this javascript file in Rails app/assets/javascripts/application.js, and ideally get it working with hot module reloading?
Alternatively, is this a bad approach, and do you recommend something more streamlined, that lets me use mostly traditional .html.erb Rails templates but sprinkle in ReactJS UI components here and there?
I was able to do this perfectly in Parcel 1 but I am finding that the setup is quite different in Parcel 2.
I am using the traditional Rails 5 application.html.erb templating.
My attempt so far with Parcel 2 has been to use the library tutorial in the Parcel documentation. I can mount the simplest React component to my Rails app but then if I try to import anything I get errors like require is not defined, and HMR is not working.
I'm not sure how much context to provide for this question. But if it helps, my previous setup with Parcel 1 was to have a separate npm app at the root folder (next to the Rails /app folder) which output a dist/index.js file using the npm script parcel start app/index.jsx --out-dir dist --public-url /assets/dist. This was added to the Rails assets via config.assets.paths << Rails.root.join('frontend') and included in app/assets/javascripts/application.js .
Now for Parcel 2 I removed the include in application.js and added <script type="module" src="/assets/dist/index.js"></script> in application.html.erb
There are two errors you're seeing that I might be able to help with:
Runtime errors like require is not defined. This would be expected if you set up parcel to output your javascript as a library target (as you indicated). Libraries are typically npm packages that are consumed by other projects. In this context, parcel will output a commonjs bundle (including require-based imports) that's intended to be processed by another bunder (maybe parcel again, or webpack) before it's ready to be executed in a browser. You probably want to build your javascript with a browser target instead, so that all dependencies are included without require statements to other packages.
Assuming your package.json for the parcel project currently looks similar to the the library tutorial, the way you'd do that would be to remove the "main": "path-to/my-root-javascript.js" field, and change the build script to parcel build path-to/my-root-javascript.js. There's also a bunch of ways to customize this - see the targets documentation.
HMR not working. If parcel is only in charge of bundling your javascript (and rails is building your html from templates), then the "normal" way that parcel does HMR won't work. This "normal" way would involve parcel injecting an HMR script in the html file that's its responsible for building and serving. But if Rails is taking care of the HTML, parcel can't do this automatically. Perhaps there is a way to configure rails to watch when parcel rebuilds the js bundle, and refresh the page?
This might be a bit of an onion-peeling exercise - it's hard to say if the above suggestions will solve it all end-to-end. If you can post a minimal reproduction of the issue (maybe a simplified version of your ruby + parcel project?) on github that shows the problem, I'm happy to troubleshoot it further.

How to make Rails use images/fonts contained in Yarn packages?

In migrating an app to Rails 5.1 for the first time, I'm experimenting with yarn instead of bower to manage frontend dependencies.
Everything is working great, and I'm able to include js and css files via the relevant application.js/css manifest files.
But, if these files depend on other resources (e.g., a directory of images, fonts, etc.), then Rails knows nothing about them.
Given a yarn package that includes:
/css
|-styles.css
/images
|-background.jpg
where
#styles.css
.background {
background-image: url(../images/background.jpg);
}
Logs are (obviously) showing
No route matches [GET] "/images/background.jpg"
How do I ensure that Rails is aware of these images in Yarn packages and properly compiles them?
Is this possible? Or am I overlooking something obvious?
I had the same problem. Following are some good references
https://edgeguides.rubyonrails.org/webpacker.html#using-webpacker-for-css
https://github.com/rails/webpacker/issues/349
Basically you need to do following things
Require the css file like a js file but with css extension included in /app/javascript/packs/application.js (Yes, require css in javascript)
Add <%= stylesheet_pack_tag "application" %> in layout file if already not there
If you don't already have it, create file (even if empty) /app/javascript/packs/application.scss

Is it possible in Rails to have different assets directories for different environments?

Before executing Asset Precompile (rake assets:precompile) I want to apply gulp tasks to my JavaScript files which are located here:
app/assets/javascripts/my_angular/directives/*.js
app/assets/javascripts/my_angular/controllers/*.js
So when I later run rake assets:precompile it will pick already processed-by-gulp files.
But the problem for me is that I do not want to simply overwrite existing JS files with its gulp output files since I still need original, not touched files for comfortable development.
I think I need to have two folders:
1) development_assets
2) production_assets (auto-generated folder with gulp output)
Is it possible in Rails to have different assets directories for different environments? (development, production). If yes - how to configure it?
Maybe there is another solution for my problem? Without having two separate directories... I am open to suggestions.
You can configure the assets path with config.assets.prefix = '/gulped-assets'. If you do that in config/environments/production.rb, it'll apply to production but not development, letting you still use your original files in dev. You'll need to either make sure your deploy process runs Gulp before asset compilation, or run Gulp locally and include /gulped-assets in your repository.
You can also add a preprocessor to the asset pipeline, which would leave you needing just one /assets directory. You do that by specifying a file extension and a handler, then adding the extension to all the files you want processed that way, just like you have with .sass, .erb, etc. To crib from the Rails examples, it looks like this:
module BangBang
class Template < ::Tilt::Template
def prepare
# Do any initialization here
end
# Adds a "!" to original template.
def evaluate(scope, locals, &block)
"#{data}!"
end
end
end
# config/initializers/bang.rb
Sprockets.register_engine '.bang', BangBang::Template
Then any file containing .bang in the extensions will have a ! appended to it. There's already support for a lot of different tasks out there, so perhaps you can avoid Gulp in favor of a Sprockets-only pipeline. Depending on your Gulp tasks, you might even be able to shell out and run data through them to build a hybrid pipeline.
Or, you can go the other direction and replace Sprockets with a Gulp-only pipeline. There are a lot of people doing that, and anything I'd write here would be long and only duplicate their work, so check out the gulp-rails-pipeline gem and perhaps read Gulp - a modern approach to asset pipeline for Rails developers for another angle on it.

Grunt and Rails

I'm working on using a Grunt workflow to manage my assets in my Rails app rather than Sprockets.
So far, I have my apps JS and CSS both being concatenated and minified into public/assets/javascripts/application.js and public/assets/stylesheets/application.css respectively.
And also have my Bower components JS and CSS being concatenated and minified into public/assets/javascripts/vendor.js and public/assets/stylesheets/vendor.css respectively.
Fonts and Images from Bower components are then copied into public/assets/(images|fonts).
This is all well and good but now I need the references to fonts/images within those files to be updated to reflect their new location.
I have looked at cssmin and yes it rewrites file references but I cannot get the file path to change depending upon the type of file being referenced.
Any ideas on how I can do this?
Also, I ahve been reading about Grunt plugins which can read your view files and use those to minify and concatenate files and update the and tags in the views for you.
Surely I can't do that in a Rails app? Is there a way I can deal with this in Rails?
This other StackOverflow post may be of help:
Integrate Grunt into Rails asset pipeline
The accepted answer recommends using the Half Pipe gem.
The second answer linked to a blog post about a Do-It Yourself solution: Goodbye, Sprockets! A Grunt-based Rails Asset Pipeline.
I haven't used either solution, but they are worth a try.

Does it make sense to create a Ruby gem that consists of only Rails template partials?

I'm trying to understand what does and doesn't work in Ruby gems (primarily from the perspective of creating one for myself to reuse functionality from one project to the next).
When you create a Ruby gem, are you limited to including only Ruby functionality or could you create a gem that consisted of Rails templates, CSS files and JavaScripts?
If so, how would the user of the gem access those resources in a Rails project (besides adding a 'config gem' statement to the environment.rb file?
No it doesn't. Gems are code libraries, not static files.
When you load a gem, you don't move the files to your public directory. You load a ruby file and get access to a module. So, you can only execute ruby code within it.
But, your ruby code can give you some helpers allowing you to easily build your design for example.
If you want to include some static file resources in each of your ruby projects, I'd see two solutions:
Create a template project, including all the css, javascript etc. that you use every time. You can take suspenders as an example of this.
Create a new repository with all these static files and add it as a submodule (with git or svn) and include it in each of your projects.
The first solution would be my favorite, but you might want to take the other one.
As dmathieu notes, gems are for ruby libraries that are installed in the system load path, not for things that go into your project directory. If you do want to package your rails project skeleton as a gem, though, it would be simple enough to have a small ruby executable that served as a generator to copy the skeleton project tree into a target directory. You could then package the whole thing up into a gem, your skeleton files could sit in the gem directory, and you could say generate_new_project someproject to copy them into your new project directory.
You can easily add partials and other HAML, Builder, or ERB views. The following structure should work:
# in my_gem/rails/init.rb
ActionController::Base.append_view_path(File.expand_path(File.join(File.dirname(__FILE__), '..', 'views')))
# in my_gem/views/my_gem/_some_partial.html.erb
This is a partial from MyGem!
# in your_rails_app/views/some_controller/some_view.html.erb:
<%= render :partial => '/my_gem/some_partial' -%>
That doesn't directly answer your question about static files, though. Often the best bet is a generator that copies the CSS, JS, and other files to your public directory. Alternatively, you could use a StaticFilesController and put the static files in that views directory.

Resources