classy-yaml 1.3.1 → 1.4.0

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: 82933744cd15e116066a4004c44169d70d36b2c6e58b4fbb6654d957ecea0ed4
4
- data.tar.gz: 305cfa5b8a5a53bd50d5187fe8b2d13b0ecc52ea295a591dcca694c407567503
3
+ metadata.gz: 8a6e53b93d281f5b22bc929c6b1df6f389fcd81ed4bc8b9bd3710830a1974ed1
4
+ data.tar.gz: 0702b4fe219583b61140e89487fb0f42c87b4f6de1c2b5889cbacc7186d546b2
5
5
  SHA512:
6
- metadata.gz: d9e8f6d551c9b482b0712a8b4b50734ae579f876012dd1414df37f3db8f4ad9e5030a008ebdb1172e560d517f10c8b387237c45739ffc6f68d85c9d1184674eb
7
- data.tar.gz: c6347754df73ef47fd53bc7d9ee1650a0aea5b7042d8ae08c3310fc4579281dc3763816cbe1aa4fb612f98d7d7eac165fa5566fd4a75b08f76f4c6b9ea292385
6
+ metadata.gz: ef1645ba8e34171a8b2a4eb24830789f5d3d412445e2bca83b6e30285a3a20fdd0911ac65388e092cd44197c3e1b9980bc0b565c8d4e623f72671c678ba1935f
7
+ data.tar.gz: c62c9a28bb4dc589fe04838f5ed33b6fcf27ee2dc68c833161f8e8a57ead9421ef78705c31f8baa546921de1366e247be4f3caf6e9e122720f428940a94e6246
data/README.md CHANGED
@@ -85,6 +85,23 @@ btn:
85
85
 
86
86
  # will add the classes "px-3 py-2 text-blue-50"
87
87
  ```
88
+
89
+ yass(:button, :primary) # => "btn btn-primary"
90
+ yass(:button, :secondary) # => "btn btn-secondary"
91
+ yass(:button, :large) # => "btn btn-lg"
92
+ yass(:button, :primary, :large) # => "btn btn-primary btn-lg"
93
+
94
+ ### Array style definition
95
+
96
+ ```
97
+ array:
98
+ - "px-3 py-2"
99
+ - "bg-gray"
100
+ - "text-purple"
101
+ ```
102
+
103
+ yass(:array) # => "px-3 py-2 bg-gray text-purple"
104
+
88
105
  ## Configuration
89
106
  You can configure the gem by creating an initializer in your Rails app. The following options are available:
90
107
 
@@ -1,38 +1,69 @@
1
1
  module Classy
2
2
  module Yaml
3
3
  module Helpers
4
+ # Fetches utility classes from YAML files based on the provided keys.
5
+ # The method follows a priority order:
6
+ # 1. Extra files (highest priority)
7
+ # 2. Default YAML
8
+ # 3. ViewComponent YAMLs (lowest priority)
9
+ #
10
+ # @param args [Array] Array of keys to look up in the YAML files
11
+ # @return [String] Space-separated list of CSS classes
4
12
  def yass(*args)
5
- classy_yamls = []
13
+ # Start with ViewComponent YAMLs (lowest priority)
14
+ classy_yamls = Classy::Yaml.cached_engine_yamls.dup
6
15
 
7
- Classy::Yaml.engine_files.each do |file|
8
- if File.exist?(file) && YAML.load_file(file)
9
- file = YAML.load_file(file)
10
- classy_yamls << file if file
11
- end
12
- end
16
+ # Add default YAML (next priority)
17
+ default_yaml = Classy::Yaml.cached_default_yaml
18
+ classy_yamls << default_yaml if default_yaml
13
19
 
14
- classy_yamls << YAML.load_file(Rails.root.join(Classy::Yaml.default_file)) if File.exist?(Rails.root.join(Classy::Yaml.default_file))
20
+ # Add extra files (highest priority)
21
+ Classy::Yaml.extra_files.each do |file_path|
22
+ load_yaml_file(file_path, classy_yamls, "extra")
23
+ end
15
24
 
25
+ # Add classy_files (highest priority)
16
26
  classy_files_hash = args.find { |arg| arg.is_a?(Hash) && arg.keys.include?(:classy_files) } || { classy_files: [] }
17
-
18
- (classy_files_hash[:classy_files] + Classy::Yaml.extra_files).each do |file|
19
- if File.exist?(file) && YAML.load_file(file)
20
- file = YAML.load_file(file)
21
- classy_yamls << file if file
22
- end
27
+ classy_files_hash[:classy_files].each do |file_path|
28
+ load_yaml_file(file_path, classy_yamls, "classy")
23
29
  end
24
30
 
25
- return if classy_yamls.blank?
31
+ return "" if classy_yamls.blank?
26
32
 
27
33
  skip_base_hash = args.find { |arg| arg.is_a?(Hash) && arg.keys.include?(:skip_base) } || {}
28
34
  keys, classes = flatten_args(values: args)
29
35
  classes += fetch_classes(keys, classy_yamls: classy_yamls, skip_base: skip_base_hash[:skip_base])
30
36
 
31
- return classes.flatten.join(" ")
37
+ classes.flatten.uniq.join(" ")
32
38
  end
33
39
 
34
40
  private
35
41
 
42
+ # Loads a YAML file and adds its contents to the classy_yamls array
43
+ # @param file_path [String, Pathname] Path to the YAML file
44
+ # @param classy_yamls [Array] Array to add the parsed YAML to
45
+ # @param file_type [String] Type of file being loaded (for error messages)
46
+ def load_yaml_file(file_path, classy_yamls, file_type)
47
+ begin
48
+ path_obj = file_path.is_a?(Pathname) ? file_path : Rails.root.join(file_path)
49
+ if File.exist?(path_obj)
50
+ content = File.read(path_obj, encoding: 'UTF-8')
51
+ parsed_yaml = YAML.safe_load(content, permitted_classes: [Symbol, String, Array, Hash], aliases: true)
52
+ classy_yamls << parsed_yaml if parsed_yaml && parsed_yaml.is_a?(Hash)
53
+ end
54
+ rescue Psych::SyntaxError => e
55
+ Rails.logger.error "Classy::Yaml (yass helper): Failed to parse #{file_type} YAML file #{file_path}: #{e.message}"
56
+ rescue => e
57
+ Rails.logger.error "Classy::Yaml (yass helper): Error loading #{file_type} YAML file #{file_path}: #{e.message}"
58
+ end
59
+ end
60
+
61
+ # Flattens the arguments into keys and classes
62
+ # @param root [Array] Current root path in the argument tree
63
+ # @param values [Array] Values to process
64
+ # @param keys [Array] Array to store found keys
65
+ # @param added_classes [Array] Array to store found classes
66
+ # @return [Array] Tuple of [keys, added_classes]
36
67
  def flatten_args(root: [], values: [], keys: [], added_classes: [])
37
68
  values.each do |value|
38
69
  if value.is_a?(Hash)
@@ -57,6 +88,11 @@ module Classy
57
88
  return keys, added_classes
58
89
  end
59
90
 
91
+ # Fetches classes from the YAML files based on the provided keys
92
+ # @param keys [Array] Array of keys to look up
93
+ # @param classy_yamls [Array] Array of YAML files to search in
94
+ # @param skip_base [Boolean] Whether to skip base classes
95
+ # @return [Array] Array of found classes
60
96
  def fetch_classes(keys, classy_yamls: [], skip_base: false)
61
97
  classes = []
62
98
 
@@ -65,36 +101,58 @@ module Classy
65
101
  fetched_classes = nil
66
102
 
67
103
  classy_yamls.reverse_each do |classy_yaml|
104
+ # Base Class Lookup
68
105
  unless skip_base == true
69
106
  begin
70
- base_classes ||= if classy_yaml.send(:dig, *key).is_a?(Hash)
71
- classy_yaml.send(:dig, *(key + ['base'])).try(:split, " ")
72
- else
73
- classy_yaml.send(:dig, *(key[0...-1] + ['base'])).try(:split, " ")
74
- end
107
+ value_at_key = classy_yaml.send(:dig, *key)
108
+ base_value = if value_at_key.is_a?(Hash)
109
+ classy_yaml.send(:dig, *(key + ['base']))
110
+ elsif key.length > 1
111
+ classy_yaml.send(:dig, *(key[0...-1] + ['base']))
112
+ else
113
+ nil
114
+ end
115
+ normalized_base = normalize_original(base_value)
116
+ base_classes ||= normalized_base
75
117
  rescue
76
118
  Rails.logger.warn(Classy::Yaml::InvalidKeyError.new(data: key))
77
119
  end
78
120
  end
79
121
 
122
+ # Specific Key Class Lookup
80
123
  begin
81
- fetched_classes ||= unless classy_yaml.send(:dig, *key).is_a?(Hash)
82
- classy_yaml.send(:dig, *key).try(:split, " ")
83
- end
84
-
85
- base_classes = nil if base_classes.blank?
86
- fetched_classes = nil if fetched_classes.blank?
124
+ value = classy_yaml.send(:dig, *key)
125
+ unless value.is_a?(Hash)
126
+ normalized_fetched = normalize_original(value)
127
+ fetched_classes ||= normalized_fetched
128
+ end
87
129
  rescue
88
130
  Rails.logger.warn(Classy::Yaml::InvalidKeyError.new(data: key))
89
131
  end
132
+
133
+ base_classes = nil if base_classes.blank?
134
+ fetched_classes = nil if fetched_classes.blank?
90
135
  end
91
136
 
92
137
  classes << base_classes unless base_classes.blank?
93
138
  classes << fetched_classes unless fetched_classes.blank?
94
139
  end
95
140
 
96
- classes.reject!(&:blank?)
97
- return classes.flatten.uniq
141
+ classes.flatten.uniq
142
+ end
143
+
144
+ # Normalizes a value into an array of classes
145
+ # @param value [String, Array, nil] Value to normalize
146
+ # @return [Array, nil] Array of classes or nil if value is invalid
147
+ def normalize_original(value)
148
+ case value
149
+ when String
150
+ value.split(" ").reject(&:blank?)
151
+ when Array
152
+ value.flatten.map(&:to_s).reject(&:blank?)
153
+ else
154
+ nil
155
+ end
98
156
  end
99
157
  end
100
158
  end
@@ -1,5 +1,5 @@
1
1
  module Classy
2
2
  module Yaml
3
- VERSION = '1.3.1'
3
+ VERSION = '1.4.0'
4
4
  end
5
5
  end
data/lib/classy/yaml.rb CHANGED
@@ -3,6 +3,7 @@ require "classy/yaml/engine"
3
3
 
4
4
  module Classy
5
5
  module Yaml
6
+ # -- Configuration Accessors --
6
7
  mattr_accessor :default_file
7
8
  @@default_file = "config/utility_classes.yml"
8
9
 
@@ -12,20 +13,103 @@ module Classy
12
13
  mattr_accessor :extra_files
13
14
  @@extra_files = []
14
15
 
16
+ # -- Autoloads --
15
17
  autoload :Helpers, "classy/yaml/helpers"
16
18
  autoload :ComponentHelpers, "classy/yaml/component_helpers"
17
19
  autoload :InvalidKeyError, "classy/yaml/invalid_key_error"
18
20
 
21
+ # -- Class Instance Variables for Caching --
22
+ @cached_engine_yamls = nil
23
+ @cached_default_yaml = nil
24
+ @load_lock = Mutex.new # Prevent race conditions during lazy loading
25
+
26
+ # -- Configuration Setters with Path Resolution --
19
27
  def self.engine_files=(value)
20
28
  @@engine_files = Array.wrap(value).reject(&:blank?).map { |file| Rails.root.join(file) }
29
+ @cached_engine_yamls = nil # Clear cache on reassignment
21
30
  end
22
31
 
23
32
  def self.extra_files=(value)
24
33
  @@extra_files = Array.wrap(value).reject(&:blank?).map { |file| Rails.root.join(file) }
34
+ # Note: extra_files are not cached globally by default
35
+ end
36
+
37
+ def self.default_file=(value)
38
+ @@default_file = value
39
+ @cached_default_yaml = nil # Clear cache on reassignment
40
+ end
41
+
42
+ # -- Cached Data Accessors (Lazy Loading) --
43
+ def self.cached_engine_yamls
44
+ # Bypass cache in development and test environments
45
+ return load_engine_yamls if Rails.env.development? || Rails.env.test?
46
+
47
+ # Use cache in other environments
48
+ return @cached_engine_yamls if @cached_engine_yamls
49
+
50
+ @load_lock.synchronize do
51
+ # Double-check idiom to ensure loading happens only once
52
+ return @cached_engine_yamls if @cached_engine_yamls
53
+ @cached_engine_yamls = load_engine_yamls
54
+ end
55
+ end
56
+
57
+ def self.cached_default_yaml
58
+ # Bypass cache in development and test environments
59
+ return load_default_yaml if Rails.env.development? || Rails.env.test?
60
+
61
+ # Use cache in other environments
62
+ return @cached_default_yaml if @cached_default_yaml
63
+
64
+ @load_lock.synchronize do
65
+ return @cached_default_yaml if @cached_default_yaml
66
+ @cached_default_yaml = load_default_yaml
67
+ end
25
68
  end
26
69
 
70
+ # -- Setup Method --
27
71
  def self.setup
28
72
  yield self
73
+ # Clear all caches when configuration changes
74
+ @cached_engine_yamls = nil
75
+ @cached_default_yaml = nil
76
+ end
77
+
78
+ private
79
+
80
+ # -- Private Loading Methods --
81
+ def self.load_engine_yamls
82
+ yamls = []
83
+ self.engine_files.each do |file_path|
84
+ begin
85
+ if File.exist?(file_path)
86
+ content = File.read(file_path, encoding: 'UTF-8')
87
+ parsed_yaml = YAML.safe_load(content, permitted_classes: [Symbol, String, Array, Hash], aliases: true)
88
+ yamls << parsed_yaml if parsed_yaml && parsed_yaml.is_a?(Hash)
89
+ end
90
+ rescue Psych::SyntaxError => e
91
+ Rails.logger.error "Classy::Yaml: Failed to parse engine YAML file #{file_path}: #{e.message}"
92
+ rescue => e
93
+ Rails.logger.error "Classy::Yaml: Error loading engine YAML file #{file_path}: #{e.message}"
94
+ end
95
+ end
96
+ yamls
97
+ end
98
+
99
+ def self.load_default_yaml
100
+ default_path = Rails.root.join(self.default_file)
101
+ begin
102
+ if File.exist?(default_path)
103
+ content = File.read(default_path, encoding: 'UTF-8')
104
+ parsed_yaml = YAML.safe_load(content, permitted_classes: [Symbol, String, Array, Hash], aliases: true)
105
+ return parsed_yaml if parsed_yaml && parsed_yaml.is_a?(Hash)
106
+ end
107
+ rescue Psych::SyntaxError => e
108
+ Rails.logger.error "Classy::Yaml: Failed to parse default YAML file #{default_path}: #{e.message}"
109
+ rescue => e
110
+ Rails.logger.error "Classy::Yaml: Error loading default YAML file #{default_path}: #{e.message}"
111
+ end
112
+ nil # Return nil if file doesn't exist or fails to load/parse
29
113
  end
30
114
  end
31
115
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: classy-yaml
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tonksthebear
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-01-30 00:00:00.000000000 Z
11
+ date: 2025-03-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -80,6 +80,48 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '3.36'
83
+ - !ruby/object:Gem::Dependency
84
+ name: appraisal
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.60'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.60'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-rails-omakase
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
83
125
  description: Rails helper to allow yaml configuration of utility css classes
84
126
  email:
85
127
  - ''
@@ -120,7 +162,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
162
  - !ruby/object:Gem::Version
121
163
  version: '0'
122
164
  requirements: []
123
- rubygems_version: 3.4.1
165
+ rubygems_version: 3.5.16
124
166
  signing_key:
125
167
  specification_version: 4
126
168
  summary: Rails helper to allow yaml configuration of utility css classes