preferences 0.0.1 → 0.1.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.
- 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)
|