dry_crud 2.1.1 → 2.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +9 -9
- data/VERSION +1 -1
- data/app/assets/stylesheets/sample.scss +1 -1
- data/app/controllers/crud_controller.rb +1 -1
- data/app/controllers/dry_crud/generic_model.rb +45 -45
- data/app/controllers/dry_crud/nestable.rb +1 -1
- data/app/controllers/dry_crud/render_callbacks.rb +2 -0
- data/app/controllers/dry_crud/responder.rb +3 -0
- data/app/controllers/list_controller.rb +1 -1
- data/app/helpers/dry_crud/form/builder.rb +286 -261
- data/app/helpers/dry_crud/form/control.rb +156 -153
- data/app/helpers/dry_crud/table/actions.rb +69 -67
- data/app/helpers/dry_crud/table/builder.rb +96 -95
- data/app/helpers/dry_crud/table/col.rb +17 -16
- data/app/helpers/dry_crud/table/sorting.rb +48 -46
- data/app/helpers/format_helper.rb +2 -2
- data/app/helpers/table_helper.rb +2 -2
- data/lib/generators/dry_crud/file_generator.rb +2 -2
- data/lib/generators/dry_crud/templates/spec/controllers/crud_test_models_controller_spec.rb +4 -6
- data/lib/generators/dry_crud/templates/spec/helpers/dry_crud/form/builder_spec.rb +11 -11
- data/lib/generators/dry_crud/templates/spec/helpers/form_helper_spec.rb +21 -21
- data/lib/generators/dry_crud/templates/spec/helpers/format_helper_spec.rb +21 -17
- data/lib/generators/dry_crud/templates/spec/support/crud_controller_examples.rb +8 -8
- data/lib/generators/dry_crud/templates/spec/support/crud_controller_test_helper.rb +11 -1
- data/lib/generators/dry_crud/templates/test/controllers/crud_test_models_controller_test.rb +6 -6
- data/lib/generators/dry_crud/templates/test/helpers/custom_assertions_test.rb +1 -0
- data/lib/generators/dry_crud/templates/test/helpers/dry_crud/form/builder_test.rb +250 -245
- data/lib/generators/dry_crud/templates/test/helpers/dry_crud/table/builder_test.rb +141 -128
- data/lib/generators/dry_crud/templates/test/helpers/form_helper_test.rb +54 -54
- data/lib/generators/dry_crud/templates/test/helpers/format_helper_test.rb +4 -4
- data/lib/generators/dry_crud/templates/test/helpers/table_helper_test.rb +1 -1
- data/lib/generators/dry_crud/templates/test/support/crud_controller_test_helper.rb +3 -2
- data/lib/generators/dry_crud/templates/test/support/crud_test_helper.rb +8 -8
- data/lib/generators/dry_crud/templates/test/support/crud_test_model.rb +0 -2
- data/lib/generators/dry_crud/templates/test/support/crud_test_models_controller.rb +7 -7
- data/lib/generators/dry_crud/templates/test/support/custom_assertions.rb +3 -3
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MWE4YzQzMjYxYmZlZWExOWUzZjczNDE2OWRlMTQwZDM4NDU5OTNlMg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
7
|
-
|
6
|
+
OTUzZWFjODA5NzRlZTVlMGQzYzZiOTMzNGRkNjIxNDM4OWM0OTZlMw==
|
7
|
+
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MWY5NDJlYmQ2NjE1OGJlZjEzMGRlZmE1YmUzM2EzZDQ4Nzk3YjJhYzVhZWI5
|
10
|
+
OGQyNzI5OGJmMDgwYjU1N2Y3OTk5MjA2ZTgyNTU3OGI1NDhjODViNmJjZDVm
|
11
|
+
YWIyODY4YzIzODIxNDk2ZGQ4M2E2ODE4ZDQwOWQ2NmFjYTczYzE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ODc3YTliNjQ5YzZhMGQwMjIxMTMwYzBlMDMzNDhjM2I5MDg0NzEwNGY0YTk0
|
14
|
+
NGRjN2Q2MWVkZjBhZjQzMmYzMTJhMDYyM2MwYzlkZGZmZTk0ZjY0NzVjMTE0
|
15
|
+
OTVmODcyNDNkNzUwNDJlNmIyN2E4NDFlZjhkZjIzZTY3NTRjYzM=
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.1.
|
1
|
+
2.1.2
|
@@ -109,7 +109,7 @@ class CrudController < ListController
|
|
109
109
|
|
110
110
|
# Main accessor method for the handled model entry.
|
111
111
|
def entry
|
112
|
-
|
112
|
+
model_ivar_get || model_ivar_set(params[:id] ? find_entry : build_entry)
|
113
113
|
end
|
114
114
|
|
115
115
|
# Creates a new model entry.
|
@@ -13,23 +13,23 @@ module DryCrud
|
|
13
13
|
included do
|
14
14
|
helper_method :model_class, :models_label, :path_args
|
15
15
|
|
16
|
-
|
16
|
+
private
|
17
17
|
|
18
18
|
delegate :model_class, :models_label, :model_identifier, to: 'self.class'
|
19
19
|
end
|
20
20
|
|
21
21
|
private
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
23
|
+
# The scope where model entries will be listed and created.
|
24
|
+
# This is mainly used for nested models to provide the
|
25
|
+
# required context.
|
26
|
+
def model_scope
|
27
|
+
if Rails.version < '4.0'
|
28
|
+
model_class.scoped
|
29
|
+
else
|
30
|
+
model_class.all
|
31
|
+
end
|
31
32
|
end
|
32
|
-
end
|
33
33
|
|
34
34
|
# The path arguments to link to the given model entry.
|
35
35
|
# If the controller is nested, this provides the required context.
|
@@ -37,53 +37,53 @@ module DryCrud
|
|
37
37
|
last
|
38
38
|
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
# Sets an instance variable with the underscored class name if the given
|
49
|
-
# value. If the value is a collection, sets the plural name.
|
50
|
-
def set_model_ivar(value)
|
51
|
-
name = if value.respond_to?(:klass) # ActiveRecord::Relation
|
52
|
-
ivar_name(value.klass).pluralize
|
53
|
-
elsif value.respond_to?(:each) # Array
|
54
|
-
ivar_name(value.first.class).pluralize
|
55
|
-
else
|
56
|
-
ivar_name(value.class)
|
57
|
-
end
|
58
|
-
instance_variable_set(:"@#{name}", value)
|
59
|
-
end
|
40
|
+
# Get the instance variable named after the +model_class+.
|
41
|
+
# If the collection variable is required, pass true as the second argument.
|
42
|
+
def model_ivar_get(plural = false)
|
43
|
+
name = ivar_name(model_class)
|
44
|
+
name = name.pluralize if plural
|
45
|
+
instance_variable_get(:"@#{name}")
|
46
|
+
end
|
60
47
|
|
61
|
-
|
62
|
-
|
63
|
-
|
48
|
+
# Sets an instance variable with the underscored class name if the given
|
49
|
+
# value. If the value is a collection, sets the plural name.
|
50
|
+
def model_ivar_set(value)
|
51
|
+
name = if value.respond_to?(:klass) # ActiveRecord::Relation
|
52
|
+
ivar_name(value.klass).pluralize
|
53
|
+
elsif value.respond_to?(:each) # Array
|
54
|
+
ivar_name(value.first.class).pluralize
|
55
|
+
else
|
56
|
+
ivar_name(value.class)
|
57
|
+
end
|
58
|
+
instance_variable_set(:"@#{name}", value)
|
59
|
+
end
|
64
60
|
|
65
|
-
|
66
|
-
|
67
|
-
# The ActiveRecord class of the model.
|
68
|
-
def model_class
|
69
|
-
@model_class ||= controller_name.classify.constantize
|
61
|
+
def ivar_name(klass)
|
62
|
+
klass.model_name.param_key
|
70
63
|
end
|
71
64
|
|
65
|
+
# Class methods from GenericModel.
|
66
|
+
module ClassMethods
|
67
|
+
# The ActiveRecord class of the model.
|
68
|
+
def model_class
|
69
|
+
@model_class ||= controller_name.classify.constantize
|
70
|
+
end
|
71
|
+
|
72
72
|
# The identifier of the model used for form parameters.
|
73
73
|
# I.e., the symbol of the underscored model name.
|
74
74
|
def model_identifier
|
75
75
|
@model_identifier ||= model_class.model_name.param_key
|
76
76
|
end
|
77
77
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
78
|
+
# A human readable plural name of the model.
|
79
|
+
def models_label(plural = true)
|
80
|
+
opts = { count: (plural ? 3 : 1) }
|
81
|
+
opts[:default] = model_class.model_name.human.titleize
|
82
|
+
opts[:default] = opts[:default].pluralize if plural
|
83
83
|
|
84
|
-
|
84
|
+
model_class.model_name.human(opts)
|
85
|
+
end
|
85
86
|
end
|
86
|
-
end
|
87
87
|
|
88
88
|
end
|
89
89
|
end
|
@@ -45,7 +45,7 @@ module DryCrud
|
|
45
45
|
# Loads the parent entry for the given ActiveRecord class.
|
46
46
|
# By default, performs a find with the class_name_id param.
|
47
47
|
def parent_entry(clazz)
|
48
|
-
|
48
|
+
model_ivar_set(clazz.find(params["#{clazz.name.underscore}_id"]))
|
49
49
|
end
|
50
50
|
|
51
51
|
# An array of objects used in url_for and related functions.
|
@@ -37,6 +37,8 @@ module DryCrud
|
|
37
37
|
# Defines before callbacks for the render actions.
|
38
38
|
def define_render_callbacks(*actions)
|
39
39
|
args = actions.map { |a| :"render_#{a}" }
|
40
|
+
# Rails 4.1 terminator:
|
41
|
+
# ->(ctrl, result) { result == false || ctrl.performed? }
|
40
42
|
args << { only: :before,
|
41
43
|
terminator: 'result == false || performed?' }
|
42
44
|
define_model_callbacks(*args)
|
@@ -12,11 +12,14 @@ module DryCrud
|
|
12
12
|
|
13
13
|
private
|
14
14
|
|
15
|
+
# rubocop:disable PredicateName
|
16
|
+
|
15
17
|
# Check whether the resource has errors. Additionally checks the :success
|
16
18
|
# option.
|
17
19
|
def has_errors?
|
18
20
|
options[:success] == false || super
|
19
21
|
end
|
22
|
+
# rubocop:enable PredicateName
|
20
23
|
|
21
24
|
# Wraps the resources with the path_args for correct nesting.
|
22
25
|
def with_path_args(resources, controller)
|
@@ -35,7 +35,7 @@ class ListController < ApplicationController
|
|
35
35
|
# Helper method to access the entries to be displayed in the current index
|
36
36
|
# page in an uniform way.
|
37
37
|
def entries
|
38
|
-
|
38
|
+
model_ivar_get(true) || model_ivar_set(list_entries)
|
39
39
|
end
|
40
40
|
|
41
41
|
# The base relation used to filter the entries.
|
@@ -1,314 +1,339 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
module DryCrud
|
4
|
-
|
5
|
-
# A form builder that automatically selects the corresponding input field
|
6
|
-
# for ActiveRecord column types. Convenience methods for each column type
|
7
|
-
# allow one to customize the different fields.
|
8
|
-
#
|
9
|
-
# All field methods may be prefixed with +labeled_+ in order to render
|
10
|
-
# a standard label, required mark and an optional help block with them.
|
11
|
-
#
|
12
|
-
# Use #labeled_input_field or #input_field to render a input field
|
13
|
-
# corresponding to the given attribute.
|
14
|
-
#
|
15
|
-
# See the Control class for how to customize the html rendered for a
|
16
|
-
# single input field.
|
17
|
-
class Builder < ActionView::Helpers::FormBuilder
|
18
|
-
|
19
|
-
class_attribute :control_class
|
20
|
-
self.control_class = Control
|
21
|
-
|
22
|
-
attr_reader :template
|
23
|
-
|
24
|
-
delegate :association, :column_type, :column_property, :captionize,
|
25
|
-
:ti, :ta, :link_to, :content_tag, :safe_join, :capture,
|
26
|
-
:add_css_class, :assoc_and_id_attr,
|
27
|
-
to: :template
|
28
|
-
|
29
|
-
### INPUT FIELDS
|
30
|
-
|
31
|
-
# Render multiple input controls together with a label for the given
|
32
|
-
# attributes.
|
33
|
-
def labeled_input_fields(*attrs)
|
34
|
-
options = attrs.extract_options!
|
35
|
-
safe_join(attrs) { |a| labeled_input_field(a, options.dup) }
|
36
|
-
end
|
3
|
+
module DryCrud
|
4
|
+
module Form
|
37
5
|
|
38
|
-
#
|
39
|
-
#
|
6
|
+
# A form builder that automatically selects the corresponding input field
|
7
|
+
# for ActiveRecord column types. Convenience methods for each column type
|
8
|
+
# allow one to customize the different fields.
|
40
9
|
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
# * <tt>:help</tt> - A help text displayd below the input field.
|
44
|
-
# * <tt>:span</tt> - Number of columns the input field should span.
|
45
|
-
# * <tt>:caption</tt> - Different caption for the label.
|
46
|
-
# * <tt>:field_method</tt> - Different method to create the input field.
|
10
|
+
# All field methods may be prefixed with +labeled_+ in order to render
|
11
|
+
# a standard label, required mark and an optional help block with them.
|
47
12
|
#
|
48
|
-
# Use
|
49
|
-
|
50
|
-
control_class.new(self, attr, html_options).render_labeled
|
51
|
-
end
|
52
|
-
|
53
|
-
# Render a corresponding input control for the given attribute.
|
54
|
-
# The input field is chosen based on the ActiveRecord column type.
|
13
|
+
# Use #labeled_input_field or #input_field to render a input field
|
14
|
+
# corresponding to the given attribute.
|
55
15
|
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
|
59
|
-
# * <tt>:span</tt> - Number of columns the input field should span.
|
60
|
-
# * <tt>:field_method</tt> - Different method to create the input field.
|
61
|
-
#
|
62
|
-
# Use additional html_options for the input element.
|
63
|
-
def input_field(attr, html_options = {})
|
64
|
-
control_class.new(self, attr, html_options).render_content
|
65
|
-
end
|
16
|
+
# See the Control class for how to customize the html rendered for a
|
17
|
+
# single input field.
|
18
|
+
class Builder < ActionView::Helpers::FormBuilder
|
66
19
|
|
67
|
-
|
68
|
-
|
69
|
-
html_options[:maxlength] ||= column_property(@object, attr, :limit)
|
70
|
-
text_field(attr, html_options)
|
71
|
-
end
|
20
|
+
class_attribute :control_class
|
21
|
+
self.control_class = Control
|
72
22
|
|
73
|
-
|
74
|
-
def boolean_field(attr, html_options = {})
|
75
|
-
add_css_class(html_options, 'form-control')
|
76
|
-
check_box(attr, html_options)
|
77
|
-
end
|
23
|
+
attr_reader :template
|
78
24
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
end
|
25
|
+
delegate :association, :column_type, :column_property, :captionize,
|
26
|
+
:ti, :ta, :link_to, :content_tag, :safe_join, :capture,
|
27
|
+
:add_css_class, :assoc_and_id_attr,
|
28
|
+
to: :template
|
84
29
|
|
85
|
-
|
86
|
-
alias_method :float_field, :number_field
|
87
|
-
alias_method :decimal_field, :number_field
|
30
|
+
### INPUT FIELDS
|
88
31
|
|
89
|
-
|
90
|
-
#
|
91
|
-
def
|
92
|
-
|
93
|
-
|
32
|
+
# Render multiple input controls together with a label for the given
|
33
|
+
# attributes.
|
34
|
+
def labeled_input_fields(*attrs)
|
35
|
+
options = attrs.extract_options!
|
36
|
+
safe_join(attrs) { |a| labeled_input_field(a, options.dup) }
|
94
37
|
end
|
95
38
|
|
96
|
-
# Render a
|
97
|
-
|
98
|
-
|
99
|
-
|
39
|
+
# Render a corresponding input control and label for the given attribute.
|
40
|
+
# The input field is chosen based on the ActiveRecord column type.
|
41
|
+
#
|
42
|
+
# The following options may be passed:
|
43
|
+
# * <tt>:addon</tt> - Addon content displayd just after the input field.
|
44
|
+
# * <tt>:help</tt> - A help text displayd below the input field.
|
45
|
+
# * <tt>:span</tt> - Number of columns the input field should span.
|
46
|
+
# * <tt>:caption</tt> - Different caption for the label.
|
47
|
+
# * <tt>:field_method</tt> - Different method to create the input field.
|
48
|
+
#
|
49
|
+
# Use additional html_options for the input element.
|
50
|
+
def labeled_input_field(attr, html_options = {})
|
51
|
+
control_class.new(self, attr, html_options).render_labeled
|
100
52
|
end
|
101
53
|
|
102
|
-
# Render a
|
103
|
-
#
|
104
|
-
|
105
|
-
|
54
|
+
# Render a corresponding input control for the given attribute.
|
55
|
+
# The input field is chosen based on the ActiveRecord column type.
|
56
|
+
#
|
57
|
+
# The following options may be passed:
|
58
|
+
# * <tt>:addon</tt> - Addon content displayd just after the input field.
|
59
|
+
# * <tt>:help</tt> - A help text displayd below the input field.
|
60
|
+
# * <tt>:span</tt> - Number of columns the input field should span.
|
61
|
+
# * <tt>:field_method</tt> - Different method to create the input field.
|
62
|
+
#
|
63
|
+
# Use additional html_options for the input element.
|
64
|
+
def input_field(attr, html_options = {})
|
65
|
+
control_class.new(self, attr, html_options).render_content
|
66
|
+
end
|
67
|
+
|
68
|
+
# Render a standard string field with column contraints.
|
69
|
+
def string_field(attr, html_options = {})
|
70
|
+
html_options[:maxlength] ||= column_property(@object, attr, :limit)
|
106
71
|
text_field(attr, html_options)
|
107
72
|
end
|
108
|
-
end
|
109
73
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
select_options(attr, html_options),
|
119
|
-
html_options)
|
120
|
-
else
|
121
|
-
static_text(ta(:none_available, association(@object, attr)).html_safe)
|
74
|
+
# Render a boolean field.
|
75
|
+
def boolean_field(attr, html_options = {})
|
76
|
+
content_tag(:div, class: 'checkbox') do
|
77
|
+
content_tag(:label) do
|
78
|
+
detail = html_options.delete(:detail) || ' '.html_safe
|
79
|
+
safe_join([check_box(attr, html_options), ' ', detail])
|
80
|
+
end
|
81
|
+
end
|
122
82
|
end
|
123
|
-
end
|
124
83
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
belongs_to_field(attr, html_options)
|
134
|
-
end
|
84
|
+
# Add form-control class to all input fields.
|
85
|
+
%w(text_field password_field email_field text_area
|
86
|
+
number_fielc date_field time_field datetime_field).each do |method|
|
87
|
+
define_method(method) do |attr, html_options = {}|
|
88
|
+
add_css_class(html_options, 'form-control')
|
89
|
+
super(attr, html_options)
|
90
|
+
end
|
91
|
+
end
|
135
92
|
|
136
|
-
|
93
|
+
# Customize the standard text area to have 5 rows by default.
|
94
|
+
def text_area(attr, html_options = {})
|
95
|
+
add_css_class(html_options, 'form-control')
|
96
|
+
html_options[:rows] ||= 5
|
97
|
+
super(attr, html_options)
|
98
|
+
end
|
137
99
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
100
|
+
alias_method :integer_field, :number_field
|
101
|
+
alias_method :float_field, :number_field
|
102
|
+
alias_method :decimal_field, :number_field
|
103
|
+
|
104
|
+
if Rails.version < '4.0'
|
105
|
+
# Render a field to select a date. You might want to customize this.
|
106
|
+
def date_field(attr, html_options = {})
|
107
|
+
html_options[:type] = 'date'
|
108
|
+
text_field(attr, html_options)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Render a field to enter a time. You might want to customize this.
|
112
|
+
def time_field(attr, html_options = {})
|
113
|
+
html_options[:type] = 'time'
|
114
|
+
text_field(attr, html_options)
|
115
|
+
end
|
144
116
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
117
|
+
# Render a field to enter a date and time.
|
118
|
+
# You might want to customize this.
|
119
|
+
def datetime_field(attr, html_options = {})
|
120
|
+
html_options[:type] = 'datetime'
|
121
|
+
text_field(attr, html_options)
|
122
|
+
end
|
149
123
|
end
|
150
|
-
end
|
151
124
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
125
|
+
# Render a select element for a :belongs_to association defined by attr.
|
126
|
+
# Use additional html_options for the select element.
|
127
|
+
# To pass a custom element list, specify the list with the :list key or
|
128
|
+
# define an instance variable with the pluralized name of the
|
129
|
+
# association.
|
130
|
+
def belongs_to_field(attr, html_options = {})
|
131
|
+
list = association_entries(attr, html_options).to_a
|
132
|
+
if list.present?
|
133
|
+
add_css_class(html_options, 'form-control')
|
134
|
+
collection_select(attr, list, :id, :to_s,
|
135
|
+
select_options(attr, html_options),
|
136
|
+
html_options)
|
137
|
+
else
|
138
|
+
static_text(
|
139
|
+
ta(:none_available, association(@object, attr)).html_safe)
|
140
|
+
end
|
141
|
+
end
|
156
142
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
143
|
+
# rubocop:disable PredicateName
|
144
|
+
|
145
|
+
# Render a multi select element for a :has_many or
|
146
|
+
# :has_and_belongs_to_many association defined by attr.
|
147
|
+
# Use additional html_options for the select element.
|
148
|
+
# To pass a custom element list, specify the list with the :list key or
|
149
|
+
# define an instance variable with the pluralized name of the
|
150
|
+
# association.
|
151
|
+
def has_many_field(attr, html_options = {})
|
152
|
+
html_options[:multiple] = true
|
153
|
+
add_css_class(html_options, 'multiselect')
|
154
|
+
belongs_to_field(attr, html_options)
|
155
|
+
end
|
156
|
+
# rubocop:enable PredicateName
|
157
|
+
|
158
|
+
### VARIOUS FORM ELEMENTS
|
161
159
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
160
|
+
# Render the error messages for the current form.
|
161
|
+
def error_messages
|
162
|
+
@template.render('shared/error_messages',
|
163
|
+
errors: @object.errors,
|
164
|
+
object: @object)
|
166
165
|
end
|
167
|
-
end
|
168
166
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
167
|
+
# Renders the given content with an addon.
|
168
|
+
def with_addon(content, addon)
|
169
|
+
content_tag(:div, class: 'input-group') do
|
170
|
+
content + content_tag(:span, addon, class: 'input-group-addon')
|
171
|
+
end
|
172
|
+
end
|
173
173
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
end
|
174
|
+
# Renders a static text where otherwise form inputs appear.
|
175
|
+
def static_text(text)
|
176
|
+
content_tag(:p, text, class: 'form-control-static')
|
177
|
+
end
|
179
178
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
else
|
192
|
-
assoc = association(@object, attr)
|
193
|
-
if required?(attr)
|
194
|
-
{ prompt: ta(:please_select, assoc) }
|
195
|
-
else
|
196
|
-
{ include_blank: ta(:no_entry, assoc) }
|
179
|
+
# Generates a help block for fields
|
180
|
+
def help_block(text)
|
181
|
+
content_tag(:p, text, class: 'help-block')
|
182
|
+
end
|
183
|
+
|
184
|
+
# Render a submit button and a cancel link for this form.
|
185
|
+
def standard_actions(submit_label = ti('button.save'), cancel_url = nil)
|
186
|
+
content_tag(:div, class: 'col-md-offset-2 col-md-8') do
|
187
|
+
safe_join([submit_button(submit_label),
|
188
|
+
cancel_link(cancel_url)],
|
189
|
+
' ')
|
197
190
|
end
|
198
191
|
end
|
199
|
-
end
|
200
192
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
validators = @object.class.validators_on(attr) +
|
205
|
-
@object.class.validators_on(attr_id)
|
206
|
-
validators.any? do |v|
|
207
|
-
v.kind == :presence &&
|
208
|
-
!v.options.key?(:if) &&
|
209
|
-
!v.options.key?(:unless)
|
193
|
+
# Render a standard submit button with the given label.
|
194
|
+
def submit_button(label = ti('button.save'))
|
195
|
+
button(label, class: 'btn btn-primary', data: { disable_with: label })
|
210
196
|
end
|
211
|
-
end
|
212
197
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
#
|
218
|
-
# The following options may be passed:
|
219
|
-
# * <tt>:span</tt> - Number of columns the content should span.
|
220
|
-
# * <tt>:caption</tt> - Different caption for the label.
|
221
|
-
def labeled(attr, content = {}, options = {}, &block)
|
222
|
-
if block_given?
|
223
|
-
options = content
|
224
|
-
content = capture(&block)
|
198
|
+
# Render a cancel link pointing to the given url.
|
199
|
+
def cancel_link(url = nil)
|
200
|
+
url ||= cancel_url
|
201
|
+
link_to(ti('button.cancel'), url, class: 'cancel')
|
225
202
|
end
|
226
|
-
control = control_class.new(self, attr, options)
|
227
|
-
control.render_labeled(content)
|
228
|
-
end
|
229
203
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
204
|
+
# Depending if the given attribute must be present, return
|
205
|
+
# only an initial selection prompt or a blank option, respectively.
|
206
|
+
def select_options(attr, options = {})
|
207
|
+
prompt = options.delete(:prompt)
|
208
|
+
blank = options.delete(:include_blank)
|
209
|
+
if options[:multiple]
|
210
|
+
{}
|
211
|
+
elsif prompt
|
212
|
+
{ prompt: prompt }
|
213
|
+
elsif blank
|
214
|
+
{ include_blank: blank }
|
215
|
+
else
|
216
|
+
assoc = association(@object, attr)
|
217
|
+
if required?(attr)
|
218
|
+
{ prompt: ta(:please_select, assoc) }
|
219
|
+
else
|
220
|
+
{ include_blank: ta(:no_entry, assoc) }
|
221
|
+
end
|
222
|
+
end
|
241
223
|
end
|
242
|
-
end
|
243
224
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
225
|
+
# Returns true if the given attribute must be present.
|
226
|
+
def required?(attr)
|
227
|
+
attr, attr_id = assoc_and_id_attr(attr)
|
228
|
+
validators = @object.class.validators_on(attr) +
|
229
|
+
@object.class.validators_on(attr_id)
|
230
|
+
validators.any? do |v|
|
231
|
+
v.kind == :presence &&
|
232
|
+
!v.options.key?(:if) &&
|
233
|
+
!v.options.key?(:unless)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Render a label for the given attribute with the passed content.
|
238
|
+
# The content may be given as an argument or as a block:
|
239
|
+
# labeled(:attr) { #content }
|
240
|
+
# labeled(:attr, content)
|
241
|
+
#
|
242
|
+
# The following options may be passed:
|
243
|
+
# * <tt>:span</tt> - Number of columns the content should span.
|
244
|
+
# * <tt>:caption</tt> - Different caption for the label.
|
245
|
+
def labeled(attr, content = {}, options = {}, &block)
|
246
|
+
if block_given?
|
247
|
+
options = content
|
248
|
+
content = capture(&block)
|
249
|
+
end
|
250
|
+
control = control_class.new(self, attr, options)
|
251
|
+
control.render_labeled(content)
|
252
|
+
end
|
248
253
|
|
249
|
-
|
254
|
+
# Dispatch methods starting with 'labeled_' to render a label and the
|
255
|
+
# corresponding input field.
|
256
|
+
# E.g. labeled_boolean_field(:checked, class: 'bold')
|
257
|
+
# To add an additional help text, use the help option.
|
258
|
+
# E.g. labeled_boolean_field(:checked, help: 'Some Help')
|
259
|
+
def method_missing(name, *args)
|
260
|
+
field_method = labeled_field_method?(name)
|
261
|
+
if field_method
|
262
|
+
build_labeled_field(field_method, *args)
|
263
|
+
else
|
264
|
+
super(name, *args)
|
265
|
+
end
|
266
|
+
end
|
250
267
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
prefix = 'labeled_'
|
255
|
-
if name.to_s.start_with?(prefix)
|
256
|
-
field_method = name.to_s[prefix.size..-1]
|
257
|
-
field_method if respond_to?(field_method)
|
268
|
+
# Overriden to fullfill contract with method_missing 'labeled_' methods.
|
269
|
+
def respond_to?(name)
|
270
|
+
labeled_field_method?(name).present? || super(name)
|
258
271
|
end
|
259
|
-
end
|
260
272
|
|
261
|
-
|
262
|
-
# an optional help block.
|
263
|
-
def build_labeled_field(field_method, *args)
|
264
|
-
options = args.extract_options!
|
265
|
-
options[:field_method] = field_method
|
266
|
-
control_class.new(self, *(args << options)).render_labeled
|
267
|
-
end
|
273
|
+
private
|
268
274
|
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
275
|
+
# Checks if the passed name corresponds to a field method with a
|
276
|
+
# 'labeled_' prefix.
|
277
|
+
def labeled_field_method?(name)
|
278
|
+
prefix = 'labeled_'
|
279
|
+
if name.to_s.start_with?(prefix)
|
280
|
+
field_method = name.to_s[prefix.size..-1]
|
281
|
+
field_method if respond_to?(field_method)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
# Renders the corresponding field together with a label, required mark
|
286
|
+
# and an optional help block.
|
287
|
+
def build_labeled_field(field_method, *args)
|
288
|
+
options = args.extract_options!
|
289
|
+
options[:field_method] = field_method
|
290
|
+
control_class.new(self, *(args << options)).render_labeled
|
281
291
|
end
|
282
|
-
list
|
283
|
-
end
|
284
292
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
list.list
|
299
|
-
else
|
293
|
+
# Returns the list of association entries, either from options[:list] or
|
294
|
+
# the instance variable with the pluralized association name.
|
295
|
+
# Otherwise, if the association defines a #options_list or #list scope,
|
296
|
+
# this is used to load the entries.
|
297
|
+
# As a last resort, all entries from the association class are returned.
|
298
|
+
def association_entries(attr, options)
|
299
|
+
list = options.delete(:list)
|
300
|
+
unless list
|
301
|
+
assoc = association(@object, attr)
|
302
|
+
list = @template.send(:instance_variable_get,
|
303
|
+
:"@#{assoc.name.to_s.pluralize}")
|
304
|
+
list ||= load_association_entries(assoc)
|
305
|
+
end
|
300
306
|
list
|
301
307
|
end
|
302
|
-
end
|
303
308
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
309
|
+
# Automatically load the entries for the given association.
|
310
|
+
def load_association_entries(assoc)
|
311
|
+
klass = assoc.klass
|
312
|
+
list = if Rails.version >= '4.0'
|
313
|
+
klass.all.merge(assoc.scope)
|
314
|
+
else
|
315
|
+
klass.where(assoc.options[:conditions])
|
316
|
+
.order(assoc.options[:order])
|
317
|
+
end
|
318
|
+
# Use special scopes if they are defined
|
319
|
+
if klass.respond_to?(:options_list)
|
320
|
+
list.options_list
|
321
|
+
elsif klass.respond_to?(:list)
|
322
|
+
list.list
|
323
|
+
else
|
324
|
+
list
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
# Get the cancel url for the given object considering options:
|
329
|
+
# 1. Use :cancel_url_new or :cancel_url_edit option, if present
|
330
|
+
# 2. Use :cancel_url option, if present
|
331
|
+
def cancel_url
|
332
|
+
url = @object.new_record? ? options[:cancel_url_new] :
|
333
|
+
options[:cancel_url_edit]
|
334
|
+
url || options[:cancel_url]
|
335
|
+
end
|
312
336
|
|
337
|
+
end
|
313
338
|
end
|
314
339
|
end
|