bureaucrat 0.0.3 → 0.10.0

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.
@@ -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