amber_component 0.0.2 → 0.0.3

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -1
  5. data/CONTRIBUTING.md +87 -0
  6. data/Gemfile +3 -0
  7. data/Gemfile.lock +16 -91
  8. data/README.md +25 -42
  9. data/Rakefile +17 -1
  10. data/amber_component.gemspec +4 -2
  11. data/banner.png +0 -0
  12. data/docs/.bundle/config +2 -0
  13. data/docs/.gitignore +5 -0
  14. data/docs/404.html +25 -0
  15. data/docs/Gemfile +37 -0
  16. data/docs/Gemfile.lock +89 -0
  17. data/docs/README.md +19 -0
  18. data/docs/_config.yml +148 -0
  19. data/docs/_data/amber_component.yml +3 -0
  20. data/docs/_sass/_variables.scss +2 -0
  21. data/docs/_sass/color_schemes/amber_component.scss +11 -0
  22. data/docs/_sass/custom/custom.scss +60 -0
  23. data/docs/api/index.md +8 -0
  24. data/docs/assets/images/logo_wide.png +0 -0
  25. data/docs/changelog/index.md +8 -0
  26. data/docs/favicon.ico +0 -0
  27. data/docs/getting_started/index.md +8 -0
  28. data/docs/getting_started/installation.md +7 -0
  29. data/docs/getting_started/ruby_support.md +7 -0
  30. data/docs/getting_started/wireframes.md +7 -0
  31. data/docs/index.md +17 -0
  32. data/docs/introduction/basic_usage.md +7 -0
  33. data/docs/introduction/index.md +8 -0
  34. data/docs/introduction/why_amber_component.md +7 -0
  35. data/docs/resources/index.md +8 -0
  36. data/docs/styles/index.md +8 -0
  37. data/docs/styles/usage.md +7 -0
  38. data/docs/views/index.md +8 -0
  39. data/docs/views/usage.md +7 -0
  40. data/icon.png +0 -0
  41. data/lib/amber_component/assets.rb +59 -0
  42. data/lib/amber_component/base.rb +66 -434
  43. data/lib/amber_component/helpers/class_helper.rb +22 -0
  44. data/lib/amber_component/helpers/component_helper.rb +18 -0
  45. data/lib/amber_component/helpers/css_helper.rb +25 -0
  46. data/lib/amber_component/helpers.rb +11 -0
  47. data/lib/amber_component/railtie.rb +21 -0
  48. data/lib/amber_component/rendering.rb +53 -0
  49. data/lib/amber_component/template_handler/erb.rb +17 -0
  50. data/lib/amber_component/template_handler.rb +26 -0
  51. data/lib/amber_component/version.rb +1 -1
  52. data/lib/amber_component/views.rb +198 -0
  53. data/lib/amber_component.rb +6 -2
  54. data/lib/generators/amber_component/install_generator.rb +23 -0
  55. data/lib/generators/amber_component/templates/application_component.rb +13 -0
  56. data/lib/generators/amber_component_generator.rb +24 -0
  57. data/lib/generators/templates/component.rb.erb +9 -0
  58. data/lib/generators/templates/component_test.rb.erb +9 -0
  59. data/lib/generators/templates/style.css.erb +3 -0
  60. data/lib/generators/templates/view.html.erb +3 -0
  61. metadata +85 -19
  62. data/.kanbn/index.md +0 -23
  63. data/.kanbn/tasks/add-instance-variables-to-view-when-block-given-markdown.md +0 -9
  64. data/.kanbn/tasks/bind-clas-to-action-view-method-call-example-component-data-data-without-calling-any-method.md +0 -9
  65. data/.kanbn/tasks/bind-scoped-css-to-head-of-doc.md +0 -10
  66. data/.kanbn/tasks/check-if-we-need-full-rails-gem-pack.md +0 -9
  67. data/.kanbn/tasks/simple-proto.md +0 -10
  68. data/.kanbn/tasks/verify-if-template-is-haml-or-erb.md +0 -9
  69. data/PLANS.md +0 -17
  70. data/lib/amber_component/helper.rb +0 -8
  71. data/lib/amber_component/style_injector.rb +0 -52
  72. data/sig/amber_components.rbs +0 -4
@@ -3,226 +3,102 @@
3
3
  require 'set'
4
4
  require 'erb'
5
5
  require 'tilt'
6
- require 'active_model/callbacks'
7
6
  require 'memery'
7
+ require 'active_model/callbacks'
8
+ require 'action_view'
8
9
 
9
- require_relative './style_injector'
10
-
11
- # Abstract class which serves as a base
12
- # for all Amber Components.
13
- #
14
- # There are a few life cycle callbacks that can be defined.
15
- # The same way as in `ActiveRecord` models and `ActionController` controllers.
16
- #
17
- # - before_render
18
- # - around_render
19
- # - after_render
20
- # - before_initialize
21
- # - around_initialize
22
- # - after_initialize
23
- #
24
- # class ButtonComponent < ::AmberComponent::Base
25
- # # You can provide a Symbol of the method that should be called
26
- # before_render :before_render_method
27
- # # Or provide a block that will be executed
28
- # after_initialize do
29
- # # Your code here
30
- # end
31
- #
32
- # def before_render_method
33
- # # Your code here
34
- # end
35
- # end
36
- #
37
- #
38
- # @abstract Create a subclass to define a new component.
39
10
  module ::AmberComponent
40
- class Base # :nodoc:
11
+ # Abstract class which serves as a base
12
+ # for all Amber Components.
13
+ #
14
+ # There are a few life cycle callbacks that can be defined.
15
+ # The same way as in `ActiveRecord` models and `ActionController` controllers.
16
+ #
17
+ # - before_render
18
+ # - around_render
19
+ # - after_render
20
+ # - before_initialize
21
+ # - around_initialize
22
+ # - after_initialize
23
+ #
24
+ # class ButtonComponent < ::AmberComponent::Base
25
+ # # You can provide a Symbol of the method that should be called
26
+ # before_render :before_render_method
27
+ # # Or provide a block that will be executed
28
+ # after_initialize do
29
+ # # Your code here
30
+ # end
31
+ #
32
+ # def before_render_method
33
+ # # Your code here
34
+ # end
35
+ # end
36
+ #
37
+ #
38
+ # @abstract Create a subclass to define a new component.
39
+ class Base < ::ActionView::Base
40
+ # for defining callback such as `after_initialize`
41
41
  extend ::ActiveModel::Callbacks
42
+ extend Helpers::ClassHelper
42
43
 
43
- include Helper
44
-
45
- # @return [Regexp]
46
- VIEW_FILE_REGEXP = /^view\./.freeze
47
- # @return [Regexp]
48
- STYLE_FILE_REGEXP = /^style\./.freeze
49
-
50
- # View types with built-in embedded Ruby
51
- #
52
- # @return [Set<Symbol>]
53
- VIEW_TYPES_WITH_RUBY = ::Set[:erb, :haml, :slim].freeze
54
- # @return [Set<Symbol>]
55
- ALLOWED_VIEW_TYPES = ::Set[:erb, :haml, :slim, :html, :md, :markdown].freeze
56
- # @return [Set<Symbol>]
57
- ALLOWED_STYLE_TYPES = ::Set[:sass, :scss, :less].freeze
44
+ include Helpers::CssHelper
45
+ include Views::InstanceMethods
46
+ extend Views::ClassMethods
47
+ include Assets::InstanceMethods
48
+ extend Assets::ClassMethods
49
+ include Rendering::InstanceMethods
50
+ extend Rendering::ClassMethods
58
51
 
59
52
  class << self
60
53
  include ::Memery
61
54
 
62
- # @param kwargs [Hash{Symbol => Object}]
63
- # @return [String]
64
- def run(**kwargs, &block)
65
- comp = new(**kwargs)
66
-
67
- comp.render(&block)
68
- end
69
-
70
- alias call run
71
-
72
-
73
- # @return [String]
74
- memoize def asset_dir_path
75
- class_const_name = name.split('::').last
76
- component_file_path, = module_parent.const_source_location(class_const_name)
77
-
78
- component_file_path.delete_suffix('.rb')
79
- end
80
-
81
- # @return [String]
82
- def view_path
83
- asset_path view_file_name
84
- end
85
-
86
- # @return [String, nil]
87
- def view_file_name
88
- files = asset_file_names(VIEW_FILE_REGEXP)
89
- raise MultipleViews, "More than one view file for `#{name}` found!" if files.length > 1
90
-
91
- files.first
92
- end
93
-
94
- # @return [Symbol]
95
- def view_type
96
- (view_file_name.split('.')[1..].reject { _1.match?(/erb/) }.last || 'erb')&.to_sym
97
- end
98
-
99
- # @return [String]
100
- def style_path
101
- asset_path style_file_name
102
- end
103
-
104
- # @return [String, nil]
105
- def style_file_name
106
- files = asset_file_names(STYLE_FILE_REGEXP)
107
- raise MultipleStyles, "More than one style file for `#{name}` found!" if files.length > 1
108
-
109
- files.first
110
- end
111
-
112
- # @return [Symbol]
113
- def style_type
114
- (style_file_name.split('.')[1..].reject { _1.match?(/erb/) }.last || 'erb')&.to_sym
115
- end
55
+ memoize :asset_dir_path
116
56
 
117
57
  # Memoize these methods in production
118
- if defined?(::Rails) && ::Rails.env.production?
58
+ if ::ENV['RAILS_ENV'] == 'production'
119
59
  memoize :view_path
120
60
  memoize :view_file_name
121
61
  memoize :view_type
122
-
123
- memoize :style_path
124
- memoize :style_file_name
125
- memoize :style_type
126
- end
127
-
128
- # Register an inline view by returning a String from the passed block.
129
- #
130
- # Usage:
131
- #
132
- # view do
133
- # <<~ERB
134
- # <h1>
135
- # Hello <%= @name %>
136
- # </h1>
137
- # ERB
138
- # end
139
- #
140
- # or:
141
- #
142
- # view :haml do
143
- # <<~HAML
144
- # %h1
145
- # Hello
146
- # = @name
147
- # HAML
148
- # end
149
- #
150
- # @param type [Symbol]
151
- # @return [void]
152
- def view(type = :erb, &block)
153
- @method_view = TypedContent.new(type: type, content: block)
154
- end
155
-
156
- # ERB/Haml/Slim view registered through the `view` method.
157
- #
158
- # @return [TypedContent]
159
- attr_reader :method_view
160
-
161
- # Register an inline style by returning a String from the passed block.
162
- #
163
- # Usage:
164
- #
165
- # style do
166
- # '.my-class { color: red; }'
167
- # end
168
- #
169
- # or:
170
- #
171
- # style :sass do
172
- # <<~SASS
173
- # .my-class
174
- # color: red
175
- # SASS
176
- # end
177
- #
178
- # @param type [Symbol]
179
- # @return [void]
180
- def style(type = :css, &block)
181
- @method_style = TypedContent.new(type: type, content: block)
182
62
  end
183
63
 
184
- # CSS/SCSS/Sass styles registered through the `style` method.
185
- #
186
- # @return [TypedContent]
187
- attr_reader :method_style
188
-
189
64
  private
190
65
 
191
66
  # @param subclass [Class]
192
67
  # @return [void]
193
68
  def inherited(subclass)
69
+ super
194
70
  # @type [Module]
195
- parent_module = subclass.module_parent
196
71
  method_body = proc do |**kwargs, &block|
197
- subclass.run(**kwargs, &block)
72
+ subclass.render(**kwargs, &block)
198
73
  end
74
+ parent_module = subclass.module_parent
199
75
 
200
- Helper.define_method(subclass.name, &method_body) && return if parent_module.equal? ::Object
201
-
202
- parent_module.define_singleton_method(subclass.name.split('::').last, &method_body)
203
- end
204
-
205
- # @param file_name [String, nil]
206
- # @return [String, nil]
207
- def asset_path(file_name)
208
- return unless file_name
76
+ if parent_module.equal?(::Object)
77
+ method_name = subclass.name
78
+ define_helper_method(subclass, Helpers::ComponentHelper, method_name, method_body)
79
+ define_helper_method(subclass, Helpers::ComponentHelper, method_name.underscore, method_body)
80
+ return
81
+ end
209
82
 
210
- ::File.join(asset_dir_path, file_name)
83
+ method_name = subclass.const_name
84
+ define_helper_method(subclass, parent_module.singleton_class, method_name, method_body)
85
+ define_helper_method(subclass, parent_module.singleton_class, method_name.underscore, method_body)
211
86
  end
212
87
 
213
- # Returns the name of the file inside the asset directory
214
- # of this component that matches the provided `Regexp`
215
- #
216
- # @param type_regexp [Regexp]
217
- # @return [Array<String>]
218
- def asset_file_names(type_regexp)
219
- return [] unless ::File.directory?(asset_dir_path)
88
+ # @param component [Class]
89
+ # @param mod [Module, Class]
90
+ # @param method_name [String, Symbol]
91
+ # @param body [Proc]
92
+ def define_helper_method(component, mod, method_name, body)
93
+ mod.define_method(method_name, &body)
220
94
 
221
- ::Dir.entries(asset_dir_path).select do |file|
222
- next unless ::File.file?(::File.join(asset_dir_path, file))
95
+ return if ::ENV['RAILS_ENV'] == 'production'
223
96
 
224
- file.match? type_regexp
225
- end
97
+ ::Warning.warn <<~WARN if mod.instance_methods.include?(method_name)
98
+ #{caller(0, 1).first}: warning:
99
+ `#{component}` shadows the name of an already existing `#{mod}` method `#{method_name}`.
100
+ Consider renaming this component, because the original method will be overwritten.
101
+ WARN
226
102
  end
227
103
  end
228
104
 
@@ -235,20 +111,6 @@ module ::AmberComponent
235
111
  end
236
112
  end
237
113
 
238
- # @return [String]
239
- def render(&block)
240
- run_callbacks :render do
241
- element = inject_views(&block)
242
- styles = inject_styles
243
- element += styles unless styles.nil?
244
- element.html_safe
245
- end
246
- end
247
-
248
- def render_in(context)
249
- byebug
250
- end
251
-
252
114
  private
253
115
 
254
116
  # @param kwargs [Hash{Symbol => Object}]
@@ -258,235 +120,5 @@ module ::AmberComponent
258
120
  instance_variable_set("@#{key}", value)
259
121
  end
260
122
  end
261
-
262
- # Helper method to render view from string or with other provided type.
263
- #
264
- # Usage:
265
- #
266
- # render_custom_view('<h1>Hello World</h1>')
267
- #
268
- # or:
269
- #
270
- # render_custom_view content: '**Hello World**', type: 'md'
271
- #
272
- # @param style [TypedContent, Hash{Symbol => String, Symbol, Proc}, String]
273
- # @return [String, nil]
274
- def render_custom_view(view, &block)
275
- return '' unless view
276
- return view if view.is_a?(::String)
277
-
278
- view = TypedContent.wrap(view)
279
- type = view.type
280
- content = view.to_s
281
-
282
- if content.empty?
283
- raise EmptyView, <<~ERR.squish
284
- Custom view for `#{self.class}` from view method cannot be empty!
285
- ERR
286
- end
287
-
288
- unless ALLOWED_VIEW_TYPES.include? type
289
- raise UnknownViewType, <<~ERR.squish
290
- Unknown view type for `#{self.class}` from view method!
291
- Check return value of param type in `view :[type] do`
292
- ERR
293
- end
294
-
295
- unless VIEW_TYPES_WITH_RUBY.include? type
296
- # first render the content with ERB if the
297
- # type does not support embedding Ruby by default
298
- content = render_string(content, :erb, block)
299
- end
300
-
301
- render_string(content, type, block)
302
- end
303
-
304
- # @return [String]
305
- def render_view_from_file(&block)
306
- view_path = self.class.view_path
307
- return '' if view_path.nil? || !::File.file?(view_path)
308
-
309
- content = ::File.read(view_path)
310
- type = self.class.view_type
311
-
312
- unless VIEW_TYPES_WITH_RUBY.include? type
313
- content = render_string(content, :erb, block)
314
- end
315
-
316
- render_string(content, type, block)
317
- end
318
-
319
- # Method returning view from method in class file.
320
- # Usage:
321
- #
322
- # view do
323
- # <<~HTML
324
- # <h1>
325
- # Hello <%= @name %>
326
- # </h1>
327
- # HTML
328
- # end
329
- #
330
- # or:
331
- #
332
- # view :haml do
333
- # <<~HAML
334
- # %h1
335
- # Hello
336
- # = @name
337
- # HAML
338
- # end
339
- #
340
- # @return [String]
341
- def render_class_method_view(&block)
342
- render_custom_view(self.class.method_view, &block)
343
- end
344
-
345
- # Method returning view from params in view.
346
- # Usage:
347
- #
348
- # <%= ExampleComponent data: data, view: "<h1>Hello #{@name}</h1>" %>
349
- #
350
- # or:
351
- #
352
- # <%= ExampleComponent data: data, view: { content: "<h1>Hello #{@name}</h1>", type: 'erb' } %>
353
- #
354
- # @return [String]
355
- def render_view_from_inline(&block)
356
- data = \
357
- if @view.is_a? String
358
- TypedContent.new(
359
- type: :erb,
360
- content: @view
361
- )
362
- else
363
- @view
364
- end
365
-
366
- render_custom_view(data, &block)
367
- end
368
-
369
- # @return [String]
370
- def inject_views(&block)
371
- view_from_file = render_view_from_file(&block)
372
- view_from_method = render_class_method_view(&block)
373
- view_from_inline = render_view_from_inline(&block)
374
-
375
- view_content = view_from_file unless view_from_file.empty?
376
- view_content = view_from_method unless view_from_method.empty?
377
- view_content = view_from_inline unless view_from_inline.empty?
378
-
379
- if view_content.nil? || view_content.empty?
380
- raise ViewFileNotFound, "View for `#{self.class}` could not be found!"
381
- end
382
-
383
- view_content
384
- end
385
-
386
- # Helper method to render style from css string or with other provided type.
387
- #
388
- # Usage:
389
- #
390
- # render_custom_style('.my-class { color: red; }')
391
- #
392
- # or:
393
- #
394
- # render_custom_style style: '.my-class { color: red; }', type: 'sass'
395
- #
396
- # @param style [TypedContent, Hash{Symbol => Symbol, String, Proc}, String]
397
- # @return [String, nil]
398
- def render_custom_style(style)
399
- return '' unless style
400
- return style if style.is_a?(::String)
401
-
402
- style = TypedContent.wrap(style)
403
- type = style.type
404
- content = style.to_s
405
-
406
- if content.empty?
407
- raise EmptyStyle, <<~ERR.squish
408
- Custom style for `#{self.class}` from style method cannot be empty!
409
- ERR
410
- end
411
-
412
- unless ALLOWED_STYLE_TYPES.include? type
413
- raise UnknownStyleType, <<~ERR.squish
414
- Unknown style type for `#{self.class}` from style method!
415
- Check return value of param type in `style :[type] do`
416
- ERR
417
- end
418
-
419
- # first render the content with ERB
420
- content = render_string(content, :erb)
421
-
422
- render_string(content, type)
423
- end
424
-
425
- # Method returning style from file (style.(css|sass|scss|less)) if exists.
426
- #
427
- # @return [String]
428
- def render_style_from_file
429
- style_path = self.class.style_path
430
- return '' unless style_path
431
-
432
- content = ::File.read(style_path)
433
- type = self.class.style_type
434
-
435
- return content if type == :css
436
-
437
- content = render_string(content, :erb)
438
- render_string(content, type)
439
- end
440
-
441
- # Method returning style from method in class file.
442
- # Usage:
443
- #
444
- # style do
445
- # '.my-class { color: red; }'
446
- # end
447
- #
448
- # or:
449
- #
450
- # style :sass do
451
- # <<~SASS
452
- # .my-class
453
- # color: red
454
- # SASS
455
- # end
456
- #
457
- # @return [String]
458
- def render_class_method_style
459
- render_custom_style(self.class.method_style)
460
- end
461
-
462
- # Method returning style from params in view.
463
- # Usage:
464
- #
465
- # <%= ExampleComponent data: data, style: '.my-class { color: red; }' %>
466
- #
467
- # or:
468
- #
469
- # <%= ExampleComponent data: data, style: {style: '.my-class { color: red; }', type: 'sass'} %>
470
- #
471
- # @return [String]
472
- def render_style_from_inline
473
- render_custom_style(@style)
474
- end
475
-
476
- # @param content [String]
477
- # @param type [Symbol]
478
- # @param block [Proc, nil]
479
- # @return [String]
480
- def render_string(content, type, block = nil)
481
- ::Tilt[type].new { content }.render(self, &block)
482
- end
483
-
484
- # @return [String]
485
- def inject_styles
486
- style_content = render_style_from_file + render_class_method_style + render_style_from_inline
487
- return if style_content.empty?
488
-
489
- ::AmberComponent::StyleInjector.inject(style_content)
490
- end
491
123
  end
492
124
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ::AmberComponent
4
+ module Helpers
5
+ # Adds class-specific utilities.
6
+ module ClassHelper
7
+ # Name of the constant this class/module is saved in (in the parent module).
8
+ #
9
+ # @return [String]
10
+ def const_name
11
+ name.split('::').last
12
+ end
13
+
14
+ # Get the exact place where this class/module has been defined.
15
+ #
16
+ # @return [Array(String, Integer), Array(Boolean, Integer)] File path followed by line number.
17
+ def source_location
18
+ module_parent.const_source_location const_name
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'action_view'
4
+
5
+ module ::AmberComponent
6
+ module Helpers
7
+ # Contains methods for quickly rendering
8
+ # components defined under the root namespace `Object`.
9
+ module ComponentHelper
10
+ end
11
+ end
12
+ end
13
+
14
+ class ::ActionView::Base
15
+ # Add those convenience methods to all
16
+ # controllers and views.
17
+ include ::AmberComponent::Helpers::ComponentHelper
18
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'action_view'
4
+
5
+ module ::AmberComponent
6
+ module Helpers
7
+ # Adds a few utility methods for working with CSS
8
+ # inside components.
9
+ module CssHelper
10
+ # Helper method which creates a name for a css class or id
11
+ # which is scoped to the current component class.
12
+ #
13
+ # self.class #=> Navigation::DropdownMenuComponent
14
+ # css_id(:list_item) #=> "navigation-dropdown_menu_component--list_item"
15
+ #
16
+ # @param name [String, Symbol]
17
+ # @return [String]
18
+ def css_identifier(name)
19
+ "#{self.class.name.underscore.gsub('/', '-')}--#{name.to_s.underscore}"
20
+ end
21
+
22
+ alias css_id css_identifier
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ::AmberComponent
4
+ # Contains all helpers added by `amber_component`
5
+ module Helpers
6
+ end
7
+ end
8
+
9
+ require_relative 'helpers/component_helper'
10
+ require_relative 'helpers/class_helper'
11
+ require_relative 'helpers/css_helper'
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ::AmberComponent
4
+ # Class which hooks into Rails
5
+ # and configures the application.
6
+ class Railtie < ::Rails::Railtie
7
+ initializer 'amber_component.initialization' do |app|
8
+ app.config.assets.paths << (app.root / 'app' / 'components')
9
+
10
+ next if ::Rails.env.production?
11
+
12
+ components_root = app.root / 'app' / 'components'
13
+ component_paths = ::Dir[components_root / '**' / '*.rb']
14
+ app.config.eager_load_paths += component_paths
15
+
16
+ ::ActiveSupport::Reloader.to_prepare do
17
+ component_paths.each { |file| require_dependency(components_root / file) }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ::AmberComponent
4
+ # Provides universal methods for rendering components.
5
+ module Rendering
6
+ # Class methods for rendering.
7
+ module ClassMethods
8
+ # @param kwargs [Hash{Symbol => Object}]
9
+ # @return [String]
10
+ def render(**kwargs, &block)
11
+ comp = new(**kwargs)
12
+
13
+ comp.render(&block)
14
+ end
15
+
16
+ alias call render
17
+ end
18
+
19
+ # Instance methods for rendering.
20
+ module InstanceMethods
21
+ # @return [String]
22
+ def render(&block)
23
+ run_callbacks :render do
24
+ element = render_view(&block)
25
+ # styles = inject_styles
26
+ # element += styles unless styles.nil?
27
+ element.html_safe
28
+ end
29
+ end
30
+
31
+ # Method used internally by Rails to render an object
32
+ # passed to the `render` method.
33
+ #
34
+ # render MyComponent.new(some: :attribute)
35
+ #
36
+ # @param _context [ActionView::Base]
37
+ # @return [String]
38
+ def render_in(_context)
39
+ render
40
+ end
41
+
42
+ protected
43
+
44
+ # @param content [String]
45
+ # @param type [Symbol]
46
+ # @param block [Proc, nil]
47
+ # @return [String]
48
+ def render_string(content, type, block = nil)
49
+ TemplateHandler.render_from_string(self, content, type, block)
50
+ end
51
+ end
52
+ end
53
+ end