clsx-rails 1.0.0 → 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 +55 -3
- data/CLAUDE.md +54 -0
- data/README.md +26 -6
- data/lib/clsx/helper.rb +83 -16
- data/lib/clsx/version.rb +1 -1
- data/lib/clsx-rails.rb +1 -0
- 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
|
@@ -1,5 +1,57 @@
|
|
|
1
|
-
|
|
1
|
+
# Changelog
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and to [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
|
|
6
|
+
|
|
7
|
+
## Unreleased changes
|
|
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
|
+
|
|
30
|
+
## v1.0.1 (2024-03-04)
|
|
31
|
+
|
|
32
|
+
### Performance Improvements
|
|
33
|
+
- Speeds up the performance by 2x [`32236ed`](https://github.com/svyatov/clsx-rails/commit/32236ed)
|
|
34
|
+
|
|
35
|
+
### Chore
|
|
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)
|
|
38
|
+
|
|
39
|
+
### Documentation
|
|
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)
|
|
43
|
+
|
|
44
|
+
### Other
|
|
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)
|
|
52
|
+
|
|
53
|
+
## v1.0.0 (2024-03-03)
|
|
54
|
+
|
|
55
|
+
### Other
|
|
56
|
+
- Initial commit [`f65b9b8`](https://github.com/svyatov/clsx-rails/commit/f65b9b8)
|
|
4
57
|
|
|
5
|
-
- Initial release
|
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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# clsx-rails
|
|
1
|
+
# clsx-rails [](https://rubygems.org/gems/clsx-rails) [](https://app.codecov.io/gh/svyatov/clsx-rails) [](https://github.com/svyatov/clsx-rails/actions?query=workflow%3ACI) [](LICENSE.txt)
|
|
2
2
|
|
|
3
3
|
> A tiny Rails view helper for constructing CSS class strings conditionally.
|
|
4
4
|
|
|
@@ -6,21 +6,29 @@ This gem is essentially a clone if the [clsx](https://github.com/lukeed/clsx) np
|
|
|
6
6
|
It provides a simple way to conditionally apply classes to HTML elements in Rails views.
|
|
7
7
|
It is especially useful when you have a lot of conditional classes and you want to keep your views clean and readable.
|
|
8
8
|
|
|
9
|
+
## Supported Ruby and Rails versions
|
|
10
|
+
|
|
11
|
+
Ruby 3.1+ and Rails 7.1+ are supported.
|
|
12
|
+
|
|
9
13
|
## Installation
|
|
10
14
|
|
|
11
15
|
Install the gem and add to the application's Gemfile by executing:
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
```bash
|
|
18
|
+
bundle add clsx-rails
|
|
19
|
+
```
|
|
14
20
|
|
|
15
|
-
|
|
21
|
+
Or add it manually to the Gemfile:
|
|
16
22
|
|
|
17
|
-
|
|
23
|
+
```ruby
|
|
24
|
+
gem 'clsx-rails', '~> 1.0'
|
|
25
|
+
```
|
|
18
26
|
|
|
19
27
|
## Usage
|
|
20
28
|
|
|
21
|
-
The `clsx` method can be used in views to conditionally apply classes to HTML elements.
|
|
29
|
+
The `clsx` helper method can be used in views to conditionally apply classes to HTML elements.
|
|
22
30
|
You can also use a slightly more conise `cn` alias.
|
|
23
|
-
It accepts a variety of arguments and returns a string of classes.
|
|
31
|
+
It accepts a variety of arguments and returns a string of unique classes.
|
|
24
32
|
|
|
25
33
|
```ruby
|
|
26
34
|
# Strings (variadic)
|
|
@@ -151,6 +159,18 @@ There is a simple benchmark script in the `benchmark` directory.
|
|
|
151
159
|
You can run it with `bundle exec ruby benchmark/run.rb`.
|
|
152
160
|
I've added it for easier performance testing when making changes to the gem.
|
|
153
161
|
|
|
162
|
+
## Conventional Commits
|
|
163
|
+
|
|
164
|
+
This project uses [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for commit messages.
|
|
165
|
+
|
|
166
|
+
Types of commits are:
|
|
167
|
+
- `feat`: a new feature
|
|
168
|
+
- `fix`: a bug fix
|
|
169
|
+
- `perf`: code that improves performance
|
|
170
|
+
- `chore`: updating build tasks, configs, formatting etc; no code change
|
|
171
|
+
- `docs`: changes to documentation
|
|
172
|
+
- `refactor`: refactoring code
|
|
173
|
+
|
|
154
174
|
## Contributing
|
|
155
175
|
|
|
156
176
|
Bug reports and pull requests are welcome on GitHub at https://github.com/svyatov/clsx-rails.
|
data/lib/clsx/helper.rb
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'set'
|
|
4
|
-
require 'action_view'
|
|
5
|
-
|
|
6
3
|
# :nodoc:
|
|
7
4
|
module Clsx
|
|
8
5
|
# :nodoc:
|
|
9
6
|
module Helper
|
|
10
7
|
# The clsx function can take any number of arguments,
|
|
11
|
-
# each of which can be
|
|
8
|
+
# each of which can be Hash, Array, Boolean, String, or Symbol.
|
|
12
9
|
#
|
|
13
|
-
# **Important
|
|
10
|
+
# **Important**
|
|
11
|
+
# Any falsy values are discarded! Standalone Boolean values are discarded as well.
|
|
14
12
|
#
|
|
15
13
|
# @param [Mixed] args
|
|
16
14
|
#
|
|
@@ -26,28 +24,97 @@ module Clsx
|
|
|
26
24
|
# <div class="<%= clsx('foo', 'bar') %>">
|
|
27
25
|
# <div class="<%= clsx('foo', active: @is_active, 'another-class' => @condition) %>">
|
|
28
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
|
|
29
33
|
def clsx(*args)
|
|
30
|
-
|
|
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(' ')
|
|
31
57
|
end
|
|
58
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
59
|
+
|
|
32
60
|
alias cn clsx
|
|
33
61
|
|
|
34
62
|
private
|
|
35
63
|
|
|
36
|
-
#
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
64
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
65
|
+
def clsx_simple_hash(hash)
|
|
66
|
+
return nil if hash.empty?
|
|
67
|
+
|
|
68
|
+
seen = {}
|
|
69
|
+
hash.each do |key, value|
|
|
70
|
+
next unless value
|
|
71
|
+
|
|
72
|
+
klass = key.class
|
|
73
|
+
|
|
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
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
seen.empty? ? nil : seen.keys.join(' ')
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# rubocop:disable Style/MultipleComparison
|
|
90
|
+
def clsx_process(args, seen)
|
|
91
|
+
deferred = nil
|
|
42
92
|
|
|
43
93
|
args.each do |arg|
|
|
44
|
-
|
|
45
|
-
next result << arg.to_s unless arg.is_a?(Hash)
|
|
94
|
+
klass = arg.class
|
|
46
95
|
|
|
47
|
-
|
|
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
|
|
48
114
|
end
|
|
49
115
|
|
|
50
|
-
|
|
116
|
+
clsx_process(deferred, seen) if deferred
|
|
51
117
|
end
|
|
118
|
+
# rubocop:enable Style/MultipleComparison, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
52
119
|
end
|
|
53
120
|
end
|
data/lib/clsx/version.rb
CHANGED
data/lib/clsx-rails.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: []
|