dry_crud 1.5.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +24 -19
- data/Rakefile +12 -4
- data/VERSION +1 -1
- data/lib/generators/dry_crud/dry_crud_generator.rb +7 -7
- data/lib/generators/dry_crud/templates/app/assets/stylesheets/crud.scss +18 -7
- data/lib/generators/dry_crud/templates/app/assets/stylesheets/sample.scss +13 -18
- data/lib/generators/dry_crud/templates/app/controllers/crud_controller.rb +74 -87
- data/lib/generators/dry_crud/templates/app/controllers/list_controller.rb +95 -50
- data/lib/generators/dry_crud/templates/app/helpers/crud_helper.rb +11 -11
- data/lib/generators/dry_crud/templates/app/helpers/list_helper.rb +7 -7
- data/lib/generators/dry_crud/templates/app/helpers/standard_form_builder.rb +55 -27
- data/lib/generators/dry_crud/templates/app/helpers/standard_helper.rb +83 -30
- data/lib/generators/dry_crud/templates/app/helpers/standard_table_builder.rb +5 -13
- data/lib/generators/dry_crud/templates/app/views/layouts/_flash.html.erb +1 -4
- data/lib/generators/dry_crud/templates/app/views/layouts/_flash.html.haml +1 -3
- data/lib/generators/dry_crud/templates/app/views/layouts/_nav.html.erb +6 -6
- data/lib/generators/dry_crud/templates/app/views/layouts/_nav.html.haml +5 -3
- data/lib/generators/dry_crud/templates/app/views/layouts/crud.html.erb +13 -11
- data/lib/generators/dry_crud/templates/app/views/layouts/crud.html.haml +15 -15
- data/lib/generators/dry_crud/templates/app/views/shared/_error_messages.html.erb +10 -10
- data/lib/generators/dry_crud/templates/app/views/shared/_labeled.html.erb +2 -2
- data/lib/generators/dry_crud/templates/app/views/shared/_labeled.html.haml +2 -2
- data/lib/generators/dry_crud/templates/config/initializers/field_error_proc.rb +1 -0
- data/lib/generators/dry_crud/templates/config/locales/en_crud.yml +1 -0
- data/lib/generators/dry_crud/templates/test/crud_test_model.rb +72 -17
- data/lib/generators/dry_crud/templates/test/custom_assertions.rb +1 -1
- data/lib/generators/dry_crud/templates/test/functional/crud_controller_test_helper.rb +42 -26
- data/lib/generators/dry_crud/templates/test/functional/crud_test_models_controller_test.rb +135 -29
- data/lib/generators/dry_crud/templates/test/unit/custom_assertions_test.rb +4 -4
- data/lib/generators/dry_crud/templates/test/unit/helpers/crud_helper_test.rb +4 -2
- data/lib/generators/dry_crud/templates/test/unit/helpers/list_helper_test.rb +2 -0
- data/lib/generators/dry_crud/templates/test/unit/helpers/standard_form_builder_test.rb +94 -16
- data/lib/generators/dry_crud/templates/test/unit/helpers/standard_helper_test.rb +58 -18
- data/lib/generators/dry_crud/templates/test/unit/helpers/standard_table_builder_test.rb +4 -4
- data/test/templates/Gemfile +1 -0
- data/test/templates/app/controllers/admin/cities_controller.rb +0 -7
- data/test/templates/app/controllers/admin/countries_controller.rb +2 -3
- data/test/templates/app/controllers/ajax_controller.rb +2 -0
- data/test/templates/app/controllers/people_controller.rb +1 -1
- data/test/templates/app/models/city.rb +2 -0
- data/test/templates/app/models/country.rb +2 -0
- data/test/templates/app/models/person.rb +2 -0
- data/test/templates/app/views/admin/cities/_attrs.html.erb +1 -0
- data/test/templates/app/views/admin/cities/_attrs.html.haml +1 -0
- data/test/templates/app/views/admin/cities/_form.html.erb +7 -1
- data/test/templates/app/views/admin/cities/_form.html.haml +5 -1
- data/test/templates/app/views/admin/cities/_list.html.erb +1 -4
- data/test/templates/app/views/admin/cities/_list.html.haml +1 -3
- data/test/templates/app/views/ajax/_actions_show.html.erb +4 -0
- data/test/templates/app/views/ajax/_actions_show.html.haml +4 -0
- data/test/templates/app/views/ajax/_form.html.erb +2 -0
- data/test/templates/app/views/ajax/_form.html.haml +2 -0
- data/test/templates/app/views/ajax/edit.js.erb +1 -0
- data/test/templates/app/views/ajax/edit.js.haml +1 -0
- data/test/templates/app/views/ajax/show.js.erb +1 -0
- data/test/templates/app/views/ajax/show.js.haml +1 -0
- data/test/templates/app/views/ajax/update.js.erb +5 -0
- data/test/templates/app/views/ajax/update.js.haml +5 -0
- data/test/templates/app/views/layouts/_nav.html.erb +6 -0
- data/test/templates/app/views/layouts/_nav.html.haml +5 -0
- data/test/templates/app/views/layouts/bootstrap.html.erb +68 -0
- data/test/templates/app/views/layouts/bootstrap.html.haml +49 -0
- data/test/templates/app/views/people/_attrs.html.erb +2 -2
- data/test/templates/app/views/people/_attrs.html.haml +2 -2
- data/test/templates/config/routes.rb +5 -5
- data/test/templates/db/migrate/20100511174904_create_people_and_cities.rb +1 -1
- data/test/templates/db/seeds.rb +52 -52
- data/test/templates/test/functional/admin/cities_controller_test.rb +15 -15
- data/test/templates/test/functional/admin/countries_controller_test.rb +4 -5
- data/test/templates/test/functional/people_controller_test.rb +32 -4
- metadata +22 -9
- data/lib/generators/dry_crud/templates/app/views/layouts/_menu.html.erb +0 -3
- data/lib/generators/dry_crud/templates/app/views/layouts/_menu.html.haml +0 -3
- data/test/templates/app/views/layouts/_menu.html.erb +0 -3
- data/test/templates/app/views/layouts/_menu.html.haml +0 -3
@@ -6,62 +6,71 @@
|
|
6
6
|
# the user the same list as he left it.
|
7
7
|
class ListController < ApplicationController
|
8
8
|
|
9
|
-
helper_method :model_class, :models_label, :path_args
|
9
|
+
helper_method :model_class, :models_label, :entries, :path_args
|
10
10
|
|
11
11
|
delegate :model_class, :models_label, :to => 'self.class'
|
12
12
|
|
13
13
|
hide_action :model_class, :models_label, :inheritable_root_controller
|
14
14
|
|
15
|
+
respond_to :html, :json
|
15
16
|
|
16
17
|
############## ACTIONS ############################################
|
17
18
|
|
18
19
|
# List all entries of this model.
|
19
20
|
# GET /entries
|
20
21
|
# GET /entries.json
|
21
|
-
def index
|
22
|
-
|
23
|
-
respond_with @entries
|
22
|
+
def index(&block)
|
23
|
+
respond_with(entries, &block)
|
24
24
|
end
|
25
25
|
|
26
26
|
protected
|
27
27
|
|
28
|
-
#
|
28
|
+
# Helper method to access the entries to be displayed in the current index page in an uniform way.
|
29
|
+
def entries
|
30
|
+
get_model_ivar(true) || set_model_ivar(list_entries)
|
31
|
+
end
|
32
|
+
|
33
|
+
# The base relation used to filter the entries.
|
34
|
+
# This method may be adapted as long it returns an ActiveRecord::Relation.
|
29
35
|
def list_entries
|
30
36
|
model_scope
|
31
37
|
end
|
32
|
-
|
38
|
+
|
33
39
|
# The scope where model entries will be listed and created.
|
34
|
-
# This is mainly used for nested models to provide the
|
40
|
+
# This is mainly used for nested models to provide the
|
35
41
|
# required context.
|
36
42
|
def model_scope
|
37
43
|
model_class.scoped
|
38
44
|
end
|
39
|
-
|
45
|
+
|
40
46
|
# The path arguments to link to the given entry.
|
41
47
|
# If the controller is nested, this provides the required context.
|
42
48
|
def path_args(last)
|
43
49
|
last
|
44
50
|
end
|
45
51
|
|
46
|
-
#
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
+
# Get the instance variable named after the model_class.
|
53
|
+
# If the collection variable is required, pass true as the second argument.
|
54
|
+
def get_model_ivar(plural = false)
|
55
|
+
name = model_class.name.underscore
|
56
|
+
name = name.pluralize if plural
|
57
|
+
instance_variable_get(:"@#{name}")
|
52
58
|
end
|
53
59
|
|
54
|
-
#
|
55
|
-
# If
|
56
|
-
def
|
57
|
-
|
58
|
-
|
60
|
+
# Sets an instance variable with the underscored class name if the given value.
|
61
|
+
# If the value is a collection, sets the plural name.
|
62
|
+
def set_model_ivar(value)
|
63
|
+
name = if value.respond_to?(:klass) # ActiveRecord::Relation
|
64
|
+
value.klass.name.pluralize
|
65
|
+
elsif value.respond_to?(:each) # Array
|
66
|
+
value.first.klass.name.pluralize
|
67
|
+
else
|
68
|
+
value.class.name
|
69
|
+
end
|
70
|
+
instance_variable_set(:"@#{name.underscore}", value)
|
59
71
|
end
|
60
72
|
|
61
73
|
class << self
|
62
|
-
# Callbacks
|
63
|
-
include ActiveModel::Callbacks
|
64
|
-
|
65
74
|
# The ActiveRecord class of the model.
|
66
75
|
def model_class
|
67
76
|
@model_class ||= controller_name.classify.constantize
|
@@ -72,20 +81,56 @@ class ListController < ApplicationController
|
|
72
81
|
opts = {:count => (plural ? 3 : 1)}
|
73
82
|
opts[:default] = model_class.model_name.human.titleize
|
74
83
|
opts[:default] = opts[:default].pluralize if plural
|
75
|
-
|
84
|
+
|
76
85
|
model_class.model_name.human(opts)
|
77
86
|
end
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
# Provide before_render callbacks.
|
91
|
+
module Callbacks
|
92
|
+
|
93
|
+
def self.included(controller)
|
94
|
+
controller.extend ActiveModel::Callbacks
|
95
|
+
controller.extend ClassMethods
|
96
|
+
controller.alias_method_chain :render, :callbacks
|
97
|
+
|
98
|
+
controller.define_render_callbacks :index
|
99
|
+
end
|
100
|
+
|
101
|
+
# Helper method to run before_render callbacks and render the action.
|
102
|
+
# If a callback renders or redirects, the action is not rendered.
|
103
|
+
def render_with_callbacks(*args, &block)
|
104
|
+
options = _normalize_render(*args, &block)
|
105
|
+
p options if options[:location] == :back
|
106
|
+
callback = "render_#{options[:template]}"
|
107
|
+
run_callbacks(callback) if respond_to?(:"_run_#{callback}_callbacks", true)
|
108
|
+
|
109
|
+
render_without_callbacks(*args, &block) unless performed?
|
110
|
+
end
|
111
|
+
|
112
|
+
protected
|
113
|
+
|
114
|
+
# Helper method the run the given block in between the before and after
|
115
|
+
# callbacks of the given kinds.
|
116
|
+
def with_callbacks(*kinds, &block)
|
117
|
+
kinds.reverse.inject(block) do |b, kind|
|
118
|
+
lambda { run_callbacks(kind, &b) }
|
119
|
+
end.call
|
120
|
+
end
|
121
|
+
|
122
|
+
module ClassMethods
|
123
|
+
# Defines before callbacks for the render actions.
|
124
|
+
def define_render_callbacks(*actions)
|
125
|
+
args = actions.collect {|a| :"render_#{a}" }
|
126
|
+
args << {:only => :before,
|
127
|
+
:terminator => "result == false || performed?"}
|
128
|
+
define_model_callbacks *args
|
129
|
+
end
|
85
130
|
end
|
86
131
|
end
|
87
|
-
|
88
|
-
|
132
|
+
|
133
|
+
include Callbacks
|
89
134
|
|
90
135
|
# The search functionality for the index table.
|
91
136
|
# Extracted into an own module for convenience.
|
@@ -111,12 +156,12 @@ class ListController < ApplicationController
|
|
111
156
|
def search_condition
|
112
157
|
if search_support? && params[:q].present?
|
113
158
|
terms = params[:q].split(/\s+/).collect { |t| "%#{t}%" }
|
114
|
-
clause = search_columns.collect do |f|
|
159
|
+
clause = search_columns.collect do |f|
|
115
160
|
col = f.to_s.include?('.') ? f : "#{model_class.table_name}.#{f}"
|
116
161
|
"#{col} LIKE ?"
|
117
162
|
end.join(" OR ")
|
118
163
|
clause = terms.collect {|t| "(#{clause})" }.join(" AND ")
|
119
|
-
|
164
|
+
|
120
165
|
["(#{clause})"] + terms.collect {|t| [t] * search_columns.size }.flatten
|
121
166
|
end
|
122
167
|
end
|
@@ -228,7 +273,7 @@ class ListController < ApplicationController
|
|
228
273
|
session[:list_params] ||= {}
|
229
274
|
session[:list_params][remember_key] ||= {}
|
230
275
|
end
|
231
|
-
|
276
|
+
|
232
277
|
# Params are stored by request path to play nice when a controller
|
233
278
|
# is used in different routes.
|
234
279
|
def remember_key
|
@@ -245,27 +290,27 @@ class ListController < ApplicationController
|
|
245
290
|
# namespace, may define this attribute as follows:
|
246
291
|
# self.nesting = :admin, Country
|
247
292
|
module Nesting
|
248
|
-
|
293
|
+
|
249
294
|
# Adds the :nesting class attribute and parent helper methods
|
250
295
|
# to the including controller.
|
251
296
|
def self.included(controller)
|
252
297
|
controller.class_attribute :nesting
|
253
|
-
|
298
|
+
|
254
299
|
controller.helper_method :parent, :parents
|
255
|
-
|
300
|
+
|
256
301
|
controller.alias_method_chain :model_scope, :nesting
|
257
302
|
controller.alias_method_chain :path_args, :nesting
|
258
303
|
end
|
259
|
-
|
304
|
+
|
260
305
|
protected
|
261
|
-
|
306
|
+
|
262
307
|
# Returns the direct parent ActiveRecord of the current request, if any.
|
263
308
|
def parent
|
264
309
|
parents.select {|p| p.is_a?(ActiveRecord::Base) }.last
|
265
310
|
end
|
266
|
-
|
311
|
+
|
267
312
|
# Returns the parent entries of the current request, if any.
|
268
|
-
# These are ActiveRecords or namespace symbols, corresponding
|
313
|
+
# These are ActiveRecords or namespace symbols, corresponding
|
269
314
|
# to the defined nesting attribute.
|
270
315
|
def parents
|
271
316
|
@parents ||= Array(nesting).collect do |p|
|
@@ -276,18 +321,18 @@ class ListController < ApplicationController
|
|
276
321
|
end
|
277
322
|
end
|
278
323
|
end
|
279
|
-
|
324
|
+
|
280
325
|
# Loads the parent entry for the given ActiveRecord class.
|
281
326
|
# By default, performs a find with the class_name_id param.
|
282
327
|
def parent_entry(clazz)
|
283
|
-
clazz.find(params["#{clazz.name.underscore}_id"])
|
328
|
+
set_model_ivar(clazz.find(params["#{clazz.name.underscore}_id"]))
|
284
329
|
end
|
285
|
-
|
330
|
+
|
286
331
|
# An array of objects used in url_for and related functions.
|
287
332
|
def path_args_with_nesting(last)
|
288
333
|
parents + [last]
|
289
334
|
end
|
290
|
-
|
335
|
+
|
291
336
|
# Uses the parent entry (if any) to constrain the model scope.
|
292
337
|
def model_scope_with_nesting
|
293
338
|
if parent.present?
|
@@ -296,13 +341,13 @@ class ListController < ApplicationController
|
|
296
341
|
model_scope_without_nesting
|
297
342
|
end
|
298
343
|
end
|
299
|
-
|
344
|
+
|
300
345
|
# The model scope for the current parent resource.
|
301
346
|
def parent_scope
|
302
347
|
parent.send(model_class.name.underscore.pluralize)
|
303
348
|
end
|
304
349
|
end
|
305
|
-
|
350
|
+
|
306
351
|
include Nesting
|
307
|
-
|
308
|
-
end
|
352
|
+
|
353
|
+
end
|
@@ -4,7 +4,7 @@
|
|
4
4
|
module CrudHelper
|
5
5
|
|
6
6
|
# Renders a generic form for the current entry with :default_attrs or the
|
7
|
-
# given attribute array, using the StandardFormBuilder. An options hash
|
7
|
+
# given attribute array, using the StandardFormBuilder. An options hash
|
8
8
|
# may be given as the last argument.
|
9
9
|
# If a block is given, a custom form may be rendered and attrs is ignored.
|
10
10
|
def crud_form(*attrs, &block)
|
@@ -12,7 +12,7 @@ module CrudHelper
|
|
12
12
|
standard_form(path_args(entry), *attrs, &block)
|
13
13
|
end
|
14
14
|
|
15
|
-
# Create a table of the
|
15
|
+
# Create a table of the entries with the default or
|
16
16
|
# the passed attributes in its columns. An options hash may be given
|
17
17
|
# as the last argument.
|
18
18
|
def crud_table(*attrs, &block)
|
@@ -42,7 +42,7 @@ module CrudHelper
|
|
42
42
|
# Action link to edit inside a table.
|
43
43
|
# A block may be given to define the link path for the row entry.
|
44
44
|
def action_col_edit(table, &block)
|
45
|
-
action_col(table) do |e|
|
45
|
+
action_col(table) do |e|
|
46
46
|
path = action_path(e, &block)
|
47
47
|
link_table_action('pencil', path.is_a?(String) ? path : edit_polymorphic_path(path))
|
48
48
|
end
|
@@ -61,14 +61,14 @@ module CrudHelper
|
|
61
61
|
# Generic action link inside a table.
|
62
62
|
def link_table_action(icon, url, html_options = {})
|
63
63
|
add_css_class html_options, "icon-#{icon}"
|
64
|
-
|
64
|
+
link_to('', url, html_options)
|
65
65
|
end
|
66
66
|
|
67
67
|
# Defines a column with an action link.
|
68
68
|
def action_col(table, &block)
|
69
|
-
|
69
|
+
table.col('', :class => 'action', &block)
|
70
70
|
end
|
71
|
-
|
71
|
+
|
72
72
|
######## ACTION LINKS ###################################################### :nodoc:
|
73
73
|
|
74
74
|
# Standard link action to the show page of a given record.
|
@@ -107,7 +107,7 @@ module CrudHelper
|
|
107
107
|
path ||= path_args(model_class)
|
108
108
|
link_action ti(:"link.add"), 'plus', path.is_a?(String) ? path : new_polymorphic_path(path, url_options)
|
109
109
|
end
|
110
|
-
|
110
|
+
|
111
111
|
private
|
112
112
|
|
113
113
|
# If a block is given, call it to get the path for the current row entry.
|
@@ -115,12 +115,12 @@ module CrudHelper
|
|
115
115
|
def action_path(e, &block)
|
116
116
|
block_given? ? yield(e) : path_args(e)
|
117
117
|
end
|
118
|
-
|
118
|
+
|
119
119
|
# Returns default attrs for a crud table if no others are passed.
|
120
120
|
def attrs_or_default(attrs)
|
121
|
-
|
122
|
-
|
123
|
-
|
121
|
+
options = attrs.extract_options!
|
122
|
+
attrs = yield if attrs.blank?
|
123
|
+
attrs << options
|
124
124
|
end
|
125
125
|
|
126
126
|
end
|
@@ -3,25 +3,25 @@
|
|
3
3
|
# is included in CrudController.
|
4
4
|
module ListHelper
|
5
5
|
|
6
|
-
# Create a table of the
|
6
|
+
# Create a table of the entries with the default or
|
7
7
|
# the passed attributes in its columns. An options hash may be given
|
8
8
|
# as the last argument.
|
9
9
|
def list_table(*attrs, &block)
|
10
10
|
options = attrs.extract_options!
|
11
11
|
# only use default attrs if no attrs and no block are given
|
12
12
|
attributes = (block_given? || attrs.present?) ? attrs : default_attrs
|
13
|
-
table(
|
14
|
-
|
15
|
-
|
13
|
+
table(entries, options) do |t|
|
14
|
+
t.sortable_attrs(*attributes)
|
15
|
+
yield t if block_given?
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
# The default attributes to use in attrs, list and form partials.
|
20
20
|
# These are all defined attributes except certain special ones like 'id' or 'position'.
|
21
|
-
def default_attrs
|
21
|
+
def default_attrs
|
22
22
|
attrs = model_class.column_names.collect(&:to_sym)
|
23
23
|
attrs - [:id, :position, :password]
|
24
24
|
end
|
25
|
-
|
26
|
-
|
25
|
+
|
26
|
+
|
27
27
|
end
|
@@ -9,15 +9,14 @@ class StandardFormBuilder < ActionView::Helpers::FormBuilder
|
|
9
9
|
|
10
10
|
attr_reader :template
|
11
11
|
|
12
|
-
delegate :association, :column_type, :column_property, :captionize,
|
13
|
-
:content_tag, :
|
12
|
+
delegate :association, :column_type, :column_property, :captionize, :ta,
|
13
|
+
:content_tag, :safe_join, :capture, :add_css_class, :assoc_and_id_attr,
|
14
|
+
:to => :template
|
14
15
|
|
15
16
|
# Render multiple input fields together with a label for the given attributes.
|
16
17
|
def labeled_input_fields(*attrs)
|
17
18
|
options = attrs.extract_options!
|
18
|
-
attrs
|
19
|
-
labeled_input_field(a, options.clone)
|
20
|
-
end.join("\n").html_safe
|
19
|
+
safe_join(attrs) { |a| labeled_input_field(a, options.clone) }
|
21
20
|
end
|
22
21
|
|
23
22
|
# Render a corresponding input field for the given attribute.
|
@@ -27,8 +26,10 @@ class StandardFormBuilder < ActionView::Helpers::FormBuilder
|
|
27
26
|
type = column_type(@object, attr)
|
28
27
|
if type == :text
|
29
28
|
text_area(attr, html_options)
|
30
|
-
elsif
|
29
|
+
elsif association_kind?(attr, type, :belongs_to)
|
31
30
|
belongs_to_field(attr, html_options)
|
31
|
+
elsif association_kind?(attr, type, :has_and_belongs_to_many, :has_many)
|
32
|
+
has_many_field(attr, html_options)
|
32
33
|
elsif attr.to_s.include?('password')
|
33
34
|
password_field(attr, html_options)
|
34
35
|
else
|
@@ -43,11 +44,10 @@ class StandardFormBuilder < ActionView::Helpers::FormBuilder
|
|
43
44
|
|
44
45
|
# Render a number field.
|
45
46
|
def number_field(attr, html_options = {})
|
46
|
-
add_css_class html_options, 'span1'
|
47
47
|
html_options[:size] ||= 10
|
48
48
|
super(attr, html_options)
|
49
49
|
end
|
50
|
-
|
50
|
+
|
51
51
|
# Render a standard string field with column contraints.
|
52
52
|
def string_field(attr, html_options = {})
|
53
53
|
html_options[:maxlength] ||= column_property(@object, attr, :limit)
|
@@ -80,22 +80,19 @@ class StandardFormBuilder < ActionView::Helpers::FormBuilder
|
|
80
80
|
|
81
81
|
# Render a field to select a date. You might want to customize this.
|
82
82
|
def date_field(attr, html_options = {})
|
83
|
-
add_css_class html_options, 'span1'
|
84
83
|
date_select(attr, {}, html_options)
|
85
84
|
end
|
86
85
|
|
87
86
|
# Render a field to enter a time. You might want to customize this.
|
88
87
|
def time_field(attr, html_options = {})
|
89
|
-
add_css_class html_options, 'span1'
|
90
88
|
time_select(attr, {}, html_options)
|
91
89
|
end
|
92
|
-
|
90
|
+
|
93
91
|
# Render a field to enter a date and time. You might want to customize this.
|
94
92
|
def datetime_field(attr, html_options = {})
|
95
|
-
add_css_class html_options, 'span1'
|
96
93
|
datetime_select(attr, {}, html_options)
|
97
94
|
end
|
98
|
-
|
95
|
+
|
99
96
|
# Render a select element for a :belongs_to association defined by attr.
|
100
97
|
# Use additional html_options for the select element.
|
101
98
|
# To pass a custom element list, specify the list with the :list key or
|
@@ -103,24 +100,52 @@ class StandardFormBuilder < ActionView::Helpers::FormBuilder
|
|
103
100
|
def belongs_to_field(attr, html_options = {})
|
104
101
|
list = association_entries(attr, html_options)
|
105
102
|
if list.present?
|
106
|
-
collection_select(attr,
|
103
|
+
collection_select(attr,
|
104
|
+
list,
|
105
|
+
:id,
|
106
|
+
:to_s,
|
107
|
+
html_options[:multiple] ? {} : select_options(attr),
|
108
|
+
html_options)
|
107
109
|
else
|
108
110
|
ta(:none_available, association(@object, attr))
|
109
111
|
end
|
110
112
|
end
|
111
113
|
|
114
|
+
# Render a multi select element for a :has_many or :has_and_belongs_to_many
|
115
|
+
# association defined by attr.
|
116
|
+
# Use additional html_options for the select element.
|
117
|
+
# To pass a custom element list, specify the list with the :list key or
|
118
|
+
# define an instance variable with the pluralized name of the association.
|
119
|
+
def has_many_field(attr, html_options = {})
|
120
|
+
html_options[:multiple] = true
|
121
|
+
add_css_class(html_options, 'multiselect')
|
122
|
+
belongs_to_field(attr, html_options)
|
123
|
+
end
|
124
|
+
|
112
125
|
# Renders a marker if the given attr has to be present.
|
113
126
|
def required_mark(attr)
|
114
127
|
required?(attr) ? REQUIRED_MARK : ''
|
115
128
|
end
|
116
129
|
|
117
130
|
# Render a label for the given attribute with the passed field html section.
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
131
|
+
# The following parameters may be specified:
|
132
|
+
# labeled(:attr) { #content }
|
133
|
+
# labeled(:attr, content)
|
134
|
+
# labeled(:attr, 'Caption') { #content }
|
135
|
+
# labeled(:attr, 'Caption', content)
|
136
|
+
def labeled(attr, caption_or_content = nil, content = nil, &block)
|
137
|
+
if block_given?
|
138
|
+
content = capture(&block)
|
139
|
+
elsif content.nil?
|
140
|
+
content = caption_or_content
|
141
|
+
caption_or_content = nil
|
142
|
+
end
|
143
|
+
caption_or_content ||= captionize(attr, @object.class)
|
144
|
+
|
145
|
+
content_tag(:div, :class => "control-group#{' error' if @object.errors.has_key?(attr)}") do
|
146
|
+
label(attr, caption_or_content, :class => 'control-label') +
|
147
|
+
content_tag(:div, content, :class => 'controls')
|
148
|
+
end
|
124
149
|
end
|
125
150
|
|
126
151
|
# Depending if the given attribute must be present, return
|
@@ -130,7 +155,7 @@ class StandardFormBuilder < ActionView::Helpers::FormBuilder
|
|
130
155
|
required?(attr) ? { :prompt => ta(:please_select, assoc) } :
|
131
156
|
{ :include_blank => ta(:no_entry, assoc) }
|
132
157
|
end
|
133
|
-
|
158
|
+
|
134
159
|
# Dispatch methods starting with 'labeled_' to render a label and the corresponding
|
135
160
|
# input field. E.g. labeled_boolean_field(:checked, :class => 'bold')
|
136
161
|
def method_missing(name, *args)
|
@@ -148,11 +173,11 @@ class StandardFormBuilder < ActionView::Helpers::FormBuilder
|
|
148
173
|
|
149
174
|
protected
|
150
175
|
|
151
|
-
# Returns true if attr is a non-polymorphic
|
152
|
-
#
|
153
|
-
def
|
176
|
+
# Returns true if attr is a non-polymorphic association.
|
177
|
+
# If one or more macros are given, the association must be of this kind.
|
178
|
+
def association_kind?(attr, type, *macros)
|
154
179
|
if type == :integer || type.nil?
|
155
|
-
assoc = association(@object, attr,
|
180
|
+
assoc = association(@object, attr, *macros)
|
156
181
|
assoc.present? && assoc.options[:polymorphic].nil?
|
157
182
|
else
|
158
183
|
false
|
@@ -177,10 +202,13 @@ class StandardFormBuilder < ActionView::Helpers::FormBuilder
|
|
177
202
|
# Returns true if the given attribute must be present.
|
178
203
|
def required?(attr)
|
179
204
|
attr = attr.to_s
|
180
|
-
attr, attr_id = attr
|
205
|
+
attr, attr_id = assoc_and_id_attr(attr)
|
181
206
|
validators = @object.class.validators_on(attr) +
|
182
207
|
@object.class.validators_on(attr_id)
|
183
|
-
validators.any?
|
208
|
+
validators.any? do |v|
|
209
|
+
v.kind == :presence &&
|
210
|
+
!v.options.key?(:if) && !v.options.key?(:unless)
|
211
|
+
end
|
184
212
|
end
|
185
213
|
|
186
214
|
private
|