tag_options 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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