lotus-helpers 0.1.0 → 0.2.0

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