1 module Radiant
2 class Config
3 class Definition
4
5 attr_reader :empty, :default, :type, :notes, :validate_with, :select_from, :allow_blank, :allow_display, :allow_change, :units, :definer
6
7 # Configuration 'definitions' are metadata held in memory that add restriction and description to individual config entries.
8 #
9 # By default radiant's configuration machinery is open and ad-hoc: config items are just globally-accessible variables.
10 # They're created when first mentioned and then available in all parts of the application. The definition mechanism is a way
11 # to place limits on that behavior. It allows you to protect a config entry, to specify the values it can take and to
12 # validate it when it changes. In the next update it will also allow you to declare that
13 # a config item is global or site-specific.
14 #
15 # The actual defining is done by Radiant::Config#define and usually in a block like this:
16 #
17 # Radiant::Config.prepare do |config|
18 # config.namespace('users', :allow_change => true) do |users|
19 # users.define 'allow_password_reset?', :label => 'Allow password reset?'
20 # end
21 # end
22 #
23 # See the method documentation in Radiant::Config for options and conventions.
24 #
25 def initialize(options={})
26 [:empty, :default, :type, :notes, :validate_with, :select_from, :allow_blank, :allow_change, :allow_display, :units, :definer].each do |attribute|
27 instance_variable_set "@#{attribute}".to_sym, options[attribute]
28 end
29 end
30
31 # Returns true if the definition included an :empty flag, which should only be the case for the blank, unrestricting
32 # definitions created when an undefined config item is set or got.
33 #
34 def empty?
35 !!empty
36 end
37
38 # Returns true if the definition included a :type => :boolean parameter. Config entries that end in '?' are automatically
39 # considered boolean, whether a type is declared or not. config.boolean? may therefore differ from config.definition.boolean?
40 #
41 def boolean?
42 type == :boolean
43 end
44
45 # Returns true if the definition included a :select_from parameter (either as list or proc).
46 #
47 def selector?
48 !select_from.blank?
49 end
50
51 # Returns true if the definition included a :type => :integer parameter
52 def integer?
53 type == :integer
54 end
55
56 # Returns the list of possible values for this config entry in a form suitable for passing to options_for_select.
57 # if :select_from is a proc it is called first with no arguments and its return value passed through.
58 #
59 def selection
60 if selector?
61 choices = select_from
62 choices = choices.call if choices.respond_to? :call
63 choices = normalize_selection(choices)
64 choices.unshift ["",""] if allow_blank?
65 choices
66 end
67 end
68
69 # in definitions we accept anything that options_for_select would normally take
70 # here we standardises on an options array-of-arrays so that it's easier to validate input
71 #
72 def normalize_selection(choices)
73 choices = choices.to_a if Hash === choices
74 choices = choices.collect{|c| (c.is_a? Array) ? c : [c,c]}
75 end
76
77 # If the config item is a selector and :select_from specifies [name, value] pairs (as hash or array),
78 # this will return the name corresponding to the currently selected value.
79 #
80 def selected(value)
81 if value && selector? && pair = selection.find{|s| s.last == value}
82 pair.first
83 end
84 end
85
86 # Checks the supplied value against the validation rules for this definition.
87 # There are several ways in which validations might be defined or implied:
88 # * if :validate_with specifies a block, the setting object is passed to the block
89 # * if :type is :integer, we test that the supplied string resolves to a valid integer
90 # * if the config item is a selector we test that its value is one of the permitted options
91 # * if :allow_blank has been set to false, we test that the value is not blank
92 #
93 def validate(setting)
94 if allow_blank?
95 return if setting.value.blank?
96 else
97 setting.errors.add :value, :blank if setting.value.blank?
98 end
99 if validate_with.is_a? Proc
100 validate_with.call(setting)
101 end
102 if selector?
103 setting.errors.add :value, :not_permitted unless selectable?(setting.value)
104 end
105 if integer?
106 Integer(setting.value) rescue setting.errors.add :value, :not_a_number
107 end
108 end
109
110 # Returns true if the value is one of the permitted selections. Not case-sensitive.
111 def selectable?(value)
112 return true unless selector?
113 selection.map(&:last).map(&:downcase).include?(value.downcase)
114 end
115
116 # Returns true unless :allow_blank has been explicitly set to false. Defaults to true.
117 # A config item that does not allow_blank must be set or it will not be valid.
118 def allow_blank?
119 true unless allow_blank == false
120 end
121
122 # Returns true unless :allow_change has been explicitly set to false. Defaults to true.
123 # A config item that is not settable cannot be changed in the running application.
124 def settable?
125 true unless allow_change == false
126 end
127
128 # Returns true unless :allow_change has been explicitly set to false. Defaults to true.
129 # A config item that is not visible cannot be displayed in a radius tag.
130 def visible?
131 true unless allow_display == false
132 end
133
134 # Returns true if :allow_display has been explicitly set to false. Defaults to true.
135 def hidden?
136 true if allow_display == false
137 end
138
139 end
140 end
141 end
142