clsx-rails 1.0.1 → 2.0.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 +4 -4
- data/CHANGELOG.md +35 -13
- data/CLAUDE.md +54 -0
- data/README.md +1 -1
- data/lib/clsx/helper.rb +83 -20
- data/lib/clsx/version.rb +1 -1
- metadata +6 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5ac79817d0d2f653dbc76e4f3d044c6d599f342acecc1c6d6c6639dbbdec1ecf
|
|
4
|
+
data.tar.gz: ce38f0509202614f8aeab6f16f0079eb93d686e6c5c42d48170af8d50ccd801b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0c8e9e1d7100c07f7336b9a8ac4fd699b4a1a90f78bd4c2fa51cdd931583948913d9622e901a17f3fa03104fbebca4f92ac07df50287a1f4209bb531cf4886fe
|
|
7
|
+
data.tar.gz: 155e24fb75550a7a12e050fc20089fc9677e0435d7b08f0412d65b19a727f7fbca2cb86cbb0d7cf6df44b037e4e515efa947c42d2da9f020805ac8c4461d7e58
|
data/CHANGELOG.md
CHANGED
|
@@ -6,30 +6,52 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
## Unreleased changes
|
|
8
8
|
|
|
9
|
+
## v2.0.0 (2025-01-11)
|
|
10
|
+
|
|
11
|
+
### Breaking Changes
|
|
12
|
+
- Drop Rails 6.1 and 7.0 support, require Rails 7.1+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
- Add Ruby 3.4 support
|
|
16
|
+
- Add Rails 8.0, 8.1, and edge support
|
|
17
|
+
|
|
18
|
+
### Performance Improvements
|
|
19
|
+
- Rewrite algorithm for 2-5x performance improvement
|
|
20
|
+
- Add fast-paths for single string, string array, and simple hash
|
|
21
|
+
- Use Hash-based deduplication instead of Array + `uniq!`
|
|
22
|
+
- Use `Symbol#name` instead of `to_s` for faster symbol conversion
|
|
23
|
+
- Use direct class comparison for type checking
|
|
24
|
+
- Remove unused `require 'set'`
|
|
25
|
+
|
|
26
|
+
### Chore
|
|
27
|
+
- Refactor benchmark infrastructure (data.rb, original.rb, quick.rb, run.rb)
|
|
28
|
+
- Add CLAUDE.md for AI coding assistants
|
|
29
|
+
|
|
9
30
|
## v1.0.1 (2024-03-04)
|
|
10
31
|
|
|
11
32
|
### Performance Improvements
|
|
12
|
-
- Speeds up the performance by 2x [`32236ed`](
|
|
33
|
+
- Speeds up the performance by 2x [`32236ed`](https://github.com/svyatov/clsx-rails/commit/32236ed)
|
|
13
34
|
|
|
14
35
|
### Chore
|
|
15
|
-
- Fixes CI action [`f1b948c`](
|
|
16
|
-
- Upload code coverage to CodeCov for the latest combination of Ruby and ActionView only [`4e5d768`](
|
|
36
|
+
- Fixes CI action [`f1b948c`](https://github.com/svyatov/clsx-rails/commit/f1b948c)
|
|
37
|
+
- Upload code coverage to CodeCov for the latest combination of Ruby and ActionView only [`4e5d768`](https://github.com/svyatov/clsx-rails/commit/4e5d768)
|
|
17
38
|
|
|
18
39
|
### Documentation
|
|
19
|
-
-
|
|
20
|
-
- Adds
|
|
40
|
+
- Changelog update + version bump [`13b408d`](https://github.com/svyatov/clsx-rails/commit/13b408d)
|
|
41
|
+
- Adds information about supported Ruby and Rails version [skip ci] [`2e6483f`](https://github.com/svyatov/clsx-rails/commit/2e6483f)
|
|
42
|
+
- Adds link to the CodeCov badge, switch to Conventional Commits [`b48cc84`](https://github.com/svyatov/clsx-rails/commit/b48cc84)
|
|
21
43
|
|
|
22
44
|
### Other
|
|
23
|
-
- Updates CI badge [skip ci] [`a829613`](
|
|
24
|
-
- Adds code coverage tracking [`0c5d34c`](
|
|
25
|
-
- Ignore ruby-head in the CI matrix [`f3ab4df`](
|
|
26
|
-
- Better name for the GitHub Action job [`a28adb7`](
|
|
27
|
-
- Adds badges, fixes rubocop configuration for CI [`56fab44`](
|
|
28
|
-
- Create dependabot.yml [`ed1e0eb`](
|
|
29
|
-
- Fixes GitHub Actions [`f58a4b2`](
|
|
45
|
+
- Updates CI badge [skip ci] [`a829613`](https://github.com/svyatov/clsx-rails/commit/a829613)
|
|
46
|
+
- Adds code coverage tracking [`0c5d34c`](https://github.com/svyatov/clsx-rails/commit/0c5d34c)
|
|
47
|
+
- Ignore ruby-head in the CI matrix [`f3ab4df`](https://github.com/svyatov/clsx-rails/commit/f3ab4df)
|
|
48
|
+
- Better name for the GitHub Action job [`a28adb7`](https://github.com/svyatov/clsx-rails/commit/a28adb7)
|
|
49
|
+
- Adds badges, fixes rubocop configuration for CI [`56fab44`](https://github.com/svyatov/clsx-rails/commit/56fab44)
|
|
50
|
+
- Create dependabot.yml [`ed1e0eb`](https://github.com/svyatov/clsx-rails/commit/ed1e0eb)
|
|
51
|
+
- Fixes GitHub Actions [`f58a4b2`](https://github.com/svyatov/clsx-rails/commit/f58a4b2)
|
|
30
52
|
|
|
31
53
|
## v1.0.0 (2024-03-03)
|
|
32
54
|
|
|
33
55
|
### Other
|
|
34
|
-
- Initial commit [`f65b9b8`](
|
|
56
|
+
- Initial commit [`f65b9b8`](https://github.com/svyatov/clsx-rails/commit/f65b9b8)
|
|
35
57
|
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
clsx-rails is a Ruby gem that provides a Rails view helper (`clsx`/`cn`) for constructing CSS class strings conditionally. It's a Ruby port of the JavaScript [clsx](https://github.com/lukeed/clsx) package, adapted for Rails conventions.
|
|
8
|
+
|
|
9
|
+
## Common Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Run all tests and linting (default rake task)
|
|
13
|
+
bundle exec rake
|
|
14
|
+
|
|
15
|
+
# Run tests only
|
|
16
|
+
bundle exec rake test
|
|
17
|
+
|
|
18
|
+
# Run a single test file
|
|
19
|
+
bundle exec ruby -Itest test/clsx/helper_test.rb
|
|
20
|
+
|
|
21
|
+
# Run a specific test method
|
|
22
|
+
bundle exec ruby -Itest test/clsx/helper_test.rb -n test_with_strings
|
|
23
|
+
|
|
24
|
+
# Run linter
|
|
25
|
+
bundle exec rake rubocop
|
|
26
|
+
|
|
27
|
+
# Run benchmark
|
|
28
|
+
bundle exec ruby benchmark/run.rb
|
|
29
|
+
|
|
30
|
+
# Install dependencies
|
|
31
|
+
bin/setup
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Architecture
|
|
35
|
+
|
|
36
|
+
The gem has a minimal structure:
|
|
37
|
+
|
|
38
|
+
- `lib/clsx-rails.rb` - Entry point that auto-includes the helper into ActionView via `ActiveSupport.on_load`
|
|
39
|
+
- `lib/clsx/helper.rb` - Core implementation with `clsx` method and `cn` alias
|
|
40
|
+
- `lib/clsx/version.rb` - Version constant
|
|
41
|
+
|
|
42
|
+
The helper uses an optimized algorithm with fast-paths for common cases (single string, string array, simple hash) and Hash-based deduplication for complex inputs.
|
|
43
|
+
|
|
44
|
+
## Key Behaviors
|
|
45
|
+
|
|
46
|
+
- Returns `nil` (not empty string) when no classes apply - this prevents Rails from rendering empty `class=""` attributes
|
|
47
|
+
- Eliminates duplicate classes automatically
|
|
48
|
+
- Ruby falsy values are only `false` and `nil` (unlike JS, `0`, `''`, `[]`, `{}` are truthy)
|
|
49
|
+
- Ignores `Proc`/lambda objects and boolean `true` values
|
|
50
|
+
- Supports complex hash keys like `{ %w[foo bar] => true }` which resolve recursively
|
|
51
|
+
|
|
52
|
+
## Commit Convention
|
|
53
|
+
|
|
54
|
+
Uses [Conventional Commits](https://www.conventionalcommits.org/): `feat`, `fix`, `perf`, `chore`, `docs`, `refactor`
|
data/README.md
CHANGED
data/lib/clsx/helper.rb
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'set'
|
|
4
|
-
|
|
5
3
|
# :nodoc:
|
|
6
4
|
module Clsx
|
|
7
5
|
# :nodoc:
|
|
8
6
|
module Helper
|
|
9
7
|
# The clsx function can take any number of arguments,
|
|
10
|
-
# each of which can be
|
|
8
|
+
# each of which can be Hash, Array, Boolean, String, or Symbol.
|
|
11
9
|
#
|
|
12
|
-
# **Important
|
|
10
|
+
# **Important**
|
|
11
|
+
# Any falsy values are discarded! Standalone Boolean values are discarded as well.
|
|
13
12
|
#
|
|
14
13
|
# @param [Mixed] args
|
|
15
14
|
#
|
|
@@ -25,33 +24,97 @@ module Clsx
|
|
|
25
24
|
# <div class="<%= clsx('foo', 'bar') %>">
|
|
26
25
|
# <div class="<%= clsx('foo', active: @is_active, 'another-class' => @condition) %>">
|
|
27
26
|
# <%= tag.div class: clsx(%w[foo bar], hidden: @condition) do ... end %>
|
|
27
|
+
#
|
|
28
|
+
# @note Implementation prioritizes performance over readability.
|
|
29
|
+
# Direct class comparisons and explicit conditionals are used
|
|
30
|
+
# instead of more idiomatic Ruby patterns for speed.
|
|
31
|
+
|
|
32
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
28
33
|
def clsx(*args)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
return nil if args.empty?
|
|
35
|
+
|
|
36
|
+
# Fast path: single argument (most common cases)
|
|
37
|
+
if args.size == 1
|
|
38
|
+
arg = args[0]
|
|
39
|
+
klass = arg.class
|
|
40
|
+
|
|
41
|
+
if klass == String
|
|
42
|
+
return arg.empty? ? nil : arg
|
|
43
|
+
elsif klass == Symbol
|
|
44
|
+
return arg.name
|
|
45
|
+
elsif klass == Array && arg.all?(String)
|
|
46
|
+
seen = {}
|
|
47
|
+
arg.each { |s| seen[s] = true unless s.empty? || seen.key?(s) }
|
|
48
|
+
return seen.empty? ? nil : seen.keys.join(' ')
|
|
49
|
+
elsif klass == Hash
|
|
50
|
+
return clsx_simple_hash(arg)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
seen = {}
|
|
55
|
+
clsx_process(args, seen)
|
|
56
|
+
seen.empty? ? nil : seen.keys.join(' ')
|
|
32
57
|
end
|
|
58
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
59
|
+
|
|
33
60
|
alias cn clsx
|
|
34
61
|
|
|
35
62
|
private
|
|
36
63
|
|
|
37
|
-
#
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def clsx_args_processor(*args) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
41
|
-
result = []
|
|
42
|
-
complex_keys = []
|
|
64
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
65
|
+
def clsx_simple_hash(hash)
|
|
66
|
+
return nil if hash.empty?
|
|
43
67
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
next
|
|
47
|
-
|
|
68
|
+
seen = {}
|
|
69
|
+
hash.each do |key, value|
|
|
70
|
+
next unless value
|
|
71
|
+
|
|
72
|
+
klass = key.class
|
|
48
73
|
|
|
49
|
-
|
|
74
|
+
if klass == Symbol
|
|
75
|
+
seen[key.name] = true
|
|
76
|
+
elsif klass == String
|
|
77
|
+
seen[key] = true unless key.empty?
|
|
78
|
+
else
|
|
79
|
+
# Complex key - fall back to full processing
|
|
80
|
+
seen = {}
|
|
81
|
+
clsx_process([hash], seen)
|
|
82
|
+
return seen.empty? ? nil : seen.keys.join(' ')
|
|
83
|
+
end
|
|
50
84
|
end
|
|
51
85
|
|
|
52
|
-
|
|
86
|
+
seen.empty? ? nil : seen.keys.join(' ')
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# rubocop:disable Style/MultipleComparison
|
|
90
|
+
def clsx_process(args, seen)
|
|
91
|
+
deferred = nil
|
|
92
|
+
|
|
93
|
+
args.each do |arg|
|
|
94
|
+
klass = arg.class
|
|
95
|
+
|
|
96
|
+
if klass == String
|
|
97
|
+
seen[arg] = true unless arg.empty? || seen.key?(arg)
|
|
98
|
+
elsif klass == Symbol
|
|
99
|
+
str = arg.name
|
|
100
|
+
seen[str] = true unless seen.key?(str)
|
|
101
|
+
elsif klass == Array
|
|
102
|
+
clsx_process(arg, seen)
|
|
103
|
+
elsif klass == Hash
|
|
104
|
+
arg.each { |key, value| (deferred ||= []) << key if value }
|
|
105
|
+
elsif klass == Integer || klass == Float
|
|
106
|
+
str = arg.to_s
|
|
107
|
+
seen[str] = true unless seen.key?(str)
|
|
108
|
+
elsif klass == NilClass || klass == FalseClass || klass == TrueClass || klass == Proc
|
|
109
|
+
next
|
|
110
|
+
else
|
|
111
|
+
str = arg.to_s
|
|
112
|
+
seen[str] = true unless str.empty? || seen.key?(str)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
53
115
|
|
|
54
|
-
|
|
116
|
+
clsx_process(deferred, seen) if deferred
|
|
55
117
|
end
|
|
118
|
+
# rubocop:enable Style/MultipleComparison, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
56
119
|
end
|
|
57
120
|
end
|
data/lib/clsx/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: clsx-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Leonid Svyatov
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: actionview
|
|
@@ -16,14 +15,14 @@ dependencies:
|
|
|
16
15
|
requirements:
|
|
17
16
|
- - ">="
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
18
|
+
version: '7.1'
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
23
|
- - ">="
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
25
|
+
version: '7.1'
|
|
27
26
|
description: A tiny Rails view helper for constructing CSS class strings conditionally
|
|
28
27
|
email:
|
|
29
28
|
- leonid@svyatov.com
|
|
@@ -32,6 +31,7 @@ extensions: []
|
|
|
32
31
|
extra_rdoc_files: []
|
|
33
32
|
files:
|
|
34
33
|
- CHANGELOG.md
|
|
34
|
+
- CLAUDE.md
|
|
35
35
|
- LICENSE.txt
|
|
36
36
|
- README.md
|
|
37
37
|
- lib/clsx-rails.rb
|
|
@@ -45,7 +45,6 @@ metadata:
|
|
|
45
45
|
source_code_uri: https://github.com/svyatov/clsx-rails
|
|
46
46
|
changelog_uri: https://github.com/svyatov/clsx-rails/blob/main/CHANGELOG.md
|
|
47
47
|
rubygems_mfa_required: 'true'
|
|
48
|
-
post_install_message:
|
|
49
48
|
rdoc_options: []
|
|
50
49
|
require_paths:
|
|
51
50
|
- lib
|
|
@@ -60,8 +59,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
60
59
|
- !ruby/object:Gem::Version
|
|
61
60
|
version: '0'
|
|
62
61
|
requirements: []
|
|
63
|
-
rubygems_version:
|
|
64
|
-
signing_key:
|
|
62
|
+
rubygems_version: 4.0.3
|
|
65
63
|
specification_version: 4
|
|
66
64
|
summary: clsx / classnames for Rails views
|
|
67
65
|
test_files: []
|