hanami-helpers 0.0.0 → 0.3.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.
@@ -4,20 +4,24 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'hanami/helpers/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "hanami-helpers"
7
+ spec.name = 'hanami-helpers'
8
8
  spec.version = Hanami::Helpers::VERSION
9
- spec.authors = ["Luca Guidi"]
10
- spec.email = ["me@lucaguidi.com"]
9
+ spec.authors = ['Luca Guidi', 'Trung Lê', 'Alfonso Uceda']
10
+ spec.email = ['me@lucaguidi.com', 'trung.le@ruby-journal.com', 'uceda73@gmail.com']
11
+ spec.summary = %q{Hanami helpers}
12
+ spec.description = %q{View helpers for Ruby applications}
13
+ spec.homepage = 'http://hanamirb.org'
14
+ spec.license = 'MIT'
11
15
 
12
- spec.summary = %q{The web, with simplicity}
13
- spec.description = %q{Hanami is a web framework for Ruby}
14
- spec.homepage = "http://hanamirb.org"
16
+ spec.files = `git ls-files -- lib/* CHANGELOG.md LICENSE.md README.md hanami-helpers.gemspec`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+ spec.required_ruby_version = '>= 2.0.0'
15
21
 
16
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
- spec.bindir = "exe"
18
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
- spec.require_paths = ["lib"]
22
+ spec.add_dependency 'hanami-utils', '~> 0.7'
20
23
 
21
- spec.add_development_dependency "bundler", "~> 1.11"
22
- spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency 'bundler', '~> 1.6'
25
+ spec.add_development_dependency 'rake', '~> 10.0'
26
+ spec.add_development_dependency 'minitest', '~> 5.5'
23
27
  end
@@ -0,0 +1 @@
1
+ require 'hanami/helpers'
@@ -1,7 +1,33 @@
1
- require "hanami/helpers/version"
1
+ require 'hanami/helpers/version'
2
+ require 'hanami/helpers/html_helper'
3
+ require 'hanami/helpers/escape_helper'
4
+ require 'hanami/helpers/routing_helper'
5
+ require 'hanami/helpers/link_to_helper'
6
+ require 'hanami/helpers/form_helper'
7
+ require 'hanami/helpers/number_formatting_helper'
2
8
 
3
9
  module Hanami
10
+ # View helpers for Ruby applications
11
+ #
12
+ # @since 0.1.0
4
13
  module Helpers
5
- # Your code goes here...
14
+ # Override for Module.included
15
+ #
16
+ # It injects all the available helpers.
17
+ #
18
+ # @since 0.1.0
19
+ # @api private
20
+ #
21
+ # @see http://www.ruby-doc.org/core/Module.html#method-i-included
22
+ def self.included(base)
23
+ base.class_eval do
24
+ include Hanami::Helpers::HtmlHelper
25
+ include Hanami::Helpers::EscapeHelper
26
+ include Hanami::Helpers::RoutingHelper
27
+ include Hanami::Helpers::LinkToHelper
28
+ include Hanami::Helpers::FormHelper
29
+ include Hanami::Helpers::NumberFormattingHelper
30
+ end
31
+ end
6
32
  end
7
33
  end
@@ -0,0 +1,271 @@
1
+ require 'hanami/utils/escape'
2
+
3
+ module Hanami
4
+ module Helpers
5
+ # Escape helpers
6
+ #
7
+ # You can include this module inside your view and
8
+ # the view will have access all methods.
9
+ #
10
+ # By including <tt>Hanami::Helpers::EscapeHelper</tt> it will inject private
11
+ # methods as markup escape utilities.
12
+ #
13
+ # @since 0.1.0
14
+ module EscapeHelper
15
+ private
16
+ # Escape the given HTML tag content.
17
+ #
18
+ # This should be used only for untrusted contents: user input.
19
+ #
20
+ # This should be used only for tag contents.
21
+ # To escape tag attributes please use <tt>Hanami::Helpers::EscapeHelper#escape_html_attribute</tt>.
22
+ #
23
+ # @param input [String] the input
24
+ #
25
+ # @return [String] the escaped string
26
+ #
27
+ # @since 0.1.0
28
+ #
29
+ # @see Hanami::Helpers::EscapeHelper#escape_html_attribute
30
+ #
31
+ # @example Basic usage
32
+ # require 'hanami/helpers/escape_helper'
33
+ #
34
+ # class MyView
35
+ # include Hanami::Helpers::EscapeHelper
36
+ #
37
+ # def good_content
38
+ # h "hello"
39
+ # end
40
+ #
41
+ # def evil_content
42
+ # h "<script>alert('xss')</script>"
43
+ # end
44
+ # end
45
+ #
46
+ # view = MyView.new
47
+ #
48
+ # view.good_content
49
+ # # => "hello"
50
+ #
51
+ # view.evil_content
52
+ # # => "&lt;script&gt;alert(&apos;xss&apos;)&lt;&#x2F;script&gt;"
53
+ #
54
+ # @example With HTML builder
55
+ # #
56
+ # # CONTENTS ARE AUTOMATICALLY ESCAPED
57
+ # #
58
+ # require 'hanami/helpers'
59
+ #
60
+ # class MyView
61
+ # include Hanami::Helpers
62
+ #
63
+ # def evil_content
64
+ # html.div do
65
+ # "<script>alert('xss')</script>"
66
+ # end
67
+ # end
68
+ # end
69
+ #
70
+ # view = MyView.new
71
+ # view.evil_content
72
+ # # => "<div>\n&lt;script&gt;alert(&apos;xss&apos;)&lt;&#x2F;script&gt;</div>"
73
+ def escape_html(input)
74
+ Utils::Escape.html(input)
75
+ end
76
+
77
+ # @since 0.1.0
78
+ alias_method :h, :escape_html
79
+
80
+ # Escape the given HTML tag attribute.
81
+ #
82
+ # This MUST be used for escaping HTML tag attributes.
83
+ #
84
+ # This should be used only for untrusted contents: user input.
85
+ #
86
+ # This can also be used to escape tag contents, but it's slower.
87
+ # For this purpose use <tt>Hanami::Helpers::EscapeHelper#escape_html</tt>.
88
+ #
89
+ # @param input [String] the input
90
+ #
91
+ # @return [String] the escaped string
92
+ #
93
+ # @since 0.1.0
94
+ #
95
+ # @see Hanami::Helpers::EscapeHelper#escape_html
96
+ #
97
+ # @example Basic usage
98
+ # require 'hanami/helpers/escape_helper'
99
+ #
100
+ # class MyView
101
+ # include Hanami::Helpers::EscapeHelper
102
+ #
103
+ # def good_attribute
104
+ # attribute = "small"
105
+ #
106
+ # %(<span class="#{ ha(attribute) }">hello</span>
107
+ # end
108
+ #
109
+ # def evil_attribute
110
+ # attribute = %(" onclick="javascript:alert('xss')" id=")
111
+ #
112
+ # %(<span class="#{ ha(attribute) }">hello</span>
113
+ # end
114
+ # end
115
+ #
116
+ # view = MyView.new
117
+ #
118
+ # view.good_attribute
119
+ # # => %(<span class="small">hello</span>)
120
+ #
121
+ # view.evil_attribute
122
+ # # => %(<span class="&quot;&#x20;onclick&#x3d;&quot;javascript&#x3a;alert&#x28;&#x27;xss&#x27;&#x29;&quot;&#x20;id&#x3d;&quot;">hello</span>
123
+ #
124
+ # @example With HTML builder
125
+ # #
126
+ # # ATTRIBUTES AREN'T AUTOMATICALLY ESCAPED
127
+ # #
128
+ # require 'hanami/helpers'
129
+ #
130
+ # class MyView
131
+ # include Hanami::Helpers
132
+ #
133
+ # def evil_attribute
134
+ # user_input_attribute = %(" onclick="javascript:alert('xss')" id=")
135
+ #
136
+ # html.span id: 'greet', class: ha(user_input_attribute) do
137
+ # "hello"
138
+ # end
139
+ # end
140
+ # end
141
+ #
142
+ # view = MyView.new
143
+ # view.evil_attribute
144
+ # # => %(<span class="&quot;&#x20;onclick&#x3d;&quot;javascript&#x3a;alert&#x28;&#x27;xss&#x27;&#x29;&quot;&#x20;id&#x3d;&quot;">hello</span>
145
+ def escape_html_attribute(input)
146
+ Utils::Escape.html_attribute(input)
147
+ end
148
+
149
+ # @since 0.1.0
150
+ alias_method :ha, :escape_html_attribute
151
+
152
+ # Escape an URL to be used in HTML attributes
153
+ #
154
+ # This allows only URLs with whitelisted schemes to pass the filter.
155
+ # Everything else is stripped.
156
+ #
157
+ # Default schemes are:
158
+ #
159
+ # * http
160
+ # * https
161
+ # * mailto
162
+ #
163
+ # If you want to allow a different set of schemes, you should pass it as
164
+ # second argument.
165
+ #
166
+ # This should be used only for untrusted contents: user input.
167
+ #
168
+ # @param input [String] the input
169
+ # @param schemes [Array<String>] an optional array of whitelisted schemes
170
+ #
171
+ # @return [String] the escaped string
172
+ #
173
+ # @since 0.1.0
174
+ #
175
+ # @see Hanami::Utils::Escape.url
176
+ # @see Hanami::Utils::Escape::DEFAULT_URL_SCHEMES
177
+ #
178
+ # @example Basic usage
179
+ # require 'hanami/helpers/escape_helper'
180
+ #
181
+ # class MyView
182
+ # include Hanami::Helpers::EscapeHelper
183
+ #
184
+ # def good_url
185
+ # url = "http://hanamirb.org"
186
+ #
187
+ # %(<a href="#{ hu(url) }">Hanami</a>
188
+ # end
189
+ #
190
+ # def evil_url
191
+ # url = "javascript:alert('xss')"
192
+ #
193
+ # %(<a href="#{ hu(url) }">Evil</a>
194
+ # end
195
+ # end
196
+ #
197
+ # view = MyView.new
198
+ #
199
+ # view.good_url
200
+ # # => %(<a href="http://hanamirb.org">Hanami</a>)
201
+ #
202
+ # view.evil_url
203
+ # # => %(<a href="">Evil</a>)
204
+ #
205
+ # @example Custom schemes
206
+ # require 'hanami/helpers/escape_helper'
207
+ #
208
+ # class MyView
209
+ # include Hanami::Helpers::EscapeHelper
210
+ #
211
+ # def ftp_link
212
+ # schemes = ['ftp', 'ftps']
213
+ # url = 'ftps://ftp.example.org'
214
+ #
215
+ # %(<a href="#{ hu(url, schemes) }">FTP</a>
216
+ # end
217
+ # end
218
+ #
219
+ # view = MyView.new
220
+ #
221
+ # view.ftp_link
222
+ # # => %(<a href="ftps://ftp.example.org">FTP</a>)
223
+ def escape_url(input, schemes = Utils::Escape::DEFAULT_URL_SCHEMES)
224
+ Utils::Escape.url(input, schemes)
225
+ end
226
+
227
+ # @since 0.1.0
228
+ alias_method :hu, :escape_url
229
+
230
+ # Bypass escape.
231
+ #
232
+ # Please notice that this can be really dangerous.
233
+ # Use at your own peril.
234
+ #
235
+ # @param input [String] the input
236
+ #
237
+ # @return [Hanami::Utils::Escape::SafeString] the string marked as safe string
238
+ #
239
+ # @since 0.1.0
240
+ #
241
+ # @example
242
+ # require 'hanami/helpers/escape_helper'
243
+ #
244
+ # class MyView
245
+ # include Hanami::Helpers::EscapeHelper
246
+ #
247
+ # def good_content
248
+ # raw "<p>hello</p>"
249
+ # end
250
+ #
251
+ # def evil_content
252
+ # raw "<script>alert('xss')</script>"
253
+ # end
254
+ # end
255
+ #
256
+ # view = MyView.new
257
+ #
258
+ # view.good_content
259
+ # # => "<p>hello</p>"
260
+ #
261
+ # #
262
+ # # !!! WE HAVE OPENED OUR APPLICATION TO AN XSS ATTACK !!!
263
+ # #
264
+ # view.evil_content
265
+ # # => "<script>alert('xss')</script>"
266
+ def raw(input)
267
+ Utils::Escape::SafeString.new(input)
268
+ end
269
+ end
270
+ end
271
+ end
@@ -0,0 +1,424 @@
1
+ require 'hanami/helpers/form_helper/form_builder'
2
+
3
+ module Hanami
4
+ module Helpers
5
+ # Form builder
6
+ #
7
+ # By including <tt>Hanami::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>Hanami::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_area</tt>
42
+ # * <tt>text_field</tt>
43
+ # * <tt>password_field</tt>
44
+ # * <tt>radio_button</tt>
45
+ # * <tt>select</tt>
46
+ # * <tt>submit</tt>
47
+ #
48
+ # @since 0.2.0
49
+ #
50
+ # @see Hanami::Helpers::FormHelper#form_for
51
+ # @see Hanami::Helpers::HtmlHelper
52
+ #
53
+ # @example One output block (template)
54
+ # <%=
55
+ # form_for :book, routes.books_path do
56
+ # text_field :title
57
+ #
58
+ # submit 'Create'
59
+ # end
60
+ # %>
61
+ #
62
+ # @example Method (view)
63
+ # require 'hanami/helpers'
64
+ #
65
+ # class MyView
66
+ # include Hanami::Helpers::FormHelper
67
+ #
68
+ # def my_form
69
+ # form_for :book, routes.books_path do
70
+ # text_field :title
71
+ # end
72
+ # end
73
+ # end
74
+ #
75
+ # # Corresponding template:
76
+ # #
77
+ # # <%= my_form %>
78
+ module FormHelper
79
+ # Default HTTP method for form
80
+ #
81
+ # @since 0.2.0
82
+ # @api private
83
+ DEFAULT_METHOD = 'POST'.freeze
84
+
85
+ # Default charset
86
+ #
87
+ # @since 0.2.0
88
+ # @api private
89
+ DEFAULT_CHARSET = 'utf-8'.freeze
90
+
91
+ # CSRF Token session key
92
+ #
93
+ # This key is shared with <tt>hanamirb</tt>, <tt>hanami-controller</tt>.
94
+ #
95
+ # @since 0.2.0
96
+ # @api private
97
+ CSRF_TOKEN = :_csrf_token
98
+
99
+ # Form object
100
+ #
101
+ # @since 0.2.0
102
+ class Form
103
+ # @return [Symbol] the form name
104
+ #
105
+ # @since 0.2.0
106
+ # @api private
107
+ attr_reader :name
108
+
109
+ # @return [String] the form action
110
+ #
111
+ # @since 0.2.0
112
+ # @api private
113
+ attr_reader :url
114
+
115
+ # @return [::Hash] the form values
116
+ #
117
+ # @since 0.2.0
118
+ # @api private
119
+ attr_reader :values
120
+
121
+ # Initialize a form
122
+ #
123
+ # It accepts a set of values that are used in combination with request
124
+ # params to autofill <tt>value</tt> attributes for fields.
125
+ #
126
+ # The keys of this Hash, MUST correspond to the structure of the (nested)
127
+ # fields of the form.
128
+ #
129
+ # For a given input where the <tt>name</tt> is `book[title]`, Hanami will
130
+ # look for `:book` key in values.
131
+ #
132
+ # If the current params have the same key, it will be PREFERRED over the
133
+ # given values.
134
+ #
135
+ # For instance, if <tt>params.get('book.title')</tt> equals to
136
+ # <tt>"TDD"</tt> while <tt>values[:book].title</tt> returns
137
+ # <tt>"No test"</tt>, the first will win.
138
+ #
139
+ # @param name [Symbol] the name of the form
140
+ # @param url [String] the action of the form
141
+ # @param values [Hash,NilClass] a Hash of values to be used to autofill
142
+ # <tt>value</tt> attributes for fields
143
+ # @param attributes [Hash,NilClass] a Hash of attributes to pass to the
144
+ # <tt>form</tt> tag
145
+ #
146
+ # @since 0.2.0
147
+ #
148
+ # @example Pass A Value
149
+ # # Given the following view
150
+ #
151
+ # module Web::Views::Deliveries
152
+ # class Edit
153
+ # include Web::View
154
+ #
155
+ # def form
156
+ # Form.new(:delivery, routes.delivery_path(id: delivery.id),
157
+ # {delivery: delivery, customer: customer},
158
+ # {method: :patch})
159
+ # end
160
+ # end
161
+ # end
162
+ #
163
+ # # And the corresponding template:
164
+ #
165
+ # <%=
166
+ # form_for form do
167
+ # date_field :delivered_on
168
+ #
169
+ # fields_for :customer do
170
+ # text_field :name
171
+ #
172
+ # fields_for :address do
173
+ # # ...
174
+ # text_field :city
175
+ # end
176
+ # end
177
+ #
178
+ # submit 'Update'
179
+ # end
180
+ # %>
181
+ #
182
+ # # It will render:
183
+ # #
184
+ # # <form action="/deliveries/1" method="POST" accept-charset="utf-8">
185
+ # # <input type="hidden" name="_method" value="PATCH">
186
+ # #
187
+ # # # Value taken from delivery.delivered_on
188
+ # # <input type="date" name="delivery[delivered_on]" id="delivery-delivered-on" value="2015-05-27">
189
+ # #
190
+ # # # Value taken from customer.name
191
+ # # <input type="text" name="delivery[customer][name]" id="delivery-customer-name" value="Luca">
192
+ # #
193
+ # # # Value taken from customer.address.city
194
+ # # <input type="text" name="delivery[customer][address][city]" id="delivery-customer-address-city" value="Rome">
195
+ # #
196
+ # # <button type="submit">Update</button>
197
+ # # </form>
198
+ def initialize(name, url, values = {}, attributes = {})
199
+ @name = name
200
+ @url = url
201
+ @values = values
202
+ @attributes = attributes || {}
203
+ end
204
+
205
+ # Return the method specified by the given attributes or fall back to
206
+ # the default value
207
+ #
208
+ # @return [String] the method for the action
209
+ #
210
+ # @since 0.2.0
211
+ # @api private
212
+ #
213
+ # @see Hanami::Helpers::FormHelper::DEFAULT_METHOD
214
+ def verb
215
+ @attributes.fetch(:method, DEFAULT_METHOD)
216
+ end
217
+ end
218
+
219
+ # Instantiate a HTML5 form builder
220
+ #
221
+ # @overload form_for(name, url, options, &blk)
222
+ # Use inline values
223
+ # @param name [Symbol] the toplevel name of the form, it's used to generate
224
+ # input names, ids, and to lookup params to fill values.
225
+ # @param url [String] the form action URL
226
+ # @param options [Hash] HTML attributes to pass to the form tag and form values
227
+ # @option options [Hash] :values An optional payload of objects to pass
228
+ # @param blk [Proc] A block that describes the contents of the form
229
+ #
230
+ # @overload form_for(form, attributes, &blk)
231
+ # Use Form
232
+ # @param form [Hanami::Helpers::FormHelper::Form] a form object
233
+ # @param attributes [Hash] HTML attributes to pass to the form tag and form values
234
+ # @param blk [Proc] A block that describes the contents of the form
235
+ #
236
+ # @return [Hanami::Helpers::FormHelper::FormBuilder] the form builder
237
+ #
238
+ # @since 0.2.0
239
+ #
240
+ # @see Hanami::Helpers::FormHelper
241
+ # @see Hanami::Helpers::FormHelper::Form
242
+ # @see Hanami::Helpers::FormHelper::FormBuilder
243
+ #
244
+ # @example Inline Values In Template
245
+ # <%=
246
+ # form_for :book, routes.books_path, class: 'form-horizontal' do
247
+ # div do
248
+ # label :title
249
+ # text_field :title, class: 'form-control'
250
+ # end
251
+ #
252
+ # submit 'Create'
253
+ # end
254
+ # %>
255
+ #
256
+ # Output:
257
+ # # <form action="/books" method="POST" accept-charset="utf-8" id="book-form" class="form-horizontal">
258
+ # # <div>
259
+ # # <label for="book-title">Title</label>
260
+ # # <input type="text" name="book[title]" id="book-title" value="Test Driven Development">
261
+ # # </div>
262
+ # #
263
+ # # <button type="submit">Create</button>
264
+ # # </form>
265
+ #
266
+ #
267
+ #
268
+ # @example Use In A View
269
+ #
270
+ # module Web::Views::Books
271
+ # class New
272
+ #
273
+ # def form
274
+ # form_for :book, routes.books_path, class: 'form-horizontal' do
275
+ # div do
276
+ # label :title
277
+ # text_field :title, class: 'form-control'
278
+ # end
279
+ #
280
+ # submit 'Create'
281
+ # end
282
+ # end
283
+ # end
284
+ #
285
+ # <%= form %>
286
+ #
287
+ # Output:
288
+ # # <form action="/books" method="POST" accept-charset="utf-8" id="book-form" class="form-horizontal">
289
+ # # <div>
290
+ # # <label for="book-title">Title</label>
291
+ # # <input type="text" name="book[title]" id="book-title" value="Test Driven Development">
292
+ # # </div>
293
+ # #
294
+ # # <button type="submit">Create</button>
295
+ # # </form>
296
+ #
297
+ # @example Share Code Between Views
298
+ #
299
+ # # Given the following views to create and update a resource
300
+ # module Web::Views::Books
301
+ # class New
302
+ # include Web::View
303
+ #
304
+ # def form
305
+ # Form.new(:book, routes.books_path)
306
+ # end
307
+ #
308
+ # def submit_label
309
+ # 'Create'
310
+ # end
311
+ # end
312
+ #
313
+ # class Edit
314
+ # include Web::View
315
+ #
316
+ # def form
317
+ # Form.new(:book, routes.book_path(id: book.id),
318
+ # {book: book}, {method: :patch})
319
+ # end
320
+ #
321
+ # def submit_label
322
+ # 'Update'
323
+ # end
324
+ # end
325
+ # end
326
+ #
327
+ # # The respective templates can be identical:
328
+ #
329
+ # ## books/new.html.erb
330
+ # <%= render partial: 'books/form' %>
331
+ #
332
+ # ## books/edit.html.erb
333
+ # <%= render partial: 'books/form' %>
334
+ #
335
+ # # While the partial can have the following markup:
336
+ #
337
+ # ## books/_form.html.erb
338
+ # <%=
339
+ # form_for form, class: 'form-horizontal' do
340
+ # div do
341
+ # label :title
342
+ # text_field :title, class: 'form-control'
343
+ # end
344
+ #
345
+ # submit submit_label
346
+ # end
347
+ # %>
348
+ #
349
+ # Output:
350
+ # # <form action="/books" method="POST" accept-charset="utf-8" id="book-form" class="form-horizontal">
351
+ # # <div>
352
+ # # <label for="book-title">Title</label>
353
+ # # <input type="text" name="book[title]" id="book-title" value="Test Driven Development">
354
+ # # </div>
355
+ # #
356
+ # # <button type="submit">Create</button>
357
+ # # </form>
358
+ #
359
+ # @example Method override
360
+ # <%=
361
+ # form_for :book, routes.book_path(id: book.id), method: :put do
362
+ # text_field :title
363
+ #
364
+ # submit 'Update'
365
+ # end
366
+ # %>
367
+ #
368
+ # Output:
369
+ # # <form action="/books/23" accept-charset="utf-8" id="book-form" method="POST">
370
+ # # <input type="hidden" name="_method" value="PUT">
371
+ # # <input type="text" name="book[title]" id="book-title" value="Test Driven Development">
372
+ # #
373
+ # # <button type="submit">Update</button>
374
+ # # </form>
375
+ #
376
+ # @example Nested fields
377
+ # <%=
378
+ # form_for :delivery, routes.deliveries_path do
379
+ # text_field :customer_name
380
+ #
381
+ # fields_for :address do
382
+ # text_field :city
383
+ # end
384
+ #
385
+ # submit 'Create'
386
+ # end
387
+ # %>
388
+ #
389
+ # Output:
390
+ # # <form action="/deliveries" accept-charset="utf-8" id="delivery-form" method="POST">
391
+ # # <input type="text" name="delivery[customer_name]" id="delivery-customer-name" value="">
392
+ # # <input type="text" name="delivery[address][city]" id="delivery-address-city" value="">
393
+ # #
394
+ # # <button type="submit">Create</button>
395
+ # # </form>
396
+ def form_for(name, url, options = {}, &blk)
397
+ form = if name.is_a?(Form)
398
+ options = url
399
+ name
400
+ else
401
+ Form.new(name, url, options.delete(:values))
402
+ end
403
+
404
+ attributes = { action: form.url, method: form.verb, :'accept-charset' => DEFAULT_CHARSET, id: "#{ form.name }-form" }.merge(options)
405
+ FormBuilder.new(form, attributes, self, &blk)
406
+ end
407
+
408
+ # Returns CSRF Protection Token stored in session.
409
+ #
410
+ # It returns <tt>nil</tt> if sessions aren't enabled or the value is missing.
411
+ #
412
+ # @return [String,NilClass] token, if present
413
+ #
414
+ # @since 0.2.0
415
+ def csrf_token
416
+ if defined?(session)
417
+ session[CSRF_TOKEN]
418
+ elsif defined?(locals) && locals[:session]
419
+ locals[:session][CSRF_TOKEN]
420
+ end
421
+ end
422
+ end
423
+ end
424
+ end