bullet_train-themes 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/helpers/theme_helper.rb +77 -7
- data/lib/bullet_train/themes/version.rb +1 -1
- data/lib/bullet_train/themes.rb +1 -1
- metadata +2 -3
- data/lib/theme_partials.rb +0 -97
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b887cc1683a78e19df263a0ae451f8e926517bee14785408483a686f3e7045d2
|
|
4
|
+
data.tar.gz: 4634ae00490cd02fe0758bda07cade5370833fcf758a09ffe0db41d2a2189320
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 075d43fa0fe5c2a52d88eda8835e645cb12efe6b5f78685db71bfb14f6b82a685ba12791f969859dabcaaae9a54db9ef09f7f956ccbfdd456f2ed38a4525175a
|
|
7
|
+
data.tar.gz: bf007da695280d3223471a6c43356bda55a71fd48acc973b3e19f4f4e9ba70b4004a3ed9c935e8446a9880acd0cb223c284bd11fadd3792f9bf152e177e84856
|
data/app/helpers/theme_helper.rb
CHANGED
|
@@ -1,19 +1,89 @@
|
|
|
1
|
-
require_relative "../../lib/theme_partials"
|
|
2
|
-
|
|
3
1
|
module ThemeHelper
|
|
4
|
-
|
|
2
|
+
# TODO Do we want this to be configurable by downstream applications?
|
|
3
|
+
INVOCATION_PATTERNS = [
|
|
4
|
+
# ❌ This path is included for legacy purposes, but you shouldn't reference partials like this in new code.
|
|
5
|
+
/^account\/shared\//,
|
|
6
|
+
|
|
7
|
+
# ✅ This is the correct path to generically reference theme component partials with.
|
|
8
|
+
/^shared\//,
|
|
9
|
+
]
|
|
5
10
|
|
|
6
11
|
def current_theme
|
|
7
|
-
|
|
12
|
+
# Temporarily hardcoded. We actually don't want this to be just an `mattr_accessor` because the same application
|
|
13
|
+
# can use multiple themes. Need to think about this a bit.
|
|
14
|
+
:light
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def current_theme_object
|
|
18
|
+
@current_theme_object ||= "BulletTrain::Themes::#{current_theme.to_s.classify}::Theme".constantize.new
|
|
8
19
|
end
|
|
9
20
|
|
|
10
21
|
def render(options = {}, locals = {}, &block)
|
|
22
|
+
# The theme engine only supports `<%= render 'shared/box' ... %>` style calls to `render`.
|
|
11
23
|
if options.is_a?(String)
|
|
12
|
-
|
|
24
|
+
# Initialize a global variable that will cache all resolutions of a given path for the entire life of the server.
|
|
25
|
+
$resolved_theme_partial_paths ||= {}
|
|
13
26
|
|
|
14
|
-
|
|
27
|
+
# Check whether we've already resolved this partial to render from another path before.
|
|
28
|
+
# If we've already resolved this partial from a different path before, then let's just skip to that.
|
|
29
|
+
# TODO This should be disabled in development so new templates are taken into account without restarting the server.
|
|
30
|
+
if $resolved_theme_partial_paths[options]
|
|
31
|
+
# Override the value in place. This will be respected when we call `super` below.
|
|
32
|
+
options = $resolved_theme_partial_paths[options]
|
|
33
|
+
end
|
|
15
34
|
end
|
|
16
35
|
|
|
17
|
-
|
|
36
|
+
begin
|
|
37
|
+
# This is where we try to just lean on Rails default behavior. If someone renders `shared/box` and also has a
|
|
38
|
+
# `app/views/shared/_box.html.erb`, then no error will be thrown and we will have never interfered in the normal
|
|
39
|
+
# Rails behavior.
|
|
40
|
+
#
|
|
41
|
+
# We also don't do anything special if someone renders `shared/box` and we've already previously resolved that
|
|
42
|
+
# partial to be served from `themes/light/box`. In that case, we've already replaced `shared/box` with the
|
|
43
|
+
# actual path of the partial, and Rails will do the right thing from this point.
|
|
44
|
+
#
|
|
45
|
+
# However, if one of those two situations isn't true, then this call here will throw an exception and we can
|
|
46
|
+
# perform the appropriate magic to figure out where amongst the themes the partial should be rendering from.
|
|
47
|
+
return super
|
|
48
|
+
rescue ActionView::MissingTemplate => exception
|
|
49
|
+
# The theme engine only supports `<%= render 'shared/box' ... %>` style calls to `render`.
|
|
50
|
+
if options.is_a?(String)
|
|
51
|
+
|
|
52
|
+
# Does the requested partial path match one of the invocation regexes?
|
|
53
|
+
if (invocation_pattern = INVOCATION_PATTERNS.detect { |regex| options.match?(regex) })
|
|
54
|
+
# Keep track of the original options.
|
|
55
|
+
original_options = options
|
|
56
|
+
|
|
57
|
+
# Trim out the base part of the requested partial.
|
|
58
|
+
requested_partial = options.gsub(invocation_pattern, "")
|
|
59
|
+
|
|
60
|
+
# TODO We're hard-coding this for now, but this should probably come from the `Current` model.
|
|
61
|
+
current_theme_object.directory_order.each do |theme_path|
|
|
62
|
+
begin
|
|
63
|
+
# Update our options from something like `shared/box` to `themes/light/box`.
|
|
64
|
+
options = "themes/#{theme_path}/#{requested_partial}"
|
|
65
|
+
|
|
66
|
+
# Try rendering the partial again with the updated options.
|
|
67
|
+
body = super
|
|
68
|
+
|
|
69
|
+
# 🏆 If we get this far, then we've found the actual path of the theme partial. We should cache it!
|
|
70
|
+
$resolved_theme_partial_paths[original_options] = options
|
|
71
|
+
|
|
72
|
+
# We also need to return whatever the rendered body was.
|
|
73
|
+
return body
|
|
74
|
+
|
|
75
|
+
# If calling `render` with the updated options is still resulting in a missing template, we need to
|
|
76
|
+
# keep iterating over `directory_order` to work our way up the theme stack and see if we can find the
|
|
77
|
+
# partial there, e.g. going from `light` to `tailwind` to `base`.
|
|
78
|
+
rescue ActionView::MissingTemplate => _
|
|
79
|
+
next
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# If we weren't able to find the partial in some theme-based place, then just let the original error bubble up.
|
|
86
|
+
raise exception
|
|
87
|
+
end
|
|
18
88
|
end
|
|
19
89
|
end
|
data/lib/bullet_train/themes.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bullet_train-themes
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Culver
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-
|
|
11
|
+
date: 2022-02-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -65,7 +65,6 @@ files:
|
|
|
65
65
|
- lib/bullet_train/themes/engine.rb
|
|
66
66
|
- lib/bullet_train/themes/version.rb
|
|
67
67
|
- lib/tasks/bullet_train/themes_tasks.rake
|
|
68
|
-
- lib/theme_partials.rb
|
|
69
68
|
homepage: https://github.com/bullet-train-co/bullet_train-themes
|
|
70
69
|
licenses:
|
|
71
70
|
- MIT
|
data/lib/theme_partials.rb
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
module ThemePartials
|
|
2
|
-
# TODO This needs to be configurable now that we've moved this file upstream.
|
|
3
|
-
# TODO Also, this feels almost like it should be set on a per-request basis, since you can have more than one theme
|
|
4
|
-
# in an app at a time.
|
|
5
|
-
THEME_DIRECTORY_ORDER = [
|
|
6
|
-
"light",
|
|
7
|
-
"tailwind",
|
|
8
|
-
"base",
|
|
9
|
-
]
|
|
10
|
-
|
|
11
|
-
INCLUDE_TARGETS = [
|
|
12
|
-
# ❌ This path is included for legacy purposes, but you shouldn't reference partials like this in new code.
|
|
13
|
-
"account/shared",
|
|
14
|
-
|
|
15
|
-
# ✅ This is the correct path to generically reference theme component partials with.
|
|
16
|
-
"shared"
|
|
17
|
-
]
|
|
18
|
-
|
|
19
|
-
# i.e. Changes "account/shared/box" to "account/shared/_box"
|
|
20
|
-
def convert_to_literal_partial(path)
|
|
21
|
-
path.sub(/.*\K\//, "/_")
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# i.e. Changes "account/shared/_box" to "_box"
|
|
25
|
-
def remove_hierarchy_base(path, include_target)
|
|
26
|
-
path.sub(/^#{include_target}\//, "")
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# i.e. Get "app/views/themes/light/_box.html.erb" from "_box"
|
|
30
|
-
def get_full_debased_file_path(path, theme_directory)
|
|
31
|
-
"app/views/themes/#{theme_directory}/#{path}.html.erb"
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Adds a hierarchy with a specific theme to a partial.
|
|
35
|
-
# i.e. Changes "workflow/box" to "themes/light/workflow/box"
|
|
36
|
-
def add_hierarchy_to_path(file_path, theme_directory)
|
|
37
|
-
"themes/#{theme_directory}/#{file_path}"
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
class Resolver
|
|
41
|
-
extend ThemePartials
|
|
42
|
-
|
|
43
|
-
# This global variable is created once per application boot.
|
|
44
|
-
# We're not using the Rails caching system because we want everything in local memory for this.
|
|
45
|
-
# If we use the Rails caching system, we end up querying it over the wire from Redis or memcached.
|
|
46
|
-
$resolved_theme_partials = {}
|
|
47
|
-
|
|
48
|
-
def self.base_path_for(theme)
|
|
49
|
-
begin
|
|
50
|
-
"BulletTrain::Themes::#{theme.to_s.classify}::PathSnitch".constantize.method(:confess).source_location.first.split("/lib/bullet_train/themes/").first
|
|
51
|
-
rescue NameError => _
|
|
52
|
-
nil
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def self.resolve(options)
|
|
57
|
-
INCLUDE_TARGETS
|
|
58
|
-
.filter { |include_target| options.start_with? include_target }
|
|
59
|
-
.each do |include_target|
|
|
60
|
-
# If the partial path has already been resolved since boot, just return that value.
|
|
61
|
-
# This caching is not enabled in development so people can introduce new files without restarting.
|
|
62
|
-
unless Rails.env.development?
|
|
63
|
-
if $resolved_theme_partials[options]
|
|
64
|
-
return $resolved_theme_partials[options]
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Otherwise, we need to traverse the inheritance structure of the themes to find the right partial.
|
|
69
|
-
debased_file_path = remove_hierarchy_base(options, include_target)
|
|
70
|
-
normal_file_path = convert_to_literal_partial(options)
|
|
71
|
-
|
|
72
|
-
# TODO this is a hack because the main menu is still in this directory
|
|
73
|
-
# and other people might also add stuff there.
|
|
74
|
-
unless File.exist?("#{Rails.root}/app/views/#{normal_file_path}.html.erb")
|
|
75
|
-
THEME_DIRECTORY_ORDER.each do |theme_directory|
|
|
76
|
-
# First we check whether it's defined in the actual application. This takes precedence.
|
|
77
|
-
full_debased_file_path = convert_to_literal_partial(get_full_debased_file_path(debased_file_path, theme_directory))
|
|
78
|
-
|
|
79
|
-
actual_file_path = [
|
|
80
|
-
Rails.root,
|
|
81
|
-
# This will return nil if the theme isn't distributed as a Ruby gem.
|
|
82
|
-
base_path_for(theme_directory),
|
|
83
|
-
].compact.map { |path| "#{path}/#{full_debased_file_path}" }.detect { |file| File.exist?(file) }
|
|
84
|
-
|
|
85
|
-
if actual_file_path
|
|
86
|
-
# Once we've found it, ensure we don't do this again for the same partial.
|
|
87
|
-
$resolved_theme_partials[options] = add_hierarchy_to_path(debased_file_path, theme_directory)
|
|
88
|
-
return $resolved_theme_partials[options]
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
nil
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
end
|