attrify 0.4.0 → 0.5.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: c15da6170447306522cb53c74db61937e5fc59d3e5a8619d40142c253be2ab7a
4
- data.tar.gz: 70c850497d0d5ceec7a8e5693e806494477303bde85030bd3da862e98eead7c8
3
+ metadata.gz: 9ac6216b57e120efe2b749bf0d4365d5b179136311de1efda843334a0bde6492
4
+ data.tar.gz: 794a2357fa54ceadd98daa22066e5594f06afc4b750e4657eb13ae836712cd8b
5
5
  SHA512:
6
- metadata.gz: 512a4466eae20886eff9b71a615c7a08f251a3691fad2af0f8ec46ddec6fe9c039bdba4d27c56c61da909e95e5c8dfb4ecbde22f559aca151ce3c0a0fbbcfc7a
7
- data.tar.gz: 7f2726c8394496baa5e7e4e7f5a2b4a70d3ec81495e725e44291db36a77b439c6993626c08800a4697a4c5223dd522bed3027820053088050c9107b43d54aafd
6
+ metadata.gz: 68450882d13145bb7100673ee58142a17fdb905117da8223ae84001c43b5ae2d3043305bf394db031654b10d66342c5a2d7b0a11bf8ab4c635c6571a501320de
7
+ data.tar.gz: e64f4ecddcbb5761f8d24641c0c6d1e6bcc0cdba568860553112d6a8d5cb64184d71da4361ec8a9b65c9f3807982a06c19aae475e32e46e8417a2a60d522db4d
data/Gemfile.lock CHANGED
@@ -1,122 +1,132 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- attrify (0.4.0)
4
+ attrify (0.4.5)
5
5
  actionview (> 7.0)
6
6
  activesupport (> 7.0)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- actionview (7.1.4)
12
- activesupport (= 7.1.4)
11
+ actionview (8.0.2)
12
+ activesupport (= 8.0.2)
13
13
  builder (~> 3.1)
14
14
  erubi (~> 1.11)
15
15
  rails-dom-testing (~> 2.2)
16
16
  rails-html-sanitizer (~> 1.6)
17
- activesupport (7.1.4)
17
+ activesupport (8.0.2)
18
18
  base64
19
+ benchmark (>= 0.3)
19
20
  bigdecimal
20
- concurrent-ruby (~> 1.0, >= 1.0.2)
21
+ concurrent-ruby (~> 1.0, >= 1.3.1)
21
22
  connection_pool (>= 2.2.5)
22
23
  drb
23
24
  i18n (>= 1.6, < 2)
25
+ logger (>= 1.4.2)
24
26
  minitest (>= 5.1)
25
- mutex_m
26
- tzinfo (~> 2.0)
27
- ast (2.4.2)
27
+ securerandom (>= 0.3)
28
+ tzinfo (~> 2.0, >= 2.0.5)
29
+ uri (>= 0.13.1)
30
+ ast (2.4.3)
28
31
  base64 (0.2.0)
32
+ benchmark (0.4.0)
29
33
  benchmark-ips (2.14.0)
30
- bigdecimal (3.1.8)
34
+ bigdecimal (3.1.9)
31
35
  builder (3.3.0)
32
- concurrent-ruby (1.3.4)
33
- connection_pool (2.4.1)
36
+ concurrent-ruby (1.3.5)
37
+ connection_pool (2.5.0)
34
38
  crass (1.0.6)
35
- diff-lcs (1.5.1)
39
+ diff-lcs (1.6.1)
36
40
  drb (2.2.1)
37
- erubi (1.13.0)
38
- i18n (1.14.6)
41
+ erubi (1.13.1)
42
+ i18n (1.14.7)
39
43
  concurrent-ruby (~> 1.0)
40
- json (2.7.2)
41
- language_server-protocol (3.17.0.3)
44
+ json (2.10.2)
45
+ language_server-protocol (3.17.0.4)
42
46
  lint_roller (1.1.0)
43
- loofah (2.22.0)
47
+ logger (1.7.0)
48
+ loofah (2.24.0)
44
49
  crass (~> 1.0.2)
45
50
  nokogiri (>= 1.12.0)
46
- minitest (5.25.1)
47
- mutex_m (0.2.0)
48
- nokogiri (1.15.6-x86_64-linux)
51
+ minitest (5.25.5)
52
+ nokogiri (1.18.6-x86_64-linux-gnu)
49
53
  racc (~> 1.4)
50
54
  parallel (1.26.3)
51
- parser (3.3.5.0)
55
+ parser (3.3.7.3)
52
56
  ast (~> 2.4.1)
53
57
  racc
58
+ prism (1.4.0)
54
59
  racc (1.8.1)
55
- rack (3.1.7)
60
+ rack (3.1.12)
56
61
  rails-dom-testing (2.2.0)
57
62
  activesupport (>= 5.0.0)
58
63
  minitest
59
64
  nokogiri (>= 1.6)
60
- rails-html-sanitizer (1.6.0)
65
+ rails-html-sanitizer (1.6.2)
61
66
  loofah (~> 2.21)
62
- nokogiri (~> 1.14)
67
+ nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
63
68
  rainbow (3.1.1)
64
69
  rake (13.2.1)
65
- regexp_parser (2.9.2)
66
- rexml (3.3.7)
70
+ regexp_parser (2.10.0)
67
71
  rspec (3.13.0)
68
72
  rspec-core (~> 3.13.0)
69
73
  rspec-expectations (~> 3.13.0)
70
74
  rspec-mocks (~> 3.13.0)
71
- rspec-core (3.13.1)
75
+ rspec-core (3.13.3)
72
76
  rspec-support (~> 3.13.0)
73
77
  rspec-expectations (3.13.3)
74
78
  diff-lcs (>= 1.2.0, < 2.0)
75
79
  rspec-support (~> 3.13.0)
76
- rspec-mocks (3.13.1)
80
+ rspec-mocks (3.13.2)
77
81
  diff-lcs (>= 1.2.0, < 2.0)
78
82
  rspec-support (~> 3.13.0)
79
- rspec-support (3.13.1)
80
- rubocop (1.64.1)
83
+ rspec-support (3.13.2)
84
+ rubocop (1.73.2)
81
85
  json (~> 2.3)
82
- language_server-protocol (>= 3.17.0)
86
+ language_server-protocol (~> 3.17.0.2)
87
+ lint_roller (~> 1.1.0)
83
88
  parallel (~> 1.10)
84
89
  parser (>= 3.3.0.2)
85
90
  rainbow (>= 2.2.2, < 4.0)
86
- regexp_parser (>= 1.8, < 3.0)
87
- rexml (>= 3.2.5, < 4.0)
88
- rubocop-ast (>= 1.31.1, < 2.0)
91
+ regexp_parser (>= 2.9.3, < 3.0)
92
+ rubocop-ast (>= 1.38.0, < 2.0)
89
93
  ruby-progressbar (~> 1.7)
90
- unicode-display_width (>= 2.4.0, < 3.0)
91
- rubocop-ast (1.32.3)
92
- parser (>= 3.3.1.0)
93
- rubocop-performance (1.21.1)
94
- rubocop (>= 1.48.1, < 2.0)
95
- rubocop-ast (>= 1.31.1, < 2.0)
96
- rubocop-rails (2.23.1)
94
+ unicode-display_width (>= 2.4.0, < 4.0)
95
+ rubocop-ast (1.43.0)
96
+ parser (>= 3.3.7.2)
97
+ prism (~> 1.4)
98
+ rubocop-performance (1.24.0)
99
+ lint_roller (~> 1.1)
100
+ rubocop (>= 1.72.1, < 2.0)
101
+ rubocop-ast (>= 1.38.0, < 2.0)
102
+ rubocop-rails (2.26.2)
97
103
  activesupport (>= 4.2.0)
98
104
  rack (>= 1.1)
99
- rubocop (>= 1.33.0, < 2.0)
100
- rubocop-ast (>= 1.30.0, < 2.0)
105
+ rubocop (>= 1.52.0, < 2.0)
106
+ rubocop-ast (>= 1.31.1, < 2.0)
101
107
  ruby-progressbar (1.13.0)
102
- standard (1.37.0)
108
+ securerandom (0.4.1)
109
+ standard (1.47.0)
103
110
  language_server-protocol (~> 3.17.0.2)
104
111
  lint_roller (~> 1.0)
105
- rubocop (~> 1.64.0)
112
+ rubocop (~> 1.73.0)
106
113
  standard-custom (~> 1.0.0)
107
- standard-performance (~> 1.4)
114
+ standard-performance (~> 1.7)
108
115
  standard-custom (1.0.2)
109
116
  lint_roller (~> 1.0)
110
117
  rubocop (~> 1.50)
111
- standard-performance (1.4.0)
118
+ standard-performance (1.7.0)
112
119
  lint_roller (~> 1.1)
113
- rubocop-performance (~> 1.21.0)
114
- standard-rails (1.0.2)
120
+ rubocop-performance (~> 1.24.0)
121
+ standard-rails (1.2.0)
115
122
  lint_roller (~> 1.0)
116
- rubocop-rails (~> 2.23.1)
123
+ rubocop-rails (~> 2.26.0)
117
124
  tzinfo (2.0.6)
118
125
  concurrent-ruby (~> 1.0)
119
- unicode-display_width (2.6.0)
126
+ unicode-display_width (3.1.4)
127
+ unicode-emoji (~> 4.0, >= 4.0.4)
128
+ unicode-emoji (4.0.4)
129
+ uri (1.0.3)
120
130
 
121
131
  PLATFORMS
122
132
  x86_64-linux
data/README.md CHANGED
@@ -29,46 +29,46 @@ class BaseComponent
29
29
 
30
30
  # Optional: pass keyword arguments from the initializer directly to the attributes API
31
31
  def initialize(**args)
32
- with_attributes(**args)
32
+ variant.set(**args)
33
33
  end
34
34
  end
35
35
  ```
36
36
 
37
- Define attributes for your class:
37
+ Define variants for your class:
38
38
  ```ruby
39
39
  class Button < BaseComponent
40
- attributes {
40
+ variants do
41
41
  base id: ->{ "button_#{SecureRandom.uuid}}"},
42
42
  class: %w[inline-flex items-center rounded],
43
43
  data: { controller: "button_controller" }
44
44
 
45
- variant(:color) {
45
+ variant(:color) do
46
46
  primary class: { append: %w[bg-blue-500 text-white] }
47
47
  secondary class: { append: %w[bg-gray-500 text-white shadow-sm] }
48
48
  danger class: { append: %w[bg-red-500 text-white] }
49
- }
49
+ end
50
50
 
51
- variant(:size) {
51
+ variant(:size) do
52
52
  xs class: { append: %w[text-xs h-7 px-3 py-1] }
53
53
  sm class: { append: %w[text-sm h-8 px-3.5 py-1.5] }
54
54
  md class: { append: %w[text-sm h-9 px-4 py-2] }
55
55
  lg class: { append: %w[text-base h-10 px-5 py-2.5] }
56
56
  xl class: { append: %w[text-lg h-11 px-6 py-3] }
57
- }
57
+ end
58
58
 
59
59
  default color: :primary, size: :md
60
- }
60
+ end
61
61
  end
62
62
  ```
63
63
 
64
64
  Add the attributes to your component's html
65
65
  ```erb
66
- <button <%= attribs() %>> <%= content %> </button>
66
+ <button <%= attributes %>> <%= content %> </button>
67
67
  ```
68
68
 
69
69
  Try it out!
70
70
  ```ruby
71
- # Use the default attributes
71
+ # Use the default variant
72
72
  Button.new() { "Click Me" }
73
73
 
74
74
  # Use a predefined color and size
@@ -112,49 +112,36 @@ Slots allow you to define different attributes for different parts of a componen
112
112
 
113
113
  ```ruby
114
114
  class Card < BaseComponent
115
- attributes {
116
- base {
115
+ variants do
116
+ base do
117
117
  slot :card, id: ->{"card_#{id}"}, class: %w[rounded-md border]
118
118
  slot :header, class: %w[bg-gray-100 border-b]
119
119
  slot :body, class: %w[]
120
120
  slot :footer, class: %w[border-t]
121
- }
121
+ end
122
122
 
123
- variant(:color) {
124
- primary {
123
+ variant(:color) do
124
+ primary do
125
125
  slot :header, class: { append: %w[bg-blue-500] }
126
126
  slot :footer, class: { append: %w[bg-blue-500] }
127
- }
128
- danger {
127
+ end
128
+ danger do
129
129
  slot :header, class: { append: %w[bg-red-500] }
130
130
  slot :footer, class: { append: %w[bg-red-500] }
131
- }
132
- }
133
-
134
- # Wildcard applies to all slots
135
- variant(:padding) {
136
- default {
137
- slot *, class: { append: %w[p-4] }
138
- }
139
- condensed {
140
- slot *, class: { append: %w[px-4 py-2] }
141
- }
142
- spacious {
143
- slot *, class: { append: %w[p-6] }
144
- }
145
- }
146
-
147
- default padding: :default
148
- }
131
+ end
132
+ end
133
+
134
+ default color: :primary
135
+ end
149
136
  end
150
137
  ```
151
138
 
152
139
  Use it in your html
153
140
  ```erb
154
- <div <%= attribs(slot: :card) %>>
155
- <div <%= attribs(slot: :header) %>> Header </div>
156
- <div <%= attribs(slot: :body) %>> Body </div>
157
- <div <%= attribs(slot: :footer) %>> Footer </div>
141
+ <div <%= attributes(slot: :card) %>>
142
+ <div <%= attributes(slot: :header) %>> Header </div>
143
+ <div <%= attributes(slot: :body) %>> Body </div>
144
+ <div <%= attributes(slot: :footer) %>> Footer </div>
158
145
  </div>
159
146
  ```
160
147
 
@@ -169,27 +156,27 @@ We can define variants of a child component inside a parent
169
156
 
170
157
  ```ruby
171
158
  class Alert < BaseComponent
172
- attributes {
173
- base {
159
+ variants do
160
+ base do
174
161
  slot :main, class: %w[rounded-md border]
175
162
  slot :button, size: :md
176
- }
177
- variant(:color) {
178
- primary {
163
+ end
164
+ variant(:color) do
165
+ primary do
179
166
  slot :main, class: { append: %w[bg-blue-100] }
180
167
  slot :button, color: :primary
181
- }
182
- danger {
168
+ end
169
+ danger do
183
170
  slot :main, class: { append: %w[bg-red-100] }
184
171
  slot :button, color: :danger, size: :lg, id: "alert_button_1"
185
- }
186
- }
187
- }
172
+ end
173
+ end
174
+ end
188
175
  ```
189
176
 
190
177
 
191
178
  ```erb
192
- <div <%= attribs(slot: :main) %>>
179
+ <div <%= attributes(slot: :main) %>>
193
180
  Hello World
194
181
  <%= render Button.new(**attribs(slot: :button)) %>
195
182
  </div>
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "nested_variant"
4
+ require_relative "../helpers"
4
5
 
5
6
  module Attrify
6
7
  module DSL
7
8
  class Variant
9
+ include Helpers
10
+
8
11
  def initialize
9
12
  @variants = {}
10
13
  end
@@ -29,6 +32,10 @@ module Attrify
29
32
  super
30
33
  end
31
34
  end
35
+
36
+ def value(value, *args, &block)
37
+ method_missing(value, *args, &block)
38
+ end
32
39
  end
33
40
  end
34
41
  end
@@ -36,10 +36,9 @@ module Attrify
36
36
 
37
37
  def values_for(instance:, keys:)
38
38
  # if @has_procs
39
-
40
39
  @operations = run_procs_on(@operations, instance)
41
40
  @result = cache_result(@operations)[:value]
42
- @result = @result.dig(*keys)
41
+ @result = @result.dig(*keys) || {}
43
42
  if @result.present?
44
43
  if @result.has_key?(:attributes)
45
44
  @result = @result[:attributes]
@@ -46,9 +46,19 @@ module Attrify
46
46
  raise ArgumentError, "Defaults must be a hash, got #{defaults.class}"
47
47
  end
48
48
 
49
- # Ensure that all keys and values are symbols
50
- unless defaults.all? { |key, value| key.is_a?(Symbol) && value.is_a?(Symbol) }
51
- raise ArgumentError, "Defaults must be a flat hash of symbols. Got: #{defaults.inspect}"
49
+ defaults.each_with_object({}) do |(key, value), hash|
50
+ # Ensure keys are symbols.
51
+ unless key.is_a?(Symbol)
52
+ raise ArgumentError, "All keys must be symbols. Got key #{key.inspect} (#{key.class})"
53
+ end
54
+
55
+ hash[key] = if value.is_a?(Symbol)
56
+ value
57
+ elsif value.respond_to?(:to_s)
58
+ value.to_s
59
+ else
60
+ raise ArgumentError, "Value #{value.inspect} cannot be used as an default value"
61
+ end
52
62
  end
53
63
 
54
64
  defaults
@@ -0,0 +1,37 @@
1
+ module Attrify
2
+ class VariantConfig < Hash
3
+ module MergeableAttributes
4
+ def <<(other)
5
+ merge!(other) do |key, old_val, new_val|
6
+ if old_val.is_a?(Hash) && new_val.is_a?(Hash)
7
+ old_val.merge(new_val)
8
+ else
9
+ new_val
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ include MergeableAttributes
16
+
17
+ def initialize(*args)
18
+ super
19
+ # missing keys auto-initialize as new AttributeSet instances.
20
+ self.default_proc = proc { |hash, key| hash[key] = self.class.new }
21
+ end
22
+
23
+ def []=(key, value)
24
+ # Convert plain hashes to AttributeSet instances.
25
+ if value.is_a?(Hash) && !value.is_a?(self.class)
26
+ value = self.class.new.merge(value)
27
+ end
28
+ super
29
+ end
30
+
31
+ def set(**args)
32
+ clear
33
+ merge!(args)
34
+ self
35
+ end
36
+ end
37
+ end
@@ -42,12 +42,13 @@ module Attrify
42
42
  def fetch(**args)
43
43
  # Split args into variant and operations
44
44
  variant_keys = variants.keys
45
+
45
46
  variant_args = {}
46
47
  operation_args = {}
47
48
 
48
49
  args.each do |key, value|
49
50
  if variant_keys.include?(key)
50
- variant_args[key] = Array(value).join("_").to_sym
51
+ variant_args[key] = value #Array(value).join("_").to_sym
51
52
  else
52
53
  operation_args[key] = value
53
54
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Attrify
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/attrify.rb CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  require_relative "attrify/version"
4
4
  require_relative "attrify/variant_registry"
5
+ require_relative "attrify/variant_config"
5
6
  require_relative "attrify/parser"
6
-
7
7
  require_relative "attrify/dsl/engine"
8
8
 
9
9
  module Attrify
@@ -12,7 +12,7 @@ module Attrify
12
12
  end
13
13
 
14
14
  module ClassMethods
15
- def attributes(&)
15
+ def variants(&)
16
16
  result = DSL::Engine.new.build(&)
17
17
  config = variant_registry
18
18
  config.base = result.base_attr
@@ -30,24 +30,19 @@ module Attrify
30
30
  end
31
31
  end
32
32
  end
33
+
33
34
  include Helpers
34
- def attribs(slot: :main, **args)
35
- @attr_options ||= {}
36
35
 
37
- new_arguments = if slot == :main
38
- args
39
- else
40
- {slot => args}
41
- end
36
+ def attributes(slot: :main, **args)
37
+ new_arguments = (slot == :main) ? args : {slot => args}
42
38
 
43
- merged_arguments = deep_merge_hashes(@attr_options, new_arguments)
39
+ merged_arguments = deep_merge_hashes(variant, new_arguments)
44
40
 
45
41
  variant = self.class.variant_registry&.fetch(**merged_arguments)
46
42
  variant.values_for(instance: self, keys: Array(slot))
47
43
  end
48
44
 
49
- def with_attributes(**args)
50
- @attr_options = args
51
- self
45
+ def variant
46
+ @variant_config ||= VariantConfig.new
52
47
  end
53
48
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attrify
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hector Medina Fetterman
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-10-13 00:00:00.000000000 Z
11
+ date: 2025-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -65,6 +65,7 @@ files:
65
65
  - lib/attrify/operation_set.rb
66
66
  - lib/attrify/parser.rb
67
67
  - lib/attrify/variant.rb
68
+ - lib/attrify/variant_config.rb
68
69
  - lib/attrify/variant_registry.rb
69
70
  - lib/attrify/version.rb
70
71
  - sig/attribute_variants.rbs