bureaucrat 0.0.3 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,256 +1,297 @@
1
- require 'bureaucrat/utils'
2
- require 'bureaucrat/validation'
3
- require 'bureaucrat/widgets'
4
- require 'bureaucrat/forms'
5
-
6
- # TODO: needs more testing
7
1
  module Bureaucrat
8
- module Formsets
9
- TOTAL_FORM_COUNT = :'TOTAL_FORMS'
10
- INITIAL_FORM_COUNT = :'INITIAL_FORMS'
11
- ORDERING_FIELD_NAME = :'ORDER'
12
- DELETION_FIELD_NAME = :'DELETE'
13
-
14
- class ManagementForm < Forms::Form
15
- include Fields
16
- include Widgets
17
-
18
- field TOTAL_FORM_COUNT, IntegerField.new(:widget => HiddenInput)
19
- field INITIAL_FORM_COUNT, IntegerField.new(:widget => HiddenInput)
20
- end
21
-
22
- class BaseFormSet
23
- include Utils
24
- include Validation
25
- include Fields
26
- include Forms
27
-
28
- def self.default_prefix
29
- 'form'
2
+ module Formsets
3
+ TOTAL_FORM_COUNT = :'TOTAL_FORMS'
4
+ INITIAL_FORM_COUNT = :'INITIAL_FORMS'
5
+ MAX_NUM_FORM_COUNT = :'MAX_NUM_FORMS'
6
+ ORDERING_FIELD_NAME = :'ORDER'
7
+ DELETION_FIELD_NAME = :'DELETE'
8
+
9
+ class ManagementForm < Forms::Form
10
+ include Fields
11
+ include Widgets
12
+
13
+ field TOTAL_FORM_COUNT, IntegerField.new(widget: HiddenInput)
14
+ field INITIAL_FORM_COUNT, IntegerField.new(widget: HiddenInput)
15
+ field MAX_NUM_FORM_COUNT, IntegerField.new(widget: HiddenInput,
16
+ required: false)
30
17
  end
31
18
 
32
- attr_accessor :form, :extra, :can_order, :can_delete, :max_num
19
+ class BaseFormSet
20
+ include Utils
21
+ include Fields
22
+ include Forms
33
23
 
34
- def initialize(data=nil, options={})
35
- @is_bound = !data.nil?
36
- @prefix = options.fetch(:prefix, self.class.default_prefix)
37
- @auto_id = options.fetch(:auto_id, 'id_%s')
38
- @data = data
39
- @files = options[:files]
40
- @initial = options[:initial]
41
- @error_class = options.fetch(:error_class, ErrorList)
42
- @errors = nil
43
- @non_form_errors = nil
24
+ def self.default_prefix
25
+ 'form'
26
+ end
44
27
 
45
- construct_forms
46
- end
28
+ # All forms in this formset
29
+ attr_accessor :forms
30
+ # Form class used for this formset forms
31
+ attr_accessor :form
32
+ # Amount of extra (empty) forms on this formset
33
+ attr_accessor :extra
34
+ # Boolean that determines is this formset supports reordering
35
+ attr_accessor :can_order
36
+ # Boolean that determines is this formset supports deletion
37
+ attr_accessor :can_delete
38
+ # Maximum count of forms allowed on this formset
39
+ attr_accessor :max_num
40
+ # Prefix that will be used when rendering form fields
41
+ attr_accessor :prefix
42
+ # Error object class for this formset
43
+ attr_accessor :error_class
44
+ # Initial data for the forms in this formset. Array of Hashes of {field_name => initial_value}
45
+ attr_accessor :initial
46
+ # Data associated to this formset. Hash of {prefixed_field_name => value}
47
+ attr_accessor :data
48
+ # Format string for field id generator
49
+ attr_accessor :auto_id
50
+
51
+ def initialize(data=nil, options={})
52
+ set_defaults
53
+ @is_bound = !data.nil?
54
+ @prefix = options.fetch(:prefix, self.class.default_prefix)
55
+ @auto_id = options.fetch(:auto_id, 'id_%s')
56
+ @data = data || {}
57
+ @initial = options[:initial]
58
+ @error_class = options.fetch(:error_class, ErrorList)
59
+ @errors = nil
60
+ @non_form_errors = nil
61
+
62
+ construct_forms
63
+ end
47
64
 
48
- def to_s
49
- as_table
50
- end
65
+ def each(&block)
66
+ forms.each(&block)
67
+ end
51
68
 
52
- def management_form
53
- if @data || @files
54
- form = ManagementForm.new(@data, :auto_id => @auto_id,
55
- :prefix => @prefix)
56
- raise ValidationError.new('ManagementForm data is missing or has been tampered with') unless
57
- form.valid?
58
- else
59
- form = ManagementForm.new(nil, :auto_id => @auto_id,
60
- :prefix => @prefix,
61
- :initial => {
62
- TOTAL_FORM_COUNT => total_form_count,
63
- INITIAL_FORM_COUNT => initial_form_count
64
- })
69
+ def [](index)
70
+ forms[index]
65
71
  end
66
- form
67
- end
68
72
 
69
- def total_form_count
70
- if @data || @files
71
- management_form.cleaned_data[TOTAL_FORM_COUNT]
72
- else
73
- n = initial_form_count + self.extra
74
- (n > self.max_num && self.max_num > 0) ? self.max_num : n
73
+ def length
74
+ forms.length
75
75
  end
76
- end
77
76
 
78
- def initial_form_count
79
- if @data || @files
80
- management_form.cleaned_data[INITIAL_FORM_COUNT]
81
- else
82
- n = @initial ? @initial.length : 0
83
- (n > self.max_num && self.max_num > 0) ? self.max_num : n
77
+ def management_form
78
+ if @is_bound
79
+ form = ManagementForm.new(@data, auto_id: @auto_id,
80
+ prefix: @prefix)
81
+ unless form.valid?
82
+ msg = 'ManagementForm data is missing or has been tampered with'
83
+ raise ValidationError.new(msg)
84
+ end
85
+ else
86
+ form = ManagementForm.new(nil, auto_id: @auto_id,
87
+ prefix: @prefix,
88
+ initial: {
89
+ TOTAL_FORM_COUNT => total_form_count,
90
+ INITIAL_FORM_COUNT => initial_form_count,
91
+ MAX_NUM_FORM_COUNT => self.max_num
92
+ })
93
+ end
94
+ form
84
95
  end
85
- end
86
96
 
87
- def construct_forms
88
- @forms = (0...total_form_count).map { |i| construct_form(i) }
89
- end
97
+ def total_form_count
98
+ if @is_bound
99
+ management_form.cleaned_data[TOTAL_FORM_COUNT]
100
+ else
101
+ initial_forms = initial_form_count
102
+ total_forms = initial_form_count + self.extra
103
+
104
+ # Allow all existing related objects/inlines to be displayed,
105
+ # but don't allow extra beyond max_num.
106
+ if self.max_num > 0 && initial_forms > self.max_num
107
+ initial_forms
108
+ elsif self.max_num > 0 && total_forms > self.max_num
109
+ max_num
110
+ else
111
+ total_forms
112
+ end
113
+ end
114
+ end
90
115
 
91
- def construct_form(i, options={})
92
- defaults = {:auto_id => @auto_id, :prefix => add_prefix(i)}
93
- defaults[:files] = @files if @files
94
- defaults[:initial] = @initial[i] if @initial && @initial[i]
95
-
96
- # Allow extra forms to be empty.
97
- defaults[:empty_permitted] = true if i >= initial_form_count
98
- defaults.merge!(options)
99
- form = self.form.new(@data, defaults)
100
- add_fields(form, i)
101
- form
102
- end
116
+ def initial_form_count
117
+ if @is_bound
118
+ management_form.cleaned_data[INITIAL_FORM_COUNT]
119
+ else
120
+ n = @initial ? @initial.length : 0
103
121
 
104
- def initial_forms
105
- @forms[0, initial_form_count]
106
- end
122
+ (self.max_num > 0 && n > self.max_num) ? self.max_num : n
123
+ end
124
+ end
107
125
 
108
- def extra_forms
109
- n = initial_form_count
110
- @forms[n, @forms.length - n]
111
- end
126
+ def construct_forms
127
+ @forms = (0...total_form_count).map { |i| construct_form(i) }
128
+ end
112
129
 
113
- # Maybe this should just go away?
114
- def cleaned_data
115
- unless valid?
116
- raise NoMethodError.new("'#{self.class.name}' object has no method 'cleaned_data'")
130
+ def construct_form(i, options={})
131
+ defaults = {auto_id: @auto_id, prefix: add_prefix(i)}
132
+ defaults[:initial] = @initial[i] if @initial && @initial[i]
133
+
134
+ # Allow extra forms to be empty.
135
+ defaults[:empty_permitted] = true if i >= initial_form_count
136
+ defaults.merge!(options)
137
+ form_data = @is_bound ? @data : nil
138
+ form = self.form.new(form_data, defaults)
139
+ add_fields(form, i)
140
+ form
141
+ end
142
+
143
+ def initial_forms
144
+ @forms[0, initial_form_count]
117
145
  end
118
- @forms.collect(&:cleaned_data)
119
- end
120
146
 
121
- def deleted_forms
122
- unless valid? && self.can_delete
123
- raise NoMethodError.new("'#{self.class.name}' object has no method 'deleted_forms'")
147
+ def extra_forms
148
+ n = initial_form_count
149
+ @forms[n, @forms.length - n]
124
150
  end
125
151
 
126
- if @deleted_form_indexes.nil?
127
- @deleted_form_indexes = (0...total_form_count).select do |i|
152
+ # Maybe this should just go away?
153
+ def cleaned_data
154
+ unless valid?
155
+ raise NoMethodError.new("'#{self.class.name}' object has no method 'cleaned_data'")
156
+ end
157
+ @forms.collect(&:cleaned_data)
158
+ end
159
+
160
+ def deleted_forms
161
+ unless valid? && self.can_delete
162
+ raise NoMethodError.new("'#{self.class.name}' object has no method 'deleted_forms'")
163
+ end
164
+
165
+ if @deleted_form_indexes.nil?
166
+ @deleted_form_indexes = (0...total_form_count).select do |i|
128
167
  form = @forms[i]
129
- (i < initial_form_count || form.changed?) && form.cleaned_data[DELETION_FIELD_NAME]
168
+
169
+ if i >= initial_form_count && !form.changed?
170
+ false
171
+ else
172
+ should_delete_form?(form)
173
+ end
130
174
  end
131
- end
132
- @deleted_form_indexes.map {|i| @forms[i]}
133
- end
175
+ end
134
176
 
135
- def ordered_forms
136
- unless valid? && self.can_order
137
- raise NoMethodError.new("'#{self.class.name}' object has no method 'ordered_forms'")
177
+ @deleted_form_indexes.map {|i| @forms[i]}
138
178
  end
139
179
 
140
- if @ordering.nil?
141
- @ordering = (0...total_form_count).map do |i|
180
+ def ordered_forms
181
+ unless valid? && self.can_order
182
+ raise NoMethodError.new("'#{self.class.name}' object has no method 'ordered_forms'")
183
+ end
184
+
185
+ if @ordering.nil?
186
+ @ordering = (0...total_form_count).map do |i|
142
187
  form = @forms[i]
143
188
  next if i >= initial_form_count && !form.changed?
144
- next if self.can_delete && form.cleaned_data[DELETION_FIELD_NAME]
189
+ next if self.can_delete && should_delete_form?(form)
145
190
  [i, form.cleaned_data[ORDERING_FIELD_NAME]]
146
191
  end.compact
147
- @ordering.sort! do |a, b|
192
+ @ordering.sort! do |a, b|
148
193
  if x[1].nil? then 1
149
194
  elsif y[1].nil? then -1
150
195
  else x[1] - y[1]
151
196
  end
152
197
  end
198
+ end
199
+
200
+ @ordering.map {|i| @forms[i.first]}
153
201
  end
154
202
 
155
- @ordering.map {|i| @forms[i.first]}
156
- end
203
+ def non_form_errors
204
+ @non_form_errors || @error_class.new
205
+ end
157
206
 
158
- def non_form_errors
159
- @non_form_errors || @error_class.new
160
- end
207
+ def errors
208
+ full_clean if @errors.nil?
209
+ @errors
210
+ end
161
211
 
162
- def errors
163
- full_clean if @errors.nil?
164
- @errors
165
- end
212
+ def should_delete_form?(form)
213
+ field = form.fields[DELETION_FIELD_NAME]
214
+ raw_value = form.send(:raw_value, DELETION_FIELD_NAME)
215
+ field.clean(raw_value)
216
+ end
217
+
218
+ def valid?
219
+ return false unless @is_bound
166
220
 
167
- def valid?
168
- return false unless @is_bound
169
- forms_valid = true
170
- (0...total_form_count).each do |i|
221
+ forms_valid = true
222
+
223
+ (0...total_form_count).each do |i|
171
224
  form = @forms[i]
172
- if self.can_delete
173
- field = form.fields[DELETION_FIELD_NAME]
174
- raw_value = form.send(:raw_value, DELETION_FIELD_NAME)
175
- should_delete = field.clean(raw_value)
176
- next if should_delete
177
- end
225
+ next if self.can_delete && should_delete_form?(form)
226
+
178
227
  forms_valid = false unless errors[i].empty?
179
228
  end
180
- forms_valid && non_form_errors.empty?
181
- end
182
-
183
- def full_clean
184
- if @is_bound
185
- @errors = @forms.collect(&:errors)
186
229
 
187
- begin
188
- self.clean
189
- rescue ValidationError => e
190
- @non_form_errors = e.messages
191
- end
192
- else
193
- @errors = []
230
+ forms_valid && non_form_errors.empty?
194
231
  end
195
- end
196
232
 
197
- def clean
198
- end
233
+ def full_clean
234
+ if @is_bound
235
+ @errors = @forms.collect(&:errors)
199
236
 
200
- def add_fields(form, index)
201
- if self.can_order
202
- attrs = {:label => 'Order', :required => false}
203
- attrs[:initial] = index + 1 if index < initial_form_count
204
- form.fields[ORDERING_FIELD_NAME] = IntegerField.new(attrs)
205
- end
206
- if self.can_delete
207
- field = BooleanField.new(:label => 'Delete', :required => false)
208
- form.fields[DELETION_FIELD_NAME] = field
237
+ begin
238
+ self.clean
239
+ rescue ValidationError => e
240
+ @non_form_errors = @error_class.new(e.messages)
241
+ end
242
+ else
243
+ @errors = []
244
+ end
209
245
  end
210
- end
211
246
 
212
- def add_prefix(index)
213
- '%s-%s' % [@prefix, index]
214
- end
247
+ def clean
248
+ end
215
249
 
216
- def multipart?
217
- @forms && @forms.first.multipart?
218
- end
250
+ def add_fields(form, index)
251
+ if can_order
252
+ attrs = {label: 'Order', required: false}
253
+ attrs[:initial] = index + 1 if index && index < initial_form_count
254
+ form.fields[ORDERING_FIELD_NAME] = IntegerField.new(attrs)
255
+ end
256
+ if can_delete
257
+ field = BooleanField.new(label: 'Delete', required: false)
258
+ form.fields[DELETION_FIELD_NAME] = field
259
+ end
260
+ end
219
261
 
220
- def media
221
- @forms ? @forms.first.media : Media.new
222
- end
262
+ def add_prefix(index)
263
+ '%s-%s' % [@prefix, index]
264
+ end
223
265
 
224
- def as_table
225
- forms = @forms.map(&:as_table).join(' ')
226
- mark_safe([management_form.to_s, forms].join("\n"))
266
+ def multipart?
267
+ @forms && @forms.first.multipart?
268
+ end
227
269
  end
228
- end
229
270
 
230
271
  module_function
231
272
 
232
- def make_formset_class(form, options={})
233
- formset = options.fetch(:formset, BaseFormSet)
234
- attrs = {
235
- :form => form,
236
- :extra => options.fetch(:extra, 1),
237
- :can_order => options.fetch(:can_order, false),
238
- :can_delete => options.fetch(:can_delete, false),
239
- :max_num => options.fetch(:max_num, 0)
240
- }
241
- Class.new(formset) do
242
- attrs.each do |name, value|
243
- define_method(name) { value }
273
+ def make_formset_class(form, options={})
274
+ formset = options.fetch(:formset, BaseFormSet)
275
+
276
+ Class.new(formset) do
277
+ define_method :set_defaults do
278
+ @form = form
279
+ @extra = options.fetch(:extra, 1)
280
+ @can_order = options.fetch(:can_order, false)
281
+ @can_delete = options.fetch(:can_delete, false)
282
+ @max_num = options.fetch(:max_num, 0)
244
283
  end
284
+ private :set_defaults
245
285
  end
246
- end
286
+ end
247
287
 
248
- def all_valid?(formsets)
249
- valid = true
250
- formsets.each do |formset|
288
+ def all_valid?(formsets)
289
+ valid = true
290
+ formsets.each do |formset|
251
291
  valid = false unless formset.valid?
252
292
  end
253
- valid
254
- end
293
+ valid
294
+ end
255
295
 
256
- end; end
296
+ end
297
+ end