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,64 +1,92 @@
1
- require 'bureaucrat/fields'
2
-
3
1
  module Bureaucrat
4
- module Quickfields
5
- include Fields
6
-
7
- def hide(name)
8
- base_fields[name] = base_fields[name].dup
9
- base_fields[name].widget = Widgets::HiddenInput.new
10
- end
11
-
12
- def string(name, options={})
13
- field name, CharField.new(options)
14
- end
15
-
16
- def text(name, options={})
17
- field name, CharField.new(options.merge(:widget => Widgets::Textarea.new))
18
- end
19
-
20
- def password(name, options={})
21
- field name, CharField.new(options.merge(:widget => Widgets::PasswordInput.new))
2
+ # Shortcuts for declaring form fields
3
+ module Quickfields
4
+ include Fields
5
+
6
+ # Hide field named +name+
7
+ def hide(name)
8
+ base_fields[name] = base_fields[name].dup
9
+ base_fields[name].widget = Widgets::HiddenInput.new
10
+ end
11
+
12
+ # Delete field named +name+
13
+ def delete(name)
14
+ base_fields.delete name
15
+ end
16
+
17
+ # Declare a +CharField+ with text input widget
18
+ def string(name, options = {})
19
+ field name, CharField.new(options)
20
+ end
21
+
22
+ # Declare a +CharField+ with text area widget
23
+ def text(name, options = {})
24
+ field name, CharField.new(options.merge(widget: Widgets::Textarea.new))
25
+ end
26
+
27
+ # Declare a +CharField+ with password widget
28
+ def password(name, options = {})
29
+ field name, CharField.new(options.merge(widget: Widgets::PasswordInput.new))
30
+ end
31
+
32
+ # Declare an +IntegerField+
33
+ def integer(name, options = {})
34
+ field name, IntegerField.new(options)
35
+ end
36
+
37
+ # Declare a +BigDecimalField+
38
+ def decimal(name, options = {})
39
+ field name, BigDecimalField.new(options)
40
+ end
41
+
42
+ # Declare a +RegexField+
43
+ def regex(name, regexp, options = {})
44
+ field name, RegexField.new(regexp, options)
45
+ end
46
+
47
+ # Declare an +EmailField+
48
+ def email(name, options = {})
49
+ field name, EmailField.new(options)
50
+ end
51
+
52
+ # Declare a +FileField+
53
+ def file(name, options = {})
54
+ field name, FileField.new(options)
55
+ end
56
+
57
+ # Declare a +BooleanField+
58
+ def boolean(name, options = {})
59
+ field name, BooleanField.new(options)
60
+ end
61
+
62
+ # Declare a +NullBooleanField+
63
+ def null_boolean(name, options = {})
64
+ field name, NullBooleanField.new(options)
65
+ end
66
+
67
+ # Declare a +ChoiceField+ with +choices+
68
+ def choice(name, choices = [], options = {})
69
+ field name, ChoiceField.new(choices, options)
70
+ end
71
+
72
+ # Declare a +TypedChoiceField+ with +choices+
73
+ def typed_choice(name, choices = [], options = {})
74
+ field name, TypedChoiceField.new(choices, options)
75
+ end
76
+
77
+ # Declare a +MultipleChoiceField+ with +choices+
78
+ def multiple_choice(name, choices = [], options = {})
79
+ field name, MultipleChoiceField.new(choices, options)
80
+ end
81
+
82
+ # Declare a +ChoiceField+ using the +RadioSelect+ widget
83
+ def radio_choice(name, choices = [], options = {})
84
+ field name, ChoiceField.new(choices, options.merge(widget: Widgets::RadioSelect.new))
85
+ end
86
+
87
+ # Declare a +MultipleChoiceField+ with the +CheckboxSelectMultiple+ widget
88
+ def checkbox_multiple_choice(name, choices = [], options = {})
89
+ field name, MultipleChoiceField.new(choices, options.merge(widget: Widgets::CheckboxSelectMultiple.new))
90
+ end
22
91
  end
23
-
24
- def integer(name, options={})
25
- field name, IntegerField.new(options)
26
- end
27
-
28
- def decimal(name, options={})
29
- field name, BigDecimalField.new(options)
30
- end
31
-
32
- def regex(name, regexp, options={})
33
- field name, RegexField.new(regexp, options)
34
- end
35
-
36
- def email(name, options={})
37
- field name, EmailField.new(options)
38
- end
39
-
40
- def file(name, options={})
41
- field name, FileField.new(options)
42
- end
43
-
44
- def boolean(name, options={})
45
- field name, BooleanField.new(options)
46
- end
47
-
48
- def null_boolean(name, options={})
49
- field name, NullBooleanField.new(options)
50
- end
51
-
52
- def choice(name, choices=[], options={})
53
- field name, ChoiceField.new(choices, options)
54
- end
55
-
56
- def typed_choice(name, choices=[], options={})
57
- field name, TypedChoiceField.new(choices, options)
58
- end
59
-
60
- def multiple_choice(name, choices=[], options={})
61
- field name, MultipleChoiceField.new(choices, options)
62
- end
63
-
64
- end; end
92
+ end
@@ -0,0 +1,14 @@
1
+ module Bureaucrat
2
+ class TemporaryUploadedFile
3
+ attr_accessor :filename, :content_type, :name, :tempfile, :head
4
+
5
+ def initialize(data)
6
+ @filename = data[:filename]
7
+ @content_type = data[:content_type]
8
+ @name = data[:name]
9
+ @tempfile = data[:tempfile]
10
+ @size = @tempfile.size
11
+ @head = data[:head]
12
+ end
13
+ end
14
+ end
@@ -1,84 +1,101 @@
1
1
  module Bureaucrat
2
- module Utils
2
+ module Utils
3
+ extend self
3
4
 
4
- module SafeData
5
- end
5
+ module SafeData
6
+ end
6
7
 
7
- class SafeString < String
8
- include SafeData
8
+ class SafeString < String
9
+ include SafeData
9
10
 
10
- def +(rhs)
11
- rhs.is_a?(SafeString) ? SafeString.new(super(rhs)) : super(rhs)
11
+ def +(rhs)
12
+ rhs.is_a?(SafeString) ? SafeString.new(super(rhs)) : super(rhs)
13
+ end
12
14
  end
13
- end
14
15
 
15
- # Dumb implementation that is good enough for Forms
16
- class OrderedHash < Hash
17
- def initialize
18
- super()
19
- @ordered_keys = []
20
- end
16
+ class StringAccessHash < Hash
17
+ def initialize(other = {})
18
+ super()
19
+ update(other)
20
+ end
21
21
 
22
- def []=(key, value)
23
- super(key, value)
24
- @ordered_keys << key unless @ordered_keys.include?(key)
25
- end
22
+ def []=(key, value)
23
+ super(key.to_s, value)
24
+ end
26
25
 
27
- def each
28
- @ordered_keys.each do |key|
29
- yield key, self[key]
30
- end
31
- end
26
+ def [](key)
27
+ super(key.to_s)
28
+ end
32
29
 
33
- def initialize_copy(original)
34
- super(original)
35
- @ordered_keys = original.instance_eval('@ordered_keys').dup
30
+ def fetch(key, *args)
31
+ super(key.to_s, *args)
32
+ end
33
+
34
+ def include?(key)
35
+ super(key.to_s)
36
+ end
37
+
38
+ def update(other)
39
+ other.each_pair{|k, v| self[k] = v}
40
+ self
41
+ end
42
+
43
+ def merge(other)
44
+ dup.update(other)
45
+ end
46
+
47
+ def delete(key)
48
+ super(key.to_s)
49
+ end
36
50
  end
37
- end
38
51
 
39
- module_function
52
+ def blank_value?(value)
53
+ !value || value == ''
54
+ end
40
55
 
41
- def mark_safe(s)
42
- s.is_a?(SafeData) ? s : SafeString.new(s.to_s)
43
- end
56
+ def mark_safe(s)
57
+ s.is_a?(SafeData) ? s : SafeString.new(s.to_s)
58
+ end
44
59
 
45
- ESCAPES = {
46
- '&' => '&amp;',
47
- '<' => '&lt;',
48
- '>' => '&gt;',
49
- '"' => '&quot;',
50
- "'" => '&#39;'
51
- }
52
- def escape(html)
53
- mark_safe(html.gsub(/[&<>"']/) {|match| ESCAPES[match]})
54
- end
60
+ ESCAPES = {
61
+ '&' => '&amp;',
62
+ '<' => '&lt;',
63
+ '>' => '&gt;',
64
+ '"' => '&quot;',
65
+ "'" => '&#39;'
66
+ }
67
+ def escape(html)
68
+ mark_safe(html.gsub(/[&<>"']/) {|match| ESCAPES[match]})
69
+ end
55
70
 
56
- def conditional_escape(html)
57
- html.is_a?(SafeData) ? html : escape(html)
58
- end
71
+ def conditional_escape(html)
72
+ html.is_a?(SafeData) ? html : escape(html)
73
+ end
59
74
 
60
- def flatatt(attrs)
61
- attrs.map {|k, v| " #{k}=\"#{conditional_escape(v)}\""}.join('')
62
- end
75
+ def flatatt(attrs)
76
+ attrs.map {|k, v| " #{k}=\"#{conditional_escape(v)}\""}.join('')
77
+ end
63
78
 
64
- def format_string(string, values)
65
- output = string.dup
66
- values.each_pair do |variable, value|
79
+ def format_string(string, values)
80
+ output = string.dup
81
+ values.each_pair do |variable, value|
67
82
  output.gsub!(/%\(#{variable}\)s/, value.to_s)
68
83
  end
69
- output
70
- end
84
+ output
85
+ end
71
86
 
72
- def pretty_name(name)
73
- name.to_s.capitalize.gsub(/_/, ' ')
74
- end
87
+ def pretty_name(name)
88
+ name.to_s.capitalize.gsub(/_/, ' ')
89
+ end
75
90
 
76
- def make_float(value)
77
- value += '0' if value.is_a?(String) && value != '.' && value[-1,1] == '.'
78
- Float(value)
79
- end
91
+ def make_float(value)
92
+ value += '0' if value.is_a?(String) && value != '.' && value[-1,1] == '.'
93
+ Float(value)
94
+ end
95
+
96
+ def make_bool(value)
97
+ !(value.respond_to?(:empty?) ? value.empty? : [0, nil, false].include?(value))
98
+ end
80
99
 
81
- def make_bool(value)
82
- !(value.respond_to?(:empty?) ? value.empty? : [0, nil, false].include?(value))
83
100
  end
84
- end; end
101
+ end
@@ -0,0 +1,163 @@
1
+ module Bureaucrat
2
+ module Validators
3
+ def empty_value?(value)
4
+ value.nil? || value == '' || value == [] || value == {}
5
+ end
6
+ module_function :empty_value?
7
+
8
+ class RegexValidator
9
+ attr_accessor :regex, :message, :code
10
+
11
+ def initialize(options = {})
12
+ @regex = Regexp.new(options.fetch(:regex, ''))
13
+ @message = options.fetch(:message, 'Enter a valid value.')
14
+ @code = options.fetch(:code, :invalid)
15
+ end
16
+
17
+ # Validates that the input validates the regular expression
18
+ def call(value)
19
+ if regex !~ value
20
+ raise ValidationError.new(@message, code, regex: regex)
21
+ end
22
+ end
23
+ end
24
+
25
+ ValidateInteger = lambda do |value|
26
+ begin
27
+ Integer(value)
28
+ rescue ArgumentError
29
+ raise ValidationError.new('')
30
+ end
31
+ end
32
+
33
+ # Original from Django's EmailField:
34
+ # email_re = re.compile(
35
+ # r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom
36
+ # r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"' # quoted-string
37
+ # r')@(?:[A-Z0-9]+(?:-*[A-Z0-9]+)*\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain
38
+ EMAIL_RE = /
39
+ (^[-!#\$%&'*+\/=?^_`{}|~0-9A-Z]+(\.[-!#\$%&'*+\/=?^_`{}|~0-9A-Z]+)*
40
+ |^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"
41
+ )@(?:[A-Z0-9]+(?:-*[A-Z0-9]+)*\.)+[A-Z]{2,6}$
42
+ /xi
43
+
44
+ ValidateEmail =
45
+ RegexValidator.new(regex: EMAIL_RE,
46
+ message: 'Enter a valid e-mail address.')
47
+
48
+ SLUG_RE = /^[-\w]+$/
49
+
50
+ ValidateSlug =
51
+ RegexValidator.new(regex: SLUG_RE,
52
+ message: "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens.")
53
+
54
+ IPV4_RE = /^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$/
55
+
56
+ IPV4Validator =
57
+ RegexValidator.new(regex: IPV4_RE,
58
+ message: 'Enter a valid IPv4 address.')
59
+
60
+ COMMA_SEPARATED_INT_LIST_RE = /^[\d,]+$/
61
+
62
+ ValidateCommaSeparatedIntegerList =
63
+ RegexValidator.new(regex: COMMA_SEPARATED_INT_LIST_RE,
64
+ message: 'Enter only digits separated by commas.',
65
+ code: :invalid)
66
+
67
+ class BaseValidator
68
+ def initialize(limit_value)
69
+ @limit_value = limit_value
70
+ end
71
+
72
+ def message
73
+ 'Ensure this value is %(limit_value)s (it is %(show_value)s).'
74
+ end
75
+
76
+ def code
77
+ :limit_value
78
+ end
79
+
80
+ def compare(a, b)
81
+ a.object_id != b.object_id
82
+ end
83
+
84
+ def clean(x)
85
+ x
86
+ end
87
+
88
+ def call(value)
89
+ cleaned = clean(value)
90
+ params = { limit_value: @limit_value, show_value: cleaned }
91
+
92
+ if compare(cleaned, @limit_value)
93
+ msg = Utils.format_string(message, params)
94
+ raise ValidationError.new(msg, code, params)
95
+ end
96
+ end
97
+ end
98
+
99
+ class MaxValueValidator < BaseValidator
100
+ def message
101
+ 'Ensure this value is less than or equal to %(limit_value)s.'
102
+ end
103
+
104
+ def code
105
+ :max_value
106
+ end
107
+
108
+ def compare(a, b)
109
+ a > b
110
+ end
111
+ end
112
+
113
+ class MinValueValidator < BaseValidator
114
+ def message
115
+ 'Ensure this value is greater than or equal to %(limit_value)s.'
116
+ end
117
+
118
+ def code
119
+ :min_value
120
+ end
121
+
122
+ def compare(a, b)
123
+ a < b
124
+ end
125
+ end
126
+
127
+ class MinLengthValidator < BaseValidator
128
+ def message
129
+ 'Ensure this value has at least %(limit_value)d characters (it has %(show_value)d).'
130
+ end
131
+
132
+ def code
133
+ :min_length
134
+ end
135
+
136
+ def compare(a, b)
137
+ a < b
138
+ end
139
+
140
+ def clean(x)
141
+ x.length
142
+ end
143
+ end
144
+
145
+ class MaxLengthValidator < BaseValidator
146
+ def message
147
+ 'Ensure this value has at most %(limit_value)d characters (it has %(show_value)d).'
148
+ end
149
+
150
+ def code
151
+ :max_length
152
+ end
153
+
154
+ def compare(a, b)
155
+ a > b
156
+ end
157
+
158
+ def clean(x)
159
+ x.length
160
+ end
161
+ end
162
+ end
163
+ end