preferences 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +15 -0
- data/README +49 -15
- data/Rakefile +2 -2
- data/app/models/preference.rb +22 -4
- data/lib/preferences.rb +99 -52
- data/test/app_root/log/in_memory.log +8746 -0
- data/test/functional/preferences_test.rb +124 -13
- data/test/test_helper.rb +1 -1
- data/test/unit/preference_test.rb +69 -23
- metadata +13 -13
data/CHANGELOG
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
*SVN*
|
2
2
|
|
3
|
+
*0.1.0* (June 19th, 2008)
|
4
|
+
|
5
|
+
* Avoid string evaluation for dynamic methods
|
6
|
+
|
7
|
+
* Return hashes for the preference_values, e.g.
|
8
|
+
|
9
|
+
user.preference_values # => {'color' => 'red', 'number' => 11, 'website' => {'background' => 'white', 'foreground' => 'black'}}
|
10
|
+
user.preferences_values('website') # => {'background' => 'white', 'foreground' => 'black'}
|
11
|
+
|
12
|
+
* Add more generic grouping of preferences than with just other records, e.g.
|
13
|
+
|
14
|
+
user.preferred_color('cars')
|
15
|
+
|
16
|
+
* Remove support for an options hash when specifying :for associations for preference
|
17
|
+
|
3
18
|
*0.0.1* (May 10th, 2008)
|
4
19
|
|
5
20
|
* Initial public release
|
data/README
CHANGED
@@ -8,14 +8,18 @@ Wiki
|
|
8
8
|
|
9
9
|
* http://wiki.pluginaweek.org/Preferences
|
10
10
|
|
11
|
-
|
11
|
+
API
|
12
12
|
|
13
|
-
* http://
|
13
|
+
* http://api.pluginaweek.org/preferences
|
14
14
|
|
15
15
|
Development
|
16
16
|
|
17
17
|
* http://dev.pluginaweek.org/browser/trunk/preferences
|
18
18
|
|
19
|
+
Source
|
20
|
+
|
21
|
+
* http://svn.pluginaweek.org/trunk/preferences
|
22
|
+
|
19
23
|
== Description
|
20
24
|
|
21
25
|
Preferences for models within an application, such as for users, is a pretty
|
@@ -23,11 +27,11 @@ common idiom. Although the rule of thumb is to keep the number of preferences
|
|
23
27
|
available to a minimum, sometimes it's necessary if you want users to be able to
|
24
28
|
disable things like e-mail notifications.
|
25
29
|
|
26
|
-
Generally, basic preferences can be
|
30
|
+
Generally, basic preferences can be accomplished through simple designs, such as
|
27
31
|
additional columns or a bit vector described and implemented by preference_fu[http://agilewebdevelopment.com/plugins/preferencefu].
|
28
32
|
However, as you find the need for non-binary preferences and the number of
|
29
33
|
preferences becomes unmanageable as individual columns in the database, the next
|
30
|
-
step is often to create a
|
34
|
+
step is often to create a separate "preferences" table. This is where the +preferences+
|
31
35
|
plugin comes in.
|
32
36
|
|
33
37
|
+preferences+ encapsulates this design by hiding the fact that preferences are
|
@@ -56,7 +60,7 @@ In the above model, 5 preferences have been defined:
|
|
56
60
|
* language
|
57
61
|
|
58
62
|
For each preference, a data type and default value can be specified. If no
|
59
|
-
data type is given, it's considered a boolean value. If
|
63
|
+
data type is given, it's considered a boolean value. If no default value is
|
60
64
|
given, the default is assumed to be nil.
|
61
65
|
|
62
66
|
=== Accessing preferences
|
@@ -84,7 +88,7 @@ Writer methods:
|
|
84
88
|
==== Generic methods
|
85
89
|
|
86
90
|
Each shortcut method is essentially a wrapper for the various generic methods
|
87
|
-
|
91
|
+
shown below:
|
88
92
|
|
89
93
|
Query method:
|
90
94
|
user.prefers?(:hot_salsa) # => false
|
@@ -100,21 +104,32 @@ Write method:
|
|
100
104
|
|
101
105
|
=== Accessing all preferences
|
102
106
|
|
103
|
-
To get the collection of all preferences for a particular
|
104
|
-
the +preferences+ has_many association which is automatically
|
107
|
+
To get the collection of all custom, stored preferences for a particular record,
|
108
|
+
you can access the +preferences+ has_many association which is automatically
|
109
|
+
generated:
|
105
110
|
|
106
111
|
user.preferences
|
107
112
|
|
108
|
-
|
113
|
+
In addition to this, you can get a hash of all stored preferences *and* default
|
114
|
+
preferences, by accessing the +preference_values+ helper:
|
115
|
+
|
116
|
+
user.preference_values # => {"language"=>"English", "color"=>nil}
|
117
|
+
|
118
|
+
This hash will contain the value for every preference that has been defined for
|
119
|
+
the model, whether that's the default value or one that has been previously
|
120
|
+
stored.
|
121
|
+
|
122
|
+
=== Grouping preferences
|
109
123
|
|
110
124
|
In addition to defining generic preferences for the owning record, you can also
|
111
|
-
|
125
|
+
group preferences by ActiveRecord objects or basic names. This is best shown
|
126
|
+
through an example:
|
112
127
|
|
113
128
|
user = User.find(:first)
|
114
129
|
car = Car.find(:first)
|
115
130
|
|
116
|
-
user.preferred_color = 'red',
|
117
|
-
# user.set_preference(:color, 'red',
|
131
|
+
user.preferred_color = 'red', car
|
132
|
+
# user.set_preference(:color, 'red', car) # The generic way
|
118
133
|
|
119
134
|
This will create a preference for the color "red" for the given car. In this way,
|
120
135
|
you can have "color" preferences for different records.
|
@@ -122,8 +137,22 @@ you can have "color" preferences for different records.
|
|
122
137
|
To access the preference for a particular record, you can use the same accessor
|
123
138
|
methods as before:
|
124
139
|
|
125
|
-
user.preferred_color(
|
126
|
-
# user.preferred(:color,
|
140
|
+
user.preferred_color(car)
|
141
|
+
# user.preferred(:color, car) # The generic way
|
142
|
+
|
143
|
+
In addition to grouping preferences for a particular record, you can also group
|
144
|
+
preferences by name. For example,
|
145
|
+
|
146
|
+
user = User.find(:first)
|
147
|
+
|
148
|
+
user.preferred_color = 'red', 'automobiles'
|
149
|
+
user.preferred_color = 'tan', 'clothing'
|
150
|
+
|
151
|
+
user.preferred_color('automobiles') # => "red"
|
152
|
+
user.preferred_color('clothing') # => "tan"
|
153
|
+
|
154
|
+
user.preference_values # => {"color"=>nil, "automobiles"=>{"color"=>"red"}, "clothing=>{"color=>"tan"}}
|
155
|
+
user.preference_values('automobiles') # => {"color"=>"red"}
|
127
156
|
|
128
157
|
=== Saving preferences
|
129
158
|
|
@@ -134,13 +163,18 @@ are treated in a similar fashion to attributes. For example,
|
|
134
163
|
user.attributes = {:preferred_color => 'red'}
|
135
164
|
user.save!
|
136
165
|
|
137
|
-
Preferences are stored in a separate table
|
166
|
+
Preferences are stored in a separate table called "preferences".
|
138
167
|
|
139
168
|
== Testing
|
140
169
|
|
141
170
|
Before you can run any tests, the following gem must be installed:
|
142
171
|
* plugin_test_helper[http://wiki.pluginaweek.org/Plugin_test_helper]
|
143
172
|
|
173
|
+
To run against a specific version of Rails:
|
174
|
+
|
175
|
+
rake test RAILS_FRAMEWORK_ROOT=/path/to/rails
|
176
|
+
|
144
177
|
== Dependencies
|
145
178
|
|
179
|
+
* Rails 2.1 or later
|
146
180
|
* plugins_plus[http://wiki.pluginaweek.org/Plugins_plus]
|
data/Rakefile
CHANGED
@@ -4,7 +4,7 @@ require 'rake/gempackagetask'
|
|
4
4
|
require 'rake/contrib/sshpublisher'
|
5
5
|
|
6
6
|
PKG_NAME = 'preferences'
|
7
|
-
PKG_VERSION = '0.0
|
7
|
+
PKG_VERSION = '0.1.0'
|
8
8
|
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
9
9
|
RUBY_FORGE_PROJECT = 'pluginaweek'
|
10
10
|
|
@@ -68,7 +68,7 @@ desc 'Publish the release files to RubyForge.'
|
|
68
68
|
task :release => [:gem, :package] do
|
69
69
|
require 'rubyforge'
|
70
70
|
|
71
|
-
ruby_forge = RubyForge.new
|
71
|
+
ruby_forge = RubyForge.new.configure
|
72
72
|
ruby_forge.login
|
73
73
|
|
74
74
|
%w( gem tgz zip ).each do |ext|
|
data/app/models/preference.rb
CHANGED
@@ -10,15 +10,27 @@
|
|
10
10
|
class Preference < ActiveRecord::Base
|
11
11
|
belongs_to :owner,
|
12
12
|
:polymorphic => true
|
13
|
-
belongs_to :
|
13
|
+
belongs_to :group,
|
14
14
|
:polymorphic => true
|
15
15
|
|
16
16
|
validates_presence_of :attribute,
|
17
17
|
:owner_id,
|
18
18
|
:owner_type
|
19
|
-
validates_presence_of :
|
20
|
-
|
21
|
-
|
19
|
+
validates_presence_of :group_type,
|
20
|
+
:if => :group_id?
|
21
|
+
|
22
|
+
class << self
|
23
|
+
# Splits the given group into its corresponding id and type
|
24
|
+
def split_group(group = nil)
|
25
|
+
if group.is_a?(ActiveRecord::Base)
|
26
|
+
group_id, group_type = group.id, group.class.base_class.name.to_s
|
27
|
+
else
|
28
|
+
group_id, group_type = nil, group
|
29
|
+
end
|
30
|
+
|
31
|
+
return group_id, group_type
|
32
|
+
end
|
33
|
+
end
|
22
34
|
|
23
35
|
# The definition for the attribute
|
24
36
|
def definition
|
@@ -31,4 +43,10 @@ class Preference < ActiveRecord::Base
|
|
31
43
|
value = definition.type_cast(value) if definition
|
32
44
|
value
|
33
45
|
end
|
46
|
+
|
47
|
+
# Only searches for the group record if the group id is specified
|
48
|
+
def group_with_optional_lookup
|
49
|
+
group_id ? group_without_optional_lookup : group_type
|
50
|
+
end
|
51
|
+
alias_method_chain :group, :optional_lookup
|
34
52
|
end
|
data/lib/preferences.rb
CHANGED
@@ -79,22 +79,26 @@ module PluginAWeek #:nodoc:
|
|
79
79
|
# Example:
|
80
80
|
#
|
81
81
|
# user = User.find(:first)
|
82
|
-
# user.prefers_notifications?
|
83
|
-
# user.prefers_color?
|
84
|
-
# user.preferred_color
|
85
|
-
# user.preferred_color = 'blue'
|
82
|
+
# user.prefers_notifications? # => false
|
83
|
+
# user.prefers_color? # => true
|
84
|
+
# user.preferred_color # => 'red'
|
85
|
+
# user.preferred_color = 'blue' # => 'blue'
|
86
86
|
#
|
87
87
|
# user.prefers_notifications = true
|
88
88
|
#
|
89
89
|
# car = Car.find(:first)
|
90
|
-
# user.preferred_color = 'red',
|
91
|
-
# user.preferred_color(
|
92
|
-
# user.prefers_color?(
|
90
|
+
# user.preferred_color = 'red', car # => 'red'
|
91
|
+
# user.preferred_color(car) # => 'red'
|
92
|
+
# user.prefers_color?(car) # => true
|
93
93
|
#
|
94
94
|
# user.save! # => true
|
95
95
|
def preference(attribute, *args)
|
96
96
|
unless included_modules.include?(InstanceMethods)
|
97
97
|
class_inheritable_hash :preference_definitions
|
98
|
+
self.preference_definitions = {}
|
99
|
+
|
100
|
+
class_inheritable_hash :default_preference_values
|
101
|
+
self.default_preference_values = {}
|
98
102
|
|
99
103
|
has_many :preferences,
|
100
104
|
:as => :owner
|
@@ -107,73 +111,120 @@ module PluginAWeek #:nodoc:
|
|
107
111
|
# Create the definition
|
108
112
|
attribute = attribute.to_s
|
109
113
|
definition = PreferenceDefinition.new(attribute, *args)
|
110
|
-
self.preference_definitions =
|
114
|
+
self.preference_definitions[attribute] = definition
|
115
|
+
self.default_preference_values[attribute] = definition.default_value
|
111
116
|
|
112
117
|
# Create short-hand helper methods, making sure that the attribute
|
113
118
|
# is method-safe in terms of what characters are allowed
|
114
119
|
attribute = attribute.gsub(/[^A-Za-z0-9_-]/, '').underscore
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
120
|
+
|
121
|
+
# Query lookup
|
122
|
+
define_method("prefers_#{attribute}?") do |*group|
|
123
|
+
prefers?(attribute, group.first)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Writer
|
127
|
+
define_method("prefers_#{attribute}=") do |*args|
|
128
|
+
set_preference(*([attribute] + [args].flatten))
|
129
|
+
end
|
130
|
+
alias_method "preferred_#{attribute}=", "prefers_#{attribute}="
|
131
|
+
|
132
|
+
# Reader
|
133
|
+
define_method("preferred_#{attribute}") do |*group|
|
134
|
+
preferred(attribute, group.first)
|
135
|
+
end
|
130
136
|
|
131
137
|
definition
|
132
138
|
end
|
133
139
|
end
|
134
140
|
|
135
141
|
module InstanceMethods
|
142
|
+
# Finds all preferences, including defaults, for the current record. If
|
143
|
+
# any custom group preferences have been stored, then this will include
|
144
|
+
# all default preferences within that particular group.
|
145
|
+
#
|
146
|
+
# == Examples
|
147
|
+
#
|
148
|
+
# A user with no stored values:
|
149
|
+
# user = User.find(:first)
|
150
|
+
# user.preference_values
|
151
|
+
# => {"language"=>"English", "color"=>nil}
|
152
|
+
#
|
153
|
+
# A user with stored values for a particular group:
|
154
|
+
# user.preferred_color = 'red', 'cars'
|
155
|
+
# user.preference_values
|
156
|
+
# => {"language"=>"English", "color"=>nil, "cars"=>{"language=>"English", "color"=>"red"}}
|
157
|
+
#
|
158
|
+
# Getting preference values for the owning record:
|
159
|
+
# user.preference_values(nil)
|
160
|
+
# => {"language"=>"English", "color"=>nil}
|
161
|
+
#
|
162
|
+
# Getting preference values for a particular group:
|
163
|
+
# user.preference_values('cars')
|
164
|
+
# => {"language"=>"English", "color"=>"red"}
|
165
|
+
def preference_values(*args)
|
166
|
+
if args.any?
|
167
|
+
group = args.first
|
168
|
+
group_id, group_type = Preference.split_group(group)
|
169
|
+
conditions = {:group_id => group_id, :group_type => group_type}
|
170
|
+
else
|
171
|
+
conditions = {}
|
172
|
+
end
|
173
|
+
|
174
|
+
# Find all of the stored preferences
|
175
|
+
stored_preferences = preferences.find(:all, :conditions => conditions)
|
176
|
+
|
177
|
+
# Hashify attribute -> value or group -> attribute -> value
|
178
|
+
stored_preferences.inject(self.class.default_preference_values.dup) do |preferences, preference|
|
179
|
+
if group = preference.group
|
180
|
+
preference_group = preferences[group] ||= self.class.default_preference_values.dup
|
181
|
+
else
|
182
|
+
preference_group = preferences
|
183
|
+
end
|
184
|
+
|
185
|
+
preference_group[preference.attribute] = preference.value
|
186
|
+
preferences
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
136
190
|
# Queries whether or not a value has been specified for the given attribute.
|
137
191
|
# This is dependent on how the value is type-casted.
|
138
192
|
#
|
139
|
-
# Configuration options:
|
140
|
-
# * +for+ - The record being preferenced
|
141
|
-
#
|
142
193
|
# == Examples
|
143
194
|
#
|
144
195
|
# user = User.find(:first)
|
145
|
-
# user.prefers?(:notifications)
|
196
|
+
# user.prefers?(:notifications) # => true
|
197
|
+
#
|
198
|
+
# user.prefers(:notifications, 'error') # => true
|
146
199
|
#
|
147
200
|
# newsgroup = Newsgroup.find(:first)
|
148
|
-
# user.prefers?(:notifications,
|
149
|
-
def prefers?(attribute,
|
201
|
+
# user.prefers?(:notifications, newsgroup) # => false
|
202
|
+
def prefers?(attribute, group = nil)
|
150
203
|
attribute = attribute.to_s
|
151
204
|
|
152
|
-
value = preferred(attribute,
|
205
|
+
value = preferred(attribute, group)
|
153
206
|
preference_definitions[attribute].query(value)
|
154
207
|
end
|
155
208
|
|
156
209
|
# Gets the preferred value for the given attribute.
|
157
210
|
#
|
158
|
-
# Configuration options:
|
159
|
-
# * +for+ - The record being preferenced
|
160
|
-
#
|
161
211
|
# == Examples
|
162
212
|
#
|
163
213
|
# user = User.find(:first)
|
164
|
-
# user.preferred(:color)
|
214
|
+
# user.preferred(:color) # => 'red'
|
215
|
+
#
|
216
|
+
# user.preferred(:color, 'cars') # => 'blue'
|
165
217
|
#
|
166
218
|
# car = Car.find(:first)
|
167
|
-
# user.preferred(:color,
|
168
|
-
def preferred(attribute,
|
169
|
-
options.assert_valid_keys(:for)
|
219
|
+
# user.preferred(:color, car) # => 'black'
|
220
|
+
def preferred(attribute, group = nil)
|
170
221
|
attribute = attribute.to_s
|
171
222
|
|
172
|
-
if @preference_values && @preference_values[attribute] && @preference_values[attribute].include?(
|
173
|
-
value = @preference_values[attribute][
|
223
|
+
if @preference_values && @preference_values[attribute] && @preference_values[attribute].include?(group)
|
224
|
+
value = @preference_values[attribute][group]
|
174
225
|
else
|
175
|
-
|
176
|
-
preference = preferences.find(:first, :conditions => {:attribute => attribute, :
|
226
|
+
group_id, group_type = Preference.split_group(group)
|
227
|
+
preference = preferences.find(:first, :conditions => {:attribute => attribute, :group_id => group_id, :group_type => group_type})
|
177
228
|
value = preference ? preference.value : preference_definitions[attribute].default_value
|
178
229
|
end
|
179
230
|
|
@@ -183,9 +234,6 @@ module PluginAWeek #:nodoc:
|
|
183
234
|
# Sets a new value for the given attribute. The actual Preference record
|
184
235
|
# is *not* created until the actual record is saved.
|
185
236
|
#
|
186
|
-
# Configuration options:
|
187
|
-
# * +for+ - The record being preferenced
|
188
|
-
#
|
189
237
|
# == Examples
|
190
238
|
#
|
191
239
|
# user = User.find(:first)
|
@@ -193,15 +241,14 @@ module PluginAWeek #:nodoc:
|
|
193
241
|
# user.save!
|
194
242
|
#
|
195
243
|
# newsgroup = Newsgroup.find(:first)
|
196
|
-
# user.set_preference(:notifications, true,
|
244
|
+
# user.set_preference(:notifications, true, newsgroup) # => true
|
197
245
|
# user.save!
|
198
|
-
def set_preference(attribute, value,
|
199
|
-
options.assert_valid_keys(:for)
|
246
|
+
def set_preference(attribute, value, group = nil)
|
200
247
|
attribute = attribute.to_s
|
201
248
|
|
202
249
|
@preference_values ||= {}
|
203
250
|
@preference_values[attribute] ||= {}
|
204
|
-
@preference_values[attribute][
|
251
|
+
@preference_values[attribute][group] = value
|
205
252
|
|
206
253
|
value
|
207
254
|
end
|
@@ -211,10 +258,10 @@ module PluginAWeek #:nodoc:
|
|
211
258
|
# was last saved
|
212
259
|
def update_preferences
|
213
260
|
if @preference_values
|
214
|
-
@preference_values.each do |attribute,
|
215
|
-
|
216
|
-
|
217
|
-
attributes = {:attribute => attribute, :
|
261
|
+
@preference_values.each do |attribute, grouped_records|
|
262
|
+
grouped_records.each do |group, value|
|
263
|
+
group_id, group_type = Preference.split_group(group)
|
264
|
+
attributes = {:attribute => attribute, :group_id => group_id, :group_type => group_type}
|
218
265
|
|
219
266
|
# Find an existing preference or build a new one
|
220
267
|
preference = preferences.find(:first, :conditions => attributes) || preferences.build(attributes)
|