bullet_train-themes 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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