rails_components 0.0.0 → 0.0.1

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: 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