component_party 0.8.0
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/.codeclimate +23 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.rubocop.yml +24 -0
- data/.rubocop_todo.yml +0 -0
- data/.travis.yml +36 -0
- data/.yardopts +3 -0
- data/Gemfile +18 -0
- data/README.md +455 -0
- data/component_party.gemspec +32 -0
- data/lib/component_party/action_view/component_renderer/tag_wrapper_decorator.rb +26 -0
- data/lib/component_party/action_view/component_renderer.rb +54 -0
- data/lib/component_party/action_view/renderer.rb +36 -0
- data/lib/component_party/importer_helper.rb +34 -0
- data/lib/component_party/railtie.rb +16 -0
- data/lib/component_party/view_model.rb +17 -0
- data/lib/component_party.rb +34 -0
- data/partyhard.gif +0 -0
- data/rainbow_meme_v1.jpg +0 -0
- metadata +209 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 73fbc5bb12f3bbbd726ca0314afeac72fddf416b0adf9c0a324c74722675349d
|
|
4
|
+
data.tar.gz: 251c72e7dac7ad1328ba42742fa3439cfeb69b009efd1937ef981bcdf7a90aa1
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 46b22b5d9a78f1927117e331ddcd90d07806e77f8118fe46683c281767485805ac30731aa91d8f26a0aca6f37f81255ff3b2af433bc533582c1c6c5afc92abd3
|
|
7
|
+
data.tar.gz: eac0e6c9ca872d6ba997db86c52a869186bb9e4b528746b65bf35b2797a7e1932cad5a0228c6410c25f9f172bee58ce11cd5710abb5371fa7546ede646385a35
|
data/.codeclimate
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
engines:
|
|
2
|
+
rubocop:
|
|
3
|
+
enabled: true
|
|
4
|
+
#checks:
|
|
5
|
+
# Rubocop/Metrics/ClassLength:
|
|
6
|
+
# enabled: false
|
|
7
|
+
brakeman:
|
|
8
|
+
enabled: false
|
|
9
|
+
eslint:
|
|
10
|
+
enabled: false
|
|
11
|
+
csslint:
|
|
12
|
+
enabled: false
|
|
13
|
+
duplication:
|
|
14
|
+
enabled: true
|
|
15
|
+
config:
|
|
16
|
+
languages:
|
|
17
|
+
- ruby:
|
|
18
|
+
- javascript:
|
|
19
|
+
ratings:
|
|
20
|
+
paths:
|
|
21
|
+
- lib/**
|
|
22
|
+
exclude_paths:
|
|
23
|
+
- spec/**/*
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
inherit_from: .rubocop_todo.yml
|
|
2
|
+
|
|
3
|
+
Documentation:
|
|
4
|
+
Enabled: false
|
|
5
|
+
|
|
6
|
+
Style/ClassAndModuleChildren:
|
|
7
|
+
Enabled: false
|
|
8
|
+
|
|
9
|
+
Style/FrozenStringLiteralComment:
|
|
10
|
+
Enabled: false
|
|
11
|
+
|
|
12
|
+
Metrics/LineLength:
|
|
13
|
+
Max: 150
|
|
14
|
+
IgnoredPatterns: ['\A#']
|
|
15
|
+
|
|
16
|
+
Metrics/MethodLength:
|
|
17
|
+
Max: 20
|
|
18
|
+
|
|
19
|
+
AllCops:
|
|
20
|
+
Exclude:
|
|
21
|
+
- component_party.gemspec
|
|
22
|
+
- 'exe/**/*'
|
|
23
|
+
- 'bin/**/*'
|
|
24
|
+
- 'spec/**/*'
|
data/.rubocop_todo.yml
ADDED
|
File without changes
|
data/.travis.yml
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
env:
|
|
2
|
+
global:
|
|
3
|
+
- CC_TEST_REPORTER_ID=2373c9fa7226b98ccf8bef72f133bc37b4cef57085c027e144a3b2fc71ce7558
|
|
4
|
+
matrix:
|
|
5
|
+
- "RAILS_VERSION=4.2.0"
|
|
6
|
+
- "RAILS_VERSION=5.0.0"
|
|
7
|
+
- "RAILS_VERSION=5.1.0"
|
|
8
|
+
- "RAILS_VERSION=5.2.0"
|
|
9
|
+
|
|
10
|
+
language: ruby
|
|
11
|
+
|
|
12
|
+
cache:
|
|
13
|
+
bundler: true
|
|
14
|
+
|
|
15
|
+
rvm:
|
|
16
|
+
- "2.4.1"
|
|
17
|
+
|
|
18
|
+
bundler_args: "--binstubs --standalone --without documentation --path ../bundle"
|
|
19
|
+
|
|
20
|
+
before_install:
|
|
21
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
|
22
|
+
- chmod +x ./cc-test-reporter
|
|
23
|
+
|
|
24
|
+
before_script:
|
|
25
|
+
- ./cc-test-reporter before-build
|
|
26
|
+
|
|
27
|
+
script:
|
|
28
|
+
- bundle exec rubocop -c .rubocop.yml
|
|
29
|
+
- bundle exec rspec
|
|
30
|
+
|
|
31
|
+
notifications:
|
|
32
|
+
email: false
|
|
33
|
+
|
|
34
|
+
after_script:
|
|
35
|
+
- if [[ "$TRAVIS_TEST_RESULT" == 0 ]]; then ./cc-test-reporter format-coverage -t simplecov -o ./coverage/codeclimate.$CI_NODE_INDEX.json ./coverage/spec/.resultset.json; fi
|
|
36
|
+
- if [[ "$TRAVIS_TEST_RESULT" == 0 ]]; then ./cc-test-reporter sum-coverage --output - --parts $CI_NODE_TOTAL coverage/codeclimate.*.json | ./cc-test-reporter upload-coverage --input -; fi
|
data/.yardopts
ADDED
data/Gemfile
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
source 'https://rubygems.org'
|
|
2
|
+
|
|
3
|
+
gem 'yard', '~> 0.9.9', require: false
|
|
4
|
+
|
|
5
|
+
rails_version = ENV['RAILS_VERSION'] || 'default'
|
|
6
|
+
|
|
7
|
+
rails = case rails_version
|
|
8
|
+
when 'master'
|
|
9
|
+
{ github: 'rails/rails' }
|
|
10
|
+
when 'default'
|
|
11
|
+
'~> 4.2.0'
|
|
12
|
+
else
|
|
13
|
+
"~> #{rails_version}"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
gem 'rails', rails
|
|
17
|
+
# Specify your gem's dependencies in rabbitmq-spec.gemspec
|
|
18
|
+
gemspec
|
data/README.md
ADDED
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
# Welcome to component_party gem
|
|
2
|
+
|
|
3
|
+
[](https://travis-ci.com/viniciusoyama/component_party)
|
|
4
|
+
[](https://codeclimate.com/github/viniciusoyama/component_party)
|
|
5
|
+
[](https://codeclimate.com/github/viniciusoyama/component_party)
|
|
6
|
+
|
|
7
|
+
Frontend components for Ruby on Rails: group your view logic, html and css files in components to be rendered from views or directly from controllers.
|
|
8
|
+
|
|
9
|
+
# Table of contents
|
|
10
|
+
|
|
11
|
+
<!-- TOC depthFrom:1 depthTo:1 withLinks:1 updateOnSave:0 orderedList:0 -->
|
|
12
|
+
|
|
13
|
+
- [Quick overview](#quick-overview)
|
|
14
|
+
- [Importing components](#importing-components)
|
|
15
|
+
- [View Models: pass data to your components](#view-models-pass-data-to-your-components)
|
|
16
|
+
- [Using helpers inside your components](#using-helpers-inside-your-components)
|
|
17
|
+
- [Accessing params and other controller's methods](#accessing-params-and-other-controllers-methods)
|
|
18
|
+
- [Rendering a component from a controller's action](#rendering-a-component-from-a-controllers-action)
|
|
19
|
+
- [Style namespacing: css scoped by component](#style-namespacing-css-scoped-by-component)
|
|
20
|
+
- [Configuration](#configuration)
|
|
21
|
+
|
|
22
|
+
<!-- /TOC -->
|
|
23
|
+
|
|
24
|
+
# Quick overview
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
Add to your gemfile: `gem 'component_party'`
|
|
28
|
+
|
|
29
|
+
## Using the gem
|
|
30
|
+
|
|
31
|
+
1) Move things to app/components folder and organize your frontend
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
app
|
|
36
|
+
├── components
|
|
37
|
+
│ └── sidebar
|
|
38
|
+
│ └── template.erb
|
|
39
|
+
│ └── style.sass
|
|
40
|
+
│ └── header
|
|
41
|
+
│ └── template.erb
|
|
42
|
+
│ └── style.sass
|
|
43
|
+
│ └── pages
|
|
44
|
+
│ └── users
|
|
45
|
+
│ └── index
|
|
46
|
+
│ └── template.erb
|
|
47
|
+
│ └── style.sass
|
|
48
|
+
│ └── filter
|
|
49
|
+
│ └── template.erb
|
|
50
|
+
│ └── view_model.rb
|
|
51
|
+
│ └── style.sass
|
|
52
|
+
│ └── list
|
|
53
|
+
│ └── template.erb
|
|
54
|
+
│ └── style.sass
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
2) Code your templates
|
|
59
|
+
|
|
60
|
+
**app/components/pages/users/index/template.erb**
|
|
61
|
+
|
|
62
|
+
```html
|
|
63
|
+
<%
|
|
64
|
+
import_component 'Filter', path: './filter'
|
|
65
|
+
import_component 'List', path: './list'
|
|
66
|
+
%>
|
|
67
|
+
|
|
68
|
+
<%= Filter() %>
|
|
69
|
+
|
|
70
|
+
<%= List(users: users) %>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**app/components/pages/users/list/template.erb**
|
|
74
|
+
|
|
75
|
+
```html
|
|
76
|
+
<table>
|
|
77
|
+
<tbody>
|
|
78
|
+
<% vm.users.each do |user| %>
|
|
79
|
+
<tr>
|
|
80
|
+
<td><%= user.name %></td>
|
|
81
|
+
</tr>
|
|
82
|
+
<% end %>
|
|
83
|
+
</tbody>
|
|
84
|
+
</table>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
3) Render a component from your controller
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
|
|
91
|
+
class UsersController < ApplicationController
|
|
92
|
+
|
|
93
|
+
def index
|
|
94
|
+
render component: true, view_model_data: { users: User.all }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
4) Customize and namespace your css for a given component
|
|
101
|
+
|
|
102
|
+
**app/components/pages/users/list/style.sass**
|
|
103
|
+
|
|
104
|
+
```sass
|
|
105
|
+
[data-component-path=pages-users-list] table tr
|
|
106
|
+
background: blue
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
5) Be astonished by what you've accomplished
|
|
110
|
+
|
|
111
|
+

|
|
112
|
+

|
|
113
|
+
|
|
114
|
+
# Importing components
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
app
|
|
118
|
+
├── components
|
|
119
|
+
│ └── application_layout
|
|
120
|
+
│ └── header
|
|
121
|
+
│ └── template.html.erb
|
|
122
|
+
│ └── user
|
|
123
|
+
│ └── panel
|
|
124
|
+
│ └── sidebar
|
|
125
|
+
│ └── template.html.erb
|
|
126
|
+
│ └── template.html.erb
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
You can import a component inside a layout, view or a component's template.
|
|
130
|
+
|
|
131
|
+
## Absolute importing
|
|
132
|
+
|
|
133
|
+
Just use the full component path.
|
|
134
|
+
|
|
135
|
+
**app/views/layouts/application.html.erb**
|
|
136
|
+
|
|
137
|
+
```html
|
|
138
|
+
|
|
139
|
+
<%
|
|
140
|
+
import_component 'Header', path: 'application_layout/header'
|
|
141
|
+
%>
|
|
142
|
+
|
|
143
|
+
<%= Header(my_user: current_user) %>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Relative importing
|
|
147
|
+
|
|
148
|
+
Use "./" before component's path.
|
|
149
|
+
|
|
150
|
+
The next example will look for a `sidebar` component inside the app/components/user/panel folder.
|
|
151
|
+
|
|
152
|
+
**app/components/user/panel/sidebar.html.erb**
|
|
153
|
+
|
|
154
|
+
```html
|
|
155
|
+
|
|
156
|
+
<%
|
|
157
|
+
import_component 'Sidebar', path: './sidebar'
|
|
158
|
+
%>
|
|
159
|
+
|
|
160
|
+
<%= Sidebar() %>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
# View Models: pass data to your components
|
|
164
|
+
|
|
165
|
+
While rendering a component, you can pass data in a hash format. The data will automatically be exposed to your template though a `vm` variable.
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
app
|
|
169
|
+
├── components
|
|
170
|
+
│ └── header
|
|
171
|
+
│ └── template.html.erb
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Rendering the component and passing data:
|
|
175
|
+
|
|
176
|
+
```html
|
|
177
|
+
|
|
178
|
+
<%
|
|
179
|
+
import_component 'Header', path: 'header'
|
|
180
|
+
%>
|
|
181
|
+
|
|
182
|
+
<%= Header(my_user: current_user) %>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
You have access to the my_user attribute in your component's template:
|
|
186
|
+
|
|
187
|
+
The component's template code:
|
|
188
|
+
|
|
189
|
+
```html
|
|
190
|
+
<p>Hello <%= vm.my_user.name %></p>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## How it works?
|
|
194
|
+
|
|
195
|
+
The `vm` variable is an instance of a ViewModel.
|
|
196
|
+
|
|
197
|
+
By default, we instantiate our `ComponentParty::ViewModel`. This class takes all the constructor arguments (it must be a hash/named args) and creates a getter for each one of them. Example:
|
|
198
|
+
|
|
199
|
+
```ruby
|
|
200
|
+
vm = ComponentParty::ViewModel.new(name: 'John', age: 12)
|
|
201
|
+
vm.name # John
|
|
202
|
+
vm.age # 12
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
When a view model is instantiated we pass the all arguments that you provided while calling your component in the template.
|
|
206
|
+
|
|
207
|
+
## Create your own ViewModel and handle complex view logic
|
|
208
|
+
|
|
209
|
+
Suppose that we want to use a custom view model (inside our header component's folder).
|
|
210
|
+
|
|
211
|
+
**app/components/header/view_model.rb**
|
|
212
|
+
|
|
213
|
+
```ruby
|
|
214
|
+
class Header::ViewModel < ComponentParty::ViewModel
|
|
215
|
+
def random_greeting
|
|
216
|
+
hi_text = ['Hi', 'Yo'].sample
|
|
217
|
+
"#{hi_text}, #{user.name}"
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
While importing our Header component we can pass a `custom_view_model` option to the import directive.
|
|
223
|
+
|
|
224
|
+
```html
|
|
225
|
+
<%
|
|
226
|
+
import_component 'Header', path: 'header', custom_view_model: true
|
|
227
|
+
%>
|
|
228
|
+
|
|
229
|
+
<%= Header(name: 'John') %>
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
By default we will use our own view model. But if you pass the `custom_view_model` options as `true` the rendering process will look for a class following all the Rails naming conventions.
|
|
233
|
+
|
|
234
|
+
Also, you can use any class as a view model. Instead of `true` just use the class itself:
|
|
235
|
+
|
|
236
|
+
```html
|
|
237
|
+
<%
|
|
238
|
+
import_component 'Header', path: 'header', custom_view_model: MyCustomClass
|
|
239
|
+
%>
|
|
240
|
+
|
|
241
|
+
<%= Header(name: 'John') %>
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
*PS:* You need to pass the class and not an instance of it.
|
|
245
|
+
|
|
246
|
+
Note that a view model *must* inherit from ComponentParty::ViewModel in order to be compliant to the internal API that a view model must have. Also, it is not expected that you override the `initialize` method in your custom view model.
|
|
247
|
+
|
|
248
|
+
**Is it possible to (by default) automatically search for a custom view model and, if not found, just use the default one instead?**
|
|
249
|
+
|
|
250
|
+
Yes, it would be possible to avoid the need of you passing a `custom_view_model` option but we don't like this approach for 2 main reasons:
|
|
251
|
+
|
|
252
|
+
1) This feature would make the rendering process much slower.
|
|
253
|
+
|
|
254
|
+
2) We think it's better to do things more explicitly for who is reading your code.
|
|
255
|
+
|
|
256
|
+
# Using helpers inside your components
|
|
257
|
+
|
|
258
|
+
The component's template is compiled using a normal rails view context so you have access to all helpers/params/routes/etc:
|
|
259
|
+
|
|
260
|
+
```
|
|
261
|
+
<div class="child">
|
|
262
|
+
<div class="date">
|
|
263
|
+
<%= l(Date.new(2019, 01, 03)) %>
|
|
264
|
+
</div>
|
|
265
|
+
|
|
266
|
+
<div class="routes">
|
|
267
|
+
<%= users_path %>
|
|
268
|
+
</div>
|
|
269
|
+
|
|
270
|
+
<div class="translation">
|
|
271
|
+
<%= t('hello')%>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
If you want to to access it inside a view model, just use the `view` method.
|
|
277
|
+
|
|
278
|
+
```ruby
|
|
279
|
+
class Header::ViewModel < ComponentParty::ViewModel
|
|
280
|
+
def formated_date
|
|
281
|
+
view.l(Date.today)
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
**app/components/header/template.html.erb**
|
|
287
|
+
```html
|
|
288
|
+
<header>
|
|
289
|
+
Today is <%= vm.formated_date %>
|
|
290
|
+
</header>
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
# Accessing params and other controller's methods
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
```ruby
|
|
298
|
+
class Header::ViewModel < ComponentParty::ViewModel
|
|
299
|
+
|
|
300
|
+
def formated_page
|
|
301
|
+
"Current page: #{view.params[:page]}"
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def formated_search
|
|
305
|
+
"Searching for: #{view.params[:search]}"
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def hello
|
|
309
|
+
"Hi #{view.current_user.name}"
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
end
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
**template.erb**
|
|
316
|
+
|
|
317
|
+
```html
|
|
318
|
+
<%= vm.hello %>
|
|
319
|
+
|
|
320
|
+
<%= vm.formated_search %>
|
|
321
|
+
|
|
322
|
+
<%= vm.formated_page %>
|
|
323
|
+
|
|
324
|
+
<%= params[:search] %>
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
# Rendering a component from a controller's action
|
|
328
|
+
|
|
329
|
+
When you are in an action you can render a component instead of a rails view using the following syntax:
|
|
330
|
+
|
|
331
|
+
```ruby
|
|
332
|
+
|
|
333
|
+
class ClientsController < ApplicationController
|
|
334
|
+
|
|
335
|
+
def index
|
|
336
|
+
render(component: 'my/component/path', view_model_data: { new_arg: 2, more_arg: 'text'})
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
If you want to render the default component for an given action just do:
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
```ruby
|
|
347
|
+
class ClientsController < ApplicationController
|
|
348
|
+
|
|
349
|
+
def index
|
|
350
|
+
render component: true, view_model_data: { clients: Client.all }
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
end
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
This will search for a component with a path of `app/components/pages/clients/index`.
|
|
357
|
+
|
|
358
|
+
Note that we will add **pages** before the default controller+action path.
|
|
359
|
+
|
|
360
|
+
**Can I make the gem render a component instead of a view by default?**
|
|
361
|
+
|
|
362
|
+
We though about doing this but - even if the default behavior was to render a component instead of a view - you would have to pass the view model data in the action using some kind of trick like:
|
|
363
|
+
|
|
364
|
+
```
|
|
365
|
+
def index
|
|
366
|
+
set_view_model_attribute(:users, User.all)
|
|
367
|
+
end
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Using the controller's instance variables just like views doesn't make sense in a ViewModel implementation.
|
|
371
|
+
|
|
372
|
+
Also, as we want to make things more explicitly (in case of another dev that doesn't know about this gem enters the project), it's better to always have to write the command.
|
|
373
|
+
|
|
374
|
+
```
|
|
375
|
+
render component: true
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
# Style namespacing: css scoped by component
|
|
379
|
+
|
|
380
|
+
Your template **must** have only one root node in order to your component be namespaced.
|
|
381
|
+
|
|
382
|
+
Each rendered component will have its first HTML node changed with a dynamic data attribute storing the component path. This means that you can create custom css for each component. Example:
|
|
383
|
+
|
|
384
|
+
```
|
|
385
|
+
app
|
|
386
|
+
├── components
|
|
387
|
+
│ └── admin_layout
|
|
388
|
+
│ └── header
|
|
389
|
+
│ └── template.html.erb
|
|
390
|
+
│ └── style.css
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
```html
|
|
394
|
+
<section>
|
|
395
|
+
<h1>Hello <%= vm.user_name%></h1>
|
|
396
|
+
</section>
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
When we render the header it will generate a HTML like this
|
|
400
|
+
|
|
401
|
+
```html
|
|
402
|
+
<section data-component-path='admin_layout-header'>
|
|
403
|
+
<h1>Hello <%= vm.user_name%></h1>
|
|
404
|
+
</section>
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
Then you can customize the component with the following css:
|
|
408
|
+
|
|
409
|
+
```css
|
|
410
|
+
[data-component-path=admin_layout-header] {
|
|
411
|
+
background: red;
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
## How to import the css files?
|
|
416
|
+
|
|
417
|
+
You can split your css in each component folder. It doesn't matter the name or the number of css/sass/less files that you have in each folder... Just don't forget to namespace it!
|
|
418
|
+
|
|
419
|
+
Also, in your application.css file you should require all the css from the app/components folder with a relative `require_tree`. Like this:
|
|
420
|
+
|
|
421
|
+
**application.sass**
|
|
422
|
+
|
|
423
|
+
```sass
|
|
424
|
+
//*=require_tree ../../../components
|
|
425
|
+
@import "fullcalendar.min"
|
|
426
|
+
@import "bootstrap"
|
|
427
|
+
@import "datepicker"
|
|
428
|
+
|
|
429
|
+
// ...
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
# Configuration
|
|
433
|
+
|
|
434
|
+
You can customize our gem by creating an initializer on your rails app.
|
|
435
|
+
|
|
436
|
+
**config/initializers/component_party.rb**
|
|
437
|
+
|
|
438
|
+
```ruby
|
|
439
|
+
|
|
440
|
+
ComponentParty.configure do |config|
|
|
441
|
+
# Folder path to look for components
|
|
442
|
+
config.components_path = 'app/components'
|
|
443
|
+
|
|
444
|
+
# Name for the html/erb/slim/etc template file inside the component folder
|
|
445
|
+
config.template_file_name = 'template'
|
|
446
|
+
|
|
447
|
+
# Name for the view model file inside the component folder
|
|
448
|
+
config.view_model_file_name = 'view_model'
|
|
449
|
+
|
|
450
|
+
# Folder path inside the components folder to look for pages when
|
|
451
|
+
# rendering the default component for a controller#action
|
|
452
|
+
config.component_folder_for_actions = 'pages'
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Gem::Specification.new do |s|
|
|
2
|
+
s.name = 'component_party'
|
|
3
|
+
s.version = '0.8.0'
|
|
4
|
+
s.date = '2019-03-28'
|
|
5
|
+
s.summary = 'Stop using views: frontend components architecture for Ruby on Rails.'
|
|
6
|
+
s.description = 'Frontend components for Ruby on Rails: group your view logic, html and css files in components to be rendered from views or directly from controllers.'
|
|
7
|
+
s.authors = ['Vinícius Oyama']
|
|
8
|
+
s.email = 'contact@viniciusoyama.com'
|
|
9
|
+
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
10
|
+
s.homepage =
|
|
11
|
+
'https://github.com/viniciusoyama/component_party'
|
|
12
|
+
s.license = 'MIT'
|
|
13
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
|
14
|
+
|
|
15
|
+
s.add_dependency 'rails', '>= 4.2.0', '< 6'
|
|
16
|
+
|
|
17
|
+
# Quality Control
|
|
18
|
+
s.add_development_dependency 'rspec'
|
|
19
|
+
s.add_development_dependency 'rubocop'
|
|
20
|
+
s.add_development_dependency 'simplecov'
|
|
21
|
+
|
|
22
|
+
# Debugging
|
|
23
|
+
s.add_development_dependency 'awesome_print'
|
|
24
|
+
s.add_development_dependency 'pry-byebug'
|
|
25
|
+
|
|
26
|
+
# for testing a gem with a rails app (controller specs)
|
|
27
|
+
# https://codingdaily.wordpress.com/2011/01/14/test-a-gem-with-the-rails-3-stack/
|
|
28
|
+
s.add_development_dependency 'bundler'
|
|
29
|
+
s.add_development_dependency 'capybara'
|
|
30
|
+
s.add_development_dependency 'rspec-rails'
|
|
31
|
+
s.add_development_dependency 'sqlite3'
|
|
32
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Decorates the template renderer chosen to render
|
|
2
|
+
# the current view/template/file.
|
|
3
|
+
# It overrides the render method in order to wrap the rendered template
|
|
4
|
+
# in a tag with data attributes
|
|
5
|
+
class ComponentParty::ActionView::ComponentRenderer::TagWrapperDecorator < SimpleDelegator
|
|
6
|
+
attr_reader :component_path
|
|
7
|
+
|
|
8
|
+
def initialize(target, component_path)
|
|
9
|
+
@component_path = component_path
|
|
10
|
+
super(target)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def render(context, options = {}, &block)
|
|
14
|
+
rendered = __getobj__.render(context, options, &block)
|
|
15
|
+
rendered = apply_html_namespacing(rendered.to_s)
|
|
16
|
+
ActionView::OutputBuffer.new(rendered)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def apply_html_namespacing(raw_html)
|
|
20
|
+
component_id = component_path.to_s.gsub(%r{^/}, '').tr('/', '-')
|
|
21
|
+
html_tag_end_regex = %r{(/?)>}
|
|
22
|
+
raw_html.sub(html_tag_end_regex) do
|
|
23
|
+
" data-component-path='#{component_id}' #{Regexp.last_match(1)}>"
|
|
24
|
+
end.html_safe
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module ComponentParty
|
|
2
|
+
# Renders a given component
|
|
3
|
+
module ActionView
|
|
4
|
+
class ComponentRenderer < ::ActionView::TemplateRenderer
|
|
5
|
+
attr_reader :lookup_context
|
|
6
|
+
attr_reader :component_path
|
|
7
|
+
|
|
8
|
+
def initialize(lookup_context, component_path)
|
|
9
|
+
@component_path = component_path
|
|
10
|
+
super(lookup_context)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def render(context, options)
|
|
14
|
+
options[:file] = template_path_from_component_path(options[:component])
|
|
15
|
+
options[:locals] = { vm: create_view_model(context, options) }
|
|
16
|
+
super(context, options)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def render_template(template, layout_name = nil, locals = nil) #:nodoc:
|
|
20
|
+
super(decorate_template(template), layout_name, locals)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def decorate_template(template)
|
|
24
|
+
ComponentParty::ActionView::ComponentRenderer::TagWrapperDecorator.new(template, component_path)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def create_view_model(context, options)
|
|
28
|
+
view_model_data = options[:view_model_data] || {}
|
|
29
|
+
view_model_data[:view] = context
|
|
30
|
+
|
|
31
|
+
vm_class = find_vm_class(options)
|
|
32
|
+
|
|
33
|
+
vm_class.new(view_model_data)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def find_vm_class(options)
|
|
37
|
+
if options[:custom_view_model]
|
|
38
|
+
if options[:custom_view_model] == true
|
|
39
|
+
vm_file_path = Pathname.new(options[:component]).join(ComponentParty.configuration.view_model_file_name).to_s
|
|
40
|
+
ActiveSupport::Inflector.camelize(vm_file_path).constantize
|
|
41
|
+
else
|
|
42
|
+
options[:custom_view_model]
|
|
43
|
+
end
|
|
44
|
+
else
|
|
45
|
+
ComponentParty::ViewModel
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def template_path_from_component_path(component_path, template_file_name: ComponentParty.configuration.template_file_name)
|
|
50
|
+
Pathname.new(component_path).join(template_file_name).to_s
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module ComponentParty #:nodoc:
|
|
2
|
+
# Renders a given component
|
|
3
|
+
module ActionView
|
|
4
|
+
module Renderer
|
|
5
|
+
def render(context, options)
|
|
6
|
+
if options.key?(:component)
|
|
7
|
+
normalize_data_for_component_rendering!(context, options)
|
|
8
|
+
ComponentParty::ActionView::ComponentRenderer.new(lookup_context, options[:component]).render(context, options)
|
|
9
|
+
else
|
|
10
|
+
super(context, options)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def normalize_data_for_component_rendering!(context, options)
|
|
15
|
+
normalize_component_path!(context, options)
|
|
16
|
+
context.instance_variable_set('@current_component_path', options[:component])
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# An example of options argumento passed by Rails are
|
|
20
|
+
# {
|
|
21
|
+
# :prefixes=>["devise/sessions", "devise", "application"],
|
|
22
|
+
# :template=>"new",
|
|
23
|
+
# :layout=> a Proc
|
|
24
|
+
# }
|
|
25
|
+
# rubocop:disable Metrics/LineLength
|
|
26
|
+
def normalize_component_path!(_context, options)
|
|
27
|
+
if options[:component] == true
|
|
28
|
+
options[:component] = Pathname.new(ComponentParty.configuration.component_folder_for_actions).join(options[:prefixes].first.to_s, options[:template]).to_s
|
|
29
|
+
else
|
|
30
|
+
options[:component]
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
# rubocop:enable all
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module ComponentParty
|
|
2
|
+
# Exposes component rendering methods to Rails views
|
|
3
|
+
|
|
4
|
+
module ImporterHelper
|
|
5
|
+
# Description of #import_component
|
|
6
|
+
#
|
|
7
|
+
# @param local_component_name [String] Local's component name (in the view scope)
|
|
8
|
+
# @param opts [Hash] default: {} Options.
|
|
9
|
+
# @example
|
|
10
|
+
def import_component(local_component_name, opts = {})
|
|
11
|
+
raise "No path informed when importing component #{local_component_name}" if opts[:path].blank?
|
|
12
|
+
|
|
13
|
+
component_to_render_path = get_full_component_path(opts[:path])
|
|
14
|
+
|
|
15
|
+
define_singleton_method(local_component_name) do |**args|
|
|
16
|
+
render(component: component_to_render_path, view_model_data: args, custom_view_model: (opts[:custom_view_model] || false))
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def get_full_component_path(path)
|
|
23
|
+
if path.starts_with?('./')
|
|
24
|
+
current_component_path = instance_variable_get('@current_component_path')
|
|
25
|
+
|
|
26
|
+
raise "You cannot use a relative component importing outside a component's template." if current_component_path.blank?
|
|
27
|
+
|
|
28
|
+
Pathname.new(current_component_path).join(path).to_s
|
|
29
|
+
else
|
|
30
|
+
path
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'rails/railtie'
|
|
2
|
+
|
|
3
|
+
class ViewComponent
|
|
4
|
+
class Rails < Rails::Railtie
|
|
5
|
+
initializer 'view_component' do
|
|
6
|
+
config.assets.paths << ::Rails.root.join(ComponentParty.configuration.components_path)
|
|
7
|
+
|
|
8
|
+
ActiveSupport.on_load(:action_controller) do
|
|
9
|
+
prepend_view_path(::Rails.root.join(ComponentParty.configuration.components_path)) if respond_to?(:prepend_view_path)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
::ActionView::Renderer.send(:prepend, ComponentParty::ActionView::Renderer)
|
|
13
|
+
::ActionView::Base.send(:include, ComponentParty::ImporterHelper)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module ComponentParty
|
|
2
|
+
class ViewModel
|
|
3
|
+
def initialize(**args)
|
|
4
|
+
generate_methods_from_hash(args)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def generate_methods_from_hash(hash)
|
|
10
|
+
hash.each do |key, val|
|
|
11
|
+
define_singleton_method key.to_sym do
|
|
12
|
+
val
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'component_party/importer_helper'
|
|
2
|
+
require 'component_party/view_model'
|
|
3
|
+
require 'component_party/action_view/renderer'
|
|
4
|
+
require 'component_party/action_view/component_renderer'
|
|
5
|
+
require 'component_party/action_view/component_renderer/tag_wrapper_decorator'
|
|
6
|
+
|
|
7
|
+
module ComponentParty
|
|
8
|
+
# Configuration class for ComponentParty
|
|
9
|
+
# @attr components_path [String] Components folder path (defaults to 'app/components')
|
|
10
|
+
# @attr components_path [String] Component's template file name (defaults to 'template')
|
|
11
|
+
class Configuration
|
|
12
|
+
attr_accessor :components_path
|
|
13
|
+
attr_accessor :template_file_name
|
|
14
|
+
attr_accessor :view_model_file_name
|
|
15
|
+
attr_accessor :component_folder_for_actions
|
|
16
|
+
|
|
17
|
+
def initialize
|
|
18
|
+
@components_path = 'app/components'
|
|
19
|
+
@template_file_name = 'template'
|
|
20
|
+
@view_model_file_name = 'view_model'
|
|
21
|
+
@component_folder_for_actions = 'pages'
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.configuration
|
|
26
|
+
@configuration ||= Configuration.new
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.configure
|
|
30
|
+
yield(configuration)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
require 'component_party/railtie'
|
data/partyhard.gif
ADDED
|
Binary file
|
data/rainbow_meme_v1.jpg
ADDED
|
Binary file
|
metadata
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: component_party
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.8.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Vinícius Oyama
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2019-03-28 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rails
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 4.2.0
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '6'
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: 4.2.0
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '6'
|
|
33
|
+
- !ruby/object:Gem::Dependency
|
|
34
|
+
name: rspec
|
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
type: :development
|
|
41
|
+
prerelease: false
|
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
- !ruby/object:Gem::Dependency
|
|
48
|
+
name: rubocop
|
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
54
|
+
type: :development
|
|
55
|
+
prerelease: false
|
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
- !ruby/object:Gem::Dependency
|
|
62
|
+
name: simplecov
|
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
type: :development
|
|
69
|
+
prerelease: false
|
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0'
|
|
75
|
+
- !ruby/object:Gem::Dependency
|
|
76
|
+
name: awesome_print
|
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '0'
|
|
82
|
+
type: :development
|
|
83
|
+
prerelease: false
|
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0'
|
|
89
|
+
- !ruby/object:Gem::Dependency
|
|
90
|
+
name: pry-byebug
|
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0'
|
|
96
|
+
type: :development
|
|
97
|
+
prerelease: false
|
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '0'
|
|
103
|
+
- !ruby/object:Gem::Dependency
|
|
104
|
+
name: bundler
|
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0'
|
|
110
|
+
type: :development
|
|
111
|
+
prerelease: false
|
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - ">="
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '0'
|
|
117
|
+
- !ruby/object:Gem::Dependency
|
|
118
|
+
name: capybara
|
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - ">="
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '0'
|
|
124
|
+
type: :development
|
|
125
|
+
prerelease: false
|
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - ">="
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: '0'
|
|
131
|
+
- !ruby/object:Gem::Dependency
|
|
132
|
+
name: rspec-rails
|
|
133
|
+
requirement: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - ">="
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '0'
|
|
138
|
+
type: :development
|
|
139
|
+
prerelease: false
|
|
140
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
141
|
+
requirements:
|
|
142
|
+
- - ">="
|
|
143
|
+
- !ruby/object:Gem::Version
|
|
144
|
+
version: '0'
|
|
145
|
+
- !ruby/object:Gem::Dependency
|
|
146
|
+
name: sqlite3
|
|
147
|
+
requirement: !ruby/object:Gem::Requirement
|
|
148
|
+
requirements:
|
|
149
|
+
- - ">="
|
|
150
|
+
- !ruby/object:Gem::Version
|
|
151
|
+
version: '0'
|
|
152
|
+
type: :development
|
|
153
|
+
prerelease: false
|
|
154
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
155
|
+
requirements:
|
|
156
|
+
- - ">="
|
|
157
|
+
- !ruby/object:Gem::Version
|
|
158
|
+
version: '0'
|
|
159
|
+
description: 'Frontend components for Ruby on Rails: group your view logic, html and
|
|
160
|
+
css files in components to be rendered from views or directly from controllers.'
|
|
161
|
+
email: contact@viniciusoyama.com
|
|
162
|
+
executables: []
|
|
163
|
+
extensions: []
|
|
164
|
+
extra_rdoc_files: []
|
|
165
|
+
files:
|
|
166
|
+
- ".codeclimate"
|
|
167
|
+
- ".gitignore"
|
|
168
|
+
- ".rspec"
|
|
169
|
+
- ".rubocop.yml"
|
|
170
|
+
- ".rubocop_todo.yml"
|
|
171
|
+
- ".travis.yml"
|
|
172
|
+
- ".yardopts"
|
|
173
|
+
- Gemfile
|
|
174
|
+
- README.md
|
|
175
|
+
- component_party.gemspec
|
|
176
|
+
- lib/component_party.rb
|
|
177
|
+
- lib/component_party/action_view/component_renderer.rb
|
|
178
|
+
- lib/component_party/action_view/component_renderer/tag_wrapper_decorator.rb
|
|
179
|
+
- lib/component_party/action_view/renderer.rb
|
|
180
|
+
- lib/component_party/importer_helper.rb
|
|
181
|
+
- lib/component_party/railtie.rb
|
|
182
|
+
- lib/component_party/view_model.rb
|
|
183
|
+
- partyhard.gif
|
|
184
|
+
- rainbow_meme_v1.jpg
|
|
185
|
+
homepage: https://github.com/viniciusoyama/component_party
|
|
186
|
+
licenses:
|
|
187
|
+
- MIT
|
|
188
|
+
metadata: {}
|
|
189
|
+
post_install_message:
|
|
190
|
+
rdoc_options: []
|
|
191
|
+
require_paths:
|
|
192
|
+
- lib
|
|
193
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
194
|
+
requirements:
|
|
195
|
+
- - ">="
|
|
196
|
+
- !ruby/object:Gem::Version
|
|
197
|
+
version: '0'
|
|
198
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
199
|
+
requirements:
|
|
200
|
+
- - ">="
|
|
201
|
+
- !ruby/object:Gem::Version
|
|
202
|
+
version: '0'
|
|
203
|
+
requirements: []
|
|
204
|
+
rubyforge_project:
|
|
205
|
+
rubygems_version: 2.7.8
|
|
206
|
+
signing_key:
|
|
207
|
+
specification_version: 4
|
|
208
|
+
summary: 'Stop using views: frontend components architecture for Ruby on Rails.'
|
|
209
|
+
test_files: []
|