spark-component 1.0.0.pre.alpha
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 +7 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +55 -0
- data/CHANGELOG.md +0 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +179 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +16 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/spark/component/attr.rb +42 -0
- data/lib/spark/component/attribute.rb +197 -0
- data/lib/spark/component/classname.rb +56 -0
- data/lib/spark/component/element.rb +233 -0
- data/lib/spark/component/integration/action_view_component.rb +66 -0
- data/lib/spark/component/tag_attr.rb +44 -0
- data/lib/spark/component/version.rb +7 -0
- data/lib/spark/component.rb +20 -0
- data/spark-component.gemspec +38 -0
- metadata +165 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: e80ca22fbbdff560a6bf1c412bd44ddda40621249e0a88da79a7c689cfe96374
|
|
4
|
+
data.tar.gz: eb224b7a35ba9d5e638aa34b6100a33a796922edff6dccd158a2952553e82360
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 1dcfa28c2753d30bcd99b8999122be6f9001be34aa2d2e0efafdb8bd3c57edc8165826a454c9c7d166e2817fc397d072e84d4f082ad994ac05d863f05aaa9ea2
|
|
7
|
+
data.tar.gz: 673702ead1f88344bf279a0eb507c61a3e49fce8ad3b5153839bf83198a7ad166d898ead08dc5d4117cfd26b9ae7b8e33d44b4c4b7f65eab03c71ef95d8e7653
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 2.6.3
|
|
3
|
+
|
|
4
|
+
Metrics/LineLength:
|
|
5
|
+
Max: 120
|
|
6
|
+
|
|
7
|
+
Metrics/MethodLength:
|
|
8
|
+
Max: 20
|
|
9
|
+
|
|
10
|
+
Metrics/CyclomaticComplexity:
|
|
11
|
+
Max: 10
|
|
12
|
+
|
|
13
|
+
Metrics/PerceivedComplexity:
|
|
14
|
+
Max: 10
|
|
15
|
+
|
|
16
|
+
Metrics/AbcSize:
|
|
17
|
+
Max: 20
|
|
18
|
+
|
|
19
|
+
Metrics/BlockLength:
|
|
20
|
+
Exclude:
|
|
21
|
+
- 'test/**/*.rb'
|
|
22
|
+
|
|
23
|
+
Metrics/ClassLength:
|
|
24
|
+
Exclude:
|
|
25
|
+
- 'test/**/*.rb'
|
|
26
|
+
- 'lib/spark_components/element.rb'
|
|
27
|
+
|
|
28
|
+
Layout/TrailingBlankLines:
|
|
29
|
+
EnforcedStyle: final_blank_line
|
|
30
|
+
|
|
31
|
+
Naming/VariableNumber:
|
|
32
|
+
EnforcedStyle: snake_case
|
|
33
|
+
|
|
34
|
+
Style/Documentation:
|
|
35
|
+
Enabled: false
|
|
36
|
+
|
|
37
|
+
Style/ClassAndModuleChildren:
|
|
38
|
+
Exclude:
|
|
39
|
+
- 'test/tmp/app/components/baz/foobar_component.rb'
|
|
40
|
+
|
|
41
|
+
Style/IfUnlessModifier:
|
|
42
|
+
Enabled: false
|
|
43
|
+
|
|
44
|
+
Style/MixinUsage:
|
|
45
|
+
Exclude:
|
|
46
|
+
- 'test/dummy/bin/setup'
|
|
47
|
+
- 'test/dummy/bin/update'
|
|
48
|
+
|
|
49
|
+
Style/StringLiterals:
|
|
50
|
+
EnforcedStyle: double_quotes
|
|
51
|
+
|
|
52
|
+
Style/FrozenStringLiteralComment:
|
|
53
|
+
Exclude:
|
|
54
|
+
- 'test/**/*.rb'
|
|
55
|
+
|
data/CHANGELOG.md
ADDED
|
File without changes
|
data/CODE_OF_CONDUCT.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
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, gender identity and expression, level of experience,
|
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
|
10
|
+
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 both within project spaces and in public spaces
|
|
49
|
+
when an individual is representing the project or its community. Examples of
|
|
50
|
+
representing a project or community include using an official project e-mail
|
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
|
53
|
+
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 brandon@imathis.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 [http://contributor-covenant.org/version/1/4][version]
|
|
72
|
+
|
|
73
|
+
[homepage]: http://contributor-covenant.org
|
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
spark-component (1.0.0.pre.alpha)
|
|
5
|
+
|
|
6
|
+
GEM
|
|
7
|
+
remote: https://rubygems.org/
|
|
8
|
+
specs:
|
|
9
|
+
actioncable (6.0.1)
|
|
10
|
+
actionpack (= 6.0.1)
|
|
11
|
+
nio4r (~> 2.0)
|
|
12
|
+
websocket-driver (>= 0.6.1)
|
|
13
|
+
actionmailbox (6.0.1)
|
|
14
|
+
actionpack (= 6.0.1)
|
|
15
|
+
activejob (= 6.0.1)
|
|
16
|
+
activerecord (= 6.0.1)
|
|
17
|
+
activestorage (= 6.0.1)
|
|
18
|
+
activesupport (= 6.0.1)
|
|
19
|
+
mail (>= 2.7.1)
|
|
20
|
+
actionmailer (6.0.1)
|
|
21
|
+
actionpack (= 6.0.1)
|
|
22
|
+
actionview (= 6.0.1)
|
|
23
|
+
activejob (= 6.0.1)
|
|
24
|
+
mail (~> 2.5, >= 2.5.4)
|
|
25
|
+
rails-dom-testing (~> 2.0)
|
|
26
|
+
actionpack (6.0.1)
|
|
27
|
+
actionview (= 6.0.1)
|
|
28
|
+
activesupport (= 6.0.1)
|
|
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.1)
|
|
34
|
+
actionpack (= 6.0.1)
|
|
35
|
+
activerecord (= 6.0.1)
|
|
36
|
+
activestorage (= 6.0.1)
|
|
37
|
+
activesupport (= 6.0.1)
|
|
38
|
+
nokogiri (>= 1.8.5)
|
|
39
|
+
actionview (6.0.1)
|
|
40
|
+
activesupport (= 6.0.1)
|
|
41
|
+
builder (~> 3.1)
|
|
42
|
+
erubi (~> 1.4)
|
|
43
|
+
rails-dom-testing (~> 2.0)
|
|
44
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
|
45
|
+
actionview-component (1.4.0)
|
|
46
|
+
activejob (6.0.1)
|
|
47
|
+
activesupport (= 6.0.1)
|
|
48
|
+
globalid (>= 0.3.6)
|
|
49
|
+
activemodel (6.0.1)
|
|
50
|
+
activesupport (= 6.0.1)
|
|
51
|
+
activerecord (6.0.1)
|
|
52
|
+
activemodel (= 6.0.1)
|
|
53
|
+
activesupport (= 6.0.1)
|
|
54
|
+
activestorage (6.0.1)
|
|
55
|
+
actionpack (= 6.0.1)
|
|
56
|
+
activejob (= 6.0.1)
|
|
57
|
+
activerecord (= 6.0.1)
|
|
58
|
+
marcel (~> 0.3.1)
|
|
59
|
+
activesupport (6.0.1)
|
|
60
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
61
|
+
i18n (>= 0.7, < 2)
|
|
62
|
+
minitest (~> 5.1)
|
|
63
|
+
tzinfo (~> 1.1)
|
|
64
|
+
zeitwerk (~> 2.2)
|
|
65
|
+
ast (2.4.0)
|
|
66
|
+
builder (3.2.3)
|
|
67
|
+
byebug (11.0.1)
|
|
68
|
+
coderay (1.1.2)
|
|
69
|
+
concurrent-ruby (1.1.5)
|
|
70
|
+
crass (1.0.5)
|
|
71
|
+
erubi (1.9.0)
|
|
72
|
+
globalid (0.4.2)
|
|
73
|
+
activesupport (>= 4.2.0)
|
|
74
|
+
i18n (1.7.0)
|
|
75
|
+
concurrent-ruby (~> 1.0)
|
|
76
|
+
jaro_winkler (1.5.3)
|
|
77
|
+
loofah (2.3.1)
|
|
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
|
+
memory_profiler (0.9.14)
|
|
85
|
+
method_source (0.9.2)
|
|
86
|
+
mimemagic (0.3.3)
|
|
87
|
+
mini_mime (1.0.2)
|
|
88
|
+
mini_portile2 (2.4.0)
|
|
89
|
+
minitest (5.1.0)
|
|
90
|
+
nio4r (2.5.2)
|
|
91
|
+
nokogiri (1.10.5)
|
|
92
|
+
mini_portile2 (~> 2.4.0)
|
|
93
|
+
parallel (1.18.0)
|
|
94
|
+
parser (2.6.3.0)
|
|
95
|
+
ast (~> 2.4.0)
|
|
96
|
+
pry (0.12.2)
|
|
97
|
+
coderay (~> 1.1.0)
|
|
98
|
+
method_source (~> 0.9.0)
|
|
99
|
+
pry-byebug (3.7.0)
|
|
100
|
+
byebug (~> 11.0)
|
|
101
|
+
pry (~> 0.10)
|
|
102
|
+
rack (2.0.7)
|
|
103
|
+
rack-test (1.1.0)
|
|
104
|
+
rack (>= 1.0, < 3)
|
|
105
|
+
rails (6.0.1)
|
|
106
|
+
actioncable (= 6.0.1)
|
|
107
|
+
actionmailbox (= 6.0.1)
|
|
108
|
+
actionmailer (= 6.0.1)
|
|
109
|
+
actionpack (= 6.0.1)
|
|
110
|
+
actiontext (= 6.0.1)
|
|
111
|
+
actionview (= 6.0.1)
|
|
112
|
+
activejob (= 6.0.1)
|
|
113
|
+
activemodel (= 6.0.1)
|
|
114
|
+
activerecord (= 6.0.1)
|
|
115
|
+
activestorage (= 6.0.1)
|
|
116
|
+
activesupport (= 6.0.1)
|
|
117
|
+
bundler (>= 1.3.0)
|
|
118
|
+
railties (= 6.0.1)
|
|
119
|
+
sprockets-rails (>= 2.0.0)
|
|
120
|
+
rails-dom-testing (2.0.3)
|
|
121
|
+
activesupport (>= 4.2.0)
|
|
122
|
+
nokogiri (>= 1.6)
|
|
123
|
+
rails-html-sanitizer (1.3.0)
|
|
124
|
+
loofah (~> 2.3)
|
|
125
|
+
railties (6.0.1)
|
|
126
|
+
actionpack (= 6.0.1)
|
|
127
|
+
activesupport (= 6.0.1)
|
|
128
|
+
method_source
|
|
129
|
+
rake (>= 0.8.7)
|
|
130
|
+
thor (>= 0.20.3, < 2.0)
|
|
131
|
+
rainbow (3.0.0)
|
|
132
|
+
rake (10.5.0)
|
|
133
|
+
rubocop (0.74.0)
|
|
134
|
+
jaro_winkler (~> 1.5.1)
|
|
135
|
+
parallel (~> 1.10)
|
|
136
|
+
parser (>= 2.6)
|
|
137
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
138
|
+
ruby-progressbar (~> 1.7)
|
|
139
|
+
unicode-display_width (>= 1.4.0, < 1.7)
|
|
140
|
+
ruby-progressbar (1.10.1)
|
|
141
|
+
slim (4.0.1)
|
|
142
|
+
temple (>= 0.7.6, < 0.9)
|
|
143
|
+
tilt (>= 2.0.6, < 2.1)
|
|
144
|
+
sprockets (4.0.0)
|
|
145
|
+
concurrent-ruby (~> 1.0)
|
|
146
|
+
rack (> 1, < 3)
|
|
147
|
+
sprockets-rails (3.2.1)
|
|
148
|
+
actionpack (>= 4.0)
|
|
149
|
+
activesupport (>= 4.0)
|
|
150
|
+
sprockets (>= 3.0.0)
|
|
151
|
+
temple (0.8.2)
|
|
152
|
+
thor (0.20.3)
|
|
153
|
+
thread_safe (0.3.6)
|
|
154
|
+
tilt (2.0.10)
|
|
155
|
+
tzinfo (1.2.5)
|
|
156
|
+
thread_safe (~> 0.1)
|
|
157
|
+
unicode-display_width (1.6.0)
|
|
158
|
+
websocket-driver (0.7.1)
|
|
159
|
+
websocket-extensions (>= 0.1.0)
|
|
160
|
+
websocket-extensions (0.1.4)
|
|
161
|
+
zeitwerk (2.2.1)
|
|
162
|
+
|
|
163
|
+
PLATFORMS
|
|
164
|
+
ruby
|
|
165
|
+
|
|
166
|
+
DEPENDENCIES
|
|
167
|
+
actionview-component
|
|
168
|
+
bundler (~> 2.0)
|
|
169
|
+
memory_profiler
|
|
170
|
+
minitest (= 5.1.0)
|
|
171
|
+
pry-byebug
|
|
172
|
+
rails (~> 6.0.0)
|
|
173
|
+
rake (~> 10.0)
|
|
174
|
+
rubocop
|
|
175
|
+
slim
|
|
176
|
+
spark-component!
|
|
177
|
+
|
|
178
|
+
BUNDLED WITH
|
|
179
|
+
2.0.2
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 Brandon Mathis
|
|
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
|
|
13
|
+
all 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
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Spark::Component
|
|
2
|
+
|
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/spark/component`. To experiment with that code, run `bin/console` for an interactive prompt.
|
|
4
|
+
|
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Add this line to your application's Gemfile:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
gem 'spark-component'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
And then execute:
|
|
16
|
+
|
|
17
|
+
$ bundle
|
|
18
|
+
|
|
19
|
+
Or install it yourself as:
|
|
20
|
+
|
|
21
|
+
$ gem install spark-component
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
TODO: Write usage instructions here
|
|
26
|
+
|
|
27
|
+
## Development
|
|
28
|
+
|
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
30
|
+
|
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
32
|
+
|
|
33
|
+
## Contributing
|
|
34
|
+
|
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/imathis/spark-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.
|
|
36
|
+
|
|
37
|
+
## License
|
|
38
|
+
|
|
39
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
40
|
+
|
|
41
|
+
## Code of Conduct
|
|
42
|
+
|
|
43
|
+
Everyone interacting in the Spark::Component project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/imathis/spark-component/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
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 = %w(lib test)
|
|
8
|
+
t.pattern = 'test/**/*_test.rb'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
Rake::TestTask.new(:bench) do |t|
|
|
12
|
+
t.libs = %w(lib test)
|
|
13
|
+
t.pattern = 'test/**/*_benchmark.rb'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
task default: :test
|
data/bin/console
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "spark/component"
|
|
5
|
+
|
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
+
|
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
+
# require "pry"
|
|
11
|
+
# Pry.start
|
|
12
|
+
|
|
13
|
+
require "irb"
|
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spark
|
|
4
|
+
module Component
|
|
5
|
+
class Attr < Hash
|
|
6
|
+
def initialize(*args, prefix: nil)
|
|
7
|
+
super(*args)
|
|
8
|
+
@prefix = prefix
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def add(hash)
|
|
12
|
+
return self if hash.nil? || hash.keys.empty?
|
|
13
|
+
|
|
14
|
+
dasherize_keys(hash)
|
|
15
|
+
merge!(hash)
|
|
16
|
+
self
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Output all attributes as [prefix-]name="value"
|
|
20
|
+
def to_s
|
|
21
|
+
each_with_object([]) do |(name, value), array|
|
|
22
|
+
if value.is_a?(Component::Attr)
|
|
23
|
+
# Flatten nested hashs and inject them unless empty
|
|
24
|
+
value = value.to_s
|
|
25
|
+
array << value unless value.empty?
|
|
26
|
+
else
|
|
27
|
+
name = [@prefix, name].compact.join("-").gsub(/[\W_]+/, "-")
|
|
28
|
+
array << %(#{name}="#{value}") unless value.nil?
|
|
29
|
+
end
|
|
30
|
+
end.sort.join(" ")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def dasherize_keys(hash)
|
|
36
|
+
hash.merge!(hash.keys.each_with_object({}) do |key, obj|
|
|
37
|
+
obj[key.to_s.gsub(/[\W_]+/, "-")] = hash.delete(key) if key.to_s.include?("_")
|
|
38
|
+
end)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spark/component/tag_attr"
|
|
4
|
+
|
|
5
|
+
# Allows components to easily manage their attributes
|
|
6
|
+
#
|
|
7
|
+
# # Example component usage:
|
|
8
|
+
#
|
|
9
|
+
# class SomeClass
|
|
10
|
+
# include Spark::Component::Attribute
|
|
11
|
+
#
|
|
12
|
+
# # Set a :label attribute, and a :size attribute with default value :large
|
|
13
|
+
# attribute :label, { size: :large }
|
|
14
|
+
#
|
|
15
|
+
# def inialize(attributes = nil)
|
|
16
|
+
# initialize_attributes(attributes)
|
|
17
|
+
# end
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# # When initialized like:
|
|
21
|
+
#
|
|
22
|
+
# some_instance = SomeClass.new(label: "Test")
|
|
23
|
+
#
|
|
24
|
+
# The Class's instance will now have access to
|
|
25
|
+
#
|
|
26
|
+
# @label => "Test"
|
|
27
|
+
# @size => :large
|
|
28
|
+
#
|
|
29
|
+
# And will define access methods:
|
|
30
|
+
#
|
|
31
|
+
# some_instance.attribute_label
|
|
32
|
+
# some_instance.attribute_size
|
|
33
|
+
#
|
|
34
|
+
# Attributes can also be accessed with a helper method
|
|
35
|
+
#
|
|
36
|
+
# some_instance.attr(:label)
|
|
37
|
+
# some_instance.attr(:size)
|
|
38
|
+
#
|
|
39
|
+
# Extending a class will extend its attributes and their defaults.
|
|
40
|
+
#
|
|
41
|
+
# This supports a common set of base attributes such as id, class, data, aria, and html.
|
|
42
|
+
# The html attribute is meant to allow for passing along unaccounted for tag attributes.
|
|
43
|
+
#
|
|
44
|
+
module Spark
|
|
45
|
+
module Component
|
|
46
|
+
BASE_ATTRIBUTES = {
|
|
47
|
+
id: nil,
|
|
48
|
+
class: nil,
|
|
49
|
+
data: {},
|
|
50
|
+
aria: {},
|
|
51
|
+
html: {}
|
|
52
|
+
}.freeze
|
|
53
|
+
|
|
54
|
+
module Attribute
|
|
55
|
+
# All components and elements will support these attributes
|
|
56
|
+
|
|
57
|
+
def self.included(base)
|
|
58
|
+
base.extend(ClassMethods)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Assign instance variables for attributes defined by the class
|
|
62
|
+
def initialize_attributes(attrs = nil)
|
|
63
|
+
attrs ||= {}
|
|
64
|
+
|
|
65
|
+
self.class.attributes.each do |name, default|
|
|
66
|
+
value = attrs[name] || (!default.nil? ? default : nil)
|
|
67
|
+
if set?(value)
|
|
68
|
+
instance_variable_set(:"@#{name}", value)
|
|
69
|
+
attributes[name] = value
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def attr(name)
|
|
75
|
+
instance_variable_get(:"@#{name}")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def attributes
|
|
79
|
+
@attributes ||= {}
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Accepts an array of instance variables and returns
|
|
83
|
+
# a hash of variables with their values, if they are set.
|
|
84
|
+
#
|
|
85
|
+
# Example:
|
|
86
|
+
# @a = 1
|
|
87
|
+
# @b = nil
|
|
88
|
+
#
|
|
89
|
+
# attr_hash(:a, :b, :c) => { a: 1 }
|
|
90
|
+
#
|
|
91
|
+
# Example use case:
|
|
92
|
+
#
|
|
93
|
+
# tag.div(data: attr_hash(:remote, :method)) { ... }
|
|
94
|
+
#
|
|
95
|
+
def attr_hash(*args)
|
|
96
|
+
args.each_with_object({}) do |name, obj|
|
|
97
|
+
val = instance_variable_get(:"@#{name}")
|
|
98
|
+
obj[name] = val unless val.nil?
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Initialize tag attributes from BASE_ATTRIBUTES
|
|
103
|
+
#
|
|
104
|
+
# If a component or element has arguments defined as base attributes
|
|
105
|
+
# they will automatically be added to the tag_attrs
|
|
106
|
+
def tag_attrs
|
|
107
|
+
@tag_attrs ||= TagAttr.new.add attr_hash(*BASE_ATTRIBUTES.keys)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Easy reference a tag's classname
|
|
111
|
+
def classname
|
|
112
|
+
tag_attrs.classname
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Easy reference a tag's data attributes
|
|
116
|
+
def data
|
|
117
|
+
tag_attrs.data
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Easy reference a tag's aria attributes
|
|
121
|
+
def aria
|
|
122
|
+
tag_attrs.aria
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
private
|
|
126
|
+
|
|
127
|
+
# Help filter out attributes with blank values.
|
|
128
|
+
# Not using `blank?` to avoid Rails requirement
|
|
129
|
+
# and because `false` is a valid value.
|
|
130
|
+
def set?(value)
|
|
131
|
+
!(value.nil? || value.respond_to?(:empty?) && value.empty?)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
module ClassMethods
|
|
135
|
+
def attributes
|
|
136
|
+
@attributes ||= Spark::Component::BASE_ATTRIBUTES.dup
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Sets attributes, accepts an array of keys, pass a hash to set default values
|
|
140
|
+
#
|
|
141
|
+
# Examples:
|
|
142
|
+
# - attribute(:foo, :bar, :baz) => { foo: nil, bar: nil, baz: nil }
|
|
143
|
+
# - attribute(:foo, :bar, { baz: true }) => { foo: nil, bar: nil, baz: true }
|
|
144
|
+
# - attribute(foo: true, bar: true, baz: nil) => { foo: true, bar: true, baz: nil }
|
|
145
|
+
#
|
|
146
|
+
def attribute(*args)
|
|
147
|
+
args.each do |arg|
|
|
148
|
+
if arg.is_a?(::Hash)
|
|
149
|
+
arg.each do |key, value|
|
|
150
|
+
set_attribute(key.to_sym, default: value)
|
|
151
|
+
end
|
|
152
|
+
else
|
|
153
|
+
set_attribute(arg.to_sym)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# A namespaced passthrough for validating attributes
|
|
159
|
+
#
|
|
160
|
+
# Option: `choices` - easily validate against an array
|
|
161
|
+
# Essentially a simplification of `inclusion` for attributes.
|
|
162
|
+
#
|
|
163
|
+
# Examples:
|
|
164
|
+
# - validates_attr(:size, choices: %i[small medium large])
|
|
165
|
+
# - validates_attr(:size, choices: SIZES, allow_blank: true)
|
|
166
|
+
#
|
|
167
|
+
def validates_attr(name, options = {})
|
|
168
|
+
name = :"attribute_#{name}"
|
|
169
|
+
|
|
170
|
+
if (choices = options.delete(:choices))
|
|
171
|
+
supported_choices = choices.map { |c| c.is_a?(String) ? c.to_sym : c.to_s }.concat(choices)
|
|
172
|
+
|
|
173
|
+
choices = choices.map(&:inspect).to_sentence(last_word_connector: ", or ")
|
|
174
|
+
message = "\"%<value>s\" is not valid. Options include: #{choices}."
|
|
175
|
+
|
|
176
|
+
options.merge!(inclusion: { in: supported_choices, message: message })
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
validates(name, options)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
private
|
|
183
|
+
|
|
184
|
+
# Store attributes and define methods for validation
|
|
185
|
+
def set_attribute(name, default: nil)
|
|
186
|
+
attributes[name] = default
|
|
187
|
+
|
|
188
|
+
# Define a method to access attribute to support validation
|
|
189
|
+
# Namespace attribute methods to prevent collision with methods or elements
|
|
190
|
+
define_method(:"attribute_#{name}") do
|
|
191
|
+
instance_variable_get(:"@#{name}")
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spark
|
|
4
|
+
module Component
|
|
5
|
+
class Classname < Array
|
|
6
|
+
def initialize(*args, base: nil, &block)
|
|
7
|
+
super(args, &block)
|
|
8
|
+
self.base = base unless base.nil?
|
|
9
|
+
@base_set = !base.nil?
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Many elements have a base class which defines core utlitiy
|
|
13
|
+
# This classname may serve as a root for other element classnames
|
|
14
|
+
# and should be distincly accessible
|
|
15
|
+
#
|
|
16
|
+
# For example:
|
|
17
|
+
# classes = Classname.new
|
|
18
|
+
# classes.base = 'nav__item'
|
|
19
|
+
# now generate a wrapper: "#{classes.base}__wrapper"
|
|
20
|
+
#
|
|
21
|
+
# Ensure base class is the first element in the classes array.
|
|
22
|
+
#
|
|
23
|
+
def base=(klass)
|
|
24
|
+
return if klass.nil? || klass.empty?
|
|
25
|
+
|
|
26
|
+
if @base_set
|
|
27
|
+
self[0] = klass
|
|
28
|
+
else
|
|
29
|
+
unshift klass
|
|
30
|
+
@base_set = true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
uniq!
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def base
|
|
37
|
+
first if @base_set
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def add(*args)
|
|
41
|
+
push(*args.flatten.uniq.reject { |a| a.nil? || include?(a) })
|
|
42
|
+
self
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def join_base(name, separator = "-")
|
|
46
|
+
raise(StandardError, "Base class not defined for `join_base(#{name}, …)`") if base.nil?
|
|
47
|
+
|
|
48
|
+
[base, name].join(separator)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_s
|
|
52
|
+
join(" ")
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spark/component/attribute"
|
|
4
|
+
|
|
5
|
+
module Spark
|
|
6
|
+
module Component
|
|
7
|
+
module Element
|
|
8
|
+
class Error < StandardError; end
|
|
9
|
+
|
|
10
|
+
def self.included(base)
|
|
11
|
+
base.include(Spark::Component::Attribute)
|
|
12
|
+
base.extend(Spark::Component::Element::ClassMethods)
|
|
13
|
+
|
|
14
|
+
%i[_parent _block view_context].each do |name|
|
|
15
|
+
base.define_method(:"#{name}=") { |val| instance_variable_set(:"@#{name}", val) }
|
|
16
|
+
base.define_method(:"#{name}") { instance_variable_get(:"@#{name}") }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initialize(attributes = nil)
|
|
21
|
+
initialize_attributes(attributes)
|
|
22
|
+
initialize_elements
|
|
23
|
+
super
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def render_self
|
|
27
|
+
return @content unless @content.nil?
|
|
28
|
+
|
|
29
|
+
@content = render_block(view_context, &_block)
|
|
30
|
+
validate! if defined?(ActiveModel::Validations)
|
|
31
|
+
@content
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def render_block(view, &block)
|
|
35
|
+
block_given? ? view.capture(self, &block) : nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def yield
|
|
39
|
+
render_self
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
# Create instance variables for each element
|
|
45
|
+
def initialize_elements
|
|
46
|
+
self.class.elements.each do |name, options|
|
|
47
|
+
if (plural_name = options[:multiple])
|
|
48
|
+
|
|
49
|
+
# Setting an empty array allows us to enumerate
|
|
50
|
+
# without needing to check for presence first.
|
|
51
|
+
set_element_variable(plural_name, [])
|
|
52
|
+
else
|
|
53
|
+
set_element_variable(name, nil)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Simplify accessing namespaced element instance variables
|
|
59
|
+
def set_element_variable(name, value)
|
|
60
|
+
instance_variable_set(:"@element_#{name}", value)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Simplify accessing namespaced element instance variables
|
|
64
|
+
def get_element_variable(name)
|
|
65
|
+
instance_variable_get(:"@element_#{name}")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Override the default value for an element's attribute(s)
|
|
69
|
+
def set_element_attr_default(element, attrs = {})
|
|
70
|
+
element_attr_default[element] = attrs
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def element_attr_default
|
|
74
|
+
@element_attr_default ||= {}
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Merge user defined attributes with the overriden attributes of an element
|
|
78
|
+
def merge_element_attr_default(name, attributes)
|
|
79
|
+
attrs = element_attr_default[name]
|
|
80
|
+
attributes = attrs.merge(attributes || {}) unless attrs.nil? || attrs.empty?
|
|
81
|
+
attributes
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
module ClassMethods
|
|
85
|
+
def inherited(child)
|
|
86
|
+
child.elements.replace(elements.merge(child.elements))
|
|
87
|
+
child.attributes.replace(attributes.merge(child.attributes))
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def elements
|
|
91
|
+
@elements ||= {}
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Class method for adding elements
|
|
95
|
+
#
|
|
96
|
+
# Options:
|
|
97
|
+
#
|
|
98
|
+
# name: Symbol
|
|
99
|
+
#
|
|
100
|
+
# Create a method for interacting with an element
|
|
101
|
+
# This name cannot be the same as another instance method
|
|
102
|
+
#
|
|
103
|
+
# multiple: Boolean (default: false)
|
|
104
|
+
#
|
|
105
|
+
# Defining `multiple: true` causes elements to be injected
|
|
106
|
+
# into an array. A pluralized method is created to access
|
|
107
|
+
# each element instance.
|
|
108
|
+
#
|
|
109
|
+
# For example, `element(:item, multiple: true)` will create
|
|
110
|
+
# an `:items` method and each time an item is executed, its
|
|
111
|
+
# instance will be added to items.
|
|
112
|
+
#
|
|
113
|
+
# component: Class
|
|
114
|
+
#
|
|
115
|
+
# By default all elements include Element and extend its class methods
|
|
116
|
+
# Passing a class like `component: Nav::Item` will extend that component
|
|
117
|
+
# adding Element, Attributes, TagAttr and render methods.
|
|
118
|
+
#
|
|
119
|
+
# &config: Block
|
|
120
|
+
#
|
|
121
|
+
# When defining a method, you may pass an optional block to
|
|
122
|
+
# configure attributes, nested elements, or even define methods.
|
|
123
|
+
#
|
|
124
|
+
def element(name, multiple: false, component: nil, &config)
|
|
125
|
+
plural_name = name.to_s.pluralize.to_sym if multiple
|
|
126
|
+
klass = extend_class(component, &config)
|
|
127
|
+
elements[name] = { multiple: plural_name }
|
|
128
|
+
|
|
129
|
+
define_element(name: name, plural: plural_name, multiple: multiple, klass: klass)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Element method will create a new element instance, or if no
|
|
133
|
+
# attributes or block is passed it will return the instance
|
|
134
|
+
# defined for that element.
|
|
135
|
+
#
|
|
136
|
+
# For example when rendering a component, passing attributes or a
|
|
137
|
+
# block will create a new instance of that element.
|
|
138
|
+
#
|
|
139
|
+
# # Some view (Slim)
|
|
140
|
+
# = render(Nav) do |nav|
|
|
141
|
+
# - nav.item(href: "#url") { "link text" }
|
|
142
|
+
#
|
|
143
|
+
# Then when referencing the element in the component's template it
|
|
144
|
+
# the method will return the instance. Call yield to output an
|
|
145
|
+
# elemnet's block
|
|
146
|
+
#
|
|
147
|
+
# # Nav template (Slim)
|
|
148
|
+
# nav
|
|
149
|
+
# - items.each do |item|
|
|
150
|
+
# a href=item.href
|
|
151
|
+
# = item.yield
|
|
152
|
+
#
|
|
153
|
+
def define_element(name:, plural:, multiple:, klass:)
|
|
154
|
+
define_method_if_able(name) do |attributes = nil, &block|
|
|
155
|
+
return get_element_variable(multiple ? plural : name) unless block || attributes
|
|
156
|
+
|
|
157
|
+
attributes = merge_element_attr_default(name, attributes)
|
|
158
|
+
|
|
159
|
+
element = klass.new(attributes)
|
|
160
|
+
element._parent = self
|
|
161
|
+
element._block = block
|
|
162
|
+
element.view_context = view_context
|
|
163
|
+
|
|
164
|
+
# If element supports multiple instances, inject instance
|
|
165
|
+
# into array for later enumeration
|
|
166
|
+
if multiple
|
|
167
|
+
get_element_variable(plural) << element
|
|
168
|
+
else
|
|
169
|
+
set_element_variable(name, element)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
return if !multiple || name == plural
|
|
174
|
+
|
|
175
|
+
# Define a pluralized method name to access enumerable element instances.
|
|
176
|
+
define_method_if_able(plural) do
|
|
177
|
+
get_element_variable(plural)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
private
|
|
182
|
+
|
|
183
|
+
# If an element extends a component, extend that component's class and include the necessary modules
|
|
184
|
+
def extend_class(component, &config)
|
|
185
|
+
base = Class.new(component || Spark::Component::Element::Base, &config)
|
|
186
|
+
define_model_name(base) if defined?(ActiveModel)
|
|
187
|
+
|
|
188
|
+
return base unless component
|
|
189
|
+
|
|
190
|
+
# Allow element to reference its source component
|
|
191
|
+
base.define_singleton_method(:source_component) { component }
|
|
192
|
+
|
|
193
|
+
# Override component when used as an element
|
|
194
|
+
base.include(Spark::Component::Integration::Element) if defined?(Spark::Component::Integration)
|
|
195
|
+
|
|
196
|
+
base
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# ActiveModel validations require a model_name. This helps connect new classes to their proper model names.
|
|
200
|
+
def define_model_name(klass)
|
|
201
|
+
klass.define_singleton_method(:model_name) do
|
|
202
|
+
# try the current class, the parent class, or default to Spark::Component
|
|
203
|
+
named_klass = [self.class, superclass, Spark::Component].reject { |k| k == Class }.first
|
|
204
|
+
ActiveModel::Name.new(named_klass)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Prevent an element method from overwriting an existing method
|
|
209
|
+
def define_method_if_able(method_name, &block)
|
|
210
|
+
# Select instance methods but not those which are intance methods received by extending a class
|
|
211
|
+
methods = (instance_methods - superclass.instance_methods(false))
|
|
212
|
+
|
|
213
|
+
if methods.include?(method_name.to_sym)
|
|
214
|
+
raise(Element::Error, "Method '#{method_name}' already exists.")
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
define_method(method_name, &block)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Base class for non-component elements
|
|
222
|
+
class Base
|
|
223
|
+
include ActiveModel::Validations if defined?(ActiveModel)
|
|
224
|
+
include Spark::Component::Element
|
|
225
|
+
|
|
226
|
+
def initialize(attributes)
|
|
227
|
+
initialize_attributes(attributes)
|
|
228
|
+
initialize_elements
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spark
|
|
4
|
+
module Component
|
|
5
|
+
module Integration
|
|
6
|
+
def self.base_class
|
|
7
|
+
ActionView::Component::Base
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.included(base)
|
|
11
|
+
base.extend(ClassMethods)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Override ActionView::Components::Base `render_in`
|
|
15
|
+
# This is only necessary in order to pass `self` as a block parameter
|
|
16
|
+
def render_in(view_context, &block)
|
|
17
|
+
self.class.compile
|
|
18
|
+
@view_context = view_context
|
|
19
|
+
@view_renderer ||= view_context.view_renderer
|
|
20
|
+
@lookup_context ||= view_context.lookup_context
|
|
21
|
+
@view_flow ||= view_context.view_flow
|
|
22
|
+
@virtual_path ||= virtual_path
|
|
23
|
+
|
|
24
|
+
# Pass self as a block parameter
|
|
25
|
+
@content = render_block(view_context, &block) if block_given?
|
|
26
|
+
validate!
|
|
27
|
+
call
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def render_self
|
|
31
|
+
render_in(view_context, &_block)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Override class methods for components
|
|
35
|
+
module ClassMethods
|
|
36
|
+
# Override source_location to allow a component to uses the superclass's template
|
|
37
|
+
def inherit_template
|
|
38
|
+
define_singleton_method(:source_location) { superclass.source_location }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Use a template from a specific class
|
|
42
|
+
def use_template(klass)
|
|
43
|
+
define_singleton_method(:source_location) { klass.source_location }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
module Element
|
|
48
|
+
def self.included(base)
|
|
49
|
+
base.extend(ClassMethods)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Override class methods when component is used as an element
|
|
53
|
+
module ClassMethods
|
|
54
|
+
# This is used to force components to define an initialize method
|
|
55
|
+
# Overriding it means elements can defer to the original component's initialize method
|
|
56
|
+
def ensure_initializer_defined; end
|
|
57
|
+
|
|
58
|
+
# Allows elements to use component's original tempalte file.
|
|
59
|
+
def source_location
|
|
60
|
+
source_component.source_location
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spark/component/attr"
|
|
4
|
+
require "spark/component/classname"
|
|
5
|
+
|
|
6
|
+
module Spark
|
|
7
|
+
module Component
|
|
8
|
+
class TagAttr < Component::Attr
|
|
9
|
+
def add(hash)
|
|
10
|
+
return self if hash.nil? || hash.keys.empty?
|
|
11
|
+
|
|
12
|
+
# Extract keys for special properties
|
|
13
|
+
extracted = {
|
|
14
|
+
aria: hash.delete(:aria),
|
|
15
|
+
data: hash.delete(:data),
|
|
16
|
+
classname: hash.delete(:class)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# If extracted values exist, add to tag
|
|
20
|
+
extracted.each do |method, val|
|
|
21
|
+
send(method).add(val) unless val.nil? || val.empty?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Add html using recursion, this ensures that nested data, aria, or class
|
|
25
|
+
# attributes are properly merged as the right data types
|
|
26
|
+
add(hash.delete(:html))
|
|
27
|
+
|
|
28
|
+
super(hash)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def aria(*args)
|
|
32
|
+
self[:aria] ||= Component::Attr.new(*args, prefix: :aria)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def data(*args)
|
|
36
|
+
self[:data] ||= Component::Attr.new(*args, prefix: :data)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def classname(*args, base: nil)
|
|
40
|
+
self[:class] ||= Component::Classname.new(*args, base: base)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require "spark/component/version"
|
|
2
|
+
require "spark/component/element"
|
|
3
|
+
|
|
4
|
+
if defined?(ActionView::Component::Base)
|
|
5
|
+
require "spark/component/integration/action_view_component"
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
module Spark
|
|
9
|
+
module Component
|
|
10
|
+
def self.included(base)
|
|
11
|
+
base.include Spark::Component::Element unless base < Spark::Component::Element
|
|
12
|
+
|
|
13
|
+
# If an Integration is defeind include its modules if the component extends
|
|
14
|
+
# the defined base class
|
|
15
|
+
return unless defined?(Spark::Component::Integration)
|
|
16
|
+
|
|
17
|
+
base.include(Spark::Component::Integration) if base < Spark::Component::Integration.base_class
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
+
require "spark/component/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "spark-component"
|
|
7
|
+
spec.version = Spark::Component::VERSION
|
|
8
|
+
spec.authors = ["Brandon Mathis"]
|
|
9
|
+
spec.email = ["brandon@imathis.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = %q{Add a Spark of awesome to your ActionView Component.}
|
|
12
|
+
spec.homepage = "https://github.com/spark-engine/component"
|
|
13
|
+
spec.license = "MIT"
|
|
14
|
+
|
|
15
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
16
|
+
|
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
18
|
+
spec.metadata["source_code_uri"] = "https://github.com/spark-engine/component"
|
|
19
|
+
spec.metadata["changelog_uri"] = "https://github.com/spark-engine/component/blob/master/CHANGELOG.md"
|
|
20
|
+
|
|
21
|
+
# Specify which files should be added to the gem when it is released.
|
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
23
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
|
24
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
spec.bindir = "exe"
|
|
28
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
29
|
+
spec.require_paths = ["lib"]
|
|
30
|
+
|
|
31
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
|
32
|
+
spec.add_development_dependency "actionview-component"
|
|
33
|
+
spec.add_development_dependency "memory_profiler"
|
|
34
|
+
spec.add_development_dependency "minitest", "= 5.1.0"
|
|
35
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
|
36
|
+
spec.add_development_dependency "rubocop"
|
|
37
|
+
spec.add_development_dependency "slim"
|
|
38
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: spark-component
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0.pre.alpha
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Brandon Mathis
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2019-11-13 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: '2.0'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: actionview-component
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: memory_profiler
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: minitest
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - '='
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: 5.1.0
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - '='
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: 5.1.0
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rake
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '10.0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '10.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'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: slim
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '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'
|
|
111
|
+
description:
|
|
112
|
+
email:
|
|
113
|
+
- brandon@imathis.com
|
|
114
|
+
executables: []
|
|
115
|
+
extensions: []
|
|
116
|
+
extra_rdoc_files: []
|
|
117
|
+
files:
|
|
118
|
+
- ".gitignore"
|
|
119
|
+
- ".rubocop.yml"
|
|
120
|
+
- CHANGELOG.md
|
|
121
|
+
- CODE_OF_CONDUCT.md
|
|
122
|
+
- Gemfile
|
|
123
|
+
- Gemfile.lock
|
|
124
|
+
- LICENSE.txt
|
|
125
|
+
- README.md
|
|
126
|
+
- Rakefile
|
|
127
|
+
- bin/console
|
|
128
|
+
- bin/setup
|
|
129
|
+
- lib/spark/component.rb
|
|
130
|
+
- lib/spark/component/attr.rb
|
|
131
|
+
- lib/spark/component/attribute.rb
|
|
132
|
+
- lib/spark/component/classname.rb
|
|
133
|
+
- lib/spark/component/element.rb
|
|
134
|
+
- lib/spark/component/integration/action_view_component.rb
|
|
135
|
+
- lib/spark/component/tag_attr.rb
|
|
136
|
+
- lib/spark/component/version.rb
|
|
137
|
+
- spark-component.gemspec
|
|
138
|
+
homepage: https://github.com/spark-engine/component
|
|
139
|
+
licenses:
|
|
140
|
+
- MIT
|
|
141
|
+
metadata:
|
|
142
|
+
allowed_push_host: https://rubygems.org
|
|
143
|
+
homepage_uri: https://github.com/spark-engine/component
|
|
144
|
+
source_code_uri: https://github.com/spark-engine/component
|
|
145
|
+
changelog_uri: https://github.com/spark-engine/component/blob/master/CHANGELOG.md
|
|
146
|
+
post_install_message:
|
|
147
|
+
rdoc_options: []
|
|
148
|
+
require_paths:
|
|
149
|
+
- lib
|
|
150
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
151
|
+
requirements:
|
|
152
|
+
- - ">="
|
|
153
|
+
- !ruby/object:Gem::Version
|
|
154
|
+
version: '0'
|
|
155
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
156
|
+
requirements:
|
|
157
|
+
- - ">"
|
|
158
|
+
- !ruby/object:Gem::Version
|
|
159
|
+
version: 1.3.1
|
|
160
|
+
requirements: []
|
|
161
|
+
rubygems_version: 3.0.3
|
|
162
|
+
signing_key:
|
|
163
|
+
specification_version: 4
|
|
164
|
+
summary: Add a Spark of awesome to your ActionView Component.
|
|
165
|
+
test_files: []
|