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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 88b8f18381414981334281312835975fd93241577a4eff5fd85aece600fa76e3
4
- data.tar.gz: 272efa679248e0957b78e05ca1b4e96e99924b8a49e98567d1247891c416d438
3
+ metadata.gz: b887cc1683a78e19df263a0ae451f8e926517bee14785408483a686f3e7045d2
4
+ data.tar.gz: 4634ae00490cd02fe0758bda07cade5370833fcf758a09ffe0db41d2a2189320
5
5
  SHA512:
6
- metadata.gz: 7fa85c03a6e0dd87797097d8fad5f9f7af6a9b6675e89b300a90b220d87bec1798dea849a6a778b4deba4d835ad6f682eef32da234e1ee6cbc7cde91822479ed
7
- data.tar.gz: faf92219a9b463333c271c402e0d4c143758bbe785e4679ae4e7ec09c191ee3b8781eb906370fdcac31429f33d10f10bd219772f4793c40e30021ea5d15e2741
6
+ metadata.gz: 075d43fa0fe5c2a52d88eda8835e645cb12efe6b5f78685db71bfb14f6b82a685ba12791f969859dabcaaae9a54db9ef09f7f956ccbfdd456f2ed38a4525175a
7
+ data.tar.gz: bf007da695280d3223471a6c43356bda55a71fd48acc973b3e19f4f4e9ba70b4004a3ed9c935e8446a9880acd0cb223c284bd11fadd3792f9bf152e177e84856
@@ -1,19 +1,89 @@
1
- require_relative "../../lib/theme_partials"
2
-
3
1
  module ThemeHelper
4
- include ThemePartials
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
- THEME_DIRECTORY_ORDER.first
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
- theme_partial = Resolver.resolve(options)
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
- return super theme_partial, locals, &block unless theme_partial.blank?
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
- super
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
@@ -1,5 +1,5 @@
1
1
  module BulletTrain
2
2
  module Themes
3
- VERSION = "1.0.1"
3
+ VERSION = "1.0.2"
4
4
  end
5
5
  end
@@ -3,6 +3,6 @@ require "bullet_train/themes/engine"
3
3
 
4
4
  module BulletTrain
5
5
  module Themes
6
- # Your code goes here...
6
+ mattr_accessor :themes, default: {}
7
7
  end
8
8
  end
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.1
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-01-24 00:00:00.000000000 Z
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
@@ -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