lite-component 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 775fd8e9a6f315ab425672710cc83fa654b081909a670b4b849ec7b4753401b3
4
+ data.tar.gz: 12b66c28eed78a0551d55596a2d0361d08da68c83e0c85cde7cdd9b50e57e963
5
+ SHA512:
6
+ metadata.gz: cb132f66e10923ab98013677ccb5c0ec6a502c05895d45d7d91b8584da259e1f9211c87d965c7c960aa79884f7103d974b65a21386c28b68dfeb731bc46bf421
7
+ data.tar.gz: cf33d78401b9eb07adba8ea39161a5d6166b0fcfb264c06d92e3c1acb89004ff23ad22304737156d12df8beefc1c36f0f2f4383b03447cbf8d50cde255375d8e
data/.fasterer.yml ADDED
@@ -0,0 +1,19 @@
1
+ speedups:
2
+ block_vs_symbol_to_proc: true
3
+ each_with_index_vs_while: false
4
+ fetch_with_argument_vs_block: true
5
+ for_loop_vs_each: true
6
+ getter_vs_attr_reader: true
7
+ gsub_vs_tr: true
8
+ hash_merge_bang_vs_hash_brackets: true
9
+ keys_each_vs_each_key: true
10
+ map_flatten_vs_flat_map: true
11
+ module_eval: true
12
+ proc_call_vs_yield: true
13
+ rescue_vs_respond_to: true
14
+ reverse_each_vs_reverse_each: true
15
+ select_first_vs_detect: true
16
+ select_last_vs_reverse_detect: true
17
+ setter_vs_attr_writer: true
18
+ shuffle_first_vs_sample: true
19
+ sort_vs_sort_by: true
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --backtrace
2
+ --color
3
+ --format progress
4
+ --order random
data/.rubocop.yml ADDED
@@ -0,0 +1,30 @@
1
+ require:
2
+ - rubocop-performance
3
+ - rubocop-rspec
4
+ AllCops:
5
+ TargetRubyVersion: 2.6
6
+ DisplayCopNames: true
7
+ DisplayStyleGuide: true
8
+ Layout/EmptyLinesAroundBlockBody:
9
+ Exclude:
10
+ - 'spec/**/**/*'
11
+ Layout/EmptyLinesAroundClassBody:
12
+ EnforcedStyle: empty_lines_except_namespace
13
+ Layout/EmptyLinesAroundModuleBody:
14
+ EnforcedStyle: empty_lines_except_namespace
15
+ Metrics/BlockLength:
16
+ Enabled: false
17
+ Metrics/ClassLength:
18
+ Enabled: false
19
+ Metrics/LineLength:
20
+ Max: 100
21
+ Naming/FileName:
22
+ Enabled: false
23
+ RSpec/ExampleLength:
24
+ Enabled: false
25
+ RSpec/MultipleExpectations:
26
+ Enabled: false
27
+ Style/Documentation:
28
+ Enabled: false
29
+ Style/ExpandPathArguments:
30
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,24 @@
1
+ sudo: false
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.5
6
+ - 2.6
7
+ - ruby-head
8
+ matrix:
9
+ fast_finish: true
10
+ allow_failures:
11
+ - rvm: ruby-head
12
+ before_install:
13
+ - gem update --system
14
+ - gem install bundler
15
+ install:
16
+ - bundle install --jobs=3 --retry=3
17
+ script:
18
+ - bundle exec rspec
19
+ - bundle exec rubocop
20
+ - bundle exec fasterer
21
+ notifications:
22
+ email: false
23
+ slack:
24
+ secure: mtfWWZthR1xe+vfHViVYno4doSLn9ENWoDumbhnoUcanYLlhqfnGY4U9Jde6QvoxKlcrAudOwyuD9DvwmtgEduhJbAi3fydHpeDGdL5opDqhQ7uaT8Gx93I9cYh/A4kf+GscUcFUqiRjH1IdT64ysfTVfBNXR9mxBNxGJa/qcE5Vq/N6loEtDabKoStknxBJPsKTzSWrx5zSa3a3q+fdLT4XTfxYisRiTssMEqfAY9vcHqYAwZ1KJk/mQ4GrWUNJPOvHrMW6u9/wKT2nv2Tfg6mM5G7q7WCF6AiZ6ZTlgVMyXylH+YhyEJpOSwhIkILDU+/1RR2JUKMb0aafps3+GHF7gPpubtPwMISj6Nb9mmoin68KD7xS/fs5X0BZZ7Hj6PBgrZJZLYunoQqKmE4MGp6UK2BwAS/cV1iv2QTxkN9NkJW2SSZCsEzK1SaZFIK8Z0R3Kvk6ntZhpNY4tR/RKXPbHYrGugZSNWhwyRYSwXSNOdkIbVgV2ukKPLvFi9t3mQuGIRbpgtvBB8sBx8xg72sH6wIuH7bh/lTjwWP08NPH666P/9a3BFwJSynS2JFE0g1kIrJDt8J85adhmxAm9Akybqmdti9C1UmLwOOT9Jtz1f+yy4Qf0+5umFiQ6ggVDbAS9OwK9t2tJKSzMmlSWgfufqKHDPHa9ATEgAe1l+M=
data/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [1.0.0] - 2019-12-10
10
+ ### Added
11
+ - Initial project version
@@ -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 j.gomez@drexed.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
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in lite-component.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,192 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ lite-component (1.0.0)
5
+ rails (>= 5.1.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ actioncable (6.0.1)
11
+ actionpack (= 6.0.1)
12
+ nio4r (~> 2.0)
13
+ websocket-driver (>= 0.6.1)
14
+ actionmailbox (6.0.1)
15
+ actionpack (= 6.0.1)
16
+ activejob (= 6.0.1)
17
+ activerecord (= 6.0.1)
18
+ activestorage (= 6.0.1)
19
+ activesupport (= 6.0.1)
20
+ mail (>= 2.7.1)
21
+ actionmailer (6.0.1)
22
+ actionpack (= 6.0.1)
23
+ actionview (= 6.0.1)
24
+ activejob (= 6.0.1)
25
+ mail (~> 2.5, >= 2.5.4)
26
+ rails-dom-testing (~> 2.0)
27
+ actionpack (6.0.1)
28
+ actionview (= 6.0.1)
29
+ activesupport (= 6.0.1)
30
+ rack (~> 2.0)
31
+ rack-test (>= 0.6.3)
32
+ rails-dom-testing (~> 2.0)
33
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
34
+ actiontext (6.0.1)
35
+ actionpack (= 6.0.1)
36
+ activerecord (= 6.0.1)
37
+ activestorage (= 6.0.1)
38
+ activesupport (= 6.0.1)
39
+ nokogiri (>= 1.8.5)
40
+ actionview (6.0.1)
41
+ activesupport (= 6.0.1)
42
+ builder (~> 3.1)
43
+ erubi (~> 1.4)
44
+ rails-dom-testing (~> 2.0)
45
+ rails-html-sanitizer (~> 1.1, >= 1.2.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.4)
67
+ colorize (0.8.1)
68
+ concurrent-ruby (1.1.5)
69
+ crass (1.0.5)
70
+ diff-lcs (1.3)
71
+ erubi (1.9.0)
72
+ fasterer (0.8.1)
73
+ colorize (~> 0.7)
74
+ ruby_parser (>= 3.14.1)
75
+ generator_spec (0.9.4)
76
+ activesupport (>= 3.0.0)
77
+ railties (>= 3.0.0)
78
+ globalid (0.4.2)
79
+ activesupport (>= 4.2.0)
80
+ i18n (1.7.0)
81
+ concurrent-ruby (~> 1.0)
82
+ jaro_winkler (1.5.4)
83
+ loofah (2.4.0)
84
+ crass (~> 1.0.2)
85
+ nokogiri (>= 1.5.9)
86
+ mail (2.7.1)
87
+ mini_mime (>= 0.1.1)
88
+ marcel (0.3.3)
89
+ mimemagic (~> 0.3.2)
90
+ method_source (0.9.2)
91
+ mimemagic (0.3.3)
92
+ mini_mime (1.0.2)
93
+ mini_portile2 (2.4.0)
94
+ minitest (5.13.0)
95
+ nio4r (2.5.2)
96
+ nokogiri (1.10.7)
97
+ mini_portile2 (~> 2.4.0)
98
+ parallel (1.19.1)
99
+ parser (2.6.5.0)
100
+ ast (~> 2.4.0)
101
+ rack (2.0.7)
102
+ rack-test (1.1.0)
103
+ rack (>= 1.0, < 3)
104
+ rails (6.0.1)
105
+ actioncable (= 6.0.1)
106
+ actionmailbox (= 6.0.1)
107
+ actionmailer (= 6.0.1)
108
+ actionpack (= 6.0.1)
109
+ actiontext (= 6.0.1)
110
+ actionview (= 6.0.1)
111
+ activejob (= 6.0.1)
112
+ activemodel (= 6.0.1)
113
+ activerecord (= 6.0.1)
114
+ activestorage (= 6.0.1)
115
+ activesupport (= 6.0.1)
116
+ bundler (>= 1.3.0)
117
+ railties (= 6.0.1)
118
+ sprockets-rails (>= 2.0.0)
119
+ rails-dom-testing (2.0.3)
120
+ activesupport (>= 4.2.0)
121
+ nokogiri (>= 1.6)
122
+ rails-html-sanitizer (1.3.0)
123
+ loofah (~> 2.3)
124
+ railties (6.0.1)
125
+ actionpack (= 6.0.1)
126
+ activesupport (= 6.0.1)
127
+ method_source
128
+ rake (>= 0.8.7)
129
+ thor (>= 0.20.3, < 2.0)
130
+ rainbow (3.0.0)
131
+ rake (13.0.1)
132
+ rspec (3.9.0)
133
+ rspec-core (~> 3.9.0)
134
+ rspec-expectations (~> 3.9.0)
135
+ rspec-mocks (~> 3.9.0)
136
+ rspec-core (3.9.0)
137
+ rspec-support (~> 3.9.0)
138
+ rspec-expectations (3.9.0)
139
+ diff-lcs (>= 1.2.0, < 2.0)
140
+ rspec-support (~> 3.9.0)
141
+ rspec-mocks (3.9.0)
142
+ diff-lcs (>= 1.2.0, < 2.0)
143
+ rspec-support (~> 3.9.0)
144
+ rspec-support (3.9.0)
145
+ rubocop (0.77.0)
146
+ jaro_winkler (~> 1.5.1)
147
+ parallel (~> 1.10)
148
+ parser (>= 2.6)
149
+ rainbow (>= 2.2.2, < 4.0)
150
+ ruby-progressbar (~> 1.7)
151
+ unicode-display_width (>= 1.4.0, < 1.7)
152
+ rubocop-performance (1.5.1)
153
+ rubocop (>= 0.71.0)
154
+ rubocop-rspec (1.37.0)
155
+ rubocop (>= 0.68.1)
156
+ ruby-progressbar (1.10.1)
157
+ ruby_parser (3.14.1)
158
+ sexp_processor (~> 4.9)
159
+ sexp_processor (4.13.0)
160
+ sprockets (4.0.0)
161
+ concurrent-ruby (~> 1.0)
162
+ rack (> 1, < 3)
163
+ sprockets-rails (3.2.1)
164
+ actionpack (>= 4.0)
165
+ activesupport (>= 4.0)
166
+ sprockets (>= 3.0.0)
167
+ thor (0.20.3)
168
+ thread_safe (0.3.6)
169
+ tzinfo (1.2.5)
170
+ thread_safe (~> 0.1)
171
+ unicode-display_width (1.6.0)
172
+ websocket-driver (0.7.1)
173
+ websocket-extensions (>= 0.1.0)
174
+ websocket-extensions (0.1.4)
175
+ zeitwerk (2.2.2)
176
+
177
+ PLATFORMS
178
+ ruby
179
+
180
+ DEPENDENCIES
181
+ bundler
182
+ fasterer
183
+ generator_spec
184
+ lite-component!
185
+ rake
186
+ rspec
187
+ rubocop
188
+ rubocop-performance
189
+ rubocop-rspec
190
+
191
+ BUNDLED WITH
192
+ 2.0.2
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Juan Gomez
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,470 @@
1
+ # Lite::Component
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/lite-component.svg)](http://badge.fury.io/rb/lite-component)
4
+ [![Build Status](https://travis-ci.org/drexed/lite-component.svg?branch=master)](https://travis-ci.org/drexed/lite-component)
5
+
6
+ Lite::Component is a library for building component base objects. This technique simplifies
7
+ and organizes often used or logically comply page objects.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'lite-component'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install lite-component
24
+
25
+ ## Table of Contents
26
+
27
+ * [Setup](#setup)
28
+ * [Generator](#generator)
29
+ * [Assets](#assets)
30
+ * [Components](#components)
31
+ * [Usage](#usage)
32
+ * [Attribute and blocks](#attributes-and-blocks)
33
+ * [Attributes defaults](#attribute-defaults)
34
+ * [Attributes overrides](#attribute-overrides)
35
+ * [Elements](#elements)
36
+ * [Helper methods](#helper-methods)
37
+ * [Rendering components without a partial](#rendering-components-without-a-partial)
38
+ * [Namespaced components](#namespaced-components)
39
+
40
+ ## Setup
41
+
42
+ ### Generator
43
+
44
+ Use `rails g component NAME` will generate the following files:
45
+ ```
46
+ /app/assets/javascripts/components/[name].js
47
+ /app/assets/stylesheets/components/[name].scss
48
+ /app/components/[name]_query.rb
49
+ /app/views/components/_[name].html.erb
50
+ ```
51
+ The generator also takes `--skip-css`, `--skip-js` and `--skip-erb` options
52
+
53
+ ### Assets
54
+
55
+ In the basic Rails app setup component `*.scss` and `*.js` will be automatically load
56
+ via the tree lookup.
57
+
58
+ In order to require assets such manually require them in the manifest, e.g. `application.css`:
59
+
60
+ ```
61
+ /*
62
+ * All components:
63
+ *= require lite-component
64
+ *
65
+ * - or -
66
+ *
67
+ * Specific component:
68
+ *= require component/alert
69
+ */
70
+ ```
71
+
72
+ ### Components
73
+
74
+ If you create a `ApplicationComponent` file in the `app/components` directory, the generator
75
+ will create file that inherit from `ApplicationComponent` if not `Lite::Component::Base`.
76
+
77
+ ## Usage
78
+
79
+ ### Attributes and blocks
80
+
81
+ There are two ways of passing data to components: `attributes` and `blocks`. Attributes are useful for data such as ids, modifiers and data structures (models, etc). Blocks are useful when you need
82
+ to inject HTML content into components.
83
+
84
+ ```ruby
85
+ # app/components/alert_component.rb
86
+
87
+ class AlertComponent < Components::Component
88
+ attribute :context
89
+ attribute :message
90
+ end
91
+ ```
92
+
93
+ ```erb
94
+ <% # app/views/components/_alert.html.erb %>
95
+
96
+ <div class="alert alert--<%= alert.context %>" role="alert">
97
+ <%= alert.message %>
98
+ </div>
99
+ ```
100
+
101
+ ```erb
102
+ <%= component "alert", message: "Something went right!", context: "success" %>
103
+ <%= component AlertComponent, message: "Something went wrong!", context: "danger" %>
104
+ ```
105
+
106
+ To inject some text or HTML content into our component we can print the component variable in our template, and populate it by passing a block to the component helper:
107
+
108
+ ```erb
109
+ <% # app/views/components/_alert.html.erb %>
110
+
111
+ <div class="alert alert--<%= alert.context %>" role="alert">
112
+ <%= alert %>
113
+ </div>
114
+ ```
115
+
116
+ ```erb
117
+ <%= component "alert", context: "success" do %>
118
+ <em>Something</em> went right!
119
+ <% end %>
120
+ ```
121
+
122
+ Another good use case for attributes is when you have a component backed by a model:
123
+
124
+ ```ruby
125
+ # app/components/comment_component.rb
126
+
127
+ class CommentComponent < Components::Component
128
+ attribute :comment
129
+
130
+ delegate :id, :author, :body, to: :comment
131
+ end
132
+ ```
133
+
134
+ ```erb
135
+ <% # app/views/components/_comment.html.erb %>
136
+
137
+ <div id="comment-<%= comment.id %>" class="comment">
138
+ <div class="comment__author">
139
+ <%= link_to comment.author.name, author_path(comment.author) %>
140
+ </div>
141
+ <div class="comment__body">
142
+ <%= comment.body %>
143
+ </div>
144
+ </div>
145
+ ```
146
+
147
+ ```erb
148
+ <% comments.each do |comment| %>
149
+ <%= component "comment", comment: comment %>
150
+ <% end %>
151
+ ```
152
+
153
+ ### Attribute defaults
154
+
155
+ Attributes can have default values:
156
+
157
+ ```ruby
158
+ # app/components/alert_component.rb
159
+
160
+ class AlertComponent < Components::Component
161
+ attribute :message
162
+ attribute :context, default: "primary"
163
+ end
164
+ ```
165
+
166
+ ### Attribute overrides
167
+
168
+ It's easy to override an attribute with additional logic:
169
+
170
+ ```ruby
171
+ # app/components/alert_component.rb
172
+
173
+ class AlertComponent < Components::Component
174
+ attribute :message
175
+ attribute :context, default: "primary"
176
+
177
+ def message
178
+ @message.upcase if context == "danger"
179
+ end
180
+ end
181
+ ```
182
+
183
+ ### Attribute validation
184
+
185
+ To ensure your components get initialized properly you can use `ActiveModel::Validations` in your elements or components:
186
+
187
+ ```ruby
188
+ # app/components/alert_component.rb
189
+
190
+ class AlertComponent < Components::Component
191
+ attribute :label
192
+
193
+ validates :label, presence: true
194
+ end
195
+ ```
196
+
197
+ Your validations will be executed during the components initialization and raise an `Lite::Component::ValidationError` if any validation fails.
198
+
199
+ ### Elements
200
+
201
+ Attributes and blocks are great for simple components or components backed by a data structure, such as a model. Other components are more generic in nature and can be used in a variety of contexts. Often they consist of multiple parts or elements, that sometimes repeat, and sometimes need their own modifiers.
202
+
203
+ Take a card component. In React, a common approach is to create subcomponents:
204
+
205
+ ```jsx
206
+ <Card flush={true}>
207
+ <CardHeader centered={true}>
208
+ Header
209
+ </CardHeader>
210
+ <CardSection size="large">
211
+ Section 1
212
+ </CardSection>
213
+ <CardSection size="small">
214
+ Section 2
215
+ </CardSection>
216
+ <CardFooter>
217
+ Footer
218
+ </CardFooter>
219
+ </Card>
220
+ ```
221
+
222
+ There are two problems with this approach:
223
+
224
+ 1. The card header, section and footer have no standalone meaning, yet we treat them as standalone components. This means a `CardHeader` could be placed outside of a `Card`.
225
+ 2. We lose control of the structure of the elements. A `CardHeader` can be placed below, or inside a `CardFooter`.
226
+
227
+ Using this gem, the same component can be written like this:
228
+
229
+ ```ruby
230
+ # app/components/card_component.rb
231
+
232
+ class CardComponent < Components::Component
233
+ attribute :flush, default: false
234
+
235
+ element :header do
236
+ attribute :centered, default: false
237
+ end
238
+
239
+ element :section, multiple: true do
240
+ attribute :size
241
+ end
242
+
243
+ element :footer
244
+ end
245
+ ```
246
+
247
+ ```erb
248
+ <% # app/views/components/_card.html.erb %>
249
+
250
+ <div class="card <%= "card--flush" if card.flush %>">
251
+ <div class="card__header <%= "card__header--centered" if card.header.centered %>">
252
+ <%= card.header %>
253
+ </div>
254
+ <% card.sections.each do |section| %>
255
+ <div class="card__section <%= "card__section--#{section.size}" %>">
256
+ <%= section %>
257
+ </div>
258
+ <% end %>
259
+ <div class="card__footer">
260
+ <%= card.footer %>
261
+ </div>
262
+ </div>
263
+ ```
264
+
265
+ Elements can be thought of as isolated subcomponents, and they are defined on the component. Passing `multiple: true` makes it a repeating element, and passing a block lets us declare attributes on our elements, in the same way we declare attributes on components.
266
+
267
+ In order to populate them with data, we pass a block to the component helper, which yields the component, which lets us set attributes and blocks on the element in the same way we do for components:
268
+
269
+ ```erb
270
+ <%= component "card", flush: true do |c| %>
271
+ <% c.header centered: true do %>
272
+ Header
273
+ <% end %>
274
+ <% c.section size: "large" do %>
275
+ Section 1
276
+ <% end %>
277
+ <% c.section size: "large" do %>
278
+ Section 2
279
+ <% end %>
280
+ <% c.footer do %>
281
+ Footer
282
+ <% end %>
283
+ <% end %>
284
+ ```
285
+
286
+ Multiple calls to a repeating element, such as `section` in the example above, will append each section to an array.
287
+
288
+ Another good use case is a navigation component:
289
+
290
+ ```ruby
291
+ # app/components/navigation_component.rb
292
+
293
+ class NavigationComponent < Components::Component
294
+ element :items, multiple: true do
295
+ attribute :label
296
+ attribute :url
297
+ attribute :active, default: false
298
+ end
299
+ end
300
+ ```
301
+
302
+ ```erb
303
+ <%= component "navigation" do |c| %>
304
+ <% c.item label: "Home", url: root_path, active: true %>
305
+ <% c.item label: "Explore" url: explore_path %>
306
+ <% end %>
307
+ ```
308
+
309
+ An alternative here is to pass a data structure to the component as an attribute, if no HTML needs to be injected when rendering the component:
310
+
311
+ ```erb
312
+ <%= component "navigation", items: items %>
313
+ ```
314
+
315
+ Elements can have validations, too:
316
+
317
+ ```ruby
318
+ # app/components/navigation_component.rb
319
+
320
+ class NavigationComponent < Components::Component
321
+ element :items, multiple: true do
322
+ attribute :label
323
+ attribute :url
324
+ attribute :active, default: false
325
+
326
+ validates :label, presence: true
327
+ validates :url, presence: true
328
+ end
329
+ end
330
+ ```
331
+
332
+ Elements can also be nested, although it is recommended to keep nesting to a minimum:
333
+
334
+ ```ruby
335
+ # app/components/card_component.rb
336
+
337
+ class CardComponent < Components::Component
338
+ ...
339
+
340
+ element :section, multiple: true do
341
+ attribute :size
342
+
343
+ element :header
344
+ element :footer
345
+ end
346
+ end
347
+ ```
348
+
349
+ ### Helper methods
350
+
351
+ In addition to declaring attributes and elements, it is also possible to declare helper methods. This is useful if you prefer to keep logic out of your templates. Let's extract the modifier logic from the card component template:
352
+
353
+ ```ruby
354
+ # app/components/card_component.rb
355
+
356
+ class CardComponent < Components::Component
357
+ ...
358
+
359
+ def css_classes
360
+ css_classes = ["card"]
361
+ css_classes << "card--flush" if flush
362
+ css_classes.join(" ")
363
+ end
364
+ end
365
+ ```
366
+
367
+ ```erb
368
+ <% # app/views/components/_card.html.erb %>
369
+
370
+ <%= content_tag :div, class: card.css_classes do %>
371
+ ...
372
+ <% end %>
373
+ ```
374
+
375
+ It's even possible to declare helpers on elements:
376
+
377
+ ```ruby
378
+ # app/components/card_component.rb %>
379
+
380
+ class CardComponent < Components::Component
381
+ ...
382
+
383
+ element :section, multiple: true do
384
+ attribute :size
385
+
386
+ def css_classes
387
+ css_classes = ["card__section"]
388
+ css_classes << "card__section--#{size}" if size
389
+ css_classes.join(" ")
390
+ end
391
+ end
392
+ end
393
+ ```
394
+
395
+ ```erb
396
+ <% # app/views/components/_card.html.erb %>
397
+
398
+ <%= content_tag :div, class: card.css_classes do %>
399
+ ...
400
+ <%= content_tag :div, class: section.css_classes do %>
401
+ <%= section %>
402
+ <% end %>
403
+ ...
404
+ <% end %>
405
+ ```
406
+
407
+ Helper methods can also make use of the `@view` instance variable in order to call Rails helpers such as `link_to` or `content_tag`.
408
+
409
+ ### Rendering components without a partial
410
+
411
+ For some small components, such as buttons, it might make sense to skip the partial altogether, in order to speed up rendering. This can be done by overriding `render` on the component:
412
+
413
+ ```ruby
414
+ # app/components/button_component.rb
415
+
416
+ class ButtonComponent < Components::Component
417
+ attribute :label
418
+ attribute :url
419
+ attribute :context
420
+
421
+ def render
422
+ @view.link_to label, url, class: css_classes
423
+ end
424
+
425
+ def css_classes
426
+ css_classes = "button"
427
+ css_classes << "button--#{context}" if context
428
+ css_classes.join(" ")
429
+ end
430
+ end
431
+ ```
432
+
433
+ ```erb
434
+ <%= component "button", label: "Sign up", url: sign_up_path, context: "primary" %>
435
+ <%= component ButtonComponent, label: "Sign in", url: sign_in_path %>
436
+ ```
437
+
438
+ ### Namespaced components
439
+
440
+ Components can be nested under a namespace. This is useful if you want to practice things like [Atomic Design](http://bradfrost.com/blog/post/atomic-web-design/), [BEMIT](https://csswizardry.com/2015/08/bemit-taking-the-bem-naming-convention-a-step-further/) or any other component classification scheme. In order to create a namespaced component, stick it in a folder and wrap the class in a module:
441
+
442
+ ```ruby
443
+ module Objects
444
+ class MediaObject < Components::Component; end
445
+ end
446
+ ```
447
+
448
+ Then call it from a template like so:
449
+
450
+ ```erb
451
+ <%= component "objects/media_object" %>
452
+ ```
453
+
454
+ ## Development
455
+
456
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
457
+
458
+ 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).
459
+
460
+ ## Contributing
461
+
462
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/lite-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.
463
+
464
+ ## License
465
+
466
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
467
+
468
+ ## Code of Conduct
469
+
470
+ Everyone interacting in the Lite::Component project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/lite-component/blob/master/CODE_OF_CONDUCT.md).