stimulus_helpers 0.1.0 → 0.3.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: dd7023b93ba3d485fbea28c5eee63938fab133d3a142d40494e0bf37b7216ab1
4
- data.tar.gz: 61bd7b0845434ceffeece2f56560207c80f220646622abf207dde1fb79434cf8
3
+ metadata.gz: 583aa220b4b5c76f2f63dc736c29bc28a4cd48d1552a44c282d9f9cd75d4cc2d
4
+ data.tar.gz: 6fcc67385226a12b6018041d3df754bf93fe53b2306d87a917c84421c16fa9cf
5
5
  SHA512:
6
- metadata.gz: 2d5a7e5abb4d4263004243f8967f9d1f04cc1b360acd68ca13359286a79928f6ce8407dc08962325d96e2bf146dece12193187bd7dcc9aa83f6bc42916723ab0
7
- data.tar.gz: 22852bd7e2403fe4acc820cd7626a22caf3ca834f310a17a9751f9c003f7d91b497ad8065b988781cdcdc77f06c127b882904c021831dfb556f5762bc665cf23
6
+ metadata.gz: 2d957847070045995c509a2a4f53ee5d2e7f8deeb90d26c5654fc1a289aca8fcf79cd3eec008052f2b39b332b01b1aab8617e73639fd818909a68e135734a258
7
+ data.tar.gz: e98551bb3950d30266ef7b83779612c708c868c10a6c7c919057f323361f739a95a5f7a40bffd760f30f98acd536abad4d2b03fb060e90899abdc5fbc303e845
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.1.2
1
+ 4.0.1
data/CHANGELOG.md CHANGED
@@ -0,0 +1,34 @@
1
+ # Changelog
2
+
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.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.3.0] - 2026-04-18
9
+
10
+ ### Performance
11
+ - Drop the `Mutex` from `cached_dasherize`. The cached operation is deterministic (same input always yields same output), so a concurrent duplicate compute produces the same value and the overwrite is harmless. Ruby Hash writes are atomic enough at the VM level for this pattern. Saves a `Mutex#synchronize` block invocation on every call — hundreds per heavy page render in consumer apps.
12
+
13
+ ## [0.2.0] - 2025-01-06
14
+
15
+ ### Performance
16
+ - Optimize string operations and concatenations for large attribute sets
17
+ - Add caching for dasherized keys to avoid repeated string transformations
18
+ - Implement batch JSON serialization to reduce overhead
19
+ - Improve action building performance by replacing array operations with direct string building
20
+
21
+ ## [0.1.0] - 2024-XX-XX
22
+
23
+ ### Added
24
+ - Initial release of stimulus_helpers gem
25
+ - Helper methods for Stimulus controller attributes:
26
+ - `stimulus_controller(name)` - Controller binding
27
+ - `stimulus_action(controller, action, listener)` and `stimulus_actions(controller, actions)` - Action bindings
28
+ - `stimulus_class(controller, name, value)` and `stimulus_classes(controller, classes)` - CSS class bindings
29
+ - `stimulus_value(controller, name, value)` and `stimulus_values(controller, values)` - Value bindings
30
+ - `stimulus_target(controller, target)` - Target bindings
31
+ - `stimulus_param(controller, name, value)` and `stimulus_params(controller, params)` - Parameter bindings
32
+ - `stimulus_outlet(controller, name, value)` and `stimulus_outlets(controller, outlets)` - Outlet bindings
33
+ - Automatic JSON serialization for complex values (Arrays and Hashes)
34
+ - Automatic dasherization of attribute names following Stimulus conventions
data/CLAUDE.md ADDED
@@ -0,0 +1,137 @@
1
+ # Instructions for Claude Code
2
+
3
+ ## Project Overview
4
+
5
+ This is the `stimulus_helpers` Ruby gem - a utility library that provides helper methods for building Stimulus controller attributes in Ruby views. The gem makes it easier to work with Stimulus.js by providing a clean Ruby API for generating the necessary HTML attributes.
6
+
7
+ ## Key Information
8
+
9
+ - **Gem Name**: stimulus_helpers
10
+ - **Current Version**: 0.1.0
11
+ - **Ruby Version**: 3.1.2
12
+ - **License**: MIT
13
+ - **Author**: Tomáš Celizna
14
+
15
+ ## Project Structure
16
+
17
+ ```
18
+ stimulus_helpers/
19
+ ├── lib/
20
+ │ ├── stimulus_helpers.rb # Main module with all helper methods
21
+ │ └── stimulus_helpers/
22
+ │ └── version.rb # Version constant
23
+ ├── test/
24
+ │ ├── stimulus_helpers_test.rb # Test suite
25
+ │ └── test_helper.rb # Test configuration
26
+ ├── stimulus_helpers.gemspec # Gem specification
27
+ ├── Gemfile # Dependencies
28
+ ├── Rakefile # Build tasks
29
+ └── README.md # Documentation
30
+ ```
31
+
32
+ ## Development Workflow
33
+
34
+ ### Running Tests
35
+ ```bash
36
+ bundle exec rake test
37
+ # or simply
38
+ bundle exec rake
39
+ ```
40
+
41
+ ### Running Linter
42
+ ```bash
43
+ bundle exec rubocop
44
+ ```
45
+
46
+ ### Installing Dependencies
47
+ ```bash
48
+ bundle install
49
+ ```
50
+
51
+ ### Building the Gem
52
+ ```bash
53
+ bundle exec rake build
54
+ ```
55
+
56
+ ### Releasing a New Version
57
+ 1. Update the version number in `lib/stimulus_helpers/version.rb`
58
+ 2. Update CHANGELOG.md with the changes
59
+ 3. Commit the changes
60
+ 4. Run `bundle exec rake release`
61
+
62
+ ## Code Style Guidelines
63
+
64
+ - Follow the RuboCop configuration in `.rubocop.yml`
65
+ - Use Ruby 3.1+ syntax features (like value omission in hash literals)
66
+ - Keep methods focused and well-documented
67
+ - Maintain test coverage for all public methods
68
+
69
+ ## Helper Methods Overview
70
+
71
+ The gem provides the following helper methods:
72
+
73
+ 1. **Controller**: `stimulus_controller(name)`
74
+ 2. **Actions**: `stimulus_action(controller, action, listener)` and `stimulus_actions(controller, actions)`
75
+ 3. **Classes**: `stimulus_class(controller, name, value)` and `stimulus_classes(controller, classes)`
76
+ 4. **Values**: `stimulus_value(controller, name, value)` and `stimulus_values(controller, values)`
77
+ 5. **Target**: `stimulus_target(controller, target)`
78
+ 6. **Params**: `stimulus_param(controller, name, value)` and `stimulus_params(controller, params)`
79
+ 7. **Outlets**: `stimulus_outlet(controller, name, value)` and `stimulus_outlets(controller, outlets)`
80
+
81
+ ## Implementation Notes
82
+
83
+ - The main module uses `build_stimulus_action` and `build_stimulus_attribute` private methods to construct the attribute hashes
84
+ - Complex values (Arrays and Hashes) are automatically converted to JSON
85
+ - Attribute names are dasherized to follow Stimulus conventions
86
+ - The gem depends on ActiveSupport for string inflections
87
+
88
+ ## Testing Approach
89
+
90
+ - Tests are in `test/stimulus_helpers_test.rb`
91
+ - Each helper method has corresponding test coverage
92
+ - Tests verify the exact structure of returned hashes
93
+ - Run tests with `bundle exec rake test`
94
+
95
+ ## Common Tasks
96
+
97
+ ### Adding a New Helper Method
98
+
99
+ 1. Add the public method to `lib/stimulus_helpers.rb`
100
+ 2. Use the existing private methods (`build_stimulus_action` or `build_stimulus_attribute`) if applicable
101
+ 3. Add corresponding tests to `test/stimulus_helpers_test.rb`
102
+ 4. Update the README.md with usage examples
103
+ 5. Run tests and linter to ensure everything passes
104
+
105
+ ### Debugging
106
+
107
+ - Use `bin/console` to start an IRB session with the gem loaded
108
+ - The test suite uses Minitest - add debugging with `puts` or use a debugger gem
109
+ - Check the GitHub Actions workflow results for CI failures
110
+
111
+ ## Important Considerations
112
+
113
+ 1. **Backwards Compatibility**: This gem is used by other projects, so maintain backwards compatibility when making changes
114
+ 2. **Dependencies**: Keep dependencies minimal - currently only requires ActiveSupport
115
+ 3. **Documentation**: Update README.md when adding new features
116
+ 4. **Versioning**: Follow semantic versioning (MAJOR.MINOR.PATCH)
117
+
118
+ ## Git Workflow
119
+
120
+ - The main branch is protected
121
+ - Create feature branches for new work
122
+ - Ensure all tests pass before merging
123
+ - The GitHub Actions workflow runs on every push
124
+
125
+ ## Troubleshooting
126
+
127
+ - If tests fail locally but pass in CI, check Ruby version (should be 3.1.2)
128
+ - RuboCop violations can often be auto-fixed with `bundle exec rubocop -A`
129
+ - Bundle install issues: try `bundle update` or delete `Gemfile.lock` and reinstall
130
+
131
+ ## Future Improvements
132
+
133
+ Based on the current codebase, potential areas for enhancement:
134
+ - Add more comprehensive documentation/examples
135
+ - Consider adding type signatures (RBS file is currently minimal)
136
+ - Add more edge case testing
137
+ - Consider performance optimizations for large attribute sets
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![StimulusHelpers](https://github.com/tomasc/stimulus_helpers/actions/workflows/ruby.yml/badge.svg)](https://github.com/tomasc/stimulus_helpers/actions/workflows/ruby.yml)
4
4
 
5
- Helpers to build stimulus controller attributes for use in views and components.
5
+ Helpers to build stimulus controller attributes for use in views and controller-names.
6
6
 
7
7
  ## Installation
8
8
 
@@ -32,42 +32,68 @@ ActionView::Base.send :include, StimulusHelpers
32
32
 
33
33
  This will add the following helpers:
34
34
 
35
+ Controller:
36
+
35
37
  ```ruby
36
- stimulus_controller("component")
37
- # => { "controller" => "component" }
38
+ stimulus_controller("controller-name")
39
+ # => { "controller" => "controller-name" }
40
+ ```
38
41
 
39
- stimulus_action("component", "click", "open")
40
- # => { "action" => "click->component#open" }
42
+ Action:
41
43
 
42
- stimulus_actions("component", click: "open", blur: "close")
43
- # => { "action" => "click->component#open blur->component#close" }
44
+ ```ruby
45
+ stimulus_action("controller-name", "click", "open")
46
+ # => { "action" => "click->controller-name#open" }
47
+
48
+ stimulus_actions("controller-name", click: "open", blur: "close")
49
+ # => { "action" => "click->controller-name#open blur->controller-name#close" }
50
+ ```
44
51
 
45
- stimulus_class("component", "open", "component--open")
46
- # => { "component-open-class" => "component--open" }
52
+ Class:
47
53
 
48
- stimulus_classes("component", open: "component--open", closed: "component--closed")
49
- # => { "component-open-class" => "component--open", "component-closed-class" => "component--closed" }
54
+ ```ruby
55
+ stimulus_class("controller-name", "open", "controller-name--open")
56
+ # => { "controller-name-open-class" => "controller-name--open" }
50
57
 
51
- stimulus_value("component", "open", true)
52
- # => { "component-open-value" => "true" }
58
+ stimulus_classes("controller-name", open: "controller-name--open", closed: "controller-name--closed")
59
+ # => { "controller-name-open-class" => "controller-name--open", "controller-name-closed-class" => "controller-name--closed" }
60
+ ```
53
61
 
54
- stimulus_values("component", user: { name: "Jens" }, names: ["foo", "bar"])
55
- # => { "component-user-value" => "{\"name\":\"Jens\"}", "component-names-value" => "[\"foo\",\"bar\"]" }
62
+ Value:
56
63
 
57
- stimulus_target("component", :input)
58
- # => { "component-target" => "input" }
64
+ ```ruby
65
+ stimulus_value("controller-name", "open", true)
66
+ # => { "controller-name-open-value" => "true" }
59
67
 
60
- stimulus_param("component", "id", 123)
61
- # => { "component-id-param" => "123" }
68
+ stimulus_values("controller-name", user: { name: "Jens" }, names: ["foo", "bar"])
69
+ # => { "controller-name-user-value" => "{\"name\":\"Jens\"}", "controller-name-names-value" => "[\"foo\",\"bar\"]" }
70
+ ```
62
71
 
63
- stimulus_params("component", id: 123, name: "Jens")
64
- # => { "component-id-param" => "123", "component-name-param" => "Jens" }
72
+ Target:
65
73
 
66
- stimulus_outlet("component", "result", ".result")
67
- # => { "component-result-outlet" => ".result" }
74
+ ```ruby
75
+ stimulus_target("controller-name", :input)
76
+ # => { "controller-name-target" => "input" }
77
+ ```
78
+
79
+ Param:
80
+
81
+ ```ruby
82
+ stimulus_param("controller-name", "id", 123)
83
+ # => { "controller-name-id-param" => "123" }
84
+
85
+ stimulus_params("controller-name", id: 123, name: "Jens")
86
+ # => { "controller-name-id-param" => "123", "controller-name-name-param" => "Jens" }
87
+ ```
88
+
89
+ Outlet:
90
+
91
+ ```ruby
92
+ stimulus_outlet("controller-name", "result", ".result")
93
+ # => { "controller-name-result-outlet" => ".result" }
68
94
 
69
- stimulus_outlets("component", result: ".result", output: ".output")
70
- # => { "component-result-outlet" => ".result", "component-output-outlet" => ".output" }
95
+ stimulus_outlets("controller-name", result: ".result", output: ".output")
96
+ # => { "controller-name-result-outlet" => ".result", "controller-name-output-outlet" => ".output" }
71
97
  ```
72
98
 
73
99
  ## Development
@@ -78,4 +104,4 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
78
104
 
79
105
  ## Contributing
80
106
 
81
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/stimulus_helpers.
107
+ Bug reports and pull requests are welcome on GitHub at https://github.com/tomasc/stimulus_helpers.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StimulusHelpers
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -5,6 +5,16 @@ require "active_support/core_ext/string/inflections"
5
5
  require "json"
6
6
 
7
7
  module StimulusHelpers
8
+ # Cache for dasherized keys to avoid repeated string operations.
9
+ # No mutex: writes are idempotent (same input → same output), so a
10
+ # concurrent race just does the work twice and overwrites harmlessly.
11
+ # Ruby Hash writes are atomic at the VM level for this simple case.
12
+ @dasherized_cache = {}
13
+
14
+ class << self
15
+ attr_reader :dasherized_cache
16
+ end
17
+
8
18
  # @see https://blog.saeloun.com/2021/09/28/ruby-allow-value-omission-in-hash-literals
9
19
  def stimulus_controller(controller)
10
20
  { controller: }
@@ -91,19 +101,46 @@ module StimulusHelpers
91
101
 
92
102
  private
93
103
  def build_stimulus_action(controller:, actions: {})
94
- action = actions.map do |action, listeners|
95
- Array(listeners).map do |listener|
96
- "#{action}->#{controller}##{listener}"
104
+ # Use string concatenation instead of array operations for better performance
105
+ action_parts = []
106
+ actions.each do |action_name, listeners|
107
+ Array(listeners).each do |listener|
108
+ action_parts << "#{action_name}->#{controller}##{listener}"
97
109
  end
98
- end.join(" ")
99
- { action: }
110
+ end
111
+ { action: action_parts.join(" ") }
100
112
  end
101
113
 
102
114
  def build_stimulus_attribute(controller:, type: nil, attributes: {})
103
- attributes.transform_keys! { |key| "#{key}-#{type}" } if type
104
- attributes.each_with_object({}) do |(key, value), res|
105
- value = value.to_json if value.is_a?(Array) || value.is_a?(Hash)
106
- res["#{controller}-#{key.to_s.dasherize}"] = value.to_s
115
+ # Pre-process keys and batch JSON serialization for better performance
116
+ result = {}
117
+ json_values = {}
118
+
119
+ attributes.each do |key, value|
120
+ # Build the final key once
121
+ final_key = if type
122
+ "#{controller}-#{cached_dasherize("#{key}-#{type}")}"
123
+ else
124
+ "#{controller}-#{cached_dasherize(key.to_s)}"
125
+ end
126
+
127
+ # Batch JSON serialization
128
+ if value.is_a?(Array) || value.is_a?(Hash)
129
+ json_values[final_key] = value
130
+ else
131
+ result[final_key] = value.to_s
132
+ end
133
+ end
134
+
135
+ # Serialize all JSON values at once to reduce overhead
136
+ json_values.each do |key, value|
137
+ result[key] = value.to_json
107
138
  end
139
+
140
+ result
141
+ end
142
+
143
+ def cached_dasherize(string)
144
+ StimulusHelpers.dasherized_cache[string] ||= string.dasherize
108
145
  end
109
146
  end
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
30
30
 
31
31
  spec.add_development_dependency "bundler"
32
32
  spec.add_development_dependency "rake"
33
- spec.add_development_dependency "minitest", "~> 5.0"
33
+ spec.add_development_dependency "minitest", "~> 6.0"
34
34
 
35
35
  spec.add_development_dependency "lefthook"
36
36
  spec.add_development_dependency "rubocop-rails_config"
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stimulus_helpers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tomas Celizna
8
8
  - Asger Behncke Jacobsen
9
- autorequire:
10
9
  bindir: exe
11
10
  cert_chain: []
12
- date: 2023-03-11 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: activesupport
@@ -59,14 +58,14 @@ dependencies:
59
58
  requirements:
60
59
  - - "~>"
61
60
  - !ruby/object:Gem::Version
62
- version: '5.0'
61
+ version: '6.0'
63
62
  type: :development
64
63
  prerelease: false
65
64
  version_requirements: !ruby/object:Gem::Requirement
66
65
  requirements:
67
66
  - - "~>"
68
67
  - !ruby/object:Gem::Version
69
- version: '5.0'
68
+ version: '6.0'
70
69
  - !ruby/object:Gem::Dependency
71
70
  name: lefthook
72
71
  requirement: !ruby/object:Gem::Requirement
@@ -106,6 +105,7 @@ files:
106
105
  - ".rubocop.yml"
107
106
  - ".ruby-version"
108
107
  - CHANGELOG.md
108
+ - CLAUDE.md
109
109
  - Gemfile
110
110
  - LICENSE
111
111
  - README.md
@@ -121,7 +121,6 @@ metadata:
121
121
  homepage_uri: https://github.com/tomasc/stimulus_helpers
122
122
  source_code_uri: https://github.com/tomasc/stimulus_helpers
123
123
  changelog_uri: https://github.com/tomasc/stimulus_helpers/CHANGELOG.md
124
- post_install_message:
125
124
  rdoc_options: []
126
125
  require_paths:
127
126
  - lib
@@ -136,8 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
136
135
  - !ruby/object:Gem::Version
137
136
  version: '0'
138
137
  requirements: []
139
- rubygems_version: 3.3.7
140
- signing_key:
138
+ rubygems_version: 4.0.5
141
139
  specification_version: 4
142
140
  summary: Helper methods for stimulus controllers.
143
141
  test_files: []