preferences 0.0.1
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 +5 -0
- data/MIT-LICENSE +20 -0
- data/README +146 -0
- data/Rakefile +80 -0
- data/app/models/preference.rb +34 -0
- data/init.rb +1 -0
- data/lib/preferences/preference_definition.rb +50 -0
- data/lib/preferences.rb +235 -0
- data/test/app_root/app/models/car.rb +2 -0
- data/test/app_root/app/models/user.rb +7 -0
- data/test/app_root/config/environment.rb +9 -0
- data/test/app_root/db/migrate/001_create_users.rb +11 -0
- data/test/app_root/db/migrate/002_create_cars.rb +11 -0
- data/test/app_root/db/migrate/003_migrate_preferences_to_version_1.rb +9 -0
- data/test/app_root/log/in_memory.log +9137 -0
- data/test/factory.rb +45 -0
- data/test/functional/preferences_test.rb +259 -0
- data/test/test_helper.rb +13 -0
- data/test/unit/preference_definition_test.rb +185 -0
- data/test/unit/preference_test.rb +145 -0
- metadata +85 -0
data/CHANGELOG
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Aaron Pfeifer
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
== preferences
|
2
|
+
|
3
|
+
+preferences+ adds support for easily creating custom preferences for models.
|
4
|
+
|
5
|
+
== Resources
|
6
|
+
|
7
|
+
Wiki
|
8
|
+
|
9
|
+
* http://wiki.pluginaweek.org/Preferences
|
10
|
+
|
11
|
+
Source
|
12
|
+
|
13
|
+
* http://svn.pluginaweek.org/trunk/plugins/preferences
|
14
|
+
|
15
|
+
Development
|
16
|
+
|
17
|
+
* http://dev.pluginaweek.org/browser/trunk/preferences
|
18
|
+
|
19
|
+
== Description
|
20
|
+
|
21
|
+
Preferences for models within an application, such as for users, is a pretty
|
22
|
+
common idiom. Although the rule of thumb is to keep the number of preferences
|
23
|
+
available to a minimum, sometimes it's necessary if you want users to be able to
|
24
|
+
disable things like e-mail notifications.
|
25
|
+
|
26
|
+
Generally, basic preferences can be accomplish through simple designs, such as
|
27
|
+
additional columns or a bit vector described and implemented by preference_fu[http://agilewebdevelopment.com/plugins/preferencefu].
|
28
|
+
However, as you find the need for non-binary preferences and the number of
|
29
|
+
preferences becomes unmanageable as individual columns in the database, the next
|
30
|
+
step is often to create a seprate "preferences" table. This is where the +preferences+
|
31
|
+
plugin comes in.
|
32
|
+
|
33
|
+
+preferences+ encapsulates this design by hiding the fact that preferences are
|
34
|
+
stored in a separate table and making it dead-simple to define and manage
|
35
|
+
preferences.
|
36
|
+
|
37
|
+
== Usage
|
38
|
+
|
39
|
+
=== Defining preferences
|
40
|
+
|
41
|
+
To define the preferences for a model, you can do so right within the model:
|
42
|
+
|
43
|
+
class User < ActiveRecord::Base
|
44
|
+
preference :hot_salsa
|
45
|
+
preference :dark_chocolate, :default => true
|
46
|
+
preference :color, :string
|
47
|
+
preference :favorite_number
|
48
|
+
preference :language, :string, :default => 'English'
|
49
|
+
end
|
50
|
+
|
51
|
+
In the above model, 5 preferences have been defined:
|
52
|
+
* hot_salsa
|
53
|
+
* dark_chocolate
|
54
|
+
* color
|
55
|
+
* favorite_number
|
56
|
+
* language
|
57
|
+
|
58
|
+
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 not default value is
|
60
|
+
given, the default is assumed to be nil.
|
61
|
+
|
62
|
+
=== Accessing preferences
|
63
|
+
|
64
|
+
Once preferences have been defined for a model, they can be accessed either using
|
65
|
+
the shortcut methods that are generated for each preference or the generic methods
|
66
|
+
that are not specific to a particular preference.
|
67
|
+
|
68
|
+
==== Shortcut methods
|
69
|
+
|
70
|
+
There are several shortcut methods that are generated. They are shown below.
|
71
|
+
|
72
|
+
Query methods:
|
73
|
+
user.prefers_hot_salsa? # => false
|
74
|
+
user.prefers_dark_chocolate? # => false
|
75
|
+
|
76
|
+
Reader methods:
|
77
|
+
user.preferred_color # => nil
|
78
|
+
user.preferred_language # => "English"
|
79
|
+
|
80
|
+
Writer methods:
|
81
|
+
user.prefers_hot_salsa = false # => false
|
82
|
+
user.preferred_language = 'English' # => "English"
|
83
|
+
|
84
|
+
==== Generic methods
|
85
|
+
|
86
|
+
Each shortcut method is essentially a wrapper for the various generic methods
|
87
|
+
show below:
|
88
|
+
|
89
|
+
Query method:
|
90
|
+
user.prefers?(:hot_salsa) # => false
|
91
|
+
user.prefers?(:dark_chocolate) # => false
|
92
|
+
|
93
|
+
Reader method:
|
94
|
+
user.preferred(:color) # => nil
|
95
|
+
user.preferred(:language) # => "English"
|
96
|
+
|
97
|
+
Write method:
|
98
|
+
user.set_preference(:hot_salsa, false) # => false
|
99
|
+
user.set_preference(:language, "English") # => "English"
|
100
|
+
|
101
|
+
=== Accessing all preferences
|
102
|
+
|
103
|
+
To get the collection of all preferences for a particular user, you can access
|
104
|
+
the +preferences+ has_many association which is automatically generated:
|
105
|
+
|
106
|
+
user.preferences
|
107
|
+
|
108
|
+
=== Preferences for other records
|
109
|
+
|
110
|
+
In addition to defining generic preferences for the owning record, you can also
|
111
|
+
define preferences for other records. This is best shown through an example:
|
112
|
+
|
113
|
+
user = User.find(:first)
|
114
|
+
car = Car.find(:first)
|
115
|
+
|
116
|
+
user.preferred_color = 'red', {:for => car}
|
117
|
+
# user.set_preference(:color, 'red', :for => car) # The generic way
|
118
|
+
|
119
|
+
This will create a preference for the color "red" for the given car. In this way,
|
120
|
+
you can have "color" preferences for different records.
|
121
|
+
|
122
|
+
To access the preference for a particular record, you can use the same accessor
|
123
|
+
methods as before:
|
124
|
+
|
125
|
+
user.preferred_color(:for => car)
|
126
|
+
# user.preferred(:color, :for => car) # The generic way
|
127
|
+
|
128
|
+
=== Saving preferences
|
129
|
+
|
130
|
+
Note that preferences are not saved until the owning record is saved. Preferences
|
131
|
+
are treated in a similar fashion to attributes. For example,
|
132
|
+
|
133
|
+
user = user.find(:first)
|
134
|
+
user.attributes = {:preferred_color => 'red'}
|
135
|
+
user.save!
|
136
|
+
|
137
|
+
Preferences are stored in a separate table assumed to be called "preferences".
|
138
|
+
|
139
|
+
== Testing
|
140
|
+
|
141
|
+
Before you can run any tests, the following gem must be installed:
|
142
|
+
* plugin_test_helper[http://wiki.pluginaweek.org/Plugin_test_helper]
|
143
|
+
|
144
|
+
== Dependencies
|
145
|
+
|
146
|
+
* plugins_plus[http://wiki.pluginaweek.org/Plugins_plus]
|
data/Rakefile
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
require 'rake/rdoctask'
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
require 'rake/contrib/sshpublisher'
|
5
|
+
|
6
|
+
PKG_NAME = 'preferences'
|
7
|
+
PKG_VERSION = '0.0.1'
|
8
|
+
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
9
|
+
RUBY_FORGE_PROJECT = 'pluginaweek'
|
10
|
+
|
11
|
+
desc 'Default: run unit tests.'
|
12
|
+
task :default => :test
|
13
|
+
|
14
|
+
desc 'Test the preferences plugin.'
|
15
|
+
Rake::TestTask.new(:test) do |t|
|
16
|
+
t.libs << 'lib'
|
17
|
+
t.pattern = 'test/**/*_test.rb'
|
18
|
+
t.verbose = true
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'Generate documentation for the preferences plugin.'
|
22
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
23
|
+
rdoc.rdoc_dir = 'rdoc'
|
24
|
+
rdoc.title = 'Preferences'
|
25
|
+
rdoc.template = '../rdoc_template.rb'
|
26
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
27
|
+
rdoc.rdoc_files.include('README')
|
28
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
29
|
+
end
|
30
|
+
|
31
|
+
spec = Gem::Specification.new do |s|
|
32
|
+
s.name = PKG_NAME
|
33
|
+
s.version = PKG_VERSION
|
34
|
+
s.platform = Gem::Platform::RUBY
|
35
|
+
s.summary = 'Adds support for easily creating custom preferences for models'
|
36
|
+
|
37
|
+
s.files = FileList['{app,lib,test}/**/*'].to_a + %w(CHANGELOG init.rb MIT-LICENSE Rakefile README)
|
38
|
+
s.require_path = 'lib'
|
39
|
+
s.autorequire = 'preferences'
|
40
|
+
s.has_rdoc = true
|
41
|
+
s.test_files = Dir['test/**/*_test.rb']
|
42
|
+
|
43
|
+
s.author = 'Aaron Pfeifer'
|
44
|
+
s.email = 'aaron@pluginaweek.org'
|
45
|
+
s.homepage = 'http://www.pluginaweek.org'
|
46
|
+
end
|
47
|
+
|
48
|
+
Rake::GemPackageTask.new(spec) do |p|
|
49
|
+
p.gem_spec = spec
|
50
|
+
p.need_tar = true
|
51
|
+
p.need_zip = true
|
52
|
+
end
|
53
|
+
|
54
|
+
desc 'Publish the beta gem'
|
55
|
+
task :pgem => [:package] do
|
56
|
+
Rake::SshFilePublisher.new('aaron@pluginaweek.org', '/home/aaron/gems.pluginaweek.org/public/gems', 'pkg', "#{PKG_FILE_NAME}.gem").upload
|
57
|
+
end
|
58
|
+
|
59
|
+
desc 'Publish the API documentation'
|
60
|
+
task :pdoc => [:rdoc] do
|
61
|
+
Rake::SshDirPublisher.new('aaron@pluginaweek.org', "/home/aaron/api.pluginaweek.org/public/#{PKG_NAME}", 'rdoc').upload
|
62
|
+
end
|
63
|
+
|
64
|
+
desc 'Publish the API docs and gem'
|
65
|
+
task :publish => [:pgem, :pdoc, :release]
|
66
|
+
|
67
|
+
desc 'Publish the release files to RubyForge.'
|
68
|
+
task :release => [:gem, :package] do
|
69
|
+
require 'rubyforge'
|
70
|
+
|
71
|
+
ruby_forge = RubyForge.new
|
72
|
+
ruby_forge.login
|
73
|
+
|
74
|
+
%w( gem tgz zip ).each do |ext|
|
75
|
+
file = "pkg/#{PKG_FILE_NAME}.#{ext}"
|
76
|
+
puts "Releasing #{File.basename(file)}..."
|
77
|
+
|
78
|
+
ruby_forge.add_release(RUBY_FORGE_PROJECT, PKG_NAME, PKG_VERSION, file)
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Represents a preferred value for a particular preference on a model.
|
2
|
+
#
|
3
|
+
# == Targeted preferences
|
4
|
+
#
|
5
|
+
# In addition to simple named preferences, preferences can also be targeted for
|
6
|
+
# a particular record. For example, a User may have a preferred color for a
|
7
|
+
# particular Car. In this case, the +owner+ is the User, the +preference+ is
|
8
|
+
# the color, and the +target+ is the Car. This allows preferences to have a sort
|
9
|
+
# of context around them.
|
10
|
+
class Preference < ActiveRecord::Base
|
11
|
+
belongs_to :owner,
|
12
|
+
:polymorphic => true
|
13
|
+
belongs_to :preferenced,
|
14
|
+
:polymorphic => true
|
15
|
+
|
16
|
+
validates_presence_of :attribute,
|
17
|
+
:owner_id,
|
18
|
+
:owner_type
|
19
|
+
validates_presence_of :preferenced_id,
|
20
|
+
:preferenced_type,
|
21
|
+
:if => Proc.new {|p| p.preferenced_id? || p.preferenced_type?}
|
22
|
+
|
23
|
+
# The definition for the attribute
|
24
|
+
def definition
|
25
|
+
owner_type.constantize.preference_definitions[attribute] if owner_type
|
26
|
+
end
|
27
|
+
|
28
|
+
# Typecasts the value depending on the preference definition's declared type
|
29
|
+
def value
|
30
|
+
value = read_attribute(:value)
|
31
|
+
value = definition.type_cast(value) if definition
|
32
|
+
value
|
33
|
+
end
|
34
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'preferences'
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module PluginAWeek #:nodoc:
|
2
|
+
# Adds support for defining preferences on ActiveRecord models.
|
3
|
+
module Preferences
|
4
|
+
# Represents the definition of a preference for a particular model
|
5
|
+
class PreferenceDefinition
|
6
|
+
def initialize(attribute, *args) #:nodoc:
|
7
|
+
options = args.extract_options!
|
8
|
+
options.assert_valid_keys(:default)
|
9
|
+
|
10
|
+
@type = args.first ? args.first.to_s : 'boolean'
|
11
|
+
|
12
|
+
# Create a column that will be responsible for typecasting
|
13
|
+
@column = ActiveRecord::ConnectionAdapters::Column.new(attribute.to_s, options[:default], @type == 'any' ? nil : @type)
|
14
|
+
end
|
15
|
+
|
16
|
+
# The attribute which is being preferenced
|
17
|
+
def attribute
|
18
|
+
@column.name
|
19
|
+
end
|
20
|
+
|
21
|
+
# The default value to use for the preference in case none have been
|
22
|
+
# previously defined
|
23
|
+
def default_value
|
24
|
+
@column.default
|
25
|
+
end
|
26
|
+
|
27
|
+
# Typecasts the value based on the type of preference that was defined
|
28
|
+
def type_cast(value)
|
29
|
+
if @type == 'any'
|
30
|
+
value
|
31
|
+
else
|
32
|
+
@column.type_cast(value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Typecasts the value to true/false depending on the type of preference
|
37
|
+
def query(value)
|
38
|
+
unless value = type_cast(value)
|
39
|
+
false
|
40
|
+
else
|
41
|
+
if @column.number?
|
42
|
+
!value.zero?
|
43
|
+
else
|
44
|
+
!value.blank?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/preferences.rb
ADDED
@@ -0,0 +1,235 @@
|
|
1
|
+
require 'preferences/preference_definition'
|
2
|
+
|
3
|
+
module PluginAWeek #:nodoc:
|
4
|
+
# Adds support for defining preferences on ActiveRecord models.
|
5
|
+
#
|
6
|
+
# == Saving preferences
|
7
|
+
#
|
8
|
+
# Preferences are not automatically saved when they are set. You must save
|
9
|
+
# the record that the preferences were set on.
|
10
|
+
#
|
11
|
+
# For example,
|
12
|
+
#
|
13
|
+
# class User < ActiveRecord::Base
|
14
|
+
# preference :notifications
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# u = User.new(:login => 'admin', :prefers_notifications => false)
|
18
|
+
# u.save!
|
19
|
+
#
|
20
|
+
# u = User.find_by_login('admin')
|
21
|
+
# u.attributes = {:prefers_notifications => true}
|
22
|
+
# u.save!
|
23
|
+
module Preferences
|
24
|
+
def self.included(base) #:nodoc:
|
25
|
+
base.class_eval do
|
26
|
+
extend PluginAWeek::Preferences::MacroMethods
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module MacroMethods
|
31
|
+
# Defines a new preference for all records in the model. By default, preferences
|
32
|
+
# are assumed to have a boolean data type, so all values will be typecasted
|
33
|
+
# to true/false based on ActiveRecord rules.
|
34
|
+
#
|
35
|
+
# Configuration options:
|
36
|
+
# * +default+ - The default value for the preference. Default is nil.
|
37
|
+
#
|
38
|
+
# == Examples
|
39
|
+
#
|
40
|
+
# The example below shows the various ways to define a preference for a
|
41
|
+
# particular model.
|
42
|
+
#
|
43
|
+
# class User < ActiveRecord::Base
|
44
|
+
# preference :notifications, :default => false
|
45
|
+
# preference :color, :string, :default => 'red'
|
46
|
+
# preference :favorite_number, :integer
|
47
|
+
# preference :data, :any # Allows any data type to be stored
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# All preferences are also inherited by subclasses.
|
51
|
+
#
|
52
|
+
# == Associations
|
53
|
+
#
|
54
|
+
# After the first preference is defined, the following associations are
|
55
|
+
# created for the model:
|
56
|
+
# * +preferences+ - A collection of all the preferences specified for a record
|
57
|
+
#
|
58
|
+
# == Generated shortcut methods
|
59
|
+
#
|
60
|
+
# In addition to calling <tt>prefers?</tt> and +preferred+ on a record, you
|
61
|
+
# can also use the shortcut methods that are generated when a preference is
|
62
|
+
# defined. For example,
|
63
|
+
#
|
64
|
+
# class User < ActiveRecord::Base
|
65
|
+
# preference :notifications
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# ...generates the following methods:
|
69
|
+
# * <tt>prefers_notifications?</tt> - The same as calling <tt>record.prefers?(:notifications)</tt>
|
70
|
+
# * <tt>prefers_notifications=(value)</tt> - The same as calling <tt>record.set_preference(:notifications, value)</tt>
|
71
|
+
# * <tt>preferred_notifications</tt> - The same as called <tt>record.preferred(:notifications)</tt>
|
72
|
+
# * <tt>preferred_notifications=(value)</tt> - The same as calling <tt>record.set_preference(:notifications, value)</tt>
|
73
|
+
#
|
74
|
+
# Notice that there are two tenses used depending on the context of the
|
75
|
+
# preference. Conventionally, <tt>prefers_notifications?</tt> is better
|
76
|
+
# for boolean preferences, while +preferred_color+ is better for non-boolean
|
77
|
+
# preferences.
|
78
|
+
#
|
79
|
+
# Example:
|
80
|
+
#
|
81
|
+
# user = User.find(:first)
|
82
|
+
# user.prefers_notifications? # => false
|
83
|
+
# user.prefers_color? # => true
|
84
|
+
# user.preferred_color # => 'red'
|
85
|
+
# user.preferred_color = 'blue' # => 'blue'
|
86
|
+
#
|
87
|
+
# user.prefers_notifications = true
|
88
|
+
#
|
89
|
+
# car = Car.find(:first)
|
90
|
+
# user.preferred_color = 'red', {:for => car} # => 'red'
|
91
|
+
# user.preferred_color(:for => car) # => 'red'
|
92
|
+
# user.prefers_color?(:for => car) # => true
|
93
|
+
#
|
94
|
+
# user.save! # => true
|
95
|
+
def preference(attribute, *args)
|
96
|
+
unless included_modules.include?(InstanceMethods)
|
97
|
+
class_inheritable_hash :preference_definitions
|
98
|
+
|
99
|
+
has_many :preferences,
|
100
|
+
:as => :owner
|
101
|
+
|
102
|
+
after_save :update_preferences
|
103
|
+
|
104
|
+
include PluginAWeek::Preferences::InstanceMethods
|
105
|
+
end
|
106
|
+
|
107
|
+
# Create the definition
|
108
|
+
attribute = attribute.to_s
|
109
|
+
definition = PreferenceDefinition.new(attribute, *args)
|
110
|
+
self.preference_definitions = {attribute => definition}
|
111
|
+
|
112
|
+
# Create short-hand helper methods, making sure that the attribute
|
113
|
+
# is method-safe in terms of what characters are allowed
|
114
|
+
attribute = attribute.gsub(/[^A-Za-z0-9_-]/, '').underscore
|
115
|
+
class_eval <<-end_eval
|
116
|
+
def prefers_#{attribute}?(options = {})
|
117
|
+
prefers?(#{attribute.dump}, options)
|
118
|
+
end
|
119
|
+
|
120
|
+
def prefers_#{attribute}=(args)
|
121
|
+
set_preference(*([#{attribute.dump}] + [args].flatten))
|
122
|
+
end
|
123
|
+
|
124
|
+
def preferred_#{attribute}(options = {})
|
125
|
+
preferred(#{attribute.dump}, options)
|
126
|
+
end
|
127
|
+
|
128
|
+
alias_method :preferred_#{attribute}=, :prefers_#{attribute}=
|
129
|
+
end_eval
|
130
|
+
|
131
|
+
definition
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
module InstanceMethods
|
136
|
+
# Queries whether or not a value has been specified for the given attribute.
|
137
|
+
# This is dependent on how the value is type-casted.
|
138
|
+
#
|
139
|
+
# Configuration options:
|
140
|
+
# * +for+ - The record being preferenced
|
141
|
+
#
|
142
|
+
# == Examples
|
143
|
+
#
|
144
|
+
# user = User.find(:first)
|
145
|
+
# user.prefers?(:notifications) # => true
|
146
|
+
#
|
147
|
+
# newsgroup = Newsgroup.find(:first)
|
148
|
+
# user.prefers?(:notifications, :for => newsgroup) # => false
|
149
|
+
def prefers?(attribute, options = {})
|
150
|
+
attribute = attribute.to_s
|
151
|
+
|
152
|
+
value = preferred(attribute, options)
|
153
|
+
preference_definitions[attribute].query(value)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Gets the preferred value for the given attribute.
|
157
|
+
#
|
158
|
+
# Configuration options:
|
159
|
+
# * +for+ - The record being preferenced
|
160
|
+
#
|
161
|
+
# == Examples
|
162
|
+
#
|
163
|
+
# user = User.find(:first)
|
164
|
+
# user.preferred(:color) # => 'red'
|
165
|
+
#
|
166
|
+
# car = Car.find(:first)
|
167
|
+
# user.preferred(:color, :for => car) # => 'black'
|
168
|
+
def preferred(attribute, options = {})
|
169
|
+
options.assert_valid_keys(:for)
|
170
|
+
attribute = attribute.to_s
|
171
|
+
|
172
|
+
if @preference_values && @preference_values[attribute] && @preference_values[attribute].include?(options[:for])
|
173
|
+
value = @preference_values[attribute][options[:for]]
|
174
|
+
else
|
175
|
+
preferenced_id, preferenced_type = options[:for].id, options[:for].class.base_class.name.to_s if options[:for]
|
176
|
+
preference = preferences.find(:first, :conditions => {:attribute => attribute, :preferenced_id => preferenced_id, :preferenced_type => preferenced_type})
|
177
|
+
value = preference ? preference.value : preference_definitions[attribute].default_value
|
178
|
+
end
|
179
|
+
|
180
|
+
value
|
181
|
+
end
|
182
|
+
|
183
|
+
# Sets a new value for the given attribute. The actual Preference record
|
184
|
+
# is *not* created until the actual record is saved.
|
185
|
+
#
|
186
|
+
# Configuration options:
|
187
|
+
# * +for+ - The record being preferenced
|
188
|
+
#
|
189
|
+
# == Examples
|
190
|
+
#
|
191
|
+
# user = User.find(:first)
|
192
|
+
# user.set_preference(:notifications, false) # => false
|
193
|
+
# user.save!
|
194
|
+
#
|
195
|
+
# newsgroup = Newsgroup.find(:first)
|
196
|
+
# user.set_preference(:notifications, true, :for => newsgroup) # => true
|
197
|
+
# user.save!
|
198
|
+
def set_preference(attribute, value, options = {})
|
199
|
+
options.assert_valid_keys(:for)
|
200
|
+
attribute = attribute.to_s
|
201
|
+
|
202
|
+
@preference_values ||= {}
|
203
|
+
@preference_values[attribute] ||= {}
|
204
|
+
@preference_values[attribute][options[:for]] = value
|
205
|
+
|
206
|
+
value
|
207
|
+
end
|
208
|
+
|
209
|
+
private
|
210
|
+
# Updates any preferences that have been changed/added since the record
|
211
|
+
# was last saved
|
212
|
+
def update_preferences
|
213
|
+
if @preference_values
|
214
|
+
@preference_values.each do |attribute, preferenced_records|
|
215
|
+
preferenced_records.each do |preferenced, value|
|
216
|
+
preferenced_id, preferenced_type = preferenced.id, preferenced.class.base_class.name.to_s if preferenced
|
217
|
+
attributes = {:attribute => attribute, :preferenced_id => preferenced_id, :preferenced_type => preferenced_type}
|
218
|
+
|
219
|
+
# Find an existing preference or build a new one
|
220
|
+
preference = preferences.find(:first, :conditions => attributes) || preferences.build(attributes)
|
221
|
+
preference.value = value
|
222
|
+
preference.save!
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
@preference_values = nil
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
ActiveRecord::Base.class_eval do
|
234
|
+
include PluginAWeek::Preferences
|
235
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'config/boot'
|
2
|
+
require "#{File.dirname(__FILE__)}/../../../../plugins_plus/boot"
|
3
|
+
|
4
|
+
Rails::Initializer.run do |config|
|
5
|
+
config.plugin_paths << '..'
|
6
|
+
config.plugins = %w(plugins_plus preferences)
|
7
|
+
config.cache_classes = false
|
8
|
+
config.whiny_nils = true
|
9
|
+
end
|