dry_crud 2.1.1 → 2.1.2
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 +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
|