lotus-helpers 0.1.0 → 0.2.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: 97a8fa23a2691afc98472ad1155025367148d2ca
4
- data.tar.gz: 731d5dca8b9da7d4177ca6e8f51c260e88055f53
3
+ metadata.gz: c48523257c329e031af02471ad83b63a56290bca
4
+ data.tar.gz: ec1dba8d370b280d0fb14992b2955c916e9c96da
5
5
  SHA512:
6
- metadata.gz: 7298684737de0d27f28beaf7d1ba969ee37f0cc7de543c0e81d6063d2b915bf39a12e1794ad7d479956ba6c8854d49b439b4c4cab614ae1e1f959de5fca11c61
7
- data.tar.gz: 142c218a581680c85c7d6f207988d787436d85467fdacfd9a0a7d54a29c74d3adb77960fabb5a1f0a18d030f25f26e4bbe2058dda57b184276a89877fc5dca48
6
+ metadata.gz: ee14aad1c7167c3dd942f22b59a15776c3ed6381d8eef769322b4f377716e62fe07fbe0b2666f7d1e2ce0ab0ad637560e6dbcc5649dd3515cdad60b80d2dacd0
7
+ data.tar.gz: 9bf9dcaf0da1206f54d11188dc36d0ebc876d76d80c9c1a1498f177560096c66d79c1725255ee1311a349bc183390a4f209270c303be554c97743edca81b738a
data/README.md CHANGED
@@ -92,6 +92,142 @@ Output:
92
92
  </aside>
93
93
  ```
94
94
 
95
+ ### Form Helper
96
+
97
+ Form generator for HTML5 (`#form_for`)
98
+
99
+ #### Template Usage
100
+
101
+ Template:
102
+
103
+ ```erb
104
+ <%=
105
+ form_for :book, routes.books_path do
106
+ text_field :title
107
+
108
+ submit 'Create'
109
+ end
110
+ %>
111
+ ```
112
+
113
+ Output:
114
+
115
+ ```html
116
+ <form action="/books" method="POST" accept-charset="utf-8" id="book-form">
117
+ <input type="text" name="book[title]" id="book-id" value="">
118
+ <button type="submit">Create</button>
119
+ </form>
120
+ ```
121
+
122
+ #### View Usage
123
+
124
+ View:
125
+
126
+ ```ruby
127
+ module Books
128
+ class New
129
+ include Lotus::Helpers
130
+
131
+ def form
132
+ form_for :book, routes.books_path do
133
+ text_field :title
134
+
135
+ submit 'Create'
136
+ end
137
+ end
138
+ end
139
+ end
140
+ ```
141
+
142
+ Template:
143
+
144
+ ```erb
145
+ <%= form %>
146
+ ```
147
+
148
+ Output:
149
+
150
+ ```html
151
+ <form action="/books" method="POST" accept-charset="utf-8" id="book-form">
152
+ <input type="text" name="book[title]" id="book-id" value="">
153
+ <button type="submit">Create</button>
154
+ </form>
155
+ ```
156
+
157
+ #### Reuse Code
158
+
159
+ Views:
160
+
161
+ ```ruby
162
+ module Books
163
+ class New
164
+ include Lotus::Helpers
165
+
166
+ def form
167
+ Form.new(:book, routes.books_path)
168
+ end
169
+
170
+ def submit_label
171
+ 'Create'
172
+ end
173
+ end
174
+
175
+ class Edit
176
+ include Lotus::Helpers
177
+
178
+ def form
179
+ Form.new(:book, routes.book_path(id: book.id), {book: book}, {method: :patch})
180
+ end
181
+
182
+ def submit_label
183
+ 'Update'
184
+ end
185
+ end
186
+ end
187
+ ```
188
+
189
+ Templates:
190
+
191
+ ```erb
192
+ # books/new.html.erb
193
+ <%= render partial: 'books/form' %>
194
+ ```
195
+
196
+ ```erb
197
+ # books/edit.html.erb
198
+ <%= render partial: 'books/form' %>
199
+ ```
200
+
201
+ ```erb
202
+ # books/_form.html.erb
203
+ <%=
204
+ form_for form, class: 'form-horizontal' do
205
+ text_field :title
206
+
207
+ submit submit_label
208
+ end
209
+ %>
210
+ ```
211
+
212
+ Output for new:
213
+
214
+ ```html
215
+ <form action="/books" method="POST" accept-charset="utf-8" id="book-form">
216
+ <input type="text" name="book[title]" id="book-id" value="">
217
+ <button type="submit">Create</button>
218
+ </form>
219
+ ```
220
+
221
+ Output for edit:
222
+
223
+ ```html
224
+ <form action="/books/23" method="POST" accept-charset="utf-8" id="book-form">
225
+ <input type="hidden" name="_method" value="PATCH">
226
+ <input type="text" name="book[title]" id="book-id" value="TDD">
227
+ <button type="submit">Update</button>
228
+ </form>
229
+ ```
230
+
95
231
  ### Escape helper
96
232
 
97
233
  HTML (`#h`), HTML attribute (`#ha`) and URL (`#hu`) escape helpers.
@@ -128,7 +264,7 @@ Output:
128
264
  <code>puts "Hello, World!"</code>
129
265
  ```
130
266
 
131
- ### Routing helper
267
+ ### Routing Helper
132
268
 
133
269
  Lotus and Lotus::Router integration (`#routes`).
134
270
 
@@ -158,6 +294,36 @@ Output:
158
294
  <a href="/">Home</a>
159
295
  ```
160
296
 
297
+ ### Number Formatting Helper
298
+
299
+ Format numbers (`#format_number`).
300
+
301
+ View:
302
+
303
+ ```ruby
304
+ module Home
305
+ class Index
306
+ include Lotus::Helpers
307
+
308
+ def visitors_count
309
+ format_number '1000'
310
+ end
311
+ end
312
+ end
313
+ ```
314
+
315
+ Template:
316
+
317
+ ```erb
318
+ <p><%= visitors_count %></p>
319
+ ```
320
+
321
+ Output:
322
+
323
+ ```html
324
+ <p>1,000</p>
325
+ ```
326
+
161
327
  ## Philosophy
162
328
 
163
329
  All the Lotus helpers are modules to include.
@@ -2,6 +2,9 @@ require 'lotus/helpers/version'
2
2
  require 'lotus/helpers/html_helper'
3
3
  require 'lotus/helpers/escape_helper'
4
4
  require 'lotus/helpers/routing_helper'
5
+ require 'lotus/helpers/link_to_helper'
6
+ require 'lotus/helpers/form_helper'
7
+ require 'lotus/helpers/number_formatting_helper'
5
8
 
6
9
  module Lotus
7
10
  # View helpers for Ruby applications
@@ -21,6 +24,9 @@ module Lotus
21
24
  include Lotus::Helpers::HtmlHelper
22
25
  include Lotus::Helpers::EscapeHelper
23
26
  include Lotus::Helpers::RoutingHelper
27
+ include Lotus::Helpers::LinkToHelper
28
+ include Lotus::Helpers::FormHelper
29
+ include Lotus::Helpers::NumberFormattingHelper
24
30
  end
25
31
  end
26
32
  end
@@ -0,0 +1,423 @@
1
+ require 'lotus/helpers/form_helper/form_builder'
2
+
3
+ module Lotus
4
+ module Helpers
5
+ # Form builder
6
+ #
7
+ # By including <tt>Lotus::Helpers::FormHelper</tt> it will inject one public method: <tt>form_for</tt>.
8
+ # This is a HTML5 form builder.
9
+ #
10
+ # To understand the general HTML5 builder syntax of this framework, please
11
+ # consider to have a look at <tt>Lotus::Helpers::HtmlHelper</tt> documentation.
12
+ #
13
+ # This builder is independent from any template engine.
14
+ # This was hard to achieve without a compromise: the form helper should be
15
+ # used in one output block in a template or as a method in a view (see the examples below).
16
+ #
17
+ # Features:
18
+ #
19
+ # * Support for complex markup without the need of concatenation
20
+ # * Auto closing HTML5 tags
21
+ # * Support for view local variables
22
+ # * Method override support (PUT/PATCH/DELETE HTTP verbs aren't understood by browsers)
23
+ # * Automatic generation of HTML attributes for inputs: <tt>id</tt>, <tt>name</tt>, <tt>value</tt>
24
+ # * Allow to override HTML attributes
25
+ # * Extract values from request params and fill <tt>value</tt> attributes
26
+ # * Automatic selection of current value for radio button and select inputs
27
+ # * Infinite nested fields
28
+ #
29
+ # Supported tags and inputs:
30
+ #
31
+ # * <tt>color_field</tt>
32
+ # * <tt>date_field</tt>
33
+ # * <tt>datetime_field</tt>
34
+ # * <tt>datetime_local_field</tt>
35
+ # * <tt>email_field</tt>
36
+ # * <tt>hidden_field</tt>
37
+ # * <tt>file_field</tt>
38
+ # * <tt>fields_for</tt>
39
+ # * <tt>form_for</tt>
40
+ # * <tt>label</tt>
41
+ # * <tt>text_field</tt>
42
+ # * <tt>password_field</tt>
43
+ # * <tt>radio_button</tt>
44
+ # * <tt>select</tt>
45
+ # * <tt>submit</tt>
46
+ #
47
+ # @since x.x.x
48
+ #
49
+ # @see Lotus::Helpers::FormHelper#form_for
50
+ # @see Lotus::Helpers::HtmlHelper
51
+ #
52
+ # @example One output block (template)
53
+ # <%=
54
+ # form_for :book, routes.books_path do
55
+ # text_field :title
56
+ #
57
+ # submit 'Create'
58
+ # end
59
+ # %>
60
+ #
61
+ # @example Method (view)
62
+ # require 'lotus/helpers'
63
+ #
64
+ # class MyView
65
+ # include Lotus::Helpers::FormHelper
66
+ #
67
+ # def my_form
68
+ # form_for :book, routes.books_path do
69
+ # text_field :title
70
+ # end
71
+ # end
72
+ # end
73
+ #
74
+ # # Corresponding template:
75
+ # #
76
+ # # <%= my_form %>
77
+ module FormHelper
78
+ # Default HTTP method for form
79
+ #
80
+ # @since x.x.x
81
+ # @api private
82
+ DEFAULT_METHOD = 'POST'.freeze
83
+
84
+ # Default charset
85
+ #
86
+ # @since x.x.x
87
+ # @api private
88
+ DEFAULT_CHARSET = 'utf-8'.freeze
89
+
90
+ # CSRF Token session key
91
+ #
92
+ # This key is shared with <tt>lotusrb</tt>, <tt>lotus-controller</tt>.
93
+ #
94
+ # @since x.x.x
95
+ # @api private
96
+ CSRF_TOKEN = :_csrf_token
97
+
98
+ # Form object
99
+ #
100
+ # @since x.x.x
101
+ class Form
102
+ # @return [Symbol] the form name
103
+ #
104
+ # @since x.x.x
105
+ # @api private
106
+ attr_reader :name
107
+
108
+ # @return [String] the form action
109
+ #
110
+ # @since x.x.x
111
+ # @api private
112
+ attr_reader :url
113
+
114
+ # @return [::Hash] the form values
115
+ #
116
+ # @since x.x.x
117
+ # @api private
118
+ attr_reader :values
119
+
120
+ # Initialize a form
121
+ #
122
+ # It accepts a set of values that are used in combination with request
123
+ # params to autofill <tt>value</tt> attributes for fields.
124
+ #
125
+ # The keys of this Hash, MUST correspond to the structure of the (nested)
126
+ # fields of the form.
127
+ #
128
+ # For a given input where the <tt>name</tt> is `book[title]`, Lotus will
129
+ # look for `:book` key in values.
130
+ #
131
+ # If the current params have the same key, it will be PREFERRED over the
132
+ # given values.
133
+ #
134
+ # For instance, if <tt>params.get('book.title')</tt> equals to
135
+ # <tt>"TDD"</tt> while <tt>values[:book].title</tt> returns
136
+ # <tt>"No test"</tt>, the first will win.
137
+ #
138
+ # @param name [Symbol] the name of the form
139
+ # @param url [String] the action of the form
140
+ # @param values [Hash,NilClass] a Hash of values to be used to autofill
141
+ # <tt>value</tt> attributes for fields
142
+ # @param attributes [Hash,NilClass] a Hash of attributes to pass to the
143
+ # <tt>form</tt> tag
144
+ #
145
+ # @since x.x.x
146
+ #
147
+ # @example Pass A Value
148
+ # # Given the following view
149
+ #
150
+ # module Web::Views::Deliveries
151
+ # class Edit
152
+ # include Web::View
153
+ #
154
+ # def form
155
+ # Form.new(:delivery, routes.delivery_path(id: delivery.id),
156
+ # {delivery: delivery, customer: customer},
157
+ # {method: :patch})
158
+ # end
159
+ # end
160
+ # end
161
+ #
162
+ # # And the corresponding template:
163
+ #
164
+ # <%=
165
+ # form_for form do
166
+ # date_field :delivered_on
167
+ #
168
+ # fields_for :customer do
169
+ # text_field :name
170
+ #
171
+ # fields_for :address do
172
+ # # ...
173
+ # text_field :city
174
+ # end
175
+ # end
176
+ #
177
+ # submit 'Update'
178
+ # end
179
+ # %>
180
+ #
181
+ # # It will render:
182
+ # #
183
+ # # <form action="/deliveries/1" method="POST" accept-charset="utf-8">
184
+ # # <input type="hidden" name="_method" value="PATCH">
185
+ # #
186
+ # # # Value taken from delivery.delivered_on
187
+ # # <input type="date" name="delivery[delivered_on]" id="delivery-delivered-on" value="2015-05-27">
188
+ # #
189
+ # # # Value taken from customer.name
190
+ # # <input type="text" name="delivery[customer][name]" id="delivery-customer-name" value="Luca">
191
+ # #
192
+ # # # Value taken from customer.address.city
193
+ # # <input type="text" name="delivery[customer][address][city]" id="delivery-customer-address-city" value="Rome">
194
+ # #
195
+ # # <button type="submit">Update</button>
196
+ # # </form>
197
+ def initialize(name, url, values = {}, attributes = {})
198
+ @name = name
199
+ @url = url
200
+ @values = values
201
+ @attributes = attributes || {}
202
+ end
203
+
204
+ # Return the method specified by the given attributes or fall back to
205
+ # the default value
206
+ #
207
+ # @return [String] the method for the action
208
+ #
209
+ # @since x.x.x
210
+ # @api private
211
+ #
212
+ # @see Lotus::Helpers::FormHelper::DEFAULT_METHOD
213
+ def verb
214
+ @attributes.fetch(:method, DEFAULT_METHOD)
215
+ end
216
+ end
217
+
218
+ # Instantiate a HTML5 form builder
219
+ #
220
+ # @overload form_for(name, url, options, &blk)
221
+ # Use inline values
222
+ # @param name [Symbol] the toplevel name of the form, it's used to generate
223
+ # input names, ids, and to lookup params to fill values.
224
+ # @param url [String] the form action URL
225
+ # @param options [Hash] HTML attributes to pass to the form tag and form values
226
+ # @option options [Hash] :values An optional payload of objects to pass
227
+ # @param blk [Proc] A block that describes the contents of the form
228
+ #
229
+ # @overload form_for(form, attributes, &blk)
230
+ # Use Form
231
+ # @param form [Lotus::Helpers::FormHelper::Form] a form object
232
+ # @param attributes [Hash] HTML attributes to pass to the form tag and form values
233
+ # @param blk [Proc] A block that describes the contents of the form
234
+ #
235
+ # @return [Lotus::Helpers::FormHelper::FormBuilder] the form builder
236
+ #
237
+ # @since x.x.x
238
+ #
239
+ # @see Lotus::Helpers::FormHelper
240
+ # @see Lotus::Helpers::FormHelper::Form
241
+ # @see Lotus::Helpers::FormHelper::FormBuilder
242
+ #
243
+ # @example Inline Values In Template
244
+ # <%=
245
+ # form_for :book, routes.books_path, class: 'form-horizontal' do
246
+ # div do
247
+ # label :title
248
+ # text_field :title, class: 'form-control'
249
+ # end
250
+ #
251
+ # submit 'Create'
252
+ # end
253
+ # %>
254
+ #
255
+ # Output:
256
+ # # <form action="/books" method="POST" accept-charset="utf-8" id="book-form" class="form-horizontal">
257
+ # # <div>
258
+ # # <label for="book-title">Title</label>
259
+ # # <input type="text" name="book[title]" id="book-title" value="Test Driven Development">
260
+ # # </div>
261
+ # #
262
+ # # <button type="submit">Create</button>
263
+ # # </form>
264
+ #
265
+ #
266
+ #
267
+ # @example Use In A View
268
+ #
269
+ # module Web::Views::Books
270
+ # class New
271
+ #
272
+ # def form
273
+ # form_for :book, routes.books_path, class: 'form-horizontal' do
274
+ # div do
275
+ # label :title
276
+ # text_field :title, class: 'form-control'
277
+ # end
278
+ #
279
+ # submit 'Create'
280
+ # end
281
+ # end
282
+ # end
283
+ #
284
+ # <%= form %>
285
+ #
286
+ # Output:
287
+ # # <form action="/books" method="POST" accept-charset="utf-8" id="book-form" class="form-horizontal">
288
+ # # <div>
289
+ # # <label for="book-title">Title</label>
290
+ # # <input type="text" name="book[title]" id="book-title" value="Test Driven Development">
291
+ # # </div>
292
+ # #
293
+ # # <button type="submit">Create</button>
294
+ # # </form>
295
+ #
296
+ # @example Share Code Between Views
297
+ #
298
+ # # Given the following views to create and update a resource
299
+ # module Web::Views::Books
300
+ # class New
301
+ # include Web::View
302
+ #
303
+ # def form
304
+ # Form.new(:book, routes.books_path)
305
+ # end
306
+ #
307
+ # def submit_label
308
+ # 'Create'
309
+ # end
310
+ # end
311
+ #
312
+ # class Edit
313
+ # include Web::View
314
+ #
315
+ # def form
316
+ # Form.new(:book, routes.book_path(id: book.id),
317
+ # {book: book}, {method: :patch})
318
+ # end
319
+ #
320
+ # def submit_label
321
+ # 'Update'
322
+ # end
323
+ # end
324
+ # end
325
+ #
326
+ # # The respective templates can be identical:
327
+ #
328
+ # ## books/new.html.erb
329
+ # <%= render partial: 'books/form' %>
330
+ #
331
+ # ## books/edit.html.erb
332
+ # <%= render partial: 'books/form' %>
333
+ #
334
+ # # While the partial can have the following markup:
335
+ #
336
+ # ## books/_form.html.erb
337
+ # <%=
338
+ # form_for form, class: 'form-horizontal' do
339
+ # div do
340
+ # label :title
341
+ # text_field :title, class: 'form-control'
342
+ # end
343
+ #
344
+ # submit submit_label
345
+ # end
346
+ # %>
347
+ #
348
+ # Output:
349
+ # # <form action="/books" method="POST" accept-charset="utf-8" id="book-form" class="form-horizontal">
350
+ # # <div>
351
+ # # <label for="book-title">Title</label>
352
+ # # <input type="text" name="book[title]" id="book-title" value="Test Driven Development">
353
+ # # </div>
354
+ # #
355
+ # # <button type="submit">Create</button>
356
+ # # </form>
357
+ #
358
+ # @example Method override
359
+ # <%=
360
+ # form_for :book, routes.book_path(id: book.id), method: :put do
361
+ # text_field :title
362
+ #
363
+ # submit 'Update'
364
+ # end
365
+ # %>
366
+ #
367
+ # Output:
368
+ # # <form action="/books/23" accept-charset="utf-8" id="book-form" method="POST">
369
+ # # <input type="hidden" name="_method" value="PUT">
370
+ # # <input type="text" name="book[title]" id="book-title" value="Test Driven Development">
371
+ # #
372
+ # # <button type="submit">Update</button>
373
+ # # </form>
374
+ #
375
+ # @example Nested fields
376
+ # <%=
377
+ # form_for :delivery, routes.deliveries_path do
378
+ # text_field :customer_name
379
+ #
380
+ # fields_for :address do
381
+ # text_field :city
382
+ # end
383
+ #
384
+ # submit 'Create'
385
+ # end
386
+ # %>
387
+ #
388
+ # Output:
389
+ # # <form action="/deliveries" accept-charset="utf-8" id="delivery-form" method="POST">
390
+ # # <input type="text" name="delivery[customer_name]" id="delivery-customer-name" value="">
391
+ # # <input type="text" name="delivery[address][city]" id="delivery-address-city" value="">
392
+ # #
393
+ # # <button type="submit">Create</button>
394
+ # # </form>
395
+ def form_for(name, url, options = {}, &blk)
396
+ form = if name.is_a?(Form)
397
+ options = url
398
+ name
399
+ else
400
+ Form.new(name, url, options.delete(:values))
401
+ end
402
+
403
+ attributes = { action: form.url, method: form.verb, :'accept-charset' => DEFAULT_CHARSET, id: "#{ form.name }-form" }.merge(options)
404
+ FormBuilder.new(form, attributes, self, &blk)
405
+ end
406
+
407
+ # Returns CSRF Protection Token stored in session.
408
+ #
409
+ # It returns <tt>nil</tt> if sessions aren't enabled or the value is missing.
410
+ #
411
+ # @return [String,NilClass] token, if present
412
+ #
413
+ # @since x.x.x
414
+ def csrf_token
415
+ if defined?(session)
416
+ session[CSRF_TOKEN]
417
+ elsif defined?(locals) && locals[:session]
418
+ locals[:session][CSRF_TOKEN]
419
+ end
420
+ end
421
+ end
422
+ end
423
+ end