amber_component 0.0.2 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -1
- data/.solargraph.yml +1 -2
- data/CONTRIBUTING.md +87 -0
- data/Gemfile +4 -1
- data/Gemfile.lock +24 -99
- data/README.md +25 -42
- data/Rakefile +17 -1
- data/amber_component.gemspec +4 -2
- data/banner.png +0 -0
- data/docs/.bundle/config +2 -0
- data/docs/.gitignore +5 -0
- data/docs/404.html +25 -0
- data/docs/Gemfile +37 -0
- data/docs/Gemfile.lock +89 -0
- data/docs/README.md +19 -0
- data/docs/_config.yml +148 -0
- data/docs/_data/amber_component.yml +3 -0
- data/docs/_sass/_variables.scss +2 -0
- data/docs/_sass/color_schemes/amber_component.scss +11 -0
- data/docs/_sass/custom/custom.scss +60 -0
- data/docs/api/index.md +8 -0
- data/docs/assets/images/logo_wide.png +0 -0
- data/docs/changelog/index.md +8 -0
- data/docs/favicon.ico +0 -0
- data/docs/getting_started/index.md +8 -0
- data/docs/getting_started/installation.md +7 -0
- data/docs/getting_started/ruby_support.md +7 -0
- data/docs/getting_started/wireframes.md +7 -0
- data/docs/index.md +17 -0
- data/docs/introduction/basic_usage.md +7 -0
- data/docs/introduction/index.md +8 -0
- data/docs/introduction/why_amber_component.md +7 -0
- data/docs/resources/index.md +8 -0
- data/docs/styles/index.md +8 -0
- data/docs/styles/usage.md +7 -0
- data/docs/views/index.md +8 -0
- data/docs/views/usage.md +7 -0
- data/icon.png +0 -0
- data/lib/amber_component/assets.rb +59 -0
- data/lib/amber_component/base.rb +85 -434
- data/lib/amber_component/helpers/class_helper.rb +22 -0
- data/lib/amber_component/helpers/component_helper.rb +19 -0
- data/lib/amber_component/helpers/css_helper.rb +25 -0
- data/lib/amber_component/helpers.rb +11 -0
- data/lib/amber_component/prop_definition.rb +54 -0
- data/lib/amber_component/props.rb +111 -0
- data/lib/amber_component/railtie.rb +21 -0
- data/lib/amber_component/rendering.rb +53 -0
- data/lib/amber_component/template_handler/erb.rb +17 -0
- data/lib/amber_component/template_handler.rb +26 -0
- data/lib/amber_component/typed_content.rb +3 -3
- data/lib/amber_component/version.rb +1 -1
- data/lib/amber_component/views.rb +198 -0
- data/lib/amber_component.rb +14 -11
- data/lib/generators/amber_component/install_generator.rb +23 -0
- data/lib/generators/amber_component/templates/application_component.rb +13 -0
- data/lib/generators/amber_component_generator.rb +24 -0
- data/lib/generators/templates/component.rb.erb +9 -0
- data/lib/generators/templates/component_test.rb.erb +9 -0
- data/lib/generators/templates/style.css.erb +3 -0
- data/lib/generators/templates/view.html.erb +3 -0
- metadata +87 -19
- data/.kanbn/index.md +0 -23
- data/.kanbn/tasks/add-instance-variables-to-view-when-block-given-markdown.md +0 -9
- data/.kanbn/tasks/bind-clas-to-action-view-method-call-example-component-data-data-without-calling-any-method.md +0 -9
- data/.kanbn/tasks/bind-scoped-css-to-head-of-doc.md +0 -10
- data/.kanbn/tasks/check-if-we-need-full-rails-gem-pack.md +0 -9
- data/.kanbn/tasks/simple-proto.md +0 -10
- data/.kanbn/tasks/verify-if-template-is-haml-or-erb.md +0 -9
- data/PLANS.md +0 -17
- data/lib/amber_component/helper.rb +0 -8
- data/lib/amber_component/style_injector.rb +0 -52
- data/sig/amber_components.rbs +0 -4
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'prop_definition'
|
4
|
+
|
5
|
+
module ::AmberComponent
|
6
|
+
# Provides a DSL for defining component
|
7
|
+
# properties.
|
8
|
+
module Props
|
9
|
+
# Class methods for component properties.
|
10
|
+
module ClassMethods
|
11
|
+
# @return [Hash{Symbol => AmberComponent::Prop}]
|
12
|
+
attr_reader :prop_definitions
|
13
|
+
|
14
|
+
# @param names [Array<Symbol>]
|
15
|
+
# @param type [Class, nil]
|
16
|
+
# @param required [Boolean]
|
17
|
+
# @param default [Object, Proc, nil]
|
18
|
+
# @param allow_nil [Boolean]
|
19
|
+
def prop(*names, type: nil, required: false, default: nil, allow_nil: false)
|
20
|
+
@prop_definitions ||= {}
|
21
|
+
include(@prop_methods_module = ::Module.new) if @prop_methods_module.nil?
|
22
|
+
|
23
|
+
names.each do |name|
|
24
|
+
@prop_definitions[name] = prop_def = PropDefinition.new(
|
25
|
+
name: name,
|
26
|
+
type: type,
|
27
|
+
required: required,
|
28
|
+
default: default,
|
29
|
+
allow_nil: allow_nil
|
30
|
+
)
|
31
|
+
raise IncorrectPropTypeError, <<~MSG unless type.nil? || type.is_a?(::Class)
|
32
|
+
`type` should be a class but received `#{type.inspect}` (`#{type.class}`)
|
33
|
+
MSG
|
34
|
+
|
35
|
+
@prop_methods_module.attr_reader name
|
36
|
+
next @prop_methods_module.attr_writer(name) unless prop_def.type?
|
37
|
+
|
38
|
+
@prop_methods_module.class_eval( # rubocop:disable Style/DocumentDynamicEvalDefinition
|
39
|
+
# def phone=(val)
|
40
|
+
# raise IncorrectPropTypeError, <<~MSG unless val.nil? || val.is_a?(String)
|
41
|
+
# #{self.class} received `#{val.class}` instead of `String` for `phone` prop
|
42
|
+
# MSG
|
43
|
+
#
|
44
|
+
# @phone = val
|
45
|
+
# end
|
46
|
+
<<~RUB, __FILE__, __LINE__ + 1
|
47
|
+
def #{name}=(val)
|
48
|
+
raise IncorrectPropTypeError, <<~MSG unless #{allow_nil ? 'val.nil? ||' : nil} val.is_a?(#{prop_def.type})
|
49
|
+
\#{self.class} received `\#{val.class}` instead of `#{prop_def.type}` for `#{name}` prop
|
50
|
+
MSG
|
51
|
+
|
52
|
+
@#{name} = val
|
53
|
+
end
|
54
|
+
RUB
|
55
|
+
) # rubocop:disable Layout/HeredocArgumentClosingParenthesis
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Array<Symbol>, nil]
|
60
|
+
def prop_names
|
61
|
+
@prop_definitions.keys
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Array<Symbol>, nil]
|
65
|
+
def required_prop_names
|
66
|
+
@prop_definitions&.filter_map do |name, prop_def|
|
67
|
+
next unless prop_def.required
|
68
|
+
|
69
|
+
name
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Instance methods for component properties.
|
75
|
+
module InstanceMethods
|
76
|
+
private
|
77
|
+
|
78
|
+
# @param props [Hash{Symbol => Object}]
|
79
|
+
def initialize(**kwargs)
|
80
|
+
bind_props(kwargs)
|
81
|
+
end
|
82
|
+
|
83
|
+
# @param props [Hash{Symbol => Object}]
|
84
|
+
# @return [Boolean] `false` when there are no props defined on the class
|
85
|
+
# and `true` otherwise
|
86
|
+
# @raise [AmberComponent::MissingPropsError] when required props are missing
|
87
|
+
# @raise [AmberComponent::IncorrectPropTypeError]
|
88
|
+
def bind_props(props)
|
89
|
+
return false if self.class.prop_definitions.nil?
|
90
|
+
|
91
|
+
self.class.prop_definitions.each do |name, prop_def|
|
92
|
+
setter_name = :"#{name}="
|
93
|
+
public_send(setter_name, prop_def.default!) if prop_def.default?
|
94
|
+
|
95
|
+
prop_present = props.include? name
|
96
|
+
|
97
|
+
raise MissingPropsError, <<~MSG if prop_def.required? && !prop_present
|
98
|
+
`#{self.class}` has a missing required prop: `#{name.inspect}`
|
99
|
+
MSG
|
100
|
+
|
101
|
+
next unless prop_present
|
102
|
+
|
103
|
+
value = props[name]
|
104
|
+
public_send(setter_name, value)
|
105
|
+
end
|
106
|
+
|
107
|
+
true
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -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
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_view'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
module ::AmberComponent
|
7
|
+
module TemplateHandler
|
8
|
+
# Handles rendering ERB with Rails-like syntax
|
9
|
+
class ERB < ::ActionView::Template::Handlers::ERB::Erubi
|
10
|
+
def initialize(input, properties = {})
|
11
|
+
properties[:bufvar] ||= "@output_buffer"
|
12
|
+
properties[:preamble] = "#{properties[:bufvar]}=#{::ActionView::OutputBuffer}.new;"
|
13
|
+
super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ::AmberComponent
|
4
|
+
# Provides code which handles rendering different
|
5
|
+
# template languages.
|
6
|
+
module TemplateHandler
|
7
|
+
class << self
|
8
|
+
# @param context [AmberComponent::Base]
|
9
|
+
# @param content [String]
|
10
|
+
# @param type [Symbol, String]
|
11
|
+
# @param block [Proc, nil]
|
12
|
+
# @return [String]
|
13
|
+
def render_from_string(context, content, type, block = nil)
|
14
|
+
options = if type.to_sym == :erb
|
15
|
+
{ engine_class: ERB }
|
16
|
+
else
|
17
|
+
{}
|
18
|
+
end
|
19
|
+
|
20
|
+
::Tilt[type].new(options) { content }.render(context, &block)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require_relative 'template_handler/erb'
|
@@ -9,9 +9,9 @@ module ::AmberComponent
|
|
9
9
|
def wrap(val)
|
10
10
|
return val if val.is_a?(self)
|
11
11
|
|
12
|
-
unless val.respond_to?(:[])
|
13
|
-
|
14
|
-
|
12
|
+
raise InvalidTypeError, <<~MSG unless val.respond_to?(:[])
|
13
|
+
`TypedContent` should be a `Hash` or `#{self}` but was `#{val.class}` (#{val.inspect})
|
14
|
+
MSG
|
15
15
|
|
16
16
|
new(type: val[:type], content: val[:content])
|
17
17
|
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ::AmberComponent
|
4
|
+
# Provides methods concerning view registering and rendering.
|
5
|
+
module Views
|
6
|
+
# View types with built-in embedded Ruby
|
7
|
+
#
|
8
|
+
# @return [Set<Symbol>]
|
9
|
+
VIEW_TYPES_WITH_RUBY = ::Set[:erb, :haml, :slim].freeze
|
10
|
+
# @return [Set<Symbol>]
|
11
|
+
ALLOWED_VIEW_TYPES = ::Set[:erb, :haml, :slim, :html, :md, :markdown].freeze
|
12
|
+
# @return [Regexp]
|
13
|
+
VIEW_FILE_REGEXP = /^view\./.freeze
|
14
|
+
|
15
|
+
# Class methods for views.
|
16
|
+
module ClassMethods
|
17
|
+
# Register an inline view by returning a String from the passed block.
|
18
|
+
#
|
19
|
+
# Usage:
|
20
|
+
#
|
21
|
+
# view do
|
22
|
+
# <<~ERB
|
23
|
+
# <h1>
|
24
|
+
# Hello <%= @name %>
|
25
|
+
# </h1>
|
26
|
+
# ERB
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# or:
|
30
|
+
#
|
31
|
+
# view :haml do
|
32
|
+
# <<~HAML
|
33
|
+
# %h1
|
34
|
+
# Hello
|
35
|
+
# = @name
|
36
|
+
# HAML
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# @param type [Symbol]
|
40
|
+
# @return [void]
|
41
|
+
def view(type = :erb, &block)
|
42
|
+
@method_view = TypedContent.new(type: type, content: block)
|
43
|
+
end
|
44
|
+
|
45
|
+
# ERB/Haml/Slim view registered through the `view` method.
|
46
|
+
#
|
47
|
+
# @return [TypedContent]
|
48
|
+
attr_reader :method_view
|
49
|
+
|
50
|
+
# @return [String]
|
51
|
+
def view_path
|
52
|
+
asset_path view_file_name
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return [String, nil]
|
56
|
+
def view_file_name
|
57
|
+
files = asset_file_names(VIEW_FILE_REGEXP)
|
58
|
+
raise MultipleViewsError, "More than one view file for `#{name}` found!" if files.length > 1
|
59
|
+
|
60
|
+
files.first
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [Symbol]
|
64
|
+
def view_type
|
65
|
+
(view_file_name.split('.')[1..].grep_v(/erb/).last || 'erb')&.to_sym
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Instance methods for views.
|
70
|
+
module InstanceMethods
|
71
|
+
protected
|
72
|
+
|
73
|
+
# @return [String]
|
74
|
+
def render_view(&block)
|
75
|
+
view_from_file = render_view_from_file(&block)
|
76
|
+
view_from_method = render_class_method_view(&block)
|
77
|
+
view_from_inline = render_view_from_inline(&block)
|
78
|
+
|
79
|
+
view_content = view_from_file unless view_from_file.empty?
|
80
|
+
view_content = view_from_method unless view_from_method.empty?
|
81
|
+
view_content = view_from_inline unless view_from_inline.empty?
|
82
|
+
|
83
|
+
if view_content.nil? || view_content.empty?
|
84
|
+
raise ViewFileNotFoundError, "View for `#{self.class}` could not be found!"
|
85
|
+
end
|
86
|
+
|
87
|
+
view_content
|
88
|
+
end
|
89
|
+
|
90
|
+
# Helper method to render view from string or with other provided type.
|
91
|
+
#
|
92
|
+
# Usage:
|
93
|
+
#
|
94
|
+
# render_view_from_content('<h1>Hello World</h1>')
|
95
|
+
#
|
96
|
+
# or:
|
97
|
+
#
|
98
|
+
# render_view_from_content content: '**Hello World**', type: 'md'
|
99
|
+
#
|
100
|
+
# @param content [TypedContent, Hash{Symbol => String, Symbol, Proc}, String]
|
101
|
+
# @return [String, nil]
|
102
|
+
def render_view_from_content(content, &block)
|
103
|
+
return '' unless content
|
104
|
+
return content if content.is_a?(::String)
|
105
|
+
|
106
|
+
content = TypedContent.wrap(content)
|
107
|
+
type = content.type
|
108
|
+
content = content.to_s
|
109
|
+
|
110
|
+
if content.empty?
|
111
|
+
raise EmptyViewError, <<~ERR.squish
|
112
|
+
Custom view for `#{self.class}` from view method cannot be empty!
|
113
|
+
ERR
|
114
|
+
end
|
115
|
+
|
116
|
+
unless ALLOWED_VIEW_TYPES.include? type
|
117
|
+
raise UnknownViewTypeError, <<~ERR.squish
|
118
|
+
Unknown view type for `#{self.class}` from view method!
|
119
|
+
Check return value of param type in `view :[type] do`
|
120
|
+
ERR
|
121
|
+
end
|
122
|
+
|
123
|
+
unless VIEW_TYPES_WITH_RUBY.include? type
|
124
|
+
# first render the content with ERB if the
|
125
|
+
# type does not support embedding Ruby by default
|
126
|
+
content = render_string(content, :erb, block)
|
127
|
+
end
|
128
|
+
|
129
|
+
render_string(content, type, block)
|
130
|
+
end
|
131
|
+
|
132
|
+
# @return [String]
|
133
|
+
def render_view_from_file(&block)
|
134
|
+
view_path = self.class.view_path
|
135
|
+
return '' if view_path.nil? || !::File.file?(view_path)
|
136
|
+
|
137
|
+
content = ::File.read(view_path)
|
138
|
+
type = self.class.view_type
|
139
|
+
|
140
|
+
unless VIEW_TYPES_WITH_RUBY.include? type
|
141
|
+
content = render_string(content, :erb, block)
|
142
|
+
end
|
143
|
+
|
144
|
+
render_string(content, type, block)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Method returning view from method in class file.
|
148
|
+
# Usage:
|
149
|
+
#
|
150
|
+
# view do
|
151
|
+
# <<~HTML
|
152
|
+
# <h1>
|
153
|
+
# Hello <%= @name %>
|
154
|
+
# </h1>
|
155
|
+
# HTML
|
156
|
+
# end
|
157
|
+
#
|
158
|
+
# or:
|
159
|
+
#
|
160
|
+
# view :haml do
|
161
|
+
# <<~HAML
|
162
|
+
# %h1
|
163
|
+
# Hello
|
164
|
+
# = @name
|
165
|
+
# HAML
|
166
|
+
# end
|
167
|
+
#
|
168
|
+
# @return [String]
|
169
|
+
def render_class_method_view(&block)
|
170
|
+
render_view_from_content(self.class.method_view, &block)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Method returning view from params in view.
|
174
|
+
# Usage:
|
175
|
+
#
|
176
|
+
# <%= ExampleComponent data: data, view: "<h1>Hello #{@name}</h1>" %>
|
177
|
+
#
|
178
|
+
# or:
|
179
|
+
#
|
180
|
+
# <%= ExampleComponent data: data, view: { content: "<h1>Hello #{@name}</h1>", type: 'erb' } %>
|
181
|
+
#
|
182
|
+
# @return [String]
|
183
|
+
def render_view_from_inline(&block)
|
184
|
+
data = \
|
185
|
+
if @view.is_a? ::String
|
186
|
+
TypedContent.new(
|
187
|
+
type: :erb,
|
188
|
+
content: @view
|
189
|
+
)
|
190
|
+
else
|
191
|
+
@view
|
192
|
+
end
|
193
|
+
|
194
|
+
render_view_from_content(data, &block)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
data/lib/amber_component.rb
CHANGED
@@ -1,24 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'rails'
|
4
3
|
require 'active_support'
|
5
4
|
require 'active_support/core_ext'
|
6
5
|
|
7
6
|
module ::AmberComponent
|
8
7
|
class Error < ::StandardError; end
|
9
|
-
class
|
10
|
-
class
|
8
|
+
class MissingPropsError < Error; end
|
9
|
+
class IncorrectPropTypeError < Error; end
|
10
|
+
class ViewFileNotFoundError < Error; end
|
11
|
+
class InvalidTypeError < Error; end
|
11
12
|
|
12
|
-
class
|
13
|
-
class
|
14
|
-
class
|
15
|
-
|
16
|
-
class EmptyStyle < Error; end
|
17
|
-
class UnknownStyleType < Error; end
|
18
|
-
class MultipleStyles < Error; end
|
13
|
+
class EmptyViewError < Error; end
|
14
|
+
class UnknownViewTypeError < Error; end
|
15
|
+
class MultipleViewsError < Error; end
|
19
16
|
end
|
20
17
|
|
21
18
|
require_relative 'amber_component/version'
|
22
|
-
require_relative 'amber_component/
|
19
|
+
require_relative 'amber_component/helpers'
|
23
20
|
require_relative 'amber_component/typed_content'
|
21
|
+
require_relative 'amber_component/template_handler'
|
22
|
+
require_relative 'amber_component/views'
|
23
|
+
require_relative 'amber_component/assets'
|
24
|
+
require_relative 'amber_component/rendering'
|
25
|
+
require_relative 'amber_component/props'
|
24
26
|
require_relative 'amber_component/base'
|
27
|
+
require_relative 'amber_component/railtie' if defined?(::Rails::Railtie)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module ::AmberComponent
|
6
|
+
module Generators
|
7
|
+
# A Rails generator which installs the `amber_component`
|
8
|
+
# library in a Rails project.
|
9
|
+
class InstallGenerator < ::Rails::Generators::Base
|
10
|
+
desc 'Install the AmberComponent gem'
|
11
|
+
source_root ::File.expand_path('templates', __dir__)
|
12
|
+
|
13
|
+
# copy rake tasks
|
14
|
+
def copy_tasks
|
15
|
+
copy_file 'application_component.rb', 'app/components/application_component.rb'
|
16
|
+
|
17
|
+
inject_into_file 'app/assets/stylesheets/application.css', after: "*= require_tree .\n" do
|
18
|
+
" *= require_tree ./../../components\n"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Abstract class which should serve as a superclass
|
4
|
+
# for all your custom components in this app.
|
5
|
+
#
|
6
|
+
# @abstract Subclass to create a new component.
|
7
|
+
class ::ApplicationComponent < ::AmberComponent::Base
|
8
|
+
# Include your global application helper.
|
9
|
+
include ::ApplicationHelper
|
10
|
+
# Include the helper methods for your application's
|
11
|
+
# routes.
|
12
|
+
include ::Rails.application.routes.url_helpers
|
13
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
# A Rails generator which creates a new Amber component.
|
6
|
+
class AmberComponentGenerator < ::Rails::Generators::NamedBase
|
7
|
+
desc 'Generate a new component'
|
8
|
+
source_root ::File.expand_path('templates', __dir__)
|
9
|
+
|
10
|
+
# copy rake tasks
|
11
|
+
def copy_tasks
|
12
|
+
template 'component.rb.erb', "app/components/#{file_path}.rb"
|
13
|
+
template 'component_test.rb.erb', "test/components/#{file_path}_test.rb"
|
14
|
+
template 'view.html.erb', "app/components/#{file_path}/view.html.erb"
|
15
|
+
template 'style.css.erb', "app/components/#{file_path}/style.css"
|
16
|
+
end
|
17
|
+
|
18
|
+
def file_name
|
19
|
+
name = super
|
20
|
+
return name if name.end_with? '_component'
|
21
|
+
|
22
|
+
"#{name}_component"
|
23
|
+
end
|
24
|
+
end
|