Skip to content

Commit

Permalink
Initial working version with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mamhoff committed Nov 29, 2024
1 parent 7ef213e commit 8b9c4ec
Show file tree
Hide file tree
Showing 20 changed files with 417 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
/spec/reports/
/tmp/
Gemfile.lock
test/log/
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ gem "rake", "~> 13.0"
gem "minitest", "~> 5.16"

gem "standard", "~> 1.3"

gem "rails"
10 changes: 9 additions & 1 deletion lib/flickwerk.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
# frozen_string_literal: true

require_relative "flickwerk/version"
require "active_support/core_ext/module/attribute_accessors"
require "flickwerk/railtie" if defined?(Rails)

module Flickwerk
class Error < StandardError; end
# Your code goes here...

mattr_accessor :patch_paths
self.patch_paths = []

def self.included(engine)
patch_paths << engine.root.join("app/patches")
end
end
38 changes: 38 additions & 0 deletions lib/flickwerk/patch_loader.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# frozen_string_literal: true

module Flickwerk
class PatchLoader
DECORATED_CLASS_PATTERN = /(?<decorated_class>[A-Z][a-zA-Z:]+)(\.prepend[\s(])/

attr_reader :path, :autoloader

def initialize(path, autoloader: Rails.autoloaders.main)
@path = path
@autoloader = autoloader
end

def call
path.glob("**/*_patch.rb") do |patch_path|
# Match all the classes that are prepended in the file
matches = File.read(patch_path).scan(DECORATED_CLASS_PATTERN).flatten

# Don't do a thing if there's no prepending.
next unless matches.present?

# For each unique match, make sure we load the decorator when the base class is loaded
matches.uniq.each do |decorated_class|
# Zeitwerk tells us which constant it expects a file to provide.
decorator_constant = autoloader.cpath_expected_at(patch_path)
# Sprinkle some debugging.
Rails.logger.debug("Preparing to autoload #{decorated_class} with #{decorator_constant}")
# If the class has not been loaded, we can add a hook to load the decorator when it is.
# Multiple hooks are no problem, as long as all decorators are namespaced appropriately.
autoloader.on_load(decorated_class) do |base|
Rails.logger.debug("Loading #{decorator_constant} in order to modify #{base}")
decorator_constant.constantize
end
end
end
end
end
end
11 changes: 11 additions & 0 deletions lib/flickwerk/railtie.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

require "flickwerk/patch_loader"

class Flickwerk::Railtie < Rails::Railtie
initializer "flickwerk.add_patches", after: :setup_main_autoloader do
Flickwerk.patch_paths.each do |path|
Flickwerk::PatchLoader.new(path).call
end
end
end
5 changes: 5 additions & 0 deletions test/dummy_app/app/models/concerns/user_methods.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module UserMethods
def last_name
"Vader"
end
end
5 changes: 5 additions & 0 deletions test/dummy_app/app/models/user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class User
def name
"Darth"
end
end
7 changes: 7 additions & 0 deletions test/dummy_app/app/patches/page_patch.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module PagePatch
def title
"Changed from Host app"
end

DummyCms::Page.prepend(self)
end
11 changes: 11 additions & 0 deletions test/dummy_app/app/patches/user_patch.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module UserPatch
def self.prepended(base)
base.include(UserMethods)
end

def age
26
end

User.prepend(self)
end
29 changes: 29 additions & 0 deletions test/dummy_app/config/application.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require "rails"
require "flickwerk/railtie"

module DummyApp
class Application < ::Rails::Application
config.root = File.expand_path("../", __dir__)
include Flickwerk
config.autoload_paths << File.expand_path("../app/models", __dir__)

config.load_defaults("#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}")

# It needs to be explicitly set from Rails 7
# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#upgrading-from-rails-6-1-to-rails-7-0-spring
config.cache_classes = true

config.active_support.deprecation = :stderr
config.log_level = :debug

# Improve test suite performance:
config.eager_load = false
config.cache_store = :memory_store

# We don't use a web server, so we let Rails serve assets.
config.public_file_server.enabled = true

# No need to use credentials file in a test environment.
config.secret_key_base = 'SECRET_TOKEN'
end
end
218 changes: 218 additions & 0 deletions test/dummy_app/log/development.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
Preparing to autoload User with UserPatch
Loading UserPatch in order to modify User
Preparing to autoload User with UserPatch
Loading UserPatch in order to modify User
Preparing to autoload User with UserPatch
Loading UserPatch in order to modify User
Preparing to autoload User with UserPatch
Loading UserPatch in order to modify User
Preparing to autoload User with UserPatch
Loading UserPatch in order to modify User
Preparing to autoload User with UserPatch
Loading UserPatch in order to modify User
Preparing to autoload User with UserPatch
Loading UserPatch in order to modify User
Preparing to autoload User with UserPatch
Loading UserPatch in order to modify User
Preparing to autoload User with UserPatch
Preparing to autoload User with UserPatch
Preparing to autoload User with UserPatch
Loading UserPatch in order to modify User
Preparing to autoload Blog::Post with BlogPostPatch
Preparing to autoload Blog::Post with BlogPostPatch
Preparing to autoload Blog::Post with BlogPostPatch
Preparing to autoload User with UserPatch
Preparing to autoload User with UserPatch
Preparing to autoload User with UserPatch
Loading UserPatch in order to modify User
Loading BlogPostPatch in order to modify Blog::Post
Loading BlogPostPatch in order to modify Blog::Post
Preparing to autoload User with UserPatch
Preparing to autoload User with UserPatch
Preparing to autoload User with UserPatch
Preparing to autoload Blog::Post with BlogPostPatch
Loading UserPatch in order to modify User
Loading BlogPostPatch in order to modify Blog::Post
Preparing to autoload User with UserPatch
Preparing to autoload User with UserPatch
Preparing to autoload User with UserPatch
Preparing to autoload Blog::Post with BlogPostPatch
Loading UserPatch in order to modify User
Loading BlogPostPatch in order to modify Blog::Post
Preparing to autoload User with UserPatch
Preparing to autoload User with UserPatch
Preparing to autoload User with UserPatch
Preparing to autoload Blog::Post with BlogPostPatch
Loading UserPatch in order to modify User
Loading BlogPostPatch in order to modify Blog::Post
Preparing to autoload User with UserPatch
Preparing to autoload User with UserPatch
Preparing to autoload User with UserPatch
Preparing to autoload Blog::Post with BlogPostPatch
Loading UserPatch in order to modify User
Loading BlogPostPatch in order to modify Blog::Post
Preparing to autoload User with UserPatch
Preparing to autoload User with UserPatch
Preparing to autoload User with UserPatch
Preparing to autoload Blog::Post with BlogPostPatch
Loading UserPatch in order to modify User
Loading BlogPostPatch in order to modify Blog::Post
Preparing to autoload User with UserPatch
Preparing to autoload User with UserPatch
Preparing to autoload User with UserPatch
Preparing to autoload Blog::Post with BlogPostPatch
Loading UserPatch in order to modify User
Loading BlogPostPatch in order to modify Blog::Post
Preparing to autoload Cms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload Cms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload Cms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload Blog::Post with BlogPostPatch
Loading BlogPostPatch in order to modify Blog::Post
Loading UserPatch in order to modify User
Preparing to autoload Cms::Page with PagePatch
Preparing to autoload Cms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload User with UserPatch
Preparing to autoload Cms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload Blog::Post with BlogPostPatch
Loading UserPatch in order to modify User
Loading BlogPostPatch in order to modify Blog::Post
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload Blog::Post with BlogPostPatch
Loading UserPatch in order to modify User
Loading BlogPostPatch in order to modify Blog::Post
Preparing to autoload DummyBlog::Post with PostPatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyBlog::Post with PostPatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyBlog::Post with PostPatch
Preparing to autoload User with UserPatch
Preparing to autoload Blog::Post with BlogPostPatch
Loading UserPatch in order to modify User
Loading BlogPostPatch in order to modify Blog::Post
Preparing to autoload DummyBlog::Post with PostPatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyBlog::Post with PostPatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyBlog::Post with PostPatch
Preparing to autoload User with UserPatch
Preparing to autoload Blog::Post with BlogPostPatch
Loading UserPatch in order to modify User
Loading BlogPostPatch in order to modify Blog::Post
Preparing to autoload DummyBlog::Post with PostPatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyBlog::Post with PostPatch
Preparing to autoload User with UserPatch
Preparing to autoload Blog::Post with BlogPostPatch
Preparing to autoload DummyBlog::Post with PostPatch
Preparing to autoload User with UserPatch
Loading PostPatch in order to modify DummyBlog::Post
Loading UserPatch in order to modify User
Preparing to autoload DummyBlog::Post with PostPatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyBlog::Post with PostPatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyBlog::Post with PostPatch
Preparing to autoload User with UserPatch
Preparing to autoload Blog::Post with BlogPostPatch
Loading PostPatch in order to modify DummyBlog::Post
Loading UserPatch in order to modify User
Loading PostPatch in order to modify DummyBlog::Post
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload Blog::Post with BlogPostPatch
Loading UserPatch in order to modify User
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload Blog::Post with BlogPostPatch
Loading UserPatch in order to modify User
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyBlog::Post with DummyBlogPostPatch
Loading UserPatch in order to modify User
Loading DummyBlogPostPatch in order to modify DummyBlog::Post
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyBlog::Post with DummyBlogPostPatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyBlog::Post with DummyBlogPostPatch
Loading UserPatch in order to modify User
Loading PagePatch in order to modify DummyCms::Page
Loading DummyBlogPostPatch in order to modify DummyBlog::Post
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyBlog::Post with DummyBlogPostPatch
Loading UserPatch in order to modify User
Loading PagePatch in order to modify DummyCms::Page
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyBlog::Post with DummyBlogPostPatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Loading PagePatch in order to modify DummyCms::Page
Loading UserPatch in order to modify User
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyBlog::Post with DummyBlogPostPatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyBlog::Post with DummyBlogPostPatch
Loading UserPatch in order to modify User
Loading DummyBlogPostPatch in order to modify DummyBlog::Post
Loading PagePatch in order to modify DummyCms::Page
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload DummyBlog::Post with DummyBlogPostPatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyBlog::Post with DummyBlogPostPatch
Loading PagePatch in order to modify DummyCms::Page
Loading UserPatch in order to modify User
Loading DummyBlogPostPatch in order to modify DummyBlog::Post
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyBlog::Post with DummyBlogPostPatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyCms::Page with PagePatch
Preparing to autoload User with UserPatch
Preparing to autoload DummyBlog::Post with DummyBlogPostPatch
Loading UserPatch in order to modify User
Loading PagePatch in order to modify DummyCms::Page
Loading DummyBlogPostPatch in order to modify DummyBlog::Post
5 changes: 5 additions & 0 deletions test/dummy_blog/app/models/dummy_blog/post.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class DummyBlog::Post
def title
"Original title"
end
end
1 change: 1 addition & 0 deletions test/dummy_blog/lib/dummy_blog.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require "dummy_blog/engine"
5 changes: 5 additions & 0 deletions test/dummy_blog/lib/dummy_blog/engine.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
require "flickwerk"

class BlogEngine < Rails::Engine
include Flickwerk
end
2 changes: 2 additions & 0 deletions test/dummy_cms/app/models/dummy_cms/page.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class DummyCms::Page
end
7 changes: 7 additions & 0 deletions test/dummy_cms/app/patches/dummy_blog_post_patch.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module DummyBlogPostPatch
def title
"Edited from CMS"
end

DummyBlog::Post.prepend(self)
end
4 changes: 4 additions & 0 deletions test/dummy_cms/lib/dummy_cms.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module DummyCms
end

require "dummy_cms/engine"
3 changes: 3 additions & 0 deletions test/dummy_cms/lib/dummy_cms/engine.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class DummyCms::Engine < Rails::Engine
include Flickwerk
end
Loading

0 comments on commit 8b9c4ec

Please sign in to comment.