Expiring the Sprockets cache from Rails (not Rake) - ruby-on-rails

I have a special scenario where if a specific file changes, I need to expire the Sprockets cache (all stylesheets) from Ruby/Rails.
My (failed) approach so far is to prepend dependency_fresh? and attempt to expire the index.
module SprocketsAssetWrapper
def dependency_fresh?(environment, dep)
if <file is changed>
# Attempt to expire the Sprocket's Environment
# Errors with "can't modify immutable index"
environment.send(:expire_index!)
<reset file as unchanged>
end
super environment, dep
end
end
class Sprockets::Asset
prepend SprocketsAssetWrapper
end
As commented, environment.send(:expire_index!) errors with can't modify immutable index.
Any help would be great. I'm on Rails 4.1.4 and have sprockets-rails installed.
Thanks,
Erik

Related

Running webpacker:compile for a engine in my host app causes rails to abort

I have a rails engine/plugin in which i am trying to use webpacker using THIS article as a guide. So basically in my engine, i have the following code :-
lib/saddlebag.rb
require "saddlebag/engine"
module Saddlebag
# ...
class << self
def Webpacker
#Webpacker ||= ::Webpacker::Instance.new(
root_path: Saddlebag::Engine.root,
config_path: Saddlebag::Engine.root.join('config', 'webpacker.yml')
)
end
end
# ...
end
and in the lib/saddlebag/engine.rb file i have the following code :
module Saddlebag
class Engine < ::Rails::Engine
isolate_namespace Saddlebag
# use packs from saddlebag via Rack static
# file service, to enable webpacker to find them
# when running in the host application
config.app_middleware.use(
Rack::Static,
# note! this varies from the webpacker/engine documentation
urls: ["/saddlebag-packs"], root: Saddlebag::Engine.root.join("public")
)
initializer "webpacker.proxy" do |app|
insert_middleware = begin
Saddlebag.webpacker.config.dev_server.present?
rescue
nil
end
next unless insert_middleware
app.middleware.insert_before(
0, Webpacker::DevServerProxy, # "Webpacker::DevServerProxy" if Rails version < 5
ssl_verify_none: true,
webpacker: Saddlebag.webpacker
)
end
end
end
Also i have all of the files required by webpacker mainly :-
config/webpacker.yml and config/webpack/*.js files
bin/webpack and bin/webpack-dev-server files
package.json with required deps.
So the engine and my actually app are in sibling directories so:-
saddlebag
open-flights (main app)
in open flights i link saddlebag with the following line in the gem file :-
gem 'saddlebag', path: '../saddlebag'
Now when i run bin/rails saddlebag:webpacker:compile , i get the following error :-
rails aborted! Don't know how to build task
'saddlebag:webpacker:compile' (See the list of available tasks with
rails --tasks)
Why am i getting this error i have webpacker as a dependency in my saddlebag app. So not sure why this error still occures.
P.S. I found a similar guide for rails engine for enabling webpacker HERE (but uses docker)

How to set up importmap-rails in Rails 7 engine?

I have opened an issue in the importmap-rails gem github repository here about this but thought I'd throw the question out here in case anyone might have a workaround
This is what I have discovered so far
A new engine with Rails 7 alpha 2 or Rails 7.0, generated using rails plugin new custom_page --mountable --full generates a new engine that includes the importmap-rails gem in the bundled gems but there is no ability to use it. Adding spec.add_dependency 'importmap-rails' to the enginename.gemspec makes no difference, nor does adding a require importmap-rails to engine.rb. There is no importmap executable in the bin directory.
A call to bundle info importmap-rails
Produces a promising result showing that the gem is installed by default
* importmap-rails (0.8.1)
Summary: Use ESM with importmap to manage modern JavaScript in Rails without transpiling or bundling.
Homepage: https://github.com/rails/importmap-rails
Source Code: https://github.com/rails/importmap-rails
Path: /home/jamie/.rvm/gems/ruby-3.0.0#custom_page/gems/importmap-rails-0.8.1
A call to rails --tasks shows
rails app:importmap:install # Setup Importmap for the app
But I believe this is coming from the test application generated by the --full option rather than being available to the rails command for the engine.
I was expecting to see the same without app: prefix
A call to this task resolves to a template error as shown
rails app:importmap:install
Don't know how to build task 'app:template' (See the list of available
tasks with rails --tasks) Did you mean? app:tmp:create
If there is a workaround solution to this I'd be grateful to hear it and I'm sure others will too. The reason for me wanting this is that I totally failed to introduced webpacker in a rails 6.1.4 engine and I was hoping this was going to be my, much improved, solution
You don't need to use the install task to set up importmaps. All it does is a few copy paste operations and it doesn't really help with the engine set up anyway.
Add importmaps to engine's gemspec file:
# my_engine/my_engine.gemspec
spec.add_dependency "importmap-rails"
Update engine.rb:
# my_engine/lib/my_engine/engine.rb
require "importmap-rails"
module MyEngine
class Engine < ::Rails::Engine
isolate_namespace MyEngine
initializer "my-engine.importmap", before: "importmap" do |app|
# NOTE: this will add pins from this engine to the main app
# https://github.com/rails/importmap-rails#composing-import-maps
app.config.importmap.paths << root.join("config/importmap.rb")
# NOTE: something about cache; I did not look into it.
# https://github.com/rails/importmap-rails#sweeping-the-cache-in-development-and-test
app.config.importmap.cache_sweepers << root.join("app/assets/javascripts")
end
# NOTE: add engine manifest to precompile assets in production
initializer "my-engine.assets" do |app|
app.config.assets.precompile += %w[my_engine_manifest]
end
end
end
Update assets manifest:
# my_engine/app/assets/config/my_engine_manifest.js
//= link_tree ../javascripts/my_engine .js
Add javascript entry point for our engine, if needed. Pins will be available without this file.
# my_engine/app/assets/javascripts/my_engine/application.js
// do some javascript
document.querySelector("h1").innerText = "hi, i'm your engine";
console.log("hi, again");
Update engine's layout:
# my_engine/app/views/layouts/my_engine/application.html.erb
<!DOCTYPE html>
<html>
<head>
<!--
NOTE: This loads/imports main app `application.js` and all the pins from
the main app and from the engine (because we set it up in the engine.rb).
-->
<%= javascript_importmap_tags %>
<!--
NOTE: To add engine's javascript functionality we have to load the
entrypoint here or `import` it in the main app `application.js`
-->
<%= javascript_import_module_tag "my_engine/application" %>
</head>
<body> <%= yield %> </body>
</html>
Create importmap.rb and pin my_engine/application, this name has to match with javascript_import_module_tag. It cannot clash with any other name in the main app, so you can't just use application:
# my_engine/config/importmap.rb
# NOTE: this pin works because `my_engine/app/assets/javascripts
# is in the `Rails.application.config.assets.paths`
pin "my_engine/application"
Some extras to test the setup:
# config/routes.rb
Rails.application.routes.draw do
mount MyEngine::Engine => "/"
end
# my_engine/config/routes.rb
MyEngine::Engine.routes.draw do
get "home", to: "homes#index"
end
# my_engine/app/controllers/my_engine/homes_controller.rb
module MyEngine
class HomesController < ApplicationController
def index; end
end
end
# my_engine/app/views/my_engine/homes/index.html.erb
<h1>Home</h1>
At this point you should have this in your rendered layout's <head> tag, among other things:
<script type="importmap" data-turbo-track="reload">{
"imports": {
"application": "/assets/application-66ce7505c61e3e4910ff16e7c220e1fbfb39251cd82e4bab8d325b3aae987cf9.js",
"my_engine/application": "/assets/my_engine/application-31ce493e8376b4c20703a50f38d419ae309ffe410b7ab7fec47440e02eef08a8.js",
}
}</script>
<script type="module">import "application"</script>
<script type="module">import "my_engine/application"</script>
H1 tag should change to <h1>hi, i'm your engine</h1> on reload.
Additional importmaps can be added manually with https://generator.jspm.io/.
For bonus points, bin/importmap can be customized to work inside the engine. Create a new importmap file inside bin directory.
# my_engine/bin/importmap
#!/usr/bin/env ruby
# NOTE: don't forget to `chmod u+x bin/importmap` to make it executable.
# make sure we are loading the correct versions of things
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])
# NOTE: importmap requires some rails goodness that we don't have in the engine,
# because we don't have `config/application.rb` that loads the environment.
require "rails"
# importmap-rails is not loaded automatically
require "importmap-rails"
# the actual command runner
require "importmap/commands"
Run from inside the engine directory:
$ bin/importmap pin react
Pinning "react" to https://ga.jspm.io/npm:react#18.1.0/index.js
$ cat config/importmap.rb
pin "my_engine/application"
pin "react", to: "https://ga.jspm.io/npm:react#18.1.0/index.js"
I haven't tested it too much, so any feedback would be welcome. Restart the server if something doesn't show up, I don't know how reloading behaves with all this.
I fall back to the old school Javascript include in the html.
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.js"></script>
Well it definitely works on all browser instead of figuring out if the browser supports the feature that I might use later.
I have full control which page to put too... but that might not be what you want...

Change a RDoc template for generating Rails app documentation

I have just added some documentation to my Rails 3.2.13 app. I can generate the documentation just fine (running RDoc 3.12.2) by using a rake task:
# lib/tasks/documentation.rake
Rake::Task["doc:app"].clear
Rake::Task["doc/app"].clear
Rake::Task["doc/app/index.html"].clear
namespace :doc do
RDoc::Task.new('app') do |rdoc|
rdoc.rdoc_dir = 'doc/app'
rdoc.generator = 'hanna'
rdoc.title = 'Stoffi Web App Documentation'
rdoc.main = 'doc/Overview'
rdoc.options << '--charset' << 'utf-8'
rdoc.rdoc_files.include('app/**/*.rb')
rdoc.rdoc_files.include('doc/*')
end
end
...and then running rake doc:app. But I really don't like the default look of the Hanna template. Is there a way to edit the CSS, perhaps by providing my own CSS file which will override the default one used in Hanna?
Thanks!
First of all find where your templates are located:
⮀ RDPATH=$(dirname $(gem which rdoc))
# ⇒ /home/am/.rvm/rubies/ruby-head/lib/ruby/2.1.0
Now copy the default template from there to the desired location (change /tmp to your project directory or like):
⮀ cp -r $RDPATH/rdoc/generator/template/darkfish /tmp/myniftytemplate
And, finally, let’s teach the rdoc:
class RDoc::Options
def template_dir_for template
"/tmp/#{template}"
end
end
RDoc::Task.new('app') do |rdoc|
rdoc.template = 'myniftytemplate'
…
end
That’s it. Hope it helps.

Paperclip File Not Found Error

I just switched from a paperclip rails plugin to a paperclip gem. The project is a rails 2.3 application and I am using paperclip 2.7.2 gem.
I am getting the following odd error:
identify: unable to open image `file': No such file or directory # error/blob.c/OpenBlob/2617.
identify: no decode delegate for this image format `file' # error/constitute.c/ReadImage/544.
Seems like paperclip is looking for a file called 'file' but I am not sure why. I didn't change any of the code we had before. It used to work, all I did was upgrade to a newer version and use a gem over a plugin.
Any ideas?
Update
It to be a bug in paper clip where it does not parse the content of the command properly. I dug deep into the paperclip source to find:
def run(cmd, arguments = "", local_options = {})
if options[:image_magick_path]
Paperclip.log("[DEPRECATION] :image_magick_path is deprecated and will be removed. Use :command_path instead")
end
command_path = options[:command_path] || options[:image_magick_path]
Cocaine::CommandLine.path = [Cocaine::CommandLine.path, command_path].flatten.compact.uniq
local_options = local_options.merge(:logger => logger) if logging? && (options[:log_command] || local_options[:log_command])
Cocaine::CommandLine.new(cmd, arguments, local_options).run
end
and
# Uses ImageMagick to determing the dimensions of a file, passed in as either a
# File or path.
# NOTE: (race cond) Do not reassign the 'file' variable inside this method as it is likely to be
# a Tempfile object, which would be eligible for file deletion when no longer referenced.
def self.from_file file
file_path = file.respond_to?(:path) ? file.path : file
raise(Paperclip::NotIdentifiedByImageMagickError.new("Cannot find the geometry of a file with a blank name")) if file_path.blank?
geometry = begin
Paperclip.run("identify", "-format %wx%h :file", :file => "#{file_path}[0]")
rescue Cocaine::ExitStatusError
""
rescue Cocaine::CommandNotFoundError => e
raise Paperclip::CommandNotFoundError.new("Could not run the `identify` command. Please install ImageMagick.")
end
parse(geometry) ||
raise(NotIdentifiedByImageMagickError.new("#{file_path} is not recognized by the 'identify' command."))
end
The Paperclip.run command fails to replace the :file placeholder for some reason. I verified this by running the following on the commandline:
identify :file
Monkey patching the replacement by hand yields other errors where a similar thing happens.
Ok I managed to solve it.
It was a problem with Cocaine. Seems paperclip has a dependency on cocaine that only says it must be Cociane > 0.02. The latest version of Cocaine 0.4.2 (at the time of this writing) has a new API which is not backward compatible. You need to downgrade to Cocaine 0.3.2.
Simply put this in your Gemfile before paperclip:
gem "cocaine", "0.3.2"

Rails/Passenger sub URI error

I am attempting to deploy a Rails 3.0.0 application to a sub URI using passenger 2.2.15.
I believe I've made the correct RailsBaseURI changes to my http.conf , have symlinked the sub URI to the public directory of my app and added the following line of code to environments/production.rb:
config.action_controller.relative_url_root = "/sub_uri"
I've done this several times pre-rails3.0.0. That said, the app won't launch. It fails with the following Passenger error:
Error Message: wrong number of arguments(1 for 0)
Exception class: ArgumentError
/usr/lib/ruby/gems/1.8/gems/actionpack-3.0.0/lib/action_controller/railtie.rb 54 in `relative_url_root='
Is there an incompatibility between passenger 2.2.15 and rails 3.0.0 that affects sub URI's?
Any help sorting out this error is greatly appreciated.
The setter is depreciated, it's nowhere to be found in actionpack/lib/action_controller/railtie.rb.
As seen here (actionpack/lib/action_controller/depreciated/base.rb):
module ActionController
class Base
# Deprecated methods. Wrap them in a module so they can be overwritten by plugins
# (like the verify method.)
module DeprecatedBehavior #:nodoc:
def relative_url_root
ActiveSupport::Deprecation.warn "ActionController::Base.relative_url_root is ineffective. " <<
"Please stop using it.", caller
end
def relative_url_root=
ActiveSupport::Deprecation.warn "ActionController::Base.relative_url_root= is ineffective. " <<
"Please stop using it.", caller
end
end
end
end
In actionpack/lib/action_controller/metal/compatibility.rb you can see it's setter is an ENV variable:
self.config.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT']
So you need to set the ENV variable: RAILS_RELATIVE_URL_ROOT="/sub_uri"
To set the environment variable add:
SetEnv RAILS_RELATIVE_URL_ROOT /sub_uri
To the VirtualHost section (or similar) of your apache config then make sure it's being read by restarting apache and passenger.
cd <your_rails_project>
sudo apache2ctl graceful
touch tmp/restart

Resources