actionview-component 1.3.5 → 1.5.2
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/.github/workflows/ruby_on_rails.yml +2 -6
- data/CHANGELOG.md +64 -0
- data/Gemfile.lock +5 -5
- data/README.md +77 -3
- data/actionview-component.gemspec +1 -1
- data/lib/action_view/component.rb +5 -0
- data/lib/action_view/component/base.rb +120 -76
- data/lib/action_view/component/monkey_patch.rb +27 -0
- data/lib/action_view/component/preview.rb +106 -0
- data/lib/action_view/component/railtie.rb +63 -0
- data/lib/action_view/component/test_helpers.rb +9 -1
- data/lib/action_view/component/version.rb +2 -2
- data/lib/rails/generators/component/USAGE +13 -0
- data/lib/rails/generators/component/component_generator.rb +50 -0
- data/lib/rails/generators/component/templates/component.html.erb.tt +5 -0
- data/lib/rails/generators/component/templates/component.rb.tt +9 -0
- data/lib/rails/generators/test_unit/component_generator.rb +20 -0
- data/lib/rails/generators/test_unit/templates/component_test.rb.tt +12 -0
- data/lib/railties/lib/rails.rb +6 -0
- data/lib/railties/lib/rails/component_examples_controller.rb +9 -0
- data/lib/railties/lib/rails/components_controller.rb +55 -0
- data/lib/railties/lib/rails/templates/rails/components/index.html.erb +8 -0
- data/lib/railties/lib/rails/templates/rails/components/preview.html.erb +1 -0
- data/lib/railties/lib/rails/templates/rails/components/previews.html.erb +6 -0
- data/lib/railties/lib/rails/templates/rails/examples/show.html.erb +1 -0
- metadata +23 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 30c2d62abc3a38ba7f383a934ea3a95d6e490f81868d062d9263130e6d694d84
|
|
4
|
+
data.tar.gz: 8cc4375853920bd79183e9e634e00a9f4804726c2fc2a60328f3f1ad664bccef
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 98e2021c3308374c1ef1d92d044b18bdcbb071e422a52cbfe102f984cbf34521d6c19b4f9b60b511f3f9ffa1e3b2e0fc7b5ec31dcf22420f5d4240baad42e881
|
|
7
|
+
data.tar.gz: 7e68770c3ec42381c7fce1028834b95ce4c6d04e1ce71e2849ce04a99b73b3539c2246c35bd77421a63c754e66fac083adaac1ae531bc37fb306293cd7a9a61e
|
|
@@ -8,22 +8,18 @@ jobs:
|
|
|
8
8
|
strategy:
|
|
9
9
|
matrix:
|
|
10
10
|
rails_version: [5.0.0, 5.2.3, 6.0.0, master]
|
|
11
|
-
ruby_version: [2.
|
|
11
|
+
ruby_version: [2.4.x, 2.5.x, 2.6.x]
|
|
12
12
|
exclude:
|
|
13
13
|
- rails_version: master
|
|
14
14
|
ruby_version: 2.4.x
|
|
15
|
-
- rails_version: master
|
|
16
|
-
ruby_version: 2.3.x
|
|
17
15
|
- rails_version: 6.0.0
|
|
18
16
|
ruby_version: 2.4.x
|
|
19
|
-
- rails_version: 6.0.0
|
|
20
|
-
ruby_version: 2.3.x
|
|
21
17
|
steps:
|
|
22
18
|
- uses: actions/checkout@master
|
|
23
19
|
- name: Setup Ruby
|
|
24
20
|
uses: actions/setup-ruby@v1
|
|
25
21
|
with:
|
|
26
|
-
version: ${{ matrix.ruby_version }}
|
|
22
|
+
ruby-version: ${{ matrix.ruby_version }}
|
|
27
23
|
- name: Build and test with Rake
|
|
28
24
|
run: |
|
|
29
25
|
gem install bundler:1.14.0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,67 @@
|
|
|
1
|
+
# v1.5.2
|
|
2
|
+
|
|
3
|
+
* Disable eager loading initializer.
|
|
4
|
+
|
|
5
|
+
*Kasper Meyer*
|
|
6
|
+
|
|
7
|
+
# v1.5.1
|
|
8
|
+
|
|
9
|
+
* Update railties class to work with Rails 6.
|
|
10
|
+
|
|
11
|
+
*Juan Manuel Ramallo*
|
|
12
|
+
|
|
13
|
+
# v1.5.0
|
|
14
|
+
|
|
15
|
+
Note: `actionview-component` is now loaded by requiring `actionview/component`, not `actionview/component/base`.
|
|
16
|
+
|
|
17
|
+
* Fix issue with generating component method signatures.
|
|
18
|
+
|
|
19
|
+
*Ryan Workman, Dylan Clark*
|
|
20
|
+
|
|
21
|
+
* Create component generator.
|
|
22
|
+
|
|
23
|
+
*Vinicius Stock*
|
|
24
|
+
|
|
25
|
+
* Add helpers proxy.
|
|
26
|
+
|
|
27
|
+
*Kasper Meyer*
|
|
28
|
+
|
|
29
|
+
* Introduce ActionView::Component::Previews.
|
|
30
|
+
|
|
31
|
+
*Juan Manuel Ramallo*
|
|
32
|
+
|
|
33
|
+
# v1.4.0
|
|
34
|
+
|
|
35
|
+
* Fix bug where components broke in application paths with periods.
|
|
36
|
+
|
|
37
|
+
*Anton, Joel Hawksley*
|
|
38
|
+
|
|
39
|
+
* Add support for `cache_if` in component templates.
|
|
40
|
+
|
|
41
|
+
*Aaron Patterson, Joel Hawksley*
|
|
42
|
+
|
|
43
|
+
* Add support for variants.
|
|
44
|
+
|
|
45
|
+
*Juan Manuel Ramallo*
|
|
46
|
+
|
|
47
|
+
* Fix bug in virtual path lookup.
|
|
48
|
+
|
|
49
|
+
*Juan Manuel Ramallo*
|
|
50
|
+
|
|
51
|
+
* Preselect the rendered component in render_inline.
|
|
52
|
+
|
|
53
|
+
*Elia Schito*
|
|
54
|
+
|
|
55
|
+
# v1.3.6
|
|
56
|
+
|
|
57
|
+
* Allow template file names without format.
|
|
58
|
+
|
|
59
|
+
*Joel Hawksley*
|
|
60
|
+
|
|
61
|
+
* Add support for translations.
|
|
62
|
+
|
|
63
|
+
*Juan Manuel Ramallo*
|
|
64
|
+
|
|
1
65
|
# v1.3.5
|
|
2
66
|
|
|
3
67
|
* Re-expose `controller` method.
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
actionview-component (1.
|
|
4
|
+
actionview-component (1.5.2)
|
|
5
5
|
|
|
6
6
|
GEM
|
|
7
7
|
remote: https://rubygems.org/
|
|
@@ -64,7 +64,7 @@ GEM
|
|
|
64
64
|
ast (2.4.0)
|
|
65
65
|
builder (3.2.3)
|
|
66
66
|
concurrent-ruby (1.1.5)
|
|
67
|
-
crass (1.0.
|
|
67
|
+
crass (1.0.5)
|
|
68
68
|
erubi (1.8.0)
|
|
69
69
|
globalid (0.4.2)
|
|
70
70
|
activesupport (>= 4.2.0)
|
|
@@ -74,7 +74,7 @@ GEM
|
|
|
74
74
|
i18n (1.6.0)
|
|
75
75
|
concurrent-ruby (~> 1.0)
|
|
76
76
|
jaro_winkler (1.5.3)
|
|
77
|
-
loofah (2.
|
|
77
|
+
loofah (2.3.1)
|
|
78
78
|
crass (~> 1.0.2)
|
|
79
79
|
nokogiri (>= 1.5.9)
|
|
80
80
|
mail (2.7.1)
|
|
@@ -87,7 +87,7 @@ GEM
|
|
|
87
87
|
mini_portile2 (2.4.0)
|
|
88
88
|
minitest (5.1.0)
|
|
89
89
|
nio4r (2.5.2)
|
|
90
|
-
nokogiri (1.10.
|
|
90
|
+
nokogiri (1.10.5)
|
|
91
91
|
mini_portile2 (~> 2.4.0)
|
|
92
92
|
parallel (1.17.0)
|
|
93
93
|
parser (2.6.3.0)
|
|
@@ -168,7 +168,7 @@ DEPENDENCIES
|
|
|
168
168
|
minitest (= 5.1.0)
|
|
169
169
|
rails (= 6.0.0)
|
|
170
170
|
rake (~> 10.0)
|
|
171
|
-
rubocop (
|
|
171
|
+
rubocop (= 0.74)
|
|
172
172
|
rubocop-github (~> 0.13.0)
|
|
173
173
|
slim (~> 4.0)
|
|
174
174
|
|
data/README.md
CHANGED
|
@@ -15,7 +15,7 @@ As the goal of this gem is to be upstreamed into Rails, it is designed to integr
|
|
|
15
15
|
|
|
16
16
|
## Compatibility
|
|
17
17
|
|
|
18
|
-
`actionview-component` is tested for compatibility with combinations of Ruby `2.
|
|
18
|
+
`actionview-component` is tested for compatibility with combinations of Ruby `2.4`/`2.5`/`2.6` and Rails `5.0.0`/`5.2.3`/`6.0.0`/`6.1.0.alpha`.
|
|
19
19
|
|
|
20
20
|
## Installation
|
|
21
21
|
Add this line to your application's Gemfile:
|
|
@@ -32,7 +32,7 @@ $ bundle
|
|
|
32
32
|
In `config/application.rb`, add:
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
-
require "action_view/component
|
|
35
|
+
require "action_view/component"
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
## Guide
|
|
@@ -97,10 +97,26 @@ Components are subclasses of `ActionView::Component::Base` and live in `app/comp
|
|
|
97
97
|
|
|
98
98
|
Component class names end in -`Component`.
|
|
99
99
|
|
|
100
|
+
Component module names are plural, as they are for controllers. (`Users::AvatarComponent`)
|
|
101
|
+
|
|
100
102
|
Components support ActiveModel validations. Components are validated after initialization, but before rendering.
|
|
101
103
|
|
|
102
104
|
Content passed to an `ActionView::Component` as a block is captured and assigned to the `content` accessor.
|
|
103
105
|
|
|
106
|
+
#### Quick start
|
|
107
|
+
|
|
108
|
+
Use the component generator to create a new `ActionView::Component`.
|
|
109
|
+
|
|
110
|
+
The generator accepts the component name and the list of accepted properties as arguments:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
bin/rails generate component Example title content
|
|
114
|
+
invoke test_unit
|
|
115
|
+
create test/components/example_component_test.rb
|
|
116
|
+
create app/components/example_component.rb
|
|
117
|
+
create app/components/example_component.html.erb
|
|
118
|
+
```
|
|
119
|
+
|
|
104
120
|
#### Implementation
|
|
105
121
|
|
|
106
122
|
An `ActionView::Component` is a Ruby file and corresponding template file (in any format supported by Rails) with the same base name:
|
|
@@ -178,7 +194,7 @@ class MyComponentTest < Minitest::Test
|
|
|
178
194
|
def test_render_component
|
|
179
195
|
assert_equal(
|
|
180
196
|
%(<span title="my title">Hello, World!</span>),
|
|
181
|
-
render_inline(TestComponent, title: "my title") { "Hello, World!" }.
|
|
197
|
+
render_inline(TestComponent, title: "my title") { "Hello, World!" }.to_html
|
|
182
198
|
)
|
|
183
199
|
end
|
|
184
200
|
end
|
|
@@ -186,6 +202,64 @@ end
|
|
|
186
202
|
|
|
187
203
|
In general, we’ve found it makes the most sense to test components based on their rendered HTML.
|
|
188
204
|
|
|
205
|
+
#### Action Pack Variants
|
|
206
|
+
|
|
207
|
+
To test a specific variant you can wrap your test with the `with_variant` helper method as:
|
|
208
|
+
|
|
209
|
+
```ruby
|
|
210
|
+
def test_render_component_for_tablet
|
|
211
|
+
with_variant :tablet do
|
|
212
|
+
assert_equal(
|
|
213
|
+
%(<span title="my title">Hello, tablets!</span>),
|
|
214
|
+
render_inline(TestComponent, title: "my title") { "Hello, tablets!" }.css("span").to_html
|
|
215
|
+
)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Previewing Components
|
|
221
|
+
`ActionView::Component::Preview`s provide a way to see how components look by visiting a special URL that renders them.
|
|
222
|
+
In the previous example, the preview class for `TestComponent` would be called `TestComponentPreview` and located in `test/components/previews/test_component_preview.rb`.
|
|
223
|
+
To see the preview of the component with a given title, implement a method that renders the component.
|
|
224
|
+
You can define as many examples as you want:
|
|
225
|
+
|
|
226
|
+
```ruby
|
|
227
|
+
# test/components/previews/test_component_preview.rb
|
|
228
|
+
|
|
229
|
+
class TestComponentPreview < ActionView::Component::Preview
|
|
230
|
+
def with_default_title
|
|
231
|
+
render(TestComponent, title: "Test component default")
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def with_long_title
|
|
235
|
+
render(TestComponent, title: "This is a really long title to see how the component renders this")
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
The previews will be available in <http://localhost:3000/rails/components/test_component/with_default_title>
|
|
241
|
+
and <http://localhost:3000/rails/components/test_component/with_long_title>.
|
|
242
|
+
|
|
243
|
+
Previews use the application layout by default, but you can also use other layouts from your app:
|
|
244
|
+
|
|
245
|
+
```ruby
|
|
246
|
+
# test/components/previews/test_component_preview.rb
|
|
247
|
+
|
|
248
|
+
class TestComponentPreview < ActionView::Component::Preview
|
|
249
|
+
layout "admin"
|
|
250
|
+
|
|
251
|
+
...
|
|
252
|
+
end
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
By default, the preview classes live in `test/components/previews`.
|
|
256
|
+
This can be configured using the `preview_path` option.
|
|
257
|
+
For example, if you want to use `lib/component_previews`, set the following in `config/application.rb`:
|
|
258
|
+
|
|
259
|
+
```ruby
|
|
260
|
+
config.action_view_component.preview_path = "#{Rails.root}/lib/component_previews"
|
|
261
|
+
```
|
|
262
|
+
|
|
189
263
|
## Frequently Asked Questions
|
|
190
264
|
|
|
191
265
|
### Can I use other templating languages besides ERB?
|
|
@@ -39,6 +39,6 @@ Gem::Specification.new do |spec|
|
|
|
39
39
|
spec.add_development_dependency "minitest", "= 5.1.0"
|
|
40
40
|
spec.add_development_dependency "haml", "~> 5"
|
|
41
41
|
spec.add_development_dependency "slim", "~> 4.0"
|
|
42
|
-
spec.add_development_dependency "rubocop", "
|
|
42
|
+
spec.add_development_dependency "rubocop", "= 0.74"
|
|
43
43
|
spec.add_development_dependency "rubocop-github", "~> 0.13.0"
|
|
44
44
|
end
|
|
@@ -1,37 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class ActionView::Base
|
|
8
|
-
module RenderMonkeyPatch
|
|
9
|
-
def render(options = {}, args = {}, &block)
|
|
10
|
-
if options.respond_to?(:render_in)
|
|
11
|
-
ActiveSupport::Deprecation.warn(
|
|
12
|
-
"passing component instances to `render` has been deprecated and will be removed in v2.0.0. Use `render MyComponent, foo: :bar` instead."
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
options.render_in(self, &block)
|
|
16
|
-
elsif options.is_a?(Class) && options < ActionView::Component::Base
|
|
17
|
-
options.new(args).render_in(self, &block)
|
|
18
|
-
elsif options.is_a?(Hash) && options.has_key?(:component)
|
|
19
|
-
options[:component].new(options[:locals]).render_in(self, &block)
|
|
20
|
-
else
|
|
21
|
-
super
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
prepend RenderMonkeyPatch
|
|
27
|
-
end
|
|
3
|
+
require "active_model"
|
|
4
|
+
require "action_view"
|
|
5
|
+
require "active_support/configurable"
|
|
6
|
+
require_relative "preview"
|
|
28
7
|
|
|
29
8
|
module ActionView
|
|
30
9
|
module Component
|
|
31
10
|
class Base < ActionView::Base
|
|
32
11
|
include ActiveModel::Validations
|
|
33
12
|
include ActiveSupport::Configurable
|
|
34
|
-
include
|
|
13
|
+
include ActionView::Component::Previews
|
|
14
|
+
|
|
15
|
+
delegate :form_authenticity_token, :protect_against_forgery?, to: :helpers
|
|
16
|
+
|
|
17
|
+
validate :variant_exists
|
|
35
18
|
|
|
36
19
|
# Entrypoint for rendering components. Called by ActionView::Base#render.
|
|
37
20
|
#
|
|
@@ -64,10 +47,17 @@ module ActionView
|
|
|
64
47
|
@view_renderer ||= view_context.view_renderer
|
|
65
48
|
@lookup_context ||= view_context.lookup_context
|
|
66
49
|
@view_flow ||= view_context.view_flow
|
|
50
|
+
@virtual_path ||= virtual_path
|
|
51
|
+
@variant = @lookup_context.variants.first
|
|
52
|
+
old_current_template = @current_template
|
|
53
|
+
@current_template = self
|
|
67
54
|
|
|
68
55
|
@content = view_context.capture(&block) if block_given?
|
|
69
56
|
validate!
|
|
70
|
-
|
|
57
|
+
|
|
58
|
+
send(self.class.call_method_name(@variant))
|
|
59
|
+
ensure
|
|
60
|
+
@current_template = old_current_template
|
|
71
61
|
end
|
|
72
62
|
|
|
73
63
|
def initialize(*); end
|
|
@@ -84,6 +74,38 @@ module ActionView
|
|
|
84
74
|
@controller ||= view_context.controller
|
|
85
75
|
end
|
|
86
76
|
|
|
77
|
+
# Provides a proxy to access helper methods through
|
|
78
|
+
def helpers
|
|
79
|
+
@helpers ||= view_context
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Removes the first part of the path and the extension.
|
|
83
|
+
def virtual_path
|
|
84
|
+
self.class.source_location.gsub(%r{(.*app/components)|(\.rb)}, "")
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def view_cache_dependencies
|
|
88
|
+
[]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def format # :nodoc:
|
|
92
|
+
@variant
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
def variant_exists
|
|
98
|
+
return if self.class.variants.include?(@variant) || @variant.nil?
|
|
99
|
+
|
|
100
|
+
errors.add(:variant, "'#{@variant}' has no template defined")
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def request
|
|
104
|
+
@request ||= controller.request
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
attr_reader :content, :view_context
|
|
108
|
+
|
|
87
109
|
class << self
|
|
88
110
|
def inherited(child)
|
|
89
111
|
child.include Rails.application.routes.url_helpers unless child < Rails.application.routes.url_helpers
|
|
@@ -91,80 +113,102 @@ module ActionView
|
|
|
91
113
|
super
|
|
92
114
|
end
|
|
93
115
|
|
|
94
|
-
|
|
116
|
+
def call_method_name(variant)
|
|
117
|
+
if variant.present?
|
|
118
|
+
"call_#{variant}"
|
|
119
|
+
else
|
|
120
|
+
"call"
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def source_location
|
|
125
|
+
# Require #initialize to be defined so that we can use
|
|
126
|
+
# method#source_location to look up the file name
|
|
127
|
+
# of the component.
|
|
128
|
+
#
|
|
129
|
+
# If we were able to only support Ruby 2.7+,
|
|
130
|
+
# We could just use Module#const_source_location,
|
|
131
|
+
# rendering this unnecessary.
|
|
132
|
+
raise NotImplementedError.new("#{self} must implement #initialize.") unless self.instance_method(:initialize).owner == self
|
|
133
|
+
|
|
134
|
+
instance_method(:initialize).source_location[0]
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Compile templates to instance methods, assuming they haven't been compiled already.
|
|
95
138
|
# We could in theory do this on app boot, at least in production environments.
|
|
96
|
-
# Right now this just compiles the
|
|
139
|
+
# Right now this just compiles the first time the component is rendered.
|
|
97
140
|
def compile
|
|
98
141
|
return if @compiled && ActionView::Base.cache_template_loading
|
|
99
142
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
143
|
+
validate_templates
|
|
144
|
+
|
|
145
|
+
templates.each do |template|
|
|
146
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
147
|
+
def #{call_method_name(template[:variant])}
|
|
148
|
+
@output_buffer = ActionView::OutputBuffer.new
|
|
149
|
+
#{compiled_template(template[:path])}
|
|
150
|
+
end
|
|
151
|
+
RUBY
|
|
152
|
+
end
|
|
106
153
|
|
|
107
154
|
@compiled = true
|
|
108
155
|
end
|
|
109
156
|
|
|
110
|
-
|
|
157
|
+
def variants
|
|
158
|
+
templates.map { |template| template[:variant] }
|
|
159
|
+
end
|
|
111
160
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
161
|
+
# we'll eventually want to update this to support other types
|
|
162
|
+
def type
|
|
163
|
+
"text/html"
|
|
164
|
+
end
|
|
115
165
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
else
|
|
119
|
-
handler.call(DummyTemplate.new(template))
|
|
120
|
-
end
|
|
166
|
+
def identifier
|
|
167
|
+
""
|
|
121
168
|
end
|
|
122
169
|
|
|
123
|
-
|
|
124
|
-
raise NotImplementedError.new("#{self} must implement #initialize.") unless self.instance_method(:initialize).owner == self
|
|
170
|
+
private
|
|
125
171
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
172
|
+
def templates
|
|
173
|
+
@templates ||=
|
|
174
|
+
(Dir["#{source_location.sub(/#{File.extname(source_location)}$/, '')}.*{#{ActionView::Template.template_handler_extensions.join(',')}}"] - [source_location]).each_with_object([]) do |path, memo|
|
|
175
|
+
memo << {
|
|
176
|
+
path: path,
|
|
177
|
+
variant: path.split(".").second.split("+")[1]&.to_sym,
|
|
178
|
+
handler: path.split(".").last
|
|
179
|
+
}
|
|
180
|
+
end
|
|
181
|
+
end
|
|
129
182
|
|
|
130
|
-
|
|
131
|
-
|
|
183
|
+
def validate_templates
|
|
184
|
+
if templates.empty?
|
|
185
|
+
raise NotImplementedError.new("Could not find a template file for #{self}.")
|
|
132
186
|
end
|
|
133
187
|
|
|
134
|
-
if
|
|
135
|
-
raise
|
|
136
|
-
"Could not find a template file for #{self}."
|
|
137
|
-
)
|
|
188
|
+
if templates.select { |template| template[:variant].nil? }.length > 1
|
|
189
|
+
raise StandardError.new("More than one template found for #{self}. There can only be one default template file per component.")
|
|
138
190
|
end
|
|
139
191
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
class DummyTemplate
|
|
145
|
-
attr_reader :source
|
|
192
|
+
variants.each_with_object(Hash.new(0)) { |variant, counts| counts[variant] += 1 }.each do |variant, count|
|
|
193
|
+
next unless count > 1
|
|
146
194
|
|
|
147
|
-
|
|
148
|
-
|
|
195
|
+
raise StandardError.new("More than one template found for variant '#{variant}' in #{self}. There can only be one template file per variant.")
|
|
196
|
+
end
|
|
149
197
|
end
|
|
150
198
|
|
|
151
|
-
def
|
|
152
|
-
""
|
|
153
|
-
|
|
199
|
+
def compiled_template(file_path)
|
|
200
|
+
handler = ActionView::Template.handler_for_extension(File.extname(file_path).gsub(".", ""))
|
|
201
|
+
template = File.read(file_path)
|
|
154
202
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
203
|
+
if handler.method(:call).parameters.length > 1
|
|
204
|
+
handler.call(self, template)
|
|
205
|
+
else # remove before upstreaming into Rails
|
|
206
|
+
handler.call(OpenStruct.new(source: template, identifier: identifier, type: type))
|
|
207
|
+
end
|
|
158
208
|
end
|
|
159
209
|
end
|
|
160
210
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
def request
|
|
164
|
-
@request ||= controller.request
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
attr_reader :content, :view_context
|
|
211
|
+
ActiveSupport.run_load_hooks(:action_view_component, self)
|
|
168
212
|
end
|
|
169
213
|
end
|
|
170
214
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Monkey patch ActionView::Base#render to support ActionView::Component
|
|
4
|
+
#
|
|
5
|
+
# A version of this monkey patch was upstreamed in https://github.com/rails/rails/pull/36388
|
|
6
|
+
# We'll need to upstream an updated version of this eventually.
|
|
7
|
+
class ActionView::Base
|
|
8
|
+
module RenderMonkeyPatch
|
|
9
|
+
def render(options = {}, args = {}, &block)
|
|
10
|
+
if options.respond_to?(:render_in)
|
|
11
|
+
ActiveSupport::Deprecation.warn(
|
|
12
|
+
"passing component instances (`render MyComponent.new(foo: :bar)`) has been deprecated and will be removed in v2.0.0. Use `render MyComponent, foo: :bar` instead."
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
options.render_in(self, &block)
|
|
16
|
+
elsif options.is_a?(Class) && options < ActionView::Component::Base
|
|
17
|
+
options.new(args).render_in(self, &block)
|
|
18
|
+
elsif options.is_a?(Hash) && options.has_key?(:component)
|
|
19
|
+
options[:component].new(options[:locals]).render_in(self, &block)
|
|
20
|
+
else
|
|
21
|
+
super
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
prepend RenderMonkeyPatch
|
|
27
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/concern"
|
|
4
|
+
require "active_support/descendants_tracker"
|
|
5
|
+
require_relative "test_helpers"
|
|
6
|
+
|
|
7
|
+
module ActionView
|
|
8
|
+
module Component #:nodoc:
|
|
9
|
+
module Previews
|
|
10
|
+
extend ActiveSupport::Concern
|
|
11
|
+
|
|
12
|
+
included do
|
|
13
|
+
# Set the location of component previews through app configuration:
|
|
14
|
+
#
|
|
15
|
+
# config.action_view_component.preview_path = "#{Rails.root}/lib/component_previews"
|
|
16
|
+
#
|
|
17
|
+
mattr_accessor :preview_path, instance_writer: false
|
|
18
|
+
|
|
19
|
+
# Enable or disable component previews through app configuration:
|
|
20
|
+
#
|
|
21
|
+
# config.action_view_component.show_previews = true
|
|
22
|
+
#
|
|
23
|
+
# Defaults to +true+ for development environment
|
|
24
|
+
#
|
|
25
|
+
mattr_accessor :show_previews, instance_writer: false
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class Preview
|
|
30
|
+
extend ActiveSupport::DescendantsTracker
|
|
31
|
+
include ActionView::Component::TestHelpers
|
|
32
|
+
|
|
33
|
+
def render(component, *locals)
|
|
34
|
+
render_inline(component, *locals)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class << self
|
|
38
|
+
# Returns all component preview classes.
|
|
39
|
+
def all
|
|
40
|
+
load_previews if descendants.empty?
|
|
41
|
+
descendants
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Returns the html of the component in its layout
|
|
45
|
+
def call(example)
|
|
46
|
+
example_html = new.public_send(example)
|
|
47
|
+
|
|
48
|
+
Rails::ComponentExamplesController.render(template: "examples/show",
|
|
49
|
+
layout: @layout || "layouts/application",
|
|
50
|
+
assigns: { example: example_html })
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Returns the component object class associated to the preview.
|
|
54
|
+
def component
|
|
55
|
+
self.name.sub(%r{Preview$}, "").constantize
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Returns all of the available examples for the component preview.
|
|
59
|
+
def examples
|
|
60
|
+
public_instance_methods(false).map(&:to_s).sort
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Returns +true+ if the example of the component preview exists.
|
|
64
|
+
def example_exists?(example)
|
|
65
|
+
examples.include?(example)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Returns +true+ if the preview exists.
|
|
69
|
+
def exists?(preview)
|
|
70
|
+
all.any? { |p| p.preview_name == preview }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Find a component preview by its underscored class name.
|
|
74
|
+
def find(preview)
|
|
75
|
+
all.find { |p| p.preview_name == preview }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Returns the underscored name of the component preview without the suffix.
|
|
79
|
+
def preview_name
|
|
80
|
+
name.sub(/Preview$/, "").underscore
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Setter for layout name.
|
|
84
|
+
def layout(layout_name)
|
|
85
|
+
@layout = layout_name
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def load_previews
|
|
91
|
+
if preview_path
|
|
92
|
+
Dir["#{preview_path}/**/*_preview.rb"].sort.each { |file| require_dependency file }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def preview_path
|
|
97
|
+
Base.preview_path
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def show_previews
|
|
101
|
+
Base.show_previews
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "railties/lib/rails/components_controller"
|
|
4
|
+
require "railties/lib/rails/component_examples_controller"
|
|
5
|
+
|
|
6
|
+
module ActionView
|
|
7
|
+
module Component
|
|
8
|
+
class Railtie < Rails::Railtie # :nodoc:
|
|
9
|
+
config.action_view_component = ActiveSupport::OrderedOptions.new
|
|
10
|
+
|
|
11
|
+
# Disabled due to issues with ActionView::Component::Base not defining .logger
|
|
12
|
+
# initializer "action_view_component.logger" do
|
|
13
|
+
# ActiveSupport.on_load(:action_view_component) { self.logger ||= Rails.logger }
|
|
14
|
+
# end
|
|
15
|
+
|
|
16
|
+
initializer "action_view_component.set_configs" do |app|
|
|
17
|
+
options = app.config.action_view_component
|
|
18
|
+
|
|
19
|
+
options.show_previews = Rails.env.development? if options.show_previews.nil?
|
|
20
|
+
|
|
21
|
+
if options.show_previews
|
|
22
|
+
options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/components/previews" : nil
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
ActiveSupport.on_load(:action_view_component) do
|
|
26
|
+
options.each { |k, v| send("#{k}=", v) }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
initializer "action_view_component.set_autoload_paths" do |app|
|
|
31
|
+
options = app.config.action_view_component
|
|
32
|
+
|
|
33
|
+
if options.show_previews && options.preview_path
|
|
34
|
+
ActiveSupport::Dependencies.autoload_paths << options.preview_path
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
initializer "action_view_component.compile_config_methods" do
|
|
39
|
+
ActiveSupport.on_load(:action_view_component) do
|
|
40
|
+
config.compile_methods! if config.respond_to?(:compile_methods!)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Disabled because `ActionView::Component::Base` doesn't implement `#action_methods`
|
|
45
|
+
# initializer "action_view_component.eager_load_actions" do
|
|
46
|
+
# ActiveSupport.on_load(:after_initialize) do
|
|
47
|
+
# ActionView::Component::Base.descendants.each(&:action_methods) if config.eager_load
|
|
48
|
+
# end
|
|
49
|
+
# end
|
|
50
|
+
|
|
51
|
+
config.after_initialize do |app|
|
|
52
|
+
options = app.config.action_view_component
|
|
53
|
+
|
|
54
|
+
if options.show_previews
|
|
55
|
+
app.routes.prepend do
|
|
56
|
+
get "/rails/components" => "rails/components#index", :internal => true
|
|
57
|
+
get "/rails/components/*path" => "rails/components#previews", :internal => true
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -4,7 +4,7 @@ module ActionView
|
|
|
4
4
|
module Component
|
|
5
5
|
module TestHelpers
|
|
6
6
|
def render_inline(component, **args, &block)
|
|
7
|
-
Nokogiri::HTML(controller.view_context.render(component, args, &block))
|
|
7
|
+
Nokogiri::HTML(controller.view_context.render(component, args, &block)).css("body > *")
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def controller
|
|
@@ -22,6 +22,14 @@ module ActionView
|
|
|
22
22
|
|
|
23
23
|
render_inline(component, args, &block)
|
|
24
24
|
end
|
|
25
|
+
|
|
26
|
+
def with_variant(variant)
|
|
27
|
+
old_variants = controller.view_context.lookup_context.variants
|
|
28
|
+
|
|
29
|
+
controller.view_context.lookup_context.variants = variant
|
|
30
|
+
yield
|
|
31
|
+
controller.view_context.lookup_context.variants = old_variants
|
|
32
|
+
end
|
|
25
33
|
end
|
|
26
34
|
end
|
|
27
35
|
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Description:
|
|
2
|
+
============
|
|
3
|
+
Creates a new component and test.
|
|
4
|
+
Pass the component name, either CamelCased or under_scored, and an optional list of attributes as arguments.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
========
|
|
8
|
+
bin/rails generate component Profile name age
|
|
9
|
+
|
|
10
|
+
creates a Profile component and test:
|
|
11
|
+
Component: app/components/profile_component.rb
|
|
12
|
+
Template: app/components/profile_component.html.erb
|
|
13
|
+
Test: test/components/profile_component_test.rb
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rails
|
|
4
|
+
module Generators
|
|
5
|
+
class ComponentGenerator < Rails::Generators::NamedBase
|
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
|
7
|
+
|
|
8
|
+
argument :attributes, type: :array, default: [], banner: "attribute"
|
|
9
|
+
hook_for :test_framework
|
|
10
|
+
check_class_collision suffix: "Component"
|
|
11
|
+
|
|
12
|
+
def create_component_file
|
|
13
|
+
template "component.rb", File.join("app/components", "#{file_name}_component.rb")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def create_template_file
|
|
17
|
+
template "component.html.erb", File.join("app/components", "#{file_name}_component.html.erb")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def file_name
|
|
23
|
+
@_file_name ||= super.sub(/_component\z/i, "")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def requires_content?
|
|
27
|
+
return @requires_content if @asked
|
|
28
|
+
|
|
29
|
+
@asked = true
|
|
30
|
+
@requires_content = ask("Would you like #{class_name} to require content? (Y/n)").downcase == "y"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def parent_class
|
|
34
|
+
defined?(ApplicationComponent) ? "ApplicationComponent" : "ActionView::Component::Base"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def initialize_signature
|
|
38
|
+
if attributes.present?
|
|
39
|
+
attributes.map { |attr| "#{attr.name}:" }.join(", ")
|
|
40
|
+
else
|
|
41
|
+
"*"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def initialize_body
|
|
46
|
+
attributes.map { |attr| "@#{attr.name} = #{attr.name}" }.join("\n ")
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module TestUnit
|
|
4
|
+
module Generators
|
|
5
|
+
class ComponentGenerator < ::Rails::Generators::NamedBase
|
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
|
7
|
+
check_class_collision suffix: "ComponentTest"
|
|
8
|
+
|
|
9
|
+
def create_test_file
|
|
10
|
+
template "component_test.rb", File.join("test/components", "#{file_name}_component_test.rb")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def file_name
|
|
16
|
+
@_file_name ||= super.sub(/_component\z/i, "")
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
class <%= class_name %>ComponentTest < ActiveSupport::TestCase
|
|
4
|
+
include ActionView::Component::TestHelpers
|
|
5
|
+
|
|
6
|
+
test "component renders something useful" do
|
|
7
|
+
# assert_equal(
|
|
8
|
+
# %(<span title="my title">Hello, components!</span>),
|
|
9
|
+
# render_inline(<%= class_name %>, attr: "value") { "Hello, components!" }.css("span").to_html
|
|
10
|
+
# )
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/application_controller"
|
|
4
|
+
load "config/application.rb" unless Rails.root
|
|
5
|
+
|
|
6
|
+
class Rails::ComponentExamplesController < ActionController::Base # :nodoc:
|
|
7
|
+
prepend_view_path File.expand_path("templates/rails", __dir__)
|
|
8
|
+
append_view_path Rails.root.join("app/views")
|
|
9
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/application_controller"
|
|
4
|
+
|
|
5
|
+
class Rails::ComponentsController < Rails::ApplicationController # :nodoc:
|
|
6
|
+
prepend_view_path File.expand_path("templates/rails", __dir__)
|
|
7
|
+
|
|
8
|
+
around_action :set_locale, only: :previews
|
|
9
|
+
before_action :find_preview, only: :previews
|
|
10
|
+
before_action :require_local!, unless: :show_previews?
|
|
11
|
+
|
|
12
|
+
if respond_to?(:content_security_policy)
|
|
13
|
+
content_security_policy(false)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def index
|
|
17
|
+
@previews = ActionView::Component::Preview.all
|
|
18
|
+
@page_title = "Component Previews"
|
|
19
|
+
render template: "components/index"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def previews
|
|
23
|
+
if params[:path] == @preview.preview_name
|
|
24
|
+
@page_title = "Component Previews for #{@preview.preview_name}"
|
|
25
|
+
render template: "components/previews"
|
|
26
|
+
else
|
|
27
|
+
@example_name = File.basename(params[:path])
|
|
28
|
+
render template: "components/preview", layout: false
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def show_previews? # :doc:
|
|
35
|
+
ActionView::Component::Base.show_previews
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def find_preview # :doc:
|
|
39
|
+
candidates = []
|
|
40
|
+
params[:path].to_s.scan(%r{/|$}) { candidates << $` }
|
|
41
|
+
preview = candidates.detect { |candidate| ActionView::Component::Preview.exists?(candidate) }
|
|
42
|
+
|
|
43
|
+
if preview
|
|
44
|
+
@preview = ActionView::Component::Preview.find(preview)
|
|
45
|
+
else
|
|
46
|
+
raise AbstractController::ActionNotFound, "Component preview '#{params[:path]}' not found"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def set_locale
|
|
51
|
+
I18n.with_locale(params[:locale] || I18n.default_locale) do
|
|
52
|
+
yield
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<% @previews.each do |preview| %>
|
|
2
|
+
<h3><%= link_to preview.preview_name.titleize, "/rails/components/#{preview.preview_name}" %></h3>
|
|
3
|
+
<ul>
|
|
4
|
+
<% preview.examples.each do |preview_example| %>
|
|
5
|
+
<li><%= link_to preview_example, "/rails/components/#{preview.preview_name}/#{preview_example}" %></li>
|
|
6
|
+
<% end %>
|
|
7
|
+
</ul>
|
|
8
|
+
<% end %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= raw @preview.call(@example_name) %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= raw @example %>
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: actionview-component
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.5.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- GitHub Open Source
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2019-
|
|
11
|
+
date: 2019-11-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -84,16 +84,16 @@ dependencies:
|
|
|
84
84
|
name: rubocop
|
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
|
86
86
|
requirements:
|
|
87
|
-
- -
|
|
87
|
+
- - '='
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: '0.
|
|
89
|
+
version: '0.74'
|
|
90
90
|
type: :development
|
|
91
91
|
prerelease: false
|
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
|
94
|
-
- -
|
|
94
|
+
- - '='
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
|
-
version: '0.
|
|
96
|
+
version: '0.74'
|
|
97
97
|
- !ruby/object:Gem::Dependency
|
|
98
98
|
name: rubocop-github
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -127,9 +127,26 @@ files:
|
|
|
127
127
|
- README.md
|
|
128
128
|
- Rakefile
|
|
129
129
|
- actionview-component.gemspec
|
|
130
|
+
- lib/action_view/component.rb
|
|
130
131
|
- lib/action_view/component/base.rb
|
|
132
|
+
- lib/action_view/component/monkey_patch.rb
|
|
133
|
+
- lib/action_view/component/preview.rb
|
|
134
|
+
- lib/action_view/component/railtie.rb
|
|
131
135
|
- lib/action_view/component/test_helpers.rb
|
|
132
136
|
- lib/action_view/component/version.rb
|
|
137
|
+
- lib/rails/generators/component/USAGE
|
|
138
|
+
- lib/rails/generators/component/component_generator.rb
|
|
139
|
+
- lib/rails/generators/component/templates/component.html.erb.tt
|
|
140
|
+
- lib/rails/generators/component/templates/component.rb.tt
|
|
141
|
+
- lib/rails/generators/test_unit/component_generator.rb
|
|
142
|
+
- lib/rails/generators/test_unit/templates/component_test.rb.tt
|
|
143
|
+
- lib/railties/lib/rails.rb
|
|
144
|
+
- lib/railties/lib/rails/component_examples_controller.rb
|
|
145
|
+
- lib/railties/lib/rails/components_controller.rb
|
|
146
|
+
- lib/railties/lib/rails/templates/rails/components/index.html.erb
|
|
147
|
+
- lib/railties/lib/rails/templates/rails/components/preview.html.erb
|
|
148
|
+
- lib/railties/lib/rails/templates/rails/components/previews.html.erb
|
|
149
|
+
- lib/railties/lib/rails/templates/rails/examples/show.html.erb
|
|
133
150
|
- script/bootstrap
|
|
134
151
|
- script/console
|
|
135
152
|
- script/install
|