glyphs 0.4.1 → 0.4.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: 0e0ed91cb2af5c3414aa3db654c772ec9c3ad506f4f7d9998223b76144897d33
4
- data.tar.gz: 37a81b162e603e83060c7adfd395cef55f1d20c4896d9bb4d4b0ec96e3e83908
3
+ metadata.gz: 798fe439ca9ea93ff3bfb84f23de90f87abd6ffee953cd85de5543066b52a4f0
4
+ data.tar.gz: 58829c2ec4bf5ed6aaa24da8b40a376b7aae48525277be499eae957754a99c11
5
5
  SHA512:
6
- metadata.gz: 82eb60a70f7fd553e38130cf57025834ca8daf9c02c54c315df9900c07346138eb1230d48783204509ab897791203434db38cf62ed1035337bf3ded8aee7b2ed
7
- data.tar.gz: 188c29bbe5a8a8f6dbae785e39af4a6b49ee4c660a302bf8fd032525012c47ce7b6559e45c342f5b6544f06994162ab443b4e4fc8e51eb81b2c0bbc6ceb38152
6
+ metadata.gz: 49acc1efae75b8d2eb0fac2079ef20c2d5fb211a519c598d61f306453c3dbc000f4ee2fd4bb75090b10a63f009e8bbbddde0e4a3277bc340b58e5eead644eec2
7
+ data.tar.gz: 7b3adbeb6816fbeb13eea1584a33ef1cbcb241339c7caedab14b92b44a185050ebd8240ae0ed99ab5715cc8ccaeb471968f491354e009112eee76ccbd23831a3
data/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@
4
4
 
5
5
  ### Added
6
6
 
7
+ - **`config.merge_default_data` keeps a library's default data next to a
8
+ call's own.** Off by default. The icons gem replaces a library's configured
9
+ `default.data` with a call's `data:` hash, so a default marker such as
10
+ `data-icon` disappears from exactly the icons that carry data of their own.
11
+ With the setting on, the call's `data:` hash is merged over the defaults (the
12
+ call's keys win, its hash is not mutated; `data: nil` keeps the defaults, uncached).
13
+ Cached and uncached renders are byte-identical, the cache key is unchanged,
14
+ and a library with no default data renders as before. With it off, output is
15
+ unchanged. Refs #40
7
16
  - **A non-main Ractor renders icons it has not cached.** A miss there used to
8
17
  raise `Ractor::IsolationError` (`@configuration from Icons`), because the
9
18
  icons gem reads an unshareable module ivar and parses with Nokogiri, which
data/README.md CHANGED
@@ -105,6 +105,16 @@ Glyphs.configure do |config|
105
105
  # on every render that goes uncached.
106
106
  config.cache_svgs = true
107
107
 
108
+ # Merge a call's data: hash over the library's configured default data
109
+ # (`default.data` in the icons configuration) instead of replacing it, as the
110
+ # icons gem does. The call's keys win on a conflict; its hash is not mutated.
111
+ # data: nil keeps the defaults, as no data: does; any other non-hash data:
112
+ # renders as the icons gem renders it. Both render uncached. Cached and
113
+ # uncached renders are identical either way. Default: false (the icons gem's behavior).
114
+ # The cache holds the merged markup: call Glyphs.reset_cache! after changing
115
+ # this or the icons gem's default data at runtime (tests).
116
+ config.merge_default_data = false
117
+
108
118
  # How many icons the cache holds (least recently used evicted first). One
109
119
  # cache per Ractor, so a plain threaded server has one. Default: 1024.
110
120
  config.svg_cache_size = 1024
@@ -137,6 +147,8 @@ Active Support; elsewhere, add `gem "ractor-dispatch"`. The markup is the main R
137
147
  the calling Ractor's cache keeps it, so each icon crosses once per cache entry; cache hits never leave the
138
148
  Ractor. The missing-icon policy runs on the main Ractor too, `on_missing_icon` included. A call with an
139
149
  attribute that renders uncached, or any call with `cache_svgs = false`, goes to the main Ractor every time.
150
+ `merge_default_data` reads the default data where the icons gem renders, on the main Ractor, so it adds no
151
+ crossing: a hit merges the call's `data:` in the calling Ractor. With it on, a non-hash `data:` (`nil` included) renders uncached.
140
152
 
141
153
  Without ractor-dispatch, a miss in a non-main Ractor raises `Glyphs::MainRactor::Unavailable`, naming the
142
154
  icon. You can still render on the main Ractor and hand the markup over with `Glyphs.preload_svg`; cache hits
@@ -18,6 +18,9 @@ module Glyphs
18
18
  # cache_svgs: memoize rendered SVGs per [library, variant, name]; the
19
19
  # caller's attributes are applied to the cached markup on every call.
20
20
  # svg_cache_size: how many icons each Ractor's LRU cache holds (1024).
21
+ # merge_default_data: boolean; when true, a caller's data: hash is merged
22
+ # over the library/variant's configured default data (the caller's keys
23
+ # win) instead of replacing it, as the icons gem does. Default: false.
21
24
  # keep_icons: icons the pruner must keep despite no static reference
22
25
  # (dynamic / data-driven names). A flat list of names/globs, or a
23
26
  # { library => [names/globs] } hash. fallback_icons are always kept.
@@ -28,7 +31,7 @@ module Glyphs
28
31
  # would delete raises Glyphs::PrunableIconError. For the test environment:
29
32
  # the first uncached render scans the source, once per process.
30
33
  attr_accessor :raise_on_missing, :on_missing_icon, :fallback_icons, :cache_svgs,
31
- :keep_icons, :prune_source_globs, :raise_on_prunable
34
+ :keep_icons, :prune_source_globs, :raise_on_prunable, :merge_default_data
32
35
  attr_reader :svg_cache_size
33
36
 
34
37
  def initialize
@@ -40,6 +43,7 @@ module Glyphs
40
43
  @keep_icons = []
41
44
  @prune_source_globs = nil
42
45
  @raise_on_prunable = false
46
+ @merge_default_data = false
43
47
  end
44
48
 
45
49
  def svg_cache_size=(size)
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glyphs
4
+ # config.merge_default_data: merges a library's configured default data under
5
+ # a caller's data: hash, which the icons gem would otherwise replace it with.
6
+ # Reads the icons gem's configuration, so it runs on the main Ractor only
7
+ # (Glyphs.build_svg, which a non-main Ractor reaches through MainRactor).
8
+ # A default data key named like SvgTemplate's slot marker (glyphs-data-slot)
9
+ # would be dropped by the cached data: template, so such defaults are not
10
+ # merged: both paths render as the icons gem does.
11
+ module DefaultData
12
+ SLOT_NAME = SvgTemplate::SLOT_ATTRIBUTE.delete_prefix("data-").freeze
13
+
14
+ class << self
15
+ # The attributes with the default data merged under a data: hash, or
16
+ # without a data: nil (the defaults alone, as with no data:), when the
17
+ # library has default data; else the attributes as given. A new Hash: the
18
+ # caller's is never mutated.
19
+ def merge(attributes, library:, variant:)
20
+ return attributes unless attributes.key?(:data)
21
+
22
+ data = attributes[:data]
23
+ return attributes unless data.nil? || data.is_a?(Hash)
24
+
25
+ defaults = lookup(library:, variant:)
26
+ return attributes unless mergeable?(defaults)
27
+
28
+ data.nil? ? attributes.except(:data) : attributes.merge(data: by_name(defaults, data))
29
+ end
30
+
31
+ private
32
+
33
+ def mergeable?(defaults)
34
+ defaults.is_a?(Hash) && !defaults.empty? && defaults.each_key.none? { |key| key.to_s.tr("_", "-") == SLOT_NAME }
35
+ end
36
+
37
+ # One entry per attribute name, however its key is spelled (:toggle_target,
38
+ # "toggle-target"): at the position its name first appears, with the value
39
+ # it was given last, the caller's after the defaults. That is how a cache
40
+ # hit splices the caller's data over the template's defaults.
41
+ def by_name(defaults, data)
42
+ merged = {}
43
+ defaults.each { |key, value| merged[key.to_s.tr("_", "-")] = value }
44
+ data.each { |key, value| merged[key.to_s.tr("_", "-")] = value }
45
+ merged
46
+ end
47
+
48
+ # The icons gem's own lookup (Icons::Icon::Configurable): the variant, else
49
+ # the library's default_variant, else the global default_variant.
50
+ def lookup(library:, variant:)
51
+ icons = Icons.configuration
52
+ library = library.to_sym
53
+ variant ||= icons.libraries.dig(library, :default_variant) || icons.default_variant
54
+ variant = variant.to_s.empty? ? nil : variant.to_sym
55
+ (icons.libraries.dig(*[library, variant].compact) || {}).dig(:default, :data)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -18,6 +18,9 @@ module Glyphs
18
18
  # A caller's data: replaces the library's default data (the icons gem merges
19
19
  # them that way) and lands where the defaults would have, so data: calls use
20
20
  # a template rendered with DATA_SLOT, whose marker attribute holds that place.
21
+ # With merge_default_data on, that template is rendered with the default data
22
+ # before the marker (Glyphs::DefaultData), and a caller's key that names a
23
+ # default replaces it in place, as rendering the merged hash would.
21
24
  class SvgTemplate
22
25
  DATA_SLOT = { data: { "glyphs-data-slot" => "" }.freeze }.freeze
23
26
  SLOT_ATTRIBUTE = "data-glyphs-data-slot"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Glyphs
4
- VERSION = "0.4.1"
4
+ VERSION = "0.4.2"
5
5
  end
data/lib/glyphs.rb CHANGED
@@ -92,8 +92,15 @@ module Glyphs
92
92
  # policy (raise / instrument / fallback) and the per-Ractor cache: one
93
93
  # entry per [library, variant, name], the caller's attributes applied to
94
94
  # the cached markup on every call.
95
+ #
96
+ # With merge_default_data on, the library's default data is merged under a
97
+ # caller's data: hash when the icons gem renders (build_svg, main Ractor
98
+ # only, where its configuration is readable). The data: template is then
99
+ # rendered with the defaults beside the slot marker, so a cache hit splices
100
+ # the caller's keys over them exactly as the merged hash would render. A
101
+ # non-hash data: renders uncached (data: nil keeps the defaults, if any).
95
102
  def svg_for(name:, library:, variant:, attributes:)
96
- return render_svg(name:, library:, variant:, attributes:) unless configuration.cache_svgs
103
+ return render_svg(name:, library:, variant:, attributes:) if uncached?(attributes)
97
104
 
98
105
  data = attributes.key?(:data)
99
106
  template = svg_cache.compute_if_absent([library, variant, name, data], configuration.svg_cache_size) do
@@ -123,6 +130,13 @@ module Glyphs
123
130
 
124
131
  private
125
132
 
133
+ def uncached?(attributes)
134
+ config = configuration
135
+ return true unless config.cache_svgs
136
+
137
+ config.merge_default_data && attributes.key?(:data) && !attributes[:data].is_a?(Hash)
138
+ end
139
+
126
140
  # A non-main Ractor cannot run the icons gem, so it renders on the main
127
141
  # Ractor (see MainRactor); the missing-icon policy runs there too.
128
142
  def render_svg(name:, library:, variant:, attributes:)
@@ -154,6 +168,7 @@ module Glyphs
154
168
  end
155
169
 
156
170
  def build_svg(name:, library:, variant:, attributes:)
171
+ attributes = DefaultData.merge(attributes, library:, variant:) if configuration.merge_default_data
157
172
  Icons::Icon.new(name:, library:, variant:, arguments: attributes).svg
158
173
  end
159
174
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glyphs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikael Henriksson
@@ -68,6 +68,7 @@ files:
68
68
  - lib/glyphs/animated_icon.rb
69
69
  - lib/glyphs/box_icon.rb
70
70
  - lib/glyphs/configuration.rb
71
+ - lib/glyphs/default_data.rb
71
72
  - lib/glyphs/feather_icon.rb
72
73
  - lib/glyphs/flag_icon.rb
73
74
  - lib/glyphs/hero_icon.rb