tag_options 1.2.0 → 1.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: 6e2a2a1f14c436d95569da59c07b36ea56ca1ef5f0a69546cb445423243d69ab
4
- data.tar.gz: 2a5f69f7f214a7a00d3dfd47d7b627968960c6525ee4c65ec91a4590cdbab140
3
+ metadata.gz: 5cee8f74f051ffc41909e62f0a613904ce5f342a6be1fd8d961b8f7663d66087
4
+ data.tar.gz: 5e8c5f2b69fe32e13330a99f4d112a412bc4e6b3a78a5233d66f11a3ad032e7c
5
5
  SHA512:
6
- metadata.gz: 2eee9711c6e3cf2163ef43aa45ea15c50fa247c1dada13d21e225e0cab19dbc5e52cb3843168d23cd33434d8e496e4a6da12247232cc881af3ec5f98a1adf0ff
7
- data.tar.gz: cb281c5639b6381795d7d7f975ef5cf558ac0f9065ad38ac25d9c62e7257748b84df3d07a9f72d161709ae436c4712196aa26805bffc359522b1db0719305fcc
6
+ metadata.gz: d6613c09fbf756a9a15e3a4001e478a0c225f643c1dd3f8b58be3e63fe8c3afb0dab00bed9d8984ca75ae58b37ae4fbb4e26d0410b7d36bb0cf59f52fecbd869
7
+ data.tar.gz: ed905332c293482cd3610c5374bb9a7b27328cd1c245bb55c6d69fc10b4c36e7c67f6a38ca3a32ef3a0f879344c9751b9a22ae18f91f1ed21d4bf5a6b7a8cac4
data/CHANGELOG.md CHANGED
@@ -2,20 +2,37 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
- ## [1.2.0] - 2023-03-01
5
+ ## [1.3.0] - 2023-03-03
6
+
7
+ - Added `at().remove!` option for removing values.
8
+
9
+ **NOTE**: If you have implemented custom resolvers, you will need to modify them
10
+ in order to support `remove!`. For examples, see the [built-in
11
+ handlers](https://github.com/wamonroe/tag_options/tree/main/lib/tag_options/resolvers)
12
+ for more information.
13
+
14
+ ## [1.2.1] - 2023-03-02
15
+
16
+ - Fixed bug introduced when switching to
17
+ `ActiveSupport::HashWithIndifferentAccess` that prevented a `TagOptions::Hash`
18
+ from being passed to a method using double splat, e.g. `some_method
19
+ **options`.
20
+
21
+ ## [1.2.0] - 2023-03-02
6
22
 
7
23
  - Added `at().default!` option for setting values that are not already present.
8
24
  - Fix for passing an array of values to `combine1` or `set!`
9
25
 
10
26
  ## [1.1.0] - 2023-03-01
11
27
 
12
- - Switched to inheriting from ActiveSupport::HashWithIndifferentAccess.
28
+ - Switched to inheriting from `ActiveSupport::HashWithIndifferentAccess`.
13
29
  - Added before/after/around initialize callback support.
14
30
 
15
31
  ## [1.0.0] - 2022-06-14
16
32
 
17
33
  - Rewrote and simplified TagOptions::Hash and supporting classes.
18
- - BREAKING CHANGES, read documentation for updated usage before updating.
34
+
35
+ **BREAKING CHANGES**: Read documentation for updated usage before updating.
19
36
 
20
37
  ## [0.9.3] - 2021-11-11
21
38
 
data/README.md CHANGED
@@ -42,6 +42,7 @@ Would render:
42
42
  - [combine!](#combine)
43
43
  - [set!](#set)
44
44
  - [default!](#default)
45
+ - [remove!](#remove)
45
46
  - [Conditional Usage](#conditional-usage)
46
47
  - [Custom Property Resolvers](#custom-property-resolvers)
47
48
  - [Development](#development)
@@ -72,11 +73,14 @@ TagOptions::Hash.new
72
73
 
73
74
  hash = {class: "flex"}
74
75
  TagOptions::Hash.new(hash)
75
- => {"class"=>"flex"}
76
+ => {:class=>"flex"}
76
77
  ```
77
78
 
78
79
  `TagOptions::Hash` inherits from `ActiveSupport::HashWithIndifferentAccess`,
79
80
  implementing a hash where keys `:foo` and `"foo"` are considered to be the same.
81
+ It differs from `ActiveSupport::HashWithIndifferentAccess`, however, by storing
82
+ the keys as symbols instead of strings to make it easier to pass the hash as
83
+ an method argument using double splat, e.g. `some_method **options`.
80
84
 
81
85
  ### combine!
82
86
 
@@ -86,7 +90,7 @@ Combine HTML attributes with an existing `TagOptions::Hash` by chaining `at` and
86
90
  ```ruby
87
91
  options = TagOptions::Hash.new(class: "flex")
88
92
  options.at(:class).combine!("mt-1")
89
- => {"class"=>"flex mt-1"}
93
+ => {:class=>"flex mt-1"}
90
94
  ```
91
95
 
92
96
  Values can also be specified as arrays.
@@ -94,7 +98,7 @@ Values can also be specified as arrays.
94
98
  ```ruby
95
99
  options = TagOptions::Hash.new(class: "flex")
96
100
  options.at(:class).combine!(["mt-1", "mx-2"])
97
- => {"class"=>"flex mt-1 mx-2"}
101
+ => {:class=>"flex mt-1 mx-2"}
98
102
  ```
99
103
 
100
104
  HTML attributes are only added if they don't already exist.
@@ -102,7 +106,7 @@ HTML attributes are only added if they don't already exist.
102
106
  ```ruby
103
107
  options = TagOptions::Hash.new(class: "flex")
104
108
  options.at(:class).combine!("flex flex-col")
105
- => {"class"=>"flex flex-col"}
109
+ => {:class=>"flex flex-col"}
106
110
  ```
107
111
 
108
112
  You can also combine values on nested hashes.
@@ -110,7 +114,7 @@ You can also combine values on nested hashes.
110
114
  ```ruby
111
115
  options = TagOptions::Hash.new(class: "flex", data: {controller: "dropdown"})
112
116
  options.at(:data, :controller).combine!("toggle")
113
- => {"class"=>"flex", "data"=>{"controller"=>"dropdown toggle"}}
117
+ => {:class=>"flex", :data=>{:controller=>"dropdown toggle"}
114
118
  ```
115
119
 
116
120
  If a nested hash doesn't already exist it will be automatically added.
@@ -118,7 +122,7 @@ If a nested hash doesn't already exist it will be automatically added.
118
122
  ```ruby
119
123
  options = TagOptions::Hash.new(class: "flex")
120
124
  options.at(:data, :controller).combine!("dropdown")
121
- => {"class"=>"flex", "data"=>{"controller"=>"dropdown"}}
125
+ => {:class=>"flex", :data=>{:controller=>"dropdown"}
122
126
  ```
123
127
 
124
128
  ### set!
@@ -130,7 +134,7 @@ existing values.
130
134
  ```ruby
131
135
  options = TagOptions::Hash.new(class: "flex")
132
136
  options.at(:class).set!("block")
133
- => {"class"=>"block"}
137
+ => {:class=>"block"}
134
138
  ```
135
139
 
136
140
  ### default!
@@ -143,20 +147,61 @@ set the specified values if the value is not already specified.
143
147
  options = TagOptions::Hash.new(class: "flex")
144
148
  options.at(:class).default!("block")
145
149
  options.at(:role).default!("alert")
146
- => {"class"=>"flex", "role"=>"alert"}
150
+ => {:class=>"flex", :role=>"alert"}
151
+ ```
152
+
153
+ ### remove!
154
+
155
+ Remove HTML attributes from an existing `TagOptions::Hash` by chaining `at` and
156
+ `remove!`.
157
+
158
+ ```ruby
159
+ options = TagOptions::Hash.new(class: "flex ml-1 mr-1")
160
+ options.at(:class).remove!("mr-1")
161
+ => {:class=>"flex ml-1"}
162
+ ```
163
+
164
+ In addition to string values, you can also pass regular expression to `remove!`.
165
+
166
+ ```ruby
167
+ options = TagOptions::Hash.new(class: "flex ml-1 mr-2")
168
+ options.at(:class).remove!(/m.-\d/)
169
+ => {:class=>"flex"}
147
170
  ```
148
171
 
149
172
  ## Conditional Usage
150
173
 
151
- Both the `combine!` and `set!` allow for values to be conditionally added to
152
- HTML attributes using an argument array. Where the values are added
153
- unconditionally and key/value pairs have their key added _IF_ the value is true.
174
+ The `combine!`, `set!`, `default!`, and `remove!` methods allow for values to be
175
+ conditionally resolved using an argument array. Where the values are passed
176
+ unconditionally and key/value pairs have their key passed _IF_ the value is
177
+ true.
154
178
 
155
179
  ```ruby
156
180
  # assuming `centered?` returns `true`
157
181
  options = TagOptions::Hash.new(class: "flex")
158
182
  options.at(:class).combine!("mt-1", "mx-auto": centered?, "mx-2": !centered?)
159
- => {"class"=>"flex mt-1 mx-auto"}
183
+ => {:class=>"flex mt-1 mx-auto"}
184
+ ```
185
+
186
+ ```ruby
187
+ # assuming `centered?` returns `true`
188
+ options = TagOptions::Hash.new(class: "flex")
189
+ options.at(:class).set!("block", "mx-auto": centered?, "mx-2": !centered?)
190
+ => {:class=>"block mx-auto"}
191
+ ```
192
+
193
+ ```ruby
194
+ # assuming `centered?` returns `true`
195
+ options = TagOptions::Hash.new(role: "alert")
196
+ options.at(:class).default!("flex", "mx-auto": centered?, "mx-2": !centered?)
197
+ => {:role=>"alert", :class=>"flex mx-auto"}
198
+ ```
199
+
200
+ ```ruby
201
+ # assuming `centered?` returns `true`
202
+ options = TagOptions::Hash.new(class: "flex mx-auto mx-2")
203
+ options.at(:class).remove!("mt-1", "mx-auto": centered?, "mx-2": !centered?)
204
+ => {:class=>"flex mx-2"}
160
205
  ```
161
206
 
162
207
  ## Custom Property Resolvers
@@ -181,7 +226,7 @@ pass `as: :style` to `at`.
181
226
  ```ruby
182
227
  options = TagOptions::Hash.new(style: "display: block; margin-left: 0;")
183
228
  options.at(:style, as: :style).combine!("display: flex; margin-right: 0;")
184
- => {"style"=>"display: flex; margin-left: 0; margin-right: 0;"}
229
+ => {:style=>"display: flex; margin-left: 0; margin-right: 0;"}
185
230
  ```
186
231
 
187
232
  A `TagOptions::Resolver` class is available if you wish to implement your own
@@ -0,0 +1,7 @@
1
+ module TagOptions
2
+ module ConvertKey
3
+ def convert_key(key)
4
+ key&.to_s&.to_sym
5
+ end
6
+ end
7
+ end
@@ -1,17 +1,20 @@
1
1
  require "active_support/callbacks"
2
2
  require "active_support/core_ext/hash/indifferent_access"
3
+ require "tag_options/convert_key"
3
4
  require "tag_options/hash_at"
4
5
  require "tag_options/errors/not_hash_error"
5
6
 
6
7
  module TagOptions
7
8
  class Hash < ActiveSupport::HashWithIndifferentAccess
8
9
  include ActiveSupport::Callbacks
10
+ include ConvertKey
11
+
9
12
  define_callbacks :initialize
10
13
 
11
14
  def initialize(hash = {})
12
15
  run_callbacks :initialize do
13
16
  hash.each do |key, value|
14
- self[key] = value.is_a?(::Hash) ? self.class.new(value) : value
17
+ self[convert_key(key)] = value.is_a?(::Hash) ? self.class.new(value) : value
15
18
  end
16
19
  end
17
20
  end
@@ -21,15 +24,16 @@ module TagOptions
21
24
  end
22
25
 
23
26
  def dig(*keys)
24
- keys.size.zero? ? self : super
27
+ keys = keys.map { |key| convert_key(key) }
28
+ keys.size.zero? ? self : super(*keys)
25
29
  end
26
30
 
27
31
  def populate!(*keys)
28
32
  populated_keys = []
29
33
  data = self
30
34
  keys.each do |key|
31
- data[key] ||= self.class.new
32
- data = data[key]
35
+ data[convert_key(key)] ||= self.class.new
36
+ data = data[convert_key(key)]
33
37
  unless data.is_a?(self.class)
34
38
  raise TagOptions::Errors::NotHashError.new(populated_keys, type: data.class)
35
39
  end
@@ -1,11 +1,14 @@
1
1
  require "tag_options/configuration"
2
+ require "tag_options/convert_key"
2
3
 
3
4
  module TagOptions
4
5
  class HashAt
6
+ include ConvertKey
7
+
5
8
  def initialize(opt_hash:, keys:, as:)
6
9
  @opt_hash = opt_hash
7
- @keys = keys[..-2]
8
- @value_key = keys[-1]
10
+ @keys = keys[..-2].map { |key| convert_key(key) }
11
+ @value_key = convert_key(keys[-1])
9
12
  @resolver = TagOptions.configuration.resolver(as)
10
13
  end
11
14
 
@@ -25,8 +28,27 @@ module TagOptions
25
28
  set_value! @resolver.call(*values, **conditions)
26
29
  end
27
30
 
31
+ def remove!(*values, **conditions)
32
+ @opt_hash.populate!(*@keys)
33
+ regex_values, values = values.flatten.partition { |v| v.is_a?(Regexp) }
34
+ remove_values!(*regex_values, *@resolver.values(*values, **conditions))
35
+ end
36
+
28
37
  private
29
38
 
39
+ def remove_values!(*values_to_remove)
40
+ values = @resolver.values(@opt_hash.dig(*@keys)[@value_key])
41
+ values_to_remove.each do |value|
42
+ if value.is_a?(Regexp)
43
+ values.reject! { |current_value| value.match?(current_value) }
44
+ else
45
+ values.reject! { |current_value| value == current_value }
46
+ end
47
+ end
48
+ @opt_hash.dig(*@keys)[@value_key] = @resolver.call(*values)
49
+ @opt_hash
50
+ end
51
+
30
52
  def set_default!(value)
31
53
  root = @opt_hash.dig(*@keys)
32
54
  root[@value_key] = value unless root.key?(@value_key)
@@ -8,6 +8,10 @@ module TagOptions
8
8
  new(...).call
9
9
  end
10
10
 
11
+ def self.values(...)
12
+ new(...).values
13
+ end
14
+
11
15
  private
12
16
 
13
17
  def resolve_conditional_values(conditional_values)
@@ -4,7 +4,11 @@ module TagOptions
4
4
  module Resolvers
5
5
  class Default < Resolver
6
6
  def call
7
- @values.map { |v| v.to_s.split }.flatten.compact.uniq.join(" ")
7
+ values.join(" ")
8
+ end
9
+
10
+ def values
11
+ @values.map { |v| v.to_s.split }.flatten.compact.uniq
8
12
  end
9
13
  end
10
14
  end
@@ -4,7 +4,11 @@ module TagOptions
4
4
  module Resolvers
5
5
  class Style < Resolver
6
6
  def call
7
- styles.map { |p, v| "#{p}: #{v};" }.join(" ")
7
+ values.join(" ")
8
+ end
9
+
10
+ def values
11
+ styles.map { |p, v| "#{p}: #{v};" }
8
12
  end
9
13
 
10
14
  private
@@ -1,3 +1,3 @@
1
1
  module TagOptions
2
- VERSION = "1.2.0"
2
+ VERSION = "1.3.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tag_options
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Monroe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-02 00:00:00.000000000 Z
11
+ date: 2023-03-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -51,6 +51,7 @@ files:
51
51
  - Rakefile
52
52
  - lib/tag_options.rb
53
53
  - lib/tag_options/configuration.rb
54
+ - lib/tag_options/convert_key.rb
54
55
  - lib/tag_options/error.rb
55
56
  - lib/tag_options/errors/not_hash_error.rb
56
57
  - lib/tag_options/errors/resolver_error.rb