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
@@ -1,179 +1,182 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
module DryCrud
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
INPUT_SPANS[:
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
INPUT_SPANS[:
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
3
|
+
module DryCrud
|
4
|
+
module Form
|
5
|
+
|
6
|
+
# Internal class to handle the rendering of a single form control,
|
7
|
+
# consisting of a label, input field, addon, help text or
|
8
|
+
# required mark.
|
9
|
+
class Control
|
10
|
+
|
11
|
+
attr_reader :builder, :attr, :args, :options, :span, :addon, :help
|
12
|
+
|
13
|
+
delegate :content_tag, :object,
|
14
|
+
to: :builder
|
15
|
+
|
16
|
+
# Html displayed to mark an input as required.
|
17
|
+
REQUIRED_MARK = '*'
|
18
|
+
|
19
|
+
# Number of default input field span columns depending
|
20
|
+
# on the #field_method.
|
21
|
+
INPUT_SPANS = Hash.new(8)
|
22
|
+
INPUT_SPANS[:number_field] =
|
23
|
+
INPUT_SPANS[:integer_field] =
|
24
|
+
INPUT_SPANS[:float_field] =
|
25
|
+
INPUT_SPANS[:decimal_field] = 2
|
26
|
+
INPUT_SPANS[:date_field] =
|
27
|
+
INPUT_SPANS[:time_field] = 3
|
28
|
+
|
29
|
+
# Create a new control instance.
|
30
|
+
# Takes the form builder, the attribute to build the control for
|
31
|
+
# as well as any additional arguments for the field method.
|
32
|
+
# This includes an options hash as the last argument, that
|
33
|
+
# may contain the following special options:
|
34
|
+
#
|
35
|
+
# * <tt>:addon</tt> - Addon content displayd just after the input field.
|
36
|
+
# * <tt>:help</tt> - A help text displayd below the input field.
|
37
|
+
# * <tt>:span</tt> - Number of columns the input field should span.
|
38
|
+
# * <tt>:caption</tt> - Different caption for the label.
|
39
|
+
# * <tt>:field_method</tt> - Different method to create the input field.
|
40
|
+
# * <tt>:required</tt> - Sets the field as required
|
41
|
+
# (The value for this option usually is 'required').
|
42
|
+
#
|
43
|
+
# All the other options will go to the field_method.
|
44
|
+
def initialize(builder, attr, *args)
|
45
|
+
@builder = builder
|
46
|
+
@attr = attr
|
47
|
+
@options = args.extract_options!
|
48
|
+
@args = args
|
49
|
+
|
50
|
+
@addon = options.delete(:addon)
|
51
|
+
@help = options.delete(:help)
|
52
|
+
@span = options.delete(:span)
|
53
|
+
@caption = options.delete(:caption)
|
54
|
+
@field_method = options.delete(:field_method)
|
55
|
+
@required = options[:required]
|
56
|
+
end
|
56
57
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
58
|
+
# Renders only the content of the control.
|
59
|
+
# I.e. no label and span divs.
|
60
|
+
def render_content
|
61
|
+
content
|
62
|
+
end
|
62
63
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
64
|
+
# Renders the complete control with label and everything.
|
65
|
+
# Render the content given or the default one.
|
66
|
+
def render_labeled(content = nil)
|
67
|
+
@content = content if content
|
68
|
+
labeled
|
69
|
+
end
|
69
70
|
|
70
|
-
|
71
|
+
private
|
71
72
|
|
72
|
-
|
73
|
-
|
74
|
-
|
73
|
+
# Create the HTML markup for any labeled content.
|
74
|
+
def labeled
|
75
|
+
errors = errors? ? ' has-error' : ''
|
75
76
|
|
76
|
-
|
77
|
-
|
78
|
-
|
77
|
+
content_tag(:div, class: "form-group#{errors}") do
|
78
|
+
builder.label(attr, caption, class: 'col-md-2 control-label') +
|
79
|
+
content_tag(:div, content, class: "col-md-#{span}")
|
80
|
+
end
|
79
81
|
end
|
80
|
-
end
|
81
82
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
83
|
+
# Return the currently set content or create it
|
84
|
+
# based on the various options given.
|
85
|
+
#
|
86
|
+
# Optionally renders addon, required mark and/or a help block
|
87
|
+
# additionally to the input field.
|
88
|
+
def content
|
89
|
+
@content ||= begin
|
90
|
+
content = input
|
91
|
+
if addon
|
92
|
+
content = builder.with_addon(content, addon)
|
93
|
+
elsif required
|
94
|
+
content = builder.with_addon(content, REQUIRED_MARK)
|
95
|
+
end
|
96
|
+
content << builder.help_block(help) if help.present?
|
97
|
+
content
|
94
98
|
end
|
95
|
-
content << builder.help_block(help) if help.present?
|
96
|
-
content
|
97
99
|
end
|
98
|
-
end
|
99
100
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
101
|
+
# Return the currently set input field or create it
|
102
|
+
# depending on the attribute.
|
103
|
+
def input
|
104
|
+
@input ||= begin
|
105
|
+
options[:required] = 'required' if required
|
106
|
+
builder.send(field_method, attr, *(args << options))
|
107
|
+
end
|
107
108
|
end
|
108
|
-
end
|
109
109
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
110
|
+
# The field method used to create the input.
|
111
|
+
# If none is set, detect it from the attribute type.
|
112
|
+
def field_method
|
113
|
+
@field_method ||= detect_field_method
|
114
|
+
end
|
115
115
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
116
|
+
# True if the attr is required, false otherwise.
|
117
|
+
def required
|
118
|
+
@required = @required.nil? ? builder.required?(attr) : @required
|
119
|
+
end
|
120
120
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
121
|
+
# Number of grid columns the input field should span.
|
122
|
+
def span
|
123
|
+
@span ||= INPUT_SPANS[field_method]
|
124
|
+
end
|
125
125
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
126
|
+
# The caption of the label.
|
127
|
+
# If none is set, uses the I18n value of the attribute.
|
128
|
+
def caption
|
129
|
+
@caption ||= builder.captionize(attr, object.class)
|
130
|
+
end
|
131
131
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
132
|
+
# Returns true if any errors are found on the passed attribute or its
|
133
|
+
# association.
|
134
|
+
def errors?
|
135
|
+
attr_plain, attr_id = builder.assoc_and_id_attr(attr)
|
136
|
+
# errors aint a Hash
|
137
|
+
# rubocop:disable DeprecatedHashMethods
|
138
|
+
object.errors.has_key?(attr_plain.to_sym) ||
|
139
|
+
object.errors.has_key?(attr_id.to_sym)
|
140
|
+
# rubocop:enable DeprecatedHashMethods
|
141
|
+
end
|
142
142
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
:
|
148
|
-
|
149
|
-
:
|
150
|
-
|
151
|
-
:
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
:"#{type}_field"
|
158
|
-
|
159
|
-
|
143
|
+
# Defines the field method to use based on the attribute
|
144
|
+
# type, association or name.
|
145
|
+
# rubocop:disable PerceivedComplexity
|
146
|
+
def detect_field_method
|
147
|
+
if type == :text
|
148
|
+
:text_area
|
149
|
+
elsif association_kind?(:belongs_to)
|
150
|
+
:belongs_to_field
|
151
|
+
elsif association_kind?(:has_and_belongs_to_many, :has_many)
|
152
|
+
:has_many_field
|
153
|
+
elsif attr.to_s.include?('password')
|
154
|
+
:password_field
|
155
|
+
elsif attr.to_s.include?('email')
|
156
|
+
:email_field
|
157
|
+
elsif builder.respond_to?(:"#{type}_field")
|
158
|
+
:"#{type}_field"
|
159
|
+
else
|
160
|
+
:text_field
|
161
|
+
end
|
160
162
|
end
|
161
|
-
|
163
|
+
# rubocop:enable PerceivedComplexity
|
162
164
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
165
|
+
# The column type of the attribute.
|
166
|
+
def type
|
167
|
+
@type ||= builder.column_type(object, attr)
|
168
|
+
end
|
167
169
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
170
|
+
# Returns true if attr is a non-polymorphic association.
|
171
|
+
# If one or more macros are given, the association must be of this kind.
|
172
|
+
def association_kind?(*macros)
|
173
|
+
if type == :integer || type.nil?
|
174
|
+
assoc = builder.association(object, attr, *macros)
|
173
175
|
|
174
|
-
|
175
|
-
|
176
|
-
|
176
|
+
assoc.present? && assoc.options[:polymorphic].nil?
|
177
|
+
else
|
178
|
+
false
|
179
|
+
end
|
177
180
|
end
|
178
181
|
end
|
179
182
|
end
|
@@ -1,88 +1,90 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
module DryCrud
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
module DryCrud
|
4
|
+
module Table
|
5
|
+
# Adds action columns to the table builder.
|
6
|
+
# Predefined actions are available for show, edit and destroy.
|
7
|
+
# Additionally, a special col type to define cells linked to the show page
|
8
|
+
# of the row entry is provided.
|
9
|
+
module Actions
|
10
|
+
extend ActiveSupport::Concern
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
included do
|
13
|
+
delegate :link_to, :path_args, :edit_polymorphic_path, :ti,
|
14
|
+
to: :template
|
15
|
+
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
# Renders the passed attr with a link to the show action for
|
18
|
+
# the current entry.
|
19
|
+
# A block may be given to define the link path for the row entry.
|
20
|
+
def attr_with_show_link(attr, &block)
|
21
|
+
sortable_attr(attr) do |e|
|
22
|
+
link_to(format_attr(e, attr), action_path(e, &block))
|
23
|
+
end
|
22
24
|
end
|
23
|
-
end
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
26
|
+
# Action column to show the row entry.
|
27
|
+
# A block may be given to define the link path for the row entry.
|
28
|
+
# If the block returns nil, no link is rendered.
|
29
|
+
def show_action_col(html_options = {}, &block)
|
30
|
+
action_col do |e|
|
31
|
+
path = action_path(e, &block)
|
32
|
+
if path
|
33
|
+
table_action_link('zoom-in',
|
34
|
+
path,
|
35
|
+
html_options.clone)
|
36
|
+
end
|
35
37
|
end
|
36
38
|
end
|
37
|
-
end
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
40
|
+
# Action column to edit the row entry.
|
41
|
+
# A block may be given to define the link path for the row entry.
|
42
|
+
# If the block returns nil, no link is rendered.
|
43
|
+
def edit_action_col(html_options = {}, &block)
|
44
|
+
action_col do |e|
|
45
|
+
path = action_path(e, &block)
|
46
|
+
if path
|
47
|
+
path = path.is_a?(String) ? path : edit_polymorphic_path(path)
|
48
|
+
table_action_link('pencil', path, html_options.clone)
|
49
|
+
end
|
48
50
|
end
|
49
51
|
end
|
50
|
-
end
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
53
|
+
# Action column to destroy the row entry.
|
54
|
+
# A block may be given to define the link path for the row entry.
|
55
|
+
# If the block returns nil, no link is rendered.
|
56
|
+
def destroy_action_col(html_options = {}, &block)
|
57
|
+
action_col do |e|
|
58
|
+
path = action_path(e, &block)
|
59
|
+
if path
|
60
|
+
table_action_link('remove',
|
61
|
+
path,
|
62
|
+
html_options.merge(
|
63
|
+
data: { confirm: ti(:confirm_delete),
|
64
|
+
method: :delete }))
|
65
|
+
end
|
64
66
|
end
|
65
67
|
end
|
66
|
-
end
|
67
68
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
69
|
+
# Action column inside a table. No header.
|
70
|
+
# The cell content should be defined in the passed block.
|
71
|
+
def action_col(&block)
|
72
|
+
col('', class: 'action', &block)
|
73
|
+
end
|
73
74
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
75
|
+
# Generic action link inside a table.
|
76
|
+
def table_action_link(icon, url, html_options = {})
|
77
|
+
add_css_class(html_options, "icon icon-#{icon}")
|
78
|
+
link_to('', url, html_options)
|
79
|
+
end
|
79
80
|
|
80
|
-
|
81
|
+
private
|
81
82
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
83
|
+
# If a block is given, call it to get the path for the current row entry.
|
84
|
+
# Otherwise, return the standard path args.
|
85
|
+
def action_path(e)
|
86
|
+
block_given? ? yield(e) : path_args(e)
|
87
|
+
end
|
86
88
|
end
|
87
89
|
end
|
88
90
|
end
|