line-message-builder 0.9.0 → 0.10.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +1 -1
  3. data/.rubocop.yml +5 -0
  4. data/CHANGELOG.md +20 -0
  5. data/CLAUDE.md +124 -0
  6. data/README.md +24 -16
  7. data/claudekit.json +11 -0
  8. data/docs/rubrics/rdoc.md +169 -0
  9. data/lib/line/message/builder/actions/message.rb +47 -26
  10. data/lib/line/message/builder/actions/postback.rb +56 -26
  11. data/lib/line/message/builder/actions/uri.rb +139 -0
  12. data/lib/line/message/builder/actions.rb +6 -3
  13. data/lib/line/message/builder/base.rb +92 -67
  14. data/lib/line/message/builder/container.rb +55 -60
  15. data/lib/line/message/builder/context.rb +51 -72
  16. data/lib/line/message/builder/flex/actionable.rb +53 -32
  17. data/lib/line/message/builder/flex/box.rb +342 -79
  18. data/lib/line/message/builder/flex/bubble.rb +73 -46
  19. data/lib/line/message/builder/flex/builder.rb +39 -28
  20. data/lib/line/message/builder/flex/button.rb +90 -42
  21. data/lib/line/message/builder/flex/carousel.rb +48 -20
  22. data/lib/line/message/builder/flex/icon.rb +152 -0
  23. data/lib/line/message/builder/flex/image.rb +95 -33
  24. data/lib/line/message/builder/flex/partial.rb +47 -49
  25. data/lib/line/message/builder/flex/position.rb +187 -100
  26. data/lib/line/message/builder/flex/separator.rb +49 -6
  27. data/lib/line/message/builder/flex/size.rb +67 -47
  28. data/lib/line/message/builder/flex/span.rb +106 -32
  29. data/lib/line/message/builder/flex/text.rb +166 -54
  30. data/lib/line/message/builder/flex.rb +26 -23
  31. data/lib/line/message/builder/quick_reply.rb +174 -4
  32. data/lib/line/message/builder/text.rb +70 -3
  33. data/lib/line/message/builder/version.rb +1 -1
  34. data/lib/line/message/builder.rb +14 -11
  35. data/lib/line/message/rspec/matchers/have_flex_bubble.rb +1 -1
  36. data/lib/line/message/rspec/matchers/have_flex_component.rb +16 -6
  37. data/lib/line/message/rspec/matchers/have_flex_message.rb +1 -1
  38. data/lib/line/message/rspec/matchers/have_flex_separator.rb +1 -1
  39. data/lib/line/message/rspec/matchers/have_quick_reply.rb +1 -1
  40. data/lib/line/message/rspec/matchers/have_text_message.rb +1 -1
  41. data/llm.txt +276 -35
  42. data/release-please-config.json +3 -1
  43. metadata +9 -3
@@ -7,7 +7,7 @@ module Line
7
7
  # Represents a "box" component in a LINE Flex Message.
8
8
  #
9
9
  # A box is a fundamental layout container that arranges its child components
10
- # (`contents`) in a specified direction (`layout`: horizontal, vertical, or
10
+ # (+contents+) in a specified direction (+layout+: horizontal, vertical, or
11
11
  # baseline). It can hold various other components like text, images, buttons,
12
12
  # or even nested boxes, allowing for complex layouts.
13
13
  #
@@ -15,10 +15,11 @@ module Line
15
15
  # arrangement of their children, such as padding, margin, spacing between
16
16
  # items, justification, and alignment.
17
17
  #
18
- # A box can also have an `action` associated with it, making the entire
18
+ # A box can also have an +action+ associated with it, making the entire
19
19
  # box area tappable.
20
20
  #
21
- # @example Creating a horizontal box with two text components
21
+ # == Example
22
+ #
22
23
  # Line::Message::Builder.with do
23
24
  # flex alt_text: "Box example" do
24
25
  # bubble do
@@ -32,119 +33,284 @@ module Line
32
33
  # end
33
34
  # end
34
35
  #
35
- # @see https://developers.line.biz/en/reference/messaging-api/#box
36
- # @see Actionable For making the box tappable.
37
- # @see HasPartial For including reusable component groups.
38
- # @see Position::Padding For padding properties.
39
- # @see Position::Margin For margin properties.
40
- # @see Position::Offset For offset properties.
41
- # @see Size::Flex For flex sizing property.
42
- class Box < Line::Message::Builder::Base
36
+ # See also:
37
+ # - https://developers.line.biz/en/reference/messaging-api/#box
38
+ # - Actionable for making the box tappable
39
+ # - HasPartial for including reusable component groups
40
+ # - Position::Padding for padding properties
41
+ # - Position::Margin for margin properties
42
+ # - Position::Offset for offset properties
43
+ # - Size::Flex for flex sizing property
44
+ class Box < Line::Message::Builder::Base # rubocop:disable Metrics/ClassLength
43
45
  include HasPartial # Allows including predefined partial component sets.
44
46
  include Actionable # Enables defining an action for the entire box.
45
- include Position::Padding # Adds padding options like `padding_all`, `padding_top`, etc.
46
- include Position::Margin # Adds `margin` option.
47
- include Position::Offset # Adds offset options like `offset_top`, `offset_start`, etc.
48
- include Size::Flex # Adds `flex` option for controlling size within a parent box.
49
-
50
- # @!attribute [r] contents
51
- # @return [Array<Base>] An array holding the child components (e.g.,
52
- # {Text}, {Image}, nested {Box} instances) of this box.
47
+ include Position::Padding # Adds padding options like +padding_all+, +padding_top+, etc.
48
+ include Position::Margin # Adds +margin+ option.
49
+ include Position::Offset # Adds offset options like +offset_top+, +offset_start+, etc.
50
+ include Size::Flex # Adds +flex+ option for controlling size within a parent box.
51
+
52
+ # An array holding the child components (e.g., Text, Image, nested Box instances) of this box.
53
53
  attr_reader :contents
54
54
 
55
+ # :method: layout
56
+ # :call-seq:
57
+ # layout() -> Symbol
58
+ # layout(value) -> Symbol
59
+ #
55
60
  # Specifies the arrangement of child components.
56
- # @!method layout(value)
57
- # @param value [Symbol] `:horizontal` (default), `:vertical`, or `:baseline`.
58
- # @return [Symbol] The current layout.
61
+ #
62
+ # [value]
63
+ # +:horizontal+ (default), +:vertical+, or +:baseline+
59
64
  option :layout, default: :horizontal, validator: Validators::Enum.new(
60
65
  :horizontal, :vertical, :baseline
61
66
  )
62
67
 
68
+ # :method: justify_content
69
+ # :call-seq:
70
+ # justify_content() -> Symbol or nil
71
+ # justify_content(value) -> Symbol
72
+ #
63
73
  # Horizontal alignment of content in a horizontal box; vertical alignment in a vertical box.
64
- # @!method justify_content(value)
65
- # @param value [Symbol, nil] E.g., `:flex_start`, `:center`, `:flex_end`,
66
- # `:space_between`, `:space_around`, `:space_evenly`.
67
- # @return [Symbol, nil] The current justify_content value.
74
+ #
75
+ # [value]
76
+ # +:flex_start+, +:center+, +:flex_end+, +:space_between+, +:space_around+, or +:space_evenly+
68
77
  option :justify_content, default: nil, validator: Validators::Enum.new(
69
78
  :flex_start, :center, :flex_end, :space_between, :space_around, :space_evenly
70
79
  )
71
80
 
81
+ # :method: align_items
82
+ # :call-seq:
83
+ # align_items() -> Symbol or nil
84
+ # align_items(value) -> Symbol
85
+ #
72
86
  # Vertical alignment of content in a horizontal box; horizontal alignment in a vertical box.
73
- # @!method align_items(value)
74
- # @param value [Symbol, nil] E.g., `:flex_start`, `:center`, `:flex_end`.
75
- # @return [Symbol, nil] The current align_items value.
87
+ #
88
+ # [value]
89
+ # +:flex_start+, +:center+, or +:flex_end+
76
90
  option :align_items, default: nil, validator: Validators::Enum.new(
77
91
  :flex_start, :center, :flex_end
78
92
  )
79
93
 
94
+ # :method: spacing
95
+ # :call-seq:
96
+ # spacing() -> String, Symbol, or nil
97
+ # spacing(value) -> String or Symbol
98
+ #
80
99
  # Specifies the minimum spacing between components.
81
- # Not applicable if `layout` is `:baseline` or if the box contains only one component.
82
- # @!method spacing(value)
83
- # @param value [String, Symbol, nil] E.g., `"md"`, `:lg`, `"10px"`.
84
- # Keywords: `:none`, `:xs`, `:sm`, `:md`, `:lg`, `:xl`, `:xxl`.
85
- # @return [String, Symbol, nil] The current spacing value.
100
+ # Not applicable if +layout+ is +:baseline+ or if the box contains only one component.
101
+ #
102
+ # [value]
103
+ # <code>"md"</code>, +:lg+, <code>"10px"</code>, or keywords:
104
+ # +:none+, +:xs+, +:sm+, +:md+, +:lg+, +:xl+, +:xxl+
86
105
  option :spacing, default: nil, validator: Validators::Size.new(:pixel, :keyword)
87
106
 
107
+ # :method: width
108
+ # :call-seq:
109
+ # width() -> String or nil
110
+ # width(value) -> String
111
+ #
88
112
  # Width of the box. Can be a percentage or pixel value.
89
- # @!method width(value)
90
- # @param value [String, nil] E.g., `"100px"`, `"50%"`.
91
- # @return [String, nil] The current width.
113
+ #
114
+ # [value]
115
+ # <code>"100px"</code> or <code>"50%"</code>
92
116
  option :width, default: nil, validator: Validators::Size.new(:pixel, :percentage)
93
117
 
118
+ # :method: max_width
119
+ # :call-seq:
120
+ # max_width() -> String or nil
121
+ # max_width(value) -> String
122
+ #
94
123
  # Maximum width of the box.
95
- # @!method max_width(value)
96
- # @param value [String, nil] E.g., `"100px"`, `"50%"`.
97
- # @return [String, nil] The current maximum width.
124
+ #
125
+ # [value]
126
+ # <code>"100px"</code> or <code>"50%"</code>
98
127
  option :max_width, default: nil, validator: Validators::Size.new(:pixel, :percentage)
99
128
 
129
+ # :method: height
130
+ # :call-seq:
131
+ # height() -> String or nil
132
+ # height(value) -> String
133
+ #
100
134
  # Height of the box.
101
- # @!method height(value)
102
- # @param value [String, nil] E.g., `"100px"`, `"50%"`.
103
- # @return [String, nil] The current height.
135
+ #
136
+ # [value]
137
+ # <code>"100px"</code> or <code>"50%"</code>
104
138
  option :height, default: nil, validator: Validators::Size.new(:pixel, :percentage)
105
139
 
140
+ # :method: max_height
141
+ # :call-seq:
142
+ # max_height() -> String or nil
143
+ # max_height(value) -> String
144
+ #
106
145
  # Maximum height of the box.
107
- # @!method max_height(value)
108
- # @param value [String, nil] E.g., `"100px"`, `"50%"`.
109
- # @return [String, nil] The current maximum height.
146
+ #
147
+ # [value]
148
+ # <code>"100px"</code> or <code>"50%"</code>
110
149
  option :max_height, default: nil, validator: Validators::Size.new(:pixel, :percentage)
111
150
 
151
+ # :method: background_color
152
+ # :call-seq:
153
+ # background_color() -> String or nil
154
+ # background_color(value) -> String
155
+ #
156
+ # Background color of the box. An alpha channel may be included.
157
+ #
158
+ # [value]
159
+ # Hexadecimal color code (e.g., <code>"#RRGGBB"</code>, <code>"#RRGGBBAA"</code>)
160
+ option :background_color, default: nil # API key: backgroundColor
161
+
162
+ # :method: border_color
163
+ # :call-seq:
164
+ # border_color() -> String or nil
165
+ # border_color(value) -> String
166
+ #
167
+ # Color of the box border.
168
+ #
169
+ # [value]
170
+ # Hexadecimal color code (e.g., <code>"#RRGGBB"</code>)
171
+ option :border_color, default: nil # API key: borderColor
172
+
173
+ # :method: border_width
174
+ # :call-seq:
175
+ # border_width() -> String, Symbol, or nil
176
+ # border_width(value) -> String or Symbol
177
+ #
178
+ # Width of the box border. Listed keywords increase in width.
179
+ #
180
+ # [value]
181
+ # <code>"2px"</code>, or one of +:none+, +:light+, +:normal+,
182
+ # +:medium+, <code>:"semi-bold"</code>, +:bold+
183
+ option :border_width, default: nil # API key: borderWidth
184
+
185
+ # :method: corner_radius
186
+ # :call-seq:
187
+ # corner_radius() -> String, Symbol, or nil
188
+ # corner_radius(value) -> String or Symbol
189
+ #
190
+ # Radius used when rounding the corners of the box.
191
+ #
192
+ # [value]
193
+ # <code>"4px"</code>, or one of +:none+, +:xs+, +:sm+, +:md+, +:lg+,
194
+ # +:xl+, +:xxl+
195
+ option :corner_radius, default: nil, # API key: cornerRadius
196
+ validator: Validators::Size.new(:pixel, :keyword)
197
+
198
+ # :method: background_angle
199
+ # :call-seq:
200
+ # background_angle() -> String or nil
201
+ # background_angle(value) -> String
202
+ #
203
+ # Angle at which the linear gradient background moves. +0deg+ runs from
204
+ # bottom to top and the direction rotates clockwise as the angle grows.
205
+ #
206
+ # Required when any other +background_*+ property is set.
207
+ #
208
+ # [value]
209
+ # An angle in the half-open interval [0, 360) (e.g., <code>"90deg"</code>)
210
+ option :background_angle, default: nil
211
+
212
+ # :method: background_start_color
213
+ # :call-seq:
214
+ # background_start_color() -> String or nil
215
+ # background_start_color(value) -> String
216
+ #
217
+ # Color at the starting point of the linear gradient background.
218
+ #
219
+ # Required when any other +background_*+ property is set.
220
+ #
221
+ # [value]
222
+ # Hexadecimal color code (e.g., <code>"#RRGGBB"</code>)
223
+ option :background_start_color, default: nil
224
+
225
+ # :method: background_end_color
226
+ # :call-seq:
227
+ # background_end_color() -> String or nil
228
+ # background_end_color(value) -> String
229
+ #
230
+ # Color at the ending point of the linear gradient background.
231
+ #
232
+ # Required when any other +background_*+ property is set.
233
+ #
234
+ # [value]
235
+ # Hexadecimal color code (e.g., <code>"#RRGGBB"</code>)
236
+ option :background_end_color, default: nil
237
+
238
+ # :method: background_center_color
239
+ # :call-seq:
240
+ # background_center_color() -> String or nil
241
+ # background_center_color(value) -> String
242
+ #
243
+ # Intermediate color of the linear gradient background. Setting it makes
244
+ # the gradient run through three colors.
245
+ #
246
+ # [value]
247
+ # Hexadecimal color code (e.g., <code>"#RRGGBB"</code>)
248
+ option :background_center_color, default: nil
249
+
250
+ # :method: background_center_position
251
+ # :call-seq:
252
+ # background_center_position() -> String or nil
253
+ # background_center_position(value) -> String
254
+ #
255
+ # Position of the intermediate color stop, between the start and end of
256
+ # the gradient. Defaults to <code>"50%"</code>.
257
+ #
258
+ # [value]
259
+ # A percentage between <code>"0%"</code> and <code>"100%"</code>
260
+ option :background_center_position, default: nil
261
+
112
262
  # Initializes a new Flex Message Box component.
113
263
  # The provided block is instance-eval'd, allowing DSL methods for adding
114
- # child components (e.g., {#text}, {#button}, nested {#box}) to be called.
264
+ # child components (e.g., +text+, +button+, nested +box+) to be called.
115
265
  #
116
- # @param context [Object, nil] An optional context for the builder.
117
- # @param options [Hash] A hash of options to set instance variables.
118
- # Corresponds to the `option` definitions in this class and included modules.
119
- # @param block [Proc, nil] A block to define the contents of this box.
266
+ # [context]
267
+ # An optional context for the builder
268
+ # [options]
269
+ # A hash of options to set instance variables.
270
+ # Corresponds to the +option+ definitions in this class and included modules
271
+ # [block]
272
+ # A block to define the contents of this box
120
273
  def initialize(context: nil, **options, &)
121
274
  @contents = [] # Holds child components
122
275
  super # Calls Base#initialize, sets options, and evals block
123
276
  end
124
277
 
125
- # Adds a nested Flex {Box} component to this box's contents.
278
+ # Adds a nested Flex Box component to this box's contents.
279
+ #
280
+ # [options]
281
+ # Options for the nested box (see Box)
282
+ # [block]
283
+ # A block to define the contents of the nested box
126
284
  #
127
- # @param options [Hash] Options for the nested box. See {Box#initialize}.
128
- # @param block [Proc] A block to define the contents of the nested box.
129
- # @return [Flex::Box] The newly created nested Box object.
285
+ # Returns the newly created nested Box object.
130
286
  def box(**options, &)
131
287
  @contents << Flex::Box.new(context: context, **options, &)
132
288
  end
133
289
 
134
- # Adds a Flex {Text} component to this box's contents.
290
+ # Adds a Flex Text component to this box's contents.
291
+ #
292
+ # [text]
293
+ # The text content
294
+ # [options]
295
+ # Options for the text component (see Text)
296
+ # [block]
297
+ # An optional block for the text component. This can be used
298
+ # to define an action for the text or to add Span components within the text
299
+ #
300
+ # Returns the newly created Text object.
301
+ #
302
+ # == Example: Simple text component
135
303
  #
136
- # @param text [String] The text content.
137
- # @param options [Hash] Options for the text component. See {Text#initialize}.
138
- # @param block [Proc, nil] An optional block for the text component. This can be used
139
- # to define an action for the text or to add {Span} components within the text.
140
- # @return [Flex::Text] The newly created Text object.
141
- # @example Simple text component
142
304
  # text "Hello, World!"
143
- # @example Text with an action
305
+ #
306
+ # === Example: Text with an action
307
+ #
144
308
  # text "Click me" do
145
- # message "Action", text: "You clicked me!"
309
+ # message "You clicked me!", label: "Action"
146
310
  # end
147
- # @example Text with spans
311
+ #
312
+ # === Example: Text with spans
313
+ #
148
314
  # text "This has " do
149
315
  # span "styled", color: "#FF0000"
150
316
  # span " parts"
@@ -153,38 +319,70 @@ module Line
153
319
  @contents << Flex::Text.new(text, context: context, **options, &)
154
320
  end
155
321
 
156
- # Adds a Flex {Button} component to this box's contents.
322
+ # Adds a Flex Button component to this box's contents.
323
+ #
324
+ # [options]
325
+ # Options for the button component (see Button)
326
+ # [block]
327
+ # A block to define the button's action and properties
157
328
  #
158
- # @param options [Hash] Options for the button component. See {Button#initialize}.
159
- # @param block [Proc] A block to define the button's action and properties.
160
- # @return [Flex::Button] The newly created Button object.
329
+ # Returns the newly created Button object.
161
330
  def button(**options, &)
162
331
  @contents << Flex::Button.new(context: context, **options, &)
163
332
  end
164
333
 
165
- # Adds a Flex {Image} component to this box's contents.
334
+ # Adds a Flex Image component to this box's contents.
166
335
  #
167
- # @param url [String] The URL of the image.
168
- # @param options [Hash] Options for the image component. See {Image#initialize}.
169
- # @param block [Proc, nil] An optional block for the image component (e.g., for an action).
170
- # @return [Flex::Image] The newly created Image object.
336
+ # [url]
337
+ # The URL of the image
338
+ # [options]
339
+ # Options for the image component (see Image)
340
+ # [block]
341
+ # An optional block for the image component (e.g., for an action)
342
+ #
343
+ # Returns the newly created Image object.
171
344
  def image(url, **options, &)
172
345
  @contents << Flex::Image.new(url, context: context, **options, &)
173
346
  end
174
347
 
175
- # Adds a Flex {Separator} component to this box's contents.
348
+ # Adds an Icon component to this box's contents.
349
+ #
350
+ # Icons decorate the text next to them and can only be used in a box
351
+ # whose +layout+ is +:baseline+.
352
+ #
353
+ # [url]
354
+ # The HTTPS URL of the icon image
355
+ # [options]
356
+ # Options for the icon component (e.g., +:size+, +:aspect_ratio+)
357
+ # [block]
358
+ # An optional block for further configuration
359
+ #
360
+ # == Example
361
+ #
362
+ # box layout: :baseline do
363
+ # icon "https://example.com/star.png", size: :sm
364
+ # text "4.0", margin: :md
365
+ # end
366
+ def icon(url, **options, &)
367
+ @contents << Flex::Icon.new(url, context: context, **options, &)
368
+ end
369
+
370
+ # Adds a Flex Separator component to this box's contents.
176
371
  #
177
372
  # A separator is a simple component that draws a horizontal line,
178
373
  # creating a visual division between other components in a container.
179
374
  #
180
- # @param options [Hash] Options for the separator component. See {Separator}.
181
- # @param block [Proc, nil] An optional block for advanced separator configuration.
182
- # @return [Flex::Separator] The newly created Separator object.
375
+ # [options]
376
+ # Options for the separator component (see Separator)
377
+ # [block]
378
+ # An optional block for advanced separator configuration
379
+ #
380
+ # Returns the newly created Separator object.
183
381
  def separator(**options, &)
184
382
  @contents << Flex::Separator.new(context: context, **options, &)
185
383
  end
186
384
 
187
- def to_h
385
+ def to_h # :nodoc:
188
386
  raise RequiredError, "layout is required" if layout.nil?
189
387
 
190
388
  return to_sdkv2 if context.sdkv2?
@@ -194,10 +392,17 @@ module Line
194
392
 
195
393
  private
196
394
 
395
+ # :nodoc:
197
396
  def to_api # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
198
397
  {
199
398
  type: "box",
200
399
  layout: layout,
400
+ # Appearance
401
+ backgroundColor: background_color,
402
+ borderColor: border_color,
403
+ borderWidth: border_width,
404
+ cornerRadius: corner_radius,
405
+ background: api_background,
201
406
  # Position & Layout
202
407
  justifyContent: justify_content,
203
408
  alignItems: align_items,
@@ -229,10 +434,17 @@ module Line
229
434
  }.compact
230
435
  end
231
436
 
437
+ # :nodoc:
232
438
  def to_sdkv2 # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
233
439
  {
234
440
  type: "box",
235
441
  layout: layout,
442
+ # Appearance
443
+ background_color: background_color,
444
+ border_color: border_color,
445
+ border_width: border_width,
446
+ corner_radius: corner_radius,
447
+ background: sdkv2_background,
236
448
  # Position & Layout
237
449
  justify_content: justify_content,
238
450
  align_items: align_items,
@@ -263,6 +475,57 @@ module Line
263
475
  action: action&.to_h # From Actionable module
264
476
  }.compact
265
477
  end
478
+
479
+ # Builds the nested +background+ object for a linear gradient, or +nil+
480
+ # when no gradient is configured.
481
+ def api_background # :nodoc:
482
+ return if no_gradient?
483
+
484
+ validate_gradient!
485
+
486
+ {
487
+ type: "linearGradient",
488
+ angle: background_angle,
489
+ startColor: background_start_color,
490
+ endColor: background_end_color,
491
+ centerColor: background_center_color,
492
+ centerPosition: background_center_position
493
+ }.compact
494
+ end
495
+
496
+ # :nodoc:
497
+ def sdkv2_background
498
+ return if no_gradient?
499
+
500
+ validate_gradient!
501
+
502
+ {
503
+ type: "linearGradient",
504
+ angle: background_angle,
505
+ start_color: background_start_color,
506
+ end_color: background_end_color,
507
+ center_color: background_center_color,
508
+ center_position: background_center_position
509
+ }.compact
510
+ end
511
+
512
+ def no_gradient? # :nodoc:
513
+ (gradient_required + [background_center_color, background_center_position]).compact.empty?
514
+ end
515
+
516
+ def gradient_required # :nodoc:
517
+ [background_angle, background_start_color, background_end_color]
518
+ end
519
+
520
+ # Raises RequiredError when the gradient is only partly specified, since
521
+ # LINE rejects a background that is missing its angle or end colors.
522
+ def validate_gradient! # :nodoc:
523
+ return unless gradient_required.any?(&:nil?)
524
+
525
+ raise RequiredError,
526
+ "background_angle, background_start_color and background_end_color " \
527
+ "are required for a linear gradient"
528
+ end
266
529
  end
267
530
  end
268
531
  end