elemental_components 1.0.0.rc1

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: 8724d56949364ada3c44e3db2924edb30ea0c4d3929295fc7d0a683a576b2089
4
+ data.tar.gz: d7c2d081abf83dbf6e42e68a3eca26ab186dec2f446380a5a94b0a7ec5634857
5
+ SHA512:
6
+ metadata.gz: a619164930556d15e744814440632e2a44afabcce5ffefafbe99e8d2c5d88883b9e9e849ec4595ddd8d6a8fbf921d89ebc097a098efd084f6e63237abd67c15a
7
+ data.tar.gz: 96dfa689ebbb64c40ca722bc9656a45ba27951e0824318f9da114932314652150c47ec4983434a2b3f0efaf58bbf1289e6a3c59a2e1b83aaeee343d36264e0c6
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2018 Jens Ljungblad
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,483 @@
1
+ # Elemental Components
2
+
3
+ Simple view components for Rails 5.1+, designed to go well with [elemental_styleguide](https://github.com/jensljungblad/elemental_styleguide). The two together are inspired by the works of [Brad Frost](http://bradfrost.com) and by the [thoughts behind](http://engineering.lonelyplanet.com/2014/05/18/a-maintainable-styleguide.html) Lonely Planet's style guide [Rizzo](http://rizzo.lonelyplanet.com).
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem "elemental_components"
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```sh
16
+ $ bundle
17
+ ```
18
+
19
+ ## Components
20
+
21
+ The examples provided here will use the [BEM naming conventions](http://getbem.com/naming/).
22
+
23
+ Components live in `app/components`. Generate a component by executing:
24
+
25
+ ```sh
26
+ $ bin/rails g elemental_components:component alert
27
+ ```
28
+
29
+ This will create the following files:
30
+
31
+ ```
32
+ app/
33
+ components/
34
+ alert/
35
+ _alert.html.erb
36
+ alert.css
37
+ alert.js
38
+ alert_component.rb
39
+ ```
40
+
41
+ The generator also takes `--skip-css` and `--skip-js` options.
42
+
43
+ Let's add some markup and CSS:
44
+
45
+ ```erb
46
+ <% # app/components/alert/_alert.html.erb %>
47
+
48
+ <div class="alert alert--primary" role="alert">
49
+ Message
50
+ </div>
51
+ ```
52
+
53
+ ```css
54
+ /* app/components/alert/alert.css */
55
+
56
+ .alert {
57
+ padding: 1rem;
58
+ }
59
+
60
+ .alert--primary {
61
+ background: blue;
62
+ }
63
+
64
+ .alert--success {
65
+ background: green;
66
+ }
67
+
68
+ .alert--danger {
69
+ background: red;
70
+ }
71
+ ```
72
+
73
+ This component can now be rendered using the `component` helper:
74
+
75
+ ```erb
76
+ <%= component "alert" %>
77
+ ```
78
+
79
+ ### Assets
80
+
81
+ In order to require assets such as CSS, either require them manually in the manifest, e.g. `application.css`:
82
+
83
+ ```css
84
+ /*
85
+ *= require alert/alert
86
+ */
87
+ ```
88
+
89
+ Or require `components`, which will in turn require the assets for all components:
90
+
91
+ ```css
92
+ /*
93
+ *= require elemental_components
94
+ */
95
+ ```
96
+
97
+ ### Attributes and blocks
98
+
99
+ 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 to inject HTML content into components.
100
+
101
+ Let's define some attributes for the component we just created:
102
+
103
+ ```ruby
104
+ # app/components/alert_component.rb %>
105
+
106
+ class AlertComponent < ElementalComponents::Component
107
+ attribute :context
108
+ attribute :message
109
+ end
110
+ ```
111
+
112
+ ```erb
113
+ <% # app/components/alert/_alert.html.erb %>
114
+
115
+ <div class="alert alert--<%= alert.context %>" role="alert">
116
+ <%= alert.message %>
117
+ </div>
118
+ ```
119
+
120
+ ```erb
121
+ <%= component "alert", message: "Something went right!", context: "success" %>
122
+ <%= component "alert", message: "Something went wrong!", context: "danger" %>
123
+ ```
124
+
125
+ 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:
126
+
127
+ ```erb
128
+ <% # app/components/alert/_alert.html.erb %>
129
+
130
+ <div class="alert alert--<%= alert.context %>" role="alert">
131
+ <%= alert %>
132
+ </div>
133
+ ```
134
+
135
+ ```erb
136
+ <%= component "alert", context: "success" do %>
137
+ <em>Something</em> went right!
138
+ <% end %>
139
+ ```
140
+
141
+ Another good use case for attributes is when you have a component backed by a model:
142
+
143
+ ```ruby
144
+ # app/components/comment_component.rb %>
145
+
146
+ class CommentComponent < ElementalComponents::Component
147
+ attribute :comment
148
+
149
+ delegate :id,
150
+ :author,
151
+ :body, to: :comment
152
+ end
153
+ ```
154
+
155
+ ```erb
156
+ <% # app/components/comment/_comment.html.erb %>
157
+
158
+ <div id="comment-<%= comment.id %>" class="comment">
159
+ <div class="comment__author">
160
+ <%= link_to comment.author.name, author_path(comment.author) %>
161
+ </div>
162
+ <div class="comment__body">
163
+ <%= comment.body %>
164
+ </div>
165
+ </div>
166
+ ```
167
+
168
+ ```erb
169
+ <% comments.each do |comment| %>
170
+ <%= component "comment", comment: comment %>
171
+ <% end %>
172
+ ```
173
+
174
+ ### Attribute defaults
175
+
176
+ Attributes can have default values:
177
+
178
+ ```ruby
179
+ # app/components/alert_component.rb %>
180
+
181
+ class AlertComponent < ElementalComponents::Component
182
+ attribute :message
183
+ attribute :context, default: "primary"
184
+ end
185
+ ```
186
+
187
+ ### Attribute overrides
188
+
189
+ It's easy to override an attribute with additional logic:
190
+
191
+ ```ruby
192
+ # app/components/alert_component.rb %>
193
+
194
+ class AlertComponent < ElementalComponents::Component
195
+ attribute :message
196
+ attribute :context, default: "primary"
197
+
198
+ def message
199
+ @message.upcase if context == "danger"
200
+ end
201
+ end
202
+ ```
203
+
204
+ ### Attribute validation
205
+
206
+ To ensure your components get initialized properly you can use `ActiveModel::Validations` in your elements or components:
207
+
208
+ ```ruby
209
+ # app/components/alert_component.rb %>
210
+
211
+ class AlertComponent < ElementalComponents::Component
212
+ attribute :label
213
+
214
+ validates :label, presence: true
215
+ end
216
+ ```
217
+
218
+ Your validations will be executed during the components initialization and raise an `ActiveModel::ValidationError` if any validation fails.
219
+
220
+ ### Elements
221
+
222
+ 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.
223
+
224
+ Take a card component. In React, a common approach is to create subcomponents:
225
+
226
+ ```jsx
227
+ <Card flush={true}>
228
+ <CardHeader centered={true}>
229
+ Header
230
+ </CardHeader>
231
+ <CardSection size="large">
232
+ Section 1
233
+ </CardSection>
234
+ <CardSection size="small">
235
+ Section 2
236
+ </CardSection>
237
+ <CardFooter>
238
+ Footer
239
+ </CardFooter>
240
+ </Card>
241
+ ```
242
+
243
+ There are two problems with this approach:
244
+
245
+ 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`.
246
+ 2. We lose control of the structure of the elements. A `CardHeader` can be placed below, or inside a `CardFooter`.
247
+
248
+ Using this gem, the same component can be written like this:
249
+
250
+ ```ruby
251
+ # app/components/card_component.rb %>
252
+
253
+ class CardComponent < ElementalComponents::Component
254
+ attribute :flush, default: false
255
+
256
+ element :header do
257
+ attribute :centered, default: false
258
+ end
259
+
260
+ element :section, multiple: true do
261
+ attribute :size
262
+ end
263
+
264
+ element :footer
265
+ end
266
+ ```
267
+
268
+ ```erb
269
+ <% # app/components/card/_card.html.erb %>
270
+
271
+ <div class="card <%= "card--flush" if card.flush %>">
272
+ <div class="card__header <%= "card__header--centered" if card.header.centered %>">
273
+ <%= card.header %>
274
+ </div>
275
+ <% card.sections.each do |section| %>
276
+ <div class="card__section <%= "card__section--#{section.size}" %>">
277
+ <%= section %>
278
+ </div>
279
+ <% end %>
280
+ <div class="card__footer">
281
+ <%= card.footer %>
282
+ </div>
283
+ </div>
284
+ ```
285
+
286
+ 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.
287
+
288
+ 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:
289
+
290
+ ```erb
291
+ <%= component "card", flush: true do |c| %>
292
+ <% c.header centered: true do %>
293
+ Header
294
+ <% end %>
295
+ <% c.section size: "large" do %>
296
+ Section 1
297
+ <% end %>
298
+ <% c.section size: "large" do %>
299
+ Section 2
300
+ <% end %>
301
+ <% c.footer do %>
302
+ Footer
303
+ <% end %>
304
+ <% end %>
305
+ ```
306
+
307
+ Multiple calls to a repeating element, such as `section` in the example above, will append each section to an array.
308
+
309
+ Another good use case is a navigation component:
310
+
311
+ ```ruby
312
+ # app/components/navigation_component.rb %>
313
+
314
+ class NavigationComponent < ElementalComponents::Component
315
+ element :items, multiple: true do
316
+ attribute :label
317
+ attribute :url
318
+ attribute :active, default: false
319
+ end
320
+ end
321
+ ```
322
+
323
+ ```erb
324
+ <%= component "navigation" do |c| %>
325
+ <% c.item label: "Home", url: root_path, active: true %>
326
+ <% c.item label: "Explore" url: explore_path %>
327
+ <% end %>
328
+ ```
329
+
330
+ 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:
331
+
332
+ ```erb
333
+ <%= component "navigation", items: items %>
334
+ ```
335
+
336
+ Elements can have validations, too:
337
+
338
+ ```ruby
339
+ class NavigationComponent < ElementalComponents::Component
340
+ element :items, multiple: true do
341
+ attribute :label
342
+ attribute :url
343
+ attribute :active, default: false
344
+
345
+ validates :label, presence: true
346
+ validates :url, presence: true
347
+ end
348
+ end
349
+ ```
350
+
351
+ Elements can also be nested, although it is recommended to keep nesting to a minimum:
352
+
353
+ ```ruby
354
+ # app/components/card_component.rb %>
355
+
356
+ class CardComponent < ElementalComponents::Component
357
+ ...
358
+
359
+ element :section, multiple: true do
360
+ attribute :size
361
+
362
+ element :header
363
+ element :footer
364
+ end
365
+ end
366
+ ```
367
+
368
+ ### Helper methods
369
+
370
+ 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:
371
+
372
+ ```ruby
373
+ # app/components/card_component.rb %>
374
+
375
+ class CardComponent < ElementalComponents::Component
376
+ ...
377
+
378
+ def css_classes
379
+ css_classes = ["card"]
380
+ css_classes << "card--flush" if flush
381
+ css_classes.join(" ")
382
+ end
383
+ end
384
+ ```
385
+
386
+ ```erb
387
+ <% # app/components/card/_card.html.erb %>
388
+
389
+ <%= content_tag :div, class: card.css_classes do %>
390
+ ...
391
+ <% end %>
392
+ ```
393
+
394
+ It's even possible to declare helpers on elements:
395
+
396
+ ```ruby
397
+ # app/components/card_component.rb %>
398
+
399
+ class CardComponent < ElementalComponents::Component
400
+ ...
401
+
402
+ element :section, multiple: true do
403
+ attribute :size
404
+
405
+ def css_classes
406
+ css_classes = ["card__section"]
407
+ css_classes << "card__section--#{size}" if size
408
+ css_classes.join(" ")
409
+ end
410
+ end
411
+ end
412
+ ```
413
+
414
+ ```erb
415
+ <% # app/components/card/_card.html.erb %>
416
+
417
+ <%= content_tag :div, class: card.css_classes do %>
418
+ ...
419
+ <%= content_tag :div, class: section.css_classes do %>
420
+ <%= section %>
421
+ <% end %>
422
+ ...
423
+ <% end %>
424
+ ```
425
+
426
+ Helper methods can also make use of the `@view` instance variable in order to call Rails helpers such as `link_to` or `content_tag`.
427
+
428
+ ### Rendering components without a partial
429
+
430
+ 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:
431
+
432
+ ```ruby
433
+ # app/components/button_component.rb %>
434
+
435
+ class ButtonComponent < ElementalComponents::Component
436
+ attribute :label
437
+ attribute :url
438
+ attribute :context
439
+
440
+ def render
441
+ @view.link_to label, url, class: css_classes
442
+ end
443
+
444
+ def css_classes
445
+ css_classes = "button"
446
+ css_classes << "button--#{context}" if context
447
+ css_classes.join(" ")
448
+ end
449
+ end
450
+ ```
451
+
452
+ ```erb
453
+ <%= component "button", label: "Sign up", url: sign_up_path, context: "primary" %>
454
+ <%= component "button", label: "Sign in", url: sign_in_path %>
455
+ ```
456
+
457
+ ### Namespaced components
458
+
459
+ 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:
460
+
461
+ ```ruby
462
+ module Objects
463
+ class MediaObject < ElementalComponents::Component; end
464
+ end
465
+ ```
466
+
467
+ Then call it from a template like so:
468
+
469
+ ```erb
470
+ <%= component "objects/media_object" %>
471
+ ```
472
+
473
+ ## Acknowledgements
474
+
475
+ This library, together with [elemental_styleguide](https://github.com/jensljungblad/elemental_styleguide), was inspired by the writings of [Brad Frost](http://bradfrost.com) on atomic design and living style guides, and [Rizzo](http://rizzo.lonelyplanet.com), the Lonely Planet style guide. Other inspirations were:
476
+
477
+ - [Catalog](https://www.catalog.style) - style guide for React
478
+ - [Storybook](https://storybook.js.org) - style guide for React
479
+ - [React Styleguidist](https://react-styleguidist.js.org) - style guide for React
480
+ - [Cells](https://github.com/trailblazer/cells) - view components for Ruby
481
+ - [Komponent](https://github.com/komposable/komponent) - view components for Ruby
482
+
483
+ For a list of real world style guides, check out http://styleguides.io.
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require "bundler/setup"
5
+ rescue LoadError
6
+ puts "You must `gem install bundler` and `bundle install` to run rake tasks"
7
+ end
8
+
9
+ require "rdoc/task"
10
+
11
+ RDoc::Task.new(:rdoc) do |rdoc|
12
+ rdoc.rdoc_dir = "rdoc"
13
+ rdoc.title = "Elemental Components"
14
+ rdoc.options << "--line-numbers"
15
+ rdoc.rdoc_files.include("README.md")
16
+ rdoc.rdoc_files.include("lib/**/*.rb")
17
+ end
18
+
19
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
20
+ load "rails/tasks/engine.rake"
21
+
22
+ load "rails/tasks/statistics.rake"
23
+
24
+ require "bundler/gem_tasks"
25
+
26
+ require "rake/testtask"
27
+
28
+ Rake::TestTask.new(:test) do |t|
29
+ t.libs << "test"
30
+ t.pattern = "test/**/*_test.rb"
31
+ t.verbose = false
32
+ end
33
+
34
+ task default: :test
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts/elemental_components .js
2
+ //= link_directory ../stylesheets/elemental_components .css
@@ -0,0 +1,7 @@
1
+ <% ElementalComponents.component_names.each do |name| %>
2
+ <% begin %>
3
+ <% require_asset "#{name}/#{name.split('/')[-1]}" %>
4
+ <% rescue Sprockets::FileNotFound %>
5
+ <% Rails.logger.debug "ElementalComponents: JS not found for #{name}" %>
6
+ <% end %>
7
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <% ElementalComponents.component_names.each do |name| %>
2
+ <% begin %>
3
+ <% require_asset "#{name}/#{name.split('/')[-1]}" %>
4
+ <% rescue Sprockets::FileNotFound %>
5
+ <% Rails.logger.debug "ElementalComponents: CSS not found for #{name}" %>
6
+ <% end %>
7
+ <% end %>
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElementalComponents
4
+ module ComponentHelper
5
+ def component(name, attrs = nil, &block)
6
+ "#{name}_component".classify.constantize.new(self, attrs, &block).render
7
+ end
8
+ end
9
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ ElementalComponents::Engine.routes.draw do
4
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "elemental_components/element"
4
+ require "elemental_components/component"
5
+ require "elemental_components/engine"
6
+
7
+ module ElementalComponents
8
+ class Error < StandardError; end
9
+
10
+ def self.components_path
11
+ Rails.root.join("app", "components")
12
+ end
13
+
14
+ def self.component_names
15
+ Dir.chdir(components_path) do
16
+ Dir.glob("**/*_component.rb").map { |component| component.chomp("_component.rb") }.sort
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElementalComponents
4
+ class Component < Element
5
+ def self.model_name
6
+ ActiveModel::Name.new(ElementalComponents::Component)
7
+ end
8
+
9
+ def self.component_name
10
+ name.chomp("Component").demodulize.underscore
11
+ end
12
+
13
+ def self.component_path
14
+ name.chomp("Component").underscore
15
+ end
16
+
17
+ def render
18
+ @view.render partial: to_partial_path, object: self
19
+ end
20
+
21
+ def to_partial_path
22
+ [self.class.component_path, self.class.component_name].join("/")
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElementalComponents
4
+ class Element
5
+ include ActiveModel::Validations
6
+
7
+ def self.model_name
8
+ ActiveModel::Name.new(ElementalComponents::Element)
9
+ end
10
+
11
+ def self.attributes
12
+ @attributes ||= {}
13
+ end
14
+
15
+ def self.attribute(name, default: nil)
16
+ attributes[name] = { default: default }
17
+
18
+ define_method_or_raise(name) do
19
+ get_instance_variable(name)
20
+ end
21
+ end
22
+
23
+ def self.elements
24
+ @elements ||= {}
25
+ end
26
+
27
+ # rubocop:disable Metrics/AbcSize
28
+ # rubocop:disable Metrics/CyclomaticComplexity
29
+ # rubocop:disable Metrics/MethodLength
30
+ # rubocop:disable Metrics/PerceivedComplexity
31
+ def self.element(name, multiple: false, &config)
32
+ plural_name = name.to_s.pluralize.to_sym if multiple
33
+
34
+ elements[name] = {
35
+ multiple: plural_name || false, class: Class.new(Element, &config)
36
+ }
37
+
38
+ define_method_or_raise(name) do |attributes = nil, &block|
39
+ return get_instance_variable(multiple ? plural_name : name) unless attributes || block
40
+
41
+ element = self.class.elements[name][:class].new(@view, attributes, &block)
42
+
43
+ if multiple
44
+ get_instance_variable(plural_name) << element
45
+ else
46
+ set_instance_variable(name, element)
47
+ end
48
+ end
49
+
50
+ return if !multiple || name == plural_name
51
+
52
+ define_method_or_raise(plural_name) do
53
+ get_instance_variable(plural_name)
54
+ end
55
+ end
56
+ # rubocop:enable Metrics/AbcSize
57
+ # rubocop:enable Metrics/CyclomaticComplexity
58
+ # rubocop:enable Metrics/MethodLength
59
+ # rubocop:enable Metrics/PerceivedComplexity
60
+
61
+ def self.define_method_or_raise(method_name, &block)
62
+ if method_defined?(method_name.to_sym)
63
+ raise(ElementalComponents::Error, "Method '#{method_name}' already exists.")
64
+ end
65
+
66
+ define_method(method_name, &block)
67
+ end
68
+ private_class_method :define_method_or_raise
69
+
70
+ def initialize(view, attributes = nil, &block)
71
+ @view = view
72
+ initialize_attributes(attributes || {})
73
+ initialize_elements
74
+ @yield = block_given? ? @view.capture(self, &block) : nil
75
+ validate!
76
+ end
77
+
78
+ def to_s
79
+ @yield
80
+ end
81
+
82
+ protected
83
+
84
+ def initialize_attributes(attributes)
85
+ self.class.attributes.each do |name, options|
86
+ set_instance_variable(name, attributes[name] || (options[:default] && options[:default].dup))
87
+ end
88
+ end
89
+
90
+ def initialize_elements
91
+ self.class.elements.each do |name, options|
92
+ if (plural_name = options[:multiple])
93
+ set_instance_variable(plural_name, [])
94
+ else
95
+ set_instance_variable(name, nil)
96
+ end
97
+ end
98
+ end
99
+
100
+ private
101
+
102
+ def get_instance_variable(name)
103
+ instance_variable_get(:"@#{name}")
104
+ end
105
+
106
+ def set_instance_variable(name, value)
107
+ instance_variable_set(:"@#{name}", value)
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElementalComponents
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace ElementalComponents
6
+
7
+ initializer "elemental_components.asset_paths" do |app|
8
+ app.config.assets.paths << ElementalComponents.components_path if app.config.respond_to?(:assets)
9
+ end
10
+
11
+ initializer "elemental_components.view_helpers" do
12
+ ActiveSupport.on_load :action_controller do
13
+ helper ElementalComponents::ComponentHelper
14
+ end
15
+ end
16
+
17
+ initializer "elemental_components.view_paths" do
18
+ ActiveSupport.on_load :action_controller do
19
+ append_view_path ElementalComponents.components_path
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElementalComponents
4
+ class Railtie < ::Rails::Railtie
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElementalComponents
4
+ VERSION = "1.0.0.rc1"
5
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElementalComponents
4
+ class ComponentGenerator < Rails::Generators::NamedBase
5
+ desc "Generate a component"
6
+ class_option :skip_erb, type: :boolean, default: false
7
+ class_option :skip_css, type: :boolean, default: false
8
+ class_option :skip_js, type: :boolean, default: false
9
+
10
+ source_root File.expand_path("templates", __dir__)
11
+
12
+ def create_component_file
13
+ template "component.rb.erb", "app/components/#{name}_component.rb"
14
+ end
15
+
16
+ def create_erb_file
17
+ return if options["skip_erb"]
18
+
19
+ create_file "app/components/#{name}/_#{filename}.html.erb"
20
+ end
21
+
22
+ def create_css_file
23
+ return if options["skip_css"]
24
+
25
+ create_file "app/components/#{name}/#{filename}.css"
26
+ end
27
+
28
+ def create_js_file
29
+ return if options["skip_js"]
30
+
31
+ create_file "app/components/#{name}/#{filename}.js"
32
+ end
33
+
34
+ private
35
+
36
+ def filename
37
+ name.split("/").last
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,2 @@
1
+ class <%= name.classify %>Component < ElementalComponents::Component
2
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # desc "Explaining what the task does"
4
+ # task :elemental_components do
5
+ # # Task goes here
6
+ # end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: elemental_components
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.rc1
5
+ platform: ruby
6
+ authors:
7
+ - Jens Ljungblad
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-02-01 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: 5.1.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 5.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: appraisal
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.74.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.74.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.4.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.4.0
69
+ description: Simple view components for Rails 5.1+
70
+ email:
71
+ - jens.ljungblad@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - MIT-LICENSE
77
+ - README.md
78
+ - Rakefile
79
+ - app/assets/config/elemental_components_manifest.js
80
+ - app/assets/javascripts/elemental_components.js.erb
81
+ - app/assets/stylesheets/elemental_components.css.erb
82
+ - app/helpers/elemental_components/component_helper.rb
83
+ - config/routes.rb
84
+ - lib/elemental_components.rb
85
+ - lib/elemental_components/component.rb
86
+ - lib/elemental_components/element.rb
87
+ - lib/elemental_components/engine.rb
88
+ - lib/elemental_components/railtie.rb
89
+ - lib/elemental_components/version.rb
90
+ - lib/generators/elemental_components/component_generator.rb
91
+ - lib/generators/elemental_components/templates/component.rb.erb
92
+ - lib/tasks/elemental_components_tasks.rake
93
+ homepage: https://www.github.com/jensljungblad/elemental_components
94
+ licenses:
95
+ - MIT
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">"
109
+ - !ruby/object:Gem::Version
110
+ version: 1.3.1
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.7.6
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: Simple view components for Rails 5.1+
117
+ test_files: []