amber_component 0.0.2 → 0.0.4
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/.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
|