actionview-component 1.3.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 03a3c04aa78f3fcc16c350b49955870f60e712ad0383eb924e7053d7208431d0
4
+ data.tar.gz: 299741730bcfe4eb6640e5899e3973d4fd303dc28aadc79103eb72bdcd2382ec
5
+ SHA512:
6
+ metadata.gz: '01091f9da0436fcbfcbdfd602bcf7595c9c061738f49b9fb70305474218b6b928382daed58c4a3ebc1d08991815ecc965cc6a19b8c4384c35338d672ee5b456e'
7
+ data.tar.gz: 32fae5a27cb1f1f939ce5b59947b3785f1b4ee73a8e9b3b1a95eb1351800aa005518b61b6f24d8570790c1e45d128bf87ab28e4f14cecac1479d33a4ed30a524
@@ -0,0 +1,35 @@
1
+ name: Ruby on Rails
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ rails_version: [5.0.0, 5.2.3, 6.0.0, master]
11
+ ruby_version: [2.3.x, 2.4.x, 2.5.x, 2.6.x]
12
+ exclude:
13
+ - rails_version: master
14
+ ruby_version: 2.4.x
15
+ - rails_version: master
16
+ ruby_version: 2.3.x
17
+ - rails_version: 6.0.0
18
+ ruby_version: 2.4.x
19
+ - rails_version: 6.0.0
20
+ ruby_version: 2.3.x
21
+ steps:
22
+ - uses: actions/checkout@master
23
+ - name: Setup Ruby
24
+ uses: actions/setup-ruby@v1
25
+ with:
26
+ version: ${{ matrix.ruby_version }}
27
+ - name: Build and test with Rake
28
+ run: |
29
+ gem install bundler:1.14.0
30
+ bundle update
31
+ bundle install --jobs 4 --retry 3
32
+ bundle exec rake
33
+ bundle exec rubocop
34
+ env:
35
+ RAILS_VERSION: ${{ matrix.rails_version }}
@@ -0,0 +1,51 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+ /test/log/*
13
+
14
+ # Used by dotenv library to load environment variables.
15
+ # .env
16
+
17
+ ## Specific to RubyMotion:
18
+ .dat*
19
+ .repl_history
20
+ build/
21
+ *.bridgesupport
22
+ build-iPhoneOS/
23
+ build-iPhoneSimulator/
24
+
25
+ ## Specific to RubyMotion (use of CocoaPods):
26
+ #
27
+ # We recommend against adding the Pods directory to your .gitignore. However
28
+ # you should judge for yourself, the pros and cons are mentioned at:
29
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
30
+ #
31
+ # vendor/Pods/
32
+
33
+ ## Documentation cache and generated files:
34
+ /.yardoc/
35
+ /_yardoc/
36
+ /doc/
37
+ /rdoc/
38
+
39
+ ## Environment normalization:
40
+ /.bundle/
41
+ /vendor/bundle
42
+ /lib/bundler/man/
43
+
44
+ # for a library or gem, you might want to ignore these files since the code is
45
+ # intended to run in multiple environments; otherwise, check them in:
46
+ # Gemfile.lock
47
+ # .ruby-version
48
+ # .ruby-gemset
49
+
50
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
51
+ .rvmrc
@@ -0,0 +1,4 @@
1
+ inherit_gem:
2
+ rubocop-github:
3
+ - config/default.yml
4
+ - config/rails.yml
@@ -0,0 +1,65 @@
1
+ # v1.3.4
2
+
3
+ * Template errors surface correct file and line number.
4
+
5
+ *Justin Coyne*
6
+
7
+ * Allow access to `request` inside components.
8
+
9
+ *Joel Hawksley*
10
+
11
+ # v1.3.3
12
+
13
+ * Do not raise error when sidecar files that are not templates exist.
14
+
15
+ *Joel Hawksley*
16
+
17
+ # v1.3.2
18
+
19
+ * Support rendering views from inside component templates.
20
+
21
+ *Patrick Sinclair*
22
+
23
+ # v1.3.1
24
+
25
+ * Fix bug where rendering nested content caused an error.
26
+
27
+ *Joel Hawksley, Aaron Patterson*
28
+
29
+ # v1.3.0
30
+
31
+ * Components are rendered with enough controller context to support rendering of partials and forms.
32
+
33
+ *Patrick Sinclair, Joel Hawksley, Aaron Patterson*
34
+
35
+ # v1.2.1
36
+
37
+ * `actionview-component` is now tested against Ruby 2.3/2.4 and Rails 5.0.0.
38
+
39
+ # v1.2.0
40
+
41
+ * The `render_component` test helper has been renamed to `render_inline`. `render_component` has been deprecated and will be removed in v2.0.0.
42
+
43
+ *Joel Hawksley*
44
+
45
+ * Components are now rendered with `render MyComponent, foo: :bar` syntax. The existing `render MyComponent.new(foo: :bar)` syntax has been deprecated and will be removed in v2.0.0.
46
+
47
+ *Joel Hawksley*
48
+
49
+ # v1.1.0
50
+
51
+ * Components now inherit from ActionView::Component::Base
52
+
53
+ *Joel Hawksley*
54
+
55
+ # v1.0.1
56
+
57
+ * Always recompile component templates outside production.
58
+
59
+ *Joel Hawksley, John Hawthorn*
60
+
61
+ # v1.0.0
62
+
63
+ This release extracts the `ActionView::Component` library from the GitHub application.
64
+
65
+ It will be published on RubyGems under the existing `actionview-component` gem name, as @chancancode has passed us ownership of the gem.
@@ -0,0 +1,76 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, sex characteristics, gender identity and expression,
9
+ level of experience, education, socio-economic status, nationality, personal
10
+ appearance, race, religion, or sexual identity and orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies within all project spaces, and it also applies when
49
+ an individual is representing the project or its community in public spaces.
50
+ Examples of representing a project or community include using an official
51
+ project e-mail address, posting via an official social media account, or acting
52
+ as an appointed representative at an online or offline event. Representation of
53
+ a project may be further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at opensource@github.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72
+
73
+ [homepage]: https://www.contributor-covenant.org
74
+
75
+ For answers to common questions about this code of conduct, see
76
+ https://www.contributor-covenant.org/faq
@@ -0,0 +1,54 @@
1
+ ## Contributing
2
+
3
+ [fork]: https://github.com/github/actionview-component/fork
4
+ [pr]: https://github.com/github/actionview-component/compare
5
+ [style]: https://github.com/styleguide/ruby
6
+ [code-of-conduct]: CODE_OF_CONDUCT.md
7
+
8
+ Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.
9
+
10
+ Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [project's open source license](LICENSE.txt).
11
+
12
+ Please note that this project is released with a [Contributor Code of Conduct][code-of-conduct]. By participating in this project you agree to abide by its terms.
13
+
14
+ ## Submitting a pull request
15
+
16
+ 0. [Fork][fork] and clone the repository
17
+ 0. Configure and install the dependencies: `bundle`
18
+ 0. Make sure the tests pass on your machine: `rake`
19
+ 0. Create a new branch: `git checkout -b my-branch-name`
20
+ 0. Make your change, add tests, and make sure the tests still pass
21
+ 0. Push to your fork and [submit a pull request][pr]
22
+ 0. Pat your self on the back and wait for your pull request to be reviewed and merged.
23
+
24
+ Here are a few things you can do that will increase the likelihood of your pull request being accepted:
25
+
26
+ - Write tests.
27
+ - Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests.
28
+ - Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
29
+
30
+ ## Releasing
31
+
32
+ If you are the current maintainer of this gem:
33
+
34
+ 1. Create a branch for the release: `git checkout -b release-vxx.xx.xx`
35
+ 1. Bump gem version in `actionview-component.gemspec`.
36
+ 1. Make sure your local dependencies are up to date: `bundle`
37
+ 1. Ensure that tests are green: `bundle exec rake`
38
+ 1. Build a test gem `GEM_VERSION=$(git describe --tags 2>/dev/null | sed 's/-/./g' | sed 's/v//') gem build actionview-component.gemspec`
39
+ 1. Test the test gem:
40
+ 1. Bump the Gemfile and Gemfile.lock versions for an app which relies on this gem
41
+ 1. Install the new gem locally
42
+ 1. Test behavior locally, branch deploy, whatever needs to happen
43
+ 1. Make a PR to github/actionview-component.
44
+ 1. Build a local gem: `gem build actionview-component.gemspec`
45
+ 1. Merge github/actionview-component PR
46
+ 1. Tag and push: `git tag vx.xx.xx; git push --tags`
47
+ 1. Create a GitHub release with the pushed tag (https://github.com/github/actionview-component/releases/new) and populate it with a list of the commits from `git log --pretty=format:"- %s" --reverse refs/tags/[OLD TAG]...refs/tags/[NEW TAG]`
48
+ 1. Push to rubygems.org -- `gem push actionview-component-VERSION.gem`
49
+
50
+ ## Resources
51
+
52
+ - [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
53
+ - [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
54
+ - [GitHub Help](https://help.github.com)
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+ gemspec
5
+
6
+ rails_version = "#{ENV['RAILS_VERSION'] || '6.0.0'}"
7
+
8
+ gem "rails", rails_version == "master" ? { github: "rails/rails" } : rails_version
@@ -0,0 +1,176 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ actionview-component (1.3.4)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ actioncable (6.0.0)
10
+ actionpack (= 6.0.0)
11
+ nio4r (~> 2.0)
12
+ websocket-driver (>= 0.6.1)
13
+ actionmailbox (6.0.0)
14
+ actionpack (= 6.0.0)
15
+ activejob (= 6.0.0)
16
+ activerecord (= 6.0.0)
17
+ activestorage (= 6.0.0)
18
+ activesupport (= 6.0.0)
19
+ mail (>= 2.7.1)
20
+ actionmailer (6.0.0)
21
+ actionpack (= 6.0.0)
22
+ actionview (= 6.0.0)
23
+ activejob (= 6.0.0)
24
+ mail (~> 2.5, >= 2.5.4)
25
+ rails-dom-testing (~> 2.0)
26
+ actionpack (6.0.0)
27
+ actionview (= 6.0.0)
28
+ activesupport (= 6.0.0)
29
+ rack (~> 2.0)
30
+ rack-test (>= 0.6.3)
31
+ rails-dom-testing (~> 2.0)
32
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
33
+ actiontext (6.0.0)
34
+ actionpack (= 6.0.0)
35
+ activerecord (= 6.0.0)
36
+ activestorage (= 6.0.0)
37
+ activesupport (= 6.0.0)
38
+ nokogiri (>= 1.8.5)
39
+ actionview (6.0.0)
40
+ activesupport (= 6.0.0)
41
+ builder (~> 3.1)
42
+ erubi (~> 1.4)
43
+ rails-dom-testing (~> 2.0)
44
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
45
+ activejob (6.0.0)
46
+ activesupport (= 6.0.0)
47
+ globalid (>= 0.3.6)
48
+ activemodel (6.0.0)
49
+ activesupport (= 6.0.0)
50
+ activerecord (6.0.0)
51
+ activemodel (= 6.0.0)
52
+ activesupport (= 6.0.0)
53
+ activestorage (6.0.0)
54
+ actionpack (= 6.0.0)
55
+ activejob (= 6.0.0)
56
+ activerecord (= 6.0.0)
57
+ marcel (~> 0.3.1)
58
+ activesupport (6.0.0)
59
+ concurrent-ruby (~> 1.0, >= 1.0.2)
60
+ i18n (>= 0.7, < 2)
61
+ minitest (~> 5.1)
62
+ tzinfo (~> 1.1)
63
+ zeitwerk (~> 2.1, >= 2.1.8)
64
+ ast (2.4.0)
65
+ builder (3.2.3)
66
+ concurrent-ruby (1.1.5)
67
+ crass (1.0.4)
68
+ erubi (1.8.0)
69
+ globalid (0.4.2)
70
+ activesupport (>= 4.2.0)
71
+ haml (5.1.2)
72
+ temple (>= 0.8.0)
73
+ tilt
74
+ i18n (1.6.0)
75
+ concurrent-ruby (~> 1.0)
76
+ jaro_winkler (1.5.3)
77
+ loofah (2.2.3)
78
+ crass (~> 1.0.2)
79
+ nokogiri (>= 1.5.9)
80
+ mail (2.7.1)
81
+ mini_mime (>= 0.1.1)
82
+ marcel (0.3.3)
83
+ mimemagic (~> 0.3.2)
84
+ method_source (0.9.2)
85
+ mimemagic (0.3.3)
86
+ mini_mime (1.0.2)
87
+ mini_portile2 (2.4.0)
88
+ minitest (5.1.0)
89
+ nio4r (2.5.2)
90
+ nokogiri (1.10.4)
91
+ mini_portile2 (~> 2.4.0)
92
+ parallel (1.17.0)
93
+ parser (2.6.3.0)
94
+ ast (~> 2.4.0)
95
+ rack (2.0.7)
96
+ rack-test (1.1.0)
97
+ rack (>= 1.0, < 3)
98
+ rails (6.0.0)
99
+ actioncable (= 6.0.0)
100
+ actionmailbox (= 6.0.0)
101
+ actionmailer (= 6.0.0)
102
+ actionpack (= 6.0.0)
103
+ actiontext (= 6.0.0)
104
+ actionview (= 6.0.0)
105
+ activejob (= 6.0.0)
106
+ activemodel (= 6.0.0)
107
+ activerecord (= 6.0.0)
108
+ activestorage (= 6.0.0)
109
+ activesupport (= 6.0.0)
110
+ bundler (>= 1.3.0)
111
+ railties (= 6.0.0)
112
+ sprockets-rails (>= 2.0.0)
113
+ rails-dom-testing (2.0.3)
114
+ activesupport (>= 4.2.0)
115
+ nokogiri (>= 1.6)
116
+ rails-html-sanitizer (1.2.0)
117
+ loofah (~> 2.2, >= 2.2.2)
118
+ railties (6.0.0)
119
+ actionpack (= 6.0.0)
120
+ activesupport (= 6.0.0)
121
+ method_source
122
+ rake (>= 0.8.7)
123
+ thor (>= 0.20.3, < 2.0)
124
+ rainbow (3.0.0)
125
+ rake (10.5.0)
126
+ rubocop (0.74.0)
127
+ jaro_winkler (~> 1.5.1)
128
+ parallel (~> 1.10)
129
+ parser (>= 2.6)
130
+ rainbow (>= 2.2.2, < 4.0)
131
+ ruby-progressbar (~> 1.7)
132
+ unicode-display_width (>= 1.4.0, < 1.7)
133
+ rubocop-github (0.13.0)
134
+ rubocop (~> 0.70)
135
+ rubocop-performance (~> 1.3.0)
136
+ rubocop-performance (1.3.0)
137
+ rubocop (>= 0.68.0)
138
+ ruby-progressbar (1.10.1)
139
+ slim (4.0.1)
140
+ temple (>= 0.7.6, < 0.9)
141
+ tilt (>= 2.0.6, < 2.1)
142
+ sprockets (3.7.2)
143
+ concurrent-ruby (~> 1.0)
144
+ rack (> 1, < 3)
145
+ sprockets-rails (3.2.1)
146
+ actionpack (>= 4.0)
147
+ activesupport (>= 4.0)
148
+ sprockets (>= 3.0.0)
149
+ temple (0.8.1)
150
+ thor (0.20.3)
151
+ thread_safe (0.3.6)
152
+ tilt (2.0.9)
153
+ tzinfo (1.2.5)
154
+ thread_safe (~> 0.1)
155
+ unicode-display_width (1.6.0)
156
+ websocket-driver (0.7.1)
157
+ websocket-extensions (>= 0.1.0)
158
+ websocket-extensions (0.1.4)
159
+ zeitwerk (2.1.10)
160
+
161
+ PLATFORMS
162
+ ruby
163
+
164
+ DEPENDENCIES
165
+ actionview-component!
166
+ bundler (>= 1.14)
167
+ haml (~> 5)
168
+ minitest (= 5.1.0)
169
+ rails (= 6.0.0)
170
+ rake (~> 10.0)
171
+ rubocop (~> 0.59)
172
+ rubocop-github (~> 0.13.0)
173
+ slim (~> 4.0)
174
+
175
+ BUNDLED WITH
176
+ 1.17.3
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 GitHub
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,222 @@
1
+ # ActionView::Component
2
+ `ActionView::Component` is a framework for building view components in Rails.
3
+
4
+ **Current Status**: Used in production at GitHub. Because of this, all changes will be thoroughly vetted, which could slow down the process of contributing. We will do our best to actively communicate status of pull requests with any contributors. If you have any substantial changes that you would like to make, it would be great to first [open an issue](http://github.com/github/actionview-component/issues/new) to discuss them with us.
5
+
6
+ ## Roadmap
7
+
8
+ This gem is meant to serve as a precursor to upstreaming the `ActionView::Component` class into Rails. It also serves to enable the usage of `ActionView::Component` in older versions of Rails.
9
+
10
+ Preliminary support for rendering components was merged into Rails `6.1.0.alpha` in https://github.com/rails/rails/pull/36388. Assuming `ActionView::Component` makes it into Rails `6.1`, this gem will then exist to serve as a backport.
11
+
12
+ ## Design philosophy
13
+
14
+ As the goal of this gem is to be upstreamed into Rails, it is designed to integrate as seamlessly as possible, with the [least surprise](https://www.artima.com/intv/ruby4.html).
15
+
16
+ ## Compatibility
17
+
18
+ `actionview-component` is tested for compatibility with combinations of Ruby `2.3`/`2.4`/`2.5`/`2.6` and Rails `5.0.0`/`5.2.3`/`6.0.0`/`6.1.0.alpha`.
19
+
20
+ ## Installation
21
+ Add this line to your application's Gemfile:
22
+
23
+ ```ruby
24
+ gem "actionview-component"
25
+ ```
26
+
27
+ And then execute:
28
+ ```bash
29
+ $ bundle
30
+ ```
31
+
32
+ In `config/application.rb`, add:
33
+
34
+ ```bash
35
+ require "action_view/component/base"
36
+ ```
37
+
38
+ ## Guide
39
+
40
+ ### What are components?
41
+
42
+ `ActionView::Component`s are Ruby classes that are used to render views. They take data as input and return output-safe HTML. Think of them as an evolution of the presenter/decorator/view model pattern, inspired by [React Components](https://reactjs.org/docs/react-component.html).
43
+
44
+ ### Why components?
45
+
46
+ In working on views in the Rails monolith at GitHub (which has over 3700 templates), we have run into several key pain points:
47
+
48
+ #### Testing
49
+
50
+ Currently, Rails encourages testing views via integration or system tests. This discourages us from testing our views thoroughly, due to the costly overhead of exercising the routing/controller layer, instead of just the view. It also often leads to partials being tested for each view they are included in, cheapening the benefit of DRYing up our views.
51
+
52
+ #### Code Coverage
53
+
54
+ Many common Ruby code coverage tools cannot properly handle coverage of views, making it difficult to audit how thorough our tests are and leading to gaps in our test suite.
55
+
56
+ #### Data Flow
57
+
58
+ Unlike a method declaration on an object, views do not declare the values they are expected to receive, making it hard to figure out what context is necessary to render them. This often leads to subtle bugs when we reuse a view across different contexts.
59
+
60
+ #### Standards
61
+
62
+ Our views often fail even the most basic standards of code quality we expect out of our Ruby classes: long methods, deep conditional nesting, and mystery guests abound.
63
+
64
+ ### What are the benefits?
65
+
66
+ #### Testing
67
+
68
+ `ActionView::Component` allows views to be unit-tested. In the main GitHub codebase, our unit tests run in around 25ms/test, vs. ~6s/test for integration tests.
69
+
70
+ #### Code Coverage
71
+
72
+ `ActionView::Component` is at least partially compatible with code coverage tools. We’ve seen some success with SimpleCov.
73
+
74
+ #### Data flow
75
+
76
+ By clearly defining the context necessary to render a component, we’ve found them to be easier to reuse than partials.
77
+
78
+ #### Performance
79
+
80
+ In early benchmarks, we’ve seen performance improvements over the existing rendering pipeline. For a test page with nested renders 10 levels deep, we’re seeing around a 5x increase in speed over partials:
81
+
82
+ ```
83
+ Comparison:
84
+ component: 6515.4 i/s
85
+ partial: 1251.2 i/s - 5.21x slower
86
+ ```
87
+
88
+ _Rails 6.1.0.alpha, [joelhawksley/actionview-component-demo](https://github.com/joelhawksley/actionview-component-demo), /benchmark route, via `RAILS_ENV=production rails s`, measured with [evanphx/benchmark-ips](https://github.com/evanphx/benchmark-ips)_
89
+
90
+ ### When should I use components?
91
+
92
+ Components are most effective in cases where view code is reused or needs to be tested directly.
93
+
94
+ ### Building components
95
+
96
+ Components are subclasses of `ActionView::Component::Base` and live in `app/components`. You may wish to create an `ApplicationComponent` that is a subclass of `ActionView::Component::Base` and inherit from that instead.
97
+
98
+ Component class names end in -`Component`.
99
+
100
+ Components support ActiveModel validations. Components are validated after initialization, but before rendering.
101
+
102
+ Content passed to an `ActionView::Component` as a block is captured and assigned to the `content` accessor.
103
+
104
+ #### Implementation
105
+
106
+ An `ActionView::Component` is a Ruby file and corresponding template file (in any format supported by Rails) with the same base name:
107
+
108
+ `app/components/test_component.rb`:
109
+ ```ruby
110
+ class TestComponent < ActionView::Component::Base
111
+ validates :content, :title, presence: true
112
+
113
+ def initialize(title:)
114
+ @title = title
115
+ end
116
+
117
+ private
118
+
119
+ attr_reader :title
120
+ end
121
+ ```
122
+
123
+ `app/components/test_component.html.erb`:
124
+ ```erb
125
+ <span title="<%= title %>"><%= content %></span>
126
+ ```
127
+
128
+ We can render it in a view as:
129
+
130
+ ```erb
131
+ <%= render(TestComponent, title: "my title") do %>
132
+ Hello, World!
133
+ <% end %>
134
+ ```
135
+
136
+ Which returns:
137
+
138
+ ```html
139
+ <span title="my title">Hello, World!</span>
140
+ ```
141
+
142
+ ##### Supported `render` syntaxes
143
+
144
+ Components can be rendered via:
145
+
146
+ `render(TestComponent, foo: :bar)`
147
+
148
+ `render(component: TestComponent, locals: { foo: :bar })`
149
+
150
+ The following syntax has been deprecated and will be removed in v2.0.0:
151
+
152
+ `render(TestComponent.new(foo: :bar)`
153
+
154
+ #### Error case
155
+
156
+ If the component is rendered with a blank title:
157
+
158
+ ```erb
159
+ <%= render(TestComponent, title: "") do %>
160
+ Hello, World!
161
+ <% end %>
162
+ ```
163
+
164
+ An error will be raised:
165
+
166
+ `ActiveModel::ValidationError: Validation failed: Title can't be blank`
167
+
168
+ ### Testing
169
+
170
+ Components are unit tested directly. The `render_inline` test helper wraps the result in `Nokogiri.HTML`, allowing us to test the component above as:
171
+
172
+ ```ruby
173
+ require "action_view/component/test_helpers"
174
+
175
+ class MyComponentTest < Minitest::Test
176
+ include ActionView::Component::TestHelpers
177
+
178
+ def test_render_component
179
+ assert_equal(
180
+ %(<span title="my title">Hello, World!</span>),
181
+ render_inline(TestComponent, title: "my title") { "Hello, World!" }.css("span").to_html
182
+ )
183
+ end
184
+ end
185
+ ```
186
+
187
+ In general, we’ve found it makes the most sense to test components based on their rendered HTML.
188
+
189
+ ## Frequently Asked Questions
190
+
191
+ ### Can I use other templating languages besides ERB?
192
+
193
+ Yes. This gem is tested against ERB, Haml, and Slim, but it should support most Rails template handlers.
194
+
195
+ ### What happened to inline templates?
196
+
197
+ Inline templates have been removed (for now) due to concerns raised by [@soutaro](https://github.com/soutaro) regarding compatibility with the type systems being developed for Ruby 3.
198
+
199
+ ### Isn't this just like X library?
200
+
201
+ `ActionView::Component` is far from a novel idea! Popular implementations of view components in Ruby include, but are not limited to:
202
+
203
+ - [trailblazer/cells](https://github.com/trailblazer/cells)
204
+ - [dry-rb/dry-view](https://github.com/dry-rb/dry-view)
205
+ - [komposable/komponent](https://github.com/komposable/komponent)
206
+ - [activeadmin/arbre](https://github.com/activeadmin/arbre)
207
+
208
+ ## Resources
209
+
210
+ - [Rethinking the View Layer with Components, RailsConf 2019](https://www.youtube.com/watch?v=y5Z5a6QdA-M)
211
+ - [Introducing ActionView::Component with Joel Hawksley, Ruby on Rails Podcast](http://5by5.tv/rubyonrails/276)
212
+ - [Rails to Introduce View Components, Dev.to](https://dev.to/andy/rails-to-introduce-view-components-3ome)
213
+ - [ActionView::Components in Rails 6.1, Drifting Ruby](https://www.driftingruby.com/episodes/actionview-components-in-rails-6-1)
214
+ - [Demo repository, actionview-component-demo](https://github.com/joelhawksley/actionview-component-demo)
215
+
216
+ ## Contributing
217
+
218
+ Bug reports and pull requests are welcome on GitHub at https://github.com/github/actionview-component. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. We recommend reading the [contributing guide](./CONTRIBUTING.md) as well.
219
+
220
+ ## License
221
+
222
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ task default: :test
@@ -0,0 +1,43 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ lib = File.expand_path("../lib", __FILE__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "actionview-component"
9
+ spec.version = "1.3.4"
10
+ spec.authors = ["GitHub Open Source"]
11
+ spec.email = ["opensource+actionview-component@github.com"]
12
+
13
+ spec.summary = %q{View components for Rails}
14
+ spec.description = %q{View components for Rails, intended for upstreaming in Rails 6.1}
15
+ spec.homepage = "https://github.com/github/actionview-component"
16
+ spec.license = "MIT"
17
+
18
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
19
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
20
+ if spec.respond_to?(:metadata)
21
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
22
+ else
23
+ raise "RubyGems 2.0 or newer is required to protect against " \
24
+ "public gem pushes."
25
+ end
26
+
27
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
28
+ f.match(%r{^(test|spec|features)/})
29
+ end
30
+ spec.bindir = "exe"
31
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ["lib"]
33
+
34
+ spec.required_ruby_version = ">= 2.3.0"
35
+
36
+ spec.add_development_dependency "bundler", ">= 1.14"
37
+ spec.add_development_dependency "rake", "~> 10.0"
38
+ spec.add_development_dependency "minitest", "= 5.1.0"
39
+ spec.add_development_dependency "haml", "~> 5"
40
+ spec.add_development_dependency "slim", "~> 4.0"
41
+ spec.add_development_dependency "rubocop", "~> 0.59"
42
+ spec.add_development_dependency "rubocop-github", "~> 0.13.0"
43
+ end
@@ -0,0 +1,170 @@
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 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
28
+
29
+ module ActionView
30
+ module Component
31
+ class Base < ActionView::Base
32
+ include ActiveModel::Validations
33
+ include ActiveSupport::Configurable
34
+ include ActionController::RequestForgeryProtection
35
+
36
+ # Entrypoint for rendering components. Called by ActionView::Base#render.
37
+ #
38
+ # view_context: ActionView context from calling view
39
+ # args(hash): params to be passed to component being rendered
40
+ # block: optional block to be captured within the view context
41
+ #
42
+ # returns HTML that has been escaped by the respective template handler
43
+ #
44
+ # Example subclass:
45
+ #
46
+ # app/components/my_component.rb:
47
+ # class MyComponent < ActionView::Component::Base
48
+ # def initialize(title:)
49
+ # @title = title
50
+ # end
51
+ # end
52
+ #
53
+ # app/components/my_component.html.erb
54
+ # <span title="<%= @title %>">Hello, <%= content %>!</span>
55
+ #
56
+ # In use:
57
+ # <%= render MyComponent, title: "greeting" do %>world<% end %>
58
+ # returns:
59
+ # <span title="greeting">Hello, world!</span>
60
+ #
61
+ def render_in(view_context, *args, &block)
62
+ self.class.compile
63
+ @view_context = view_context
64
+ @view_renderer ||= view_context.view_renderer
65
+ @lookup_context ||= view_context.lookup_context
66
+ @view_flow ||= view_context.view_flow
67
+
68
+ @content = view_context.capture(&block) if block_given?
69
+ validate!
70
+ call
71
+ end
72
+
73
+ def initialize(*); end
74
+
75
+ def render(options = {}, args = {}, &block)
76
+ if options.is_a?(String) || (options.is_a?(Hash) && options.has_key?(:partial))
77
+ view_context.render(options, args, &block)
78
+ else
79
+ super
80
+ end
81
+ end
82
+
83
+ class << self
84
+ def inherited(child)
85
+ child.include Rails.application.routes.url_helpers unless child < Rails.application.routes.url_helpers
86
+
87
+ super
88
+ end
89
+
90
+ # Compile template to #call instance method, assuming it hasn't been compiled already.
91
+ # We could in theory do this on app boot, at least in production environments.
92
+ # Right now this just compiles the template the first time the component is rendered.
93
+ def compile
94
+ return if @compiled && ActionView::Base.cache_template_loading
95
+
96
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
97
+ def call
98
+ @output_buffer = ActionView::OutputBuffer.new
99
+ #{compiled_template}
100
+ end
101
+ RUBY
102
+
103
+ @compiled = true
104
+ end
105
+
106
+ private
107
+
108
+ def compiled_template
109
+ handler = ActionView::Template.handler_for_extension(File.extname(template_file_path).gsub(".", ""))
110
+ template = File.read(template_file_path)
111
+
112
+ if handler.method(:call).parameters.length > 1
113
+ handler.call(DummyTemplate.new, template)
114
+ else
115
+ handler.call(DummyTemplate.new(template))
116
+ end
117
+ end
118
+
119
+ def template_file_path
120
+ raise NotImplementedError.new("#{self} must implement #initialize.") unless self.instance_method(:initialize).owner == self
121
+
122
+ filename = self.instance_method(:initialize).source_location[0]
123
+ filename_without_extension = filename[0..-(File.extname(filename).length + 1)]
124
+ sibling_template_files = Dir["#{filename_without_extension}.????.{#{ActionView::Template.template_handler_extensions.join(',')}}"] - [filename]
125
+
126
+ if sibling_template_files.length > 1
127
+ raise StandardError.new("More than one template found for #{self}. There can only be one sidecar template file per component.")
128
+ end
129
+
130
+ if sibling_template_files.length == 0
131
+ raise NotImplementedError.new(
132
+ "Could not find a template file for #{self}."
133
+ )
134
+ end
135
+
136
+ sibling_template_files[0]
137
+ end
138
+ end
139
+
140
+ class DummyTemplate
141
+ attr_reader :source
142
+
143
+ def initialize(source = nil)
144
+ @source = source
145
+ end
146
+
147
+ def identifier
148
+ ""
149
+ end
150
+
151
+ # we'll eventually want to update this to support other types
152
+ def type
153
+ "text/html"
154
+ end
155
+ end
156
+
157
+ private
158
+
159
+ def controller
160
+ @controller ||= view_context.controller
161
+ end
162
+
163
+ def request
164
+ @request ||= controller.request
165
+ end
166
+
167
+ attr_reader :content, :view_context
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Component
5
+ module TestHelpers
6
+ def render_inline(component, **args, &block)
7
+ Nokogiri::HTML(controller.view_context.render(component, args, &block))
8
+ end
9
+
10
+ def controller
11
+ @controller ||= ApplicationController.new.tap { |c| c.request = request }
12
+ end
13
+
14
+ def request
15
+ @request ||= ActionDispatch::TestRequest.create
16
+ end
17
+
18
+ def render_component(component, **args, &block)
19
+ ActiveSupport::Deprecation.warn(
20
+ "`render_component` has been deprecated in favor of `render_inline`, and will be removed in v2.0.0."
21
+ )
22
+
23
+ render_inline(component, args, &block)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "action_view/component"
6
+
7
+ require "irb"
8
+ IRB.start(__FILE__)
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle exec rake install
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle exec rake release
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle exec rake
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: actionview-component
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.4
5
+ platform: ruby
6
+ authors:
7
+ - GitHub Open Source
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-10-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 5.1.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 5.1.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: haml
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5'
69
+ - !ruby/object:Gem::Dependency
70
+ name: slim
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '4.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '4.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.59'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.59'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-github
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.13.0
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.13.0
111
+ description: View components for Rails, intended for upstreaming in Rails 6.1
112
+ email:
113
+ - opensource+actionview-component@github.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".github/workflows/ruby_on_rails.yml"
119
+ - ".gitignore"
120
+ - ".rubocop.yml"
121
+ - CHANGELOG.md
122
+ - CODE_OF_CONDUCT.md
123
+ - CONTRIBUTING.md
124
+ - Gemfile
125
+ - Gemfile.lock
126
+ - LICENSE.txt
127
+ - README.md
128
+ - Rakefile
129
+ - actionview-component.gemspec
130
+ - lib/action_view/component/base.rb
131
+ - lib/action_view/component/test_helpers.rb
132
+ - script/bootstrap
133
+ - script/console
134
+ - script/install
135
+ - script/release
136
+ - script/test
137
+ homepage: https://github.com/github/actionview-component
138
+ licenses:
139
+ - MIT
140
+ metadata:
141
+ allowed_push_host: https://rubygems.org
142
+ post_install_message:
143
+ rdoc_options: []
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: 2.3.0
151
+ required_rubygems_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ requirements: []
157
+ rubygems_version: 3.0.3
158
+ signing_key:
159
+ specification_version: 4
160
+ summary: View components for Rails
161
+ test_files: []