komponent 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a99b2271c7cb864588a945d95ffe2bb96b1c29f8
4
- data.tar.gz: 523d53f622f49450b839515dd34441087123d32c
3
+ metadata.gz: 4658a034e4427f5870e308f64d0699e9b588b198
4
+ data.tar.gz: ef78a5b00c52b95676b15741765c8d221d12c697
5
5
  SHA512:
6
- metadata.gz: 3c4501a2a63283281a76cf550c40e4544c6fe5d834c539bf3b6e88b1999a17917f6b13033cc86ee0c861e13204f296a6c22ea89ef724da4b67861728f721254e
7
- data.tar.gz: 117abc530d8d1bdff4e68d63ebc43eae8eaf80babd0bc4778b91673c8edbf62c03f752410d37b545e9c49127c8741bd0d1d7c96969330a0d481d87090f98951e
6
+ metadata.gz: e362c8e4e277a68bfbf469c27c15f47a2544925710c16901b94eaf4b3d56a7359237f2b747a9b157ec68cc256476d11212456a37bc89e151b2d6aa6b8c823e2d
7
+ data.tar.gz: d8d8e23f3b219769d250cb58c46428bcc6344bbc68c18b300b39ef3d75d7d2ad8935ae44920da98d0a756dfd4efa08073d45f6e79222d492ff799454b027b5d2
data/.rubocop.yml ADDED
@@ -0,0 +1,112 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.4
3
+ DisabledByDefault: true
4
+ Exclude:
5
+ - 'node_modules/**/*'
6
+ - 'vendor/**/*'
7
+ - 'tmp/**/*'
8
+
9
+ Gemspec/OrderedDependencies:
10
+ Enabled: true
11
+
12
+ Style/BlockDelimiters:
13
+ Enabled: true
14
+
15
+ Style/BracesAroundHashParameters:
16
+ Enabled: true
17
+ EnforcedStyle: no_braces
18
+
19
+ Style/Dir:
20
+ Enabled: true
21
+
22
+ Style/Encoding:
23
+ Enabled: true
24
+
25
+ Style/For:
26
+ Enabled: true
27
+ EnforcedStyle: each
28
+
29
+ Style/MethodCallWithoutArgsParentheses:
30
+ Enabled: true
31
+
32
+ Style/MethodDefParentheses:
33
+ Enabled: true
34
+
35
+ Naming/ConstantName:
36
+ Enabled: true
37
+
38
+ Naming/FileName:
39
+ Enabled: true
40
+
41
+ Naming/MethodName:
42
+ Enabled: true
43
+
44
+ Naming/PredicateName:
45
+ Enabled: true
46
+
47
+ Naming/VariableName:
48
+ Enabled: true
49
+
50
+ Lint/BlockAlignment:
51
+ Enabled: true
52
+ EnforcedStyleAlignWith: start_of_block
53
+
54
+ Lint/ConditionPosition:
55
+ Enabled: true
56
+
57
+ Lint/DefEndAlignment:
58
+ Enabled: true
59
+
60
+ Lint/DeprecatedClassMethods:
61
+ Enabled: true
62
+
63
+ Layout/AlignParameters:
64
+ Enabled: true
65
+
66
+ Layout/BlockEndNewline:
67
+ Enabled: true
68
+
69
+ Layout/CaseIndentation:
70
+ Enabled: true
71
+
72
+ Layout/ClosingParenthesisIndentation:
73
+ Enabled: true
74
+
75
+ Layout/DotPosition:
76
+ Enabled: true
77
+
78
+ Layout/ElseAlignment:
79
+ Enabled: true
80
+
81
+ Layout/EmptyLines:
82
+ Enabled: true
83
+
84
+ Layout/EmptyLinesAroundAccessModifier:
85
+ Enabled: true
86
+
87
+ Layout/SpaceAroundBlockParameters:
88
+ Enabled: true
89
+
90
+ Layout/SpaceAroundEqualsInParameterDefault:
91
+ Enabled: true
92
+
93
+ Layout/SpaceAroundOperators:
94
+ Enabled: true
95
+
96
+ Layout/SpaceBeforeBlockBraces:
97
+ Enabled: true
98
+
99
+ Layout/SpaceBeforeComma:
100
+ Enabled: true
101
+
102
+ Layout/SpaceInsideArrayLiteralBrackets:
103
+ Enabled: true
104
+
105
+ Layout/SpaceInsideHashLiteralBraces:
106
+ Enabled: true
107
+
108
+ Layout/SpaceInsideParens:
109
+ Enabled: true
110
+
111
+ Layout/TrailingWhitespace:
112
+ Enabled: true
data/.travis.yml CHANGED
@@ -1,4 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 2.4
4
- script: bundle exec cucumber
4
+ script:
5
+ - bundle exec rubocop
6
+ - bundle exec cucumber
data/CHANGELOG.md ADDED
@@ -0,0 +1,45 @@
1
+ # Changelog
2
+
3
+ ## Upcoming release
4
+
5
+ ## v1.1.0 (2018-01-12)
6
+
7
+ **Enhancements:**
8
+ - [stimulus](https://github.com/stimulusjs/stimulus) integration
9
+ - Component generator supports `css`, `scss`, `sass` stylesheet engine
10
+ - Add a component path resolver
11
+
12
+ **Bug fixes**
13
+ - Make `content_for` work in component
14
+ - Fix issue with wrong stylesheet extension being used when
15
+ imported in JavaScript files
16
+
17
+ **Deprecation**
18
+ - `render_partial` is deprecated in favor of default `render`
19
+ - The file naming convention will be changed in the next major version, for namespaced components, to include the namespace:
20
+ - `frontend/components/admin/button/_button.html.slim` -> `frontend/components/admin/button/_admin_button.html.slim`
21
+ - `frontend/components/admin/button/button.js` -> `frontend/components/admin/button/admin_button.js`
22
+ - `frontend/components/admin/button/button.css` -> `frontend/components/admin/button/admin_button.css`
23
+
24
+ ## v1.0.0 (2018-01-01)
25
+
26
+ **Enhancements:**
27
+
28
+ - Components namespacing
29
+ - Implement basic features
30
+ - Implement `render_partial` helper
31
+
32
+ ## v1.0.0.pre.2 (2017-12-09)
33
+
34
+ **Enhancements:**
35
+
36
+ - Lazy-load helpers and configuration
37
+ - Add an install generator (`rails g komponent:install`)
38
+ - Standardize components name (underscore for all except css classes are dasherized)
39
+ - Support `erb`, `slim`, and `haml` template engines
40
+ - Add an `--locale` option to component generator
41
+ - Documentation improvements
42
+
43
+ ## v1.0.0.pre.1 (2017-12-09)
44
+
45
+ First pre-release
@@ -0,0 +1,46 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
+
7
+ ## Our Standards
8
+
9
+ Examples of behavior that contributes to creating a positive environment include:
10
+
11
+ * Using welcoming and inclusive language
12
+ * Being respectful of differing viewpoints and experiences
13
+ * Gracefully accepting constructive criticism
14
+ * Focusing on what is best for the community
15
+ * Showing empathy towards other community members
16
+
17
+ Examples of unacceptable behavior by participants include:
18
+
19
+ * The use of sexualized language or imagery and unwelcome sexual attention or advances
20
+ * Trolling, insulting/derogatory comments, and personal or political attacks
21
+ * Public or private harassment
22
+ * Publishing others' private information, such as a physical or electronic address, without explicit permission
23
+ * Other conduct which could reasonably be considered inappropriate in a professional setting
24
+
25
+ ## Our Responsibilities
26
+
27
+ Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28
+
29
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30
+
31
+ ## Scope
32
+
33
+ This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34
+
35
+ ## Enforcement
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@komposable.io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38
+
39
+ Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40
+
41
+ ## Attribution
42
+
43
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44
+
45
+ [homepage]: http://contributor-covenant.org
46
+ [version]: http://contributor-covenant.org/version/1/4/
data/README.md CHANGED
@@ -1,15 +1,37 @@
1
1
  # <img alt="Komponent" src="https://raw.github.com/ouvrages/komponent/master/logo.svg?sanitize=true" width="200" height="40" />
2
2
  [![Build Status](https://travis-ci.org/komposable/komponent.svg?branch=master)](https://travis-ci.org/komposable/komponent)
3
- =======
3
+ [![Gem](https://img.shields.io/gem/v/komponent.svg)](https://github.com/komposable/komponent)
4
4
 
5
5
  **Komponent** implements an opinionated way of organizing front-end code in Ruby on Rails, based on _components_.
6
6
 
7
- Each component has its own folder, containing a Ruby module, a slim partial, a PostCSS stylesheet and a JavaScript file.
7
+ Each component has its own folder, containing a Ruby module, a partial, a stylesheet and a JavaScript file.
8
8
 
9
9
  Komponent relies heavily on webpacker to manage dependencies and generate the production JS and CSS files.
10
10
 
11
+ This README examples are written in Slim, but Komponent is compatible with:
12
+
13
+ - your preferred templating language (Slim, Haml, erb)
14
+ - your stylesheet language of choice (Sass, SCSS, CSS, PostCSS)
15
+
11
16
  This gem has been inspired by our Rails development practices at [Ouvrages](https://ouvrages-web.fr) and [Etamin Studio](https://etaminstudio.com), and the (excellent) [_Modern Front-end in Rails_](https://evilmartians.com/chronicles/evil-front-part-1) article from Evil Martians.
12
17
 
18
+ ## Table of contents
19
+
20
+ - [Getting started](#getting-started)
21
+ - [Usage](#usage)
22
+ - [Passing variables](#passing-variables)
23
+ - [Passing a block](#passing-a-block)
24
+ - [Properties](#properties)
25
+ - [Helpers](#helpers)
26
+ - [Component partials](#component-partials)
27
+ - [Namespacing components](#namespacing-components)
28
+ - [Stimulus integration](#stimulus-integration)
29
+ - [Internationalization](#internationalization)
30
+ - [Configuration](#configuration)
31
+ - [Default options for the generators](#default-options-for-the-generators)
32
+ - [Additional paths](#additional-paths)
33
+ - [Contributing](#contributing)
34
+ - [License](#license)
13
35
 
14
36
  ## Getting started
15
37
 
@@ -137,19 +159,18 @@ a.button(href=@href)
137
159
 
138
160
  ### Component partials
139
161
 
140
- You can also choose to split your component into partials. In this case, use the `render_partial` helper to render a partial, stored inside the component directory. It's a simple shorthand of the default `render` helper from Rails.
162
+ You can also choose to split your component into partials. In this case, we can use the default `render` helper to render a partial, stored inside the component directory.
141
163
 
142
164
  ```slim
143
165
  / frontend/components/button/_button.html.slim
144
166
 
145
167
  = a.button(href=@href)
146
168
  = @text
147
- / The line below is similar to: render("components/button/suffix", text: "external link") if external_link?
148
- = render_partial("suffix", text: "external link") if external_link?
169
+ = render("suffix", text: "external link") if external_link?
149
170
  ```
150
171
 
151
172
  ```slim
152
- / frontend/components/button/_external_link.html.slim
173
+ / frontend/components/button/_suffix.html.slim
153
174
 
154
175
  = " (#{text})"
155
176
  ```
@@ -165,9 +186,144 @@ rails generate component admin/header
165
186
  This will create the component in an `admin` folder, and name its Ruby module `AdminHeaderComponent`.
166
187
 
167
188
 
189
+ ### Stimulus integration
190
+
191
+ You can pass `--stimulus` to both generators to use [stimulus](https://github.com/stimulusjs/stimulus) in your components.
192
+
193
+ ```sh
194
+ rails generate komponent:install --stimulus
195
+ ```
196
+
197
+ This will yarn `stimulus` package, and create a `stimulus_application.js` in the `frontend` folder.
198
+
199
+ ```sh
200
+ rails generate component button --stimulus
201
+ ```
202
+
203
+ This will create a component with an additional `button_controller.js` file, and define a `data-controller` in the generated view.
204
+
205
+ ### Internationalization
206
+
207
+ In case your component will contain text strings you want to localize, you can pass the `--locale` option to generate localization files in your component directory.
208
+
209
+ ```sh
210
+ rails generate component button --locale
211
+ ```
212
+
213
+ This will create a `yml` file for each locale (using `I18n.available_locales`). In your component, the `t` helper will use the same ["lazy" lookup](http://guides.rubyonrails.org/i18n.html#lazy-lookup) as Rails.
214
+
215
+ ```slim
216
+ / frontend/components/button/_button.html.slim
217
+ = a.button(href=@href)
218
+ = @text
219
+ = render("suffix", text: t(".external_link")) if external_link?
220
+ ```
221
+
222
+ ```yml
223
+ / frontend/components/button/button.en.yml
224
+ en:
225
+ button_component:
226
+ external_link: "external link"
227
+ ```
228
+
229
+ ```yml
230
+ / frontend/components/button/button.fr.yml
231
+ fr:
232
+ button_component:
233
+ external_link: "lien externe"
234
+ ```
235
+
236
+ ### Configuration
237
+
238
+ #### Default options for the generators
239
+
240
+ You can configure the generators in an initializer or in `application.rb`, so you don't have to add `--locale` and/or `--stimulus` flags every time you generate a fresh component.
241
+
242
+ ```rb
243
+ config.generators do |g|
244
+ g.komponent stimulus: true, locale: true # both are false by default
245
+ end
246
+ ```
247
+
248
+ #### Additional paths
249
+
250
+ You may want to use components in a gem, or a Rails engine, and expose them to the main app. In order to do that, you just have to configure the paths where Komponent will look for components.
251
+
252
+ From a gem:
253
+
254
+ ```rb
255
+ module MyGem
256
+ class Railtie < Rails::Railtie
257
+ config.after_initialize do |app|
258
+ app.config.komponent.component_paths.append(self.root.join("frontend/components"))
259
+ end
260
+
261
+ initializer "my_gem.action_dispatch" do |app|
262
+ ActiveSupport.on_load :action_controller do
263
+ ActionController::Base.prepend_view_path self.root.join("frontend")
264
+ end
265
+ end
266
+
267
+ initializer 'my_gem.autoload', before: :set_autoload_paths do |app|
268
+ app.config.autoload_paths << self.root.join("frontend")
269
+ end
270
+
271
+ private
272
+
273
+ def self.root
274
+ Pathname.new(File.dirname(__dir__))
275
+ end
276
+ end
277
+ end
278
+ ```
279
+
280
+ or from an engine:
281
+
282
+
283
+ ```rb
284
+ module MyEngine
285
+ class Engine < Rails::Engine
286
+ isolate_namespace MyEngine
287
+
288
+ config.after_initialize do |app|
289
+ app.config.komponent.component_paths.append(MyEngine::Engine.root.join("frontend/components"))
290
+ end
291
+
292
+ initializer "my_engine.action_dispatch" do |app|
293
+ ActiveSupport.on_load :action_controller do
294
+ ActionController::Base.prepend_view_path MyEngine::Engine.root.join("frontend")
295
+ end
296
+ end
297
+
298
+ initializer 'my_engine.autoload', before: :set_autoload_paths do |app|
299
+ app.config.autoload_paths << MyEngine::Engine.root.join("frontend")
300
+ end
301
+ end
302
+ end
303
+ ```
304
+
305
+ Make sure you add `komponent` to the runtime dependencies in your `gemspec`.
306
+
307
+ In order to compile packs from engine, and to use `javascript_pack_tag 'engine'`, you need to:
308
+
309
+ - Create a pack file in main app
310
+
311
+ ```js
312
+ // frontend/packs/engine.js
313
+
314
+ import 'packs/engine';
315
+ ```
316
+
317
+ - Append engine frontend folder to `resolved_paths` in `config/webpacker.yml` from your main app
318
+
319
+ ```yml
320
+ resolved_paths:
321
+ - engine/frontend
322
+ ```
323
+
168
324
  ## Contributing
169
325
 
170
- Bug reports and pull requests are welcome on GitHub at https://github.com/ouvrages/komponent.
326
+ Bug reports and pull requests are welcome on GitHub at https://github.com/komposable/komponent.
171
327
 
172
328
  ## License
173
329
 
data/komponent.gemspec CHANGED
@@ -1,4 +1,3 @@
1
- # coding: utf-8
2
1
  lib = File.expand_path("../lib", __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require "komponent/version"
@@ -21,10 +20,12 @@ Gem::Specification.new do |spec|
21
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
21
  spec.require_paths = ["lib"]
23
22
 
23
+ spec.add_development_dependency "aruba"
24
24
  spec.add_development_dependency "bundler", "~> 1.15"
25
- spec.add_development_dependency "rake", "~> 10.0"
26
-
27
25
  spec.add_development_dependency "cucumber"
28
- spec.add_development_dependency "aruba"
29
26
  spec.add_development_dependency "rails", ">= 5.0"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "rubocop", '~> 0.52.1'
29
+
30
+ spec.add_runtime_dependency "webpacker", ">= 3.0.0"
30
31
  end
@@ -2,17 +2,21 @@ class ComponentGenerator < Rails::Generators::NamedBase
2
2
  source_root File.expand_path('../templates', __FILE__)
3
3
 
4
4
  class_option :locale, type: :boolean, default: false
5
+ class_option :stimulus, type: :boolean, default: false
5
6
 
6
7
  def create_view_file
7
- template "view.html.#{template_engine}.erb", component_path + "_#{component_name}.html.#{template_engine}"
8
+ template "#{template_prefix}view.html.#{template_engine}.erb", component_path + "_#{name_with_namespace.underscore}.html.#{template_engine}"
8
9
  end
9
10
 
10
11
  def create_css_file
11
- template "css.erb", component_path + "#{component_name}.css"
12
+ template "#{stylesheet_engine}.erb", component_path + "#{name_with_namespace}.#{stylesheet_engine}"
12
13
  end
13
14
 
14
15
  def create_js_file
15
- template "js.erb", component_path + "#{component_name}.js"
16
+ template "js.erb", component_path + "#{name_with_namespace}.js"
17
+ if stimulus?
18
+ template "stimulus_controller_js.erb", component_path + "#{name_with_namespace}_controller.js"
19
+ end
16
20
  end
17
21
 
18
22
  def create_rb_file
@@ -24,7 +28,7 @@ class ComponentGenerator < Rails::Generators::NamedBase
24
28
 
25
29
  I18n.available_locales.each do |locale|
26
30
  @locale = locale
27
- template "locale.erb", component_path + "#{component_name}.#{locale}.yml"
31
+ template "locale.erb", component_path + "#{name_with_namespace}.#{locale}.yml"
28
32
  end
29
33
  end
30
34
 
@@ -37,7 +41,7 @@ class ComponentGenerator < Rails::Generators::NamedBase
37
41
  split_name[0..-2].each do |split|
38
42
  base_path += split
39
43
  file_path = base_path + "index.js"
40
- create_file(file_path) unless File.exists?(file_path)
44
+ create_file(file_path) unless File.exist?(file_path)
41
45
  imports << base_path.relative_path_from(root_path)
42
46
  end
43
47
 
@@ -54,12 +58,16 @@ class ComponentGenerator < Rails::Generators::NamedBase
54
58
  end
55
59
 
56
60
  append_to_file(base_path + "index.js") do
57
- "import \"#{base_path.relative_path_from(root_path)}/#{component_name}/#{component_name}\";\n"
58
- end
61
+ "import \"#{base_path.relative_path_from(root_path)}/#{component_name}/#{name_with_namespace.underscore}\";\n"
62
+ end
59
63
  end
60
64
 
61
65
  protected
62
66
 
67
+ def template_prefix
68
+ stimulus? ? "stimulus_" : ""
69
+ end
70
+
63
71
  def split_name
64
72
  name.split(/[:,::,\/]/).reject(&:blank?).map(&:underscore)
65
73
  end
@@ -81,16 +89,44 @@ class ComponentGenerator < Rails::Generators::NamedBase
81
89
  def component_class_name
82
90
  name_with_namespace.dasherize
83
91
  end
84
-
92
+
85
93
  def component_name
86
94
  split_name.last.underscore
87
95
  end
88
96
 
89
97
  def template_engine
90
- Rails.application.config.app_generators.rails[:template_engine] || :erb
98
+ app_generators.rails[:template_engine] || :erb
99
+ end
100
+
101
+ def stylesheet_engine
102
+ if sass = rails_configuration.try(:sass)
103
+ sass[:preferred_syntax]
104
+ else
105
+ :css
106
+ end
91
107
  end
92
108
 
93
109
  def locale?
94
- options[:locale]
110
+ return options[:locale] if options[:locale]
111
+ komponent_configuration[:locale]
112
+ end
113
+
114
+ def stimulus?
115
+ return options[:stimulus] if options[:stimulus]
116
+ komponent_configuration[:stimulus]
117
+ end
118
+
119
+ private
120
+
121
+ def komponent_configuration
122
+ { stimulus: nil, locale: nil }.merge app_generators.komponent
123
+ end
124
+
125
+ def rails_configuration
126
+ Rails.application.config
127
+ end
128
+
129
+ def app_generators
130
+ rails_configuration.app_generators
95
131
  end
96
132
  end
@@ -1 +1,11 @@
1
- import "./<%= component_name %>.css";
1
+ <%- if stimulus? -%>
2
+ import application from 'stimulus_application';
3
+ import { autoload } from "stimulus/webpack-helpers";
4
+
5
+ import "./<%= name_with_namespace %>.<%= stylesheet_engine %>";
6
+
7
+ const context = require.context('./', true, /_controller\.js$/);
8
+ autoload(context, application);
9
+ <%- else -%>
10
+ import "./<%= name_with_namespace %>.<%= stylesheet_engine %>";
11
+ <%- end -%>
@@ -1,3 +1,3 @@
1
1
  <%= @locale %>:
2
- <%= component_name %>:
3
- component_name: <%= component_name %>
2
+ <%= module_name.underscore %>:
3
+ component_name: <%= module_name.underscore %>
@@ -0,0 +1 @@
1
+ .<%= component_class_name %>
@@ -0,0 +1 @@
1
+ .<%= component_class_name %> {}
@@ -0,0 +1,9 @@
1
+ import { Controller } from "stimulus";
2
+ import "./<%= name_with_namespace %>.<%= stylesheet_engine %>";
3
+
4
+ export default class extends Controller {
5
+ connect() {
6
+ console.log("Hello, Stimulus!", this.element);
7
+ }
8
+ }
9
+
@@ -0,0 +1,7 @@
1
+ <div data-controller="<%= component_class_name %>" class="<%= component_class_name %>">
2
+ <%- if locale? -%>
3
+ <%%= t ".component_name" %>
4
+ <%- else -%>
5
+ <%= module_name.underscore %>
6
+ <%- end -%>
7
+ </div>
@@ -0,0 +1,6 @@
1
+ .<%= component_class_name %>{'data-controller': "<%= component_class_name %>", class: "<%= component_class_name %>"}
2
+ <%- if locale? -%>
3
+ = t ".component_name"
4
+ <%- else -%>
5
+ <%= module_name.underscore %>
6
+ <%- end -%>
@@ -0,0 +1,6 @@
1
+ div data-controller="<%= component_class_name %>" class="<%= component_class_name %>"
2
+ <%- if locale? -%>
3
+ = t ".component_name"
4
+ <%- else -%>
5
+ <%= module_name.underscore %>
6
+ <%- end -%>
@@ -1,3 +1,7 @@
1
1
  <div class="<%= component_class_name %>">
2
- <%= component_name %>
2
+ <%- if locale? -%>
3
+ <%%= t "<%= module_name.underscore %>.component_name" %>
4
+ <%- else -%>
5
+ <%= module_name.underscore %>
6
+ <%- end -%>
3
7
  </div>
@@ -1 +1,6 @@
1
- .<%= component_class_name %> <%= component_name %>
1
+ .<%= component_class_name %>{class: "<%= component_class_name %>"}
2
+ <%- if locale? -%>
3
+ = t "<%= module_name.underscore %>.component_name"
4
+ <%- else -%>
5
+ <%= module_name.underscore %>
6
+ <%- end -%>
@@ -1 +1,6 @@
1
- .<%= component_class_name %> <%= component_name %>
1
+ div class="<%= component_class_name %>"
2
+ <%- if locale? -%>
3
+ = t "<%= module_name.underscore %>.component_name"
4
+ <%- else -%>
5
+ <%= module_name.underscore %>
6
+ <%- end -%>
@@ -1,8 +1,10 @@
1
1
  module Komponent
2
2
  module Generators
3
3
  class InstallGenerator < Rails::Generators::Base
4
+ class_option :stimulus, type: :boolean, default: false
5
+
4
6
  def check_webpacker_dependency
5
- unless File.exists?(webpacker_configuration_file) and File.directory?(webpacker_default_structure)
7
+ unless File.exist?(webpacker_configuration_file) and File.directory?(webpacker_default_structure)
6
8
  raise Thor::Error, dependencies_not_met_error_message
7
9
  end
8
10
  end
@@ -24,12 +26,33 @@ module Komponent
24
26
  create_file(components_directory.join("index.js"))
25
27
  end
26
28
 
29
+ def create_stimulus_file
30
+ template = <<-eos
31
+ import { Application } from "stimulus";
32
+ const application = Application.start();
33
+ export default application;
34
+ eos
35
+ create_file(stimulus_application_path, stimulus? ? template : "")
36
+ end
37
+
27
38
  def append_to_application_pack
28
39
  append_to_file(application_pack_path, "import 'components';")
29
40
  end
30
41
 
42
+ def install_stimulus
43
+ if stimulus?
44
+ in_root do
45
+ run("yarn add stimulus")
46
+ end
47
+ end
48
+ end
49
+
31
50
  private
32
51
 
52
+ def stimulus_application_path
53
+ komponent_root_directory.join("stimulus_application.js")
54
+ end
55
+
33
56
  def application_pack_path
34
57
  komponent_root_directory.join("packs", "application.js")
35
58
  end
@@ -53,6 +76,25 @@ module Komponent
53
76
  def dependencies_not_met_error_message
54
77
  "Seems you don't have webpacker installed in your project. Please install webpacker, and follow instructions at https://github.com/rails/webpacker"
55
78
  end
79
+
80
+ def stimulus?
81
+ return options[:stimulus] if options[:stimulus]
82
+ komponent_configuration[:stimulus]
83
+ end
84
+
85
+ private
86
+
87
+ def komponent_configuration
88
+ { stimulus: nil, locale: nil }.merge app_generators.komponent
89
+ end
90
+
91
+ def rails_configuration
92
+ Rails.application.config
93
+ end
94
+
95
+ def app_generators
96
+ rails_configuration.app_generators
97
+ end
56
98
  end
57
99
  end
58
100
  end
@@ -0,0 +1,52 @@
1
+ module Komponent
2
+ class ComponentPathResolver
3
+ def resolve(component_name)
4
+ root_path = component_paths.find do |path|
5
+ path_has_component?(path, component_name)
6
+ end
7
+
8
+ if root_path.nil?
9
+ raise ComponentPathResolver::MissingComponentError.new(
10
+ component_name,
11
+ component_paths,
12
+ )
13
+ end
14
+
15
+ root_path.join(*component_name)
16
+ end
17
+
18
+ protected
19
+
20
+ def path_has_component?(path, component_name)
21
+ file_name = path.join(*[
22
+ component_name,
23
+ "#{component_name.split("/").join("_")}_component.rb",
24
+ ])
25
+ File.exist?(file_name)
26
+ end
27
+
28
+ def component_paths
29
+ komponent_configuration.component_paths.map do |path|
30
+ Pathname.new(path)
31
+ end
32
+ end
33
+
34
+ def komponent_configuration
35
+ app_configuration.komponent
36
+ end
37
+
38
+ def app_configuration
39
+ Rails.application.config
40
+ end
41
+
42
+ class MissingComponentError < StandardError
43
+ def initialize(component_name, paths)
44
+ message = "Component `#{component_name}` not found in:\n"
45
+ paths.each do |path|
46
+ message += " * #{path}\n"
47
+ end
48
+ super(message)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,22 @@
1
+ module Komponent
2
+ module Translation
3
+ def translate(key, options = {})
4
+ virtual_path = @virtual_path
5
+
6
+ is_component = key.to_s.first == "." and
7
+ virtual_path =~ /^components/
8
+
9
+ if is_component
10
+ path = virtual_path.match(/^components\/.+\/_(.+)/)[1]
11
+ path += "_component"
12
+ defaults = [:"#{path}#{key}"]
13
+ defaults << options[:default] if options[:default]
14
+ options[:default] = defaults.flatten
15
+ key = "#{path}.#{key}"
16
+ end
17
+
18
+ super(key, options)
19
+ end
20
+ alias :t :translate
21
+ end
22
+ end
@@ -1,16 +1,25 @@
1
1
  module KomponentHelper
2
2
  def component(component, locals = {}, &block)
3
- components_path = Rails.root.join("frontend", "components")
3
+ component_path = Komponent::ComponentPathResolver.new.resolve(component)
4
4
 
5
5
  parts = component.split("/")
6
- component_path = components_path.join(*parts)
7
6
  component_name = parts.join("_")
8
7
  component_path = component_path.join("#{component_name}_component")
8
+
9
9
  require_dependency(component_path)
10
10
 
11
11
  component_module = "#{component_name}_component".camelize.constantize
12
12
  context = controller.view_context.dup
13
+
14
+ context.view_flow = view_flow
15
+
16
+ view_renderer = context.view_renderer = context.view_renderer.dup
17
+ lookup_context = view_renderer.lookup_context = view_renderer.lookup_context.dup
18
+ lookup_context.prefixes = ["components/#{component}"]
19
+
13
20
  context.class_eval { prepend component_module }
21
+ context.class_eval { prepend Komponent::Translation }
22
+
14
23
  capture_block = proc { capture(&block) } if block
15
24
 
16
25
  context.instance_eval do
@@ -36,33 +45,37 @@ module KomponentHelper
36
45
  if context.respond_to?(custom_method)
37
46
  context.public_send(custom_method, locals, &capture_block)
38
47
  else
39
- context.render("components/#{component}/#{parts.last}", &capture_block)
48
+ begin
49
+ context.render("components/#{component}/#{parts.join('_')}", &capture_block)
50
+ rescue ActionView::MissingTemplate
51
+ warn "[DEPRECATION WARNING] `#{parts.last}` filename in namespace is deprecated in favor of `#{parts.join('_')}`."
52
+ context.render("components/#{component}/#{parts.last}", &capture_block)
53
+ end
40
54
  end
41
55
  end
42
-
43
56
  alias :c :component
44
57
 
45
58
  def render_partial(partial_name, locals = {}, &block)
46
- benchmark("Rendered partial #{partial_name}") do
47
- context = controller.view_context
48
- view_paths = context.lookup_context.view_paths.dup
49
- components_path = Rails.root.join "frontend/components"
50
-
51
- capture_block = proc { capture(&block) } if block
59
+ warn "[DEPRECATION WARNING] `render_partial` is deprecated. Please use default `render` instead."
52
60
 
53
- current_dir = Pathname.new(@virtual_path).dirname
61
+ context = controller.view_context
62
+ view_paths = context.lookup_context.view_paths.dup
63
+ components_path = Rails.root.join "frontend/components"
54
64
 
55
- context.lookup_context.prefixes.prepend current_dir
56
- context.lookup_context.view_paths.unshift components_path
65
+ capture_block = proc { capture(&block) } if block
57
66
 
58
- rendered_partial = capture do
59
- context.render partial_name, locals, &capture_block
60
- end
67
+ current_dir = Pathname.new(@virtual_path).dirname
61
68
 
62
- context.lookup_context.prefixes.delete current_dir
63
- context.lookup_context.view_paths = view_paths
69
+ context.lookup_context.prefixes.prepend current_dir
70
+ context.lookup_context.view_paths.unshift components_path
64
71
 
65
- rendered_partial
72
+ rendered_partial = capture do
73
+ context.render partial_name, locals, &capture_block
66
74
  end
75
+
76
+ context.lookup_context.prefixes.delete current_dir
77
+ context.lookup_context.view_paths = view_paths
78
+
79
+ rendered_partial
67
80
  end
68
81
  end
@@ -1,7 +1,18 @@
1
+ require 'webpacker'
1
2
  require 'komponent/core/component_helper'
3
+ require 'komponent/core/translation'
4
+ require 'komponent/core/component_path_resolver'
2
5
 
3
6
  module Komponent
4
7
  class Railtie < Rails::Railtie
8
+ config.komponent = ActiveSupport::OrderedOptions.new
9
+ config.komponent.component_paths = []
10
+
11
+ config.before_configuration do |app|
12
+ app.config.komponent = config.komponent
13
+ app.config.komponent.component_paths.append(app.config.root.join("frontend/components"))
14
+ end
15
+
5
16
  initializer "komponent.action_view" do |app|
6
17
  ActiveSupport.on_load :action_view do
7
18
  require 'komponent/komponent_helper'
@@ -17,7 +28,7 @@ module Komponent
17
28
 
18
29
  initializer "komponent.i18n" do |app|
19
30
  ActiveSupport.on_load :i18n do
20
- I18n.load_path.concat(Dir["#{app.config.root}/frontend/components/*/*.yml"])
31
+ I18n.load_path.concat(Dir["#{app.config.root}/frontend/components/**/*.yml"])
21
32
  end
22
33
  end
23
34
 
@@ -1,3 +1,3 @@
1
1
  module Komponent
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: komponent
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ouvrages
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-01-01 00:00:00.000000000 Z
11
+ date: 2018-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: bundler
14
+ name: aruba
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.15'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.15'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '1.15'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: '1.15'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: cucumber
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -53,33 +53,61 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: aruba
56
+ name: rails
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '5.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '5.0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: rails
70
+ name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '5.0'
75
+ version: '10.0'
76
76
  type: :development
77
77
  prerelease: false
78
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.52.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.52.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: webpacker
99
+ requirement: !ruby/object:Gem::Requirement
79
100
  requirements:
80
101
  - - ">="
81
102
  - !ruby/object:Gem::Version
82
- version: '5.0'
103
+ version: 3.0.0
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: 3.0.0
83
111
  description: An opinionated way of organizing front-end code in Ruby on Rails, based
84
112
  on components
85
113
  email:
@@ -89,7 +117,10 @@ extensions: []
89
117
  extra_rdoc_files: []
90
118
  files:
91
119
  - ".gitignore"
120
+ - ".rubocop.yml"
92
121
  - ".travis.yml"
122
+ - CHANGELOG.md
123
+ - CODE_OF_CONDUCT.md
93
124
  - Gemfile
94
125
  - LICENSE.txt
95
126
  - README.md
@@ -103,12 +134,20 @@ files:
103
134
  - lib/generators/component/templates/js.erb
104
135
  - lib/generators/component/templates/locale.erb
105
136
  - lib/generators/component/templates/rb.erb
137
+ - lib/generators/component/templates/sass.erb
138
+ - lib/generators/component/templates/scss.erb
139
+ - lib/generators/component/templates/stimulus_controller_js.erb
140
+ - lib/generators/component/templates/stimulus_view.html.erb.erb
141
+ - lib/generators/component/templates/stimulus_view.html.haml.erb
142
+ - lib/generators/component/templates/stimulus_view.html.slim.erb
106
143
  - lib/generators/component/templates/view.html.erb.erb
107
144
  - lib/generators/component/templates/view.html.haml.erb
108
145
  - lib/generators/component/templates/view.html.slim.erb
109
146
  - lib/generators/komponent/install_generator.rb
110
147
  - lib/komponent.rb
111
148
  - lib/komponent/core/component_helper.rb
149
+ - lib/komponent/core/component_path_resolver.rb
150
+ - lib/komponent/core/translation.rb
112
151
  - lib/komponent/komponent_helper.rb
113
152
  - lib/komponent/railtie.rb
114
153
  - lib/komponent/version.rb