Sprockets not adding fingerprint to files added in scss file - ruby-on-rails

I'm having an issue with Rails, Sprockets, SCSS and them not adding a fingerprint to the generated image files.
When I try to precompile my assets in an environment other than development, I get the following error:
rake aborted!
Custom asset_path helper is not implemented
Extend your environment context with a custom method.
environment.context_class.class_eval do
def asset_path(path, options = {})
end
end
So I add the following to an initializer:
Rails.application.assets.context_class.class_eval do
def asset_path(path, options = {})
end
end
Which causes the error to go away, although I don't know if this is the right way about fixing the issue. It then produces a new error:
undefined method `include?' for nil:NilClass
However, if I add the following code:
Rails.application.assets.context_class.class_eval do
def asset_path(path, options = {})
"/assets/#{path}"
end
end
It precompiles fine, but then the generated css does not have the fingerprint generated at the end of the url and therefore doesn't load when viewing the page.
The weird thing is that images that aren't added in a scss file, like in an img tag in the html, has the fingerprint appended fine and shows up when viewing the page.
Here is an example of some scss that doesn't load the fingerprint:
background: $blue url( asset-path('hero/hero-bg.jpg') ) repeat-x;
I'm running Rails 4.0.2. Here is my gemfile: https://gist.github.com/anthonycollini/8025540
Any help would be greatly appreciated. Thank you.

You shouldn't have to define asset_path since that is a helper that is already provided by Rails. It looks like you may have missed <%= %> when using asset_path in your scss file.
background: $blue url(<%= asset-path('hero/hero-bg.jpg') %>) repeat-x;
Also, don't forget to add the .erb file extension on your scss file like example.css.scss.erb.
Hope that helps!

Related

Rails 6 ActionText isn't working on Heroku (undefined method 'rich_text_area_tag')

I am using ActionText to edit a paragraph and it works perfectly locally but when I deploy it to Heroku the page which has the form with rich_text_area it throws an error saying undefined method rich_text_area_tag even though I followed the rails guide. I thought I needed to configure Active Storage on production but that's not the case.
Here is what I am getting in Heroku's logs:
ActionView::Template::Error (undefined method 'rich_text_area_tag' for #<#<Class> Did you mean? rich_text_area)
<%= f.label :something, class:'label' %>
<%= f.rich_text_area :something %>
I found this online, and it was helpful for me:
https://github.com/JoeWoodward/spree_helper_issue
I am not sure if this is the correct way to do it, but it's a temporary workaround.
SOLUTION:
In application_controller.rb enter the following:
require 'action_text'
class ApplicationController < ActionController::Base
helper ActionText::Engine.helpers
...
end
Docs for helper: https://apidock.com/rails/AbstractController/Helpers/ClassMethods/helper
First of all make sure that you have this line in config/application.rb:
require 'rails/all'
If it's a rails 6 app action_text/engine should be loaded.
If it doesn't it's possible taht zeitwerk is not loaded. This happens when you have a rails 5.2 app that was updated to rails 6.
In the same file (config/application.rb) you have to change config.load_defaults to 6.0:
config.load_defaults 6.0
If you want to know what happens in the background take a look at this link on line 125.
To avoid cluttering the ApplicationController code, you can use an initializer:
i.e. add a new file config/initializers/action_text.rb:
ActiveSupport.on_load(:action_view) do
include ActionText::ContentHelper
include ActionText::TagHelper
end
Inspired by p8's comment in a closed Rails issue.

How to copy over /public folder of Rails Engine to a Rails application in production?

I know a simple solution would be just to manually copy over all the files in the Rails Engine's /public folder to the Rails application's /public folder. However, this means that each installation would require manually copying.
Furthermore because the Javascript files that my engine uses have hard-coded image paths, I cannot simply throw all my static files under app/assets or vendor/assets, as then Rails would copy them over to public/assets. I can't change the path where Sprockets outputs the files as I have other gems that expect their assets to be in the default public/assets folder.
I tried doing something like
class Engine < ::Rails::Engine
if Rails.application.config.serve_static_assets
initializer "static assets" do |app|
app.middleware.insert_before(::ActionDispatch::Static, ::ActionDispatch::Static, "#{root}/public")
end
end
end
but this only works for development.
Personally, I believe that the best solution would be to update your javascript files to follow Rails' conventions by replacing the hard-coded images in the javascript with the asset path helper - http://guides.rubyonrails.org/asset_pipeline.html#coding-links-to-assets and then throwing everything into app/assets.
With that said, I can think of situations where you may not want to do that, and that may be the case here.
From your post, I'm guessing that you're precompiling assets for production (i.e. - running rake assets:precompile). In that case, you can just hook into the assets:precompile rake task and copy the files over. It would look something like this in your engine's lib/tasks/my_engine_tasks.rake:
Rake::Task["assets:precompile"].enhance do
Rake::Task["my_engine:copy_assets"].invoke
end
namespace :my_engine do
task :copy_assets => :"assets:environment" do
assets = ["file1", "file2"]
assets.each do |asset|
source_file = File.join(MyEngine::Engine.root, 'public', asset)
dest_file = File.join(Rails.root, 'public', asset)
FileUtils.copy_file source_file, dest_file, true
end
end
end
The above is a simple copy operation but you can also manually run sprockets over those files as well. Take a look at the code for the assets:precompile task (lib/sprockets/rails/task.rb in the sprockets-rails gem) and the Sprockets Manifest class (lib/sprockets/manifest.rb in the sprockets gem).
Seems Rails doesnt provide an obvious way to serve static assets from an engine. So heres my dynamic solution for this problem.
module MyApp
class Engine < ::Rails::Engine
isolate_namespace MyApp
initializer "my_app.assets.precompile" do |app|
app.config.assets.precompile << "my_app_manifest.js" ### manifest file required
### Precompile all static assets
### Note in this example use a non-standard folder (app/assets/public/)
["app/assets/images/", "app/assets/public/"].each do |folder|
dir = app.root.join(folder)
if Dir.exist?(dir)
Dir.glob(File.join(dir, "**/*")).each do |f|
asset_name = f.to_s
.split(folder).last # Remove fullpath
.sub(/^\/*/, '') ### Remove leading '/'
app.config.assets.precompile << asset_name
end
end
end
end
end
end

Sprockets cache stopping changes to initializer config

I have a gem that I've been trying to make configurable. The goal is to have a config block in an initializers file that lets the developer customize the media_query breakpoints in the gems scss file.
My congiguration class looks like this
# my_gem/lib/my_gem/configuration.rb
module MyGem
class << self
attr_accessor :configuration
end
def self.configure
self.configuration ||= Configuration.new
yield(configuration)
end
class Configuration
attr_accessor :med_pixel_width, :lrg_pixel_width
def initialize
#med_pixel_width = 600
#lrg_pixel_width = 1024
end
end
end
I have created a generator that works fine rails g my_gemwhich creates the following initializer file with the cofig block
# config/initializers/my_gem.rb
MyGem.configure do |config|
config.med_pixel_width = 768
config.lrg_pixel_width = 1024
end
And then in my gems stylesheet I catch the configuration
# app/assets/stylesheets/my_gem/my_style.scss.erb
#media only screen and (min-width: <%= MyGem.configuration.med_pixel_width %>px ) {
.styles {
color: red;
}
}
After running bundle and including the gems css asset files in my test app's application.css I can see the styles in the browser being applied as expected. AND the configuration is working. BUT only on the first time the server is started. After I try to change the configuration again no changes take place.
I have discovered that sprockets being cached is the culprit. I confirmed by adding rake tmp:clear right above my config block and then all works whenever i restart the server I can see the changes taking place
# config/initializers/my_gem.rb
system `rake tmp:clear`
MyGem.configure do |config|
config.med_pixel_width = 768
config.lrg_pixel_width = 1024
end
My question is Is there some other simple solution I'm missing here? And if there's not ... is putting this rake tmp:clear in my initializer file bad practice? Would it potentially slow down the development environment workflow for developers using the gem?

How to add markdown as a preprocessor to the asset pipeline?

I have markdown files in app/assets/md/*.html.md which I would like to precompile with rake assets:precompile.
How can I set the asset pipeline up so that it will use a markdown parser to produce the expected HTML files?
Also, how can I include these precompiled assets directly within my views, such as:
<div>
<%= yield asset("my_markdown_fragment.html") %>
</div>
I have since found the solution. Here is what I did:
Add rdiscount to the Gemfile (rdiscount is a Markdown processor for Ruby):
gem 'rdiscount'
Create the file lib/markdown_preprocessor.rb with these contents:
require 'rdiscount'
class MarkdownPreprocessor < Sprockets::Processor
def evaluate(context, locals)
RDiscount.new(begin;data;end).to_html
end
end
Add the MarkdownPreprocessor to the asset pipeline in the config/initializers/sprockets.rb file (create this file if it does not exist) like this:
require 'markdown_preprocessor'
Rails.application.assets.register_engine('.md', MarkdownPreprocessor)
Add the following line to the config/initializers/assets.rb file:
Rails.application.config.assets.paths << Rails.root.join('app', 'assets', 'md')
Configuration is done now. Here are some other guides and tips:
Place your Markdown files into the app/assets/md/ folder.
The extension of your files end with .md, but I suggest .html.md (since the result is an HTML file).
You can insert <%= ruby %> into your Markdown files when you add the .erb extension. For example, try creating this file app/assets/md/hello.html.md.erb with the following contents:
# Hello <%= "_World_ " * 42 %>
You can access the resulting HTML content in your views and controllers with:
Rails.application.assets[asset_path].to_s.html_safe

Rails ERB route helper _path method not found in JSX asset

I'm compiling some JSX assets as part of my Rails 4 app with ReactJS (using the gem) and I'm unable to use route helpers despite including them as specified in these answers.
/railsapp/app/assets/javascripts/components/GigSearchForm.js.jsx.erb:47:in `block in singleton class': undefined local variable or method `gigs_path' for #<#<Class:0x007f80bb0b1df8>:0x007f80bb1528e8> (NameError)
As you can see, the asset is being transformed from ERB to JSX to JS, and while processing the ERB the route helper gigs_path cannot be found. I've already tried implementing solutions such as including an initializer like so:
Rails.application.assets.context_class.class_eval do
include ActionView::Helpers
include Rails.application.routes.url_helpers
end
Sprockets::Context.send :include, Rails.application.routes.url_helpers
Sprockets::Context.send :include, ActionView::Helpers
And at the top of the JSX file including the line: <% environment.context_class.instance_eval { include Rails.application.routes.url_helpers } %>
Nonetheless, I keep getting a similar error that gigs_path is undefined in some manner.

Resources