rails_components 0.0.0 → 0.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b85fde4793b42ad98c8b8e90318431f671f31f2a
4
- data.tar.gz: 9c2b9254764b6ed2105973680a8259eacc7e1048
3
+ metadata.gz: 6f8e93ccb73397896fa9905a032a2134c1c0de2c
4
+ data.tar.gz: af568ee8b46b7e64aec5c6c1491d0984239a3ac8
5
5
  SHA512:
6
- metadata.gz: 20bc74881d692dc5383758061c89d6cf6905e20f42e20ec9c16e52f057137bf467729be30c40d35e50c3a21d19409f426700de4ff262bcb1e886ae156117751b
7
- data.tar.gz: 7aac8b7081ca95612535116dea76506a84d64dac865c94575fc40e8d111bcd443a52bc36624a3a0335237de7f881e938609872d37cf24f873667772d4931023d
6
+ metadata.gz: 4a407dcd0bd60b1a211966988fbf454eb44ec3fa97e4d435c2ca6b43f3331bc624b6cce1dec52ca43d867cbb7e9a6d69f98e3e0be4f23f5bdeb1acbec80595fb
7
+ data.tar.gz: a34cbac6e4c88caebcbf49f46defcba19ba7a97ee57a7858d7e5b6ac4c97f9e443b9eed7484f692afbbee2e8d6a3d25ed90c7ba59bedfabb59235ba15d710975
data/README.md CHANGED
@@ -1,16 +1,17 @@
1
1
  # Rails Components
2
2
 
3
- It allows you to write reusable components in your rails views.
3
+ write reusable components in your rails views. a thin wrapper around `render`
4
+ that makes passing blocks, and html attributes to templates simple as pie.
4
5
 
5
6
  ## Installation
6
7
 
7
- Install the rails components gem by adding it to your Gemfile:
8
+ get the gem
8
9
 
9
10
  ```rb
10
11
  gem 'rails_components'
11
12
  ```
12
13
 
13
- Include RailsComponents in your app/helpers/application_helper.rb:
14
+ and make the `component` helper available in your views
14
15
 
15
16
  ```rb
16
17
  module ApplicationHelper
@@ -18,48 +19,245 @@ module ApplicationHelper
18
19
  end
19
20
  ```
20
21
 
22
+ ## Usage
21
23
 
22
- ## Examples:
24
+ a component is a template
23
25
 
24
- Bootstrap modal, with an ID, in erb:
26
+ ```erb
27
+ <!-- app/views/components/_my_cool_component.html.erb -->
28
+ <div class="my-cool-component">Hello!</div>
29
+ ```
30
+
31
+ a component can yield, and you can pass a block to it
32
+
33
+ ```erb
34
+ <!-- app/views/components/_my_cool_component.html.erb -->
35
+ <div class="my-cool-component"><%= yield %></div>
36
+ ```
25
37
 
26
38
  ```erb
27
- <% content_tag :div, class: ['modal', props[:class]] do %>
28
- <div class="modal fade" tabindex="-1" role="dialog">
29
- <div class="modal-dialog" role="document">
30
- <div class="modal-content">
31
- <%= yield %>
32
- </div><!-- /.modal-content -->
33
- </div><!-- /.modal-dialog -->
34
- </div><!-- /.modal -->
39
+ <!-- in a view -->
40
+ <h1>My website!</h1>
41
+ <%= component 'my_cool_component' do %>
42
+ <p>It's great.</p>
35
43
  <% end %>
36
44
  ```
37
45
 
46
+ or pass it an argument instead of a block, like `link_to` or `content_tag`
47
+
38
48
  ```erb
39
- <div class="modal-header">
40
- <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
41
- <h4 class="modal-title"><%= yield %></h4>
49
+ <!-- in a view -->
50
+ <%= component 'my_cool_component', "It's great." %>
51
+ ```
52
+
53
+ they live in `app/views/components` by default.
54
+
55
+ components have a special method: `props`. `props` is the same as the
56
+ template's local variables, except it includes reserved words.
57
+ This makes it useful for passing html attributes like `class`.
58
+
59
+ ```erb
60
+ <!-- component -->
61
+ <%= content_tag :div, props do %>
62
+ <%= yield %>
63
+ <% end %>
64
+
65
+ <!-- in a view -->
66
+ <%= component 'box', class: "box", data: { foo: 'bar' } do %>
67
+ <p>my box!</p>
68
+ <% end %>
69
+ ```
70
+
71
+ ```html
72
+ <!-- output -->
73
+ <div class="box" data-foo="bar">
74
+ <p>my box!</p>
42
75
  </div>
43
76
  ```
44
77
 
78
+ `props` has a method called `html` which combines it with additional html attributes:
79
+
45
80
  ```erb
46
- <div class="modal-body">
81
+ <!-- component -->
82
+ <%= content_tag :div, props.html(class: "box") do %>
47
83
  <%= yield %>
84
+ <% end %>
85
+
86
+ <!-- in a view -->
87
+ <%= component 'box', class: "big" do %>
88
+ <p>my big box!</p>
89
+ <% end %>
90
+ ```
91
+
92
+ ```html
93
+ <!-- output -->
94
+ <div class="big box">
95
+ <p>my big box!</p>
48
96
  </div>
49
97
  ```
50
98
 
99
+ if you're using haml, it already does this for you, and you can use props directly:
100
+
101
+ ```haml
102
+ / component
103
+ .box{ props }
104
+ = yield
105
+ ```
106
+
107
+
108
+ ## Bigger Examples
109
+
110
+ [Bootstrap modal][bsmodal]:
111
+
51
112
  ```erb
52
- <div class="modal-footer">
113
+ <!-- in a view -->
114
+ <%= component 'modal', id: "important-message", class: "my-fancy-modal" do %>
115
+ <%= component 'modal/header', "Cool" %>
116
+ <%= component 'modal/body' do %>
117
+ <p>
118
+ Some important stuff!
119
+ </p>
120
+ <% end %>
121
+ <%= component 'modal/footer' %>
122
+ <% end %>
123
+ ```
124
+
125
+ ```erb
126
+ <!-- components -->
127
+
128
+ <!-- app/views/components/_modal.html.erb -->
129
+ <%= content_tag :div, props.html(class: 'modal fade', tabindex: '-1', role: 'dialog') do %>
130
+ <div class="modal-dialog" role="document">
131
+ <div class="modal-content">
132
+ <%= yield %>
133
+ </div><!-- /.modal-content -->
134
+ </div><!-- /.modal-dialog -->
135
+ <% end %>
136
+
137
+ <!-- app/views/components/modal/_header.html.erb -->
138
+ <%= component 'modal/header_container' do %>
139
+ <%= component 'close_button', data: { dismiss: "modal" } %>
140
+ <%= component 'modal/title' do %>
141
+ <%= yield %>
142
+ <% end %>
143
+ <% end %>
144
+
145
+ <!-- app/views/components/modal/_header_container.html.erb -->
146
+ <%= content_tag :div, props.html(class: "modal-header") do %>
147
+ <%= yield %>
148
+ <% end %>
149
+
150
+ <!-- app/views/components/modal/_title.html.erb -->
151
+ <%= content_tag props.fetch(:tag, "h4"), props.html(class: "modal-title").except(:tag) do %>
53
152
  <%= yield %>
153
+ <% end %>
154
+
155
+ <!-- app/views/components/_close_button.html.erb -->
156
+ <%= content_tag :button, props.html(type: 'button', class: 'close', :"aria-label" => 'close' ) do %>
157
+ <span aria-hidden="true">&times;</span>
158
+ <% end %>
159
+
160
+ <!-- app/views/components/modal/_body.html.erb -->
161
+ <%= content_tag :div, props.html(class: 'modal-body') do %>
162
+ <%= yield %>
163
+ <% end %>
164
+
165
+ <!-- app/views/components/modal/_footer.html.erb -->
166
+ <%= component 'modal/footer_container', props do %>
167
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
168
+ <%= yield %>
169
+ <% end %>
170
+
171
+ <!-- app/views/components/modal/_footer_container.html.erb -->
172
+ <%= content_tag :div, props.html(class: "modal-footer") do %>
173
+ <%= yield %>
174
+ <% end %>
175
+ ```
176
+
177
+ ```html
178
+ <!-- output -->
179
+ <div id="important-message" class="my-fancy-modal modal fade" tabindex="-1" role="dialog">
180
+ <div class="modal-dialog" role="document">
181
+ <div class="modal-content">
182
+ <div class="modal-header">
183
+ <button data-dismiss="modal" type="button" class="close" aria-label="close">
184
+ <span aria-hidden="true">&times;</span>
185
+ </button>
186
+ <h4 class="modal-title">Cool</h1>
187
+ </div>
188
+ <div class="modal-body">
189
+ <p>Some important stuff!</p>
190
+ </div>
191
+ <div class="modal-footer">
192
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
193
+ </div>
194
+ </div><!-- /.modal-content -->
195
+ </div><!-- /.modal-dialog -->
54
196
  </div>
55
197
  ```
56
198
 
199
+
200
+ [Basscss navigation]
201
+
202
+ ## How this compares to `render`
203
+
204
+ `component` is a wrapper around `render` [under the hood](./lib/rails_components.rb):
205
+
206
+ ```erb
207
+ <%= component 'modal', title: "Example" do %>
208
+ Modal content!
209
+ <% end %>
210
+ ```
211
+
57
212
  ```erb
58
- <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
213
+ <%= render layout: 'component/modal', locals: { title: "Example" } do %>
214
+ Modal content!
215
+ <% end %>
59
216
  ```
60
217
 
218
+ Where it shines is taking arguments instead of blocks
219
+
220
+ ```erb
221
+ <%= component 'modal', 'Modal content!', title: "Example" do %>
222
+ ```
61
223
 
62
- # Goals
224
+ And allowing you to use reserved words, which doesn't work with render
225
+
226
+ ```erb
227
+ <!-- won't work! rails can't make `class` a local variable -->
228
+ <%= render layout: 'component/modal', locals: { class: "fancy-modal" } do %>
229
+ Modal content!
230
+ <% end %>
231
+ ```
232
+
233
+ ```erb
234
+ <!-- works! -->
235
+ <%= component 'modal', 'Modal content!', class: "fancy-modal" do %>
236
+ ```
237
+
238
+ ## What's the future of this project, will it be maintained, etc
239
+
240
+ Hard to say. If you're worried about dependencies, copy it into your project
241
+ as a helper. I would be surprised if `render` gets any big changes.
242
+
243
+ This project serves more as documentation and examples of how to write
244
+ components in a rails app (as opposed to adhoc files in `app/views/shared`.)
245
+
246
+ ## Configuration
247
+
248
+ TODO
249
+
250
+ ## Goals
63
251
 
64
252
  - make it as easy to write reusable components
65
- - feel familiar to existing rails helpers, like `link_to` or `content_tag`
253
+ - feel familiar to existing rails helpers, like `link_to` or `content_tag`
254
+
255
+ ## TODO
256
+
257
+ - tests
258
+ - test configuration
259
+ - figure out how to make vim-rails jump to files (gf) properly
260
+ - point out that mixing erb and haml has issues
261
+
262
+ [bsmodal]: http://v4-alpha.getbootstrap.com/components/modal/
263
+ [bspanel]: http://v4-alpha.getbootstrap.com/components/card/
@@ -0,0 +1,22 @@
1
+ # https://robots.thoughtbot.com/mygem-configure-block
2
+ module RailsComponents
3
+ class << self
4
+ attr_accessor :configuration
5
+ end
6
+
7
+ def self.configuration
8
+ @configuration ||= Configuration.new
9
+ end
10
+
11
+ def self.configure
12
+ yield(configuration) if block_given?
13
+ end
14
+
15
+ class Configuration
16
+ attr_accessor :template_directory
17
+
18
+ def initialize
19
+ @template_directory = 'components'
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,8 @@
1
+ # meant to be mixed into a hash
2
+ module RailsComponents
3
+ module HtmlHelpers
4
+ def html(html_attributes_to_merge = {})
5
+ merge(html_attributes_to_merge) { |key, v1, v2| [v1, v2].flatten(1) }
6
+ end
7
+ end
8
+ end
@@ -1,14 +1,35 @@
1
+ require 'rails_components/html_helpers'
2
+ require 'rails_components/configuration'
3
+
1
4
  module RailsComponents
2
5
  COMPONENT_RESERVED_WORDS = %i(class return super).freeze
3
6
 
4
7
  def component(component_template, text_or_locals_with_block = nil, locals = nil, &block)
8
+ unless RailsComponents.configuration.template_directory.nil?
9
+ component_template = [RailsComponents.configuration.template_directory, component_template].join('/')
10
+ end
11
+
5
12
  if block_given?
6
13
  render({ layout: component_template, locals: component_locals(text_or_locals_with_block) }, &block)
14
+ elsif text_or_locals_with_block.is_a?(Hash) && locals.nil?
15
+ render(layout: component_template, locals: component_locals(text_or_locals_with_block)) {}
7
16
  else
8
17
  render(layout: component_template, locals: component_locals(locals)) { text_or_locals_with_block }
9
18
  end
10
19
  end
11
20
 
21
+ # references:
22
+ # - https://github.com/rails/rails/blob/v5.0.0/actionview/lib/action_view/helpers/tag_helper.rb#L104
23
+ def component_content_tag(props, name, content_or_options_with_block = nil, options = nil, escape = true, &block)
24
+ if block_given?
25
+ content_or_options_with_block = props.merge_html(content_or_options_with_block) if content_or_options_with_block.is_a? Hash
26
+ content_tag(name, content_or_options_with_block, options, escape, &block)
27
+ else
28
+ options = props.merge_html(options)
29
+ content_tag(name, content_or_options_with_block, options, escape, &block)
30
+ end
31
+ end
32
+
12
33
  private
13
34
 
14
35
  # because rails does some weird stuff to make it easy to access the locals in
@@ -20,6 +41,7 @@ module RailsComponents
20
41
  # - https://github.com/rails/rails/blob/master/actionview/lib/action_view/template.rb
21
42
  def component_locals(locals)
22
43
  locals ||= {}
44
+ locals.extend(HtmlHelpers)
23
45
  locals.except(*COMPONENT_RESERVED_WORDS).merge(props: locals)
24
46
  end
25
47
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_components
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Schilling
@@ -20,6 +20,8 @@ files:
20
20
  - LICENSE
21
21
  - README.md
22
22
  - lib/rails_components.rb
23
+ - lib/rails_components/configuration.rb
24
+ - lib/rails_components/html_helpers.rb
23
25
  homepage: https://github.com/schpet/rails_components
24
26
  licenses:
25
27
  - MIT