sai 0.2.0 → 0.3.1
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/.yardopts +1 -1
- data/CHANGELOG.md +38 -1
- data/README.md +39 -241
- data/docs/USAGE.md +351 -0
- data/lib/sai/ansi/color_parser.rb +109 -0
- data/lib/sai/ansi/sequence_processor.rb +269 -0
- data/lib/sai/ansi/sequenced_string.rb +475 -0
- data/lib/sai/ansi/style_parser.rb +66 -0
- data/lib/sai/ansi.rb +0 -27
- data/lib/sai/conversion/color_sequence.rb +4 -4
- data/lib/sai/conversion/rgb/color_classifier.rb +209 -0
- data/lib/sai/conversion/rgb/color_indexer.rb +48 -0
- data/lib/sai/conversion/rgb/color_space.rb +192 -0
- data/lib/sai/conversion/rgb/color_transformer.rb +140 -0
- data/lib/sai/conversion/rgb.rb +23 -269
- data/lib/sai/decorator/color_manipulations.rb +157 -0
- data/lib/sai/decorator/delegation.rb +84 -0
- data/lib/sai/decorator/gradients.rb +363 -0
- data/lib/sai/decorator/hex_colors.rb +56 -0
- data/lib/sai/decorator/named_colors.rb +780 -0
- data/lib/sai/decorator/named_styles.rb +276 -0
- data/lib/sai/decorator/rgb_colors.rb +64 -0
- data/lib/sai/decorator.rb +35 -795
- data/lib/sai/mode_selector.rb +19 -19
- data/lib/sai/named_colors.rb +437 -0
- data/lib/sai.rb +753 -23
- data/sig/manifest.yaml +3 -0
- data/sig/sai/ansi/color_parser.rbs +77 -0
- data/sig/sai/ansi/sequence_processor.rbs +178 -0
- data/sig/sai/ansi/sequenced_string.rbs +380 -0
- data/sig/sai/ansi/style_parser.rbs +59 -0
- data/sig/sai/ansi.rbs +0 -10
- data/sig/sai/conversion/rgb/color_classifier.rbs +165 -0
- data/sig/sai/conversion/rgb/color_indexer.rbs +41 -0
- data/sig/sai/conversion/rgb/color_space.rbs +129 -0
- data/sig/sai/conversion/rgb/color_transformer.rbs +99 -0
- data/sig/sai/conversion/rgb.rbs +15 -198
- data/sig/sai/decorator/color_manipulations.rbs +125 -0
- data/sig/sai/decorator/delegation.rbs +47 -0
- data/sig/sai/decorator/gradients.rbs +267 -0
- data/sig/sai/decorator/hex_colors.rbs +48 -0
- data/sig/sai/decorator/named_colors.rbs +1491 -0
- data/sig/sai/decorator/named_styles.rbs +72 -0
- data/sig/sai/decorator/rgb_colors.rbs +52 -0
- data/sig/sai/decorator.rbs +25 -202
- data/sig/sai/mode_selector.rbs +19 -19
- data/sig/sai/named_colors.rbs +65 -0
- data/sig/sai.rbs +1485 -44
- metadata +38 -4
@@ -0,0 +1,363 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sai/ansi/sequenced_string'
|
4
|
+
require 'sai/conversion/rgb'
|
5
|
+
|
6
|
+
module Sai
|
7
|
+
class Decorator
|
8
|
+
# Color gradient methods for the {Decorator} class
|
9
|
+
#
|
10
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
11
|
+
# @since 0.3.1
|
12
|
+
#
|
13
|
+
# @abstract This module is meant to be included in the {Decorator} class to provide color gradient methods
|
14
|
+
# @api private
|
15
|
+
module Gradients
|
16
|
+
# Build a foreground gradient between two colors for text decoration
|
17
|
+
#
|
18
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
19
|
+
# @since 0.3.1
|
20
|
+
#
|
21
|
+
# @api public
|
22
|
+
#
|
23
|
+
# @example Create a foreground gradient from red to blue
|
24
|
+
# decorator.gradient(:red, :blue, 10).decorate('Hello, World!')
|
25
|
+
# #=> "\e[38;2;255;0;0mH\e[0m\e[38;2;204;0;51me\e[0m..."
|
26
|
+
#
|
27
|
+
# @param start_color [Array<Integer>, String, Symbol] the starting color
|
28
|
+
# @param end_color [Array<Integer>, String, Symbol] the ending color
|
29
|
+
# @param steps [Integer] the number of gradient steps (minimum 2)
|
30
|
+
#
|
31
|
+
# @raise [ArgumentError] if steps is less than 2
|
32
|
+
# @return [Decorator] a new instance of Decorator with foreground gradient colors
|
33
|
+
# @rbs (
|
34
|
+
# Array[Integer] | String | Symbol start_color,
|
35
|
+
# Array[Integer] | String | Symbol end_color,
|
36
|
+
# Integer steps
|
37
|
+
# ) -> Decorator
|
38
|
+
def gradient(start_color, end_color, steps)
|
39
|
+
colors = Conversion::RGB.transform.gradient(start_color, end_color, steps)
|
40
|
+
dup.tap { |duped| duped.instance_variable_set(:@foreground_sequence, colors) } #: Decorator
|
41
|
+
end
|
42
|
+
|
43
|
+
# Build a background gradient between two colors for text decoration
|
44
|
+
#
|
45
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
46
|
+
# @since 0.3.1
|
47
|
+
#
|
48
|
+
# @api public
|
49
|
+
#
|
50
|
+
# @example Create a background gradient from red to blue
|
51
|
+
# decorator.on_gradient(:red, :blue, 10).decorate('Hello, World!')
|
52
|
+
# #=> "\e[48;2;255;0;0mH\e[0m\e[48;2;204;0;51me\e[0m..."
|
53
|
+
#
|
54
|
+
# @param start_color [Array<Integer>, String, Symbol] the starting color
|
55
|
+
# @param end_color [Array<Integer>, String, Symbol] the ending color
|
56
|
+
# @param steps [Integer] the number of gradient steps (minimum 2)
|
57
|
+
#
|
58
|
+
# @raise [ArgumentError] if steps is less than 2
|
59
|
+
# @return [Decorator] a new instance of Decorator with background gradient colors
|
60
|
+
# @rbs (
|
61
|
+
# Array[Integer] | String | Symbol start_color,
|
62
|
+
# Array[Integer] | String | Symbol end_color,
|
63
|
+
# Integer steps
|
64
|
+
# ) -> Decorator
|
65
|
+
def on_gradient(start_color, end_color, steps)
|
66
|
+
colors = Conversion::RGB.transform.gradient(start_color, end_color, steps)
|
67
|
+
dup.tap { |duped| duped.instance_variable_set(:@background_sequence, colors) } #: Decorator
|
68
|
+
end
|
69
|
+
|
70
|
+
# Build a background rainbow gradient for text decoration
|
71
|
+
#
|
72
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
73
|
+
# @since 0.3.1
|
74
|
+
#
|
75
|
+
# @api public
|
76
|
+
#
|
77
|
+
# @example Create a rainbow background gradient
|
78
|
+
# decorator.on_rainbow(6).decorate('Hello, World!')
|
79
|
+
# #=> "\e[48;2;255;0;0mH\e[0m\e[48;2;255;255;0me\e[0m..."
|
80
|
+
#
|
81
|
+
# @param steps [Integer] the number of colors to generate (minimum 2)
|
82
|
+
#
|
83
|
+
# @raise [ArgumentError] if steps is less than 2
|
84
|
+
# @return [Decorator] a new instance of Decorator with background rainbow colors
|
85
|
+
# @rbs (Integer steps) -> Decorator
|
86
|
+
def on_rainbow(steps)
|
87
|
+
colors = Conversion::RGB.transform.rainbow_gradient(steps)
|
88
|
+
dup.tap { |duped| duped.instance_variable_set(:@background_sequence, colors) } #: Decorator
|
89
|
+
end
|
90
|
+
|
91
|
+
# Build a foreground rainbow gradient for text decoration
|
92
|
+
#
|
93
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
94
|
+
# @since 0.3.1
|
95
|
+
#
|
96
|
+
# @api public
|
97
|
+
#
|
98
|
+
# @example Create a rainbow text gradient
|
99
|
+
# decorator.rainbow(6).decorate('Hello, World!')
|
100
|
+
# #=> "\e[38;2;255;0;0mH\e[0m\e[38;2;255;255;0me\e[0m..."
|
101
|
+
#
|
102
|
+
# @param steps [Integer] the number of colors to generate (minimum 2)
|
103
|
+
#
|
104
|
+
# @raise [ArgumentError] if steps is less than 2
|
105
|
+
# @return [Decorator] a new instance of Decorator with foreground rainbow colors
|
106
|
+
# @rbs (Integer steps) -> Decorator
|
107
|
+
def rainbow(steps)
|
108
|
+
colors = Conversion::RGB.transform.rainbow_gradient(steps)
|
109
|
+
dup.tap { |duped| duped.instance_variable_set(:@foreground_sequence, colors) } #: Decorator
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
# Adjust number of colors to match text length
|
115
|
+
#
|
116
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
117
|
+
# @since 0.3.1
|
118
|
+
#
|
119
|
+
# @api private
|
120
|
+
#
|
121
|
+
# @param colors [Array<Array<Integer>>] original color sequence
|
122
|
+
# @param text_length [Integer] desired number of colors
|
123
|
+
#
|
124
|
+
# @return [Array<Array<Integer>>] adjusted color sequence
|
125
|
+
# @rbs (Array[Array[Integer]] colors, Integer text_length) -> Array[Array[Integer]]
|
126
|
+
def adjust_colors_to_text_length(colors, text_length)
|
127
|
+
return colors if colors.length == text_length
|
128
|
+
return stretch_colors(colors, text_length) if colors.length < text_length
|
129
|
+
|
130
|
+
shrink_colors(colors, text_length)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Apply color sequence gradients to text
|
134
|
+
#
|
135
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
136
|
+
# @since 0.3.1
|
137
|
+
#
|
138
|
+
# @api private
|
139
|
+
#
|
140
|
+
# @param text [String] the text to apply the gradient to
|
141
|
+
#
|
142
|
+
# @return [ANSI::SequencedString] the text with gradient applied
|
143
|
+
# @rbs (String text) -> ANSI::SequencedString
|
144
|
+
def apply_sequence_gradient(text)
|
145
|
+
# @type self: Decorator
|
146
|
+
return ANSI::SequencedString.new(text) unless should_decorate?
|
147
|
+
|
148
|
+
chars = text.chars
|
149
|
+
adjusted_colors = prepare_color_sequences(chars.length)
|
150
|
+
gradient_text = build_gradient_text(chars, adjusted_colors)
|
151
|
+
|
152
|
+
ANSI::SequencedString.new(gradient_text.join)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Build color sequences for a single character
|
156
|
+
#
|
157
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
158
|
+
# @since 0.3.1
|
159
|
+
#
|
160
|
+
# @api private
|
161
|
+
#
|
162
|
+
# @param colors [Hash] color sequences for foreground and background
|
163
|
+
# @param index [Integer] character position
|
164
|
+
#
|
165
|
+
# @return [Array<String>] ANSI sequences for the character
|
166
|
+
# @rbs (Hash[Symbol, Array[Array[Integer]]] colors, Integer index) -> Array[String]
|
167
|
+
def build_color_sequences(colors, index)
|
168
|
+
# @type self: Decorator
|
169
|
+
sequences = []
|
170
|
+
sequences << get_foreground_sequence(colors[:foreground], index) if colors[:foreground]
|
171
|
+
sequences << get_background_sequence(colors[:background], index) if colors[:background]
|
172
|
+
sequences += style_sequences
|
173
|
+
sequences.compact
|
174
|
+
end
|
175
|
+
|
176
|
+
# Build gradient text from characters and color sequences
|
177
|
+
#
|
178
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
179
|
+
# @since 0.3.1
|
180
|
+
#
|
181
|
+
# @api private
|
182
|
+
#
|
183
|
+
# @param chars [Array<String>] text characters
|
184
|
+
# @param colors [Hash] color sequences for foreground and background
|
185
|
+
#
|
186
|
+
# @return [Array<String>] colored characters
|
187
|
+
# @rbs (Array[String] chars, Hash[Symbol, Array[Array[Integer]]] colors) -> Array[String]
|
188
|
+
def build_gradient_text(chars, colors)
|
189
|
+
chars.each_with_index.map do |char, i|
|
190
|
+
next char if char == ' '
|
191
|
+
|
192
|
+
sequences = build_color_sequences(colors, i)
|
193
|
+
"#{sequences.join}#{char}#{ANSI::RESET}"
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# Calculate indices and progress for color interpolation
|
198
|
+
#
|
199
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
200
|
+
# @since 0.3.1
|
201
|
+
#
|
202
|
+
# @api private
|
203
|
+
#
|
204
|
+
# @param position [Integer] current position in sequence
|
205
|
+
# @param step_size [Float] size of each step
|
206
|
+
# @param max_index [Integer] maximum index allowed
|
207
|
+
#
|
208
|
+
# @return [Hash] interpolation indices and progress
|
209
|
+
# @rbs (Integer position, Float step_size, Integer max_index) -> Hash[Symbol, Integer | Float]
|
210
|
+
def calculate_interpolation_indices(position, step_size, max_index)
|
211
|
+
position_in_sequence = position * step_size
|
212
|
+
lower = position_in_sequence.floor
|
213
|
+
upper = [lower + 1, max_index - 1].min
|
214
|
+
progress = position_in_sequence - lower
|
215
|
+
|
216
|
+
{ lower: lower, upper: upper, progress: progress }
|
217
|
+
end
|
218
|
+
|
219
|
+
# Get background sequence for a character
|
220
|
+
#
|
221
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
222
|
+
# @since 0.3.1
|
223
|
+
#
|
224
|
+
# @api private
|
225
|
+
#
|
226
|
+
# @param colors [Array<Array<Integer>>, nil] background color sequence
|
227
|
+
# @param index [Integer] character position
|
228
|
+
#
|
229
|
+
# @return [String, nil] ANSI sequence for background
|
230
|
+
# @rbs (Array[Array[Integer]]? colors, Integer index) -> String?
|
231
|
+
def get_background_sequence(colors, index)
|
232
|
+
if colors
|
233
|
+
Conversion::ColorSequence.resolve(colors[index], @mode, :background)
|
234
|
+
elsif @background_sequence
|
235
|
+
Conversion::ColorSequence.resolve(@background_sequence[index], @mode, :background)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# Get foreground sequence for a character
|
240
|
+
#
|
241
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
242
|
+
# @since 0.3.1
|
243
|
+
#
|
244
|
+
# @api private
|
245
|
+
#
|
246
|
+
# @param colors [Array<Array<Integer>>, nil] foreground color sequence
|
247
|
+
# @param index [Integer] character position
|
248
|
+
#
|
249
|
+
# @return [String, nil] ANSI sequence for foreground
|
250
|
+
# @rbs (Array[Array[Integer]]? colors, Integer index) -> String?
|
251
|
+
def get_foreground_sequence(colors, index)
|
252
|
+
if colors
|
253
|
+
Conversion::ColorSequence.resolve(colors[index], @mode)
|
254
|
+
elsif @foreground_sequence
|
255
|
+
Conversion::ColorSequence.resolve(@foreground_sequence[index], @mode)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
# Interpolate between two colors in a sequence
|
260
|
+
#
|
261
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
262
|
+
# @since 0.3.1
|
263
|
+
#
|
264
|
+
# @api private
|
265
|
+
#
|
266
|
+
# @param colors [Array<Array<Integer>>] color sequence
|
267
|
+
# @param indices [Hash] interpolation indices and progress
|
268
|
+
#
|
269
|
+
# @return [Array<Integer>] interpolated color
|
270
|
+
# @rbs (Array[Array[Integer]] colors, Hash[Symbol, Integer | Float]) -> Array[Integer]
|
271
|
+
def interpolate_sequence_colors(colors, indices)
|
272
|
+
lower_index = indices[:lower].to_i
|
273
|
+
upper_index = indices[:upper].to_i
|
274
|
+
progress = indices[:progress].to_f
|
275
|
+
|
276
|
+
color1 = colors[lower_index]
|
277
|
+
color2 = colors[upper_index]
|
278
|
+
|
279
|
+
# Add nil guards
|
280
|
+
return [0, 0, 0] unless color1 && color2
|
281
|
+
|
282
|
+
color1.zip(color2).map do |c1, c2|
|
283
|
+
next 0 unless c1 && c2 # Add nil guard for individual components
|
284
|
+
|
285
|
+
(c1 + ((c2 - c1) * progress)).round
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
# Prepare foreground and background color sequences for text
|
290
|
+
#
|
291
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
292
|
+
# @since 0.3.1
|
293
|
+
#
|
294
|
+
# @api private
|
295
|
+
#
|
296
|
+
# @param text_length [Integer] length of text to color
|
297
|
+
#
|
298
|
+
# @return [Hash] adjusted color sequences
|
299
|
+
# @rbs (Integer text_length) -> Hash[Symbol, Array[Array[Integer]]]
|
300
|
+
def prepare_color_sequences(text_length)
|
301
|
+
sequences = {}
|
302
|
+
sequences[:foreground] = prepare_sequence(@foreground_sequence, text_length) if @foreground_sequence
|
303
|
+
sequences[:background] = prepare_sequence(@background_sequence, text_length) if @background_sequence
|
304
|
+
sequences
|
305
|
+
end
|
306
|
+
|
307
|
+
# Prepare a single color sequence
|
308
|
+
#
|
309
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
310
|
+
# @since 0.3.1
|
311
|
+
#
|
312
|
+
# @api private
|
313
|
+
#
|
314
|
+
# @param sequence [Array<Array<Integer>>, nil] color sequence to prepare
|
315
|
+
# @param text_length [Integer] length of text to color
|
316
|
+
#
|
317
|
+
# @return [Array<Array<Integer>>, nil] adjusted color sequence
|
318
|
+
# @rbs (Array[Array[Integer]]? sequence, Integer text_length) -> Array[Array[Integer]]?
|
319
|
+
def prepare_sequence(sequence, text_length)
|
320
|
+
sequence && adjust_colors_to_text_length(sequence, text_length)
|
321
|
+
end
|
322
|
+
|
323
|
+
# Shrink a color sequence to fit desired length
|
324
|
+
#
|
325
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
326
|
+
# @since 0.3.1
|
327
|
+
#
|
328
|
+
# @api private
|
329
|
+
#
|
330
|
+
# @param colors [Array<Array<Integer>>] original color sequence
|
331
|
+
# @param target_length [Integer] desired number of colors
|
332
|
+
#
|
333
|
+
# @return [Array<Array<Integer>>] shrunk color sequence
|
334
|
+
# @rbs (Array[Array[Integer]] colors, Integer target_length) -> Array[Array[Integer]]
|
335
|
+
def shrink_colors(colors, target_length)
|
336
|
+
step_size = (target_length - 1).to_f / (colors.length - 1)
|
337
|
+
indices = (0...colors.length).select { |i| (i * step_size).round < target_length }
|
338
|
+
indices.map { |i| colors[i] }
|
339
|
+
end
|
340
|
+
|
341
|
+
# Stretch a color sequence to fit desired length
|
342
|
+
#
|
343
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
344
|
+
# @since 0.3.1
|
345
|
+
#
|
346
|
+
# @api private
|
347
|
+
#
|
348
|
+
# @param colors [Array<Array<Integer>>] original color sequence
|
349
|
+
# @param target_length [Integer] desired number of colors
|
350
|
+
#
|
351
|
+
# @return [Array<Array<Integer>>] stretched color sequence
|
352
|
+
# @rbs (Array[Array[Integer]] colors, Integer target_length) -> Array[Array[Integer]]
|
353
|
+
def stretch_colors(colors, target_length)
|
354
|
+
step_size = (colors.length - 1).to_f / (target_length - 1)
|
355
|
+
|
356
|
+
(0...target_length).map do |i|
|
357
|
+
indices = calculate_interpolation_indices(i, step_size, colors.length)
|
358
|
+
interpolate_sequence_colors(colors, indices)
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sai
|
4
|
+
class Decorator
|
5
|
+
# Hexadecimal color methods for the {Decorator} class
|
6
|
+
#
|
7
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
8
|
+
# @since 0.3.1
|
9
|
+
#
|
10
|
+
# @abstract This module is meant to be included in the {Decorator} class to provide hexadecimal color methods
|
11
|
+
# @api private
|
12
|
+
module HexColors
|
13
|
+
# Apply a hexadecimal color to the foreground
|
14
|
+
#
|
15
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
16
|
+
# @since 0.1.0
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# decorator.hex("#EB4133").decorate('Hello, world!').to_s #=> "\e[38;2;235;65;51mHello, world!\e[0m"
|
22
|
+
#
|
23
|
+
# @param code [String] the hex color code
|
24
|
+
#
|
25
|
+
# @raise [ArgumentError] if the hex code is invalid
|
26
|
+
# @return [Decorator] a new instance of Decorator with the hex color applied
|
27
|
+
# @rbs (String code) -> Decorator
|
28
|
+
def hex(code)
|
29
|
+
raise ArgumentError, "Invalid hex color code: #{code}" unless /^#?([A-Fa-f0-9]{6})$/.match?(code)
|
30
|
+
|
31
|
+
dup.tap { |duped| duped.instance_variable_set(:@foreground, code) } #: Decorator
|
32
|
+
end
|
33
|
+
|
34
|
+
# Apply a hexadecimal color to the background
|
35
|
+
#
|
36
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
37
|
+
# @since 0.1.0
|
38
|
+
#
|
39
|
+
# @api public
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# decorator.on_hex("#EB4133").decorate('Hello, world!').to_s #=> "\e[48;2;235;65;51mHello, world!\e[0m"
|
43
|
+
#
|
44
|
+
# @param code [String] the hex color code
|
45
|
+
#
|
46
|
+
# @raise [ArgumentError] if the hex code is invalid
|
47
|
+
# @return [Decorator] a new instance of Decorator with the hex color applied
|
48
|
+
# @rbs (String code) -> Decorator
|
49
|
+
def on_hex(code)
|
50
|
+
raise ArgumentError, "Invalid hex color code: #{code}" unless /^#?([A-Fa-f0-9]{6})$/.match?(code)
|
51
|
+
|
52
|
+
dup.tap { |duped| duped.instance_variable_set(:@background, code) } #: Decorator
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|